diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 0000000000..934256cb29 --- /dev/null +++ b/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/.flowconfig b/.flowconfig index c3edaf9464..b69d071bc2 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,65 +1,58 @@ [ignore] # We fork some components by platform. -.*/*.web.js .*/*.android.js -# Some modules have their own node_modules with overlap -.*/node_modules/node-haste/.* +# Ignore templates with `@flow` in header +.*/local-cli/generator.* -# Ugh -.*/node_modules/babel.* -.*/node_modules/babylon.* -.*/node_modules/invariant.* +# Ignore malformed json +.*/node_modules/y18n/test/.*\.json -# Ignore react and fbjs where there are overlaps, but don't ignore -# anything that react-native relies on -.*/node_modules/fbjs/lib/Map.js -.*/node_modules/fbjs/lib/Promise.js -.*/node_modules/fbjs/lib/fetch.js -.*/node_modules/fbjs/lib/ExecutionEnvironment.js -.*/node_modules/fbjs/lib/isEmpty.js -.*/node_modules/fbjs/lib/crc32.js -.*/node_modules/fbjs/lib/ErrorUtils.js +# Ignore the website subdir +/website/.* -# Flow has a built-in definition for the 'react' module which we prefer to use -# over the currently-untyped source -.*/node_modules/react/react.js -.*/node_modules/react/lib/React.js -.*/node_modules/react/lib/ReactDOM.js +# Ignore BUCK generated dirs +/\.buckd/ -# Ignore commoner tests -.*/node_modules/commoner/test/.* +# Ignore unexpected extra @providesModule +.*/node_modules/commoner/test/source/widget/share.js -# See https://github.com/facebook/flow/issues/442 -.*/react-tools/node_modules/commoner/lib/reader.js - -# Ignore jest -.*/node_modules/jest-cli/.* - -# Ignore Website -.*/website/.* +# Ignore duplicate module providers +# For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root +.*/Libraries/react-native/React.js +.*/Libraries/react-native/ReactNative.js +.*/node_modules/jest-runtime/build/__tests__/.* [include] [libs] node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow +flow/ [options] module.system=haste +esproposal.class_static_fields=enable +esproposal.class_instance_fields=enable + +experimental.strict_type_args=true + munge_underscores=true module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' -module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub' +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(30\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(30\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +unsafe.enable_getters_and_setters=true + [version] -0.21.0 +^0.30.0 diff --git a/.gitignore b/.gitignore index 3096ce4217..4e18fe503c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # OSX # .DS_Store +.projectile +.cljs_rhino_repl/ +.#* # Xcode # @@ -27,19 +30,51 @@ project.xcworkspace .idea .gradle local.properties -syng-im.iml +status-im.iml # node.js # node_modules/ npm-debug.log +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + # Generated by re-natal # index.android.js index.ios.js target/ +# Generated by lein voom +# +/pom.xml + + # Figwheel # figwheel_server.log +.nrepl-port + +# Lein +# +.lein-failures +.lein-repl-history + +## Doo +# +out +doo-index.html + +# Re-natal +re-natal + +# status-go +Statusgo.framework + +#ios +ios/Pods +ios/StatusIm.xcworkspace diff --git a/.re-natal b/.re-natal index 8fd652b4ea..d64f30c702 100644 --- a/.re-natal +++ b/.re-natal @@ -1,5 +1,5 @@ { - "name": "SyngIm", + "name": "StatusIm", "interface": "reagent", "androidHost": "10.0.3.2", "modules": [ @@ -7,15 +7,42 @@ "react-native-invertible-scroll-view", "awesome-phonenumber", "realm", - "react-native-loading-spinner-overlay", "react-native-i18n", "realm/react-native", "react-native-action-button", "react-native-vector-icons/Ionicons", + "react-native-vector-icons/Octicons", "react-native-circle-checkbox", - "react-native-randombytes" + "react-native-randombytes", + "dismissKeyboard", + "react-native-linear-gradient", + "react-native-splash-screen", + "react-native-android-sms-listener", + "react-native-status", + "react-native-camera", + "react-native-qrcode", + "react-native-orientation", + "identicon.js", + "react-native-fs", + "react-native-dialogs", + "react-native-image-resizer", + "react-native-image-crop-picker", + "react-native-webview-bridge", + "react-native-drawer-layout", + "homoglyph-finder", + "web3", + "eccjs", + "chance", + "react-native-swiper", + "react-native-share", + "react-native-emoji-picker" ], "imageDirs": [ "images" - ] + ], + "iosHost": "localhost", + "envRoots": { + "dev": "env/dev", + "prod": "env/prod" + } } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e03cc81c3e..77afa77091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,5 +20,5 @@ All notable changes to this project will be documented in this file. This change - Files from the new template. - Widget maker public API - `make-widget-sync`. -[unreleased]: https://github.com/your-name/syng-im/compare/0.1.1...HEAD -[0.1.1]: https://github.com/your-name/syng-im/compare/0.1.0...0.1.1 +[unreleased]: https://github.com/your-name/status-im/compare/0.1.1...HEAD +[0.1.1]: https://github.com/your-name/status-im/compare/0.1.0...0.1.1 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7689f30efd..0000000000 --- a/LICENSE +++ /dev/null @@ -1,214 +0,0 @@ -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC -LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM -CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and -documentation distributed under this Agreement, and - -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and are -distributed by that particular Contributor. A Contribution 'originates' from -a Contributor if it was added to the Program by such Contributor itself or -anyone acting on such Contributor's behalf. Contributions do not include -additions to the Program which: (i) are separate modules of software -distributed in conjunction with the Program under their own license -agreement, and (ii) are not derivative works of the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents" mean patent claims licensable by a Contributor which are -necessarily infringed by the use or sale of its Contribution alone or when -combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -2. GRANT OF RIGHTS - -a) Subject to the terms of this Agreement, each Contributor hereby grants -Recipient a non-exclusive, worldwide, royalty-free copyright license to -reproduce, prepare derivative works of, publicly display, publicly perform, -distribute and sublicense the Contribution of such Contributor, if any, and -such derivative works, in source code and object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby grants -Recipient a non-exclusive, worldwide, royalty-free patent license under -Licensed Patents to make, use, sell, offer to sell, import and otherwise -transfer the Contribution of such Contributor, if any, in source code and -object code form. This patent license shall apply to the combination of the -Contribution and the Program if, at the time the Contribution is added by the -Contributor, such addition of the Contribution causes such combination to be -covered by the Licensed Patents. The patent license shall not apply to any -other combinations which include the Contribution. No hardware per se is -licensed hereunder. - -c) Recipient understands that although each Contributor grants the licenses -to its Contributions set forth herein, no assurances are provided by any -Contributor that the Program does not infringe the patent or other -intellectual property rights of any other entity. Each Contributor disclaims -any liability to Recipient for claims brought by any other entity based on -infringement of intellectual property rights or otherwise. As a condition to -exercising the rights and licenses granted hereunder, each Recipient hereby -assumes sole responsibility to secure any other intellectual property rights -needed, if any. For example, if a third party patent license is required to -allow Recipient to distribute the Program, it is Recipient's responsibility -to acquire that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient -copyright rights in its Contribution, if any, to grant the copyright license -set forth in this Agreement. - -3. REQUIREMENTS - -A Contributor may choose to distribute the Program in object code form under -its own license agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties and -conditions, express and implied, including warranties or conditions of title -and non-infringement, and implied warranties or conditions of merchantability -and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for -damages, including direct, indirect, special, incidental and consequential -damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are offered -by that Contributor alone and not by any other party; and - -iv) states that source code for the Program is available from such -Contributor, and informs licensees how to obtain it in a reasonable manner on -or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained within -the Program. - -Each Contributor must identify itself as the originator of its Contribution, -if any, in a manner that reasonably allows subsequent Recipients to identify -the originator of the Contribution. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities with -respect to end users, business partners and the like. While this license is -intended to facilitate the commercial use of the Program, the Contributor who -includes the Program in a commercial product offering should do so in a -manner which does not create potential liability for other Contributors. -Therefore, if a Contributor includes the Program in a commercial product -offering, such Contributor ("Commercial Contributor") hereby agrees to defend -and indemnify every other Contributor ("Indemnified Contributor") against any -losses, damages and costs (collectively "Losses") arising from claims, -lawsuits and other legal actions brought by a third party against the -Indemnified Contributor to the extent caused by the acts or omissions of such -Commercial Contributor in connection with its distribution of the Program in -a commercial product offering. The obligations in this section do not apply -to any claims or Losses relating to any actual or alleged intellectual -property infringement. In order to qualify, an Indemnified Contributor must: -a) promptly notify the Commercial Contributor in writing of such claim, and -b) allow the Commercial Contributor tocontrol, and cooperate with the -Commercial Contributor in, the defense and any related settlement -negotiations. The Indemnified Contributor may participate in any such claim -at its own expense. - -For example, a Contributor might include the Program in a commercial product -offering, Product X. That Contributor is then a Commercial Contributor. If -that Commercial Contributor then makes performance claims, or offers -warranties related to Product X, those performance claims and warranties are -such Commercial Contributor's responsibility alone. Under this section, the -Commercial Contributor would have to defend claims against the other -Contributors related to those performance claims and warranties, and if a -court requires any other Contributor to pay any damages as a result, the -Commercial Contributor must pay those damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON -AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER -EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR -CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A -PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the -appropriateness of using and distributing the Program and assumes all risks -associated with its exercise of rights under this Agreement , including but -not limited to the risks and costs of program errors, compliance with -applicable laws, damage to or loss of data, programs or equipment, and -unavailability or interruption of operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY -CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION -LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE -EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGES. - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of the -remainder of the terms of this Agreement, and without further action by the -parties hereto, such provision shall be reformed to the minimum extent -necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Program itself -(excluding combinations of the Program with other software or hardware) -infringes such Recipient's patent(s), then such Recipient's rights granted -under Section 2(b) shall terminate as of the date such litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails to -comply with any of the material terms or conditions of this Agreement and -does not cure such failure in a reasonable period of time after becoming -aware of such noncompliance. If all Recipient's rights under this Agreement -terminate, Recipient agrees to cease use and distribution of the Program as -soon as reasonably practicable. However, Recipient's obligations under this -Agreement and any licenses granted by Recipient relating to the Program shall -continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, but in -order to avoid inconsistency the Agreement is copyrighted and may only be -modified in the following manner. The Agreement Steward reserves the right to -publish new versions (including revisions) of this Agreement from time to -time. No one other than the Agreement Steward has the right to modify this -Agreement. The Eclipse Foundation is the initial Agreement Steward. The -Eclipse Foundation may assign the responsibility to serve as the Agreement -Steward to a suitable separate entity. Each new version of the Agreement will -be given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version of -the Agreement is published, Contributor may elect to distribute the Program -(including its Contributions) under the new version. Except as expressly -stated in Sections 2(a) and 2(b) above, Recipient receives no rights or -licenses to the intellectual property of any Contributor under this -Agreement, whether expressly, by implication, estoppel or otherwise. All -rights in the Program not expressly granted under this Agreement are -reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to this -Agreement will bring a legal action under this Agreement more than one year -after the cause of action arose. Each party waives its rights to a jury trial -in any resulting litigation. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..3759302a47 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,356 @@ +Mozilla Public License Version 2.0 +================================== + +### 1. Definitions + +**1.1. “Contributor”** + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +**1.2. “Contributor Version”** + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +**1.3. “Contribution”** + means Covered Software of a particular Contributor. + +**1.4. “Covered Software”** + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +**1.5. “Incompatible With Secondary Licenses”** + means + +* **(a)** that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or +* **(b)** that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +**1.6. “Executable Form”** + means any form of the work other than Source Code Form. + +**1.7. “Larger Work”** + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +**1.8. “License”** + means this document. + +**1.9. “Licensable”** + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +**1.10. “Modifications”** + means any of the following: + +* **(a)** any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or +* **(b)** any new file in Source Code Form that contains any Covered + Software. + +**1.11. “Patent Claims” of a Contributor** + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +**1.12. “Secondary License”** + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +**1.13. “Source Code Form”** + means the form of the work preferred for making modifications. + +**1.14. “You” (or “Your”)** + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, “control” means **(a)** the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or **(b)** ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + + +### 2. License Grants and Conditions + +#### 2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +* **(a)** under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and +* **(b)** under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +#### 2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +#### 2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +* **(a)** for any code that a Contributor has removed from Covered Software; + or +* **(b)** for infringements caused by: **(i)** Your and any other third party's + modifications of Covered Software, or **(ii)** the combination of its + Contributions with other software (except as part of its Contributor + Version); or +* **(c)** under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +#### 2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +#### 2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +#### 2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +#### 2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + + +### 3. Responsibilities + +#### 3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +#### 3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +* **(a)** such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +* **(b)** You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +#### 3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +#### 3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +#### 3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + + +### 4. Inability to Comply Due to Statute or Regulation + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: **(a)** comply with +the terms of this License to the maximum extent possible; and **(b)** +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + + +### 5. Termination + +**5.1.** The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated **(a)** provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and **(b)** on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +**5.2.** If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + + +### 6. Disclaimer of Warranty + +> Covered Software is provided under this License on an “as is” +> basis, without warranty of any kind, either expressed, implied, or +> statutory, including, without limitation, warranties that the +> Covered Software is free of defects, merchantable, fit for a +> particular purpose or non-infringing. The entire risk as to the +> quality and performance of the Covered Software is with You. +> Should any Covered Software prove defective in any respect, You +> (not any Contributor) assume the cost of any necessary servicing, +> repair, or correction. This disclaimer of warranty constitutes an +> essential part of this License. No use of any Covered Software is +> authorized under this License except under this disclaimer. + +### 7. Limitation of Liability + +> Under no circumstances and under no legal theory, whether tort +> (including negligence), contract, or otherwise, shall any +> Contributor, or anyone who distributes Covered Software as +> permitted above, be liable to You for any direct, indirect, +> special, incidental, or consequential damages of any character +> including, without limitation, damages for lost profits, loss of +> goodwill, work stoppage, computer failure or malfunction, or any +> and all other commercial damages or losses, even if such party +> shall have been informed of the possibility of such damages. This +> limitation of liability shall not apply to liability for death or +> personal injury resulting from such party's negligence to the +> extent applicable law prohibits such limitation. Some +> jurisdictions do not allow the exclusion or limitation of +> incidental or consequential damages, so this exclusion and +> limitation may not apply to You. + + +### 8. Litigation + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + + +### 9. Miscellaneous + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + + +### 10. Versions of the License + +#### 10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +#### 10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +#### 10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +## Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +## Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + diff --git a/README.md b/README.md index 19e1fbc53f..2b196d624f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,80 @@ -# syng-im +![Status - A Browser, Messenger, and gateway to the decentralised world of Ethereum](https://status.im/img/status-github-banner@2x.png?v=1.1 "Status - A Browser, Messenger, and gateway to the decentralised world of Ethereum") +// TODO badges -A Clojure library designed to ... well, that part is up to you. +# Status - a Mobile Ethereum Operating System -## Usage +Join us in creating a browser, messenger, and gateway to a decentralised world. Status is a free and open source mobile client targeting Android & iOS built entirely on [Ethereum](https://ethereum.org/) technologies. That's right, no middle men and `go-ethereum` running directly on your device. -FIXME +[![Getting started with Status](https://i.imgur.com/C0aZZEL.jpg)](https://www.youtube.com/watch?v=oDCSEmS9c3o "Getting started with Status") + + +## Why? + +We believe in a medium of pure free trade, economies with fair, permissionless access and a world without intermediaries. We want to create policies that can exist between friends or scale globally, we want to communicate securely and be uninhibited by legacy systems. + +We want to take responsibilty for our data, the way we conduct ourselves privately and promote this way of life to a mass audience. + +We want deep insights into our own economies so we can make informed, data-driven decisions on how to make our lives better. The Ethereum blockchain, Smart Contracts, Swarm and Whisper provides us a path forward. + +If this interests you, **help us make Status a reality** - anyone can contribute and we need everyone at any skill level to participate. + +## How to Contribute? + +Go straight to the [wiki](https://wiki.status.im) or [join our Slack](http://slack.status.im) or choose what interests you: + +- **Developer** +Developers are the heart of software and to keep Status beating we need all the help we can get! If you're looking to code in ClojureScript or Golang then Status is the project for you! We use React Native and there is even some Java/Objective-C too! +Want to learn more about it? Start by [Building Status](https://wiki.status.im/contributing/development/building-status/) for more details on configuring your environment, managing project dependencies, coding guidelines and testing procedures. + +- **Community Management** +Metcalfe's law states that the value of a network is proportional to the square of the number of connected users of the system - without community Status is meaningless. We're looking to create a positive, fun environment to explore new ideas, experiment and grow the Status community. Building a community takes alot of work but the people you'll meet and long lasting relationships you form will be well worth it, check out our [Community Development Guide](https://wiki.status.im/community/development/) + +- **Specification / Documentation** +John Dewey once said "Education is not preparation for life; education is life *itself* ". Developers & Designers need guidance and it all starts from documentation and specifications. Our software is only as good as its documentation, head over to our [wiki](https://wiki.status.im) and see how you can improve what we have. + +- **Blog Writing** +Content is King, keeping our blog up to date and informing the community of news helps keep everyone on the same page. [Jump on our Slack](http://slack.status.im) and discuss with Carl how you can contribute. + +- **Marketing** +In this day and age attention is limited, we need all the help we can get to find people who are interested in Status, whether that is paid user aquisition, public relations or hype building, check out our [Marketers' Guide](https://wiki.status.im/contributing/marketing/) to see how you can spread the word. + +- **Testers** +It's bug hunting season! Status is currently in Alpha and there are sure to be a bunch of education, [grab your fresh copy of Status](http://TODO) and shake your phone to submit bug reports, or start browsing our [Github Issues](http://github.com/status-im/status-react/issues). Every bug you find brings Status closer to stable, usable software for everyone to enjoy! + +- **Security** +Status is a visual interface to make permanent changes on the Blockchain, it handles crypto-tokens that have real value and allows 3rd party code execution. Security is paramount to its success. You are given permission to break Status as hard as you can, as long as you share your findings with the community! + +- **Design & User Experience** +It's all about that sex appeal, we want cryptocurrencies to be easy and fun to use, ugly software doesn't help adoption. [Check out our designs](https://TODO) and show us how to make them better! + +- **Evangelism** +Help us spread the word! Tell a friend *right now*, infact tell **everyone** - yell from a mountain if you have to, every person counts! + +## Give me Binaries! + +Currently the fastest way to get your hands on a binary is to join our Early Access by submitting your email on our [website](https://status.im) or by [building it yourself](https://wiki.status.im/contributing/development/building-status/). + +// TODO GPlay, TestFlight Instructions & FDroid + +## Core Contributors +// TODO mugshots + +- Jarrad ([@jarradh](https://github.com/jarradh)) - Cofounder and Thought Leader. +- Carl ([@carlbennetts](https://github.com/carlbennetts)) - Cofounder and Communications & Marketing. +- Roman ([@rasom](https://github.com/rasom)) - Clojure and project lead. +- Alexander ([@alwx](https://github.com/alwx)) - Clojure ninja! +- Victor ([@farazdagi](https://github.com/farazdagi)) - Golang one-man-army. +- Andrei ([@andmironov](https://github.com/andmironov)) - Visual design and user experience conjurer. + +Special thanks to [@adrian-tiberius](https://github.com/adrian-tiberius). +Without the dedication of these fine gentleman, Status would not exist. + +## Contact us + +Feel free to email us at [support@status.im](mailto:support@status.im) or better yet, [join our Slack](http://slack.status.im). + +Got a feature request or suggestion, download Status and shake your phone, failing that search our [Github Issues](http://github.com/status-im/status-react/issues). ## License -Copyright © 2016 FIXME - -Distributed under the Eclipse Public License either version 1.0 or (at -your option) any later version. +Licensed under the [Mozilla Public License v2.0](https://github.com/status-im/status-react/blob/develop/LICENSE.md) diff --git a/android/app/BUCK b/android/app/BUCK new file mode 100644 index 0000000000..7e80564a8b --- /dev/null +++ b/android/app/BUCK @@ -0,0 +1,66 @@ +import re + +# To learn about Buck see [Docs](https://buckbuild.com/). +# To run your application with Buck: +# - install Buck +# - `npm start` - to start the packager +# - `cd android` +# - `cp ~/.android/debug.keystore keystores/debug.keystore` +# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck +# - `buck install -r android/app` - compile, install and run application +# + +lib_deps = [] +for jarfile in glob(['libs/*.jar']): + name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) + lib_deps.append(':' + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) + +for aarfile in glob(['libs/*.aar']): + name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) + lib_deps.append(':' + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +android_library( + name = 'all-libs', + exported_deps = lib_deps +) + +android_library( + name = 'app-code', + srcs = glob([ + 'src/main/java/**/*.java', + ]), + deps = [ + ':all-libs', + ':build_config', + ':res', + ], +) + +android_build_config( + name = 'build_config', + package = 'im.status.ethereum', +) + +android_resource( + name = 'res', + res = 'src/main/res', + package = 'im.status.ethereum', +) + +android_binary( + name = 'app', + package_type = 'debug', + manifest = 'src/main/AndroidManifest.xml', + keystore = '//android/keystores:debug', + deps = [ + ':app-code', + ], +) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9f152c2e9b..e565b5ffac 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -9,7 +9,7 @@ import com.android.build.OutputFile * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the - * `apply from: "react.gradle"` line. + * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle @@ -26,7 +26,9 @@ import com.android.build.OutputFile * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants - * // The configuration property is in the format 'bundleIn${productFlavor}${buildType}' + * // The configuration property can be in the following formats + * // 'bundleIn${productFlavor}${buildType}' + * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, @@ -56,12 +58,14 @@ import com.android.build.OutputFile * inputExcludes: ["android/**", "ios/**"] * ] */ +project.ext.react = [ + nodeExecutableAndArgs: ["node", "--max-old-space-size=4096"] +] -apply from: "react.gradle" +apply from: "../../node_modules/react-native/react.gradle" /** - * Set this to true to create three separate APKs instead of one: - * - A universal APK that works on all devices + * Set this to true to create two separate APKs instead of one: * - An APK that only works on ARM devices * - An APK that only works on x86 devices * The advantage is the size of the APK is reduced by about 4MB. @@ -75,32 +79,67 @@ def enableSeparateBuildPerCPUArchitecture = false */ def enableProguardInReleaseBuilds = false +def getVersionCode = { -> + new ByteArrayOutputStream().withStream { stdOut -> + exec { + commandLine "git", "rev-list", "--count", "HEAD" + standardOutput = stdOut + } + return stdOut.toString().toInteger() + } +} + +def getVersionName = { -> + new ByteArrayOutputStream().withStream { stdOut -> + exec { + commandLine "git", "describe", "--tags", "--always", "--dirty=+" + standardOutput = stdOut + } + return stdOut.toString().replaceAll("\\s","") + } +} + android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { - applicationId "com.syngim" - minSdkVersion 16 + applicationId "im.status.ethereum" + minSdkVersion 18 targetSdkVersion 22 - versionCode 1 - versionName "1.0" + versionCode getVersionCode() + versionName getVersionName() ndk { abiFilters "armeabi-v7a", "x86" } } + dexOptions { + jumboMode true + } + signingConfigs { + release { + storeFile file(STATUS_RELEASE_STORE_FILE) + storePassword STATUS_RELEASE_STORE_PASSWORD + keyAlias STATUS_RELEASE_KEY_ALIAS + keyPassword STATUS_RELEASE_KEY_PASSWORD + } + } splits { abi { - enable enableSeparateBuildPerCPUArchitecture - universalApk false reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK include "armeabi-v7a", "x86" } } buildTypes { + debug { + versionNameSuffix "-SNAPSHOT" + } release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + signingConfig signingConfigs.release } } // applicationVariants are e.g. debug, release @@ -119,7 +158,12 @@ android { } dependencies { - compile project(':randombytes') + compile project(':instabug-reactnative') + compile project(':react-native-splash-screen') + compile project(':react-native-image-resizer') + compile project(':react-native-dialogs') + compile project(':react-native-randombytes') + compile project(':react-native-android-sms-listener') compile project(':realm') compile project(':react-native-vector-icons') compile fileTree(dir: "libs", include: ["*.jar"]) @@ -127,8 +171,21 @@ dependencies { compile "com.facebook.react:react-native:+" // From node_modules compile project(':react-native-contacts') compile project(':react-native-i18n') -// compile(name:'geth', ext:'aar') - compile(group: 'syng-im', name: 'android-geth', version: '1.4.0-201603131817-92d65cf', ext: 'aar') + compile project(':react-native-linear-gradient') + compile project(':react-native-camera') + compile project(':react-native-status') + compile project(':react-native-orientation') + compile project(':react-native-fs') + compile project(':react-native-image-crop-picker') + compile project(':react-native-webview-bridge') + compile project(':react-native-share') compile fileTree(dir: "node_modules/realm/android/libs", include: ["*.jar"]) } +// Run this once to be able to run the application with BUCK +// puts all compile dependencies into folder libs for BUCK to use +task copyDownloadableDepsToLibs(type: Copy) { + from configurations.compile + into 'libs' +} + diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 7d72e46927..48361a9015 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -26,11 +26,14 @@ # See http://sourceforge.net/p/proguard/bugs/466/ -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters +-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip # Do not strip any method/class that is annotated with @DoNotStrip -keep @com.facebook.proguard.annotations.DoNotStrip class * +-keep @com.facebook.common.internal.DoNotStrip class * -keepclassmembers class * { @com.facebook.proguard.annotations.DoNotStrip *; + @com.facebook.common.internal.DoNotStrip *; } -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { @@ -51,9 +54,9 @@ -keepattributes Signature -keepattributes *Annotation* --keep class com.squareup.okhttp.** { *; } --keep interface com.squareup.okhttp.** { *; } --dontwarn com.squareup.okhttp.** +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** # okio @@ -61,7 +64,3 @@ -dontwarn java.nio.file.* -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn okio.** - -# stetho - --dontwarn com.facebook.stetho.** diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c56b2f64b8..187790fd58 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,25 +1,35 @@ + package="im.status.ethereum"> - - - + + + + + + - - - - - - - + android:allowBackup="true" + android:label="@string/app_name" + android:icon="@mipmap/ic_launcher" + android:theme="@style/AppTheme" + android:name=".MainApplication"> + + + + + + + + diff --git a/android/app/src/main/assets/fonts/FontAwesome.ttf b/android/app/src/main/assets/fonts/FontAwesome.ttf index 26dea7951a..f221e50a2e 100644 Binary files a/android/app/src/main/assets/fonts/FontAwesome.ttf and b/android/app/src/main/assets/fonts/FontAwesome.ttf differ diff --git a/android/app/src/main/assets/fonts/Ionicons.ttf b/android/app/src/main/assets/fonts/Ionicons.ttf index c4e4632486..307ad889aa 100644 Binary files a/android/app/src/main/assets/fonts/Ionicons.ttf and b/android/app/src/main/assets/fonts/Ionicons.ttf differ diff --git a/android/app/src/main/java/com/syngim/MainActivity.java b/android/app/src/main/java/com/syngim/MainActivity.java deleted file mode 100644 index b080070f49..0000000000 --- a/android/app/src/main/java/com/syngim/MainActivity.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.syngim; - -import com.facebook.react.ReactActivity; -import io.realm.react.RealmReactPackage; -import com.oblador.vectoricons.VectorIconsPackage; -import com.facebook.react.ReactPackage; -import com.facebook.react.shell.MainReactPackage; -import com.rt2zz.reactnativecontacts.ReactNativeContacts; -import android.os.Bundle; -import android.os.Environment; -import com.github.ethereum.go_ethereum.cmd.Geth; -import com.bitgo.randombytes.RandomBytesPackage; - -import java.util.Arrays; -import java.util.List; -import java.util.Properties; -import java.io.File; - -import com.i18n.reactnativei18n.ReactNativeI18n; -import io.realm.react.RealmReactPackage; - -public class MainActivity extends ReactActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Required for android-16 (???) - System.loadLibrary("gethraw"); - System.loadLibrary("geth"); - - // Required because of crazy APN settings redirecting localhost - Properties properties = System.getProperties(); - properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1"); - properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1"); - - File extStore = Environment.getExternalStorageDirectory(); - - final String dataFolder = extStore.exists() ? - extStore.getAbsolutePath() : - getApplicationInfo().dataDir; - - // Launch! - new Thread(new Runnable() { - public void run() { - Geth.run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh,admin --fast --datadir=" + dataFolder); - } - }).start(); - } - - /** - * Returns the name of the main component registered from JavaScript. - * This is used to schedule rendering of the component. - */ - @Override - protected String getMainComponentName() { - return "SyngIm"; - } - - /** - * Returns whether dev mode should be enabled. - * This enables e.g. the dev menu. - */ - @Override - protected boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - - /** - * A list of packages used by the app. If the app uses additional views - * or modules besides the default ones, add more packages here. - */ - @Override - protected List getPackages() { - return Arrays.asList( - new MainReactPackage(), - new RealmReactPackage(), - new VectorIconsPackage(), - new ReactNativeContacts(), - new ReactNativeI18n(), - new RandomBytesPackage() - ); - } -} diff --git a/android/app/src/main/java/im/status/ethereum/MainActivity.java b/android/app/src/main/java/im/status/ethereum/MainActivity.java new file mode 100644 index 0000000000..95f124e981 --- /dev/null +++ b/android/app/src/main/java/im/status/ethereum/MainActivity.java @@ -0,0 +1,83 @@ +package im.status.ethereum; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import com.facebook.react.ReactActivity; +import com.cboy.rn.splashscreen.SplashScreen; + +import java.util.Properties; + +public class MainActivity extends ReactActivity { + private static final String TAG = "MainActivity"; + + protected void configureStatus() { + // Required because of crazy APN settings redirecting localhost (found in GB) + Properties properties = System.getProperties(); + properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1"); + properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + SplashScreen.show(this); + super.onCreate(savedInstanceState); + + if (!RootUtil.isDeviceRooted()) { + configureStatus(); + } else { + AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) + .setMessage(getResources().getString(R.string.root_warning)) + .setPositiveButton(getResources().getString(R.string.root_okay), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + configureStatus(); + } + }) + .setNegativeButton(getResources().getString(R.string.root_cancel), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + MainActivity.this.finishAffinity(); + } + }) + .setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + dialog.dismiss(); + MainActivity.this.finishAffinity(); + } + }) + .create(); + + dialog.show(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "StatusIm"; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Intent intent = new Intent("onConfigurationChanged"); + intent.putExtra("newConfig", newConfig); + this.sendBroadcast(intent); + } +} diff --git a/android/app/src/main/java/im/status/ethereum/MainApplication.java b/android/app/src/main/java/im/status/ethereum/MainApplication.java new file mode 100644 index 0000000000..e4f8f7acbd --- /dev/null +++ b/android/app/src/main/java/im/status/ethereum/MainApplication.java @@ -0,0 +1,70 @@ +package im.status.ethereum; + +import android.app.Application; + +import com.facebook.react.ReactApplication; +import com.instabug.reactlibrary.RNInstabugReactnativePackage; +import com.cboy.rn.splashscreen.SplashScreenReactPackage; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; + +import im.status.ethereum.module.StatusPackage; +import io.realm.react.RealmReactPackage; +import com.oblador.vectoricons.VectorIconsPackage; +import com.rt2zz.reactnativecontacts.ReactNativeContacts; +import com.i18n.reactnativei18n.ReactNativeI18n; +import com.bitgo.randombytes.RandomBytesPackage; +import com.BV.LinearGradient.LinearGradientPackage; +import com.lwansbrough.RCTCamera.*; +import com.centaurwarchief.smslistener.SmsListenerPackage; +import com.github.yamill.orientation.OrientationPackage; +import com.rnfs.RNFSPackage; +import com.aakashns.reactnativedialogs.ReactNativeDialogsPackage; +import fr.bamlab.rnimageresizer.ImageResizerPackage; +import com.reactnative.ivpusic.imagepicker.PickerPackage; +import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage; +import cl.json.RNSharePackage; + +import java.util.Arrays; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + protected boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new RNInstabugReactnativePackage("b239f82a9cb00464e4c72cc703e6821e",MainApplication.this,"shake"), + new SplashScreenReactPackage(), + new StatusPackage(), + new RealmReactPackage(), + new VectorIconsPackage(), + new ReactNativeContacts(), + new ReactNativeI18n(), + new RandomBytesPackage(), + new LinearGradientPackage(), + new RCTCameraPackage(), + new SmsListenerPackage(), + new OrientationPackage(), + new RNFSPackage(), + new ReactNativeDialogsPackage(), + new ImageResizerPackage(), + new PickerPackage(), + new WebViewBridgePackage(), + new RNSharePackage() + ); + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } +} diff --git a/android/app/src/main/java/im/status/ethereum/RootUtil.java b/android/app/src/main/java/im/status/ethereum/RootUtil.java new file mode 100644 index 0000000000..5c74f566df --- /dev/null +++ b/android/app/src/main/java/im/status/ethereum/RootUtil.java @@ -0,0 +1,40 @@ +package im.status.ethereum; + +import java.io.File; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** @author Kevin Kowalewski */ +public class RootUtil { + + public static boolean isDeviceRooted() { + return checkRootMethod1() || checkRootMethod2() || checkRootMethod3(); + } + + private static boolean checkRootMethod1() { + String buildTags = android.os.Build.TAGS; + return buildTags != null && buildTags.contains("test-keys"); + } + + private static boolean checkRootMethod2() { + String[] paths = { "/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", + "/system/bin/failsafe/su", "/data/local/su" }; + for (String path : paths) { + if (new File(path).exists()) return true; + } + return false; + } + + private static boolean checkRootMethod3() { + Process process = null; + try { + process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" }); + BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); + return in.readLine() != null; + } catch (Throwable t) { + return false; + } finally { + if (process != null) process.destroy(); + } + } +} diff --git a/android/app/src/main/res/drawable-hdpi/avatar.png b/android/app/src/main/res/drawable-hdpi/avatar.png new file mode 100644 index 0000000000..b1c2c64e3d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/avatar.png differ diff --git a/android/app/src/main/res/drawable-hdpi/console.png b/android/app/src/main/res/drawable-hdpi/console.png new file mode 100644 index 0000000000..4dc664c678 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/console.png differ diff --git a/android/app/src/main/res/drawable-hdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-hdpi/corner_left_bottom.png new file mode 100644 index 0000000000..1c9c9b446d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_left_bottom.png differ diff --git a/android/app/src/main/res/drawable-hdpi/corner_left_top.png b/android/app/src/main/res/drawable-hdpi/corner_left_top.png new file mode 100644 index 0000000000..1a0a4e57f8 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_left_top.png differ diff --git a/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png new file mode 100644 index 0000000000..e6071d1ae1 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_right_bottom.png differ diff --git a/android/app/src/main/res/drawable-hdpi/corner_right_top.png b/android/app/src/main/res/drawable-hdpi/corner_right_top.png new file mode 100644 index 0000000000..6eaca3cd9a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/corner_right_top.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_add.png b/android/app/src/main/res/drawable-hdpi/icon_add.png new file mode 100755 index 0000000000..4e3f4cc71d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_add.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_add_gray.png b/android/app/src/main/res/drawable-hdpi/icon_add_gray.png new file mode 100644 index 0000000000..20ba082172 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_add_gray.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_add_white.png b/android/app/src/main/res/drawable-hdpi/icon_add_white.png new file mode 100644 index 0000000000..60ab6e1281 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_add_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_avatar.png b/android/app/src/main/res/drawable-hdpi/icon_avatar.png new file mode 100644 index 0000000000..d28594fcf1 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_avatar.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_back.png b/android/app/src/main/res/drawable-hdpi/icon_back.png new file mode 100644 index 0000000000..ce2e4b5734 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_back.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_back_white.png b/android/app/src/main/res/drawable-hdpi/icon_back_white.png new file mode 100644 index 0000000000..6bd5aec7c4 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_back_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_bin.png b/android/app/src/main/res/drawable-hdpi/icon_bin.png new file mode 100644 index 0000000000..a968627384 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_bin.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_close_gray.png b/android/app/src/main/res/drawable-hdpi/icon_close_gray.png new file mode 100644 index 0000000000..4342d2e4f0 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_close_gray.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_close_small_gray.png b/android/app/src/main/res/drawable-hdpi/icon_close_small_gray.png new file mode 100755 index 0000000000..2fb7bd1b7f Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_close_small_gray.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_close_white.png b/android/app/src/main/res/drawable-hdpi/icon_close_white.png new file mode 100644 index 0000000000..79f55e700a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_close_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_dollar.png b/android/app/src/main/res/drawable-hdpi/icon_dollar.png new file mode 100644 index 0000000000..c2a449b532 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_dollar.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png new file mode 100644 index 0000000000..32cf51e421 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_dollar_green.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_dots.png b/android/app/src/main/res/drawable-hdpi/icon_dots.png new file mode 100644 index 0000000000..7f9961ec1b Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_dots.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_drag_down.png b/android/app/src/main/res/drawable-hdpi/icon_drag_down.png new file mode 100644 index 0000000000..7d0dfec989 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_drag_down.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_drag_white.png b/android/app/src/main/res/drawable-hdpi/icon_drag_white.png new file mode 100644 index 0000000000..6a079b6f6f Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_drag_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_enter_address.png b/android/app/src/main/res/drawable-hdpi/icon_enter_address.png new file mode 100755 index 0000000000..776aadf1ed Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_enter_address.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_group.png b/android/app/src/main/res/drawable-hdpi/icon_group.png new file mode 100644 index 0000000000..ba7d13d4aa Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_group.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_group_big.png b/android/app/src/main/res/drawable-hdpi/icon_group_big.png new file mode 100644 index 0000000000..68687a6dfa Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_hamburger.png b/android/app/src/main/res/drawable-hdpi/icon_hamburger.png new file mode 100644 index 0000000000..33beaca124 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_hamburger.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_input_list.png b/android/app/src/main/res/drawable-hdpi/icon_input_list.png new file mode 100644 index 0000000000..033344a21f Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_input_list.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_list.png b/android/app/src/main/res/drawable-hdpi/icon_list.png new file mode 100644 index 0000000000..d0d15ed91a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_list.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_location.png b/android/app/src/main/res/drawable-hdpi/icon_location.png new file mode 100755 index 0000000000..e0f69de116 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_location.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_lock_gray.png b/android/app/src/main/res/drawable-hdpi/icon_lock_gray.png new file mode 100644 index 0000000000..5e7c958046 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_lock_gray.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_lock_white.png b/android/app/src/main/res/drawable-hdpi/icon_lock_white.png new file mode 100644 index 0000000000..700c8fdf81 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_lock_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_menu_group.png b/android/app/src/main/res/drawable-hdpi/icon_menu_group.png new file mode 100644 index 0000000000..40e9b7dc41 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_money_white.png b/android/app/src/main/res/drawable-hdpi/icon_money_white.png new file mode 100755 index 0000000000..6510ff9c04 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_money_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_more_vertical.png b/android/app/src/main/res/drawable-hdpi/icon_more_vertical.png new file mode 100644 index 0000000000..b0948a2a7e Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_more_vertical.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_more_vertical_blue.png b/android/app/src/main/res/drawable-hdpi/icon_more_vertical_blue.png new file mode 100644 index 0000000000..f8bc4ec674 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_more_vertical_blue.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_muted.png b/android/app/src/main/res/drawable-hdpi/icon_muted.png new file mode 100644 index 0000000000..2c862042ba Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_notifications_on.png b/android/app/src/main/res/drawable-hdpi/icon_notifications_on.png new file mode 100644 index 0000000000..207cb4c16f Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_notifications_on.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok.png b/android/app/src/main/res/drawable-hdpi/icon_ok.png new file mode 100644 index 0000000000..d9bb8a1cbf Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_blue.png b/android/app/src/main/res/drawable-hdpi/icon_ok_blue.png new file mode 100644 index 0000000000..66e62ce1a3 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_blue.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_disabled.png b/android/app/src/main/res/drawable-hdpi/icon_ok_disabled.png new file mode 100644 index 0000000000..dc6068da44 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_disabled.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000..5235f7f078 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_disabled_inversed.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_purple.png b/android/app/src/main/res/drawable-hdpi/icon_ok_purple.png new file mode 100644 index 0000000000..b28a5ca6d0 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_purple.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_small.png b/android/app/src/main/res/drawable-hdpi/icon_ok_small.png new file mode 100644 index 0000000000..6475aebf6a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_small.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_ok_small_copy_2.png b/android/app/src/main/res/drawable-hdpi/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..6475aebf6a Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_ok_small_copy_2.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_phone_white.png b/android/app/src/main/res/drawable-hdpi/icon_phone_white.png new file mode 100755 index 0000000000..d60a9ac7a2 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_phone_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_plus.png b/android/app/src/main/res/drawable-hdpi/icon_plus.png new file mode 100644 index 0000000000..19e2a45b57 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_plus.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_qr.png b/android/app/src/main/res/drawable-hdpi/icon_qr.png new file mode 100644 index 0000000000..6b2f825374 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_qr.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_qr_gray.png b/android/app/src/main/res/drawable-hdpi/icon_qr_gray.png new file mode 100644 index 0000000000..ce7a826d0d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_qr_gray.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_scan_q_r.png b/android/app/src/main/res/drawable-hdpi/icon_scan_q_r.png new file mode 100755 index 0000000000..df2c269ee6 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_scan_q_r.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_scan_white.png b/android/app/src/main/res/drawable-hdpi/icon_scan_white.png new file mode 100644 index 0000000000..a9496cb185 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_scan_white.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_search.png b/android/app/src/main/res/drawable-hdpi/icon_search.png new file mode 100644 index 0000000000..d6fe0b2cc1 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_search.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..fe1fd1299c Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_send.png b/android/app/src/main/res/drawable-hdpi/icon_send.png new file mode 100644 index 0000000000..af524d1b38 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_send.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_settings.png b/android/app/src/main/res/drawable-hdpi/icon_settings.png new file mode 100644 index 0000000000..ab60673483 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_smile.png b/android/app/src/main/res/drawable-hdpi/icon_smile.png new file mode 100644 index 0000000000..2c5ca1da03 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_smile.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_tab_chats.png b/android/app/src/main/res/drawable-hdpi/icon_tab_chats.png new file mode 100644 index 0000000000..cd49ce4003 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_tab_chats.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_tab_contacts.png b/android/app/src/main/res/drawable-hdpi/icon_tab_contacts.png new file mode 100644 index 0000000000..b7f48eff4c Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_tab_contacts.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_tab_discover.png b/android/app/src/main/res/drawable-hdpi/icon_tab_discover.png new file mode 100644 index 0000000000..1166cab6f6 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_tab_discover.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_up.png b/android/app/src/main/res/drawable-hdpi/icon_up.png new file mode 100644 index 0000000000..421b5f459d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_wallet_avatar.png b/android/app/src/main/res/drawable-hdpi/icon_wallet_avatar.png new file mode 100755 index 0000000000..0beb98c8ca Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_wallet_avatar.png differ diff --git a/android/app/src/main/res/drawable-hdpi/scan_blue.png b/android/app/src/main/res/drawable-hdpi/scan_blue.png new file mode 100644 index 0000000000..7ea6117534 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/scan_blue.png differ diff --git a/android/app/src/main/res/drawable-mdpi/avatar.png b/android/app/src/main/res/drawable-mdpi/avatar.png new file mode 100644 index 0000000000..f208650ddb Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/avatar.png differ diff --git a/android/app/src/main/res/drawable-mdpi/console.png b/android/app/src/main/res/drawable-mdpi/console.png new file mode 100644 index 0000000000..2ef4eeac8f Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/console.png differ diff --git a/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png new file mode 100644 index 0000000000..da09ea7a24 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_left_bottom.png differ diff --git a/android/app/src/main/res/drawable-mdpi/corner_left_top.png b/android/app/src/main/res/drawable-mdpi/corner_left_top.png new file mode 100644 index 0000000000..988d93651c Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_left_top.png differ diff --git a/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png new file mode 100644 index 0000000000..e74544e83b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_right_bottom.png differ diff --git a/android/app/src/main/res/drawable-mdpi/corner_right_top.png b/android/app/src/main/res/drawable-mdpi/corner_right_top.png new file mode 100644 index 0000000000..33726dfa01 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/corner_right_top.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_add.png b/android/app/src/main/res/drawable-mdpi/icon_add.png new file mode 100644 index 0000000000..8593bd6d04 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_add.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_add_gray.png b/android/app/src/main/res/drawable-mdpi/icon_add_gray.png new file mode 100644 index 0000000000..8ddec19c24 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_add_gray.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_avatar.png b/android/app/src/main/res/drawable-mdpi/icon_avatar.png new file mode 100644 index 0000000000..d76df40ebc Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_avatar.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_back.png b/android/app/src/main/res/drawable-mdpi/icon_back.png new file mode 100644 index 0000000000..70e85f3fc4 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_back.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_back_white.png b/android/app/src/main/res/drawable-mdpi/icon_back_white.png new file mode 100644 index 0000000000..ac448ee920 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_back_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_bin.png b/android/app/src/main/res/drawable-mdpi/icon_bin.png new file mode 100644 index 0000000000..d26fff597d Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_bin.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_close_gray.png b/android/app/src/main/res/drawable-mdpi/icon_close_gray.png new file mode 100644 index 0000000000..c0cca73335 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_close_gray.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_close_small_gray.png b/android/app/src/main/res/drawable-mdpi/icon_close_small_gray.png new file mode 100755 index 0000000000..a53e5eab90 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_close_small_gray.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_close_white.png b/android/app/src/main/res/drawable-mdpi/icon_close_white.png new file mode 100644 index 0000000000..956a2c04fb Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_close_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_dollar.png b/android/app/src/main/res/drawable-mdpi/icon_dollar.png new file mode 100644 index 0000000000..be7735955e Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_dollar.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png new file mode 100644 index 0000000000..fc6f77f756 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_dollar_green.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_dots.png b/android/app/src/main/res/drawable-mdpi/icon_dots.png new file mode 100644 index 0000000000..a5a003f459 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_dots.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_drag_down.png b/android/app/src/main/res/drawable-mdpi/icon_drag_down.png new file mode 100644 index 0000000000..88182afaf3 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_drag_down.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_drag_white.png b/android/app/src/main/res/drawable-mdpi/icon_drag_white.png new file mode 100644 index 0000000000..7845600020 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_drag_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_enter_address.png b/android/app/src/main/res/drawable-mdpi/icon_enter_address.png new file mode 100755 index 0000000000..37fb533bfa Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_enter_address.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_group.png b/android/app/src/main/res/drawable-mdpi/icon_group.png new file mode 100644 index 0000000000..373056e72b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_group.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_group_big.png b/android/app/src/main/res/drawable-mdpi/icon_group_big.png new file mode 100644 index 0000000000..c4bf8a7989 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_hamburger.png b/android/app/src/main/res/drawable-mdpi/icon_hamburger.png new file mode 100644 index 0000000000..c3c67a10df Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_hamburger.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_input_list.png b/android/app/src/main/res/drawable-mdpi/icon_input_list.png new file mode 100644 index 0000000000..df86c2c636 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_input_list.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_list.png b/android/app/src/main/res/drawable-mdpi/icon_list.png new file mode 100644 index 0000000000..37d20bf9c7 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_list.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_location.png b/android/app/src/main/res/drawable-mdpi/icon_location.png new file mode 100755 index 0000000000..c063b68d31 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_location.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_lock_gray.png b/android/app/src/main/res/drawable-mdpi/icon_lock_gray.png new file mode 100644 index 0000000000..dce5f8dff8 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_lock_gray.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_lock_white.png b/android/app/src/main/res/drawable-mdpi/icon_lock_white.png new file mode 100644 index 0000000000..7da52c18af Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_lock_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_menu_group.png b/android/app/src/main/res/drawable-mdpi/icon_menu_group.png new file mode 100644 index 0000000000..f17ab21946 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_money_white.png b/android/app/src/main/res/drawable-mdpi/icon_money_white.png new file mode 100755 index 0000000000..1584a09d0a Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_money_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_more_vertical.png b/android/app/src/main/res/drawable-mdpi/icon_more_vertical.png new file mode 100644 index 0000000000..8146a3ffeb Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_more_vertical.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_more_vertical_blue.png b/android/app/src/main/res/drawable-mdpi/icon_more_vertical_blue.png new file mode 100644 index 0000000000..acbe47357d Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_more_vertical_blue.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_muted.png b/android/app/src/main/res/drawable-mdpi/icon_muted.png new file mode 100644 index 0000000000..21c7dc3962 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_notifications_on.png b/android/app/src/main/res/drawable-mdpi/icon_notifications_on.png new file mode 100644 index 0000000000..89a9ca8f15 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_notifications_on.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok.png b/android/app/src/main/res/drawable-mdpi/icon_ok.png new file mode 100644 index 0000000000..eb19a3fea5 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_blue.png b/android/app/src/main/res/drawable-mdpi/icon_ok_blue.png new file mode 100644 index 0000000000..8b6c5482cf Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_blue.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_disabled.png b/android/app/src/main/res/drawable-mdpi/icon_ok_disabled.png new file mode 100644 index 0000000000..62a6bdebf4 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_disabled.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000..6052db9847 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_disabled_inversed.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_purple.png b/android/app/src/main/res/drawable-mdpi/icon_ok_purple.png new file mode 100644 index 0000000000..b6c7a413fd Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_purple.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_small.png b/android/app/src/main/res/drawable-mdpi/icon_ok_small.png new file mode 100644 index 0000000000..86047df5f3 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_small.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_ok_small_copy_2.png b/android/app/src/main/res/drawable-mdpi/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..86047df5f3 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_ok_small_copy_2.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_phone_white.png b/android/app/src/main/res/drawable-mdpi/icon_phone_white.png new file mode 100755 index 0000000000..224f48c50d Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_phone_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_plus.png b/android/app/src/main/res/drawable-mdpi/icon_plus.png new file mode 100644 index 0000000000..a26e891239 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_plus.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_qr.png b/android/app/src/main/res/drawable-mdpi/icon_qr.png new file mode 100644 index 0000000000..cde09052fb Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_qr.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_qr_gray.png b/android/app/src/main/res/drawable-mdpi/icon_qr_gray.png new file mode 100644 index 0000000000..cb73ad1e04 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_qr_gray.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_scan_q_r.png b/android/app/src/main/res/drawable-mdpi/icon_scan_q_r.png new file mode 100755 index 0000000000..906e8f7d08 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_scan_q_r.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_scan_white.png b/android/app/src/main/res/drawable-mdpi/icon_scan_white.png new file mode 100644 index 0000000000..e1fe0fe57b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_scan_white.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_search.png b/android/app/src/main/res/drawable-mdpi/icon_search.png new file mode 100644 index 0000000000..7ea3e3bf36 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_search.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..61041d5c3b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_send.png b/android/app/src/main/res/drawable-mdpi/icon_send.png new file mode 100644 index 0000000000..45eda4e76e Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_send.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_settings.png b/android/app/src/main/res/drawable-mdpi/icon_settings.png new file mode 100644 index 0000000000..f01c784c4c Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_smile.png b/android/app/src/main/res/drawable-mdpi/icon_smile.png new file mode 100644 index 0000000000..07d91c4588 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_smile.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_tab_chats.png b/android/app/src/main/res/drawable-mdpi/icon_tab_chats.png new file mode 100644 index 0000000000..6bb1a35f92 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_tab_chats.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_tab_contacts.png b/android/app/src/main/res/drawable-mdpi/icon_tab_contacts.png new file mode 100644 index 0000000000..277368a9b8 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_tab_contacts.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_tab_discover.png b/android/app/src/main/res/drawable-mdpi/icon_tab_discover.png new file mode 100644 index 0000000000..6b726371f7 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_tab_discover.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_up.png b/android/app/src/main/res/drawable-mdpi/icon_up.png new file mode 100644 index 0000000000..a2db655bd6 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_wallet_avatar.png b/android/app/src/main/res/drawable-mdpi/icon_wallet_avatar.png new file mode 100755 index 0000000000..629bb81604 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_wallet_avatar.png differ diff --git a/android/app/src/main/res/drawable-mdpi/scan_blue.png b/android/app/src/main/res/drawable-mdpi/scan_blue.png new file mode 100644 index 0000000000..f154c2ee43 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/scan_blue.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/avatar.png b/android/app/src/main/res/drawable-xhdpi/avatar.png new file mode 100644 index 0000000000..8cc91adff5 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/avatar.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/console.png b/android/app/src/main/res/drawable-xhdpi/console.png new file mode 100644 index 0000000000..70d539f951 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/console.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png new file mode 100644 index 0000000000..b43f25a58d Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_left_bottom.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xhdpi/corner_left_top.png new file mode 100644 index 0000000000..f8180a9c1c Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_left_top.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png new file mode 100644 index 0000000000..ce0a7ab9c0 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_right_bottom.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xhdpi/corner_right_top.png new file mode 100644 index 0000000000..2ae2e61860 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/corner_right_top.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_add.png b/android/app/src/main/res/drawable-xhdpi/icon_add.png new file mode 100755 index 0000000000..88136956c2 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_add.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_add_gray.png b/android/app/src/main/res/drawable-xhdpi/icon_add_gray.png new file mode 100644 index 0000000000..a05f10fa16 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_add_gray.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_add_white.png b/android/app/src/main/res/drawable-xhdpi/icon_add_white.png new file mode 100644 index 0000000000..be85af38f4 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_add_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_avatar.png b/android/app/src/main/res/drawable-xhdpi/icon_avatar.png new file mode 100644 index 0000000000..63e1078f8e Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_avatar.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_back.png b/android/app/src/main/res/drawable-xhdpi/icon_back.png new file mode 100644 index 0000000000..07072c52c1 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_back.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xhdpi/icon_back_white.png new file mode 100644 index 0000000000..bcc735db8e Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_back_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_bin.png b/android/app/src/main/res/drawable-xhdpi/icon_bin.png new file mode 100644 index 0000000000..beed3a3490 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_bin.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_close_gray.png b/android/app/src/main/res/drawable-xhdpi/icon_close_gray.png new file mode 100644 index 0000000000..1eea738abe Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_close_gray.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_close_small_gray.png b/android/app/src/main/res/drawable-xhdpi/icon_close_small_gray.png new file mode 100755 index 0000000000..3b641a8b92 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_close_small_gray.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xhdpi/icon_close_white.png new file mode 100644 index 0000000000..5c8005f904 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_close_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_dollar.png b/android/app/src/main/res/drawable-xhdpi/icon_dollar.png new file mode 100644 index 0000000000..615f013c40 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_dollar.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png new file mode 100644 index 0000000000..cdea0d4af4 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_dollar_green.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_dots.png b/android/app/src/main/res/drawable-xhdpi/icon_dots.png new file mode 100644 index 0000000000..e4d3d40921 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_dots.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_drag_down.png b/android/app/src/main/res/drawable-xhdpi/icon_drag_down.png new file mode 100644 index 0000000000..ca0f2b6f4e Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_drag_down.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png new file mode 100644 index 0000000000..2b200673bb Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_drag_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_enter_address.png b/android/app/src/main/res/drawable-xhdpi/icon_enter_address.png new file mode 100755 index 0000000000..9f8f14f509 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_enter_address.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_group.png b/android/app/src/main/res/drawable-xhdpi/icon_group.png new file mode 100644 index 0000000000..8576e6f1e2 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_group.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xhdpi/icon_group_big.png new file mode 100644 index 0000000000..8b47926213 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_hamburger.png b/android/app/src/main/res/drawable-xhdpi/icon_hamburger.png new file mode 100644 index 0000000000..1f85e4ca7f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_hamburger.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_input_list.png b/android/app/src/main/res/drawable-xhdpi/icon_input_list.png new file mode 100644 index 0000000000..45c40f49c8 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_input_list.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_list.png b/android/app/src/main/res/drawable-xhdpi/icon_list.png new file mode 100644 index 0000000000..d50123e3c6 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_list.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_location.png b/android/app/src/main/res/drawable-xhdpi/icon_location.png new file mode 100755 index 0000000000..0e43aa738e Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_location.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_lock_gray.png b/android/app/src/main/res/drawable-xhdpi/icon_lock_gray.png new file mode 100644 index 0000000000..40369d5424 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_lock_gray.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_lock_white.png b/android/app/src/main/res/drawable-xhdpi/icon_lock_white.png new file mode 100644 index 0000000000..827cd72242 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_lock_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png new file mode 100644 index 0000000000..28c99ec40f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_money_white.png b/android/app/src/main/res/drawable-xhdpi/icon_money_white.png new file mode 100755 index 0000000000..da077578fa Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_money_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_more_vertical.png b/android/app/src/main/res/drawable-xhdpi/icon_more_vertical.png new file mode 100644 index 0000000000..bba25f4642 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_more_vertical.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_more_vertical_blue.png b/android/app/src/main/res/drawable-xhdpi/icon_more_vertical_blue.png new file mode 100644 index 0000000000..d58d42bf3c Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_more_vertical_blue.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_muted.png b/android/app/src/main/res/drawable-xhdpi/icon_muted.png new file mode 100644 index 0000000000..4195619670 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_notifications_on.png b/android/app/src/main/res/drawable-xhdpi/icon_notifications_on.png new file mode 100644 index 0000000000..9322a10bec Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_notifications_on.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok.png b/android/app/src/main/res/drawable-xhdpi/icon_ok.png new file mode 100644 index 0000000000..80888a7606 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_blue.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_blue.png new file mode 100644 index 0000000000..df4ddc7541 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_blue.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled.png new file mode 100644 index 0000000000..07c323e77e Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000..5d215f58a4 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_disabled_inversed.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_purple.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_purple.png new file mode 100644 index 0000000000..3357120bb0 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_purple.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_small.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_small.png new file mode 100644 index 0000000000..a8bc5e7185 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_small.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_ok_small_copy_2.png b/android/app/src/main/res/drawable-xhdpi/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..a8bc5e7185 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_ok_small_copy_2.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_phone_white.png b/android/app/src/main/res/drawable-xhdpi/icon_phone_white.png new file mode 100755 index 0000000000..f08867e855 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_phone_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_plus.png b/android/app/src/main/res/drawable-xhdpi/icon_plus.png new file mode 100644 index 0000000000..00f6f9cdb8 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_plus.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_qr.png b/android/app/src/main/res/drawable-xhdpi/icon_qr.png new file mode 100644 index 0000000000..c55551f687 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_qr.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_qr_gray.png b/android/app/src/main/res/drawable-xhdpi/icon_qr_gray.png new file mode 100644 index 0000000000..71c6fd567d Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_qr_gray.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_scan_q_r.png b/android/app/src/main/res/drawable-xhdpi/icon_scan_q_r.png new file mode 100755 index 0000000000..e914b927df Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_scan_q_r.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_scan_white.png b/android/app/src/main/res/drawable-xhdpi/icon_scan_white.png new file mode 100644 index 0000000000..ad6ad72070 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_scan_white.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_search.png b/android/app/src/main/res/drawable-xhdpi/icon_search.png new file mode 100644 index 0000000000..8be3300e49 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_search.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..f0a3045947 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_send.png b/android/app/src/main/res/drawable-xhdpi/icon_send.png new file mode 100644 index 0000000000..f8e71b580f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_send.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_settings.png b/android/app/src/main/res/drawable-xhdpi/icon_settings.png new file mode 100644 index 0000000000..72aaee326f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_smile.png b/android/app/src/main/res/drawable-xhdpi/icon_smile.png new file mode 100644 index 0000000000..fbb4b0e99d Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_smile.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_tab_chats.png b/android/app/src/main/res/drawable-xhdpi/icon_tab_chats.png new file mode 100644 index 0000000000..87c2bc79f9 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_tab_chats.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_tab_contacts.png b/android/app/src/main/res/drawable-xhdpi/icon_tab_contacts.png new file mode 100644 index 0000000000..9192c55522 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_tab_contacts.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_tab_discover.png b/android/app/src/main/res/drawable-xhdpi/icon_tab_discover.png new file mode 100644 index 0000000000..f40ba84519 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_tab_discover.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_up.png b/android/app/src/main/res/drawable-xhdpi/icon_up.png new file mode 100644 index 0000000000..9b150cd9e1 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_wallet_avatar.png b/android/app/src/main/res/drawable-xhdpi/icon_wallet_avatar.png new file mode 100755 index 0000000000..3aad8494b9 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_wallet_avatar.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/launch_logo.png b/android/app/src/main/res/drawable-xhdpi/launch_logo.png new file mode 100644 index 0000000000..c19041c566 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/launch_logo.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/launch_screen.png b/android/app/src/main/res/drawable-xhdpi/launch_screen.png new file mode 100644 index 0000000000..a5e56ed0ad Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/launch_screen.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/scan_blue.png b/android/app/src/main/res/drawable-xhdpi/scan_blue.png new file mode 100644 index 0000000000..df46334a87 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/scan_blue.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/avatar.png b/android/app/src/main/res/drawable-xxhdpi/avatar.png new file mode 100644 index 0000000000..7fb2d0b42b Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/avatar.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/console.png b/android/app/src/main/res/drawable-xxhdpi/console.png new file mode 100644 index 0000000000..c1f41d6edd Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/console.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png new file mode 100644 index 0000000000..0fff341220 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_left_bottom.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png new file mode 100644 index 0000000000..e25037ece3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_left_top.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png new file mode 100644 index 0000000000..e542ec0d64 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_right_bottom.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png new file mode 100644 index 0000000000..a6a65aedc9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/corner_right_top.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_add.png b/android/app/src/main/res/drawable-xxhdpi/icon_add.png new file mode 100755 index 0000000000..ce0b329d94 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_add.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_add_gray.png b/android/app/src/main/res/drawable-xxhdpi/icon_add_gray.png new file mode 100644 index 0000000000..55a1592352 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_add_gray.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_add_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_add_white.png new file mode 100644 index 0000000000..2780585768 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_add_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_avatar.png b/android/app/src/main/res/drawable-xxhdpi/icon_avatar.png new file mode 100644 index 0000000000..f3b8d0770d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_avatar.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_back.png b/android/app/src/main/res/drawable-xxhdpi/icon_back.png new file mode 100644 index 0000000000..4d688372c9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_back.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png new file mode 100644 index 0000000000..166bc865a4 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_back_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_bin.png b/android/app/src/main/res/drawable-xxhdpi/icon_bin.png new file mode 100644 index 0000000000..fa960dbf1d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_bin.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_close_gray.png b/android/app/src/main/res/drawable-xxhdpi/icon_close_gray.png new file mode 100644 index 0000000000..c1f1c5f300 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_close_gray.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_close_small_gray.png b/android/app/src/main/res/drawable-xxhdpi/icon_close_small_gray.png new file mode 100755 index 0000000000..15c440e082 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_close_small_gray.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png new file mode 100644 index 0000000000..32e84becb6 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_close_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_dollar.png b/android/app/src/main/res/drawable-xxhdpi/icon_dollar.png new file mode 100644 index 0000000000..85d93cab2c Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_dollar.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png new file mode 100644 index 0000000000..dac30955fe Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_dollar_green.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_dots.png b/android/app/src/main/res/drawable-xxhdpi/icon_dots.png new file mode 100644 index 0000000000..464cfeb09f Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_dots.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_drag_down.png b/android/app/src/main/res/drawable-xxhdpi/icon_drag_down.png new file mode 100644 index 0000000000..7e7a4912bc Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_drag_down.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png new file mode 100644 index 0000000000..23fa971c5e Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_drag_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_enter_address.png b/android/app/src/main/res/drawable-xxhdpi/icon_enter_address.png new file mode 100755 index 0000000000..2adebc2c9a Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_enter_address.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_group.png b/android/app/src/main/res/drawable-xxhdpi/icon_group.png new file mode 100644 index 0000000000..b14d23f73b Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_group.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png new file mode 100644 index 0000000000..a1bff88bb0 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_hamburger.png b/android/app/src/main/res/drawable-xxhdpi/icon_hamburger.png new file mode 100644 index 0000000000..8bd85bc66a Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_hamburger.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_input_list.png b/android/app/src/main/res/drawable-xxhdpi/icon_input_list.png new file mode 100644 index 0000000000..9e4fd528b3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_input_list.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_list.png b/android/app/src/main/res/drawable-xxhdpi/icon_list.png new file mode 100644 index 0000000000..e253971bae Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_list.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_location.png b/android/app/src/main/res/drawable-xxhdpi/icon_location.png new file mode 100755 index 0000000000..4dfb48d63d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_location.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_lock_gray.png b/android/app/src/main/res/drawable-xxhdpi/icon_lock_gray.png new file mode 100644 index 0000000000..f18aaa61e1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_lock_gray.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_lock_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_lock_white.png new file mode 100644 index 0000000000..ccddc79dec Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_lock_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png new file mode 100644 index 0000000000..a356f7ccdd Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_money_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_money_white.png new file mode 100755 index 0000000000..243469d9a9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_money_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical.png b/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical.png new file mode 100644 index 0000000000..d30018f924 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical_blue.png b/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical_blue.png new file mode 100644 index 0000000000..79449d9dff Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_more_vertical_blue.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_muted.png b/android/app/src/main/res/drawable-xxhdpi/icon_muted.png new file mode 100644 index 0000000000..674a9faa66 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_notifications_on.png b/android/app/src/main/res/drawable-xxhdpi/icon_notifications_on.png new file mode 100644 index 0000000000..a0ee3c3fd5 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_notifications_on.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok.png new file mode 100644 index 0000000000..4abb3e2e57 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_blue.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_blue.png new file mode 100644 index 0000000000..9e8661249d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_blue.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled.png new file mode 100644 index 0000000000..513a2177a2 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000..787972f470 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_disabled_inversed.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_purple.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_purple.png new file mode 100644 index 0000000000..b294224264 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_purple.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_small.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_small.png new file mode 100644 index 0000000000..e824f5e8eb Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_small.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_ok_small_copy_2.png b/android/app/src/main/res/drawable-xxhdpi/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..e824f5e8eb Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_ok_small_copy_2.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_phone_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_phone_white.png new file mode 100755 index 0000000000..b49ec0439d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_phone_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_plus.png b/android/app/src/main/res/drawable-xxhdpi/icon_plus.png new file mode 100644 index 0000000000..dad95d1814 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_plus.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_qr.png b/android/app/src/main/res/drawable-xxhdpi/icon_qr.png new file mode 100644 index 0000000000..f7b3c5ad15 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_qr.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_qr_gray.png b/android/app/src/main/res/drawable-xxhdpi/icon_qr_gray.png new file mode 100644 index 0000000000..9100f6d22b Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_qr_gray.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_scan_q_r.png b/android/app/src/main/res/drawable-xxhdpi/icon_scan_q_r.png new file mode 100755 index 0000000000..205e92cd8e Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_scan_q_r.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_scan_white.png b/android/app/src/main/res/drawable-xxhdpi/icon_scan_white.png new file mode 100644 index 0000000000..9aeeecba40 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_scan_white.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_search.png b/android/app/src/main/res/drawable-xxhdpi/icon_search.png new file mode 100644 index 0000000000..b057823f3b Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_search.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..d138f4e4b9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_send.png b/android/app/src/main/res/drawable-xxhdpi/icon_send.png new file mode 100644 index 0000000000..29c51712c3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_send.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_settings.png b/android/app/src/main/res/drawable-xxhdpi/icon_settings.png new file mode 100644 index 0000000000..561a5dcc25 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_smile.png b/android/app/src/main/res/drawable-xxhdpi/icon_smile.png new file mode 100644 index 0000000000..45dfb63996 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_smile.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_tab_chats.png b/android/app/src/main/res/drawable-xxhdpi/icon_tab_chats.png new file mode 100644 index 0000000000..382bcaaaf6 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_tab_chats.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_tab_contacts.png b/android/app/src/main/res/drawable-xxhdpi/icon_tab_contacts.png new file mode 100644 index 0000000000..19f17a0942 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_tab_contacts.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_tab_discover.png b/android/app/src/main/res/drawable-xxhdpi/icon_tab_discover.png new file mode 100644 index 0000000000..8bc25d2ab3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_tab_discover.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_up.png b/android/app/src/main/res/drawable-xxhdpi/icon_up.png new file mode 100644 index 0000000000..01e16f8ce1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_wallet_avatar.png b/android/app/src/main/res/drawable-xxhdpi/icon_wallet_avatar.png new file mode 100755 index 0000000000..eb3076c861 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_wallet_avatar.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/launch_logo.png b/android/app/src/main/res/drawable-xxhdpi/launch_logo.png new file mode 100644 index 0000000000..70f491126d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/launch_logo.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/launch_screen.png b/android/app/src/main/res/drawable-xxhdpi/launch_screen.png new file mode 100644 index 0000000000..8f796039a2 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/launch_screen.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/scan_blue.png b/android/app/src/main/res/drawable-xxhdpi/scan_blue.png new file mode 100644 index 0000000000..b89f57307c Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/scan_blue.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/avatar.png b/android/app/src/main/res/drawable-xxxhdpi/avatar.png new file mode 100644 index 0000000000..c33ac67619 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/avatar.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/console.png b/android/app/src/main/res/drawable-xxxhdpi/console.png new file mode 100644 index 0000000000..19a59f96ea Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/console.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png b/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png new file mode 100644 index 0000000000..79d8ff98d3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_left_bottom.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png b/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png new file mode 100644 index 0000000000..8ccdd18d77 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_left_top.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png b/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png new file mode 100644 index 0000000000..1fa347dffe Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_right_bottom.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png b/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png new file mode 100644 index 0000000000..a26ebe0e61 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/corner_right_top.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_add_gray.png b/android/app/src/main/res/drawable-xxxhdpi/icon_add_gray.png new file mode 100644 index 0000000000..89ada28fa4 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_add_gray.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_add_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_add_white.png new file mode 100644 index 0000000000..97b4682acb Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_add_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_avatar.png b/android/app/src/main/res/drawable-xxxhdpi/icon_avatar.png new file mode 100644 index 0000000000..a0ab8e6657 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_avatar.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_back.png b/android/app/src/main/res/drawable-xxxhdpi/icon_back.png new file mode 100644 index 0000000000..a420d369f3 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_back.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png new file mode 100644 index 0000000000..8992428891 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_back_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_bin.png b/android/app/src/main/res/drawable-xxxhdpi/icon_bin.png new file mode 100644 index 0000000000..ac64c38731 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_bin.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_close_gray.png b/android/app/src/main/res/drawable-xxxhdpi/icon_close_gray.png new file mode 100644 index 0000000000..07b1e5d3da Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_close_gray.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_close_small_gray.png b/android/app/src/main/res/drawable-xxxhdpi/icon_close_small_gray.png new file mode 100755 index 0000000000..98884bb94e Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_close_small_gray.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png new file mode 100644 index 0000000000..68dbc7fb36 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_close_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_dollar.png b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar.png new file mode 100644 index 0000000000..56a12cb0b0 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png new file mode 100644 index 0000000000..1f3c1c57d1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_dollar_green.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_dots.png b/android/app/src/main/res/drawable-xxxhdpi/icon_dots.png new file mode 100644 index 0000000000..972f7be2fd Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_dots.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_drag_down.png b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_down.png new file mode 100644 index 0000000000..0fe8e31411 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_down.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png new file mode 100644 index 0000000000..fd3f08e9ae Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_drag_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_enter_address.png b/android/app/src/main/res/drawable-xxxhdpi/icon_enter_address.png new file mode 100755 index 0000000000..7e6c232c99 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_enter_address.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_group.png b/android/app/src/main/res/drawable-xxxhdpi/icon_group.png new file mode 100644 index 0000000000..0cefda6ed1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_group.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png b/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png new file mode 100644 index 0000000000..bf47ac4f7b Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_hamburger.png b/android/app/src/main/res/drawable-xxxhdpi/icon_hamburger.png new file mode 100644 index 0000000000..edc06a5b04 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_hamburger.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_input_list.png b/android/app/src/main/res/drawable-xxxhdpi/icon_input_list.png new file mode 100644 index 0000000000..d136f96ef8 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_input_list.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_list.png b/android/app/src/main/res/drawable-xxxhdpi/icon_list.png new file mode 100644 index 0000000000..052e33d187 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_list.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_location.png b/android/app/src/main/res/drawable-xxxhdpi/icon_location.png new file mode 100755 index 0000000000..3683564c74 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_location.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_lock_gray.png b/android/app/src/main/res/drawable-xxxhdpi/icon_lock_gray.png new file mode 100644 index 0000000000..8b3739e6a8 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_lock_gray.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_lock_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_lock_white.png new file mode 100644 index 0000000000..41875c78c2 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_lock_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png new file mode 100644 index 0000000000..7c51fd4dd0 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_money_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_money_white.png new file mode 100755 index 0000000000..9fcfca7496 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_money_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical.png b/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical.png new file mode 100644 index 0000000000..3fada95805 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical_blue.png b/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical_blue.png new file mode 100644 index 0000000000..460e8081e4 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_more_vertical_blue.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png b/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png new file mode 100644 index 0000000000..bb46070875 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_notifications_on.png b/android/app/src/main/res/drawable-xxxhdpi/icon_notifications_on.png new file mode 100644 index 0000000000..cc4a37280a Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_notifications_on.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok.png new file mode 100644 index 0000000000..6b15f29aec Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_blue.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_blue.png new file mode 100644 index 0000000000..f8a9b938b4 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_blue.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled.png new file mode 100644 index 0000000000..5f4358614b Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png new file mode 100644 index 0000000000..1846eb534e Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_disabled_inversed.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_purple.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_purple.png new file mode 100644 index 0000000000..9429e65fbf Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_purple.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small.png new file mode 100644 index 0000000000..ced7bb97ca Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small_copy_2.png b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..ced7bb97ca Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_ok_small_copy_2.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_phone_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_phone_white.png new file mode 100755 index 0000000000..eb66d11f8d Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_phone_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_plus.png b/android/app/src/main/res/drawable-xxxhdpi/icon_plus.png new file mode 100644 index 0000000000..4474449b88 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_plus.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png b/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png new file mode 100644 index 0000000000..863a0a5eb1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_qr.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_qr_gray.png b/android/app/src/main/res/drawable-xxxhdpi/icon_qr_gray.png new file mode 100644 index 0000000000..cd5d3df69a Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_qr_gray.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_scan_q_r.png b/android/app/src/main/res/drawable-xxxhdpi/icon_scan_q_r.png new file mode 100755 index 0000000000..9e140304dd Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_scan_q_r.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_scan_white.png b/android/app/src/main/res/drawable-xxxhdpi/icon_scan_white.png new file mode 100644 index 0000000000..8011c4a64d Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_scan_white.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_search.png b/android/app/src/main/res/drawable-xxxhdpi/icon_search.png new file mode 100644 index 0000000000..5a83f68df9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_search.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..4e1948e8ff Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_send.png b/android/app/src/main/res/drawable-xxxhdpi/icon_send.png new file mode 100644 index 0000000000..a109667f64 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_send.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png b/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png new file mode 100644 index 0000000000..b9f142032e Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_smile.png b/android/app/src/main/res/drawable-xxxhdpi/icon_smile.png new file mode 100644 index 0000000000..49d62400dd Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_smile.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_tab_chats.png b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_chats.png new file mode 100644 index 0000000000..19020ea5b5 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_chats.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_tab_contacts.png b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_contacts.png new file mode 100644 index 0000000000..d5bfcba9d2 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_contacts.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_tab_discover.png b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_discover.png new file mode 100644 index 0000000000..cfc93ab11a Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_tab_discover.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_up.png b/android/app/src/main/res/drawable-xxxhdpi/icon_up.png new file mode 100644 index 0000000000..d98dff7ff6 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_wallet_avatar.png b/android/app/src/main/res/drawable-xxxhdpi/icon_wallet_avatar.png new file mode 100755 index 0000000000..061acdf9d7 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_wallet_avatar.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/scan_blue.png b/android/app/src/main/res/drawable-xxxhdpi/scan_blue.png new file mode 100644 index 0000000000..2365f04fc5 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/scan_blue.png differ diff --git a/android/app/src/main/res/ic_launcher-web.png b/android/app/src/main/res/ic_launcher-web.png new file mode 100644 index 0000000000..8309501779 Binary files /dev/null and b/android/app/src/main/res/ic_launcher-web.png differ diff --git a/android/app/src/main/res/layout/launch_screen.xml b/android/app/src/main/res/layout/launch_screen.xml new file mode 100644 index 0000000000..b72e71736d --- /dev/null +++ b/android/app/src/main/res/layout/launch_screen.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bccce..89a6326247 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png new file mode 100644 index 0000000000..717e45735e Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0cbd3..110f2e7a5f 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0e7b..652c9645fd 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd7..6c35a232ce 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..26aab572b4 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..629b151f53 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + #7099e6 + #5c6bc0 + #5c6bc0 + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 7b66980444..428623ba43 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,6 @@ - SyngIm - + Status + Your phone appears to be ROOTED, by pressing CONTINUE you understand and accept the risks in using this software. + Continue + Exit + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 319eb0ca10..b6c9d952be 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,8 +1,10 @@ - - + @color/primary + @color/primary_dark + @color/accent + @color/primary + @color/primary + diff --git a/android/build.gradle b/android/build.gradle index f05bf1a9b7..eb73d12235 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:2.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -16,16 +16,10 @@ allprojects { repositories { mavenLocal() jcenter() - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url "$projectDir/../../node_modules/react-native/android" - } + maven { url "$rootDir/../node_modules/react-native/android" } // for geth - flatDir { - dirs 'libs' - } - maven { - url "http://85.90.244.96:8081/artifactory/libs-release-local" - } + flatDir { dirs 'libs' } + maven { url "http://139.162.11.12:8081/artifactory/libs-release-local" } + maven { url "https://jitpack.io" } } } diff --git a/android/gradle.properties b/android/gradle.properties index 1fd964e90b..3007437da7 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -18,3 +18,7 @@ # org.gradle.parallel=true android.useDeprecatedNdk=true +STATUS_RELEASE_STORE_FILE=/path/to/store/file +STATUS_RELEASE_STORE_PASSWORD=password +STATUS_RELEASE_KEY_ALIAS=alias +STATUS_RELEASE_KEY_PASSWORD=password diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index b9fbfaba0e..4db8c48fe5 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Wed Jul 20 10:42:35 EEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 784925e19a..e729defd4c 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,16 +1,42 @@ -rootProject.name = 'SyngIm' +rootProject.name = 'StatusIm' include ':app' - +include ':instabug-reactnative' +project(':instabug-reactnative').projectDir = new File(rootProject.projectDir, '../node_modules/instabug-reactnative/android') +include ':react-native-splash-screen' +project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android') +include ':react-native-image-resizer' +project(':react-native-image-resizer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-resizer/android') +include ':react-native-dialogs' +project(':react-native-dialogs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dialogs/android') +include ':react-native-randombytes' +project(':react-native-randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/android') +include ':react-native-android-sms-listener' +project(':react-native-android-sms-listener').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-sms-listener/android') include ':react-native-contacts' project(':react-native-contacts').projectDir = new File(settingsDir, '../node_modules/react-native-contacts/android') - include ':react-native-i18n' project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android') -// realm dependency include ':react-native-vector-icons' project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android') -include ':randombytes' -project(':randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/app') \ No newline at end of file +include ':react-native-linear-gradient' +project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') +include ':react-native-status' +project(':react-native-status').projectDir = new File(rootProject.projectDir, '../modules/react-native-status/android') +include ':react-native-camera' +project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android') +include ':react-native-orientation', ':app' +project(':react-native-orientation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation/android') +include ':react-native-fs' +project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android') + +include ':react-native-image-crop-picker' +project(':react-native-image-crop-picker').projectDir = new File(settingsDir, '../node_modules/react-native-image-crop-picker/android') + +include ':react-native-webview-bridge' +project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') + +include ':react-native-share' +project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android') \ No newline at end of file diff --git a/doc/intro.md b/doc/intro.md index 7a0eb4e81c..b3702d4122 100644 --- a/doc/intro.md +++ b/doc/intro.md @@ -1,3 +1,3 @@ -# Introduction to syng-im +# Introduction to status-im TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) diff --git a/env/dev/env/android/main.cljs b/env/dev/env/android/main.cljs index 2348604c4a..a26f34785c 100644 --- a/env/dev/env/android/main.cljs +++ b/env/dev/env/android/main.cljs @@ -1,18 +1,21 @@ - (ns ^:figwheel-no-load env.android.main - (:require [reagent.core :as r] - [syng-im.android.core :refer [app-root]] - [figwheel.client :as figwheel :include-macros true] - [syng-im.android.core :as core])) +(ns ^:figwheel-no-load env.android.main + (:require [reagent.core :as r] + [status-im.android.core :as core] + [figwheel.client :as figwheel :include-macros true])) - (enable-console-print!) +(enable-console-print!) (def cnt (r/atom 0)) -(defn reloader [] @cnt [app-root]) +(defn reloader [] @cnt [core/app-root]) (def root-el (r/as-element [reloader])) +(defn callback [] + (swap! cnt inc) + (status-im.components.status/init-jail) + (re-frame.core/dispatch [:load-commands!])) (figwheel/watch-and-reload - :websocket-url "ws://10.0.3.2:3449/figwheel-ws" - :heads-up-display true - :jsload-callback #(swap! cnt inc)) + :websocket-url "ws://10.0.3.2:3449/figwheel-ws" + :heads-up-display false + :jsload-callback callback) -(core/init) \ No newline at end of file +(core/init) diff --git a/env/dev/env/android_test/main.cljs b/env/dev/env/android_test/main.cljs new file mode 100644 index 0000000000..8599609053 --- /dev/null +++ b/env/dev/env/android_test/main.cljs @@ -0,0 +1,21 @@ +(ns ^:figwheel-no-load env.android-test.main + (:require [reagent.core :as r] + [status-im.android.core :as core] + [figwheel.client :as figwheel :include-macros true] + ;[status-im.test.handlers-stubs :refer [init-stubs]] + )) +(enable-console-print!) + +(set! js/console.disableYellowBox true) + +(def cnt (r/atom 0)) +(defn reloader [] @cnt [core/app-root]) +(def root-el (r/as-element [reloader])) + +(figwheel/watch-and-reload + :websocket-url "ws://localhost:3449/figwheel-ws" + :heads-up-display false + :jsload-callback #(swap! cnt inc)) + +(core/init :test) +;(init-stubs) diff --git a/env/dev/env/ios/main.cljs b/env/dev/env/ios/main.cljs index 748936a414..07d23b6f62 100644 --- a/env/dev/env/ios/main.cljs +++ b/env/dev/env/ios/main.cljs @@ -1,9 +1,10 @@ (ns ^:figwheel-no-load env.ios.main (:require [reagent.core :as r] - [syng-im.ios.core :as core] - [figwheel.client :as figwheel :include-macros true])) + [status-im.ios.core :as core] + [figwheel.client :as figwheel :include-macros true] + [cljs.pprint])) - (enable-console-print!) +(enable-console-print!) (def cnt (r/atom 0)) (defn reloader [] @cnt [core/app-root]) @@ -11,7 +12,7 @@ (figwheel/watch-and-reload :websocket-url "ws://localhost:3449/figwheel-ws" - :heads-up-display true + :heads-up-display false :jsload-callback #(swap! cnt inc)) (core/init) \ No newline at end of file diff --git a/env/dev/user.clj b/env/dev/user.clj index 4c2ca8be74..6f5702afa6 100644 --- a/env/dev/user.clj +++ b/env/dev/user.clj @@ -1,5 +1,7 @@ (ns user - (:use [figwheel-sidecar.repl-api :as ra])) + (:use [figwheel-sidecar.repl-api :as ra]) + (:require [hawk.core :as hawk] + [clojure.string :as s])) ;; This namespace is loaded automatically by nREPL ;; read project.clj to get build configs @@ -10,17 +12,34 @@ (apply hash-map) :profiles)) -(def cljs-builds (get-in profiles [:dev :cljsbuild :builds])) +(def cljs-builds + (get-in profiles [:dev :cljsbuild :builds])) (defn start-figwheel - "Start figwheel for one or more builds" - [& build-ids] - (ra/start-figwheel! - {:build-ids build-ids - :all-builds cljs-builds}) - (ra/cljs-repl)) + "Start figwheel for one or more builds" + [build-ids] + (ra/start-figwheel! + {:figwheel-options {:nrepl-port 7888} + :build-ids build-ids + :all-builds cljs-builds})) + +(def start-cljs-repl ra/cljs-repl) (defn stop-figwheel - "Stops figwheel" - [] - (ra/stop-figwheel!)) \ No newline at end of file + "Stops figwheel" + [] + (ra/stop-figwheel!)) + +(hawk/watch! [{:paths ["resources"] + :handler (fn [ctx e] + (let [path "src/status_im/utils/js_resources.cljs" + js-resourced (slurp path)] + (spit path (str js-resourced " ;;")) + (spit path js-resourced)) + ctx)}]) + +(let [env-build-ids (System/getenv "BUILD_IDS") + build-ids (if env-build-ids + (map keyword (s/split env-build-ids #",")) + [:android])] + (start-figwheel build-ids)) diff --git a/env/prod/env/android/main.cljs b/env/prod/env/android/main.cljs index f112100ec7..b1e717e078 100644 --- a/env/prod/env/android/main.cljs +++ b/env/prod/env/android/main.cljs @@ -1,5 +1,5 @@ (ns env.android.main - (:require [syng-im.android.core :as core])) + (:require [status-im.android.core :as core])) (core/init) diff --git a/env/prod/env/ios/main.cljs b/env/prod/env/ios/main.cljs index 11392b5e72..67664f36de 100644 --- a/env/prod/env/ios/main.cljs +++ b/env/prod/env/ios/main.cljs @@ -1,5 +1,5 @@ (ns env.ios.main - (:require [syng-im.ios.core :as core])) + (:require [status-im.ios.core :as core])) (core/init) diff --git a/externs/externs.js b/externs/externs.js new file mode 100644 index 0000000000..4bf1237200 --- /dev/null +++ b/externs/externs.js @@ -0,0 +1,197 @@ +var TopLevel = { +"ActionSheetIOS" : function () {}, +"addEntropy" : function () {}, +"addEventListener" : function () {}, +"addListener" : function () {}, +"addOrientationListener" : function () {}, +"Alert" : function () {}, +"alert" : function () {}, +"Animated" : function () {}, +"Array" : function () {}, +"awesome-phonenumber" : function () {}, +"blur" : function () {}, +"call" : function () {}, +"callJail" : function () {}, +"capture" : function () {}, +"catch" : function () {}, +"Chance" : function () {}, +"clearInterval" : function () {}, +"clearTimeout" : function () {}, +"Clipboard" : function () {}, +"cloneWithRows" : function () {}, +"close" : function () {}, +"closeDrawer" : function () {}, +"codec" : function () {}, +"completeTransaction" : function () {}, +"console" : function () {}, +"contentOffset" : function () {}, +"contentSize" : function () {}, +"create" : function () {}, +"createAccount" : function () {}, +"data" : function () {}, +"Date" : function () {}, +"DEBUG" : function () {}, +"decay" : function () {}, +"decrypt" : function () {}, +"default" : function () {}, +"defaultPath" : function () {}, +"defaultSeparator" : function () {}, +"delay" : function () {}, +"delete" : function () {}, +"deleteAll" : function () {}, +"DeviceEventEmitter" : function () {}, +"digest" : function () {}, +"Dimensions" : function () {}, +"disableYellowBox" : function () {}, +"discardTransaction" : function () {}, +"dy" : function () {}, +"encrypt" : function () {}, +"ENC_DEC" : function () {}, +"end" : function () {}, +"endCoordinates" : function () {}, +"eth" : function () {}, +"getBlock" : function () {}, +"event" : function () {}, +"fallbacks" : function () {}, +"fetch" : function () {}, +"filter" : function () {}, +"filtered" : function () {}, +"find" : function () {}, +"finished" : function () {}, +"focus" : function () {}, +"fromUtf8" : function () {}, +"fromWei" : function () {}, +"generate" : function () {}, +"get" : function () {}, +"getAll" : function () {}, +"getInitialOrientation" : function () {}, +"getLayout" : function () {}, +"getNumber" : function () {}, +"getTime" : function () {}, +"getTimezoneOffset" : function () {}, +"goog" : function () {}, +"guid" : function () {}, +"hash" : function () {}, +"height" : function () {}, +"hex" : function () {}, +"hide" : function () {}, +"indexOf" : function () {}, +"initialPage" : function () {}, +"initJail" : function () {}, +"isAddress" : function () {}, +"isConnected" : function () {}, +"isMatches" : function () {}, +"isMobile" : function () {}, +"isSyncing" : function () {}, +"isValid" : function () {}, +"Item" : function () {}, +"JSON" : function () {}, +"jsonEvent" : function () {}, +"Keyboard" : function () {}, +"layout" : function () {}, +"length" : function () {}, +"locale" : function () {}, +"lockToPortrait" : function () {}, +"log" : function () {}, +"login" : function () {}, +"map" : function () {}, +"Math" : function () {}, +"message" : function () {}, +"moveFile" : function () {}, +"moveY" : function () {}, +"nativeEvent" : function () {}, +"NativeModules" : function () {}, +"objects" : function () {}, +"open" : function () {}, +"openDrawer" : function () {}, +"openPicker" : function () {}, +"OS" : function () {}, +"p" : function () {}, +"panHandlers" : function () {}, +"PanResponder" : function () {}, +"parallel" : function () {}, +"parse" : function () {}, +"parseInt" : function () {}, +"parseJail" : function () {}, +"path" : function () {}, +"Platform" : function () {}, +"post" : function () {}, +"prototype" : function () {}, +"push" : function () {}, +"random" : function () {}, +"randomBytes" : function () {}, +"ReactNative" : function () {}, +"readFile" : function () {}, +"realm-class" : function () {}, +"recoverAccount" : function () {}, +"registerComponent" : function () {}, +"remove" : function () {}, +"removeAllListeners" : function () {}, +"require" : function () {}, +"reset" : function () {}, +"reverse" : function () {}, +"round" : function () {}, +"schema" : function () {}, +"schemaVersion" : function () {}, +"scrollBy" : function () {}, +"scrollTo" : function () {}, +"scrollView" : function () {}, +"selection" : function () {}, +"sendToBridge" : function () {}, +"sequence" : function () {}, +"set" : function () {}, +"setInterval" : function () {}, +"setSoftInputMode" : function () {}, +"setState" : function () {}, +"setString" : function () {}, +"setTimeout" : function () {}, +"setValue" : function () {}, +"Sha256" : function () {}, +"shh" : function () {}, +"show" : function () {}, +"showActionSheetWithOptions" : function () {}, +"sjcl" : function () {}, +"slice" : function () {}, +"sorted" : function () {}, +"SplashScreen" : function () {}, +"spring" : function () {}, +"start" : function () {}, +"startNode" : function () {}, +"startNodeRPCServer" : function () {}, +"startsWith" : function () {}, +"Status" : function () {}, +"status" : function () {}, +"stopAnimation" : function () {}, +"stopNodeRPCServer" : function () {}, +"stopWatching" : function () {}, +"stringify" : function () {}, +"t" : function () {}, +"Text" : function () {}, +"text" : function () {}, +"then" : function () {}, +"timing" : function () {}, +"toBits" : function () {}, +"toDecimal" : function () {}, +"toLowerCase" : function () {}, +"toString" : function () {}, +"toUtf8" : function () {}, +"translations" : function () {}, +"update" : function () {}, +"Value" : function () {}, +"value" : function () {}, +"ValueXY" : function () {}, +"View" : function () {}, +"vy" : function () {}, +"Web3" : function () {}, +"web3" : function () {}, +"providers" : function () {}, +"HttpProvider": function () {}, +"width" : function () {}, +"window" : function () {}, +"write" : function () {}, +"x" : function () {}, +"y" : function () {}, +"_value" : function () {}, +"ListView": function() {}, +"DataSource": function() {} +}; diff --git a/figwheel-bridge.js b/figwheel-bridge.js index 2a7cb1cc2f..9e3163203a 100644 --- a/figwheel-bridge.js +++ b/figwheel-bridge.js @@ -5,6 +5,7 @@ */ var CLOSURE_UNCOMPILED_DEFINES = null; +var debugEnabled = false; var config = { basePath: "target/", @@ -12,14 +13,26 @@ var config = { serverPort: 8081 }; -var React = require('react-native'); +var React = require('react'); +var ReactNative = require('react-native'); +var WebSocket = require('WebSocket'); var self; var scriptQueue = []; var serverHost = null; // will be set dynamically var fileBasePath = null; // will be set dynamically var evaluate = eval; // This is needed, direct calls to eval does not work (RN packager???) var externalModules = {}; -var evalListeners = []; // functions to be called when a script is evaluated +var evalListeners = [ // Functions to be called after each js file is loaded and evaluated + function (url) { + if (url.indexOf('jsloader') > -1) { + shimJsLoader(); + } + }, + function (url) { + if (url.indexOf('/figwheel/client/socket') > -1) { + setCorrectWebSocketImpl(); + } + }]; var figwheelApp = function (platform, devHost) { return React.createClass({ @@ -30,9 +43,9 @@ var figwheelApp = function (platform, devHost) { if (!this.state.loaded) { var plainStyle = {flex: 1, alignItems: 'center', justifyContent: 'center'}; return ( - - Waiting for Figwheel to load files. - + + Waiting for Figwheel to load files. + ); } return this.state.root; @@ -40,7 +53,7 @@ var figwheelApp = function (platform, devHost) { componentDidMount: function () { var app = this; if (typeof goog === "undefined") { - loadApp(platform, devHost, function(appRoot) { + loadApp(platform, devHost, function (appRoot) { app.setState({root: appRoot, loaded: true}) }); } @@ -48,20 +61,25 @@ var figwheelApp = function (platform, devHost) { }) }; +function logDebug(msg) { + if (debugEnabled) { + console.log(msg); + } +} + // evaluates js code ensuring proper ordering function customEval(url, javascript, success, error) { if (scriptQueue.length > 0) { if (scriptQueue[0] === url) { try { evaluate(javascript); - console.info('Evaluated: ' + url); + logDebug('Evaluated: ' + url); scriptQueue.shift(); evalListeners.forEach(function (listener) { listener(url) }); success(); } catch (e) { - console.error('Evaluation error in: ' + url); console.error(e); error(); } @@ -81,7 +99,7 @@ var isChrome = function () { }; function asyncImportScripts(url, success, error) { - console.info('(asyncImportScripts) Importing: ' + url); + logDebug('(asyncImportScripts) Importing: ' + url); scriptQueue.push(url); fetch(url) .then(function (response) { @@ -91,7 +109,6 @@ function asyncImportScripts(url, success, error) { return customEval(url, responseText, success, error); }) .catch(function (error) { - console.error('Error loading script, please check your config setup.'); console.error(error); return error(); }); @@ -100,12 +117,13 @@ function asyncImportScripts(url, success, error) { function syncImportScripts(url, success, error) { try { importScripts(url); - console.info('Evaluated: ' + url); + logDebug('Evaluated: ' + url); evalListeners.forEach(function (listener) { listener(url) }); success(); } catch (e) { + console.error(e); error() } } @@ -123,7 +141,7 @@ function importJs(src, success, error) { var file = fileBasePath + '/' + src; - console.info('(importJs) Importing: ' + file); + logDebug('(importJs) Importing: ' + file); if (isChrome()) { syncImportScripts(serverBaseUrl("localhost") + '/' + file, success, error); } else { @@ -143,55 +161,73 @@ function interceptRequire() { }; } -// do not show debug messages in yellow box -function debugToLog() { - console.debug = console.log; +function compileWarningsToYellowBox() { + var log = window.console.log; + var compileWarningRx = /Figwheel: Compile/; + var compileExceptionRx = /Figwheel: Compile Exception/; + var errorInFileRx = /Error on file/; + var isBuffering = false; + var compileExceptionBuffer = ""; + window.console.log = function (msg) { + log.apply(window.console, arguments); + if (compileExceptionRx.test(msg)) { // enter buffering mode to get all the messages for exception + isBuffering = true; + compileExceptionBuffer = msg + "\n"; + } else if (errorInFileRx.test(msg) && isBuffering) { // exit buffering mode and log buffered messages to YellowBox + isBuffering = false; + console.warn(compileExceptionBuffer + msg); + compileExceptionBuffer = ""; + } else if (isBuffering) { //log messages buffering mode + compileExceptionBuffer += msg + "\n"; + } else if (compileWarningRx.test(msg)) { + console.warn(msg); + } + }; } function serverBaseUrl(host) { return "http://" + host + ":" + config.serverPort } +function setCorrectWebSocketImpl() { + figwheel.client.socket.get_websocket_imp = function () { + return WebSocket; + }; +} + function loadApp(platform, devHost, onLoadCb) { serverHost = devHost; fileBasePath = config.basePath + platform; - evalListeners.push(function (url) { - if (url.indexOf('jsloader') > -1) { - shimJsLoader(); - } - }); - // callback when app is ready to get the reloadable component var mainJs = '/env/' + platform + '/main.js'; evalListeners.push(function (url) { if (url.indexOf(mainJs) > -1) { onLoadCb(env[platform].main.root_el); - console.log('Done loading Clojure app'); + console.info('Done loading Clojure app'); } }); if (typeof goog === "undefined") { - console.log('Loading Closure base.'); + console.info('Loading Closure base.'); interceptRequire(); + compileWarningsToYellowBox(); importJs('goog/base.js', function () { shimBaseGoog(); - fakeLocalStorageAndDocument(); importJs('cljs_deps.js'); importJs('goog/deps.js', function () { - debugToLog(); // This is needed because of RN packager // seriously React packager? why. var googreq = goog.require; - googreq('figwheel.connect'); + googreq('figwheel.connect.' + platform); }); }); } } function startApp(appName, platform, devHost) { - React.AppRegistry.registerComponent( + ReactNative.AppRegistry.registerComponent( appName, () => figwheelApp(platform, devHost)); } @@ -209,39 +245,6 @@ function shimBaseGoog() { importJs(src); return true; }; - goog.inHtmlDocument_ = function () { - return true; - }; -} - -function fakeLocalStorageAndDocument() { - window.localStorage = {}; - window.localStorage.getItem = function () { - return 'true'; - }; - window.localStorage.setItem = function () { - }; - - window.document = {}; - window.document.body = {}; - window.document.body.dispatchEvent = function () { - }; - window.document.createElement = function () { - }; - - if (typeof window.location === 'undefined') { - window.location = {}; - } - console.debug = console.warn; - window.addEventListener = function () { - }; - // make figwheel think that heads-up-display divs are there - window.document.querySelector = function (selector) { - return {}; - }; - window.document.getElementById = function (id) { - return {style:{}}; - }; } // Figwheel fixes @@ -287,4 +290,4 @@ self = { start: startApp }; -module.exports = self; \ No newline at end of file +module.exports = self; diff --git a/images/cljs.png b/images/cljs.png deleted file mode 100644 index a7eca5dfe0..0000000000 Binary files a/images/cljs.png and /dev/null differ diff --git a/images/cljs@2x.png b/images/cljs@2x.png deleted file mode 100644 index 79af2b668d..0000000000 Binary files a/images/cljs@2x.png and /dev/null differ diff --git a/images/cljs@3x.png b/images/cljs@3x.png deleted file mode 100644 index 685d699682..0000000000 Binary files a/images/cljs@3x.png and /dev/null differ diff --git a/images/contacts/auction-house.png b/images/contacts/auction-house.png new file mode 100644 index 0000000000..fee9fdf9f0 Binary files /dev/null and b/images/contacts/auction-house.png differ diff --git a/images/contacts/flight-delays-suck.png b/images/contacts/flight-delays-suck.png new file mode 100644 index 0000000000..05b6fcf7aa Binary files /dev/null and b/images/contacts/flight-delays-suck.png differ diff --git a/images/contacts/jarrad.png b/images/contacts/jarrad.png new file mode 100644 index 0000000000..7b6720535c Binary files /dev/null and b/images/contacts/jarrad.png differ diff --git a/images/contacts/mkr-market.png b/images/contacts/mkr-market.png new file mode 100644 index 0000000000..16e4f78071 Binary files /dev/null and b/images/contacts/mkr-market.png differ diff --git a/images/contacts/oaken-water-meter.png b/images/contacts/oaken-water-meter.png new file mode 100644 index 0000000000..2377175391 Binary files /dev/null and b/images/contacts/oaken-water-meter.png differ diff --git a/images/icon_close_gray.png b/images/icon_close_gray.png new file mode 100644 index 0000000000..c0cca73335 Binary files /dev/null and b/images/icon_close_gray.png differ diff --git a/ios/Messenger.xcodeproj/project.pbxproj b/ios/Messenger.xcodeproj/project.pbxproj deleted file mode 100644 index 2780f9a5f1..0000000000 --- a/ios/Messenger.xcodeproj/project.pbxproj +++ /dev/null @@ -1,947 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { -/* Begin PBXBuildFile section */ - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; - 00E356F31AD99517003FC87E /* MessengerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MessengerTests.m */; }; - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; - E343FE8E1C96F54100C01DB5 /* libRNI18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E343FE8B1C96F4E200C01DB5 /* libRNI18n.a */; }; - E343FE8F1C96F54A00C01DB5 /* libRCTContacts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E343FE841C96F4DA00C01DB5 /* libRCTContacts.a */; }; - E343FE911C971D4200C01DB5 /* Geth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E343FE901C971D4200C01DB5 /* Geth.framework */; }; - 5F301739E81C4A7B92E80915 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1429DFB76DE749D59880DD64 /* libRNVectorIcons.a */; }; - 2171A55FE83747678065F72A /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 93C8E68B46DA4C0A98C39F9D /* Entypo.ttf */; }; - BDF23426F8854AC2A0416A03 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 38E7B297EE0748008056796A /* EvilIcons.ttf */; }; - 221509FA6D5443C0B5215C52 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CF9FE7E98A3454D980A0BED /* FontAwesome.ttf */; }; - A33AC3EAFBC04C0B93BD2EC4 /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5A831378BBA14830A9540B8C /* Foundation.ttf */; }; - 1A4121618C94428C9F0DF1FE /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5F085711AC4A4BA3887A4655 /* Ionicons.ttf */; }; - 59FCD4F0208949BE80CC72BF /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0063E967269B49EE80DC3A77 /* MaterialIcons.ttf */; }; - 4452A93C123F4392A99A7719 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A74F40BECFF8439FB1F41D8A /* Octicons.ttf */; }; - 1AE91A3E2C1A43EFA90C1CD7 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BCEC948817B84597AC493C0B /* Zocial.ttf */; }; - 34535F62E77D4A75A25E2981 /* libRNRandomBytes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2925028DE4684DDAAA7389D7 /* libRNRandomBytes.a */; }; - 97BD21CBE96A4223B253A1E2 /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B29C182EE0942C889049F91 /* libRealmReact.a */; }; - 22802A9C745148D885F0AA01 /* libc++.tbd in Resources */ = {isa = PBXBuildFile; fileRef = 8003209F13C24D35AABD3933 /* libc++.tbd */; }; - 50EE6BCC6F1A48BBA1BDB893 /* libz.tbd in Resources */ = {isa = PBXBuildFile; fileRef = 313A77B1D7804DBDBE6FF6F5 /* libz.tbd */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTActionSheet; - }; - 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTGeolocation; - }; - 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; - remoteInfo = RCTVibration; - }; - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = Messenger; - }; - 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; - }; - 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; - 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; - E343FE831C96F4DA00C01DB5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E343FE7F1C96F4DA00C01DB5 /* RCTContacts.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 1441618E1BD0A79300FA4F59; - remoteInfo = RCTContacts; - }; - E343FE8A1C96F4E200C01DB5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E343FE851C96F4E200C01DB5 /* RNI18n.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CDD7BF781B2D5125006FDA75; - remoteInfo = RNI18n; - }; - E343FE8C1C96F4E200C01DB5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E343FE851C96F4E200C01DB5 /* RNI18n.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CDD7BF831B2D5126006FDA75; - remoteInfo = RNI18nTests; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; - 00E356EE1AD99517003FC87E /* MessengerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MessengerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* MessengerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessengerTests.m; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* Messenger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Messenger.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Messenger/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Messenger/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Messenger/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Messenger/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Messenger/main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; - E343FE7F1C96F4DA00C01DB5 /* RCTContacts.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTContacts.xcodeproj; path = "../node_modules/react-native-contacts/ios/RCTContacts.xcodeproj"; sourceTree = ""; }; - E343FE851C96F4E200C01DB5 /* RNI18n.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNI18n.xcodeproj; path = "../node_modules/react-native-i18n/RNI18n.xcodeproj"; sourceTree = ""; }; - E343FE901C971D4200C01DB5 /* Geth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Geth.framework; path = target/Frameworks/Geth.framework; sourceTree = ""; }; - F04672B9D3824B768FCF6F71 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; name = "RNVectorIcons.xcodeproj"; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; - 1429DFB76DE749D59880DD64 /* libRNVectorIcons.a */ = {isa = PBXFileReference; name = "libRNVectorIcons.a"; path = "libRNVectorIcons.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; - 93C8E68B46DA4C0A98C39F9D /* Entypo.ttf */ = {isa = PBXFileReference; name = "Entypo.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 38E7B297EE0748008056796A /* EvilIcons.ttf */ = {isa = PBXFileReference; name = "EvilIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 2CF9FE7E98A3454D980A0BED /* FontAwesome.ttf */ = {isa = PBXFileReference; name = "FontAwesome.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 5A831378BBA14830A9540B8C /* Foundation.ttf */ = {isa = PBXFileReference; name = "Foundation.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 5F085711AC4A4BA3887A4655 /* Ionicons.ttf */ = {isa = PBXFileReference; name = "Ionicons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 0063E967269B49EE80DC3A77 /* MaterialIcons.ttf */ = {isa = PBXFileReference; name = "MaterialIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - A74F40BECFF8439FB1F41D8A /* Octicons.ttf */ = {isa = PBXFileReference; name = "Octicons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - BCEC948817B84597AC493C0B /* Zocial.ttf */ = {isa = PBXFileReference; name = "Zocial.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 06F26555B4054594966B6B08 /* RNRandomBytes.xcodeproj */ = {isa = PBXFileReference; name = "RNRandomBytes.xcodeproj"; path = "../node_modules/react-native-randombytes/RNRandomBytes.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; - 2925028DE4684DDAAA7389D7 /* libRNRandomBytes.a */ = {isa = PBXFileReference; name = "libRNRandomBytes.a"; path = "libRNRandomBytes.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; - 0D4A52AE301842E1B2533BD3 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; name = "RealmReact.xcodeproj"; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; - 2B29C182EE0942C889049F91 /* libRealmReact.a */ = {isa = PBXFileReference; name = "libRealmReact.a"; path = "libRealmReact.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; - 8003209F13C24D35AABD3933 /* libc++.tbd */ = {isa = PBXFileReference; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; }; - 313A77B1D7804DBDBE6FF6F5 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - E343FE911C971D4200C01DB5 /* Geth.framework in Frameworks */, - 146834051AC3E58100842450 /* libReact.a in Frameworks */, - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, - E343FE8F1C96F54A00C01DB5 /* libRCTContacts.a in Frameworks */, - E343FE8E1C96F54100C01DB5 /* libRNI18n.a in Frameworks */, - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, - 5F301739E81C4A7B92E80915 /* libRNVectorIcons.a in Frameworks */, - 34535F62E77D4A75A25E2981 /* libRNRandomBytes.a in Frameworks */, - 97BD21CBE96A4223B253A1E2 /* libRealmReact.a in Frameworks */, - 22802A9C745148D885F0AA01 /* libc++.tbd in Resources */, - 50EE6BCC6F1A48BBA1BDB893 /* libz.tbd in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 00C302A81ABCB8CE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302B61ABCB90400DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302BC1ABCB91800DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302D41ABCB9D200DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302E01ABCB9EE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, - ); - name = Products; - sourceTree = ""; - }; - 00E356EF1AD99517003FC87E /* MessengerTests */ = { - isa = PBXGroup; - children = ( - 00E356F21AD99517003FC87E /* MessengerTests.m */, - 00E356F01AD99517003FC87E /* Supporting Files */, - ); - path = MessengerTests; - sourceTree = ""; - }; - 00E356F01AD99517003FC87E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 00E356F11AD99517003FC87E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 139105B71AF99BAD00B5F7CC /* Products */ = { - isa = PBXGroup; - children = ( - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, - ); - name = Products; - sourceTree = ""; - }; - 139FDEE71B06529A00C62182 /* Products */ = { - isa = PBXGroup; - children = ( - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* Messenger */ = { - isa = PBXGroup; - children = ( - 008F07F21AC5B25A0029DE68 /* main.jsbundle */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = Messenger; - sourceTree = ""; - }; - 146834001AC3E56700842450 /* Products */ = { - isa = PBXGroup; - children = ( - 146834041AC3E56700842450 /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 78C398B11ACF4ADC00677621 /* Products */ = { - isa = PBXGroup; - children = ( - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, - ); - name = Products; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - E343FE851C96F4E200C01DB5 /* RNI18n.xcodeproj */, - E343FE7F1C96F4DA00C01DB5 /* RCTContacts.xcodeproj */, - 146833FF1AC3E56700842450 /* React.xcodeproj */, - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, - F04672B9D3824B768FCF6F71 /* RNVectorIcons.xcodeproj */, - 06F26555B4054594966B6B08 /* RNRandomBytes.xcodeproj */, - 0D4A52AE301842E1B2533BD3 /* RealmReact.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 832341B11AAA6A8300B99B32 /* Products */ = { - isa = PBXGroup; - children = ( - 832341B51AAA6A8300B99B32 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - E343FE921C971D5400C01DB5 /* Frameworks */, - 13B07FAE1A68108700A75B9A /* Messenger */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 00E356EF1AD99517003FC87E /* MessengerTests */, - 83CBBA001A601CBA00E9B192 /* Products */, - A72CA0FB822D450E98649ACB /* Resources */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* Messenger.app */, - 00E356EE1AD99517003FC87E /* MessengerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - E343FE801C96F4DA00C01DB5 /* Products */ = { - isa = PBXGroup; - children = ( - E343FE841C96F4DA00C01DB5 /* libRCTContacts.a */, - ); - name = Products; - sourceTree = ""; - }; - E343FE861C96F4E200C01DB5 /* Products */ = { - isa = PBXGroup; - children = ( - E343FE8B1C96F4E200C01DB5 /* libRNI18n.a */, - E343FE8D1C96F4E200C01DB5 /* RNI18nTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - E343FE921C971D5400C01DB5 /* Frameworks */ = { - isa = PBXGroup; - children = ( - E343FE901C971D4200C01DB5 /* Geth.framework */, - 8003209F13C24D35AABD3933 /* libc++.tbd */, - 313A77B1D7804DBDBE6FF6F5 /* libz.tbd */, - ); - name = Frameworks; - sourceTree = ""; - }; - A72CA0FB822D450E98649ACB /* Resources */ = { - isa = PBXGroup; - children = ( - 93C8E68B46DA4C0A98C39F9D /* Entypo.ttf */, - 38E7B297EE0748008056796A /* EvilIcons.ttf */, - 2CF9FE7E98A3454D980A0BED /* FontAwesome.ttf */, - 5A831378BBA14830A9540B8C /* Foundation.ttf */, - 5F085711AC4A4BA3887A4655 /* Ionicons.ttf */, - 0063E967269B49EE80DC3A77 /* MaterialIcons.ttf */, - A74F40BECFF8439FB1F41D8A /* Octicons.ttf */, - BCEC948817B84597AC493C0B /* Zocial.ttf */, - ); - name = Resources; - path = ""; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* MessengerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MessengerTests" */; - buildPhases = ( - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = MessengerTests; - productName = MessengerTests; - productReference = 00E356EE1AD99517003FC87E /* MessengerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 13B07F861A680F5B00A75B9A /* Messenger */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Messenger" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Messenger; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* Messenger.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 720; - ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Messenger" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; - ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - }, - { - ProductGroup = E343FE801C96F4DA00C01DB5 /* Products */; - ProjectRef = E343FE7F1C96F4DA00C01DB5 /* RCTContacts.xcodeproj */; - }, - { - ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; - ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - }, - { - ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; - ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; - ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - }, - { - ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; - ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; - ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - }, - { - ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; - ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; - ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - }, - { - ProductGroup = 139FDEE71B06529A00C62182 /* Products */; - ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 146834001AC3E56700842450 /* Products */; - ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; - }, - { - ProductGroup = E343FE861C96F4E200C01DB5 /* Products */; - ProjectRef = E343FE851C96F4E200C01DB5 /* RNI18n.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* Messenger */, - 00E356ED1AD99517003FC87E /* MessengerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTActionSheet.a; - remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTGeolocation.a; - remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVibration.a; - remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 146834041AC3E56700842450 /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTLinking.a; - remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E343FE841C96F4DA00C01DB5 /* libRCTContacts.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTContacts.a; - remoteRef = E343FE831C96F4DA00C01DB5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E343FE8B1C96F4E200C01DB5 /* libRNI18n.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRNI18n.a; - remoteRef = E343FE8A1C96F4E200C01DB5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E343FE8D1C96F4E200C01DB5 /* RNI18nTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = RNI18nTests.xctest; - remoteRef = E343FE8C1C96F4E200C01DB5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - 2171A55FE83747678065F72A /* Entypo.ttf in Resources */, - BDF23426F8854AC2A0416A03 /* EvilIcons.ttf in Resources */, - 221509FA6D5443C0B5215C52 /* FontAwesome.ttf in Resources */, - A33AC3EAFBC04C0B93BD2EC4 /* Foundation.ttf in Resources */, - 1A4121618C94428C9F0DF1FE /* Ionicons.ttf in Resources */, - 59FCD4F0208949BE80CC72BF /* MaterialIcons.ttf in Resources */, - 4452A93C123F4392A99A7719 /* Octicons.ttf in Resources */, - 1AE91A3E2C1A43EFA90C1CD7 /* Zocial.ttf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00E356F31AD99517003FC87E /* MessengerTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* Messenger */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = Messenger; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = MessengerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Messenger.app/Messenger"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - ); - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = MessengerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Messenger.app/Messenger"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - ); - }; - name = Release; - }; - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEAD_CODE_STRIPPING = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/target/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", - "$(SRCROOT)/../node_modules/react-native-randombytes", - "$(SRCROOT)/../node_modules/realm/src/**", - ); - INFOPLIST_FILE = Messenger/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = Messenger; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/target/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", - "$(SRCROOT)/../node_modules/react-native-randombytes", - "$(SRCROOT)/../node_modules/realm/src/**", - ); - INFOPLIST_FILE = Messenger/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = Messenger; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", - "$(SRCROOT)/../node_modules/react-native-randombytes", - "$(SRCROOT)/../node_modules/realm/src/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", - "$(SRCROOT)/../node_modules/react-native-randombytes", - "$(SRCROOT)/../node_modules/realm/src/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MessengerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Messenger" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Messenger" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/ios/Messenger/Base.lproj/LaunchScreen.xib b/ios/Messenger/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 3841771591..0000000000 --- a/ios/Messenger/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Messenger/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Messenger/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f746..0000000000 --- a/ios/Messenger/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/ios/Messenger/Info.plist b/ios/Messenger/Info.plist deleted file mode 100644 index 682480a6e6..0000000000 --- a/ios/Messenger/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSLocationWhenInUseUsageDescription - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000000..50b20b7584 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,16 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'StatusIm' do + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + + # Pods for StatusIm + pod 'Instabug', '~> 6.0.0' + + target 'StatusImTests' do + inherit! :search_paths + # Pods for testing + end + +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000000..0dc083d449 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,12 @@ +PODS: + - Instabug (6.0.4) + +DEPENDENCIES: + - Instabug (~> 6.0.0) + +SPEC CHECKSUMS: + Instabug: 7431c30f6e8883b2e6a9ce4b922ed51cbb25ad19 + +PODFILE CHECKSUM: da0b8d375a7fe107abe5b59bce9340e2fd528084 + +COCOAPODS: 1.1.1 diff --git a/ios/SF-UI-Display-Medium.otf b/ios/SF-UI-Display-Medium.otf new file mode 100644 index 0000000000..dc2df0571e Binary files /dev/null and b/ios/SF-UI-Display-Medium.otf differ diff --git a/ios/SF-UI-Display-Regular.otf b/ios/SF-UI-Display-Regular.otf new file mode 100644 index 0000000000..14c96528c4 Binary files /dev/null and b/ios/SF-UI-Display-Regular.otf differ diff --git a/ios/SF-UI-Display-Semibold.otf b/ios/SF-UI-Display-Semibold.otf new file mode 100644 index 0000000000..4ea7ea73ec Binary files /dev/null and b/ios/SF-UI-Display-Semibold.otf differ diff --git a/ios/SF-UI-Display-Thin.otf b/ios/SF-UI-Display-Thin.otf new file mode 100644 index 0000000000..f02dcda5e4 Binary files /dev/null and b/ios/SF-UI-Display-Thin.otf differ diff --git a/ios/SF-UI-Text-Bold.otf b/ios/SF-UI-Text-Bold.otf new file mode 100644 index 0000000000..4e9fc0492a Binary files /dev/null and b/ios/SF-UI-Text-Bold.otf differ diff --git a/ios/SF-UI-Text-Light.otf b/ios/SF-UI-Text-Light.otf new file mode 100644 index 0000000000..313f439f2c Binary files /dev/null and b/ios/SF-UI-Text-Light.otf differ diff --git a/ios/SF-UI-Text-Medium.otf b/ios/SF-UI-Text-Medium.otf new file mode 100644 index 0000000000..a638385309 Binary files /dev/null and b/ios/SF-UI-Text-Medium.otf differ diff --git a/ios/SF-UI-Text-Regular.otf b/ios/SF-UI-Text-Regular.otf new file mode 100644 index 0000000000..a36c8f62b0 Binary files /dev/null and b/ios/SF-UI-Text-Regular.otf differ diff --git a/ios/StatusIm.xcodeproj/project.pbxproj b/ios/StatusIm.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..06442fed52 --- /dev/null +++ b/ios/StatusIm.xcodeproj/project.pbxproj @@ -0,0 +1,1909 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; + 00E356F31AD99517003FC87E /* StatusImTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StatusImTests.m */; }; + 0F942CF509F74CCDB5CB35B0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 54E2B86FB12D4CC49DA05C69 /* MaterialIcons.ttf */; }; + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 2028DFF91D4275B600227DCD /* SF-UI-Display-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF51D4275B600227DCD /* SF-UI-Display-Medium.otf */; }; + 2028DFFA1D4275B600227DCD /* SF-UI-Display-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF61D4275B600227DCD /* SF-UI-Display-Regular.otf */; }; + 2028DFFB1D4275B600227DCD /* SF-UI-Display-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF71D4275B600227DCD /* SF-UI-Display-Semibold.otf */; }; + 2028DFFC1D4275B600227DCD /* SF-UI-Display-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2028DFF81D4275B600227DCD /* SF-UI-Display-Thin.otf */; }; + 20AB9EC61D47CC0300E7FD9C /* libRCTStatus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 201067C41D4789F700FA83B6 /* libRCTStatus.a */; }; + 20B6B6841D92C42600CC5C6A /* RSKImageCropper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */; }; + 20B6B6851D92C42600CC5C6A /* RSKImageCropper.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 20B6B6871D92C42600CC5C6A /* QBImagePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */; }; + 20B6B6881D92C42600CC5C6A /* QBImagePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 213311F38CA74CE280FD09AD /* libRNI18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 52F6ED6465184513A082652B /* libRNI18n.a */; }; + 22118DE1207A419FBFE7B62D /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD48A32459B64E96843BB238 /* libRealmReact.a */; }; + 25DC9C9DC25846BD8D084888 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B9A886A2CB448B1ABA0EB62 /* libc++.tbd */; }; + 3E15DFEC1F6F4D7CAE088F49 /* libTcpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2A4E93F6B154AEFA3485B45 /* libTcpSockets.a */; }; + 4D3D740D5EFA4F8592B048D7 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF1CD4C3D1254774ACCAE4E8 /* libBVLinearGradient.a */; }; + 4FFAE7B0414A463991039A2E /* libRNRandomBytes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C55F15EB4D4DAF9202A662 /* libRNRandomBytes.a */; }; + 5F8585D411844E5981B94F40 /* libRNInstabug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC426A98043452BB6F9C134 /* libRNInstabug.a */; }; + 67F099D82798449FADF8358A /* libRCTOrientation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5535217F57E44D77AA9CF083 /* libRCTOrientation.a */; }; + 6CE6374707AC4EC788354DD1 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 11632AA0A5F84F029DD91797 /* libRNVectorIcons.a */; }; + 74242ACAF37A48D0BFACDE82 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2756305FAFF144C4A6B0A039 /* Zocial.ttf */; }; + 81C6E6AE0AA739BE9D87C1D0 /* libPods-StatusImTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC1CBCFE6C906043D6CCEEE1 /* libPods-StatusImTests.a */; }; + 82E689BAF9FB43C8AC6FF1CA /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CEB0E2659D1A4F5FA842057A /* EvilIcons.ttf */; }; + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; + 8E55E6877F950B81C8D711C5 /* libPods-StatusIm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 101A4045637A2ADF57D28EF5 /* libPods-StatusIm.a */; }; + 9E0B01A11DDC5DA7002B0359 /* SF-UI-Text-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9E0B01A01DDC5DA7002B0359 /* SF-UI-Text-Light.otf */; }; + 9E3AB6D01D87DB2B008846B4 /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E3AB6C61D87DA2B008846B4 /* libReact-Native-Webview-Bridge.a */; }; + 9E7C64731E03FDDE004C7042 /* libRCTCamera.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D1151D3F74CD00B70F14 /* libRCTCamera.a */; }; + 9E7C64991E03FDE5004C7042 /* libRCTContacts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20B7D10A1D3F74CD00B70F14 /* libRCTContacts.a */; }; + 9ED2F45E1D9D535A00B36508 /* SF-UI-Text-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */; }; + 9ED2F45F1D9D535A00B36508 /* SF-UI-Text-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F45C1D9D52C100B36508 /* SF-UI-Text-Medium.otf */; }; + 9ED2F4611D9D579900B36508 /* SF-UI-Text-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */; }; + 9EE89E271E03FCB7007D3C25 /* libSplashScreen.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7F21DE718EF00D694FF /* libSplashScreen.a */; }; + 9EE89E291E03FD3D007D3C25 /* libRNShare.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FA1DE7190C00D694FF /* libRNShare.a */; }; + 9EE89E2D1E03FD9F007D3C25 /* libimageCropPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20A5C9531D927137002C4965 /* libimageCropPicker.a */; }; + A6AF670051B842249D520C7B /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7ED174A34D7D42358313368B /* Foundation.ttf */; }; + AD5063BC2B2A4C52ACE0A0B4 /* libUdpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A96279092BEC4C4B93914F48 /* libUdpSockets.a */; }; + AE97D4B08C9F4821B8E9C50B /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 359B076A658B4FBAB5128B03 /* Ionicons.ttf */; }; + B24FC7FD1DE7195700D694FF /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FC1DE7195700D694FF /* Social.framework */; }; + B24FC7FF1DE7195F00D694FF /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FE1DE7195F00D694FF /* MessageUI.framework */; }; + B2A5F45C1DEC36BB00174F4D /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2A5F4381DEC36B200174F4D /* libRCTAnimation.a */; }; + B2F2D1BC1D9D531B00B7B453 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B2F2D1BB1D9D531B00B7B453 /* Images.xcassets */; }; + BA68A2377A20496EA737000D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E586E1B0E544F64AA9F5BD1 /* libz.tbd */; }; + C3EE9AEA6F77464588FBAA64 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7B5870D9ED504F32B6A09C35 /* FontAwesome.ttf */; }; + CE4E31B31D8695250033ED64 /* Statusgo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4E31B21D8695250033ED64 /* Statusgo.framework */; }; + D28AEFB4C39548EB80416889 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 52E205D210BC48B7A553BB62 /* Entypo.ttf */; }; + E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BEE3436791D42248F853999 /* libRCTImageResizer.a */; }; + EF2B5857B4A34E0C9707FB3F /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B3B19223008342D096AA356E /* Octicons.ttf */; }; + FD4F213C3873473CB703B1D2 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674B3D9595A047AB8D518F4E /* libRNFS.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTActionSheet; + }; + 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTGeolocation; + }; + 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTVibration; + }; + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = StatusIm; + }; + 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; + 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; + }; + 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; + remoteInfo = React; + }; + 2010676C1D477F5E00FA83B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = TcpSockets; + }; + 201067711D477F5E00FA83B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = UdpSockets; + }; + 201067C31D4789F700FA83B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 206C9F3A1D474E910063E3E6; + remoteInfo = RCTStatus; + }; + 20A5C9521D927137002C4965 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 20A5C94B1D927137002C4965 /* imageCropPicker.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3400A8081CEB54A6008A0BC7; + remoteInfo = imageCropPicker; + }; + 20B7D0FD1D3F74CC00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 45FB5F523DE04BDE9877869C /* RNRandomBytes.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 73EEC9391BFE4B1D00D468EB; + remoteInfo = RNRandomBytes; + }; + 20B7D1021D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8AE71EE8751F4652B13BFE83 /* RNVectorIcons.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5DBEB1501B18CEA900B34395; + remoteInfo = RNVectorIcons; + }; + 20B7D1091D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 09F98D1521C24548AC72563A /* RCTContacts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1441618E1BD0A79300FA4F59; + remoteInfo = RCTContacts; + }; + 20B7D10D1D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 46E2F6052EB44C698C680894 /* RNI18n.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = CDD7BF781B2D5125006FDA75; + remoteInfo = RNI18n; + }; + 20B7D10F1D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 46E2F6052EB44C698C680894 /* RNI18n.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = CDD7BF831B2D5126006FDA75; + remoteInfo = RNI18nTests; + }; + 20B7D1141D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4107012F1ACB723B00C6AA39; + remoteInfo = RCTCamera; + }; + 20B7D1191D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 305F194186D848FDB07AF34C /* RNFS.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F12AFB9B1ADAF8F800E0535D; + remoteInfo = RNFS; + }; + 20B7D11E1D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 807594C429CA44128AB5666B /* BVLinearGradient.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = BVLinearGradient; + }; + 20B7D1231D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43A6FA689D844B0BAF3AA8B4 /* RCTOrientation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTOrientation; + }; + 20B7D1281D3F74CD00B70F14 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F090E261B9854867A728CE4F /* RealmReact.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F60690131CA2766F0003FB26; + remoteInfo = RealmReact; + }; + 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; + 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5119B1A9E6C1200147676; + remoteInfo = RCTText; + }; + 9E3AB6C51D87DA2B008846B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9E3AB6B21D87DA2A008846B4 /* React-Native-Webview-Bridge.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4114DC4C1C187C3A003CD988; + remoteInfo = "React-Native-Webview-Bridge"; + }; + 9E3F1BED1DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; + remoteInfo = "RCTImage-tvOS"; + }; + 9E3F1BF21DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28471D9B043800D4039D; + remoteInfo = "RCTLinking-tvOS"; + }; + 9E3F1BF61DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28541D9B044C00D4039D; + remoteInfo = "RCTNetwork-tvOS"; + }; + 9E3F1BFB1DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28611D9B046600D4039D; + remoteInfo = "RCTSettings-tvOS"; + }; + 9E3F1C001DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A287B1D9B048500D4039D; + remoteInfo = "RCTText-tvOS"; + }; + 9E3F1C051DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28881D9B049200D4039D; + remoteInfo = "RCTWebSocket-tvOS"; + }; + 9E3F1C0A1DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28131D9B038B00D4039D; + remoteInfo = "React-tvOS"; + }; + 9E3F1C111DDAE781005E4779 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F3548417D8DA4362B6796A54 /* RNInstabug.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 24DF11ED1DA3A2B90056F77C; + remoteInfo = RNInstabug; + }; + 9EC013781E06FB1900155B5C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9EC0135C1E06FB1900155B5C /* RCTWKWebView.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 0974579A1D2A440A000D9368; + remoteInfo = RCTWKWebView; + }; + B24FC7F11DE718EF00D694FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3A7EB0491DD9CABC00A4FCC8 /* SplashScreen.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D7682761D8E76B80014119E; + remoteInfo = SplashScreen; + }; + B24FC7F91DE7190C00D694FF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B24FC7F51DE7190C00D694FF /* RNShare.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RNShare; + }; + B2A5F4371DEC36B200174F4D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B2A5F42F1DEC36B200174F4D /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTAnimation; + }; + B2A5F4391DEC36B200174F4D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B2A5F42F1DEC36B200174F4D /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28201D9B03D100D4039D; + remoteInfo = "RCTAnimation-tvOS"; + }; + CE4E319D1D8693090033ED64 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5D72D2E81C16249000E22EC1; + remoteInfo = RCTImageResizer; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 20B6B6891D92C42700CC5C6A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 20B6B6851D92C42600CC5C6A /* RSKImageCropper.framework in Embed Frameworks */, + 20B6B6881D92C42600CC5C6A /* QBImagePicker.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; + 00E356EE1AD99517003FC87E /* StatusImTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StatusImTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* StatusImTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StatusImTests.m; sourceTree = ""; }; + 064351D2FD901F8B6C9AE2A5 /* Pods-StatusImTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusImTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StatusImTests/Pods-StatusImTests.debug.xcconfig"; sourceTree = ""; }; + 09F98D1521C24548AC72563A /* RCTContacts.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTContacts.xcodeproj; path = "../node_modules/react-native-contacts/ios/RCTContacts.xcodeproj"; sourceTree = ""; }; + 101A4045637A2ADF57D28EF5 /* libPods-StatusIm.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-StatusIm.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 11632AA0A5F84F029DD91797 /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* StatusIm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StatusIm.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = StatusIm/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = StatusIm/AppDelegate.m; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = StatusIm/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = StatusIm/main.m; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 2028DFF51D4275B600227DCD /* SF-UI-Display-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Display-Medium.otf"; sourceTree = ""; }; + 2028DFF61D4275B600227DCD /* SF-UI-Display-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Display-Regular.otf"; sourceTree = ""; }; + 2028DFF71D4275B600227DCD /* SF-UI-Display-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Display-Semibold.otf"; sourceTree = ""; }; + 2028DFF81D4275B600227DCD /* SF-UI-Display-Thin.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Display-Thin.otf"; sourceTree = ""; }; + 20A5C94B1D927137002C4965 /* imageCropPicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = imageCropPicker.xcodeproj; path = "../node_modules/react-native-image-crop-picker/ios/imageCropPicker.xcodeproj"; sourceTree = ""; }; + 20A5C96C1D92715E002C4965 /* RSKImageCropper.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSKImageCropper.framework; path = "../node_modules/react-native-image-crop-picker/ios/RSKImageCropper/build/Debug-iphoneos/RSKImageCropper.framework"; sourceTree = ""; }; + 20A5C96E1D92716C002C4965 /* QBImagePicker.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QBImagePicker.framework; path = "../node_modules/react-native-image-crop-picker/ios/QBImagePicker/build/Debug-iphoneos/QBImagePicker.framework"; sourceTree = ""; }; + 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSKImageCropper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = QBImagePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2756305FAFF144C4A6B0A039 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; }; + 2BEE3436791D42248F853999 /* libRCTImageResizer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTImageResizer.a; sourceTree = ""; }; + 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = UdpSockets.xcodeproj; path = "../node_modules/react-native-udp/ios/UdpSockets.xcodeproj"; sourceTree = ""; }; + 305F194186D848FDB07AF34C /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = ""; }; + 359B076A658B4FBAB5128B03 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = ""; }; + 38A44830EC5708E89387F641 /* Pods-StatusIm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.release.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.release.xcconfig"; sourceTree = ""; }; + 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = TcpSockets.xcodeproj; path = "../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj"; sourceTree = ""; }; + 3A7EB0491DD9CABC00A4FCC8 /* SplashScreen.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SplashScreen.xcodeproj; path = "../node_modules/react-native-splash-screen/ios/SplashScreen.xcodeproj"; sourceTree = ""; }; + 439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTStatus.xcodeproj; path = "../modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj"; sourceTree = ""; }; + 43A6FA689D844B0BAF3AA8B4 /* RCTOrientation.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTOrientation.xcodeproj; path = "../node_modules/react-native-orientation/iOS/RCTOrientation.xcodeproj"; sourceTree = ""; }; + 45FB5F523DE04BDE9877869C /* RNRandomBytes.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNRandomBytes.xcodeproj; path = "../node_modules/react-native-randombytes/RNRandomBytes.xcodeproj"; sourceTree = ""; }; + 46E2F6052EB44C698C680894 /* RNI18n.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNI18n.xcodeproj; path = "../node_modules/react-native-i18n/RNI18n.xcodeproj"; sourceTree = ""; }; + 4E586E1B0E544F64AA9F5BD1 /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 4EC426A98043452BB6F9C134 /* libRNInstabug.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNInstabug.a; sourceTree = ""; }; + 52E205D210BC48B7A553BB62 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; }; + 52F6ED6465184513A082652B /* libRNI18n.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNI18n.a; sourceTree = ""; }; + 54E2B86FB12D4CC49DA05C69 /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = ""; }; + 5535217F57E44D77AA9CF083 /* libRCTOrientation.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTOrientation.a; sourceTree = ""; }; + 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTImageResizer.xcodeproj; path = "../node_modules/react-native-image-resizer/ios/RCTImageResizer.xcodeproj"; sourceTree = ""; }; + 674B3D9595A047AB8D518F4E /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 78C55F15EB4D4DAF9202A662 /* libRNRandomBytes.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNRandomBytes.a; sourceTree = ""; }; + 7B5870D9ED504F32B6A09C35 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; + 7ED174A34D7D42358313368B /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; + 807594C429CA44128AB5666B /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; + 8AE71EE8751F4652B13BFE83 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = ""; }; + 8B9A886A2CB448B1ABA0EB62 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + 9E0B01A01DDC5DA7002B0359 /* SF-UI-Text-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Light.otf"; sourceTree = ""; }; + 9E3AB6B21D87DA2A008846B4 /* React-Native-Webview-Bridge.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "React-Native-Webview-Bridge.xcodeproj"; path = "../node_modules/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj"; sourceTree = ""; }; + 9EC0135C1E06FB1900155B5C /* RCTWKWebView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWKWebView.xcodeproj; path = "../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView.xcodeproj"; sourceTree = ""; }; + 9ED2F45C1D9D52C100B36508 /* SF-UI-Text-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Medium.otf"; sourceTree = ""; }; + 9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Regular.otf"; sourceTree = ""; }; + 9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Bold.otf"; sourceTree = ""; }; + 9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTCamera.xcodeproj; path = "../node_modules/react-native-camera/ios/RCTCamera.xcodeproj"; sourceTree = ""; }; + A96279092BEC4C4B93914F48 /* libUdpSockets.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libUdpSockets.a; sourceTree = ""; }; + ACA66A8F16CD2FE21F38738B /* Pods-StatusIm.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.debug.xcconfig"; sourceTree = ""; }; + B24FC7F51DE7190C00D694FF /* RNShare.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNShare.xcodeproj; path = "../node_modules/react-native-share/ios/RNShare.xcodeproj"; sourceTree = ""; }; + B24FC7FC1DE7195700D694FF /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; + B24FC7FE1DE7195F00D694FF /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; + B2A5F42F1DEC36B200174F4D /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; + B2F2D1BB1D9D531B00B7B453 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = StatusIm/Images.xcassets; sourceTree = ""; }; + B3B19223008342D096AA356E /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; }; + C2A4E93F6B154AEFA3485B45 /* libTcpSockets.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libTcpSockets.a; sourceTree = ""; }; + CD48A32459B64E96843BB238 /* libRealmReact.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRealmReact.a; sourceTree = ""; }; + CE4E31B21D8695250033ED64 /* Statusgo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Statusgo.framework; path = "../modules/react-native-status/ios/RCTStatus/Statusgo.framework"; sourceTree = ""; }; + CEB0E2659D1A4F5FA842057A /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; + D489EE8D5F52DA10AC715727 /* Pods-StatusImTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusImTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-StatusImTests/Pods-StatusImTests.release.xcconfig"; sourceTree = ""; }; + DF1CD4C3D1254774ACCAE4E8 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; + F090E261B9854867A728CE4F /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; }; + F3548417D8DA4362B6796A54 /* RNInstabug.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNInstabug.xcodeproj; path = "../node_modules/instabug-reactnative/ios/RNInstabug.xcodeproj"; sourceTree = ""; }; + FC1CBCFE6C906043D6CCEEE1 /* libPods-StatusImTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-StatusImTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81C6E6AE0AA739BE9D87C1D0 /* libPods-StatusImTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9EE89E271E03FCB7007D3C25 /* libSplashScreen.a in Frameworks */, + B2A5F45C1DEC36BB00174F4D /* libRCTAnimation.a in Frameworks */, + B24FC7FF1DE7195F00D694FF /* MessageUI.framework in Frameworks */, + B24FC7FD1DE7195700D694FF /* Social.framework in Frameworks */, + 9EE89E291E03FD3D007D3C25 /* libRNShare.a in Frameworks */, + 9EE89E2D1E03FD9F007D3C25 /* libimageCropPicker.a in Frameworks */, + 9E3AB6D01D87DB2B008846B4 /* libReact-Native-Webview-Bridge.a in Frameworks */, + 20B6B6841D92C42600CC5C6A /* RSKImageCropper.framework in Frameworks */, + CE4E31B31D8695250033ED64 /* Statusgo.framework in Frameworks */, + 20AB9EC61D47CC0300E7FD9C /* libRCTStatus.a in Frameworks */, + 20B6B6871D92C42600CC5C6A /* QBImagePicker.framework in Frameworks */, + 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + 9E7C64731E03FDDE004C7042 /* libRCTCamera.a in Frameworks */, + 9E7C64991E03FDE5004C7042 /* libRCTContacts.a in Frameworks */, + FD4F213C3873473CB703B1D2 /* libRNFS.a in Frameworks */, + 213311F38CA74CE280FD09AD /* libRNI18n.a in Frameworks */, + 4D3D740D5EFA4F8592B048D7 /* libBVLinearGradient.a in Frameworks */, + 67F099D82798449FADF8358A /* libRCTOrientation.a in Frameworks */, + 4FFAE7B0414A463991039A2E /* libRNRandomBytes.a in Frameworks */, + 6CE6374707AC4EC788354DD1 /* libRNVectorIcons.a in Frameworks */, + 22118DE1207A419FBFE7B62D /* libRealmReact.a in Frameworks */, + 25DC9C9DC25846BD8D084888 /* libc++.tbd in Frameworks */, + BA68A2377A20496EA737000D /* libz.tbd in Frameworks */, + 3E15DFEC1F6F4D7CAE088F49 /* libTcpSockets.a in Frameworks */, + AD5063BC2B2A4C52ACE0A0B4 /* libUdpSockets.a in Frameworks */, + E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */, + 5F8585D411844E5981B94F40 /* libRNInstabug.a in Frameworks */, + 8E55E6877F950B81C8D711C5 /* libPods-StatusIm.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B61ABCB90400DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302BC1ABCB91800DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, + 9E3F1BEE1DDAE781005E4779 /* libRCTImage-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302D41ABCB9D200DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, + 9E3F1BF71DDAE781005E4779 /* libRCTNetwork-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302E01ABCB9EE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, + ); + name = Products; + sourceTree = ""; + }; + 00E356EF1AD99517003FC87E /* StatusImTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* StatusImTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = StatusImTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 139105B71AF99BAD00B5F7CC /* Products */ = { + isa = PBXGroup; + children = ( + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, + 9E3F1BFC1DDAE781005E4779 /* libRCTSettings-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 139FDEE71B06529A00C62182 /* Products */ = { + isa = PBXGroup; + children = ( + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, + 9E3F1C061DDAE781005E4779 /* libRCTWebSocket-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* StatusIm */ = { + isa = PBXGroup; + children = ( + B2F2D1BB1D9D531B00B7B453 /* Images.xcassets */, + 008F07F21AC5B25A0029DE68 /* main.jsbundle */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = StatusIm; + sourceTree = ""; + }; + 146834001AC3E56700842450 /* Products */ = { + isa = PBXGroup; + children = ( + 146834041AC3E56700842450 /* libReact.a */, + 9E3F1C0B1DDAE781005E4779 /* libReact-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 1E7837547A9A40E18AD63CF3 /* Resources */ = { + isa = PBXGroup; + children = ( + 2028E0111D4275BD00227DCD /* SF */, + 52E205D210BC48B7A553BB62 /* Entypo.ttf */, + CEB0E2659D1A4F5FA842057A /* EvilIcons.ttf */, + 7B5870D9ED504F32B6A09C35 /* FontAwesome.ttf */, + 7ED174A34D7D42358313368B /* Foundation.ttf */, + 359B076A658B4FBAB5128B03 /* Ionicons.ttf */, + 54E2B86FB12D4CC49DA05C69 /* MaterialIcons.ttf */, + B3B19223008342D096AA356E /* Octicons.ttf */, + 2756305FAFF144C4A6B0A039 /* Zocial.ttf */, + ); + name = Resources; + sourceTree = ""; + }; + 201067551D477F5E00FA83B6 /* Products */ = { + isa = PBXGroup; + children = ( + 2010676D1D477F5E00FA83B6 /* libTcpSockets.a */, + ); + name = Products; + sourceTree = ""; + }; + 2010676E1D477F5E00FA83B6 /* Products */ = { + isa = PBXGroup; + children = ( + 201067721D477F5E00FA83B6 /* libUdpSockets.a */, + ); + name = Products; + sourceTree = ""; + }; + 201067BA1D4789F700FA83B6 /* Products */ = { + isa = PBXGroup; + children = ( + 201067C41D4789F700FA83B6 /* libRCTStatus.a */, + ); + name = Products; + sourceTree = ""; + }; + 2028E0111D4275BD00227DCD /* SF */ = { + isa = PBXGroup; + children = ( + 9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */, + 9E0B01A01DDC5DA7002B0359 /* SF-UI-Text-Light.otf */, + 9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */, + 9ED2F45C1D9D52C100B36508 /* SF-UI-Text-Medium.otf */, + 2028DFF51D4275B600227DCD /* SF-UI-Display-Medium.otf */, + 2028DFF61D4275B600227DCD /* SF-UI-Display-Regular.otf */, + 2028DFF71D4275B600227DCD /* SF-UI-Display-Semibold.otf */, + 2028DFF81D4275B600227DCD /* SF-UI-Display-Thin.otf */, + ); + name = SF; + sourceTree = ""; + }; + 20A5C94C1D927137002C4965 /* Products */ = { + isa = PBXGroup; + children = ( + 20A5C9531D927137002C4965 /* libimageCropPicker.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D0F01D3F74CC00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D0FE1D3F74CC00B70F14 /* libRNRandomBytes.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D0FF1D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D1031D3F74CD00B70F14 /* libRNVectorIcons.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1041D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D10E1D3F74CD00B70F14 /* libRNI18n.a */, + 20B7D1101D3F74CD00B70F14 /* RNI18nTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1061D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D10A1D3F74CD00B70F14 /* libRCTContacts.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1111D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D1151D3F74CD00B70F14 /* libRCTCamera.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1161D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D11A1D3F74CD00B70F14 /* libRNFS.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D11B1D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D11F1D3F74CD00B70F14 /* libBVLinearGradient.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1201D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D1241D3F74CD00B70F14 /* libRCTOrientation.a */, + ); + name = Products; + sourceTree = ""; + }; + 20B7D1251D3F74CD00B70F14 /* Products */ = { + isa = PBXGroup; + children = ( + 20B7D1291D3F74CD00B70F14 /* libRealmReact.a */, + ); + name = Products; + sourceTree = ""; + }; + 5C1C8762251D6EF495FB2384 /* Pods */ = { + isa = PBXGroup; + children = ( + ACA66A8F16CD2FE21F38738B /* Pods-StatusIm.debug.xcconfig */, + 38A44830EC5708E89387F641 /* Pods-StatusIm.release.xcconfig */, + 064351D2FD901F8B6C9AE2A5 /* Pods-StatusImTests.debug.xcconfig */, + D489EE8D5F52DA10AC715727 /* Pods-StatusImTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 78C398B11ACF4ADC00677621 /* Products */ = { + isa = PBXGroup; + children = ( + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, + 9E3F1BF31DDAE781005E4779 /* libRCTLinking-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + 9EC0135C1E06FB1900155B5C /* RCTWKWebView.xcodeproj */, + B2A5F42F1DEC36B200174F4D /* RCTAnimation.xcodeproj */, + B24FC7F51DE7190C00D694FF /* RNShare.xcodeproj */, + 3A7EB0491DD9CABC00A4FCC8 /* SplashScreen.xcodeproj */, + 20A5C94B1D927137002C4965 /* imageCropPicker.xcodeproj */, + 9E3AB6B21D87DA2A008846B4 /* React-Native-Webview-Bridge.xcodeproj */, + 146833FF1AC3E56700842450 /* React.xcodeproj */, + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, + 9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */, + 09F98D1521C24548AC72563A /* RCTContacts.xcodeproj */, + 305F194186D848FDB07AF34C /* RNFS.xcodeproj */, + 46E2F6052EB44C698C680894 /* RNI18n.xcodeproj */, + 807594C429CA44128AB5666B /* BVLinearGradient.xcodeproj */, + 43A6FA689D844B0BAF3AA8B4 /* RCTOrientation.xcodeproj */, + 45FB5F523DE04BDE9877869C /* RNRandomBytes.xcodeproj */, + 8AE71EE8751F4652B13BFE83 /* RNVectorIcons.xcodeproj */, + F090E261B9854867A728CE4F /* RealmReact.xcodeproj */, + 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */, + 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */, + 439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */, + 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */, + F3548417D8DA4362B6796A54 /* RNInstabug.xcodeproj */, + ); + name = Libraries; + sourceTree = ""; + }; + 832341B11AAA6A8300B99B32 /* Products */ = { + isa = PBXGroup; + children = ( + 832341B51AAA6A8300B99B32 /* libRCTText.a */, + 9E3F1C011DDAE781005E4779 /* libRCTText-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */, + 20B6B6831D92C42600CC5C6A /* RSKImageCropper.framework */, + 13B07FAE1A68108700A75B9A /* StatusIm */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* StatusImTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + A97BA941B2FB44B4B66EE6D3 /* Frameworks */, + 1E7837547A9A40E18AD63CF3 /* Resources */, + 5C1C8762251D6EF495FB2384 /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* StatusIm.app */, + 00E356EE1AD99517003FC87E /* StatusImTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 9E3AB6B31D87DA2A008846B4 /* Products */ = { + isa = PBXGroup; + children = ( + 9E3AB6C61D87DA2B008846B4 /* libReact-Native-Webview-Bridge.a */, + ); + name = Products; + sourceTree = ""; + }; + 9E3F1BE31DDAE781005E4779 /* Products */ = { + isa = PBXGroup; + children = ( + 9E3F1C121DDAE781005E4779 /* libRNInstabug.a */, + ); + name = Products; + sourceTree = ""; + }; + 9EC0135D1E06FB1900155B5C /* Products */ = { + isa = PBXGroup; + children = ( + 9EC013791E06FB1900155B5C /* libRCTWKWebView.a */, + ); + name = Products; + sourceTree = ""; + }; + A97BA941B2FB44B4B66EE6D3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B24FC7FE1DE7195F00D694FF /* MessageUI.framework */, + B24FC7FC1DE7195700D694FF /* Social.framework */, + 20A5C96E1D92716C002C4965 /* QBImagePicker.framework */, + 20A5C96C1D92715E002C4965 /* RSKImageCropper.framework */, + CE4E31B21D8695250033ED64 /* Statusgo.framework */, + 8B9A886A2CB448B1ABA0EB62 /* libc++.tbd */, + 4E586E1B0E544F64AA9F5BD1 /* libz.tbd */, + 101A4045637A2ADF57D28EF5 /* libPods-StatusIm.a */, + FC1CBCFE6C906043D6CCEEE1 /* libPods-StatusImTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + B24FC7BB1DE718EA00D694FF /* Products */ = { + isa = PBXGroup; + children = ( + B24FC7F21DE718EF00D694FF /* libSplashScreen.a */, + ); + name = Products; + sourceTree = ""; + }; + B24FC7F61DE7190C00D694FF /* Products */ = { + isa = PBXGroup; + children = ( + B24FC7FA1DE7190C00D694FF /* libRNShare.a */, + ); + name = Products; + sourceTree = ""; + }; + B2A5F4301DEC36B200174F4D /* Products */ = { + isa = PBXGroup; + children = ( + B2A5F4381DEC36B200174F4D /* libRCTAnimation.a */, + B2A5F43A1DEC36B200174F4D /* libRCTAnimation-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + CE4E31941D8693090033ED64 /* Products */ = { + isa = PBXGroup; + children = ( + CE4E319E1D8693090033ED64 /* libRCTImageResizer.a */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* StatusImTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "StatusImTests" */; + buildPhases = ( + 49A69B853BD9751F84878340 /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + 1986C962445001A2631E6AB0 /* [CP] Embed Pods Frameworks */, + 967469AF8BA27D5CEC47B13C /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = StatusImTests; + productName = StatusImTests; + productReference = 00E356EE1AD99517003FC87E /* StatusImTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* StatusIm */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StatusIm" */; + buildPhases = ( + 2EAC54E16AB243C3EBBFE1BA /* [CP] Check Pods Manifest.lock */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 20B6B6891D92C42700CC5C6A /* Embed Frameworks */, + 9E71BA90038083A3D24E18E9 /* [CP] Embed Pods Frameworks */, + E883D1F9B22B8292CC879292 /* [CP] Copy Pods Resources */, + E3914A731DF919ED00EBB515 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = StatusIm; + productName = "Hello World"; + productReference = 13B07F961A680F5B00A75B9A /* StatusIm.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + DevelopmentTeam = DTX7Z4U3YA; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = DTX7Z4U3YA; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "StatusIm" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 20B7D11B1D3F74CD00B70F14 /* Products */; + ProjectRef = 807594C429CA44128AB5666B /* BVLinearGradient.xcodeproj */; + }, + { + ProductGroup = 20A5C94C1D927137002C4965 /* Products */; + ProjectRef = 20A5C94B1D927137002C4965 /* imageCropPicker.xcodeproj */; + }, + { + ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; + ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + }, + { + ProductGroup = B2A5F4301DEC36B200174F4D /* Products */; + ProjectRef = B2A5F42F1DEC36B200174F4D /* RCTAnimation.xcodeproj */; + }, + { + ProductGroup = 20B7D1111D3F74CD00B70F14 /* Products */; + ProjectRef = 9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */; + }, + { + ProductGroup = 20B7D1061D3F74CD00B70F14 /* Products */; + ProjectRef = 09F98D1521C24548AC72563A /* RCTContacts.xcodeproj */; + }, + { + ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; + ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + }, + { + ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; + ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + }, + { + ProductGroup = CE4E31941D8693090033ED64 /* Products */; + ProjectRef = 5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */; + }, + { + ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; + ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + }, + { + ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; + ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + }, + { + ProductGroup = 20B7D1201D3F74CD00B70F14 /* Products */; + ProjectRef = 43A6FA689D844B0BAF3AA8B4 /* RCTOrientation.xcodeproj */; + }, + { + ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; + ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + }, + { + ProductGroup = 201067BA1D4789F700FA83B6 /* Products */; + ProjectRef = 439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */; + }, + { + ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; + ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + }, + { + ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; + ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + }, + { + ProductGroup = 139FDEE71B06529A00C62182 /* Products */; + ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + }, + { + ProductGroup = 9EC0135D1E06FB1900155B5C /* Products */; + ProjectRef = 9EC0135C1E06FB1900155B5C /* RCTWKWebView.xcodeproj */; + }, + { + ProductGroup = 9E3AB6B31D87DA2A008846B4 /* Products */; + ProjectRef = 9E3AB6B21D87DA2A008846B4 /* React-Native-Webview-Bridge.xcodeproj */; + }, + { + ProductGroup = 146834001AC3E56700842450 /* Products */; + ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; + }, + { + ProductGroup = 20B7D1251D3F74CD00B70F14 /* Products */; + ProjectRef = F090E261B9854867A728CE4F /* RealmReact.xcodeproj */; + }, + { + ProductGroup = 20B7D1161D3F74CD00B70F14 /* Products */; + ProjectRef = 305F194186D848FDB07AF34C /* RNFS.xcodeproj */; + }, + { + ProductGroup = 20B7D1041D3F74CD00B70F14 /* Products */; + ProjectRef = 46E2F6052EB44C698C680894 /* RNI18n.xcodeproj */; + }, + { + ProductGroup = 9E3F1BE31DDAE781005E4779 /* Products */; + ProjectRef = F3548417D8DA4362B6796A54 /* RNInstabug.xcodeproj */; + }, + { + ProductGroup = 20B7D0F01D3F74CC00B70F14 /* Products */; + ProjectRef = 45FB5F523DE04BDE9877869C /* RNRandomBytes.xcodeproj */; + }, + { + ProductGroup = B24FC7F61DE7190C00D694FF /* Products */; + ProjectRef = B24FC7F51DE7190C00D694FF /* RNShare.xcodeproj */; + }, + { + ProductGroup = 20B7D0FF1D3F74CD00B70F14 /* Products */; + ProjectRef = 8AE71EE8751F4652B13BFE83 /* RNVectorIcons.xcodeproj */; + }, + { + ProductGroup = B24FC7BB1DE718EA00D694FF /* Products */; + ProjectRef = 3A7EB0491DD9CABC00A4FCC8 /* SplashScreen.xcodeproj */; + }, + { + ProductGroup = 201067551D477F5E00FA83B6 /* Products */; + ProjectRef = 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */; + }, + { + ProductGroup = 2010676E1D477F5E00FA83B6 /* Products */; + ProjectRef = 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* StatusIm */, + 00E356ED1AD99517003FC87E /* StatusImTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTGeolocation.a; + remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVibration.a; + remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 146834041AC3E56700842450 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2010676D1D477F5E00FA83B6 /* libTcpSockets.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libTcpSockets.a; + remoteRef = 2010676C1D477F5E00FA83B6 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 201067721D477F5E00FA83B6 /* libUdpSockets.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libUdpSockets.a; + remoteRef = 201067711D477F5E00FA83B6 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 201067C41D4789F700FA83B6 /* libRCTStatus.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTStatus.a; + remoteRef = 201067C31D4789F700FA83B6 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20A5C9531D927137002C4965 /* libimageCropPicker.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libimageCropPicker.a; + remoteRef = 20A5C9521D927137002C4965 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D0FE1D3F74CC00B70F14 /* libRNRandomBytes.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNRandomBytes.a; + remoteRef = 20B7D0FD1D3F74CC00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D1031D3F74CD00B70F14 /* libRNVectorIcons.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNVectorIcons.a; + remoteRef = 20B7D1021D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D10A1D3F74CD00B70F14 /* libRCTContacts.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTContacts.a; + remoteRef = 20B7D1091D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D10E1D3F74CD00B70F14 /* libRNI18n.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNI18n.a; + remoteRef = 20B7D10D1D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D1101D3F74CD00B70F14 /* RNI18nTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = RNI18nTests.xctest; + remoteRef = 20B7D10F1D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D1151D3F74CD00B70F14 /* libRCTCamera.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTCamera.a; + remoteRef = 20B7D1141D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D11A1D3F74CD00B70F14 /* libRNFS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNFS.a; + remoteRef = 20B7D1191D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D11F1D3F74CD00B70F14 /* libBVLinearGradient.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libBVLinearGradient.a; + remoteRef = 20B7D11E1D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D1241D3F74CD00B70F14 /* libRCTOrientation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTOrientation.a; + remoteRef = 20B7D1231D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 20B7D1291D3F74CD00B70F14 /* libRealmReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRealmReact.a; + remoteRef = 20B7D1281D3F74CD00B70F14 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3AB6C61D87DA2B008846B4 /* libReact-Native-Webview-Bridge.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libReact-Native-Webview-Bridge.a"; + remoteRef = 9E3AB6C51D87DA2B008846B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1BEE1DDAE781005E4779 /* libRCTImage-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTImage-tvOS.a"; + remoteRef = 9E3F1BED1DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1BF31DDAE781005E4779 /* libRCTLinking-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTLinking-tvOS.a"; + remoteRef = 9E3F1BF21DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1BF71DDAE781005E4779 /* libRCTNetwork-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTNetwork-tvOS.a"; + remoteRef = 9E3F1BF61DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1BFC1DDAE781005E4779 /* libRCTSettings-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTSettings-tvOS.a"; + remoteRef = 9E3F1BFB1DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1C011DDAE781005E4779 /* libRCTText-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTText-tvOS.a"; + remoteRef = 9E3F1C001DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1C061DDAE781005E4779 /* libRCTWebSocket-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTWebSocket-tvOS.a"; + remoteRef = 9E3F1C051DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1C0B1DDAE781005E4779 /* libReact-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libReact-tvOS.a"; + remoteRef = 9E3F1C0A1DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9E3F1C121DDAE781005E4779 /* libRNInstabug.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNInstabug.a; + remoteRef = 9E3F1C111DDAE781005E4779 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 9EC013791E06FB1900155B5C /* libRCTWKWebView.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWKWebView.a; + remoteRef = 9EC013781E06FB1900155B5C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + B24FC7F21DE718EF00D694FF /* libSplashScreen.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libSplashScreen.a; + remoteRef = B24FC7F11DE718EF00D694FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + B24FC7FA1DE7190C00D694FF /* libRNShare.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNShare.a; + remoteRef = B24FC7F91DE7190C00D694FF /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + B2A5F4381DEC36B200174F4D /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = B2A5F4371DEC36B200174F4D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + B2A5F43A1DEC36B200174F4D /* libRCTAnimation-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTAnimation-tvOS.a"; + remoteRef = B2A5F4391DEC36B200174F4D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + CE4E319E1D8693090033ED64 /* libRCTImageResizer.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImageResizer.a; + remoteRef = CE4E319D1D8693090033ED64 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9ED2F4611D9D579900B36508 /* SF-UI-Text-Bold.otf in Resources */, + 9E0B01A11DDC5DA7002B0359 /* SF-UI-Text-Light.otf in Resources */, + 9ED2F45E1D9D535A00B36508 /* SF-UI-Text-Regular.otf in Resources */, + 9ED2F45F1D9D535A00B36508 /* SF-UI-Text-Medium.otf in Resources */, + 2028DFFA1D4275B600227DCD /* SF-UI-Display-Regular.otf in Resources */, + 2028DFF91D4275B600227DCD /* SF-UI-Display-Medium.otf in Resources */, + 2028DFFC1D4275B600227DCD /* SF-UI-Display-Thin.otf in Resources */, + 2028DFFB1D4275B600227DCD /* SF-UI-Display-Semibold.otf in Resources */, + D28AEFB4C39548EB80416889 /* Entypo.ttf in Resources */, + 82E689BAF9FB43C8AC6FF1CA /* EvilIcons.ttf in Resources */, + C3EE9AEA6F77464588FBAA64 /* FontAwesome.ttf in Resources */, + A6AF670051B842249D520C7B /* Foundation.ttf in Resources */, + B2F2D1BC1D9D531B00B7B453 /* Images.xcassets in Resources */, + AE97D4B08C9F4821B8E9C50B /* Ionicons.ttf in Resources */, + 0F942CF509F74CCDB5CB35B0 /* MaterialIcons.ttf in Resources */, + EF2B5857B4A34E0C9707FB3F /* Octicons.ttf in Resources */, + 74242ACAF37A48D0BFACDE82 /* Zocial.ttf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=\"node --max-old-space-size=4096\" \n../node_modules/react-native/packager/react-native-xcode.sh"; + }; + 1986C962445001A2631E6AB0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StatusImTests/Pods-StatusImTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 2EAC54E16AB243C3EBBFE1BA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 49A69B853BD9751F84878340 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 967469AF8BA27D5CEC47B13C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StatusImTests/Pods-StatusImTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9E71BA90038083A3D24E18E9 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E3914A731DF919ED00EBB515 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\n\n# This script automatically sets the version and short version string of\n# an Xcode project from the Git repository containing the project.\n#\n# To use this script in Xcode, add the script's path to a \"Run Script\" build\n# phase for your application target.\n\nset -o errexit\nset -o nounset\n\n# First, check for git in $PATH\nhash git 2>/dev/null || { echo >&2 \"Git required, not installed. Aborting build number update script.\"; exit 0; }\n\n# Alternatively, we could use Xcode's copy of the Git binary,\n# but old Xcodes don't have this.\n#GIT=$(xcrun -find git)\n\n# Run Script build phases that operate on product files of the target that defines them should use the value of this build setting [TARGET_BUILD_DIR]. But Run Script build phases that operate on product files of other targets should use “BUILT_PRODUCTS_DIR” instead.\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n\n# Build version (closest-tag-or-branch \"-\" commits-since-tag \"-\" short-hash dirty-flag)\nBUILD_VERSION=$(git describe --tags --always --dirty=+)\n\n# Use the latest tag for short version (expected tag format \"vn[.n[.n]]\")\n# or if there are no tags, we make up version 0.0.\nLATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null) || LATEST_TAG=\"HEAD\"\nif [ $LATEST_TAG = \"HEAD\" ]\nthen COMMIT_COUNT=$(git rev-list --count HEAD)\n LATEST_TAG=\"0.0.$COMMIT_COUNT\"\n COMMIT_COUNT_SINCE_TAG=0\nelse\n COMMIT_COUNT_SINCE_TAG=$(git rev-list --count ${LATEST_TAG}..)\n LATEST_TAG=${LATEST_TAG##v} # Remove the \"v\" from the front of the tag\nfi\nif [ $COMMIT_COUNT_SINCE_TAG = 0 ]; then\n SHORT_VERSION=\"$LATEST_TAG\"\nelse\n # increment final digit of tag and append \"d\" + commit-count-since-tag\n # e.g. commit after 1.0 is 1.1d1, commit after 1.0.0 is 1.0.1d1\n # this is the bit that requires /bin/bash\n OLD_IFS=$IFS\n IFS=\".\"\n VERSION_PARTS=($LATEST_TAG)\n LAST_PART=$((${#VERSION_PARTS[@]}-1))\n VERSION_PARTS[$LAST_PART]=$((${VERSION_PARTS[${LAST_PART}]}+1))\n SHORT_VERSION=\"${VERSION_PARTS[*]}d${COMMIT_COUNT_SINCE_TAG}\"\n IFS=$OLD_IFS\nfi\n \n# Bundle version (commits-on-master[-until-branch \".\" commits-on-branch])\n# Assumes that two release branches will not diverge from the same commit on master.\nif [ $(git rev-parse --abbrev-ref HEAD) = \"master\" ]; then\n MASTER_COMMIT_COUNT=$(git rev-list --count HEAD)\n BRANCH_COMMIT_COUNT=0\n BUNDLE_VERSION=\"$MASTER_COMMIT_COUNT\"\nelse\n MASTER_COMMIT_COUNT=$(git rev-list --count $(git rev-list master.. | tail -n 1)^)\n BRANCH_COMMIT_COUNT=$(git rev-list --count master..)\n if [ $BRANCH_COMMIT_COUNT = 0 ]\n then BUNDLE_VERSION=\"$MASTER_COMMIT_COUNT\"\n else BUNDLE_VERSION=\"${MASTER_COMMIT_COUNT}.${BRANCH_COMMIT_COUNT}\"\n fi\nfi\n \n# For debugging:\necho \"BUILD VERSION: $BUILD_VERSION\"\necho \"LATEST_TAG: $LATEST_TAG\"\necho \"COMMIT_COUNT_SINCE_TAG: $COMMIT_COUNT_SINCE_TAG\"\necho \"SHORT VERSION: $SHORT_VERSION\"\necho \"MASTER_COMMIT_COUNT: $MASTER_COMMIT_COUNT\"\necho \"BRANCH_COMMIT_COUNT: $BRANCH_COMMIT_COUNT\"\necho \"BUNDLE_VERSION: $BUNDLE_VERSION\"\n \n/usr/libexec/PlistBuddy -c \"Add :CFBundleBuildVersion string $BUILD_VERSION\" \"$INFO_PLIST\" 2>/dev/null || /usr/libexec/PlistBuddy -c \"Set :CFBundleBuildVersion $BUILD_VERSION\" \"$INFO_PLIST\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $SHORT_VERSION\" \"$INFO_PLIST\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $BUNDLE_VERSION\" \"$INFO_PLIST\""; + }; + E883D1F9B22B8292CC879292 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* StatusImTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* StatusIm */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 064351D2FD901F8B6C9AE2A5 /* Pods-StatusImTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = StatusImTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusIm.app/StatusIm"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D489EE8D5F52DA10AC715727 /* Pods-StatusImTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = StatusImTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusIm.app/StatusIm"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ACA66A8F16CD2FE21F38738B /* Pods-StatusIm.debug.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = NO; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + FRAMEWORK_SEARCH_PATHS = ( + "$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus", + "$(inherited)", + "$(PROJECT_DIR)/Pods/**", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + "$(SRCROOT)/../node_modules/react-native-camera/ios", + "$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts", + "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-i18n/RNI18n", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-orientation/iOS/RCTOrientation/**", + "$(SRCROOT)/../node_modules/react-native-randombytes", + "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", + "$(SRCROOT)/../node_modules/realm/src/**", + "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", + "$(SRCROOT)/../node_modules/react-native-udp/ios/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", + "$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug", + "$(SRCROOT)/../node_modules/react-native-splash-screen/ios", + ); + INFOPLIST_FILE = StatusIm/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-lc++", + "-ObjC", + ); + PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum; + PRODUCT_NAME = StatusIm; + VALID_ARCHS = "arm64 armv7 armv7s"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 38A44830EC5708E89387F641 /* Pods-StatusIm.release.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = NO; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + FRAMEWORK_SEARCH_PATHS = ( + "$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus", + "$(inherited)", + "$(PROJECT_DIR)/Pods/**", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + "$(SRCROOT)/../node_modules/react-native-camera/ios", + "$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts", + "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-i18n/RNI18n", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-orientation/iOS/RCTOrientation/**", + "$(SRCROOT)/../node_modules/react-native-randombytes", + "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", + "$(SRCROOT)/../node_modules/realm/src/**", + "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", + "$(SRCROOT)/../node_modules/react-native-udp/ios/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", + "$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug", + "$(SRCROOT)/../node_modules/react-native-splash-screen/ios", + ); + INFOPLIST_FILE = StatusIm/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-lc++", + "-ObjC", + ); + PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum; + PRODUCT_NAME = StatusIm; + VALID_ARCHS = "armv7 armv7s arm64"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + ENABLE_BITCODE = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + "$(SRCROOT)/../node_modules/react-native-camera/ios", + "$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts", + "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-i18n/RNI18n", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-orientation/iOS/RCTOrientation/**", + "$(SRCROOT)/../node_modules/react-native-randombytes", + "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", + "$(SRCROOT)/../node_modules/realm/src/**", + "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", + "$(SRCROOT)/../node_modules/react-native-udp/ios/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", + "$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug", + "$(SRCROOT)/../node_modules/react-native-splash-screen/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEVELOPMENT_TEAM = DTX7Z4U3YA; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + "$(SRCROOT)/../node_modules/react-native-camera/ios", + "$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts", + "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-i18n/RNI18n", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-orientation/iOS/RCTOrientation/**", + "$(SRCROOT)/../node_modules/react-native-randombytes", + "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", + "$(SRCROOT)/../node_modules/realm/src/**", + "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", + "$(SRCROOT)/../node_modules/react-native-udp/ios/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**", + "$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer", + "$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug", + "$(SRCROOT)/../node_modules/react-native-splash-screen/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "armv7 armv7s arm64"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "StatusImTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StatusIm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "StatusIm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/ios/Messenger.xcodeproj/xcshareddata/xcschemes/Messenger.xcscheme b/ios/StatusIm.xcodeproj/xcshareddata/xcschemes/StatusIm.xcscheme similarity index 77% rename from ios/Messenger.xcodeproj/xcshareddata/xcschemes/Messenger.xcscheme rename to ios/StatusIm.xcodeproj/xcshareddata/xcschemes/StatusIm.xcscheme index 0a73b677db..bca2f64833 100644 --- a/ios/Messenger.xcodeproj/xcshareddata/xcschemes/Messenger.xcscheme +++ b/ios/StatusIm.xcodeproj/xcshareddata/xcschemes/StatusIm.xcscheme @@ -1,6 +1,6 @@ + BuildableName = "StatusIm.app" + BlueprintName = "StatusIm" + ReferencedContainer = "container:StatusIm.xcodeproj"> + BuildableName = "StatusImTests.xctest" + BlueprintName = "StatusImTests" + ReferencedContainer = "container:StatusIm.xcodeproj"> @@ -47,9 +47,9 @@ + BuildableName = "StatusImTests.xctest" + BlueprintName = "StatusImTests" + ReferencedContainer = "container:StatusIm.xcodeproj"> @@ -57,9 +57,9 @@ + BuildableName = "StatusIm.app" + BlueprintName = "StatusIm" + ReferencedContainer = "container:StatusIm.xcodeproj"> @@ -80,9 +80,9 @@ + BuildableName = "StatusIm.app" + BlueprintName = "StatusIm" + ReferencedContainer = "container:StatusIm.xcodeproj"> @@ -99,9 +99,9 @@ + BuildableName = "StatusIm.app" + BlueprintName = "StatusIm" + ReferencedContainer = "container:StatusIm.xcodeproj"> diff --git a/ios/Messenger/AppDelegate.h b/ios/StatusIm/AppDelegate.h similarity index 100% rename from ios/Messenger/AppDelegate.h rename to ios/StatusIm/AppDelegate.h diff --git a/ios/Messenger/AppDelegate.m b/ios/StatusIm/AppDelegate.m similarity index 56% rename from ios/Messenger/AppDelegate.m rename to ios/StatusIm/AppDelegate.m index e270ac3689..8b7d8aea71 100644 --- a/ios/Messenger/AppDelegate.m +++ b/ios/StatusIm/AppDelegate.m @@ -9,48 +9,33 @@ #import "AppDelegate.h" +#import "RCTBundleURLProvider.h" #import "RCTRootView.h" +#import +#import "SplashScreen.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + signal(SIGPIPE, SIG_IGN); NSURL *jsCodeLocation; - /** - * Loading JavaScript code - uncomment the one you want. - * - * OPTION 1 - * Load from development server. Start the server from the repository root: - * - * $ npm start - * - * To run on device, change `localhost` to the IP address of your computer - * (you can get this by typing `ifconfig` into the terminal and selecting the - * `inet` value under `en0:`) and make sure your computer and iOS device are - * on the same Wi-Fi network. - */ - - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; - - /** - * OPTION 2 - * Load from pre-bundled file on disk. The static bundle is automatically - * generated by "Bundle React Native code and images" build step. - */ - -// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"Messenger" + moduleName:@"StatusIm" initialProperties:nil launchOptions:launchOptions]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; + [Instabug startWithToken:@"5534212f4a44f477c9ab270ab5cd2062" invocationEvent:IBGInvocationEventShake]; + [SplashScreen show]; return YES; } diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..7cea480fc8 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,148 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-Notification@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-Notification@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon.png", + "scale" : "1x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-Notification.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-Notification@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50.png", + "scale" : "1x" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50@2x.png", + "scale" : "2x" + }, + { + "size" : "72x72", + "idiom" : "ipad", + "filename" : "Icon-72.png", + "scale" : "1x" + }, + { + "size" : "72x72", + "idiom" : "ipad", + "filename" : "Icon-72@2x.png", + "scale" : "2x" + } + ] +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000000..0b91c5b015 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000000..3fd9dee3e5 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72.png new file mode 100644 index 0000000000..f0bcf8788a Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png new file mode 100644 index 0000000000..2f3dd91308 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000000..5dbff6b716 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000000..568c98bfa9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 0000000000..8e270c5ad9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification.png new file mode 100644 index 0000000000..2bddd6210b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@2x.png new file mode 100644 index 0000000000..efb30718c4 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@3x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@3x.png new file mode 100644 index 0000000000..a150cebf40 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Notification@3x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png new file mode 100644 index 0000000000..b8af75b5ab Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png new file mode 100644 index 0000000000..5ea266579c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png new file mode 100644 index 0000000000..e0723dbe91 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png new file mode 100644 index 0000000000..b4d877bf3c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png new file mode 100644 index 0000000000..a95454f7de Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000000..e15522bc62 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000000..e9b7da4343 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000000..1349487ef7 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon.png new file mode 100644 index 0000000000..b7f42855a8 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon.png differ diff --git a/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon@2x.png b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon@2x.png new file mode 100644 index 0000000000..e416c21c72 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/AppIcon.appiconset/Icon@2x.png differ diff --git a/ios/StatusIm/Images.xcassets/Contents.json b/ios/StatusIm/Images.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/Contents.json b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100755 index 0000000000..f4f35aa35b --- /dev/null +++ b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,66 @@ +{ + "images" : [ + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "736h", + "filename" : "LaunchScreen1242x2208.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "3x" + }, + { + "orientation" : "landscape", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "8.0", + "subtype" : "736h", + "scale" : "3x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "667h", + "filename" : "LaunchScreen750x1334-1.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "LaunchScreen_640_960.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "LaunchScreen-1.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "LaunchScreen_640_960-1.png", + "extent" : "full-screen", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "LaunchScreen-2.png", + "extent" : "full-screen", + "subtype" : "retina4", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-1.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-1.png new file mode 100644 index 0000000000..2cd83e9e3c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-1.png differ diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-2.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-2.png new file mode 100644 index 0000000000..2cd83e9e3c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen-2.png differ diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen1242x2208.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen1242x2208.png new file mode 100644 index 0000000000..83f7db19d2 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen1242x2208.png differ diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen750x1334-1.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen750x1334-1.png new file mode 100644 index 0000000000..35646a6588 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen750x1334-1.png differ diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960-1.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960-1.png new file mode 100644 index 0000000000..3b46c36976 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960-1.png differ diff --git a/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960.png b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960.png new file mode 100644 index 0000000000..3b46c36976 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/LaunchImage.launchimage/LaunchScreen_640_960.png differ diff --git a/ios/StatusIm/Images.xcassets/avatar.imageset/Contents.json b/ios/StatusIm/Images.xcassets/avatar.imageset/Contents.json new file mode 100644 index 0000000000..c995bd82c7 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/avatar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "avatar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/avatar.imageset/avatar.png b/ios/StatusIm/Images.xcassets/avatar.imageset/avatar.png new file mode 100644 index 0000000000..7fb2d0b42b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/avatar.imageset/avatar.png differ diff --git a/ios/StatusIm/Images.xcassets/console.imageset/Contents.json b/ios/StatusIm/Images.xcassets/console.imageset/Contents.json new file mode 100644 index 0000000000..d70f121274 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/console.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "console.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "console-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "console-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/console.imageset/console-1.png b/ios/StatusIm/Images.xcassets/console.imageset/console-1.png new file mode 100644 index 0000000000..70d539f951 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/console.imageset/console-1.png differ diff --git a/ios/StatusIm/Images.xcassets/console.imageset/console-2.png b/ios/StatusIm/Images.xcassets/console.imageset/console-2.png new file mode 100644 index 0000000000..c1f41d6edd Binary files /dev/null and b/ios/StatusIm/Images.xcassets/console.imageset/console-2.png differ diff --git a/ios/StatusIm/Images.xcassets/console.imageset/console.png b/ios/StatusIm/Images.xcassets/console.imageset/console.png new file mode 100644 index 0000000000..2ef4eeac8f Binary files /dev/null and b/ios/StatusIm/Images.xcassets/console.imageset/console.png differ diff --git a/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/Contents.json b/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/Contents.json new file mode 100644 index 0000000000..8f7abaebbb --- /dev/null +++ b/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "corner_left_bottom.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/corner_left_bottom.png b/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/corner_left_bottom.png new file mode 100644 index 0000000000..0fff341220 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/corner_left_bottom.imageset/corner_left_bottom.png differ diff --git a/ios/StatusIm/Images.xcassets/corner_left_top.imageset/Contents.json b/ios/StatusIm/Images.xcassets/corner_left_top.imageset/Contents.json new file mode 100644 index 0000000000..8a4261dfff --- /dev/null +++ b/ios/StatusIm/Images.xcassets/corner_left_top.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "corner_left_top.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/corner_left_top.imageset/corner_left_top.png b/ios/StatusIm/Images.xcassets/corner_left_top.imageset/corner_left_top.png new file mode 100644 index 0000000000..e25037ece3 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/corner_left_top.imageset/corner_left_top.png differ diff --git a/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/Contents.json b/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/Contents.json new file mode 100644 index 0000000000..077dc66507 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "corner_right_bottom.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/corner_right_bottom.png b/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/corner_right_bottom.png new file mode 100644 index 0000000000..e542ec0d64 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/corner_right_bottom.imageset/corner_right_bottom.png differ diff --git a/ios/StatusIm/Images.xcassets/corner_right_top.imageset/Contents.json b/ios/StatusIm/Images.xcassets/corner_right_top.imageset/Contents.json new file mode 100644 index 0000000000..4a70ee3948 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/corner_right_top.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "corner_right_top.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/corner_right_top.imageset/corner_right_top.png b/ios/StatusIm/Images.xcassets/corner_right_top.imageset/corner_right_top.png new file mode 100644 index 0000000000..a6a65aedc9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/corner_right_top.imageset/corner_right_top.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_add.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_add.imageset/Contents.json new file mode 100644 index 0000000000..32c01ec112 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_add.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_add.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_add.imageset/icon_add.png b/ios/StatusIm/Images.xcassets/icon_add.imageset/icon_add.png new file mode 100644 index 0000000000..ce0b329d94 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_add.imageset/icon_add.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/Contents.json new file mode 100644 index 0000000000..bd31b2dad5 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_add_gray.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/icon_add_gray.png b/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/icon_add_gray.png new file mode 100644 index 0000000000..55a1592352 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_add_gray.imageset/icon_add_gray.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_add_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_add_white.imageset/Contents.json new file mode 100644 index 0000000000..32c01ec112 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_add_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_add.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_add_white.imageset/icon_add.png b/ios/StatusIm/Images.xcassets/icon_add_white.imageset/icon_add.png new file mode 100644 index 0000000000..2780585768 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_add_white.imageset/icon_add.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_avatar.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_avatar.imageset/Contents.json new file mode 100644 index 0000000000..d7258658d9 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_avatar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_avatar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_avatar.imageset/icon_avatar.png b/ios/StatusIm/Images.xcassets/icon_avatar.imageset/icon_avatar.png new file mode 100644 index 0000000000..f3b8d0770d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_avatar.imageset/icon_avatar.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_back.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_back.imageset/Contents.json new file mode 100644 index 0000000000..26468eea27 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_back.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_back.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_back.imageset/icon_back.png b/ios/StatusIm/Images.xcassets/icon_back.imageset/icon_back.png new file mode 100644 index 0000000000..4d688372c9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_back.imageset/icon_back.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_back_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_back_white.imageset/Contents.json new file mode 100644 index 0000000000..1a8177ac16 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_back_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_back_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_back_white.imageset/icon_back_white.png b/ios/StatusIm/Images.xcassets/icon_back_white.imageset/icon_back_white.png new file mode 100644 index 0000000000..166bc865a4 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_back_white.imageset/icon_back_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_bin.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_bin.imageset/Contents.json new file mode 100644 index 0000000000..3dd5b54b3c --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_bin.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_bin.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_bin.imageset/icon_bin.png b/ios/StatusIm/Images.xcassets/icon_bin.imageset/icon_bin.png new file mode 100644 index 0000000000..fa960dbf1d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_bin.imageset/icon_bin.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/Contents.json new file mode 100644 index 0000000000..cdbac7a9e1 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_close_gray.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/icon_close_gray.png b/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/icon_close_gray.png new file mode 100644 index 0000000000..c1f1c5f300 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_close_gray.imageset/icon_close_gray.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/Contents.json new file mode 100644 index 0000000000..4596ecf424 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_close_small_gray.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_close_small_gray-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_close_small_gray-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-1.png b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-1.png new file mode 100644 index 0000000000..3b641a8b92 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-1.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-2.png b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-2.png new file mode 100644 index 0000000000..15c440e082 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray-2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray.png b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray.png new file mode 100644 index 0000000000..a53e5eab90 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_close_small_gray.imageset/ic_close_small_gray.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_close_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_close_white.imageset/Contents.json new file mode 100644 index 0000000000..aa3fb77e9e --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_close_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_close_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_close_white.imageset/icon_close_white.png b/ios/StatusIm/Images.xcassets/icon_close_white.imageset/icon_close_white.png new file mode 100644 index 0000000000..32e84becb6 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_close_white.imageset/icon_close_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_dollar.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_dollar.imageset/Contents.json new file mode 100644 index 0000000000..ed48a38167 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_dollar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_dollar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_dollar.imageset/icon_dollar.png b/ios/StatusIm/Images.xcassets/icon_dollar.imageset/icon_dollar.png new file mode 100644 index 0000000000..85d93cab2c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_dollar.imageset/icon_dollar.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/Contents.json new file mode 100644 index 0000000000..4acd2dadd6 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_dollar_green.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/icon_dollar_green.png b/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/icon_dollar_green.png new file mode 100644 index 0000000000..dac30955fe Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_dollar_green.imageset/icon_dollar_green.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_dots.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_dots.imageset/Contents.json new file mode 100644 index 0000000000..0e5ab17112 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_dots.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_dots.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_dots.imageset/icon_dots.png b/ios/StatusIm/Images.xcassets/icon_dots.imageset/icon_dots.png new file mode 100644 index 0000000000..464cfeb09f Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_dots.imageset/icon_dots.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/Contents.json new file mode 100644 index 0000000000..25da82bc5e --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_drag_down.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/icon_drag_down.png b/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/icon_drag_down.png new file mode 100644 index 0000000000..7e7a4912bc Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_drag_down.imageset/icon_drag_down.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/Contents.json new file mode 100644 index 0000000000..f802fecef4 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_drag_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/icon_drag_white.png b/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/icon_drag_white.png new file mode 100644 index 0000000000..23fa971c5e Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_drag_white.imageset/icon_drag_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/Contents.json new file mode 100644 index 0000000000..7a263fe74c --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_enter_address.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/icon_enter_address.png b/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/icon_enter_address.png new file mode 100644 index 0000000000..7e6c232c99 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_enter_address.imageset/icon_enter_address.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_group.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_group.imageset/Contents.json new file mode 100644 index 0000000000..1e9bce3278 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_group.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_group.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_group.imageset/icon_group.png b/ios/StatusIm/Images.xcassets/icon_group.imageset/icon_group.png new file mode 100644 index 0000000000..b14d23f73b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_group.imageset/icon_group.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_group_big.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_group_big.imageset/Contents.json new file mode 100644 index 0000000000..54bb50362d --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_group_big.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_group_big.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_group_big.imageset/icon_group_big.png b/ios/StatusIm/Images.xcassets/icon_group_big.imageset/icon_group_big.png new file mode 100644 index 0000000000..a1bff88bb0 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_group_big.imageset/icon_group_big.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/Contents.json new file mode 100644 index 0000000000..7466451000 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_hamburger.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/icon_hamburger.png b/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/icon_hamburger.png new file mode 100644 index 0000000000..8bd85bc66a Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_hamburger.imageset/icon_hamburger.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_input_list.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_input_list.imageset/Contents.json new file mode 100644 index 0000000000..5fcc1377bb --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_input_list.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_input_list.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_input_list.imageset/icon_input_list.png b/ios/StatusIm/Images.xcassets/icon_input_list.imageset/icon_input_list.png new file mode 100644 index 0000000000..9e4fd528b3 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_input_list.imageset/icon_input_list.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_list.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_list.imageset/Contents.json new file mode 100644 index 0000000000..5a463f3dea --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_list.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_list.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_list.imageset/icon_list.png b/ios/StatusIm/Images.xcassets/icon_list.imageset/icon_list.png new file mode 100644 index 0000000000..e253971bae Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_list.imageset/icon_list.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_location.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_location.imageset/Contents.json new file mode 100644 index 0000000000..5d68b0d3e3 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_location.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_location.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon_location-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icon_location-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-1.png b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-1.png new file mode 100644 index 0000000000..0e43aa738e Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-1.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-2.png b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-2.png new file mode 100644 index 0000000000..4dfb48d63d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location-2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location.png b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location.png new file mode 100644 index 0000000000..c063b68d31 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_location.imageset/icon_location.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/Contents.json new file mode 100644 index 0000000000..ced131029f --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_lock_gray.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/icon_lock_gray.png b/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/icon_lock_gray.png new file mode 100644 index 0000000000..f18aaa61e1 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_lock_gray.imageset/icon_lock_gray.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/Contents.json new file mode 100644 index 0000000000..1ceca537cf --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_lock_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/icon_lock_white.png b/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/icon_lock_white.png new file mode 100644 index 0000000000..ccddc79dec Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_lock_white.imageset/icon_lock_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/Contents.json new file mode 100644 index 0000000000..e499cb9f09 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_menu_group.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/icon_menu_group.png b/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/icon_menu_group.png new file mode 100644 index 0000000000..a356f7ccdd Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_menu_group.imageset/icon_menu_group.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_money_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/Contents.json new file mode 100644 index 0000000000..6ad2e9bcdc --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_money_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon_money_white-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icon_money_white-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-1.png b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-1.png new file mode 100644 index 0000000000..da077578fa Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-1.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-2.png b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-2.png new file mode 100644 index 0000000000..243469d9a9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white-2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white.png b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white.png new file mode 100644 index 0000000000..1584a09d0a Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_money_white.imageset/icon_money_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/Contents.json new file mode 100644 index 0000000000..bb21bc5206 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_more_vertical.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/icon_more_vertical.png b/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/icon_more_vertical.png new file mode 100644 index 0000000000..d30018f924 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_more_vertical.imageset/icon_more_vertical.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/Contents.json new file mode 100644 index 0000000000..2581b977ee --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_more_vertical_blue.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/icon_more_vertical_blue.png b/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/icon_more_vertical_blue.png new file mode 100644 index 0000000000..79449d9dff Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_more_vertical_blue.imageset/icon_more_vertical_blue.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_muted.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_muted.imageset/Contents.json new file mode 100644 index 0000000000..052a64996d --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_muted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_muted.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_muted.imageset/icon_muted.png b/ios/StatusIm/Images.xcassets/icon_muted.imageset/icon_muted.png new file mode 100644 index 0000000000..674a9faa66 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_muted.imageset/icon_muted.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/Contents.json new file mode 100644 index 0000000000..fecfd53e27 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_notifications_on.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/icon_notifications_on.png b/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/icon_notifications_on.png new file mode 100644 index 0000000000..a0ee3c3fd5 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_notifications_on.imageset/icon_notifications_on.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok.imageset/Contents.json new file mode 100644 index 0000000000..3fe06efeae --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok.imageset/icon_ok.png b/ios/StatusIm/Images.xcassets/icon_ok.imageset/icon_ok.png new file mode 100644 index 0000000000..4abb3e2e57 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok.imageset/icon_ok.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/Contents.json new file mode 100644 index 0000000000..d29ccc9482 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok_blue.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/icon_ok_blue.png b/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/icon_ok_blue.png new file mode 100644 index 0000000000..9e8661249d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok_blue.imageset/icon_ok_blue.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/Contents.json new file mode 100644 index 0000000000..66360b34ca --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok_disabled.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/icon_ok_disabled.png b/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/icon_ok_disabled.png new file mode 100644 index 0000000000..513a2177a2 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok_disabled.imageset/icon_ok_disabled.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/Contents.json new file mode 100644 index 0000000000..96fbd01abb --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok_purple.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/icon_ok_purple.png b/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/icon_ok_purple.png new file mode 100644 index 0000000000..b294224264 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok_purple.imageset/icon_ok_purple.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/Contents.json new file mode 100644 index 0000000000..92a81a0b71 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok_small.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/icon_ok_small.png b/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/icon_ok_small.png new file mode 100644 index 0000000000..e824f5e8eb Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok_small.imageset/icon_ok_small.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/Contents.json new file mode 100644 index 0000000000..eac35ff2a0 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_ok_small_copy_2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/icon_ok_small_copy_2.png b/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/icon_ok_small_copy_2.png new file mode 100644 index 0000000000..e824f5e8eb Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_ok_small_copy_2.imageset/icon_ok_small_copy_2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/Contents.json new file mode 100644 index 0000000000..5f10533309 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_phone_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon_phone_white-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icon_phone_white-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-1.png b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-1.png new file mode 100644 index 0000000000..f08867e855 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-1.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-2.png b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-2.png new file mode 100644 index 0000000000..b49ec0439d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white-2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white.png b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white.png new file mode 100644 index 0000000000..224f48c50d Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_phone_white.imageset/icon_phone_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_plus.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_plus.imageset/Contents.json new file mode 100644 index 0000000000..1b8c20f1c6 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_plus.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_plus.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_plus.imageset/icon_plus.png b/ios/StatusIm/Images.xcassets/icon_plus.imageset/icon_plus.png new file mode 100644 index 0000000000..dad95d1814 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_plus.imageset/icon_plus.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_qr.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_qr.imageset/Contents.json new file mode 100644 index 0000000000..bd62d825e7 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_qr.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_qr.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_qr.imageset/icon_qr.png b/ios/StatusIm/Images.xcassets/icon_qr.imageset/icon_qr.png new file mode 100644 index 0000000000..f7b3c5ad15 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_qr.imageset/icon_qr.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/Contents.json new file mode 100644 index 0000000000..1738a7c226 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_qr_gray.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/icon_qr_gray.png b/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/icon_qr_gray.png new file mode 100644 index 0000000000..9100f6d22b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_qr_gray.imageset/icon_qr_gray.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/Contents.json new file mode 100644 index 0000000000..182153f038 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_scan_q_r.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/icon_scan_q_r.png b/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/icon_scan_q_r.png new file mode 100644 index 0000000000..9e140304dd Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_scan_q_r.imageset/icon_scan_q_r.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/Contents.json new file mode 100644 index 0000000000..dcdc616423 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_scan_white.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/icon_scan_white.png b/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/icon_scan_white.png new file mode 100644 index 0000000000..9aeeecba40 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_scan_white.imageset/icon_scan_white.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_search.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_search.imageset/Contents.json new file mode 100644 index 0000000000..aaef9bba5c --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_search.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_search.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_search.imageset/icon_search.png b/ios/StatusIm/Images.xcassets/icon_search.imageset/icon_search.png new file mode 100644 index 0000000000..b057823f3b Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_search.imageset/icon_search.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/Contents.json new file mode 100644 index 0000000000..90a07d3f4b --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_search_gray_copy.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/icon_search_gray_copy.png b/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/icon_search_gray_copy.png new file mode 100644 index 0000000000..d138f4e4b9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_search_gray_copy.imageset/icon_search_gray_copy.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_send.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_send.imageset/Contents.json new file mode 100644 index 0000000000..9498f71507 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_send.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_send.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_send.imageset/icon_send.png b/ios/StatusIm/Images.xcassets/icon_send.imageset/icon_send.png new file mode 100644 index 0000000000..29c51712c3 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_send.imageset/icon_send.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_settings.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_settings.imageset/Contents.json new file mode 100644 index 0000000000..dfa2c91077 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_settings.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_settings.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_settings.imageset/icon_settings.png b/ios/StatusIm/Images.xcassets/icon_settings.imageset/icon_settings.png new file mode 100644 index 0000000000..561a5dcc25 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_settings.imageset/icon_settings.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_smile.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_smile.imageset/Contents.json new file mode 100644 index 0000000000..322839f98e --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_smile.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_smile.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_smile.imageset/icon_smile.png b/ios/StatusIm/Images.xcassets/icon_smile.imageset/icon_smile.png new file mode 100644 index 0000000000..45dfb63996 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_smile.imageset/icon_smile.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/Contents.json new file mode 100644 index 0000000000..02a839408c --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_tab_chats.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/icon_tab_chats.png b/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/icon_tab_chats.png new file mode 100644 index 0000000000..382bcaaaf6 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_tab_chats.imageset/icon_tab_chats.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/Contents.json new file mode 100644 index 0000000000..1bd385d0a8 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_tab_contacts.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/icon_tab_contacts.png b/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/icon_tab_contacts.png new file mode 100644 index 0000000000..19f17a0942 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_tab_contacts.imageset/icon_tab_contacts.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/Contents.json new file mode 100644 index 0000000000..4b8a714e71 --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_tab_discover.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/icon_tab_discover.png b/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/icon_tab_discover.png new file mode 100644 index 0000000000..8bc25d2ab3 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_tab_discover.imageset/icon_tab_discover.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_up.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_up.imageset/Contents.json new file mode 100644 index 0000000000..1b137c08cd --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_up.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_up.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_up.imageset/icon_up.png b/ios/StatusIm/Images.xcassets/icon_up.imageset/icon_up.png new file mode 100644 index 0000000000..01e16f8ce1 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_up.imageset/icon_up.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/Contents.json b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/Contents.json new file mode 100644 index 0000000000..f301255a8c --- /dev/null +++ b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "wallet_avatar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "wallet_avatar-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "wallet_avatar-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-1.png b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-1.png new file mode 100644 index 0000000000..3aad8494b9 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-1.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-2.png b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-2.png new file mode 100644 index 0000000000..eb3076c861 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar-2.png differ diff --git a/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar.png b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar.png new file mode 100644 index 0000000000..629bb81604 Binary files /dev/null and b/ios/StatusIm/Images.xcassets/icon_wallet_avatar.imageset/wallet_avatar.png differ diff --git a/ios/StatusIm/Images.xcassets/scan_blue.imageset/Contents.json b/ios/StatusIm/Images.xcassets/scan_blue.imageset/Contents.json new file mode 100644 index 0000000000..3be5fbd2eb --- /dev/null +++ b/ios/StatusIm/Images.xcassets/scan_blue.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "scan_blue.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/StatusIm/Images.xcassets/scan_blue.imageset/scan_blue.png b/ios/StatusIm/Images.xcassets/scan_blue.imageset/scan_blue.png new file mode 100644 index 0000000000..b89f57307c Binary files /dev/null and b/ios/StatusIm/Images.xcassets/scan_blue.imageset/scan_blue.png differ diff --git a/ios/StatusIm/Info.plist b/ios/StatusIm/Info.plist new file mode 100644 index 0000000000..2a71f7e0f5 --- /dev/null +++ b/ios/StatusIm/Info.plist @@ -0,0 +1,89 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Status + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + im.status.ethereum + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationQueriesSchemes + + whatsapp + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + api.status.im + + NSTemporaryExceptionAllowsInsecureHTTPLoads + + + localhost + + NSTemporaryExceptionAllowsInsecureHTTPLoads + + + + + NSMicrophoneUsageDescription + Need microphone access for Instabug and Audio Messages + NSCameraUsageDescription + We need to access your camera + NSContactsUsageDescription + We need to access your contacts + NSLocationWhenInUseUsageDescription + + NSPhotoLibraryUsageDescription + We need to access your photo storage to give you an ability to select photos + UIAppFonts + + SF-UI-Text-Bold.otf + SF-UI-Text-Regular.otf + SF-UI-Text-Medium.otf + SF-UI-Text-Light.otf + SF-UI-Display-Medium.otf + SF-UI-Display-Regular.otf + SF-UI-Display-Semibold.otf + SF-UI-Display-Thin.otf + Entypo.ttf + EvilIcons.ttf + FontAwesome.ttf + Foundation.ttf + Ionicons.ttf + MaterialIcons.ttf + Octicons.ttf + Zocial.ttf + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Messenger/main.m b/ios/StatusIm/main.m similarity index 57% rename from ios/Messenger/main.m rename to ios/StatusIm/main.m index afe0335e03..3d767fcbb9 100644 --- a/ios/Messenger/main.m +++ b/ios/StatusIm/main.m @@ -8,15 +8,11 @@ */ #import -#import + #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - run("--bootnodes enode://e2f28126720452aa82f7d3083e49e6b3945502cb94d9750a15e27ee310eed6991618199f878e5fbc7dfa0e20f0af9554b41f491dc8f1dbae8f0f2d37a3a613aa@139.162.13.89:30303 --shh --ipcdisable --nodiscover --rpc --rpcapi db,eth,net,web3,shh --fast"); - }); - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } diff --git a/ios/MessengerTests/Info.plist b/ios/StatusImTests/Info.plist similarity index 93% rename from ios/MessengerTests/Info.plist rename to ios/StatusImTests/Info.plist index ba72822e87..43f88fd7a9 100644 --- a/ios/MessengerTests/Info.plist +++ b/ios/StatusImTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + im.status.ethereum CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/ios/MessengerTests/MessengerTests.m b/ios/StatusImTests/StatusImTests.m similarity index 95% rename from ios/MessengerTests/MessengerTests.m rename to ios/StatusImTests/StatusImTests.m index 3040a05056..56fd4597f4 100644 --- a/ios/MessengerTests/MessengerTests.m +++ b/ios/StatusImTests/StatusImTests.m @@ -13,14 +13,14 @@ #import "RCTLog.h" #import "RCTRootView.h" -#define TIMEOUT_SECONDS 240 +#define TIMEOUT_SECONDS 600 #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" -@interface MessengerTests : XCTestCase +@interface StatusImTests : XCTestCase @end -@implementation MessengerTests +@implementation StatusImTests - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test { diff --git a/ios/pom.xml b/ios/pom.xml deleted file mode 100644 index 1f41b6d02b..0000000000 --- a/ios/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - 4.0.0 - - org.reactjs.native.example - Messenger - 1.0-SNAPSHOT - - - - ci.syng.im - http://ci.syng.im:8081/artifactory/libs-release-local/ - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - 2.10 - - - - syng-im - ios-geth - 1.4.0-201603151613-92d65cf - zip - true - ${project.build.directory}/Frameworks - - - - - - - \ No newline at end of file diff --git a/modules/react-native-status/android/build.gradle b/modules/react-native-status/android/build.gradle new file mode 100644 index 0000000000..33968eb7be --- /dev/null +++ b/modules/react-native-status/android/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } +} + +dependencies { + compile 'com.facebook.react:react-native:+' + compile(group: 'status-im', name: 'status-go', version: '1.1.0-14-g85f29c2', ext: 'aar') +} diff --git a/modules/react-native-status/android/src/main/AndroidManifest.xml b/modules/react-native-status/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..46151203f0 --- /dev/null +++ b/modules/react-native-status/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ConnectorHandler.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ConnectorHandler.java new file mode 100644 index 0000000000..3dbba9b662 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ConnectorHandler.java @@ -0,0 +1,10 @@ +package im.status.ethereum.module; + + +import android.os.Message; + +public interface ConnectorHandler { + boolean handleMessage(Message message); + void onConnectorConnected(); + void onConnectorDisconnected(); +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ServiceConnector.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ServiceConnector.java new file mode 100644 index 0000000000..860a216d98 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/ServiceConnector.java @@ -0,0 +1,139 @@ +package im.status.ethereum.module; + + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.*; + +import java.util.ArrayList; + +public class ServiceConnector { + + /** Context of the activity from which this connector was launched */ + private Context context; + + /** The class of the service we want to connect to */ + private Class serviceClass; + + /** Flag indicating if the service is bound. */ + boolean isBound; + + /** Sends messages to the service. */ + Messenger serviceMessenger = null; + + /** Receives messages from the service. */ + Messenger clientMessenger = null; + + private ArrayList handlers = new ArrayList<>(); + + /** Handles incoming messages from service. */ + private class IncomingHandler extends Handler { + + IncomingHandler(HandlerThread thread) { + + super(thread.getLooper()); + } + + @Override + public void handleMessage(Message message) { + + boolean isClaimed = false; + //if (message.obj != null) { + // String identifier = ((Bundle) message.obj).getString("identifier"); + //if (identifier != null) { + + for (ConnectorHandler handler : handlers) { + // if (identifier.equals(handler.getID())) { + isClaimed = handler.handleMessage(message); + // } + } + // } + //} + if (!isClaimed) { + super.handleMessage(message); + } + } + } + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection serviceConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + + serviceMessenger = new Messenger(service); + isBound = true; + for (ConnectorHandler handler: handlers) { + handler.onConnectorConnected(); + } + } + + public void onServiceDisconnected(ComponentName className) { + + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + serviceMessenger = null; + isBound = false; + for (ConnectorHandler handler: handlers) { + handler.onConnectorDisconnected(); + } + } + }; + + ServiceConnector(Context context, Class serviceClass) { + this.context = context; + this.serviceClass = serviceClass; + // Handler thread to avoid running on the main UI thread + HandlerThread handlerThread = new HandlerThread("HandlerThread"); + handlerThread.start(); + // Incoming message handler. Calls to its binder are sequential! + IncomingHandler handler = new IncomingHandler(handlerThread); + clientMessenger = new Messenger(handler); + } + + /** Bind to the service */ + public boolean bindService() { + + if (serviceConnection != null) { + Intent intent = new Intent(context, serviceClass); + context.getApplicationContext().startService(intent); + return context.getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + } else { + return false; + } + } + + /** Unbind from the service */ + public void unbindService() { + + if (isBound && serviceConnection != null) { + context.getApplicationContext().unbindService(serviceConnection); + isBound = false; +/* + Intent intent = new Intent(context, serviceClass); + context.getApplicationContext().stopService(intent); +*/ + } + } + + public void registerHandler(ConnectorHandler handler) { + + if (!handlers.contains(handler)) { + handlers.add(handler); + } + } + + public void removeHandler(ConnectorHandler handler) { + + handlers.remove(handler); + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusConnector.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusConnector.java new file mode 100644 index 0000000000..e44645ae06 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusConnector.java @@ -0,0 +1,156 @@ +package im.status.ethereum.module; + + +import android.content.Context; +import android.os.Bundle; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +public class StatusConnector extends ServiceConnector { + + private static final String TAG = "StatusConnector"; + + public static final String CALLBACK_IDENTIFIER = "callbackIdentifier"; + + public StatusConnector(Context context, Class serviceClass) { + + super(context, serviceClass); + } + + public void startNode(String callbackIdentifier) { + + if (checkBound()) { + sendMessage(callbackIdentifier, StatusMessages.MSG_START_NODE, null); + } + } + + public void stopNode(String callbackIdentifier) { + + if (checkBound()) { + sendMessage(callbackIdentifier, StatusMessages.MSG_STOP_NODE, null); + } + } + + public void startRPC() { + if (checkBound()) { + sendMessage(null, StatusMessages.MSG_START_RPC, null); + } + } + + public void stopRPC() { + if (checkBound()) { + sendMessage(null, StatusMessages.MSG_STOP_RPC, null); + } + } + + public void login(String callbackIdentifier, String address, String password) { + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("address", address); + data.putString("password", password); + sendMessage(callbackIdentifier, StatusMessages.MSG_LOGIN, data); + } + } + + public void createAccount(String callbackIdentifier, String password) { + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("password", password); + sendMessage(callbackIdentifier, StatusMessages.MSG_CREATE_ACCOUNT, data); + } + } + + public void recoverAccount(String callbackIdentifier, String passphrase, String password) { + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("passphrase", passphrase); + data.putString("password", password); + sendMessage(callbackIdentifier, StatusMessages.MSG_RECOVER_ACCOUNT, data); + } + } + + public void completeTransaction(String callbackIdentifier, String hash, String password){ + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("hash", hash); + data.putString("password", password); + sendMessage(callbackIdentifier, StatusMessages.MSG_COMPLETE_TRANSACTION, data); + } + } + + public void discardTransaction(String id){ + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("id", id); + sendMessage(null, StatusMessages.MSG_DISCARD_TRANSACTION, data); + } + } + + public void initJail(String callbackIdentifier, String js){ + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("js", js); + sendMessage(callbackIdentifier, StatusMessages.MSG_JAIL_INIT, data); + } + } + + public void parseJail(String callbackIdentifier, String chatId, String js){ + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("chatId", chatId); + data.putString("js", js); + sendMessage(callbackIdentifier, StatusMessages.MSG_JAIL_PARSE, data); + } + } + + public void callJail(String callbackIdentifier, String chatId, String path, String params){ + + if (checkBound()) { + Bundle data = new Bundle(); + data.putString("chatId", chatId); + data.putString("path", path); + data.putString("params", params); + sendMessage(callbackIdentifier, StatusMessages.MSG_JAIL_CALL, data); + } + } + + private boolean checkBound() { + + if (!isBound) { + Log.d(TAG, "StatusConnector not bound!"); + return false; + } + return true; + } + + private Message createMessage(String callbackIdentifier, int idMessage, Bundle data) { + + Log.d(TAG, "Client messenger: " + clientMessenger.toString()); + Message msg = Message.obtain(null, idMessage, 0, 0); + msg.replyTo = clientMessenger; + if (data == null) { + data = new Bundle(); + } + data.putString(CALLBACK_IDENTIFIER, callbackIdentifier); + msg.setData(data); + return msg; + } + + private void sendMessage(String callbackIdentifier, int idMessage, Bundle data) { + + Message msg = createMessage(callbackIdentifier, idMessage, data); + try { + serviceMessenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Exception sending message(" + msg.toString() + ") to service: ", e); + } + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusMessages.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusMessages.java new file mode 100644 index 0000000000..4113dfe3a7 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusMessages.java @@ -0,0 +1,72 @@ +package im.status.ethereum.module; + + +public class StatusMessages { + + /** + * Start the node + */ + static final int MSG_START_NODE = 1; + + /** + * Stop the node + */ + static final int MSG_STOP_NODE = 2; + + /** + * Unlock an account + */ + static final int MSG_LOGIN = 3; + + /** + * Create an account + */ + static final int MSG_CREATE_ACCOUNT = 4; + + /** + * Create an account + */ + static final int MSG_RECOVER_ACCOUNT = 5; + + /** + * Account complete transaction event + */ + static final int MSG_COMPLETE_TRANSACTION = 6; + + /** + * Geth event + */ + public static final int MSG_GETH_EVENT = 7; + + /** + * Initialize jail + */ + public static final int MSG_JAIL_INIT = 8; + + + /** + * Parse js in jail + */ + public static final int MSG_JAIL_PARSE = 9; + + /** + * Parse js in jail + */ + public static final int MSG_JAIL_CALL = 10; + + /** + * Account discard transaction event + */ + public static final int MSG_DISCARD_TRANSACTION = 11; + + /** + * Start RPC server + */ + static final int MSG_START_RPC = 12; + + /** + * Stop RPC server + */ + static final int MSG_STOP_RPC = 13; + +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java new file mode 100644 index 0000000000..551f3ef27f --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -0,0 +1,333 @@ +package im.status.ethereum.module; + +import android.app.Activity; +import android.view.WindowManager; +import android.os.Bundle; +import android.os.Message; +import android.os.RemoteException; +import com.facebook.react.bridge.*; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import android.util.Log; + +import java.util.HashMap; +import java.util.UUID; + +class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventListener, ConnectorHandler { + + private static final String TAG = "StatusModule"; + + private StatusConnector status = null; + + private HashMap callbacks = new HashMap<>(); + + StatusModule(ReactApplicationContext reactContext) { + super(reactContext); + reactContext.addLifecycleEventListener(this); + } + + @Override + public String getName() { + return "Status"; + } + + @Override + public void onHostResume() { // Actvity `onResume` + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + Log.d(TAG, "On host Activity doesn't exist"); + return; + } + if (status == null) { + status = new StatusConnector(currentActivity, StatusService.class); + status.registerHandler(this); + } + + status.bindService(); + + WritableMap params = Arguments.createMap(); + Log.d(TAG, "Send module.initialized event"); + params.putString("jsonEvent", "{\"type\":\"module.initialized\"}"); + getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("gethEvent", params); + } + + @Override + public void onHostPause() { // Actvity `onPause` + if (status != null) { + status.unbindService(); + } + } + + @Override + public void onHostDestroy() { // Actvity `onDestroy` + if (status != null) { + status.stopNode(null); + } + } + + @Override + public void onConnectorConnected() { + } + + @Override + public void onConnectorDisconnected() { + } + + @Override + public boolean handleMessage(Message message) { + + Log.d(TAG, "Received message: " + message.toString()); + boolean isClaimed = true; + Bundle bundle = message.getData(); + String callbackIdentifier = bundle.getString(StatusConnector.CALLBACK_IDENTIFIER); + String data = bundle.getString("data"); + Callback callback = callbacks.remove(callbackIdentifier); + switch (message.what) { + case StatusMessages.MSG_START_NODE: + case StatusMessages.MSG_STOP_NODE: + case StatusMessages.MSG_LOGIN: + case StatusMessages.MSG_CREATE_ACCOUNT: + case StatusMessages.MSG_RECOVER_ACCOUNT: + case StatusMessages.MSG_COMPLETE_TRANSACTION: + case StatusMessages.MSG_JAIL_INIT: + case StatusMessages.MSG_JAIL_PARSE: + case StatusMessages.MSG_JAIL_CALL: + if (callback == null) { + Log.d(TAG, "Could not find callback: " + callbackIdentifier); + } else { + callback.invoke(data); + } + break; + case StatusMessages.MSG_GETH_EVENT: + String event = bundle.getString("event"); + WritableMap params = Arguments.createMap(); + params.putString("jsonEvent", event); + getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("gethEvent", params); + break; + default: + isClaimed = false; + } + + return isClaimed; + } + + private boolean checkAvailability() { + + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + Log.d(TAG, "Activity doesn't exist"); + return false; + } + + if (status == null) { + Log.d(TAG, "Status connector is null"); + return false; + } + + return true; + } + + // Geth + + @ReactMethod + public void startNode(Callback callback) { + Log.d(TAG, "startNode"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.startNode(callbackIdentifier); + } + + @ReactMethod + public void startNodeRPCServer() { + Log.d(TAG, "startNodeRPCServer"); + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + status.startRPC(); + } + + @ReactMethod + public void stopNodeRPCServer() { + Log.d(TAG, "stopNodeRPCServer"); + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + status.stopRPC(); + } + + @ReactMethod + public void login(String address, String password, Callback callback) { + Log.d(TAG, "login"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.login(callbackIdentifier, address, password); + } + + @ReactMethod + public void createAccount(String password, Callback callback) { + Log.d(TAG, "createAccount"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.createAccount(callbackIdentifier, password); + } + + @ReactMethod + public void recoverAccount(String passphrase, String password, Callback callback) { + Log.d(TAG, "recoverAccount"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.recoverAccount(callbackIdentifier, passphrase, password); + } + + private String createIdentifier() { + return UUID.randomUUID().toString(); + } + + @ReactMethod + public void completeTransaction(String hash, String password, Callback callback) { + Log.d(TAG, "completeTransaction"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Log.d(TAG, "Complete transaction: " + hash); + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.completeTransaction(callbackIdentifier, hash, password); + } + + + @ReactMethod + public void discardTransaction(String id) { + Log.d(TAG, "discardTransaction"); + if (!checkAvailability()) { + return; + } + + Log.d(TAG, "Discard transaction: " + id); + status.discardTransaction(id); + } + + // Jail + + @ReactMethod + public void initJail(String js, Callback callback) { + Log.d(TAG, "initJail"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.initJail(callbackIdentifier, js); + } + + @ReactMethod + public void parseJail(String chatId, String js, Callback callback) { + Log.d(TAG, "parseJail"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.parseJail(callbackIdentifier, chatId, js); + } + + @ReactMethod + public void callJail(String chatId, String path, String params, Callback callback) { + Log.d(TAG, "callJail"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + String callbackIdentifier = createIdentifier(); + callbacks.put(callbackIdentifier, callback); + + status.callJail(callbackIdentifier, chatId, path, params); + } + + @ReactMethod + public void setAdjustResize() { + Log.d(TAG, "setAdjustResize"); + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + } + }); + } + + @ReactMethod + public void setAdjustPan() { + Log.d(TAG, "setAdjustPan"); + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } + }); + } + + @ReactMethod + public void setSoftInputMode(final int mode) { + Log.d(TAG, "setSoftInputMode"); + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + activity.getWindow().setSoftInputMode(mode); + } + }); + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java new file mode 100644 index 0000000000..1606184d5c --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java @@ -0,0 +1,35 @@ +package im.status.ethereum.module; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; +import im.status.ethereum.module.StatusService; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class StatusPackage implements ReactPackage { + + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + System.loadLibrary("statusgoraw"); + System.loadLibrary("statusgo"); + modules.add(new StatusModule(reactContext)); + + return modules; + } + + @Override + public List> createJSModules() { + return Collections.emptyList(); + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusService.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusService.java new file mode 100644 index 0000000000..73dce33d7e --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusService.java @@ -0,0 +1,374 @@ +package im.status.ethereum.module; + +import android.app.Service; +import android.content.Intent; +import android.os.*; +import android.support.annotation.Nullable; +import android.util.Log; +import java.util.concurrent.Callable; + +import java.lang.ref.WeakReference; + +import com.github.status_im.status_go.cmd.Statusgo; + +import java.io.File; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class StatusService extends Service { + + private static final String TAG = "StatusService"; + + private static boolean isNodeInitialized = false; + private final Handler handler = new Handler(); + + private ExecutorService executor = null; + + private static String dataFolder; + + private static Messenger applicationMessenger = null; + + private static class IncomingHandler extends Handler { + + private final WeakReference service; + + IncomingHandler(StatusService service) { + + this.service = new WeakReference<>(service); + } + + @Override + public void handleMessage(Message message) { + + StatusService service = this.service.get(); + if (service != null) { + if (!service.handleMessage(message)) { + super.handleMessage(message); + } + } + } + } + + private final Messenger serviceMessenger = new Messenger(new IncomingHandler(this)); + + + public static void signalEvent(String jsonEvent) { + + Log.d(TAG, "Signal event: " + jsonEvent); + Bundle replyData = new Bundle(); + replyData.putString("event", jsonEvent); + + Message replyMessage = Message.obtain(null, StatusMessages.MSG_GETH_EVENT, 0, 0, null); + replyMessage.setData(replyData); + sendReply(applicationMessenger, replyMessage); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return serviceMessenger.getBinder(); + } + + @Override + public void onCreate() { + + super.onCreate(); + } + + @Override + public void onDestroy() { + + super.onDestroy(); + if (executor != null) { + executor.shutdownNow(); + } + //TODO: stop geth + stopNode(null); + //isNodeInitialized = false; + Log.d(TAG, "Status Service stopped !"); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + if (executor == null) { + executor = Executors.newCachedThreadPool(); + } + return Service.START_STICKY; + } + + private boolean handleMessage(Message message) { + Log.d(TAG, "Received service message." + message.toString()); + applicationMessenger = message.replyTo; + doStartNode(); + switch (message.what) { + + case StatusMessages.MSG_START_NODE: + startNode(message); + break; + + case StatusMessages.MSG_STOP_NODE: + stopNode(message); + break; + + case StatusMessages.MSG_CREATE_ACCOUNT: + createAccount(message); + break; + + case StatusMessages.MSG_RECOVER_ACCOUNT: + recoverAccount(message); + break; + + case StatusMessages.MSG_LOGIN: + login(message); + break; + + case StatusMessages.MSG_COMPLETE_TRANSACTION: + completeTransaction(message); + break; + + case StatusMessages.MSG_DISCARD_TRANSACTION: + discardTransaction(message); + break; + + case StatusMessages.MSG_JAIL_INIT: + initJail(message); + break; + + case StatusMessages.MSG_JAIL_PARSE: + parseJail(message); + break; + + case StatusMessages.MSG_JAIL_CALL: + callJail(message); + break; + + case StatusMessages.MSG_START_RPC: + startRPC(); + break; + + case StatusMessages.MSG_STOP_RPC: + stopRPC(); + break; + + default: + return false; + } + + return true; + } + + public void doStartNode() { + if (!isNodeInitialized) { + + File extStore = Environment.getExternalStorageDirectory(); + dataFolder = extStore.exists() ? + extStore.getAbsolutePath() + "/ethereum" : + getApplicationInfo().dataDir + "/ethereum"; + Log.d(TAG, "Starting Geth node in folder: " + dataFolder); + + try { + final File newFile = new File(dataFolder); + // todo handle error? + newFile.mkdir(); + } catch (Exception e) { + Log.e(TAG, "error making folder: " + dataFolder, e); + } + + Statusgo.StartNode(dataFolder); + Log.d(TAG, "Geth node started"); + Log.w(TAG, "adding peer"); + + Statusgo.AddPeer("enode://e19d89e6faf2772e2f250e9625478ee7f313fcc0bb5e9310d5d407371496d9d7d73ccecd9f226cc2a8be34484525f72ba9db9d26f0222f4efc3c6d9d995ee224@198.199.105.122:30303"); + Statusgo.AddPeer("enode://1ad53266faaa9258ae71eef4d162022ba0d39498e1a3488e6c65fd86e0fb528e2aa68ad0e199da69fd39f4a3a38e9e8e95ac53ba5cc7676dfeaacf5fd6c0ad27@139.59.212.114:30303"); + isNodeInitialized = true; + } + } + + private void startNode(Message message) { + doStartNode(); + createAndSendReply(message, StatusMessages.MSG_START_NODE, null); + } + + private void stopNode(Message message) { + // TODO: stop node + + createAndSendReply(message, StatusMessages.MSG_STOP_NODE, null); + } + + private void startRPC() { + Statusgo.StartNodeRPCServer(); + } + + private void stopRPC() { + Statusgo.StopNodeRPCServer(); + } + + private void createAccount(Message message) { + + Bundle data = message.getData(); + String password = data.getString("password"); + Log.d(TAG, "Creating account: " + password); + String jsonData = Statusgo.CreateAccount(password); + Log.d(TAG, "Created account: " + jsonData); + + Bundle replyData = new Bundle(); + replyData.putString("data", jsonData); + createAndSendReply(message, StatusMessages.MSG_CREATE_ACCOUNT, replyData); + } + + private void recoverAccount(Message message) { + + Bundle data = message.getData(); + String passphrase = data.getString("passphrase"); + String password = data.getString("password"); + Log.d(TAG, "Recovering account: " + passphrase + " - " + password); + String jsonData = Statusgo.RecoverAccount(password, passphrase); + Log.d(TAG, "Recovered account: " + jsonData); + + Bundle replyData = new Bundle(); + replyData.putString("data", jsonData); + createAndSendReply(message, StatusMessages.MSG_RECOVER_ACCOUNT, replyData); + } + + private void login(Message message) { + + applicationMessenger = message.replyTo; + Bundle data = message.getData(); + String address = data.getString("address"); + String password = data.getString("password"); + String result = Statusgo.Login(address, password); + Log.d(TAG, "Loggedin account: " + result); + + Bundle replyData = new Bundle(); + replyData.putString("data", result); + createAndSendReply(message, StatusMessages.MSG_LOGIN, replyData); + } + + private void completeTransaction(Message message){ + + Bundle data = message.getData(); + String hash = data.getString("hash"); + String password = data.getString("password"); + + Log.d(TAG, "Before CompleteTransaction: " + hash); + String result = Statusgo.CompleteTransaction(hash, password); + Log.d(TAG, "After CompleteTransaction: " + result); + + Bundle replyData = new Bundle(); + replyData.putString("data", result); + createAndSendReply(message, StatusMessages.MSG_COMPLETE_TRANSACTION, replyData); + } + + private void discardTransaction(Message message){ + + Bundle data = message.getData(); + String id = data.getString("id"); + + Log.d(TAG, "Before DiscardTransaction: " + id); + String result = Statusgo.DiscardTransaction(id); + Log.d(TAG, "After DiscardTransaction: " + result); + } + + private void initJail(Message message){ + + Bundle data = message.getData(); + String js = data.getString("js"); + + Statusgo.InitJail(js); + + Bundle replyData = new Bundle(); + createAndSendReply(message, StatusMessages.MSG_JAIL_INIT, replyData); + } + + private void parseJail(Message message){ + + Bundle data = message.getData(); + String chatId = data.getString("chatId"); + String js = data.getString("js"); + + String result = Statusgo.Parse(chatId, js); + + Bundle replyData = new Bundle(); + replyData.putString("data", result); + createAndSendReply(message, StatusMessages.MSG_JAIL_PARSE, replyData); + } + + private void callJail(Message message){ + + Bundle data = message.getData(); + String chatId = data.getString("chatId"); + String path = data.getString("path"); + String params = data.getString("params"); + String callbackIdentifier = data.getString(StatusConnector.CALLBACK_IDENTIFIER); + + Log.d(TAG, "Before StatusGo.Call"); + Callable callable = new JailRequest(message.replyTo, chatId, path, params, callbackIdentifier); + executor.submit(callable); + } + + public class JailRequest implements Callable { + + String chatId; + String path; + String params; + String callbackIdentifier; + Messenger messenger; + + JailRequest(Messenger messenger, String chatId, String path, String params, String callbackIdentifier) { + + this.messenger = messenger; + this.chatId = chatId; + this.path = path; + this.params = params; + this.callbackIdentifier = callbackIdentifier; + } + + public String call() throws Exception { + Log.d(TAG, "StatusGo.Call"); + String result = Statusgo.Call(chatId, path, params); + + Bundle replyData = new Bundle(); + replyData.putString("data", result); + Message replyMessage = Message.obtain(null, StatusMessages.MSG_JAIL_CALL, 0, 0, null); + Log.d(TAG, "Callback identifier: " + callbackIdentifier); + replyData.putString(StatusConnector.CALLBACK_IDENTIFIER, callbackIdentifier); + replyMessage.setData(replyData); + sendReply(messenger, replyMessage); + + return result; + } + + } + + public static boolean isNodeInitialized() { + return isNodeInitialized; + } + + private static void createAndSendReply(Message message, int replyIdMessage, Bundle replyData) { + + if (message == null) { + return; + } + Message replyMessage = Message.obtain(null, replyIdMessage, 0, 0, message.obj); + if (replyData == null) { + replyData = new Bundle(); + } + Bundle data = message.getData(); + String callbackIdentifier = data.getString(StatusConnector.CALLBACK_IDENTIFIER); + Log.d(TAG, "Callback identifier: " + callbackIdentifier); + replyData.putString(StatusConnector.CALLBACK_IDENTIFIER, callbackIdentifier); + replyMessage.setData(replyData); + + sendReply(message.replyTo, replyMessage); + } + + private static void sendReply(Messenger messenger, Message message) { + + try { + messenger.send(message); + } catch (Exception e) { + Log.e(TAG, "Exception sending message id: " + message.what, e); + } + } +} diff --git a/modules/react-native-status/index.js b/modules/react-native-status/index.js new file mode 100644 index 0000000000..af2b94d150 --- /dev/null +++ b/modules/react-native-status/index.js @@ -0,0 +1,4 @@ +'use strict'; + +import { NativeModules } from 'react-native'; +module.exports = NativeModules.Status; \ No newline at end of file diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.h b/modules/react-native-status/ios/RCTStatus/RCTStatus.h new file mode 100644 index 0000000000..1bfd8ef8bd --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.h @@ -0,0 +1,7 @@ +#import +#import "RCTBridgeModule.h" +#import "RCTLog.h" + +@interface Status : NSObject ++ (void)signalEvent:(const char *) signal; +@end diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m new file mode 100644 index 0000000000..c4b9babac3 --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -0,0 +1,306 @@ +#import "RCTStatus.h" +#import "RCTBridge.h" +#import "RCTEventDispatcher.h" +#import + +static bool isStatusInitialized; +static RCTBridge *bridge; +@implementation Status{ +} + +-(RCTBridge *)bridge +{ + return bridge; +} + +-(void)setBridge:(RCTBridge *)newBridge +{ + bridge = newBridge; +} + +RCT_EXPORT_MODULE(); + +//////////////////////////////////////////////////////////////////// +#pragma mark - Jails functions +//////////////////////////////////////////////////////////////////// initJail +RCT_EXPORT_METHOD(initJail: (NSString *) js + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"InitJail() method called"); +#endif + InitJail((char *) [js UTF8String]); + callback(@[[NSNull null]]); +} + +//////////////////////////////////////////////////////////////////// parseJail +RCT_EXPORT_METHOD(parseJail:(NSString *)chatId + js:(NSString *)js + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"ParseJail() method called"); +#endif + char * result = Parse((char *) [chatId UTF8String], (char *) [js UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// callJail +RCT_EXPORT_METHOD(callJail:(NSString *)chatId + path:(NSString *)path + params:(NSString *)params + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"CallJail() method called"); +#endif + dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + char * result = Call((char *) [chatId UTF8String], (char *) [path UTF8String], (char *) [params UTF8String]); + dispatch_async( dispatch_get_main_queue(), ^{ + callback(@[[NSString stringWithUTF8String: result]]); + }); + }); +} + + +const int STATE_ACTIVE = 0; +const int STATE_LOCKED_WITH_ACTIVE_APP = 1; +const int STATE_BACKGROUND = 2; +const int STATE_LOCKED_WITH_INACTIVE_APP = 3; +int wozniakConstant = STATE_ACTIVE; + + +static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) +{ + // the "com.apple.springboard.lockcomplete" notification will always come after the "com.apple.springboard.lockstate" notification + CFStringRef nameCFString = (CFStringRef)name; + NSString *lockState = (__bridge NSString*)nameCFString; + NSLog(@"Darwin notification NAME = %@",name); + + NSString* sm = [NSString stringWithFormat:@"%i", wozniakConstant]; + NSString *s1 = [NSString stringWithFormat:@"%@ %@", @"LOCK MAGIC", sm]; + NSLog(s1); + if([lockState isEqualToString:@"com.apple.springboard.lockcomplete"]) + { + NSLog(@"DEVICE LOCKED"); + // User locks phone when application is active + if(wozniakConstant == STATE_ACTIVE){ + wozniakConstant = STATE_LOCKED_WITH_ACTIVE_APP; + StopNodeRPCServer(); + } + + // Here lockcomplete event comes when app is unlocked + // because it couldn't come when locking happened + // as application was not active (it could not handle callback) + if (wozniakConstant == STATE_LOCKED_WITH_INACTIVE_APP) { + wozniakConstant = STATE_ACTIVE; + StopNodeRPCServer(); + StartNodeRPCServer(); + } + } + else + { + NSLog(@"LOCK STATUS CHANGED"); + NSString *s = [NSString stringWithFormat:@"%@ %@", @"LOCK", lockState]; + NSLog(s); + + // if lockstate happens before lockcomplete it means + // that phone was locked when application was not active + if(wozniakConstant == STATE_ACTIVE){ + wozniakConstant = STATE_LOCKED_WITH_INACTIVE_APP; + } + + if(wozniakConstant == STATE_BACKGROUND){ + wozniakConstant = STATE_ACTIVE; + StartNodeRPCServer(); + } + + // one more lockstate event comes along with lockcomplete + // when phone is locked with active application + if(wozniakConstant == STATE_LOCKED_WITH_ACTIVE_APP){ + wozniakConstant = STATE_BACKGROUND; + } + + } +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - startNode +//////////////////////////////////////////////////////////////////// startNode +RCT_EXPORT_METHOD(startNode:(RCTResponseSenderBlock)onResultCallback) { +#if DEBUG + NSLog(@"StartNode() method called"); +#endif + if (!isStatusInitialized){ + isStatusInitialized = true; + + NSError *error = nil; + NSURL *folderName =[[[[NSFileManager defaultManager] + URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] + lastObject] + URLByAppendingPathComponent:@"ethereum"]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:folderName.path]) + [[NSFileManager defaultManager] createDirectoryAtPath:folderName.path withIntermediateDirectories:NO attributes:nil error:&error]; + + if (error){ + NSLog(@"error %@", error); + }else + NSLog(@"folderName: %@", folderName); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(void) { + StartNode((char *) [folderName.path UTF8String]); + }); + NSString *peer1 = @"enode://5f23bf4913dd005ce945648cb12d3ef970069818d8563a3fe054e5e1dc3898b9cb83e0af1f51b2dce75eaffc76e93f996caf538e21c5b64db5fa324958d59630@95.85.40.211:30303"; + NSString *peer2 = @"enode://b9de2532421f15ac55da9d9a7cddc0dc08b0d646d631fd7ab2a170bd2163fb86b095dd8bde66b857592812f7cd9539f2919b6c64bc1a784a1d1c6ec8137681ed@188.166.229.119:30303"; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(void) { + AddPeer((char *) [peer1 UTF8String]); + AddPeer((char *) [peer2 UTF8String]); + }); + onResultCallback(@[[NSNull null]]); + //Screen lock notifications + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center + NULL, // observer + displayStatusChanged, // callback + CFSTR("com.apple.springboard.lockcomplete"), // event name + NULL, // object + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center + NULL, // observer + displayStatusChanged, // callback + CFSTR("com.apple.springboard.lockstate"), // event name + NULL, // object + CFNotificationSuspensionBehaviorDeliverImmediately); + return; + } +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - StartNodeRPCServer method +//////////////////////////////////////////////////////////////////// createAccount +RCT_EXPORT_METHOD(startNodeRPCServer) { +#if DEBUG + NSLog(@"StartNodeRPCServer() method called"); +#endif + StartNodeRPCServer(); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - StopNodeRPCServer method +//////////////////////////////////////////////////////////////////// createAccount +RCT_EXPORT_METHOD(stopNodeRPCServer) { +#if DEBUG + NSLog(@"StopNodeRPCServer() method called"); +#endif + StopNodeRPCServer(); +} + +RCT_EXPORT_METHOD(stopNode:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"stopNode() method called"); +#endif + // TODO: stop node + + callback(@[[NSNull null]]); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - Accounts method +//////////////////////////////////////////////////////////////////// createAccount +RCT_EXPORT_METHOD(createAccount:(NSString *)password + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"CreateAccount() method called"); +#endif + char * result = CreateAccount((char *) [password UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// recoverAccount +RCT_EXPORT_METHOD(recoverAccount:(NSString *)passphrase + password:(NSString *)password + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"RecoverAccount() method called"); +#endif + char * result = RecoverAccount((char *) [password UTF8String], (char *) [passphrase UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// login +RCT_EXPORT_METHOD(login:(NSString *)address + password:(NSString *)password + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"Login() method called"); +#endif + char * result = Login((char *) [address UTF8String], (char *) [password UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - Complete Transaction +//////////////////////////////////////////////////////////////////// completeTransaction +RCT_EXPORT_METHOD(completeTransaction:(NSString *)hash + password:(NSString *)password + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"CompleteTransaction() method called"); +#endif + char * result = CompleteTransaction((char *) [hash UTF8String], (char *) [password UTF8String]); + callback(@[[NSString stringWithUTF8String: result]]); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - Discard Transaction +//////////////////////////////////////////////////////////////////// completeTransaction +RCT_EXPORT_METHOD(discardTransaction:(NSString *)id) { +#if DEBUG + NSLog(@"DiscardTransaction() method called"); +#endif + DiscardTransaction((char *) [id UTF8String]); +} + +//////////////////////////////////////////////////////////////////// +#pragma mark - only android methods +//////////////////////////////////////////////////////////////////// +RCT_EXPORT_METHOD(setAdjustResize) { +#if DEBUG + NSLog(@"setAdjustResize() works only on Android"); +#endif +} + +RCT_EXPORT_METHOD(setAdjustPan) { +#if DEBUG + NSLog(@"setAdjustPan() works only on Android"); +#endif +} + +RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) { +#if DEBUG + NSLog(@"setSoftInputMode() works only on Android"); +#endif +} + ++ (void)signalEvent:(const char *) signal +{ + if(!signal){ +#if DEBUG + NSLog(@"SignalEvent nil"); +#endif + return; + } + + NSString *sig = [NSString stringWithUTF8String:signal]; +#if DEBUG + NSLog(@"SignalEvent"); + NSLog(sig); +#endif + [bridge.eventDispatcher sendAppEventWithName:@"gethEvent" + body:@{@"jsonEvent": sig}]; + + return; +} + +@end diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj b/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..fc96fb457f --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj @@ -0,0 +1,300 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 206C9F3E1D474E910063E3E6 /* RCTStatus.h in Copy Files */ = {isa = PBXBuildFile; fileRef = 206C9F3D1D474E910063E3E6 /* RCTStatus.h */; }; + 206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 206C9F3F1D474E910063E3E6 /* RCTStatus.m */; }; + CE4E31B11D86951A0033ED64 /* Statusgo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4E31B01D86951A0033ED64 /* Statusgo.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 206C9F381D474E910063E3E6 /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + 206C9F3E1D474E910063E3E6 /* RCTStatus.h in Copy Files */, + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 206C9F3A1D474E910063E3E6 /* libRCTStatus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTStatus.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 206C9F3D1D474E910063E3E6 /* RCTStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTStatus.h; sourceTree = ""; }; + 206C9F3F1D474E910063E3E6 /* RCTStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTStatus.m; sourceTree = ""; }; + CE4E31B01D86951A0033ED64 /* Statusgo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Statusgo.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 206C9F371D474E910063E3E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE4E31B11D86951A0033ED64 /* Statusgo.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 206C9F311D474E910063E3E6 = { + isa = PBXGroup; + children = ( + CE4E31B01D86951A0033ED64 /* Statusgo.framework */, + 206C9F3C1D474E910063E3E6 /* Status */, + 206C9F3B1D474E910063E3E6 /* Products */, + ); + sourceTree = ""; + }; + 206C9F3B1D474E910063E3E6 /* Products */ = { + isa = PBXGroup; + children = ( + 206C9F3A1D474E910063E3E6 /* libRCTStatus.a */, + ); + name = Products; + sourceTree = ""; + }; + 206C9F3C1D474E910063E3E6 /* Status */ = { + isa = PBXGroup; + children = ( + 206C9F3D1D474E910063E3E6 /* RCTStatus.h */, + 206C9F3F1D474E910063E3E6 /* RCTStatus.m */, + ); + name = Status; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 206C9F391D474E910063E3E6 /* RCTStatus */ = { + isa = PBXNativeTarget; + buildConfigurationList = 206C9F431D474E910063E3E6 /* Build configuration list for PBXNativeTarget "RCTStatus" */; + buildPhases = ( + 206C9F361D474E910063E3E6 /* Sources */, + 206C9F371D474E910063E3E6 /* Frameworks */, + 206C9F381D474E910063E3E6 /* Copy Files */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RCTStatus; + productName = RCTStatus; + productReference = 206C9F3A1D474E910063E3E6 /* libRCTStatus.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 206C9F321D474E910063E3E6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Status.im; + TargetAttributes = { + 206C9F391D474E910063E3E6 = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 206C9F351D474E910063E3E6 /* Build configuration list for PBXProject "RCTStatus" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 206C9F311D474E910063E3E6; + productRefGroup = 206C9F3B1D474E910063E3E6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 206C9F391D474E910063E3E6 /* RCTStatus */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 206C9F361D474E910063E3E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 206C9F401D474E910063E3E6 /* RCTStatus.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 206C9F411D474E910063E3E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 206C9F421D474E910063E3E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "armv7 armv7s arm64"; + "VALID_ARCHS[sdk=*]" = "armv7 armv7s arm64"; + }; + name = Release; + }; + 206C9F441D474E910063E3E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + FRAMEWORK_VERSION = A; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../../../node_modules/React/**", + "$(SRCROOT)/../../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-ObjC", + "$(inherited)", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + VALID_ARCHS = "armv7 armv7s arm64"; + }; + name = Debug; + }; + 206C9F451D474E910063E3E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + FRAMEWORK_VERSION = A; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../../../node_modules/React/**", + "$(SRCROOT)/../../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + ONLY_ACTIVE_ARCH = NO; + OTHER_LDFLAGS = ( + "-ObjC", + "$(inherited)", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + VALID_ARCHS = "armv7 armv7s arm64"; + "VALID_ARCHS[sdk=*]" = "armv7 armv7s arm64"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 206C9F351D474E910063E3E6 /* Build configuration list for PBXProject "RCTStatus" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 206C9F411D474E910063E3E6 /* Debug */, + 206C9F421D474E910063E3E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 206C9F431D474E910063E3E6 /* Build configuration list for PBXNativeTarget "RCTStatus" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 206C9F441D474E910063E3E6 /* Debug */, + 206C9F451D474E910063E3E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 206C9F321D474E910063E3E6 /* Project object */; +} diff --git a/modules/react-native-status/ios/RCTStatus/pom.xml b/modules/react-native-status/ios/RCTStatus/pom.xml new file mode 100644 index 0000000000..1722fea199 --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + org.reactjs.native.example + Messenger + 1.0-SNAPSHOT + + + + ci.status.im + http://139.162.11.12:8081/artifactory/libs-release-local/ + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + + status-im + status-go-ios-simulator + 1.1.0-14-g85f29c2 + zip + true + ./ + + + + + + + diff --git a/modules/react-native-status/package.json b/modules/react-native-status/package.json new file mode 100644 index 0000000000..1df0d0b103 --- /dev/null +++ b/modules/react-native-status/package.json @@ -0,0 +1,21 @@ +{ + "private": true, + "nativePackage": true, + "name": "react-native-status", + "version": "1.0.0", + "description": "Manage Geth, Jail & Background Service", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/status-im/react-native-status.git" + }, + "author": "", + "license": "", + "bugs": { + "url": "https://github.com/status-im/react-native-status/issues" + }, + "homepage": "https://github.com/status-im/react-native-status#readme" +} \ No newline at end of file diff --git a/package.json b/package.json index f069c032a4..fe0035cde6 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,131 @@ { - "name": "SyngIm", + "name": "StatusIm", "version": "0.0.1", "private": true, "scripts": { - "start": "node_modules/react-native/packager/packager.sh" + "start": "node_modules/react-native/packager/packager.sh", + "postinstall": "./postinstall.sh" + }, + "devDependencies": { + "rn-nodeify": "^6.0.1" }, "dependencies": { - "awesome-phonenumber": "^1.0.12", - "react-native": "^0.22.0", - "react-native-action-button": "^1.1.3", - "react-native-circle-checkbox": "^0.1.3", - "react-native-contacts": "^0.2.1", + "assert": "^1.4.1", + "awesome-phonenumber": "^1.0.13", + "babel-core": "6.20.0", + "babel-generator": "6.20.0", + "babel-helper-builder-react-jsx": "6.18.0", + "babel-plugin-transform-es2015-block-scoping": "6.15.0", + "babel-plugin-transform-es2015-parameters": "6.18.0", + "babel-plugin-transform-flow-strip-types": "6.18.0", + "babel-plugin-transform-regenerator": "6.20.0", + "babel-preset-react-native": "1.9.0", + "browserify-zlib": "^0.1.4", + "buffer": "^3.6.0", + "chance": "1.0.4", + "console-browserify": "^1.1.0", + "constants-browserify": "0.0.1", + "dns.js": "^1.0.1", + "domain-browser": "^1.1.7", + "eccjs": "0.3.1", + "events": "^1.1.1", + "homoglyph-finder": "^1.1.1", + "https-browserify": "0.0.1", + "identicon.js": "github:status-im/identicon.js", + "instabug-reactnative": "git+https://github.com/Instabug/instabug-reactnative.git", + "os-browserify": "^0.1.2", + "path-browserify": "0.0.0", + "process": "^0.11.5", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "re-natal": "0.2.38", + "react": "^15.3.1", + "react-dom": "^15.3.1", + "react-native": "^0.38.0", + "react-native-action-button": "2.0.13", + "react-native-android-sms-listener": "github:adrian-tiberius/react-native-android-sms-listener#listener-bugfix", + "react-native-camera": "git+https://github.com/alwx/react-native-camera.git", + "react-native-circle-checkbox": "github:paramoshkinandrew/ReactNativeCircleCheckbox", + "react-native-contacts": "^0.2.4", + "react-native-crypto": "^2.0.1", + "react-native-dialogs": "0.0.16", + "react-native-drawer-layout": "^1.1.0", + "react-native-emoji-picker": "^0.2.2", + "react-native-fs": "^1.5.1", + "react-native-http": "github:tradle/react-native-http#834492d", "react-native-i18n": "0.0.8", - "react-native-invertible-scroll-view": "^0.2.0", - "react-native-loading-spinner-overlay": "0.0.6", - "react-native-randombytes": "^2.0.0", - "react-native-vector-icons": "^1.3.3", - "realm": "^0.11.0" + "react-native-image-crop-picker": "^0.9.4", + "react-native-image-resizer": "github:danieldunderfelt/react-native-image-resizer", + "react-native-invertible-scroll-view": "^1.0.0", + "react-native-level-fs": "^2.0.1", + "react-native-linear-gradient": "1.5.7", + "react-native-orientation": "github:youennPennarun/react-native-orientation", + "react-native-qrcode": "^0.2.2", + "react-native-randombytes": "^2.1.0", + "react-native-share": "^1.0.17", + "react-native-splash-screen": "^1.0.9", + "react-native-swiper": "1.5.3", + "react-native-tcp": "^2.0.4", + "react-native-udp": "^1.2.6", + "react-native-vector-icons": "^2.0.3", + "react-native-webview-bridge": "github:status-im/react-native-webview-bridge#0.33.4", + "readable-stream": "^1.0.33", + "realm": "^0.14.3", + "stream-browserify": "^1.0.0", + "timers-browserify": "^1.4.2", + "tty-browserify": "0.0.0", + "url": "^0.10.3", + "vm-browserify": "0.0.4", + "web3": "0.16.0" + }, + "browser": { + "crypto": "react-native-crypto", + "_stream_transform": "readable-stream/transform", + "_stream_readable": "readable-stream/readable", + "_stream_writable": "readable-stream/writable", + "_stream_duplex": "readable-stream/duplex", + "_stream_passthrough": "readable-stream/passthrough", + "stream": "stream-browserify", + "zlib": "browserify-zlib", + "console": "console-browserify", + "constants": "constants-browserify", + "dns": "dns.js", + "domain": "domain-browser", + "http": "react-native-http", + "https": "https-browserify", + "os": "os-browserify", + "path": "path-browserify", + "querystring": "querystring-es3", + "fs": "react-native-level-fs", + "dgram": "react-native-udp", + "timers": "timers-browserify", + "tty": "tty-browserify", + "vm": "vm-browserify", + "net": "react-native-tcp" + }, + "react-native": { + "crypto": "react-native-crypto", + "_stream_transform": "readable-stream/transform", + "_stream_readable": "readable-stream/readable", + "_stream_writable": "readable-stream/writable", + "_stream_duplex": "readable-stream/duplex", + "_stream_passthrough": "readable-stream/passthrough", + "stream": "stream-browserify", + "zlib": "browserify-zlib", + "console": "console-browserify", + "constants": "constants-browserify", + "dns": "dns.js", + "domain": "domain-browser", + "http": "react-native-http", + "https": "https-browserify", + "os": "os-browserify", + "path": "path-browserify", + "querystring": "querystring-es3", + "fs": "react-native-level-fs", + "dgram": "react-native-udp", + "timers": "timers-browserify", + "tty": "tty-browserify", + "vm": "vm-browserify", + "net": "react-native-tcp" } } diff --git a/postinstall.sh b/postinstall.sh new file mode 100755 index 0000000000..5e2ff36422 --- /dev/null +++ b/postinstall.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# rn-nodeify +# temporary hack due to https://github.com/facebook/react-native/issues/4968 +./node_modules/.bin/rn-nodeify --install --hack; +npm install --save react@15.3.1; +npm install --save react-native-tcp@2.0.4; + +# symlink for re-natal +if ! [ -f re-natal ]; then + ln -s ./node_modules/re-natal/index.js re-natal; +else + echo "exists" +fi diff --git a/project.clj b/project.clj index a66ad0cb32..8f37456580 100644 --- a/project.clj +++ b/project.clj @@ -1,50 +1,77 @@ -(defproject syng-im "0.1.0-SNAPSHOT" +(defproject status-im "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} - :dependencies [[org.clojure/clojure "1.7.0"] - [org.clojure/clojurescript "1.7.170"] - [reagent "0.5.1" :exclusions [cljsjs/react]] - [re-frame "0.6.0"] - [prismatic/schema "1.0.4"] - ^{:voom {:repo "https://github.com/status-im/status-lib.git" - :branch "master"}} - [syng-im/protocol "0.1.1-20160430_080316-gf359cb7"] - [natal-shell "0.1.6"]] - :plugins [[lein-cljsbuild "1.1.1"] - [lein-figwheel "0.5.0-2"] - [lein-voom "0.1.0-20160311_203101-g259fbfc"]] + :dependencies [[org.clojure/clojure "1.9.0-alpha13"] + [org.clojure/clojurescript "1.9.229"] + [reagent "0.6.0" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server]] + [re-frame "0.7.0"] + [natal-shell "0.3.0"] + [com.andrewmcveigh/cljs-time "0.4.0"] + [tailrecursion/cljs-priority-map "1.2.0"] + [com.taoensso/timbre "4.7.4"]] + :plugins [[lein-cljsbuild "1.1.4"] + [lein-figwheel "0.5.8"]] :clean-targets ["target/" "index.ios.js" "index.android.js"] :aliases {"prod-build" ^{:doc "Recompile code with prod profile."} ["do" "clean" ["with-profile" "prod" "cljsbuild" "once" "ios"] ["with-profile" "prod" "cljsbuild" "once" "android"]]} + :test-paths ["test/clj"] :figwheel {:nrepl-port 7888} - :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.0-2"] - [com.cemerick/piggieback "0.2.1"]] + :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.8"] + [com.cemerick/piggieback "0.2.1"] + [io.appium/java-client "3.4.1"] + [hawk "0.2.10"]] + :plugins [[lein-doo "0.1.6"]] :source-paths ["src" "env/dev"] - :cljsbuild {:builds {:ios {:source-paths ["src" "env/dev"] - :figwheel true - :compiler {:output-to "target/ios/not-used.js" - :main "env.ios.main" - :output-dir "target/ios" - :optimizations :none}} - :android {:source-paths ["src" "env/dev"] - :figwheel true - :compiler {:output-to "target/android/not-used.js" - :main "env.android.main" - :output-dir "target/android" - :optimizations :none}}}} - :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}} - :prod {:cljsbuild {:builds {:ios {:source-paths ["src" "env/prod"] - :compiler {:output-to "index.ios.js" - :main "env.ios.main" - :output-dir "target/ios" - :optimizations :simple}} - :android {:source-paths ["src" "env/prod"] - :compiler {:output-to "index.android.js" - :main "env.android.main" - :output-dir "target/android" - :optimizations :simple}}}} - }}) + :cljsbuild {:builds [{:id :ios + :source-paths ["src" "env/dev"] + :figwheel true + :compiler {:output-to "target/ios/not-used.js" + :main "env.ios.main" + :output-dir "target/ios" + :optimizations :none}} + {:id :android + :source-paths ["src" "env/dev"] + :figwheel true + :compiler {:output-to "target/android/not-used.js" + :main "env.android.main" + :output-dir "target/android" + :optimizations :none}} + {:id :android-test + :source-paths ["src" "env/dev"] + :compiler {:output-to "target/android/not-used.js" + :main "env.android-test.main" + :output-dir "target/android-test" + :optimizations :none}} + {:id :test + :source-paths ["src" "test/cljs"] + :compiler + {:main status-im.test.runner + :output-to "target/test/test.js" + :optimizations :none + :target :nodejs}}]} + :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl] + :timeout 240000}} + :prod {:cljsbuild {:builds [{:id "ios" + :source-paths ["src" "env/prod"] + :compiler {:output-to "index.ios.js" + :main "env.ios.main" + :output-dir "target/ios-prod" + :static-fns true + :optimize-constants true + :optimizations :advanced + :externs ["externs/externs.js"] + :closure-defines {"goog.DEBUG" false}}} + {:id "android" + :source-paths ["src" "env/prod"] + :compiler {:output-to "index.android.js" + :main "env.android.main" + :output-dir "target/android-prod" + :static-fns true + :optimize-constants true + :optimizations :advanced + :externs ["externs/externs.js"] + :closure-defines {"goog.DEBUG" false}}}]}}}) diff --git a/resources/commands.js b/resources/commands.js new file mode 100644 index 0000000000..c39c5bb889 --- /dev/null +++ b/resources/commands.js @@ -0,0 +1,898 @@ +I18n.translations = { + en: { + location_title: 'Location', + location_description: 'Share your location', + location_address: 'Address', + + browse_title: 'Browser', + browse_description: 'Launch the browser', + + send_title: 'Send ETH', + send_description: 'Send a payment', + + request_title: 'Request ETH', + request_description: 'Request a payment', + request_requesting: 'Requesting ', + + validation_title: 'Amount', + validation_amount_specified: 'Amount must be specified', + validation_invalid_number: 'Amount is not valid number', + validation_insufficient_amount: 'Insufficient funds for gas * price + value (balance ' + }, + ru: { + location_title: 'Местоположение', + location_description: 'Поделитесь своим местоположением', + location_address: 'Адрес', + + browse_title: 'Браузер', + browse_description: 'Запуск браузера', + + send_title: 'Отправить ETH', + send_description: 'Отправить платеж', + + request_title: 'Запросить ETH', + request_description: 'Запросить платеж', + request_requesting: 'Запрос ', + + validation_title: 'Сумма', + validation_amount_specified: 'Необходимо указать сумму', + validation_invalid_number: 'Сумма не является действительным числом', + validation_insufficient_amount: 'Недостаточно ETH на балансе (' + }, + af: { + location_title: 'Ligging', + location_description: 'Deel jou ligging', + location_address: 'Addres', + + browse_title: 'Webblaaier', + browse_description: 'Begin die webblaaier', + + send_title: 'Stuur ETH', + send_description: 'Stuur \'n betaling', + + request_title: 'Versoek ETH', + request_description: 'Versoek \'n betaling', + request_requesting: 'Besig met versoek ', + + validation_title: 'Bedrag', + validation_amount_specified: 'Bedrag moet gespesifiseer word', + validation_invalid_number: 'Bedrag is nie \'n geldige syfer nie', + validation_insufficient_amount: 'Nie genoeg ETH in rekening nie (' + }, + ar: { + location_title: 'الموقع', + location_description: 'شارك موقعك', + location_address: 'العنوان', + + browse_title: 'المتصفح', + browse_description: 'تشغيل المتصفح', + + send_title: 'إرسال ETH', + send_description: 'إرسال مدفوعات', + + request_title: 'طلب ETH', + request_description: 'طلب مدفوعات', + request_requesting: 'مُطَالَبَة ', + + validation_title: 'المبلغ', + validation_amount_specified: 'يجب تحديد المبلغ', + validation_invalid_number: 'المبلغ المحدد غير صحيح', + validation_insufficient_amount: 'لا يوجد ETH كافي بالحساب (' + }, + 'zh-hant': { + location_title: '位置', + location_description: '分享您的位置', + location_address: '地址', + + browse_title: '流覽器', + browse_description: '啟動流覽器', + + send_title: '發送 ETH', + send_description: '發送一筆付款', + + request_title: '請求 ETH', + request_description: '請求一筆付款', + request_requesting: '正在請求', + + validation_title: '金額', + validation_amount_specified: 'ي未指定金額', + validation_invalid_number: '金額數字無效', + validation_insufficient_amount: '餘額中 ETH 不足 (' + }, + 'zh-hans': { + location_title: '位置', + location_description: '分享你的位置', + location_address: '地址', + + browse_title: '浏览器', + browse_description: '启动浏览器', + + send_title: '发送ETH', + send_description: '付款', + + request_title: '请求ETH', + request_description: '要求付款', + request_requesting: '正在请求', + + validation_title: '金額', + validation_amount_specified: '必须指定金额', + validation_invalid_number: '金额不是有效数字', + validation_insufficient_amount: 'ETH余额不足 (' + }, + 'zh-yue': { + location_title: '所在位置', + location_description: '分享所在位置', + location_address: '地址', + + browse_title: '瀏覽器', + browse_description: '啟動瀏覽器', + + send_title: '發送ETH', + send_description: '發送付款', + + request_title: '徵求ETH', + request_description: '徵求付款', + request_requesting: '徵求中', + + validation_title: '金額', + validation_amount_specified: '必須指定金額', + validation_invalid_number: '指定金額並非有效數字', + validation_insufficient_amount: '沒有足夠ETH餘額 (' + }, + 'zh-wuu': { + location_title: '位置', + location_description: '分享您的位置', + location_address: '地址', + + browse_title: '浏览器', + browse_description: '启动浏览器', + + send_title: '发送ETH', + send_description: '发送付款', + + request_title: '请求ETH', + request_description: '请求付款', + request_requesting: '请求中 ', + + validation_title: '金额', + validation_amount_specified: '金额必须明确', + validation_invalid_number: '金额不是一个有效数字', + validation_insufficient_amount: 'ETH余额不足 (' + }, + nl: { + location_title: 'Locatie', + location_description: 'Deel je locatie', + location_address: 'Adres', + + browse_title: 'Browser', + browse_description: 'Start de browser', + + send_title: 'Stuur ETH', + send_description: 'Stuur een betaling', + + request_title: 'Vraag ETH aan', + request_description: 'Vraag om een betaling', + request_requesting: 'Wordt aangevraagd ', + + validation_title: 'Bedrag', + validation_amount_specified: 'Bedrag moet worden opgegeven', + validation_invalid_number: 'Bedrag is geen geldig nummer', + validation_insufficient_amount: 'Niet genoeg ETH op saldo (' + }, + fr: { + location_title: 'Emplacement', + location_description: 'Partager votre emplacement', + location_address: 'Adresse', + + browse_title: 'Navigateur', + browse_description: 'Lancer le navigateur', + + send_title: 'Envoyer l\'ETH', + send_description: 'Envoyer un paiement', + + request_title: 'Demander l\'ETH', + request_description: 'Demander un paiement', + request_requesting: 'Demande en cours...', + + validation_title: 'Montant', + validation_amount_specified: 'Le montant doit être spécifié', + validation_invalid_number: 'Le montant n\'est pas un nombre valide', + validation_insufficient_amount: 'Pas assez d\'ETH sur le solde (' + }, + de: { + location_title: 'Ort', + location_description: 'Teilen Sie Ihren Ort', + location_address: 'Adresse', + + browse_title: 'Browser', + browse_description: 'Browser starten', + + send_title: 'ETH abschicken', + send_description: 'Zahlung senden', + + request_title: 'ETH anfragen', + request_description: 'Zahlung anfragen', + request_requesting: 'Frage an', + + validation_title: 'Betrag', + validation_amount_specified: 'Betrag muss angegeben werden', + validation_invalid_number: 'Betrag ist keine gültige Zahl', + validation_insufficient_amount: 'Nicht genügend ETH auf dem Konto (' + }, + hi: { + location_title: 'स्थान', + location_description: 'अपना स्थान साझा करें', + location_address: 'पता', + + browse_title: 'ब्राउज़र', + browse_description: 'ब्राउज़र लॉन्च करें', + + send_title: 'ETH भेजें', + send_description: 'भुगतान भेजें', + + request_title: 'ETH का अनुरोध करें', + request_description: 'भुगतान का अनुरोध करें', + request_requesting: 'अनुरोध किया जा रहा है', + + validation_title: 'राशि', + validation_amount_specified: 'राशि निर्दिष्ट की जानी चाहिए', + validation_invalid_number: 'राशि वैध संख्या नहीं है', + validation_insufficient_amount: 'बैलेंस पर पर्याप्त ETH नहीं है (' + }, + hu: { + location_title: 'Helyszín', + location_description: 'Helyszín megosztása', + location_address: 'Cím', + + browse_title: 'Böngésző', + browse_description: 'Böngésző indítása', + + send_title: 'ETH küldése', + send_description: 'Kifizetés küldése', + + request_title: 'ETH igénylése', + request_description: 'Fizetés igénylése', + request_requesting: 'Igénylés ', + + validation_title: 'Összeg', + validation_amount_specified: 'Az összeget meg kell határozni', + validation_invalid_number: 'Az összeg nem egy elfogadott szám', + validation_insufficient_amount: 'Nincs elég ETH a számlán (' + }, + it: { + location_title: 'Posizione', + location_description: 'Condividi la tua posizione', + location_address: 'Indirizzo', + + browse_title: 'Browser', + browse_description: 'Lancia il browser', + + send_title: 'Invia ETH', + send_description: 'Invia un pagamento', + + request_title: 'Richiedi ETH', + request_description: 'Richiedi un pagamento', + request_requesting: 'Richiesta in corso ', + + validation_title: 'Ammontare', + validation_amount_specified: 'L\'ammontare deve essere specificato', + validation_invalid_number: 'L\'ammontare non è un numero valido', + validation_insufficient_amount: 'ETH insufficiente sul bilancio (' + }, + ja: { + location_title: '位置', + location_description: '位置情報を共有', + location_address: 'アドレス', + + browse_title: 'ブラウザ', + browse_description: 'ブラウザを起動', + + send_title: 'ETHを送信', + send_description: '支払いを送信', + + request_title: 'ETHをリクエスト', + request_description: '支払いをリクエスト', + request_requesting: 'リクエスト中', + + validation_title: '金額', + validation_amount_specified: '金額を特定する必要があります', + validation_invalid_number: '金額は有効な数字ではありません', + validation_insufficient_amount: '残高に十分なETHがありません(' + }, + ko: { + location_title: '위치', + location_description: '내 위치 공유하기', + location_address: '주소', + + browse_title: '브라우저', + browse_description: '브라우저 시작하기', + + send_title: 'ETH 보내기', + send_description: '지불금 보내기', + + request_title: 'ETH 요청', + request_description: '지불금 요청', + request_requesting: '요청 중 ', + + validation_title: '금액', + validation_amount_specified: '금액을 지정해야 합니다', + validation_invalid_number: '금액이 유효한 숫자가 아닙니다', + validation_insufficient_amount: 'ETH 잔고가 부족합니다 (' + }, + pl: { + location_title: 'Lokalizacja', + location_description: 'Udostępnij swoją lokalizację', + location_address: 'Adres', + + browse_title: 'Przeglądarka', + browse_description: 'Uruchom przeglądarkę', + + send_title: 'Wyślij ETH', + send_description: 'Wyślij płatność', + + request_title: 'Poproś o ETH', + request_description: 'Poproś o płatność', + request_requesting: 'Przesyłanie prośby', + + validation_title: 'Kwota', + validation_amount_specified: 'Należy określić kwotę', + validation_invalid_number: 'Kwota nie jest prawidłową liczbą', + validation_insufficient_amount: 'Brak wystarczającej liczby ETH na koncie (' + }, + 'pt-br': { + location_title: 'Localização', + location_description: 'Compartilhar sua localização', + location_address: 'Endereço', + + browse_title: 'Navegador', + browse_description: 'Abrir o navegador', + + send_title: 'Enviar ETH', + send_description: 'Enviar um pagamento', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar um pagamento', + request_requesting: 'Solicitando', + + validation_title: 'Quantia', + validation_amount_specified: 'É necessário especificar a quantia', + validation_invalid_number: 'A quantia não é um número válido', + validation_insufficient_amount: 'ETH insuficiente no saldo (' + }, + 'pt-pt': { + location_title: 'Location', + location_description: 'Partilhar a sua localização', + location_address: 'Endereço', + + browse_title: 'Navegador', + browse_description: 'Abrir o navegador', + + send_title: 'Enviar ETH', + send_description: 'Enviar um pagamento', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar um pagamento', + request_requesting: 'A solicitar', + + validation_title: 'Montante', + validation_amount_specified: 'O montante deve ser especificado', + validation_invalid_number: 'O montante não é um número válido', + validation_insufficient_amount: 'Não há ETH suficiente no saldo (' + }, + ro: { + location_title: 'Locație', + location_description: "Partajează locația", + location_address: 'Adresă', + + browse_title: 'Browser', + browse_description: 'Lansare browser', + + send_title: 'Trimite ETH', + send_description: 'Trimite o plată', + + request_title: 'Solicită ETH', + request_description: 'Solicită o plată', + request_requesting: 'Se solicită', + + validation_title: 'Sumă', + validation_amount_specified: 'Trebuie menționată o sumă', + validation_invalid_number: 'Suma nu are forma unui număr valid', + validation_insufficient_amount: 'Sold ETH insuficient (' + }, + sl: { + location_title: 'Lokacija', + location_description: 'Deli svojo lokacijo', + location_address: 'Naslov', + + browse_title: 'Brskalnik', + browse_description: 'Zaženi brskalnik', + + send_title: 'Pošlji ETH', + send_description: 'Pošlji plačilo', + + request_title: 'Zahtevaj ETH', + request_description: 'Zahtevaj plačilo', + request_requesting: 'Zahtevam ', + + validation_title: 'Vsota', + validation_amount_specified: 'Vsota mora biti izrecno navedena', + validation_invalid_number: 'Vsota ni veljavna številka', + validation_insufficient_amount: 'Stanje ETH na računu je prenizko (' + }, + es: { + location_title: 'Ubicación', + location_description: 'Comparte tu ubicación', + location_address: 'Dirección', + + browse_title: 'Navegador', + browse_description: 'Iniciar el navegador', + + send_title: 'Enviar ETH ', + send_description: 'Enviar un pago', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar un pago', + request_requesting: 'Solicitando', + + validation_title: 'Cantidad', + validation_amount_specified: 'Hay que especificar la cantidad', + validation_invalid_number: 'La cantidad no es un número válido', + validation_insufficient_amount: 'No hay suficiente ETH en conjunto (' + }, + 'es-ar': { + location_title: 'Ubicación', + location_description: 'Comparte tu ubicación', + location_address: 'Dirección', + + browse_title: 'Navegador', + browse_description: 'Iniciar navegador', + + send_title: 'Enviar ETH', + send_description: 'Enviar un pago', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar un pago', + request_requesting: 'Solicitando ', + + validation_title: 'Monto', + validation_amount_specified: 'Debes especificar el monto', + validation_invalid_number: 'El monto no es un número válido', + validation_insufficient_amount: 'No tienes suficiente ETH en tu saldo (' + }, + sw: { + location_title: 'Eneo', + location_description: 'Shiriki eneo lako', + location_address: 'Anwani', + + browse_title: 'Programu ya utafutaji', + browse_description: 'Zindua programu ya utafutaji', + + send_title: 'Tuma ETH', + send_description: 'Tuma malipo', + + request_title: 'Omba ETH', + request_description: 'Omba malipo', + request_requesting: 'Kuomba ', + + validation_title: 'Kiasi', + validation_amount_specified: 'Kiasi lazima kifafanuliwe', + validation_invalid_number: 'Kiasi si nambari halali', + validation_insufficient_amount: 'ETH haitoshi kwenye salio (' + }, + sv: { + location_title: 'Plats', + location_description: 'Dela din plats', + location_address: 'Adress', + + browse_title: 'Webbläsare', + browse_description: 'Starta webbläsaren', + + send_title: 'Skicka ETH', + send_description: 'Skicka en betalning', + + request_title: 'Begär ETH', + request_description: 'Begär en betalning', + request_requesting: 'Begär ', + + validation_title: 'Belopp', + validation_amount_specified: 'Beloppet måste anges', + validation_invalid_number: 'Beloppet är inte ett giltigt nummer', + validation_insufficient_amount: 'Inte tillräcklig ETH på balansen (' + }, + 'fr-ch': { + location_title: 'Emplacement', + location_description: 'Partagez votre emplacement', + location_address: 'Adresse', + + browse_title: 'Navigateur', + browse_description: 'Lancer le navigateur', + + send_title: 'Envoyer des ETH', + send_description: 'Envoyer un paiement', + + request_title: 'Demander des ETH', + request_description: 'Demander un paiement', + request_requesting: 'Demande ', + + validation_title: 'Montant', + validation_amount_specified: 'Le montant doit être spécifié', + validation_invalid_number: 'Le montant n\'est pas un nombre valable', + validation_insufficient_amount: 'Pas assez d\'ETH sur le solde (' + }, + 'de-ch': { + location_title: 'Standort', + location_description: 'Teile dein Standort', + location_address: 'Adresse', + + browse_title: 'Browser', + browse_description: 'Starte den Browser', + + send_title: 'Sende ETH', + send_description: 'Senden eine Zahlung', + + request_title: 'Fordere ETH an', + request_description: 'Eine Zahlung anfordern', + request_requesting: 'Anfordern ', + + validation_title: 'Betrag', + validation_amount_specified: 'Der Betrag muss angegeben werden', + validation_invalid_number: 'Der Betrag ist nicht gültig', + validation_insufficient_amount: 'Nicht genug ETH vorhanden (' + }, + 'it-ch': { + location_title: 'Posizione', + location_description: 'Condividi la tua posizione', + location_address: 'Indirizzo', + + browse_title: 'Browser', + browse_description: 'Avvia il browser', + + send_title: 'Invia ETH', + send_description: 'Invia un pagamento', + + request_title: 'Richiedi ETH', + request_description: 'Richiedi un pagamento', + request_requesting: 'Richiesta in corso... ', + + validation_title: 'Importo', + validation_amount_specified: 'Specificare l\'importo', + validation_invalid_number: 'Importo inserito non valido', + validation_insufficient_amount: 'Saldo ETH non sufficiente (' + }, + th: { + location_title: 'ตำแหน่ง', + location_description: 'แชร์ตำแหน่งของคุณ', + location_address: 'ที่อยู่', + + browse_title: 'เบราว์เซอร์', + browse_description: 'เปิดเบราว์เซอร์', + + send_title: 'ส่ง ETH', + send_description: 'ส่งการชำระเงิน', + + request_title: 'ร้องขอ ETH', + request_description: 'ร้องขอการชำระเงิน', + request_requesting: 'กำลังร้องขอ ', + + validation_title: 'จำนวน', + validation_amount_specified: 'จำเป็นต้องระบุจำนวน', + validation_invalid_number: 'จำนวนไม่ใช่หมายเลขที่ถูกต้อง', + validation_insufficient_amount: 'มี ETH ไม่เพียงพอในยอดคงเหลือ (' + }, + tr: { + location_title: 'Konum', + location_description: 'Konumunuzu paylaşın', + location_address: 'Adres', + + browse_title: 'Tarayıcı', + browse_description: 'Tarayıcıyı başlat', + + send_title: 'ETH gönder', + send_description: 'Bir ödeme gönder', + + request_title: 'ETH iste', + request_description: 'Bir ödeme iste', + request_requesting: 'İsteniyor ', + + validation_title: 'Miktar', + validation_amount_specified: 'Miktar belirtilmelidir', + validation_invalid_number: 'Miktar geçerli bir sayı değil', + validation_insufficient_amount: 'Yeterli ETH bakiyesi yok (' + }, + uk: { + location_title: 'Місцезнаходження', + location_description: 'Поділіться своїм місцезнаходженням', + location_address: 'Адреса', + + browse_title: 'Браузер', + browse_description: 'Запустити браузер', + + send_title: 'Надіслати ETH', + send_description: 'Надіслати платіж', + + request_title: 'Запит ETH', + request_description: 'Запит платежу', + request_requesting: 'Запит ', + + validation_title: 'Сума', + validation_amount_specified: 'Сума повинна бути вказана', + validation_invalid_number: 'Сума не дійсне число', + validation_insufficient_amount: 'Не вистачає ETH на балансі (' + }, + ur: { + location_title: 'مقام', + location_description: 'اپنا مقام بتائیں', + location_address: 'پتہ', + + browse_title: 'براؤزر', + browse_description: 'براؤزر کھولیں', + + send_title: 'ETH بھیجیں', + send_description: 'ادائیگی کریں', + + request_title: 'ETH کی درخواست دیں', + request_description: 'ادائیگی کی درخواست دیں', + request_requesting: 'درخواست کی جارہی ہے ', + + validation_title: 'رقم', + validation_amount_specified: 'رقم درج کی جانی چاہیے۔ ', + validation_invalid_number: 'رقیم درست ہندسے نہیں ہیں', + validation_insufficient_amount: 'ETH میں کافی بیلنس نہیں ہے (' + }, + vi: { + location_title: 'Vị trí', + location_description: 'Chia sẻ vị trí của bạn', + location_address: 'Địa chỉ', + + browse_title: 'Trình duyệt', + browse_description: 'Mở trình duyệt', + + send_title: 'Gửi ETH', + send_description: 'Gửi một khoản thanh toán', + + request_title: 'Yêu cầu ETH', + request_description: 'Yêu cầu một khoản thanh toán', + request_requesting: 'Đang yêu cầu ', + + validation_title: 'Số tiền', + validation_amount_specified: 'Số tiền phải được xác định', + validation_invalid_number: 'Số tiền không phải là một số hợp lệ', + validation_insufficient_amount: 'Không đủ ETH trong số dư (' + } +}; + +status.command({ + name: "location", + icon: "location", + title: I18n.t('location_title'), + description: I18n.t('location_description'), + color: "#a187d5", + preview: function (params) { + var text = status.components.text( + { + style: { + marginTop: 5, + marginHorizontal: 0, + fontSize: 14, + fontFamily: "font", + color: "black" + } + }, params.address); + var uri = "https://maps.googleapis.com/maps/api/staticmap?center=" + + params.address + + "&size=100x100&maptype=roadmap&key=AIzaSyBNsj1qoQEYPb3IllmWMAscuXW0eeuYqAA&language=en" + + "&markers=size:mid%7Ccolor:0xff0000%7Clabel:%7C" + + params.address; + + var image = status.components.image( + { + source: {uri: uri}, + style: { + width: 100, + height: 100 + } + } + ); + + return status.components.view({}, [text, image]); + } +}).param({ + name: "address", + type: status.types.TEXT, + placeholder: I18n.t('location_address') +}); + + +function browseSuggestions(params) { + if (params.url && params.url !== "undefined" && params.url != "") { + var url = params.url; + if (!/^[a-zA-Z-_]+:/.test(url)) { + url = 'http://' + url; + } + + return {webViewUrl: url}; + } +} + +status.command({ + name: "browse", + title: I18n.t('browse_title'), + description: I18n.t('browse_description'), + color: "#ffa500", + fullscreen: true, + suggestionsTrigger: 'on-send', + params: [{ + name: "url", + suggestions: browseSuggestions, + type: status.types.TEXT + }] +}); + +function validateSend(params, context) { + if (!context.to) { + return { + errors: [ + status.components.validationMessage( + "Wrong address", + "Recipient address must be specified" + ) + ] + }; + } + if (!params.amount) { + return { + errors: [ + status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_amount_specified') + ) + ] + }; + } + + try { + var val = web3.toWei(params.amount, "ether"); + } catch (err) { + return { + errors: [ + status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_invalid_number') + ) + ] + }; + } + + var balance = web3.eth.getBalance(context.from); + var estimatedGas = web3.eth.estimateGas({ + from: context.from, + to: context.to, + value: val + }); + if (bn(val).plus(bn(estimatedGas)).greaterThan(bn(balance))) { + return { + errors: [ + status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_insufficient_amount') + + web3.fromWei(balance, "ether") + + " ETH)" + ) + ] + }; + } +} + +function sendTransaction(params, context) { + var data = { + from: context.from, + to: context.to, + value: web3.toWei(params.amount, "ether") + }; + + try { + return web3.eth.sendTransaction(data); + } catch (err) { + return {error: err}; + } +} + +var send = { + name: "send", + icon: "money_white", + color: "#5fc48d", + title: I18n.t('send_title'), + description: I18n.t('send_description'), + params: [{ + name: "amount", + type: status.types.NUMBER + }], + preview: function (params, context) { + var amountStyle = { + fontSize: 36, + color: "#000000", + height: 40 + }; + + var amount = status.components.view( + { + flexDirection: "column", + alignItems: "flex-end", + }, + [status.components.text( + { + style: amountStyle, + font: "light" + }, + params.amount + )]); + + var currency = status.components.view( + { + style: { + flexDirection: "column", + justifyContent: "flex-end", + paddingBottom: 0 + } + }, + [status.components.text( + { + style: { + color: "#9199a0", + fontSize: 16, + lineHeight: 18, + marginLeft: 7.5 + } + }, + "ETH" + )] + ); + + return status.components.view( + { + style: { + flexDirection: "row", + justifyContent: "space-between", + marginTop: 8, + marginBottom: 8 + } + }, + [amount, currency] + ); + }, + handler: sendTransaction, + validator: validateSend +}; + +status.command(send); +status.response(send); + +status.command({ + name: "request", + title: I18n.t('request_title'), + color: "#7099e6", + description: I18n.t('request_description'), + params: [{ + name: "amount", + type: status.types.NUMBER + }], + preview: function (params) { + return status.components.text( + {}, + params.amount + " ETH" + ); + }, + handler: function (params) { + return { + event: "request", + params: [params.amount] + request: { + command: "send", + params: { + amount: params.amount + }, + content: I18n.t('request_requesting') + params.amount + "ETH" + } + }; + }, +}); diff --git a/resources/console.js b/resources/console.js new file mode 100644 index 0000000000..acf24a1e70 --- /dev/null +++ b/resources/console.js @@ -0,0 +1,1898 @@ +I18n.translations = { + en: { + phone_title: 'Send Phone Number', + phone_description: 'Find friends using your number', + phone_placeholder: 'Phone number', + + confirm_description: 'Confirmation code', + confirm_validation_title: 'Confirmation code', + confirm_validation_description: 'Wrong format', + + password_description: 'Password', + password_placeholder: 'Type your password', + password_placeholder2: 'Please re-enter password to confirm', + password_error: 'Password should be not less then 6 symbols.', + password_error1: 'Password confirmation doesn\'t match password.', + password_validation_title: 'Password', + + faucet_incorrect_title: 'Incorrect faucet', + faucet_incorrect_description: 'Please, select a one from the list' + }, + ru: { + phone_title: 'Отправить номер телефона', + phone_description: 'Найти друзей, используя ваш номер', + phone_placeholder: 'Номер телефона', + + confirm_description: 'Код подтверждения', + confirm_validation_title: 'Код подтверждения', + confirm_validation_description: 'Неверный формат', + + password_description: 'Пароль', + password_placeholder: 'Введите свой пароль', + password_placeholder2: 'Повторно введите пароль для подтверждения', + password_error: 'Пароль должен содержать не менее 6 символов', + password_error1: 'Подтверждение пароля не совпадает с паролем', + password_validation_title: 'Пароль' + + }, + af: { + phone_title: 'Stuur telefoonnommer', + phone_description: 'Vind vriende deur jou nommer te gebruik', + phone_placeholder: 'Telefoonnommer', + + confirm_description: 'Bevestigingskode', + confirm_validation_title: 'Bevestigingskode', + confirm_validation_description: 'Verkeerde formaat', + + password_description: 'Wagwoord', + password_placeholder: 'Tik jou wagwoord in', + password_placeholder2: 'Tik asseblief weer jou wagwoord in om te bevestig', + password_error: 'Wagwoord mag nie minder as 6 simbole wees nie.', + password_error1: 'Wagwoordbevestiging is nie dieselfde as wagwoord nie.', + password_validation_title: 'Wagwoord' + + }, + ar: { + phone_title: 'أرسل رقم الهاتف', + phone_description: 'ابحث عن الأصدقاء باستخدام رقمك', + phone_placeholder: 'رقم الهاتف', + + confirm_description: 'رمز التأكيد', + confirm_validation_title: 'رمز التأكيد', + confirm_validation_description: 'صيغة خاطئة', + + password_description: 'كلمة المرور', + password_placeholder: 'اكتب كلمة المرور الخاصة بك', + password_placeholder2: 'الرجاء إعادة إدخال كلمة المرور للتأكيد', + password_error: 'ينبغي أن لا تقل كلمة المرور عن 6 رموز.', + password_error1: 'لا يتوافق تأكيد كلمة المرور مع كلمة المرور.', + password_validation_title: 'كلمة المرور' + + }, + 'zh-hant': { + phone_title: '發送手機號碼', + phone_description: '使用您的號碼發現好友', + phone_placeholder: '手機號碼', + + confirm_description: '確認碼', + confirm_validation_title: '確認碼', + confirm_validation_description: '格式錯誤', + + password_description: '密碼', + password_placeholder: '鍵入您的密碼', + password_placeholder2: '重新鍵入您的密碼', + password_error: '密碼不得短於6個字元。', + password_error1: '確認密碼與鍵入的密碼不一致。', + password_validation_title: '密碼' + + }, + 'zh-hans': { + phone_title: '发送电话号码', + phone_description: '用你的号码来查找朋友', + phone_placeholder: '电话号码', + + confirm_description: '确认码', + confirm_validation_title: '确认码', + confirm_validation_description: '格式错误', + + password_description: '密码', + password_placeholder: '输入密码', + password_placeholder2: '请重新输入密码以确认', + password_error: '密码应不少于6个字符。', + password_error1: '密码确认信息与密码不匹配。', + password_validation_title: '密码' + + }, + 'zh-yue': { + phone_title: '發送電話號碼', + phone_description: '使用本電話號碼查找好友', + phone_placeholder: '電話號碼', + + confirm_description: '驗證碼', + confirm_validation_title: '驗證碼', + confirm_validation_description: '格式錯誤', + + password_description: '密碼', + password_placeholder: '輸入密碼', + password_placeholder2: '請重新輸入密碼確認', + password_error: '密碼不能短於6個字符.', + password_error1: '確認密碼與輸入密碼不符.', + password_validation_title: '密碼' + + }, + 'zh-wuu': { + phone_title: '发送电话号码', + phone_description: '用您的号码查找朋友', + phone_placeholder: '电话号码', + + confirm_description: '确认码', + confirm_validation_title: '确认码', + confirm_validation_description: '错误格式', + + password_description: '密码', + password_placeholder: '输入密码', + password_placeholder2: '请重新输入密码确认', + password_error: '密码应不小于6个字符。', + password_error1: '密码确认不匹配。', + password_validation_title: '密码' + + }, + nl: { + phone_title: 'Stuur telefoonnummer', + phone_description: 'Zoek vrienden met behulp van je nummer', + phone_placeholder: 'Telefoonnummer', + + confirm_description: 'Bevestigingscode', + confirm_validation_title: 'Bevestigingscode', + confirm_validation_description: 'Verkeerd format', + + password_description: 'Wachtwoord', + password_placeholder: 'Typ je wachtwoord', + password_placeholder2: 'Voer je wachtwoord opnieuw in om te bevestigen', + password_error: 'Wachtwoord moet minstens 6 tekens hebben.', + password_error1: 'Wachtwoordbevestiging komt niet overeen met wachtwoord.', + password_validation_title: 'Wachtwoord' + + }, + fr: { + phone_title: 'Envoyer le numéro de téléphone', + phone_description: 'Trouver des amis en utilisant votre numéro', + phone_placeholder: 'Numéro de téléphone', + + confirm_description: 'Code de confirmation', + confirm_validation_title: 'Code de confirmation', + confirm_validation_description: 'Format incorrect', + + password_description: 'Mot de passe', + password_placeholder: 'Tapez votre mot de passe', + password_placeholder2: 'Veuillez retapez votre mot de passe pour le confirmer', + password_error: 'Le mot de passe doit contenir 6 symboles au minimum.', + password_error1: 'Le mot de passe de confirmation ne correspond pas au mot de passe.', + password_validation_title: 'Mot de passe' + + }, + de: { + phone_title: 'Telefonnummer absenden', + phone_description: 'Freunde mit Ihrer Nummer finden', + phone_placeholder: 'Telefonnummer', + + confirm_description: 'Bestätigungscode', + confirm_validation_title: 'Bestätigungscode', + confirm_validation_description: 'Falsches Format', + + password_description: 'Passwort', + password_placeholder: 'Geben Sie Ihr Passwort ein', + password_placeholder2: 'Bitte geben Sie das Passwort zur Bestätigung erneut ein', + password_error: 'Das Passwort sollte nicht weniger als 6 Stellen beinhalten', + password_error1: 'Die Passwortbestätigung stimmt nicht mit dem Passwort überein', + password_validation_title: 'Passwort', + + }, + hi: { + phone_title: 'फ़ोन नंबर भेजें', + phone_description: 'अपने नंबर का उपयोग करके दोस्त ढूंढें', + phone_placeholder: 'फ़ोन नंबर', + + confirm_description: 'पुष्टि कोड', + confirm_validation_title: 'पुष्टि कोड', + confirm_validation_description: 'गलत प्रारूप', + + password_description: 'पासवर्ड', + password_placeholder: 'अपना पासवर्ड टाइप करें', + password_placeholder2: 'पुष्टि करने के लिए फिर से पासवर्ड दर्ज करें', + password_error: 'पासवर्ड 6 प्रतीकों से कम का नहीं होना चाहिए।', + password_error1: 'पासवर्ड पुष्टि पासवर्ड मेल नहीं खाता है।', + password_validation_title: 'पासवर्ड' + + }, + hu: { + phone_title: 'Telefonszám küldése', + phone_description: 'Ismerősök megkeresése telefonszám alapján', + phone_placeholder: 'Telefonszám', + + confirm_description: 'Megerősítési kód', + confirm_validation_title: 'Megerősítési kód', + confirm_validation_description: 'Rossz formátum', + + password_description: 'Jelszó', + password_placeholder: 'Add meg a jelszavad', + password_placeholder2: 'A megerősítéshez kérjük, add meg újra a jelszavad', + password_error: 'A jelszó nem lehet hosszabb 6 szimbólumnál.', + password_error1: 'A megerősített jelszó nem egyezik a jelszóval.', + password_validation_title: 'Jelszó' + + }, + it: { + phone_title: 'Invia numero di telefono', + phone_description: 'Trova gli amici che usano il tuo numero', + phone_placeholder: 'Numero di telefono', + + confirm_description: 'Codice di conferma', + confirm_validation_title: 'Codice di conferma', + confirm_validation_description: 'Formato errato', + + password_description: 'Password', + password_placeholder: 'Digita la tua password', + password_placeholder2: 'Reinserisci la password per confermare', + password_error: 'La password deve contenere almeno 6 caratteri.', + password_error1: 'Conferma password\ la password non corrisponde.', + password_validation_title: 'Password' + + }, + ja: { + phone_title: '電話番号を送信', + phone_description: 'あなたの番号を使用している友人を検索', + phone_placeholder: '携帯電話番号', + + confirm_description: '確認コード', + confirm_validation_title: '確認コード', + confirm_validation_description: '間違った形式', + + password_description: 'パスワード', + password_placeholder: 'パスワードを入力してください', + password_placeholder2: '確認のためにパスワードを再入力してください', + password_error: 'パスワードは6文字以下でなければなりません.', + password_error1: 'パスワードの確認がパスワードと一致しません。', + password_validation_title: 'パスワード' + + }, + ko: { + phone_title: '전화번호 보내기', + phone_description: '내 번호를 사용하여 친구 찾기', + phone_placeholder: '전화번호', + + confirm_description: '확인 코드', + confirm_validation_title: '확인 코드', + confirm_validation_description: '잘못된 형식', + + password_description: '비밀번호', + password_placeholder: '비밀번호를 입력하세요', + password_placeholder2: '확인을 위해 비밀번호를 다시 입력해 주세요', + password_error: '비밀번호는 6자 이상이어야 합니다.', + password_error1: '확인용 비밀번호가 원래 비밀번호와 일치하지 않습니다.', + password_validation_title: '비밀번호' + + }, + pl: { + phone_title: 'Wyślij numer telefonu', + phone_description: 'Znajdź znajomych, używając swojego numeru', + phone_placeholder: 'Numer telefonu', + + confirm_description: 'Kod potwierdzający', + confirm_validation_title: 'Kod potwierdzający', + confirm_validation_description: 'Nieprawidłowy format', + + password_description: 'Hasło', + password_placeholder: 'Wpisz swoje hasło', + password_placeholder2: 'Wprowadź ponownie hasło, aby potwierdzić', + password_error: 'Hasło powinno zawierać co najmniej 6 symboli.', + password_error1: 'Wprowadzone i potwierdzone hasła nie są takie same.', + password_validation_title: 'Hasło' + + }, + 'pt-br': { + phone_title: 'Enviar número de telefone', + phone_description: 'Encontrar amigos por meio do seu número', + phone_placeholder: 'Número de telefone', + + confirm_description: 'Código de confirmação', + confirm_validation_title: 'Código de confirmação', + confirm_validation_description: 'Formato incorreto', + + password_description: 'Senha', + password_placeholder: 'Digite sua senha', + password_placeholder2: 'Por favor, digite a senha novamente para confirmar', + password_error: 'A senha deve ter no mínimo 6 símbolos.', + password_error1: 'A confirmação da senha é diferente da senha.', + password_validation_title: 'Senha' + + }, + 'pt-pt': { + phone_title: 'Enviar o Número de Telefone', + phone_description: 'Encontrar amigos que utilizem o seu número', + phone_placeholder: 'Número de telefone', + + confirm_description: 'Código de confirmação', + confirm_validation_title: 'Código de confirmação', + confirm_validation_description: 'Formato errado', + + password_description: 'Palavra-passe', + password_placeholder: 'Digite a sua palavra-passe', + password_placeholder2: 'Por favor, volte a digitar a palavra-passe para confirmar', + password_error: 'A palavra-passe não deve ter menos de 6 símbolos.', + password_error1: 'A confirmação da palavra-passe não coincide com a palavra-passe.', + password_validation_title: 'Palavra-passe' + + }, + ro: { + phone_title: 'Trimite numărul de telefon', + phone_description: 'Găsește prieteni folosindu-ți numărul de telefon', + phone_placeholder: 'Număr de telefon', + + confirm_description: 'Cod de confirmare', + confirm_validation_title: 'Cod de confirmare', + confirm_validation_description: 'Format greșit', + + password_description: 'Parolă', + password_placeholder: 'Tastează parola', + password_placeholder2: 'Te rugăm să re-introduci parola pentru a confirma', + password_error: 'Parola trebuie să aibă cel puțin 6 simboluri.', + password_error1: 'Parola confirmată nu este aceeași cu parola introdusă.', + password_validation_title: 'Parolă' + + }, + sl: { + phone_title: 'Pošlji telefonsko številko', + phone_description: 'Iskanje prijateljev z uporabo tvoje telefonske številke', + phone_placeholder: 'Telefonska številka', + + confirm_description: 'Potrditvena koda', + confirm_validation_title: 'Potrditvena koda', + confirm_validation_description: 'Neveljaven format', + + password_description: 'Geslo', + password_placeholder: 'Vnesi svoje geslo', + password_placeholder2: 'Prosimo, ponovno vnesi geslo za potrditev', + password_error: 'Geslo mora vsebovati vsaj 6 simbolov.', + password_error1: 'Potrditev gesla se ne ujema z geslom.', + password_validation_title: 'Geslo' + + }, + es: { + phone_title: 'Enviar número de teléfono', + phone_description: 'Encontrar amigos que estén utilizando tu número', + phone_placeholder: 'Número de teléfono', + + confirm_description: 'Código de confirmación', + confirm_validation_title: 'Código de confirmación', + confirm_validation_description: 'Formato erróneo', + + password_description: 'Contraseña', + password_placeholder: 'Escribe tu contraseña', + password_placeholder2: 'Por favor, vuelve a escribir la contraseña para confirmar', + password_error: 'La contraseña no debe ser inferior a 6 símbolos.', + password_error1: 'La confirmación de contraseña no coincide con la contraseña.', + password_validation_title: 'Contraseña' + + }, + 'es-ar': { + phone_title: 'Envia un número telefónico', + phone_description: 'Encuentra amigos utilizando tu número', + phone_placeholder: 'Número telefónico', + + confirm_description: 'Código de confirmación', + confirm_validation_title: 'Código de confirmación', + confirm_validation_description: 'Formato incorrecto', + + password_description: 'Contraseña', + password_placeholder: 'Ingresa tu contraseña', + password_placeholder2: 'Ingresa tu contraseña para confirmar', + password_error: 'Las contraseñas deben contener no menos de 6 símbolos.', + password_error1: 'La confirmación de la contraseña no coincide con la contraseña.', + password_validation_title: 'Contraseña' + + }, + sw: { + phone_title: 'Tuma Namba ya Simu', + phone_description: 'Pata marafiki kwa kutumia namba yako', + phone_placeholder: 'Namba ya simu', + + confirm_description: 'Kificho cha uthibitisho', + confirm_validation_title: 'Kificho cha uthibitisho', + confirm_validation_description: 'Muundo hafifu', + + password_description: 'Nenosiri', + password_placeholder: 'Andika nenosiri lako', + password_placeholder2: 'Tafadhali ingiza tena nenosiri kuthibitisha', + password_error: 'Nenosiri lisiwe chini ya alama 6.', + password_error1: 'Uthibitisho wa nenosiri haulingani na nenosiri.', + password_validation_title: 'Nenosiri' + + }, + sv: { + phone_title: 'Skicka telefonnummer', + phone_description: 'Hitta vänner som använder ditt nummer', + phone_placeholder: 'Telefonnummer', + + confirm_description: 'Bekräftelsekod', + confirm_validation_title: 'Bekräftelsekod', + confirm_validation_description: 'Fel format', + + password_description: 'Lösenord', + password_placeholder: 'Skriv ditt lösenord', + password_placeholder2: 'Var god ange ditt lösenord igen för att bekräfta', + password_error: 'Lösenordet bör inte vara mindre än 6 symboler.', + password_error1: 'Lösenordsbekräftelsen matcharinte lösenordet.', + password_validation_title: 'Lösenord' + + }, + 'fr-ch': { + phone_title: 'Envoyer numéro de téléphone', + phone_description: 'Trouvez des amis en utilisant votre numéro', + phone_placeholder: 'Numéro de téléphone', + + confirm_description: 'Code de confirmation', + confirm_validation_title: 'Code de confirmation', + confirm_validation_description: 'Mauvais format', + + password_description: 'Mot de passe', + password_placeholder: 'Tapez votre mot de passe', + password_placeholder2: 'Veuillez saisir à nouveau le mot de passe pour confirmer', + password_error: 'Le mot de passe doit avoir au moins 6 caractères.', + password_error1: 'La confirmation du mot de passe ne correspond pas au premier mot de passe.', + password_validation_title: 'Mot de passe' + + }, + 'de-ch': { + phone_title: 'Sende Telefonnummer', + phone_description: 'Finde Freunde mittels deiner Telefonnummer', + phone_placeholder: 'Telefonnummer', + + confirm_description: 'Konfirmationscode', + confirm_validation_title: 'Konfirmationscode', + confirm_validation_description: 'Falsches Format', + + password_description: 'Passwort', + password_placeholder: 'Gib dein Passwort ein', + password_placeholder2: 'Bitte gib das Passwort zur Bestätigung erneut ein', + password_error: 'Passwort sollte nicht kleiner als 6 Symbole sein.', + password_error1: 'Passwort Bestätigung stimmt mit Passwort nicht überein.', + password_validation_title: 'Passwort' + + }, + 'it-ch': { + phone_title: 'Invia numero di telefono', + phone_description: 'Trova amici che utilizzano il tuo numero', + phone_placeholder: 'Numero di telefono', + + confirm_description: 'Codice di conferma', + confirm_validation_title: 'Codice di conferma', + confirm_validation_description: 'Formato errato', + + password_description: 'Password', + password_placeholder: 'Digita la tua password', + password_placeholder2: 'Inserisci nuovamente la password per confermare', + password_error: 'La password non può contenere meno di 6 caratteri.', + password_error1: 'La password di conferma non corrisponde alla password.', + password_validation_title: 'Password' + + }, + th: { + phone_title: 'ส่งหมายเลขโทรศัพท์', + phone_description: 'ค้นหาเพื่อนโดยใช้หมายเลขของคุณ ', + phone_placeholder: 'หมายเลขโทรศัพท์', + + confirm_description: 'รหัสยืนยัน', + confirm_validation_title: 'รหัสยืนยัน', + confirm_validation_description: 'รูปแบบผิด', + + password_description: 'รหัสผ่าน', + password_placeholder: 'พิมพ์รหัสผ่านของคุณ', + password_placeholder2: 'โปรดกรอกรหัสผ่านอีกครั้งเพื่อยืนยัน', + password_error: 'รหัสผ่านควรมีสัญลักษณ์ไม่น้อยกว่า 6 ตัว', + password_error1: 'การยืนยันรหัสผ่านไม่ตรงกับรหัสผ่าน', + password_validation_title: 'รหัสผ่าน' + + }, + tr: { + phone_title: 'Telefon Numarasını Gönder', + phone_description: 'Telefon numaranı kullanarak arkadaşlarınızı bulun', + phone_placeholder: 'Telefon numarası', + + confirm_description: 'Onay kodu', + confirm_validation_title: 'Onay kodu', + confirm_validation_description: 'Hatalı format', + + password_description: 'Şifre', + password_placeholder: 'Şifrenizi girin', + password_placeholder2: 'Onaylamak için lütfen parolanızı yeniden girin', + password_error: 'Şifre 6 simgeden daha kısa olmamalıdır.', + password_error1: 'Şifre onayı, şifre ile eşleşmiyor.', + password_validation_title: 'Şifre' + + }, + uk: { + phone_title: 'Надіслати номер телефону', + phone_description: 'Знайдіть друзів, використовуючи свій номер', + phone_placeholder: 'Номер телефону', + + confirm_description: 'Код підтвердження', + confirm_validation_title: 'Код підтвердження', + confirm_validation_description: 'Неправильний формат', + + password_description: 'Пароль', + password_placeholder: 'Введіть свій пароль', + password_placeholder2: 'Будь ласка, введіть пароль ще раз для підтвердження', + password_error: 'Пароль повинен бути не менше 6 символів.', + password_error1: 'Підтвердження паролю не співпадає з паролем.', + password_validation_title: 'Пароль' + + }, + ur: { + phone_title: 'فون نمبر بھیجیں', + phone_description: 'فون نمبر استعمال کرتے ہوئے دوستوں کو تلاش کریں', + phone_placeholder: 'فون نمبر', + + confirm_description: 'تصدیقی کوڈ', + confirm_validation_title: 'تصدیقی کوڈ', + confirm_validation_description: 'غلط فارمیٹ', + + password_description: 'پاسورڈ', + password_placeholder: 'اپنا پاسورڈ لکھیں', + password_placeholder2: 'برائے مہربانی تصدیق کے لیے اپنا پاسورڈ دوبارہ لکھیں', + password_error: 'پاسورڈ 6 اعداد سے چھوٹا نہیں ہونا چاہیے۔', + password_error1: 'تصدیقی پاسورڈ پاسورڈ سے مماثل نہیں', + password_validation_title: 'پاسورڈ' + + }, + vi: { + phone_title: 'Gửi số điện thoại', + phone_description: 'Tìm bạn bè bằng các sử dụng số điện thoại của bạn', + phone_placeholder: 'Số điện thoại', + + confirm_description: 'Mã xác nhận', + confirm_validation_title: 'Mã xác nhận', + confirm_validation_description: 'Sai định dạng', + + password_description: 'Mật khẩu', + password_placeholder: 'Gõ mật khẩu của bạn', + password_placeholder2: 'Vui lòng nhập lại mật khẩu để xác nhận', + password_error: 'Mật khẩu không được ít hơn 6 ký tự.', + password_error1: 'Xác nhận mật khẩu không khớp với mật khẩu.', + password_validation_title: 'Mật khẩu' + + } +}; + +var WEB3_UNIT = [ + 'kwei/ada', + 'mwei/babbage', + 'gwei/shannon', + 'szabo', + 'finney', + 'ether', + 'kether/grand/einstein', + 'mether', + 'gether', + 'tether' +]; + +// because web3 doesn't provide params or docs +var DOC_MAP = { + console: { + log : { + desc: 'Outputs a message to chat context.', + args: [{ + name: 'text', + type: 'String', + desc: 'message to output to chat context' + }] + } + }, + web3: { + // setProvider : ['provider'], // TODO + version: { + api: { + desc: 'The ethereum js api version.' + }, + node: { + desc: 'The client/node version.' + }, + network: { + desc: 'The network protocol version.' + }, + ethereum: { + desc: 'The ethereum protocol version.' + }, + whisper: { + desc: 'The whisper protocol version.' + }, + }, + isConnected: { + desc: 'Check if a connection to a node exists.', + args: [] + }, + currentProvider: { + desc: 'Will contain the current provider, if one is set. This can be used to check if mist etc. set already a provider.' + }, + reset: { + desc: 'Should be called to reset state of web3. Resets everything except manager. Uninstalls all filters. Stops polling.', + args: [{ + name: 'keepIsSyncing', + type: 'Boolean', + desc: 'If true it will uninstall all filters, but will keep the web3.eth.isSyncing() polls' + }] + }, + sha3: { + desc: 'Returns the Keccak-256 SHA3 of the given data.', + args: [{ + name: 'string', + type: 'String', + desc: 'The string to hash using the Keccak-256 SHA3 algorithm' + }, { + name: 'options', + type: 'Object', + optional: true, + desc: 'Set encoding to hex if the string to hash is encoded in hex. A leading 0x will be automatically ignored.' + }] + }, + toHex: { + desc: 'Converts any value into HEX', + args: [{ + name: 'mixed', + type: 'String|Number|Object|Array|BigNumber', + desc: 'The value to parse to HEX. If its an object or array it will be JSON.stringify first. If its a BigNumber it will make it the HEX value of a number.' + }] + }, + toAscii: { + desc: 'Converts a HEX string into a ASCII string.', + args: [{ + name: 'hexString', + type: 'String', + desc: 'A HEX string to be converted to ascii.' + }] + }, + fromAscii: { + desc: 'Converts any ASCII string to a HEX string.', + args: [{ + name: 'string', + type: 'String', + desc: 'An ASCII string to be converted to HEX.' + }, { + name: 'padding', + type: 'Number', + desc: 'The number of bytes the returned HEX string should have. ' + }] + }, + toDecimal: { + desc: 'Converts a HEX string to its number representation.', + args: [{ + name: 'hexString', + type: 'String', + desc: 'An HEX string to be converted to a number.' + }] + }, + fromDecimal: { + desc: 'Converts a number or number string to its HEX representation.', + args: [{ + name: 'number', + type: 'Number', + desc: 'A number to be converted to a HEX string.' + }] + }, + fromWei: { + desc: 'Converts a number of wei into an ethereum unit', + args: [{ + name: 'number', + type: 'Number|String|BigNumber', + desc: 'A number or BigNumber instance.' + }, { + name: 'unit', + type: 'string', + desc: 'One of the ether units' + }] + }, + toWei: { + desc: 'Converts an ethereum unit into wei', + args: [{ + name: 'number', + type: 'Number|String|BigNumber', + desc: 'A number or BigNumber instance.' + }, { + name: 'unit', + type: 'string', + desc: 'One of the ether units' + }] + }, + toBigNumber: { + desc: 'Converts a given number into a BigNumber instance', + args: [{ + name: 'numberOrHexString', + type: 'Number|String', + desc: 'A number, number string or HEX string of a number.' + }] + }, + net: { + listening: { + desc: 'Is node actively listening for network connections?' + }, + peerCount: { + desc: 'Returns the number of connected peers' + } + }, + isAddress: { + desc: '', + args: [{ + name: '', + type: 'string', + desc: 'hex string' + }], // TODO not in docs + }, + eth: { + defaultAccount: { + desc: 'The currently set default address' + }, + defaultBlock: { + desc: 'The default block number to use when querying a state.' + }, + syncing: { + desc: 'Returns the either a sync object, when the node is syncing or false.' + }, + isSyncing: { + desc: 'This convenience function calls the callback everytime a sync starts, updates and stops.', + args: [{ + name: 'callback', + type: 'Function', + desc: 'The callback will be fired with true when the syncing starts and with false when it stopped. While syncing it will return the syncing object: {startingBlock, currentBlock, highestBlock}' + }] + }, + coinbase: { + desc: 'Returns the coinbase address were the mining rewards go to.' + }, + mining: { + desc: 'Says whether the node is mining or not.' + }, + hashrate: { + desc: 'Returns the number of hashes per second that the node is mining with.' + }, + gasPrice: { + desc: 'Returns the current gas price. The gas price is determined by the x latest blocks median gas price' + }, + accounts: { + desc: 'Returns a list of accounts the node controls' + }, + blockNumber: { + desc: 'Returns the current block number' + }, + getBalance: { + desc: 'Get the balance of an address at a given block.', + args: [{ + name: 'addressHexString', + type: 'String', + desc: 'The address to get the balance of' + }, { + name: 'defaultBlock', + type: 'Number|String', + optional: true, + desc: 'If you pass this parameter it will not use the default block set with web3.eth.defaultBlock.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getStorageAt: { + desc: 'Get the storage at a specific position of an address.', + args: [{ + name: 'addressHexString', + type: 'String', + desc: 'The address to get the storage from.' + }, { + name: 'position', + type: 'Number', + desc: 'The index position of the storage.' + }, { + name: 'defaultBlock', + type: 'Number|String', + optional: true, + desc: 'If you pass this parameter it will not use the default block set with web3.eth.defaultBlock.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getCode: { + desc: 'Get the code at a specific address.', + args: [{ + name: 'addressHexString', + type: 'String', + desc: 'The address to get the code from.' + }, { + name: 'defaultBlock', + type: 'Number|String', + optional: true, + desc: 'If you pass this parameter it will not use the default block set with web3.eth.defaultBlock.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getBlock: { + desc: 'Returns a block matching the block number or block hash.', + args: [{ + name: 'blockHashOrBlockNumber', + type: 'String|Number', + desc: 'The block number or hash. Or the string "earliest", "latest" or "pending"' + }, { + name: 'returnTransactionObjects', + type: 'Boolean', + optional: true, + desc: '(default false) If true, the returned block will contain all transactions as objects, if false it will only contains the transaction hashes.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getBlockTransactionCount: { + desc: 'Returns the number of transaction in a given block.', + args: [{ + name: 'hashStringOrBlockNumber', + type: 'String|Number', + desc: 'The block number or hash. Or the string "earliest", "latest" or "pending"' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getUncle: { + desc: 'Returns a blocks uncle by a given uncle index position', + args: [{ + name: 'blockHashStringOrNumber', + type: 'String|Number', + desc: 'The block number or hash. Or the string "earliest", "latest" or "pending"' + }, { + name: 'uncleNumber', + type: 'Number', + desc: 'The index position of the uncle.' + }, { + name: 'returnTransactionObjects', + type: 'Boolean', + desc: '(default false) If true, the returned block will contain all transactions as objects, if false it will only contains the transaction hashes.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getBlockUncleCount: { + desc: '', // TODO missing from docs + }, + getTransaction: { + desc: 'Returns a transaction matching the given transaction hash.', + args: [{ + name: 'transactionHash', + type: 'String', + desc: 'The transaction hash.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getTransactionFromBlock: { + desc: 'Returns a transaction based on a block hash or number and the transactions index position.', + args: [{ + name: 'hashStringOrBlockNumber', + type: 'String|Number', + desc: 'The block number or hash. Or the string "earliest", "latest" or "pending"' + }, { + name: 'indexNumber', + type: 'Number', + desc: 'The transactions index position.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getTransactionReceipt: { + desc: 'Returns the receipt of a transaction by transaction hash.', + args: [{ + name: 'hashString', + type: 'String', + desc: 'The transaction hash.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + getTransactionCount: { + desc: 'Get the numbers of transactions sent from this address.', + args: [{ + name: 'addressHexString', + type: 'String', + desc: 'The address to get the numbers of transactions from.' + }, { + name: 'defaultBlock', + type: 'String|Number', + desc: 'If you pass this parameter it will not use the default block set with web3.eth.defaultBlock.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + sendTransaction: { + desc: 'Sends a transaction to the network.', + args: [{ + name: 'transactionObject', + type: 'Object', + desc: 'The transaction object to send: {from[, to][, value][, gas][, gasPrice][, data][, nonce]}' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + sendRawTransaction: { + desc: 'Sends an already signed transaction.', + args: [{ + name: 'signedTransactionData', + type: 'String', + desc: 'Signed transaction data in HEX format' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + sign: { + desc: 'Signs data from a specific account. This account needs to be unlocked.', + args: [{ + name: 'address', + type: 'String', + desc: 'Address to sign with.' + }, { + name: 'dataToSign', + type: 'String', + desc: 'Data to sign.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + call: { + desc: 'Executes a message call transaction, which is directly executed in the VM of the node, but never mined into the blockchain.', + args: [{ + name: 'callObject', + type: 'String', + desc: 'Address to sign with.' + }, { + name: 'defaultBlock', + type: 'String', + optional: true, + desc: 'Data to sign.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + estimateGas: { + desc: 'Executes a message call or transaction, which is directly executed in the VM of the node, but never mined into the blockchain and returns the amount of the gas used.', + args: [{ + name: 'callObject', + type: 'Object', + desc: 'The transaction object to send: {[from][, to][, value][, gas][, gasPrice][, data][, nonce]}' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + filter: { + // TODO: add description + desc: '', + args: [{ + name: 'options', + type: 'String|Object', + desc: 'The string "latest" or "pending" to watch for changes in the latest block or pending transactions respectively. Or a filter options object as follows: {fromBlock: Number|String, toBlock: Number|String, address: String, topics: StringArray}' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'Watch callback' + }] + }, + // TODO filters + // watch : ['callback'], + // stopWatching : ['callback'], + contract: { + desc: 'Creates a contract object for a solidity contract, which can be used to initiate contracts on an address.', + args: [{ + name: 'abiArray', + type: 'Array', + desc: 'ABI array with descriptions of functions and events of the contract.' + }] + }, + getCompilers: { + desc: 'Gets a list of available compilers.', + args: [{ + name: 'callback', + type: 'Function', + optional: true, + desc: 'If you pass a callback the HTTP request is made asynchronous.' + }] + }, + compile: { // TODO we should auto hide these depending on output from getCompilers + lll: { + desc: 'Compiles LLL source code.', + args: [{ + name: 'sourceString', + type: 'String', + desc: 'The LLL source code.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'Watch callback' + }] + }, + solidity: { + desc: 'Compiles solidity source code', + args: [{ + name: 'sourceString', + type: 'String', + desc: 'The solidity source code.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'Watch callback' + }], + }, + serpent: { + desc: 'Compiles serpent source code', + args: [{ + name: 'sourceString', + type: 'String', + desc: 'The serpent source code.' + }, { + name: 'callback', + type: 'Function', + optional: true, + desc: 'Watch callback' + }] + } + }, + namereg: { + desc: 'Returns GlobalRegistrar object.' + } + }, + + db: { + putString: { + desc: 'Store a string in the local leveldb database.', + args: [{ + name: 'db', + type: 'String', + desc: 'The database to store to.' + }, { + name: 'key', + type: 'String', + desc: 'The name of the store.' + }, { + name: 'value', + type: 'String', + desc: 'The string value to store.' + }] + }, + getString: { + desc: 'Retrieve a string from the local leveldb database. (db, key)', + args: [{ + name: 'db', + type: 'String', + desc: 'The database string name to retrieve from.' + }, { + name: 'key', + type: 'String', + desc: 'The name of the store.' + }] + }, + putHex: { + desc: 'Store binary data in the local leveldb database. (db, key, value)', + args: [{ + name: 'db', + type: 'String', + desc: 'The database to store to.' + }, { + name: 'key', + type: 'String', + desc: 'The name of the store.' + }, { + name: 'value', + type: 'String', + desc: 'The HEX string to store.' + }] + }, + getHex: { + desc: 'Retrieve binary data from the local leveldb database. (db, key)', + args: [{ + name: 'db', + type: 'String', + desc: 'The database string name to retrieve from.' + }, { + name: 'key', + type: 'String', + desc: 'The name of the store.' + }] + } + } + } +}; + +function jsSuggestionsContainerStyle(suggestionsCount) { + return { + marginVertical: 1, + marginHorizontal: 0, + keyboardShouldPersistTaps: true, + //height: Math.min(150, (56 * suggestionsCount)), + backgroundColor: "white", + borderRadius: 5, + keyboardShouldPersistTaps: true + }; +} + +var jsSuggestionContainerStyle = { + paddingLeft: 16, + backgroundColor: "white" +}; + +var jsSubContainerStyle = { + //height: 56, + paddingTop: 9, + borderBottomWidth: 1, + borderBottomColor: "#0000001f" +}; + +var jsValueStyle = { + fontSize: 14, + fontFamily: "font", + color: "#000000de" +}; + +var jsBoldValueStyle = { + fontSize: 14, + fontFamily: "font", + color: "#000000de", + fontWeight: "bold" +}; + +var jsDescriptionStyle = { + marginTop: 1.5, + fontSize: 14, + fontFamily: "font", + color: "#838c93de" +}; + +var messages = []; + + +console = (function(old){ + return { + log: function(text){ + old.log(text); + var message = { + type: 'log', + message: JSON.stringify(text) + }; + messages.push(message); + context.messages.push(message); + }, + info: function (text) { + old.info(text); + context.messages.push({ + type: 'info', + message: JSON.stringify(text) + }); + }, + warn: function (text) { + old.warn(text); + context.messages.push({ + type: 'warn', + message: JSON.stringify(text) + }); + }, + error: function (text) { + old.error(text); + context.messages.push({ + type: 'error', + message: JSON.stringify(text) + }); + } + }; +}(console)); + + + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function(searchString, position){ + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} + +function matchSubString(array, string) { + var matched = []; + for (var i = 0; i < array.length; i++) { + var item = array[i]; + if (item.toLowerCase().startsWith(string.toLowerCase())) { + matched.push(item); + } + } + return matched; +} + +function cleanCode(code) { + // remove comments + var commentsRegex = /\/\*.+?\*\/|\/\/.*/g; + code = code.replace(commentsRegex, ""); + // replace string literals + var literalsRegex = /\"(?:\\\\\"|[^\"])*?\"/g; + code = code.replace(literalsRegex, '""'); + var literalsRegex = /\'(?:\\\\\'|[^\'])*?\'/g; + code = code.replace(literalsRegex, '""'); + + return code +} + +function createObjectSuggestion(name, docInfo, code, parameterNumber) { + var title = name; + if (docInfo.args) { + title += "("; + for (var i = 0; i < docInfo.args.length; i++) { + var argument = docInfo.args[i]; + var argumentText = (i > 0 ? ", " : "") + (parameterNumber === i ? "*" + argument.name + "*" : argument.name); + if (argument.optional) { + argumentText = "[" + argumentText + "]"; + } + title += argumentText; + } + title += ")"; + } + if (!docInfo.desc) { + name += "."; + } else if (docInfo.args) { + name += "("; + if (docInfo.args.length == 0) { + name += ")"; + } + } + var suggestion = { + title: title, + desc: docInfo.desc + }; + + if (code != null) { + suggestion.pressValue = code + name; + } + return suggestion; +} + +var lastMessage = null; + +function getLastForm(code) { + var codeLength = code.length; + var form = ''; + var level = 0; + var index = codeLength - 1; + while(index >= 0) { + var char = code[index]; + if (level == 0 && (char == '(' || char == ',')) { + break; + } + if (char == ')') { + level --; + } + if (char == '(') { + level ++; + } + form = char + form; + index--; + } + return form; +} + +function getLastLevel(code) { + var codeLength = code.length; + var form = ''; + var index = codeLength - 1; + var nested = false; + var level = 0; + while(index >= 0) { + var char = code[index]; + if (char == ')') { + level--; + nested = true; + } + if (char == '(') { + level++; + if (level == 0) { + nested = false; + } + if (level <= 0) { + form = "argument" + form; + index--; + continue; + } + } + if ((level == 1 && char == ',') || level == 2) { + break; + } + if (!nested || level > 0) { + form = char + form; + } + index--; + } + if (form.indexOf("(") < 0) { + var parts = form.split(','); + form = parts[parts.length - 1]; + } + return form; +} + +function getPartialSuggestions(doc, fullCode, code) { + var suggestions = []; + var functionParts = code.split("("); + var objectParts = code.split(/[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/); + + var index = 0; + var suggestedFunction = ''; + while (index < objectParts.length) { + var part = objectParts[index]; + if (part != "desc" && part != "args" && doc[part] != null) { + doc = doc[part]; + suggestedFunction += part + '.'; + index++; + } else { + break; + } + } + suggestedFunction = suggestedFunction.substring(0, suggestedFunction.length - 1) + if (functionParts.length == 1) { + // object suggestions + if (index > objectParts.length - 1) { + var suggestion = objectParts[objectParts.length - 1]; + suggestions.push(createObjectSuggestion(suggestion, doc, fullCode.substring(0, fullCode.length - suggestion.length))); + } else if (index === objectParts.length - 1) { + var lastPart = objectParts[index]; + var keys = Object.keys(doc); + var matches = matchSubString(keys, lastPart); + + for (var i = 0; i < matches.length; i++) { + var suggestion = matches[i]; + if (suggestion == "desc" || suggestion == "args") { + continue; + } + var docInfo = doc[suggestion]; + if (docInfo != null) { + suggestions.push(createObjectSuggestion(suggestion, docInfo, fullCode.substring(0, fullCode.length - lastPart.length))); + } + } + } + } else if (functionParts.length == 2) { + // parameter suggestions + var parameters = functionParts[1].split(","); + if (doc.args && parameters.length <= doc.args.length && parameters[parameters.length - 1].indexOf(")") < 0) { + var paramInfo = doc.args[parameters.length - 1]; + var docInfo = doc; + docInfo.desc = paramInfo.name + ": " + paramInfo.desc; + suggestions.push(createObjectSuggestion(suggestedFunction, docInfo, null, parameters.length - 1)); + } + } + console.log(suggestions); + return suggestions; +} + +function getJsSuggestions(code, context) { + var suggestions = []; + var doc = DOC_MAP; + // TODO: what's /c / doing there ??? + console.log(code); + if (!code || code == "" || code == "c ") { + code = ""; + console.log("Last message: " + context.data); + if (context.data != null) { + suggestions.push({ + title: 'Last command used:', + desc: context.data, + pressValue: context.data + }); + } + var keys = Object.keys(doc); + for (var i = 0; i < keys.length; i++) { + var suggestion = keys[i]; + var docInfo = doc[suggestion]; + if (docInfo != null) { + suggestions.push(createObjectSuggestion(suggestion, docInfo, "")); + } + } + } else { + // TODO: what's /c / doing there ??? + if (code.startsWith("c ")) { + code = code.substring(2); + } + if (context.data != null && + (typeof context.data === 'string' || context.data instanceof String) && + context.data.startsWith(code)) { + suggestions.unshift({ + title: 'Last command used:', + desc: context.data, + pressValue: context.data + }); + } + var originalCode = code; + code = cleanCode(code); + var levelCode = getLastLevel(code); + var code = getLastForm(levelCode); + if (levelCode != code) { + suggestions = getPartialSuggestions(doc, originalCode, levelCode); + } + + console.log("Final code: " + code); + console.log("Level code: " + levelCode); + suggestions = suggestions.concat(getPartialSuggestions(doc, originalCode, code)); + } + return suggestions; +} + +function createMarkupText(text) { + var parts = []; + var index = 0; + var currentText = ''; + var isBold = false; + while (index < text.length) { + var char = text[index]; + if (char == '*') { + if (currentText != '') { + parts.push( + status.components.text( + {style: isBold ? jsBoldValueStyle : jsValueStyle}, + currentText + ) + ); + currentText = ''; + } + isBold = !isBold; + } else { + currentText += char; + } + index++; + } + if (currentText != '') { + parts.push( + status.components.text( + {style: isBold ? jsBoldValueStyle : jsValueStyle}, + currentText + ) + ); + } + console.log(parts); + return parts; +} + +function jsSuggestions(params, context) { + + console.log(context); + + var suggestions = getJsSuggestions(params.code, context); + var sugestionsMarkup = []; + + for (var i = 0; i < suggestions.length; i++) { + var suggestion = suggestions[i]; + + if (suggestion.title.indexOf('*') >= 0) { + suggestion.title = createMarkupText(suggestion.title); + } + var suggestionMarkup = status.components.view(jsSuggestionContainerStyle, + [status.components.view(jsSubContainerStyle, + [ + status.components.text({style: jsValueStyle}, + suggestion.title), + status.components.text({style: jsDescriptionStyle}, + suggestion.desc) + ])]); + if (suggestion.pressValue) { + suggestionMarkup = status.components.touchable({ + onPress: [status.events.SET_VALUE, suggestion.pressValue]}, + suggestionMarkup); + } + sugestionsMarkup.push(suggestionMarkup); + } + + if (sugestionsMarkup.length > 0) { + var view = status.components.scrollView(jsSuggestionsContainerStyle(sugestionsMarkup.length), + sugestionsMarkup + ); + return {markup: view}; + } +} + + + +function jsHandler(params, context) { + var result = { + err: null, + data: null, + messages: [] + }; + messages = []; + try { + result.data = JSON.stringify(eval(params.code)); + localStorage.set(params.code); + } catch(e) { + result.err = e; + } + + result.messages = messages; + + return result; +} + +var phones = [ // TODO this is supposed to be regionalised + { + number: "89171111111", + description: "Number format 1" + }, + { + number: "89371111111", + description: "Number format 1" + }, + { + number: "+79171111111", + description: "Number format 2" + }, + { + number: "9171111111", + description: "Number format 3" + } +]; + +function suggestionsContainerStyle(suggestionsCount) { + return { + marginVertical: 1, + marginHorizontal: 0, + keyboardShouldPersistTaps: true, + height: Math.min(150, (56 * suggestionsCount)), + backgroundColor: "white", + borderRadius: 5, + flexGrow: 1 + }; +} + +var suggestionContainerStyle = { + paddingLeft: 16, + backgroundColor: "white" +}; + +var suggestionSubContainerStyle = { + height: 56, + borderBottomWidth: 1, + borderBottomColor: "#0000001f" +}; + +var valueStyle = { + marginTop: 9, + fontSize: 14, + fontFamily: "font", + color: "#000000de" +}; + +var descriptionStyle = { + marginTop: 1.5, + fontSize: 14, + fontFamily: "font", + color: "#838c93de" +}; + +function startsWith(str1, str2) { + // String.startsWith(...) doesn't work in otto + return str1.lastIndexOf(str2, 0) == 0 && str1 != str2; +} + +function phoneSuggestions(params) { + var ph, suggestions; + if (!params.phone || params.phone == "") { + ph = phones; + } else { + ph = phones.filter(function (phone) { + return startsWith(phone.number, params.phone); + }); + } + + if (ph.length == 0) { + return; + } + + suggestions = ph.map(function (phone) { + return status.components.touchable( + {onPress: [status.events.SET_VALUE, phone.number]}, + status.components.view(suggestionContainerStyle, + [status.components.view(suggestionSubContainerStyle, + [ + status.components.text( + {style: valueStyle}, + phone.number + ), + status.components.text( + {style: descriptionStyle}, + phone.description + ) + ])]) + ); + }); + + /*var view = status.components.view( + {style: {flex: 1, flexDirection: "column"}}, + [status.components.scrollView( + suggestionsContainerStyle(ph.length), + suggestions + )] + );*/ + + var view = status.components.scrollView( + suggestionsContainerStyle(ph.length), + suggestions + ); + + return {markup: view}; +} + +var phoneConfig = { + name: "phone", + icon: "phone_white", + title: I18n.t('phone_title'), + description: I18n.t('phone_description'), + color: "#5bb2a2", + validator: function (params) { + return { + validationHandler: "phone", + parameters: [params.phone] + }; + }, + params: [{ + name: "phone", + type: status.types.PHONE, + suggestions: phoneSuggestions, + placeholder: I18n.t('phone_placeholder') + }] +}; +status.response(phoneConfig); +status.command(phoneConfig); + +var faucets = [ + { + name: "Ethereum Ropsten Faucet", + url: "http://faucet.ropsten.be:3001" + }, + { + name: "Status Testnet Faucet", + url: "http://46.101.129.137:3001", + } +]; + +function faucetSuggestions(params) { + var suggestions = faucets.map(function(entry) { + return status.components.touchable( + {onPress: [status.events.SET_VALUE, entry.url]}, + status.components.view( + suggestionContainerStyle, + [status.components.view( + suggestionSubContainerStyle, + [ + status.components.text( + {style: valueStyle}, + entry.name + ), + status.components.text( + {style: descriptionStyle}, + entry.url + ) + ] + )] + ) + ); + }); + + var view = status.components.scrollView( + suggestionsContainerStyle(faucets.length), + suggestions + ); + + return {markup: view}; +} + +status.command({ + name: "faucet", + title: "Faucet", + description: "Get some ETH", + color: "#7099e6", + registeredOnly: true, + params: [{ + name: "url", + type: status.types.TEXT, + suggestions: faucetSuggestions, + placeholder: "Faucet URL" + }], + preview: function (params) { + return status.components.text( + {}, + params.url + ); + }, + validator: function (params, context) { + var f = faucets.map(function (entry) { + return entry.url; + }); + + if (f.indexOf(params.url) == -1) { + var error = status.components.validationMessage( + I18n.t('faucet_incorrect_title'), + I18n.t('faucet_incorrect_description') + ); + + return {errors: [error]}; + } + } +}); + + +function browseSuggestions(params) { + if (params.url && params.url !== "undefined" && params.url != "") { + var url = params.url; + if (!/^[a-zA-Z-_]+:/.test(url)) { + url = 'http://' + url; + } + + return {webViewUrl: url}; + } +} + +status.command({ + name: "browse", + color: "#ffa500", + hidden: true, + fullscreen: true, + suggestionsTrigger: 'on-send', + params: [{ + name: "url", + suggestions: browseSuggestions, + type: status.types.TEXT + }] +}); + + +// status.command({ +// name: "help", +// title: "Help", +// description: "Request help from Console", +// color: "#7099e6", +// params: [{ +// name: "query", +// type: status.types.TEXT +// }] +// }); + +status.response({ + name: "confirmation-code", + color: "#7099e6", + description: I18n.t('confirm_description'), + params: [{ + name: "code", + type: status.types.NUMBER + }], + validator: function (params) { + if (!/^[\d]{4}$/.test(params.code)) { + var error = status.components.validationMessage( + I18n.t('confirm_validation_title'), + I18n.t('confirm_validation_description') + ); + + return {errors: [error]} + } + } +}); + +status.response({ + name: "password", + color: "#7099e6", + description: I18n.t('password_description'), + icon: "lock_white", + params: [{ + name: "password", + type: status.types.PASSWORD, + placeholder: I18n.t('password_placeholder') + }, { + name: "password-confirmation", + type: status.types.PASSWORD, + placeholder: I18n.t('password_placeholder2') + }], + validator: function (params, context) { + var errorMessages = []; + var currentParameter = context["current-parameter"]; + + if ( + currentParameter == "password" && + params.password.length < 6 + ) { + errorMessages.push(I18n.t('password_error')); + } + + if (currentParameter == "password-confirmation" && + params.password != params["password-confirmation"]) { + errorMessages.push(I18n.t('password_error1')); + } + + if (errorMessages.length) { + var errors = []; + for (var idx in errorMessages) { + errors.push( + status.components.validationMessage( + I18n.t('password_validation_title'), + errorMessages[idx] + ) + ); + } + + return {errors: errors}; + } + + return {params: params, context: context}; + }, + preview: function (params, context) { + var style = { + marginTop: 5, + marginHorizontal: 0, + fontSize: 14, + color: "black" + }; + + if (context.platform == "ios") { + style.fontSize = 8; + style.marginTop = 10; + style.marginBottom = 2; + style.letterSpacing = 1; + } + + return status.components.text({style: style}, "●●●●●●●●●●"); + } +}); + +status.registerFunction("message-suggestions", function(params, context) { + return jsSuggestions({code: params.message}, context); +}); + +status.registerFunction("message-handler", function(params, context) { + return jsHandler({code: params.message}, context); +}); diff --git a/resources/dapp.js b/resources/dapp.js new file mode 100644 index 0000000000..00cfb3e6c4 --- /dev/null +++ b/resources/dapp.js @@ -0,0 +1,32 @@ +I18n.translations = { + en: { + browse_title: 'Browser', + browse_description: 'Launch the browser' + } +}; + +status.command({ + name: "browse", + title: I18n.t('browse_title'), + description: I18n.t('browse_description'), + color: "#ffa500", + fullscreen: true, + suggestionsTrigger: 'on-send', + params: [{ + name: "url", + suggestions: function(params, context) { + var url = "dapp-url"; + + if (params.url && params.url !== "undefined" && params.url != "") { + url = params.url; + if (!/^[a-zA-Z-_]+:/.test(url)) { + url = 'http://' + url; + } + } + return {webViewUrl: url}; + }, + type: status.types.TEXT + }] +}); + +status.autorun("browse"); diff --git a/resources/default_contacts.json b/resources/default_contacts.json new file mode 100644 index 0000000000..0a8bfe8031 --- /dev/null +++ b/resources/default_contacts.json @@ -0,0 +1,82 @@ +{ + "wallet": + { + "name": + { + "en": "Wallet", + "es": "Monedero", + "es-ar": "Monedero", + "ru": "Кошелек" + }, + "photo-path": "icon_wallet_avatar", + "add-chat?": true, + "dapp?": true + }, + + "0x0428c9d6c1aaaa8369a7c63819684f30e34396dc0907d49afeac85a0a774ccb919b3482097d992e66bcc538e7a0c6acf874c77748f396f53c0a102e10d1a37765b": + { + "name": + { + "en": "Jarrad" + }, + "photo-path": "contacts://jarrad", + "add-chat?": false, + "dapp?": false + }, + + "auction-house": + { + "name": + { + "en": "Auction House" + }, + "photo-path": "contacts://auction-house", + "dapp?": true, + "dapp-url": + { + "en": "http://auctionhouse.dappbench.com" + } + }, + + "flight-delays-suck": + { + "name": + { + "en": "Flight Delays Suck" + }, + "photo-path": "contacts://flight-delays-suck", + "dapp?": true, + "dapp-url": + { + "en": "https://fdd.etherisc.com" + } + }, + + "mkr-market": + { + "name": + { + "en": "Maker Market" + }, + "photo-path": "contacts://mkr-market", + "dapp?": true, + "dapp-url": + { + "en": "https://mkr.market" + } + }, + + "oaken-water-meter": + { + "name": + { + "en": "Oaken Water Meter" + }, + "photo-path": "contacts://oaken-water-meter", + "dapp?": true, + "dapp-url": + { + "en": "http://waterflowdapp.projectoaken.com" + } + } +} diff --git a/resources/status.js b/resources/status.js new file mode 100644 index 0000000000..7c7f9db2be --- /dev/null +++ b/resources/status.js @@ -0,0 +1,193 @@ +var _status_catalog = { + commands: {}, + responses: {}, + functions: {} + }, + status = {}; + +function Command() { +} +function Response() { +} + +Command.prototype.addToCatalog = function () { + _status_catalog.commands[this.name] = this; +}; + +Command.prototype.param = function (parameter) { + this.params.push(parameter); + + return this; +}; + +Command.prototype.create = function (com) { + this.name = com.name; + this.title = com.title; + this.description = com.description; + this.handler = com.handler; + this["has-handler"] = com.handler != null; + this["registered-only"] = com.registeredOnly; + this.validator = com.validator; + this.color = com.color; + this.icon = com.icon; + this.params = com.params || []; + this.preview = com.preview; + this["suggestions-trigger"] = com.suggestionsTrigger || "on-change"; + this.fullscreen = com.fullscreen; + this.request = com.request; + this.addToCatalog(); + + return this; +}; + + +Response.prototype = Object.create(Command.prototype); +Response.prototype.addToCatalog = function () { + _status_catalog.responses[this.name] = this; +}; +Response.prototype.onReceiveResponse = function (handler) { + this.onReceive = handler; +}; + +var context = {} + +function addContext(ns, key, value) { + context[ns][key] = value; +} + +function call(pathStr, paramsStr) { + var params = JSON.parse(paramsStr), + path = JSON.parse(pathStr), + fn, callResult, message_id; + + if (typeof params.context !== "undefined" && + typeof params.context["message-id"] !== "undefined") { + message_id = params.context["message-id"]; + } else { + message_id = null; + } + context[message_id] = {}; + status.message_id = message_id; + + fn = path.reduce(function (catalog, name) { + if (catalog && catalog[name]) { + return catalog[name]; + } + }, + _status_catalog + ); + + if (!fn) { + return null; + } + + context.messages = []; + + callResult = fn(params.parameters, params.context); + result = { + returned: callResult, + context: context[message_id], + messages: context.messages + }; + + return JSON.stringify(result); +} + +function text(options, s) { + s = Array.isArray(s) ? s : [s]; + return ['text', options].concat(s); +} + +function view(options, elements) { + return ['view', options].concat(elements); +} + +function image(options) { + return ['image', options]; +} + +function touchable(options, element) { + return ['touchable', options, element]; +} + +function scrollView(options, elements) { + return ['scroll-view', options].concat(elements); +} + +function webView(url) { + return ['web-view', { + source: { + uri: url + }, + javaScriptEnabled: true + }]; +} + +function validationMessage(titleText, descriptionText) { + var titleStyle = { + style: { + color: "white", + fontSize: 12 + } + }; + var title = status.components.text(titleStyle, titleText); + + var descriptionStyle = { + style: { + color: "white", + fontSize: 12, + opacity: 0.9 + } + }; + var description = status.components.text(descriptionStyle, descriptionText); + + return status.components.view( + { + backgroundColor: "red", + height: 61, + paddingLeft: 16, + paddingTop: 14, + }, + [title, description] + ); +} + +var status = { + command: function (h) { + var command = new Command(); + return command.create(h); + }, + response: function (h) { + var response = new Response(); + return response.create(h); + }, + registerFunction: function (name, fn){ + _status_catalog.functions[name] = fn; + }, + autorun: function (commandName) { + _status_catalog.autorun = commandName; + }, + types: { + TEXT: 'text', + NUMBER: 'number', + PHONE: 'phone', + PASSWORD: 'password' + }, + events: { + SET_VALUE: 'set-value' + }, + components: { + view: view, + text: text, + image: image, + touchable: touchable, + scrollView: scrollView, + webView: webView, + validationMessage: validationMessage + } +}; + +// i18n.js 3.0.0.rc14 +!function(a){if("undefined"!=typeof module&&module.exports)module.exports=a(this);else if("function"==typeof define&&define.amd){var b=this;define("i18n",function(){return a(b)})}else this.I18n=a(this)}(function(a){"use strict";var b=a&&a.I18n||{},c=Array.prototype.slice,d=function(a){return("0"+a.toString()).substr(-2)},e=function(a,b){return h("round",a,-b).toFixed(b)},f=function(a){var b=typeof a;return"function"===b||"object"===b&&!!a},g=function(a){return Array.isArray?Array.isArray(a):"[object Array]"===Object.prototype.toString.call(a)},h=function(a,b,c){return"undefined"==typeof c||0===+c?Math[a](b):(b=+b,c=+c,isNaN(b)||"number"!=typeof c||c%1!==0?NaN:(b=b.toString().split("e"),b=Math[a](+(b[0]+"e"+(b[1]?+b[1]-c:-c))),b=b.toString().split("e"),+(b[0]+"e"+(b[1]?+b[1]+c:c))))},i=function(a,b){var c,d;for(c in b)b.hasOwnProperty(c)&&(d=b[c],"[object String]"===Object.prototype.toString.call(d)?a[c]=d:(null==a[c]&&(a[c]={}),i(a[c],d)));return a},j={day_names:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbr_day_names:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],month_names:[null,"January","February","March","April","May","June","July","August","September","October","November","December"],abbr_month_names:[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],meridian:["AM","PM"]},k={precision:3,separator:".",delimiter:",",strip_insignificant_zeros:!1},l={unit:"$",precision:2,format:"%u%n",sign_first:!0,delimiter:",",separator:"."},m={unit:"%",precision:3,format:"%n%u",separator:".",delimiter:""},n=[null,"kb","mb","gb","tb"],o={defaultLocale:"en",locale:"en",defaultSeparator:".",placeholder:/(?:\{\{|%\{)(.*?)(?:\}\}?)/gm,fallbacks:!1,translations:{},missingBehaviour:"message",missingTranslationPrefix:""};return b.reset=function(){this.defaultLocale=o.defaultLocale,this.locale=o.locale,this.defaultSeparator=o.defaultSeparator,this.placeholder=o.placeholder,this.fallbacks=o.fallbacks,this.translations=o.translations,this.missingBehaviour=o.missingBehaviour,this.missingTranslationPrefix=o.missingTranslationPrefix},b.initializeOptions=function(){"undefined"==typeof this.defaultLocale&&null!==this.defaultLocale&&(this.defaultLocale=o.defaultLocale),"undefined"==typeof this.locale&&null!==this.locale&&(this.locale=o.locale),"undefined"==typeof this.defaultSeparator&&null!==this.defaultSeparator&&(this.defaultSeparator=o.defaultSeparator),"undefined"==typeof this.placeholder&&null!==this.placeholder&&(this.placeholder=o.placeholder),"undefined"==typeof this.fallbacks&&null!==this.fallbacks&&(this.fallbacks=o.fallbacks),"undefined"==typeof this.translations&&null!==this.translations&&(this.translations=o.translations),"undefined"==typeof this.missingBehaviour&&null!==this.missingBehaviour&&(this.missingBehaviour=o.missingBehaviour),"undefined"==typeof this.missingTranslationPrefix&&null!==this.missingTranslationPrefix&&(this.missingTranslationPrefix=o.missingTranslationPrefix)},b.initializeOptions(),b.locales={},b.locales.get=function(a){var c=this[a]||this[b.locale]||this.default;return"function"==typeof c&&(c=c(a)),g(c)===!1&&(c=[c]),c},b.locales.default=function(a){var e,c=[],d=[];return a&&c.push(a),!a&&b.locale&&c.push(b.locale),b.fallbacks&&b.defaultLocale&&c.push(b.defaultLocale),c.forEach(function(a){e=a.split("-")[0],~d.indexOf(a)||d.push(a),b.fallbacks&&e&&e!==a&&!~d.indexOf(e)&&d.push(e)}),c.length||c.push("en"),d},b.pluralization={},b.pluralization.get=function(a){return this[a]||this[b.locale]||this.default},b.pluralization.default=function(a){switch(a){case 0:return["zero","other"];case 1:return["one"];default:return["other"]}},b.currentLocale=function(){return this.locale||this.defaultLocale},b.isSet=function(a){return void 0!==a&&null!==a},b.lookup=function(a,b){b=this.prepareOptions(b);var e,f,g,c=this.locales.get(b.locale).slice();c[0];for(a=this.getFullScope(a,b);c.length;)if(e=c.shift(),f=a.split(this.defaultSeparator),g=this.translations[e]){for(;f.length&&(g=g[f.shift()],void 0!==g&&null!==g););if(void 0!==g&&null!==g)return g}if(this.isSet(b.defaultValue))return b.defaultValue},b.meridian=function(){var a=this.lookup("time"),b=this.lookup("date");return a&&a.am&&a.pm?[a.am,a.pm]:b&&b.meridian?b.meridian:j.meridian},b.prepareOptions=function(){for(var d,a=c.call(arguments),b={};a.length;)if(d=a.shift(),"object"==typeof d)for(var e in d)d.hasOwnProperty(e)&&(this.isSet(b[e])||(b[e]=d[e]));return b},b.createTranslationOptions=function(a,b){var c=[{scope:a}];return this.isSet(b.defaults)&&(c=c.concat(b.defaults)),this.isSet(b.defaultValue)&&(c.push({message:b.defaultValue}),delete b.defaultValue),c},b.translate=function(a,b){b=this.prepareOptions(b);var d,c=this.createTranslationOptions(a,b),e=c.some(function(a){if(this.isSet(a.scope)?d=this.lookup(a.scope,b):this.isSet(a.message)&&(d=a.message),void 0!==d&&null!==d)return!0},this);return e?("string"==typeof d?d=this.interpolate(d,b):f(d)&&this.isSet(b.count)&&(d=this.pluralize(b.count,d,b)),d):this.missingTranslation(a,b)},b.interpolate=function(a,b){b=this.prepareOptions(b);var d,e,f,g,c=a.match(this.placeholder);if(!c)return a;for(var e;c.length;)d=c.shift(),f=d.replace(this.placeholder,"$1"),e=this.isSet(b[f])?b[f].toString().replace(/\$/gm,"_#$#_"):f in b?this.nullPlaceholder(d,a,b):this.missingPlaceholder(d,a,b),g=new RegExp(d.replace(/\{/gm,"\\{").replace(/\}/gm,"\\}")),a=a.replace(g,e);return a.replace(/_#\$#_/g,"$")},b.pluralize=function(a,b,c){c=this.prepareOptions(c);var d,e,g,h,i;if(d=f(b)?b:this.lookup(b,c),!d)return this.missingTranslation(b,c);for(e=this.pluralization.get(c.locale),g=e(a);g.length;)if(h=g.shift(),this.isSet(d[h])){i=d[h];break}return c.count=String(a),this.interpolate(i,c)},b.missingTranslation=function(a,b){if("guess"==this.missingBehaviour){var c=a.split(".").slice(-1)[0];return(this.missingTranslationPrefix.length>0?this.missingTranslationPrefix:"")+c.replace("_"," ").replace(/([a-z])([A-Z])/g,function(a,b,c){return b+" "+c.toLowerCase()})}var d=null!=b&&null!=b.locale?b.locale:this.currentLocale(),e=this.getFullScope(a,b),f=[d,e].join(this.defaultSeparator);return'[missing "'+f+'" translation]'},b.missingPlaceholder=function(a,b,c){return"[missing "+a+" value]"},b.nullPlaceholder=function(){return b.missingPlaceholder.apply(b,arguments)},b.toNumber=function(a,b){b=this.prepareOptions(b,this.lookup("number.format"),k);var g,i,c=a<0,d=e(Math.abs(a),b.precision).toString(),f=d.split("."),h=[],j=b.format||"%n",l=c?"-":"";for(a=f[0],g=f[1];a.length>0;)h.unshift(a.substr(Math.max(0,a.length-3),3)),a=a.substr(0,a.length-3);return i=h.join(b.delimiter),b.strip_insignificant_zeros&&g&&(g=g.replace(/0+$/,"")),b.precision>0&&g&&(i+=b.separator+g),j=b.sign_first?"%s"+j:j.replace("%n","%s%n"),i=j.replace("%u",b.unit).replace("%n",i).replace("%s",l)},b.toCurrency=function(a,b){return b=this.prepareOptions(b,this.lookup("number.currency.format"),this.lookup("number.format"),l),this.toNumber(a,b)},b.localize=function(a,b,c){switch(c||(c={}),a){case"currency":return this.toCurrency(b);case"number":return a=this.lookup("number.format"),this.toNumber(b,a);case"percentage":return this.toPercentage(b);default:var d;return d=a.match(/^(date|time)/)?this.toTime(a,b):b.toString(),this.interpolate(d,c)}},b.parseDate=function(a){var b,c,d;if("object"==typeof a)return a;if(b=a.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})([\.,]\d{1,3})?)?(Z|\+00:?00)?/)){for(var e=1;e<=6;e++)b[e]=parseInt(b[e],10)||0;b[2]-=1,d=b[7]?1e3*("0"+b[7]):null,c=b[8]?new Date(Date.UTC(b[1],b[2],b[3],b[4],b[5],b[6],d)):new Date(b[1],b[2],b[3],b[4],b[5],b[6],d)}else"number"==typeof a?(c=new Date,c.setTime(a)):a.match(/([A-Z][a-z]{2}) ([A-Z][a-z]{2}) (\d+) (\d+:\d+:\d+) ([+-]\d+) (\d+)/)?(c=new Date,c.setTime(Date.parse([RegExp.$1,RegExp.$2,RegExp.$3,RegExp.$6,RegExp.$4,RegExp.$5].join(" ")))):a.match(/\d+ \d+:\d+:\d+ [+-]\d+ \d+/)?(c=new Date,c.setTime(Date.parse(a))):(c=new Date,c.setTime(Date.parse(a)));return c},b.strftime=function(a,c){var e=this.lookup("date"),f=b.meridian();if(e||(e={}),e=this.prepareOptions(e,j),isNaN(a.getTime()))throw new Error("I18n.strftime() requires a valid date object, but received an invalid date.");var g=a.getDay(),h=a.getDate(),i=a.getFullYear(),k=a.getMonth()+1,l=a.getHours(),m=l,n=l>11?1:0,o=a.getSeconds(),p=a.getMinutes(),q=a.getTimezoneOffset(),r=Math.floor(Math.abs(q/60)),s=Math.abs(q)-60*r,t=(q>0?"-":"+")+(r.toString().length<2?"0"+r:r)+(s.toString().length<2?"0"+s:s);return m>12?m-=12:0===m&&(m=12),c=c.replace("%a",e.abbr_day_names[g]),c=c.replace("%A",e.day_names[g]),c=c.replace("%b",e.abbr_month_names[k]),c=c.replace("%B",e.month_names[k]),c=c.replace("%d",d(h)),c=c.replace("%e",h),c=c.replace("%-d",h),c=c.replace("%H",d(l)),c=c.replace("%-H",l),c=c.replace("%I",d(m)),c=c.replace("%-I",m),c=c.replace("%m",d(k)),c=c.replace("%-m",k),c=c.replace("%M",d(p)),c=c.replace("%-M",p),c=c.replace("%p",f[n]),c=c.replace("%S",d(o)),c=c.replace("%-S",o),c=c.replace("%w",g),c=c.replace("%y",d(i)),c=c.replace("%-y",d(i).replace(/^0+/,"")),c=c.replace("%Y",i),c=c.replace("%z",t)},b.toTime=function(a,b){var c=this.parseDate(b),d=this.lookup(a);return c.toString().match(/invalid/i)?c.toString():d?this.strftime(c,d):c.toString()},b.toPercentage=function(a,b){return b=this.prepareOptions(b,this.lookup("number.percentage.format"),this.lookup("number.format"),m),this.toNumber(a,b)},b.toHumanSize=function(a,b){for(var f,g,c=1024,d=a,e=0;d>=c&&e<4;)d/=c,e+=1;return 0===e?(f=this.t("number.human.storage_units.units.byte",{count:d}),g=0):(f=this.t("number.human.storage_units.units."+n[e]),g=d-Math.floor(d)===0?0:1),b=this.prepareOptions(b,{unit:f,precision:g,format:"%n%u",delimiter:""}),this.toNumber(d,b)},b.getFullScope=function(a,b){return b=this.prepareOptions(b),a.constructor===Array&&(a=a.join(this.defaultSeparator)),b.scope&&(a=[b.scope,a].join(this.defaultSeparator)),a},b.extend=function(a,b){return"undefined"==typeof a&&"undefined"==typeof b?{}:i(a,b)},b.t=b.translate,b.l=b.localize,b.p=b.pluralize,b}); +I18n.defaultLocale = "en"; +I18n.fallbacks = true; diff --git a/resources/wallet.js b/resources/wallet.js new file mode 100644 index 0000000000..fef4d3540a --- /dev/null +++ b/resources/wallet.js @@ -0,0 +1,50 @@ + +I18n.translations = { + en: { + browse_title: 'Browser', + browse_description: 'Launch the browser' + } +}; + +function wallet(params, context) { + var url = 'https://status.im/dapps/wallet'; + + if (context.debug) { + url = 'http://127.0.0.1:3450'; + } + + if (params.url && params.url !== "undefined" && params.url != "") { + url = params.url; + if (!/^[a-zA-Z-_]+:/.test(url)) { + url = 'http://' + url; + } + } + + return {webViewUrl: url}; +} + +status.command({ + name: "browse", + title: I18n.t('browse_title'), + description: I18n.t('browse_description'), + color: "#ffa500", + fullscreen: true, + suggestionsTrigger: 'on-send', + params: [{ + name: "url", + suggestions: wallet, + type: status.types.TEXT + }] +}); + +status.autorun("browse"); + +status.registerFunction("send", function (params, context) { + var data = { + from: context.from, + to: params.address, + value: web3.toWei(params.amount, "ether") + }; + + web3.eth.sendTransaction(data); +}) diff --git a/resources/web3.0_16_0.min.js b/resources/web3.0_16_0.min.js new file mode 100644 index 0000000000..25aee148d0 --- /dev/null +++ b/resources/web3.0_16_0.min.js @@ -0,0 +1,5 @@ +require=function t(e,n,r){function o(a,s){if(!n[a]){if(!e[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(i)return i(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var f=n[a]={exports:{}};e[a][0].call(f.exports,function(t){var n=e[a][1][t];return o(n?n:t)},f,f.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;aa;a++)i.push(n.encode(t[a],o));return i}():this._inputFormatter(t,e).encode()},i.prototype.decode=function(t,e,n){var r=this;if(this.isDynamicArray(n))return function(){for(var o=parseInt("0x"+t.substr(2*e,64)),i=parseInt("0x"+t.substr(2*o,64)),a=o+32,s=r.nestedName(n),c=r.staticPartLength(s),u=32*Math.floor((c+31)/32),f=[],p=0;i*u>p;p+=u)f.push(r.decode(t,a+p,s));return f}();if(this.isStaticArray(n))return function(){for(var o=r.staticArrayLength(n),i=e,a=r.nestedName(n),s=r.staticPartLength(a),c=32*Math.floor((s+31)/32),u=[],f=0;o*c>f;f+=c)u.push(r.decode(t,i+f,a));return u}();if(this.isDynamicType(n))return function(){var n=parseInt("0x"+t.substr(2*e,64)),i=parseInt("0x"+t.substr(2*n,64)),a=Math.floor((i+31)/32);return r._outputFormatter(new o(t.substr(2*n,64*(1+a)),0))}();var i=this.staticPartLength(n);return this._outputFormatter(new o(t.substr(2*e,2*i)))},e.exports=i},{"./formatters":9,"./param":11}],15:[function(t,e,n){var r=t("./formatters"),o=t("./type"),i=function(){this._inputFormatter=r.formatInputInt,this._outputFormatter=r.formatOutputUInt};i.prototype=new o({}),i.prototype.constructor=i,i.prototype.isType=function(t){return!!t.match(/^uint([0-9]*)?(\[([0-9]*)\])*$/)},i.prototype.staticPartLength=function(t){return 32*this.staticArrayLength(t)},e.exports=i},{"./formatters":9,"./type":14}],16:[function(t,e,n){var r=t("./formatters"),o=t("./type"),i=function(){this._inputFormatter=r.formatInputReal,this._outputFormatter=r.formatOutputUReal};i.prototype=new o({}),i.prototype.constructor=i,i.prototype.isType=function(t){return!!t.match(/^ureal([0-9]*)?(\[([0-9]*)\])*$/)},i.prototype.staticPartLength=function(t){return 32*this.staticArrayLength(t)},e.exports=i},{"./formatters":9,"./type":14}],17:[function(t,e,n){"use strict";"undefined"==typeof XMLHttpRequest?n.XMLHttpRequest={}:n.XMLHttpRequest=XMLHttpRequest},{}],18:[function(t,e,n){var r=t("bignumber.js"),o=["wei","kwei","Mwei","Gwei","szabo","finney","femtoether","picoether","nanoether","microether","milliether","nano","micro","milli","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:o,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:500,defaultBlock:"latest",defaultAccount:void 0}},{"bignumber.js":"bignumber.js"}],19:[function(t,e,n){var r=t("crypto-js"),o=t("crypto-js/sha3");e.exports=function(t,e){return e&&"hex"===e.encoding&&(t.length>2&&"0x"===t.substr(0,2)&&(t=t.substr(2)),t=r.enc.Hex.parse(t)),o(t,{outputLength:256}).toString()}},{"crypto-js":58,"crypto-js/sha3":79}],20:[function(t,e,n){var r=t("bignumber.js"),o=t("./sha3.js"),i=t("utf8"),a={noether:"0",wei:"1",kwei:"1000",Kwei:"1000",babbage:"1000",femtoether:"1000",mwei:"1000000",Mwei:"1000000",lovelace:"1000000",picoether:"1000000",gwei:"1000000000",Gwei:"1000000000",shannon:"1000000000",nanoether:"1000000000",nano:"1000000000",szabo:"1000000000000",microether:"1000000000000",micro:"1000000000000",finney:"1000000000000000",milliether:"1000000000000000",milli:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},s=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},c=function(t,e,n){return t+new Array(e-t.length+1).join(n?n:"0")},u=function(t){var e="",n=0,r=t.length;for("0x"===t.substring(0,2)&&(n=2);r>n;n+=2){var o=parseInt(t.substr(n,2),16);if(0===o)break;e+=String.fromCharCode(o)}return i.decode(e)},f=function(t){var e="",n=0,r=t.length;for("0x"===t.substring(0,2)&&(n=2);r>n;n+=2){var o=parseInt(t.substr(n,2),16);e+=String.fromCharCode(o)}return e},p=function(t){t=i.encode(t);for(var e="",n=0;nn;n++)if(parseInt(e[n],16)>7&&t[n].toUpperCase()!==t[n]||parseInt(e[n],16)<=7&&t[n].toLowerCase()!==t[n])return!1;return!0},C=function(t){if("undefined"==typeof t)return"";t=t.toLowerCase().replace("0x","");for(var e=o(t),n="0x",r=0;r7?t[r].toUpperCase():t[r];return n},F=function(t){return B(t)?t:/^[0-9a-f]{40}$/.test(t)?"0x"+t:"0x"+s(v(t).substr(2),40)},I=function(t){return t instanceof r||t&&t.constructor&&"BigNumber"===t.constructor.name},O=function(t){return"string"==typeof t||t&&t.constructor&&"String"===t.constructor.name},N=function(t){return"function"==typeof t},D=function(t){return"object"==typeof t},P=function(t){return"boolean"==typeof t},T=function(t){return t instanceof Array},E=function(t){try{return!!JSON.parse(t)}catch(e){return!1}};e.exports={padLeft:s,padRight:c,toHex:v,toDecimal:y,fromDecimal:g,toUtf8:u,toAscii:f,fromUtf8:p,fromAscii:l,transformToFullName:h,extractDisplayName:d,extractTypeName:m,toWei:w,fromWei:_,toBigNumber:x,toTwosComplement:k,toAddress:F,isBigNumber:I,isStrictAddress:B,isAddress:S,isChecksumAddress:A,toChecksumAddress:C,isFunction:N,isString:O,isObject:D,isBoolean:P,isArray:T,isJson:E}},{"./sha3.js":19,"bignumber.js":"bignumber.js",utf8:84}],21:[function(t,e,n){e.exports={version:"0.15.3"}},{}],22:[function(t,e,n){function r(t){this._requestManager=new o(t),this.currentProvider=t,this.eth=new a(this),this.db=new s(this),this.shh=new c(this),this.net=new u(this),this.personal=new f(this),this.settings=new p,this.version={api:l.version},this.providers={HttpProvider:v,IpcProvider:b},this._extend=m(this),this._extend({properties:_()})}var o=t("./web3/requestmanager"),i=t("./web3/iban"),a=t("./web3/methods/eth"),s=t("./web3/methods/db"),c=t("./web3/methods/shh"),u=t("./web3/methods/net"),f=t("./web3/methods/personal"),p=t("./web3/settings"),l=t("./version.json"),h=t("./utils/utils"),d=t("./utils/sha3"),m=t("./web3/extend"),y=t("./web3/batch"),g=t("./web3/property"),v=t("./web3/httpprovider"),b=t("./web3/ipcprovider");r.providers={HttpProvider:v,IpcProvider:b},r.prototype.setProvider=function(t){this._requestManager.setProvider(t),this.currentProvider=t},r.prototype.reset=function(t){this._requestManager.reset(t),this.settings=new p},r.prototype.toHex=h.toHex,r.prototype.toAscii=h.toAscii,r.prototype.toUtf8=h.toUtf8,r.prototype.fromAscii=h.fromAscii,r.prototype.fromUtf8=h.fromUtf8,r.prototype.toDecimal=h.toDecimal,r.prototype.fromDecimal=h.fromDecimal,r.prototype.toBigNumber=h.toBigNumber,r.prototype.toWei=h.toWei,r.prototype.fromWei=h.fromWei,r.prototype.isAddress=h.isAddress,r.prototype.isChecksumAddress=h.isChecksumAddress,r.prototype.toChecksumAddress=h.toChecksumAddress,r.prototype.isIBAN=h.isIBAN,r.prototype.sha3=function(t,e){return"0x"+d(t,e)},r.prototype.fromICAP=function(t){var e=new i(t);return e.address()};var _=function(){return[new g({name:"version.node",getter:"web3_clientVersion"}),new g({name:"version.network",getter:"net_version",inputFormatter:h.toDecimal}),new g({name:"version.ethereum",getter:"eth_protocolVersion",inputFormatter:h.toDecimal}),new g({name:"version.whisper",getter:"shh_version",inputFormatter:h.toDecimal})]};r.prototype.isConnected=function(){return this.currentProvider&&this.currentProvider.isConnected()},r.prototype.createBatch=function(){return new y(this)},e.exports=r},{"./utils/sha3":19,"./utils/utils":20,"./version.json":21,"./web3/batch":24,"./web3/extend":28,"./web3/httpprovider":32,"./web3/iban":33,"./web3/ipcprovider":34,"./web3/methods/db":37,"./web3/methods/eth":38,"./web3/methods/net":39,"./web3/methods/personal":40,"./web3/methods/shh":41,"./web3/property":44,"./web3/requestmanager":45,"./web3/settings":46}],23:[function(t,e,n){var r=t("../utils/sha3"),o=t("./event"),i=t("./formatters"),a=t("../utils/utils"),s=t("./filter"),c=t("./methods/watches"),u=function(t,e,n){this._requestManager=t,this._json=e,this._address=n};u.prototype.encode=function(t){t=t||{};var e={};return["fromBlock","toBlock"].filter(function(e){return void 0!==t[e]}).forEach(function(n){e[n]=i.inputBlockNumberFormatter(t[n])}),e.address=this._address,e},u.prototype.decode=function(t){t.data=t.data||"",t.topics=t.topics||[];var e=t.topics[0].slice(2),n=this._json.filter(function(t){return e===r(a.transformToFullName(t))})[0];if(!n)return console.warn("cannot find event for log"),t;var i=new o(this._requestManager,n,this._address);return i.decode(t)},u.prototype.execute=function(t,e){a.isFunction(arguments[arguments.length-1])&&(e=arguments[arguments.length-1],1===arguments.length&&(t=null));var n=this.encode(t),r=this.decode.bind(this);return new s(this._requestManager,n,c.eth(),r,e)},u.prototype.attachToContract=function(t){var e=this.execute.bind(this);t.allEvents=e},e.exports=u},{"../utils/sha3":19,"../utils/utils":20,"./event":27,"./filter":29,"./formatters":30,"./methods/watches":42}],24:[function(t,e,n){var r=t("./jsonrpc"),o=t("./errors"),i=function(t){this.requestManager=t._requestManager,this.requests=[]};i.prototype.add=function(t){this.requests.push(t)},i.prototype.execute=function(){var t=this.requests;this.requestManager.sendBatch(t,function(e,n){n=n||[],t.map(function(t,e){return n[e]||{}}).forEach(function(e,n){if(t[n].callback){if(!r.getInstance().isValidResponse(e))return t[n].callback(o.InvalidResponse(e));t[n].callback(null,t[n].format?t[n].format(e.result):e.result)}})})},e.exports=i},{"./errors":26,"./jsonrpc":35}],25:[function(t,e,n){var r=t("../utils/utils"),o=t("../solidity/coder"),i=t("./event"),a=t("./function"),s=t("./allevents"),c=function(t,e){return t.filter(function(t){return"constructor"===t.type&&t.inputs.length===e.length}).map(function(t){return t.inputs.map(function(t){return t.type})}).map(function(t){return o.encodeParams(t,e)})[0]||""},u=function(t){t.abi.filter(function(t){return"function"===t.type}).map(function(e){return new a(t._eth,e,t.address)}).forEach(function(e){e.attachToContract(t)})},f=function(t){var e=t.abi.filter(function(t){return"event"===t.type}),n=new s(t._eth._requestManager,e,t.address);n.attachToContract(t),e.map(function(e){return new i(t._eth._requestManager,e,t.address)}).forEach(function(e){e.attachToContract(t)})},p=function(t,e){var n=0,r=!1,o=t._eth.filter("latest",function(i){if(!i&&!r)if(n++,n>50){if(o.stopWatching(),r=!0,!e)throw new Error("Contract transaction couldn't be found after 50 blocks");e(new Error("Contract transaction couldn't be found after 50 blocks"))}else t._eth.getTransactionReceipt(t.transactionHash,function(n,i){i&&!r&&t._eth.getCode(i.contractAddress,function(n,a){if(!r&&a)if(o.stopWatching(),r=!0,a.length>2)t.address=i.contractAddress,u(t),f(t),e&&e(null,t);else{if(!e)throw new Error("The contract code couldn't be stored, please check your gas amount.");e(new Error("The contract code couldn't be stored, please check your gas amount."))}})})})},l=function(t,e){this.eth=t,this.abi=e,this["new"]=function(){var t,e=new h(this.eth,this.abi),n={},o=Array.prototype.slice.call(arguments);r.isFunction(o[o.length-1])&&(t=o.pop());var i=o[o.length-1];r.isObject(i)&&!r.isArray(i)&&(n=o.pop());var a=c(this.abi,o);if(n.data+=a,t)this.eth.sendTransaction(n,function(n,r){n?t(n):(e.transactionHash=r,t(null,e),p(e,t))});else{var s=this.eth.sendTransaction(n);e.transactionHash=s,p(e)}return e},this["new"].getData=this.getData.bind(this)};l.prototype.at=function(t,e){var n=new h(this.eth,this.abi,t);return u(n),f(n),e&&e(null,n),n},l.prototype.getData=function(){var t={},e=Array.prototype.slice.call(arguments),n=e[e.length-1];r.isObject(n)&&!r.isArray(n)&&(t=e.pop());var o=c(this.abi,e);return t.data+=o,t.data};var h=function(t,e,n){this._eth=t,this.transactionHash=null,this.address=n,this.abi=e};e.exports=l},{"../solidity/coder":7,"../utils/utils":20,"./allevents":23,"./event":27,"./function":31}],26:[function(t,e,n){e.exports={InvalidNumberOfParams:function(){return new Error("Invalid number of input parameters")},InvalidConnection:function(t){return new Error("CONNECTION ERROR: Couldn't connect to node "+t+".")},InvalidProvider:function(){return new Error("Provider not set or invalid")},InvalidResponse:function(t){var e=t&&t.error&&t.error.message?t.error.message:"Invalid JSON RPC response: "+JSON.stringify(t);return new Error(e)}}},{}],27:[function(t,e,n){var r=t("../utils/utils"),o=t("../solidity/coder"),i=t("./formatters"),a=t("../utils/sha3"),s=t("./filter"),c=t("./methods/watches"),u=function(t,e,n){this._requestManager=t,this._params=e.inputs,this._name=r.transformToFullName(e),this._address=n,this._anonymous=e.anonymous};u.prototype.types=function(t){return this._params.filter(function(e){return e.indexed===t}).map(function(t){return t.type})},u.prototype.displayName=function(){return r.extractDisplayName(this._name)},u.prototype.typeName=function(){return r.extractTypeName(this._name)},u.prototype.signature=function(){return a(this._name)},u.prototype.encode=function(t,e){t=t||{},e=e||{};var n={};["fromBlock","toBlock"].filter(function(t){return void 0!==e[t]}).forEach(function(t){n[t]=i.inputBlockNumberFormatter(e[t])}),n.topics=[],n.address=this._address,this._anonymous||n.topics.push("0x"+this.signature());var a=this._params.filter(function(t){return t.indexed===!0}).map(function(e){var n=t[e.name];return void 0===n||null===n?null:r.isArray(n)?n.map(function(t){return"0x"+o.encodeParam(e.type,t)}):"0x"+o.encodeParam(e.type,n)});return n.topics=n.topics.concat(a),n},u.prototype.decode=function(t){t.data=t.data||"",t.topics=t.topics||[];var e=this._anonymous?t.topics:t.topics.slice(1),n=e.map(function(t){return t.slice(2)}).join(""),r=o.decodeParams(this.types(!0),n),a=t.data.slice(2),s=o.decodeParams(this.types(!1),a),c=i.outputLogFormatter(t);return c.event=this.displayName(),c.address=t.address,c.args=this._params.reduce(function(t,e){return t[e.name]=e.indexed?r.shift():s.shift(),t},{}),delete c.data,delete c.topics,c},u.prototype.execute=function(t,e,n){r.isFunction(arguments[arguments.length-1])&&(n=arguments[arguments.length-1],2===arguments.length&&(e=null),1===arguments.length&&(e=null,t={}));var o=this.encode(t,e),i=this.decode.bind(this);return new s(this._requestManager,o,c.eth(),i,n)},u.prototype.attachToContract=function(t){var e=this.execute.bind(this),n=this.displayName();t[n]||(t[n]=e),t[n][this.typeName()]=this.execute.bind(this,t)},e.exports=u},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./filter":29,"./formatters":30,"./methods/watches":42}],28:[function(t,e,n){var r=t("./formatters"),o=t("./../utils/utils"),i=t("./method"),a=t("./property"),s=function(t){var e=function(e){var n;e.property?(t[e.property]||(t[e.property]={}),n=t[e.property]):n=t,e.methods&&e.methods.forEach(function(e){e.attachToObject(n),e.setRequestManager(t._requestManager)}),e.properties&&e.properties.forEach(function(e){e.attachToObject(n),e.setRequestManager(t._requestManager)})};return e.formatters=r,e.utils=o,e.Method=i,e.Property=a,e};e.exports=s},{"./../utils/utils":20,"./formatters":30,"./method":36,"./property":44}],29:[function(t,e,n){var r=t("./formatters"),o=t("../utils/utils"),i=function(t){return null===t||"undefined"==typeof t?null:(t=String(t),0===t.indexOf("0x")?t:o.fromUtf8(t))},a=function(t){return o.isString(t)?t:(t=t||{},t.topics=t.topics||[],t.topics=t.topics.map(function(t){return o.isArray(t)?t.map(i):i(t)}),{topics:t.topics,from:t.from,to:t.to,address:t.address,fromBlock:r.inputBlockNumberFormatter(t.fromBlock),toBlock:r.inputBlockNumberFormatter(t.toBlock)})},s=function(t,e){o.isString(t.options)||t.get(function(t,n){t&&e(t),o.isArray(n)&&n.forEach(function(t){e(null,t)})})},c=function(t){var e=function(e,n){return e?t.callbacks.forEach(function(t){t(e)}):void(o.isArray(n)&&n.forEach(function(e){e=t.formatter?t.formatter(e):e,t.callbacks.forEach(function(t){t(null,e)})}))};t.requestManager.startPolling({method:t.implementation.poll.call,params:[t.filterId]},t.filterId,e,t.stopWatching.bind(t))},u=function(t,e,n,r,o){var i=this,u={};return n.forEach(function(e){e.setRequestManager(t),e.attachToObject(u)}),this.requestManager=t,this.options=a(e),this.implementation=u,this.filterId=null,this.callbacks=[], +this.getLogsCallbacks=[],this.pollFilters=[],this.formatter=r,this.implementation.newFilter(this.options,function(t,e){if(t)i.callbacks.forEach(function(e){e(t)});else if(i.filterId=e,i.getLogsCallbacks.forEach(function(t){i.get(t)}),i.getLogsCallbacks=[],i.callbacks.forEach(function(t){s(i,t)}),i.callbacks.length>0&&c(i),"function"==typeof o)return i.watch(o)}),this};u.prototype.watch=function(t){return this.callbacks.push(t),this.filterId&&(s(this,t),c(this)),this},u.prototype.stopWatching=function(){this.requestManager.stopPolling(this.filterId),this.implementation.uninstallFilter(this.filterId,function(){}),this.callbacks=[]},u.prototype.get=function(t){var e=this;if(!o.isFunction(t)){if(null===this.filterId)throw new Error("Filter ID Error: filter().get() can't be chained synchronous, please provide a callback for the get() method.");var n=this.implementation.getLogs(this.filterId);return n.map(function(t){return e.formatter?e.formatter(t):t})}return null===this.filterId?this.getLogsCallbacks.push(t):this.implementation.getLogs(this.filterId,function(n,r){n?t(n):t(null,r.map(function(t){return e.formatter?e.formatter(t):t}))}),this},e.exports=u},{"../utils/utils":20,"./formatters":30}],30:[function(t,e,n){var r=t("../utils/utils"),o=t("../utils/config"),i=t("./iban"),a=function(t){return r.toBigNumber(t)},s=function(t){return"latest"===t||"pending"===t||"earliest"===t},c=function(t){return void 0===t?o.defaultBlock:u(t)},u=function(t){return void 0===t?void 0:s(t)?t:r.toHex(t)},f=function(t){return t.from=t.from||o.defaultAccount,t.from&&(t.from=v(t.from)),t.to&&(t.to=v(t.to)),["gasPrice","gas","value","nonce"].filter(function(e){return void 0!==t[e]}).forEach(function(e){t[e]=r.fromDecimal(t[e])}),t},p=function(t){return t.from=t.from||o.defaultAccount,t.from=v(t.from),t.to&&(t.to=v(t.to)),["gasPrice","gas","value","nonce"].filter(function(e){return void 0!==t[e]}).forEach(function(e){t[e]=r.fromDecimal(t[e])}),t},l=function(t){return null!==t.blockNumber&&(t.blockNumber=r.toDecimal(t.blockNumber)),null!==t.transactionIndex&&(t.transactionIndex=r.toDecimal(t.transactionIndex)),t.nonce=r.toDecimal(t.nonce),t.gas=r.toDecimal(t.gas),t.gasPrice=r.toBigNumber(t.gasPrice),t.value=r.toBigNumber(t.value),t},h=function(t){return null!==t.blockNumber&&(t.blockNumber=r.toDecimal(t.blockNumber)),null!==t.transactionIndex&&(t.transactionIndex=r.toDecimal(t.transactionIndex)),t.cumulativeGasUsed=r.toDecimal(t.cumulativeGasUsed),t.gasUsed=r.toDecimal(t.gasUsed),r.isArray(t.logs)&&(t.logs=t.logs.map(function(t){return m(t)})),t},d=function(t){return t.gasLimit=r.toDecimal(t.gasLimit),t.gasUsed=r.toDecimal(t.gasUsed),t.size=r.toDecimal(t.size),t.timestamp=r.toDecimal(t.timestamp),null!==t.number&&(t.number=r.toDecimal(t.number)),t.difficulty=r.toBigNumber(t.difficulty),t.totalDifficulty=r.toBigNumber(t.totalDifficulty),r.isArray(t.transactions)&&t.transactions.forEach(function(t){return r.isString(t)?void 0:l(t)}),t},m=function(t){return null!==t.blockNumber&&(t.blockNumber=r.toDecimal(t.blockNumber)),null!==t.transactionIndex&&(t.transactionIndex=r.toDecimal(t.transactionIndex)),null!==t.logIndex&&(t.logIndex=r.toDecimal(t.logIndex)),t},y=function(t){return t.ttl=r.fromDecimal(t.ttl),t.workToProve=r.fromDecimal(t.workToProve),t.priority=r.fromDecimal(t.priority),r.isArray(t.topics)||(t.topics=t.topics?[t.topics]:[]),t.topics=t.topics.map(function(t){return 0===t.indexOf("0x")?t:r.fromUtf8(t)}),t},g=function(t){return t.expiry=r.toDecimal(t.expiry),t.sent=r.toDecimal(t.sent),t.ttl=r.toDecimal(t.ttl),t.workProved=r.toDecimal(t.workProved),t.topics||(t.topics=[]),t.topics=t.topics.map(function(t){return r.toAscii(t)}),t},v=function(t){var e=new i(t);if(e.isValid()&&e.isDirect())return"0x"+e.address();if(r.isStrictAddress(t))return t;if(r.isAddress(t))return"0x"+t;throw new Error("invalid address")},b=function(t){return t.startingBlock=r.toDecimal(t.startingBlock),t.currentBlock=r.toDecimal(t.currentBlock),t.highestBlock=r.toDecimal(t.highestBlock),t};e.exports={inputDefaultBlockNumberFormatter:c,inputBlockNumberFormatter:u,inputCallFormatter:f,inputTransactionFormatter:p,inputAddressFormatter:v,inputPostFormatter:y,outputBigNumberFormatter:a,outputTransactionFormatter:l,outputTransactionReceiptFormatter:h,outputBlockFormatter:d,outputLogFormatter:m,outputPostFormatter:g,outputSyncingFormatter:b}},{"../utils/config":18,"../utils/utils":20,"./iban":33}],31:[function(t,e,n){var r=t("../solidity/coder"),o=t("../utils/utils"),i=t("./formatters"),a=t("../utils/sha3"),s=function(t,e,n){this._eth=t,this._inputTypes=e.inputs.map(function(t){return t.type}),this._outputTypes=e.outputs.map(function(t){return t.type}),this._constant=e.constant,this._name=o.transformToFullName(e),this._address=n};s.prototype.extractCallback=function(t){return o.isFunction(t[t.length-1])?t.pop():void 0},s.prototype.extractDefaultBlock=function(t){return t.length>this._inputTypes.length&&!o.isObject(t[t.length-1])?i.inputDefaultBlockNumberFormatter(t.pop()):void 0},s.prototype.toPayload=function(t){var e={};return t.length>this._inputTypes.length&&o.isObject(t[t.length-1])&&(e=t[t.length-1]),e.to=this._address,e.data="0x"+this.signature()+r.encodeParams(this._inputTypes,t),e},s.prototype.signature=function(){return a(this._name).slice(0,8)},s.prototype.unpackOutput=function(t){if(t){t=t.length>=2?t.slice(2):t;var e=r.decodeParams(this._outputTypes,t);return 1===e.length?e[0]:e}},s.prototype.call=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.extractDefaultBlock(t),r=this.toPayload(t);if(!e){var o=this._eth.call(r,n);return this.unpackOutput(o)}var i=this;this._eth.call(r,n,function(t,n){e(t,i.unpackOutput(n))})},s.prototype.sendTransaction=function(){var t=Array.prototype.slice.call(arguments).filter(function(t){return void 0!==t}),e=this.extractCallback(t),n=this.toPayload(t);return e?void this._eth.sendTransaction(n,e):this._eth.sendTransaction(n)},s.prototype.estimateGas=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t);return e?void this._eth.estimateGas(n,e):this._eth.estimateGas(n)},s.prototype.getData=function(){var t=Array.prototype.slice.call(arguments),e=this.toPayload(t);return e.data},s.prototype.displayName=function(){return o.extractDisplayName(this._name)},s.prototype.typeName=function(){return o.extractTypeName(this._name)},s.prototype.request=function(){var t=Array.prototype.slice.call(arguments),e=this.extractCallback(t),n=this.toPayload(t),r=this.unpackOutput.bind(this);return{method:this._constant?"eth_call":"eth_sendTransaction",callback:e,params:[n],format:r}},s.prototype.execute=function(){var t=!this._constant;return t?this.sendTransaction.apply(this,Array.prototype.slice.call(arguments)):this.call.apply(this,Array.prototype.slice.call(arguments))},s.prototype.attachToContract=function(t){var e=this.execute.bind(this);e.request=this.request.bind(this),e.call=this.call.bind(this),e.sendTransaction=this.sendTransaction.bind(this),e.estimateGas=this.estimateGas.bind(this),e.getData=this.getData.bind(this);var n=this.displayName();t[n]||(t[n]=e),t[n][this.typeName()]=e},e.exports=s},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./formatters":30}],32:[function(t,e,n){"use strict";var r,o=t("./errors");r="undefined"!=typeof Meteor&&Meteor.isServer?Npm.require("xmlhttprequest").XMLHttpRequest:"undefined"!=typeof window&&window.XMLHttpRequest?window.XMLHttpRequest:t("xmlhttprequest").XMLHttpRequest;var i=function(t){this.host=t||"http://localhost:8545"};i.prototype.prepareRequest=function(t){var e=new r;return e.open("POST",this.host,t),e.setRequestHeader("Content-Type","application/json"),e},i.prototype.send=function(t){var e=this.prepareRequest(!1);try{e.send(JSON.stringify(t))}catch(n){throw o.InvalidConnection(this.host)}var r=e.responseText;try{r=JSON.parse(r)}catch(i){throw o.InvalidResponse(e.responseText)}return r},i.prototype.sendAsync=function(t,e){var n=this.prepareRequest(!0);n.onreadystatechange=function(){if(4===n.readyState){var t=n.responseText,r=null;try{t=JSON.parse(t)}catch(i){r=o.InvalidResponse(n.responseText)}e(r,t)}};try{n.send(JSON.stringify(t))}catch(r){e(o.InvalidConnection(this.host))}},i.prototype.isConnected=function(){try{return this.send({id:9999999999,jsonrpc:"2.0",method:"net_listening",params:[]}),!0}catch(t){return!1}},e.exports=i},{"./errors":26,xmlhttprequest:17}],33:[function(t,e,n){var r=t("bignumber.js"),o=function(t,e){for(var n=t;n.length<2*e;)n="00"+n;return n},i=function(t){var e="A".charCodeAt(0),n="Z".charCodeAt(0);return t=t.toUpperCase(),t=t.substr(4)+t.substr(0,4),t.split("").map(function(t){var r=t.charCodeAt(0);return r>=e&&n>=r?r-e+10:t}).join("")},a=function(t){for(var e,n=t;n.length>2;)e=n.slice(0,9),n=parseInt(e,10)%97+n.slice(e.length);return parseInt(n,10)%97},s=function(t){this._iban=t};s.fromAddress=function(t){var e=new r(t,16),n=e.toString(36),i=o(n,15);return s.fromBban(i.toUpperCase())},s.fromBban=function(t){var e="XE",n=a(i(e+"00"+t)),r=("0"+(98-n)).slice(-2);return new s(e+r+t)},s.createIndirect=function(t){return s.fromBban("ETH"+t.institution+t.identifier)},s.isValid=function(t){var e=new s(t);return e.isValid()},s.prototype.isValid=function(){return/^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$/.test(this._iban)&&1===a(i(this._iban))},s.prototype.isDirect=function(){return 34===this._iban.length||35===this._iban.length},s.prototype.isIndirect=function(){return 20===this._iban.length},s.prototype.checksum=function(){return this._iban.substr(2,2)},s.prototype.institution=function(){return this.isIndirect()?this._iban.substr(7,4):""},s.prototype.client=function(){return this.isIndirect()?this._iban.substr(11):""},s.prototype.address=function(){if(this.isDirect()){var t=this._iban.substr(4),e=new r(t,36);return o(e.toString(16),20)}return""},s.prototype.toString=function(){return this._iban},e.exports=s},{"bignumber.js":"bignumber.js"}],34:[function(t,e,n){"use strict";var r=t("../utils/utils"),o=t("./errors"),i=function(t,e){var n=this;this.responseCallbacks={},this.path=t,this.connection=e.connect({path:this.path}),this.connection.on("error",function(t){console.error("IPC Connection Error",t),n._timeout()}),this.connection.on("end",function(){n._timeout()}),this.connection.on("data",function(t){n._parseResponse(t.toString()).forEach(function(t){var e=null;r.isArray(t)?t.forEach(function(t){n.responseCallbacks[t.id]&&(e=t.id)}):e=t.id,n.responseCallbacks[e]&&(n.responseCallbacks[e](null,t),delete n.responseCallbacks[e])})})};i.prototype._parseResponse=function(t){var e=this,n=[],r=t.replace(/\}[\n\r]?\{/g,"}|--|{").replace(/\}\][\n\r]?\[\{/g,"}]|--|[{").replace(/\}[\n\r]?\[\{/g,"}|--|[{").replace(/\}\][\n\r]?\{/g,"}]|--|{").split("|--|");return r.forEach(function(t){e.lastChunk&&(t=e.lastChunk+t);var r=null;try{r=JSON.parse(t)}catch(i){return e.lastChunk=t,clearTimeout(e.lastChunkTimeout),void(e.lastChunkTimeout=setTimeout(function(){throw e._timeout(),o.InvalidResponse(t)},15e3))}clearTimeout(e.lastChunkTimeout),e.lastChunk=null,r&&n.push(r)}),n},i.prototype._addResponseCallback=function(t,e){var n=t.id||t[0].id,r=t.method||t[0].method;this.responseCallbacks[n]=e,this.responseCallbacks[n].method=r},i.prototype._timeout=function(){for(var t in this.responseCallbacks)this.responseCallbacks.hasOwnProperty(t)&&(this.responseCallbacks[t](o.InvalidConnection("on IPC")),delete this.responseCallbacks[t])},i.prototype.isConnected=function(){var t=this;return t.connection.writable||t.connection.connect({path:t.path}),!!this.connection.writable},i.prototype.send=function(t){if(this.connection.writeSync){var e;this.connection.writable||this.connection.connect({path:this.path});var n=this.connection.writeSync(JSON.stringify(t));try{e=JSON.parse(n)}catch(r){throw o.InvalidResponse(n)}return e}throw new Error('You tried to send "'+t.method+'" synchronously. Synchronous requests are not supported by the IPC provider.')},i.prototype.sendAsync=function(t,e){this.connection.writable||this.connection.connect({path:this.path}),this.connection.write(JSON.stringify(t)),this._addResponseCallback(t,e)},e.exports=i},{"../utils/utils":20,"./errors":26}],35:[function(t,e,n){var r=function(){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,void(this.messageId=1))};r.getInstance=function(){var t=new r;return t},r.prototype.toPayload=function(t,e){return t||console.error("jsonrpc method should be specified!"),{jsonrpc:"2.0",method:t,params:e||[],id:this.messageId++}},r.prototype.isValidResponse=function(t){return!!t&&!t.error&&"2.0"===t.jsonrpc&&"number"==typeof t.id&&void 0!==t.result},r.prototype.toBatchPayload=function(t){var e=this;return t.map(function(t){return e.toPayload(t.method,t.params)})},e.exports=r},{}],36:[function(t,e,n){var r=t("../utils/utils"),o=t("./errors"),i=function(t){this.name=t.name,this.call=t.call,this.params=t.params||0,this.inputFormatter=t.inputFormatter,this.outputFormatter=t.outputFormatter,this.requestManager=null};i.prototype.setRequestManager=function(t){this.requestManager=t},i.prototype.getCall=function(t){return r.isFunction(this.call)?this.call(t):this.call},i.prototype.extractCallback=function(t){return r.isFunction(t[t.length-1])?t.pop():void 0},i.prototype.validateArgs=function(t){if(t.length!==this.params)throw o.InvalidNumberOfParams()},i.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter.map(function(e,n){return e?e(t[n]):t[n]}):t},i.prototype.formatOutput=function(t){return this.outputFormatter&&t?this.outputFormatter(t):t},i.prototype.toPayload=function(t){var e=this.getCall(t),n=this.extractCallback(t),r=this.formatInput(t);return this.validateArgs(r),{method:e,params:r,callback:n}},i.prototype.attachToObject=function(t){var e=this.buildCall();e.call=this.call;var n=this.name.split(".");n.length>1?(t[n[0]]=t[n[0]]||{},t[n[0]][n[1]]=e):t[n[0]]=e},i.prototype.buildCall=function(){var t=this,e=function(){var e=t.toPayload(Array.prototype.slice.call(arguments));return e.callback?t.requestManager.sendAsync(e,function(n,r){e.callback(n,t.formatOutput(r))}):t.formatOutput(t.requestManager.send(e))};return e.request=this.request.bind(this),e},i.prototype.request=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));return t.format=this.formatOutput.bind(this),t},e.exports=i},{"../utils/utils":20,"./errors":26}],37:[function(t,e,n){var r=t("../method"),o=function(t){this._requestManager=t._requestManager;var e=this;i().forEach(function(n){n.attachToObject(e),n.setRequestManager(t._requestManager)})},i=function(){var t=new r({name:"putString",call:"db_putString",params:3}),e=new r({name:"getString",call:"db_getString",params:2}),n=new r({name:"putHex",call:"db_putHex",params:3}),o=new r({name:"getHex",call:"db_getHex",params:2});return[t,e,n,o]};e.exports=o},{"../method":36}],38:[function(t,e,n){"use strict";function r(t){this._requestManager=t._requestManager;var e=this;w().forEach(function(t){t.attachToObject(e),t.setRequestManager(e._requestManager)}),x().forEach(function(t){t.attachToObject(e),t.setRequestManager(e._requestManager)}),this.iban=d,this.sendIBANTransaction=m.bind(null,this)}var o=t("../formatters"),i=t("../../utils/utils"),a=t("../method"),s=t("../property"),c=t("../../utils/config"),u=t("../contract"),f=t("./watches"),p=t("../filter"),l=t("../syncing"),h=t("../namereg"),d=t("../iban"),m=t("../transfer"),y=function(t){return i.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockByHash":"eth_getBlockByNumber"},g=function(t){return i.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getTransactionByBlockHashAndIndex":"eth_getTransactionByBlockNumberAndIndex"},v=function(t){return i.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleByBlockHashAndIndex":"eth_getUncleByBlockNumberAndIndex"},b=function(t){return i.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockTransactionCountByHash":"eth_getBlockTransactionCountByNumber"},_=function(t){return i.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleCountByBlockHash":"eth_getUncleCountByBlockNumber"};Object.defineProperty(r.prototype,"defaultBlock",{get:function(){return c.defaultBlock},set:function(t){return c.defaultBlock=t,t}}),Object.defineProperty(r.prototype,"defaultAccount",{get:function(){return c.defaultAccount},set:function(t){return c.defaultAccount=t,t}});var w=function(){var t=new a({name:"getBalance",call:"eth_getBalance",params:2,inputFormatter:[o.inputAddressFormatter,o.inputDefaultBlockNumberFormatter],outputFormatter:o.outputBigNumberFormatter}),e=new a({name:"getStorageAt",call:"eth_getStorageAt",params:3,inputFormatter:[null,i.toHex,o.inputDefaultBlockNumberFormatter]}),n=new a({name:"getCode",call:"eth_getCode",params:2,inputFormatter:[o.inputAddressFormatter,o.inputDefaultBlockNumberFormatter]}),r=new a({name:"getBlock",call:y,params:2,inputFormatter:[o.inputBlockNumberFormatter,function(t){return!!t}],outputFormatter:o.outputBlockFormatter}),s=new a({name:"getUncle",call:v,params:2,inputFormatter:[o.inputBlockNumberFormatter,i.toHex],outputFormatter:o.outputBlockFormatter}),c=new a({name:"getCompilers",call:"eth_getCompilers",params:0}),u=new a({name:"getBlockTransactionCount",call:b,params:1,inputFormatter:[o.inputBlockNumberFormatter],outputFormatter:i.toDecimal}),f=new a({name:"getBlockUncleCount",call:_,params:1,inputFormatter:[o.inputBlockNumberFormatter],outputFormatter:i.toDecimal}),p=new a({name:"getTransaction",call:"eth_getTransactionByHash",params:1,outputFormatter:o.outputTransactionFormatter}),l=new a({name:"getTransactionFromBlock",call:g,params:2,inputFormatter:[o.inputBlockNumberFormatter,i.toHex],outputFormatter:o.outputTransactionFormatter}),h=new a({name:"getTransactionReceipt",call:"eth_getTransactionReceipt",params:1,outputFormatter:o.outputTransactionReceiptFormatter}),d=new a({name:"getTransactionCount",call:"eth_getTransactionCount",params:2,inputFormatter:[null,o.inputDefaultBlockNumberFormatter],outputFormatter:i.toDecimal}),m=new a({name:"sendRawTransaction",call:"eth_sendRawTransaction",params:1,inputFormatter:[null]}),w=new a({name:"sendTransaction",call:"eth_sendTransaction",params:1,inputFormatter:[o.inputTransactionFormatter]}),x=new a({name:"sign",call:"eth_sign",params:2,inputFormatter:[o.inputAddressFormatter,null]}),k=new a({name:"call",call:"eth_call",params:2,inputFormatter:[o.inputCallFormatter,o.inputDefaultBlockNumberFormatter]}),B=new a({name:"estimateGas",call:"eth_estimateGas",params:1,inputFormatter:[o.inputCallFormatter],outputFormatter:i.toDecimal}),S=new a({name:"compile.solidity",call:"eth_compileSolidity",params:1}),A=new a({name:"compile.lll",call:"eth_compileLLL",params:1}),C=new a({name:"compile.serpent",call:"eth_compileSerpent",params:1}),F=new a({name:"submitWork",call:"eth_submitWork",params:3}),I=new a({name:"getWork",call:"eth_getWork",params:0});return[t,e,n,r,s,c,u,f,p,l,h,d,k,B,m,w,x,S,A,C,F,I]},x=function(){return[new s({name:"coinbase",getter:"eth_coinbase"}),new s({name:"mining",getter:"eth_mining"}),new s({name:"hashrate",getter:"eth_hashrate",outputFormatter:i.toDecimal}),new s({name:"syncing",getter:"eth_syncing",outputFormatter:o.outputSyncingFormatter}),new s({name:"gasPrice",getter:"eth_gasPrice",outputFormatter:o.outputBigNumberFormatter}),new s({name:"accounts",getter:"eth_accounts"}),new s({name:"blockNumber",getter:"eth_blockNumber",outputFormatter:i.toDecimal})]};r.prototype.contract=function(t){var e=new u(this,t);return e},r.prototype.filter=function(t,e){return new p(this._requestManager,t,f.eth(),o.outputLogFormatter,e)},r.prototype.namereg=function(){return this.contract(h.global.abi).at(h.global.address)},r.prototype.icapNamereg=function(){return this.contract(h.icap.abi).at(h.icap.address)},r.prototype.isSyncing=function(t){return new l(this._requestManager,t)},e.exports=r},{"../../utils/config":18,"../../utils/utils":20,"../contract":25,"../filter":29,"../formatters":30,"../iban":33,"../method":36,"../namereg":43,"../property":44,"../syncing":47,"../transfer":48,"./watches":42}],39:[function(t,e,n){var r=t("../../utils/utils"),o=t("../property"),i=function(t){this._requestManager=t._requestManager;var e=this;a().forEach(function(n){n.attachToObject(e),n.setRequestManager(t._requestManager)})},a=function(){return[new o({name:"listening",getter:"net_listening"}),new o({name:"peerCount",getter:"net_peerCount",outputFormatter:r.toDecimal})]};e.exports=i},{"../../utils/utils":20,"../property":44}],40:[function(t,e,n){"use strict";function r(t){this._requestManager=t._requestManager;var e=this;s().forEach(function(t){t.attachToObject(e),t.setRequestManager(e._requestManager)}),c().forEach(function(t){t.attachToObject(e),t.setRequestManager(e._requestManager)})}var o=t("../method"),i=t("../property"),a=t("../formatters"),s=function(){var t=new o({name:"newAccount",call:"personal_newAccount",params:1,inputFormatter:[null]}),e=new o({name:"unlockAccount",call:"personal_unlockAccount",params:3,inputFormatter:[a.inputAddressFormatter,null,null]}),n=new o({name:"lockAccount",call:"personal_lockAccount",params:1,inputFormatter:[a.inputAddressFormatter]});return[t,e,n]},c=function(){return[new i({name:"listAccounts",getter:"personal_listAccounts"})]};e.exports=r},{"../formatters":30,"../method":36,"../property":44}],41:[function(t,e,n){var r=t("../method"),o=t("../formatters"),i=t("../filter"),a=t("./watches"),s=function(t){this._requestManager=t._requestManager;var e=this;c().forEach(function(t){t.attachToObject(e),t.setRequestManager(e._requestManager)})};s.prototype.filter=function(t,e){return new i(this._requestManager,t,a.shh(),o.outputPostFormatter,e)};var c=function(){var t=new r({name:"post",call:"shh_post",params:1,inputFormatter:[o.inputPostFormatter]}),e=new r({name:"newIdentity",call:"shh_newIdentity",params:0}),n=new r({name:"hasIdentity",call:"shh_hasIdentity",params:1}),i=new r({name:"newGroup",call:"shh_newGroup",params:0}),a=new r({name:"addToGroup",call:"shh_addToGroup",params:0});return[t,e,n,i,a]};e.exports=s},{"../filter":29,"../formatters":30,"../method":36,"./watches":42}],42:[function(t,e,n){var r=t("../method"),o=function(){var t=function(t){var e=t[0];switch(e){case"latest":return t.shift(),this.params=0,"eth_newBlockFilter";case"pending":return t.shift(),this.params=0,"eth_newPendingTransactionFilter";default:return"eth_newFilter"}},e=new r({name:"newFilter",call:t,params:1}),n=new r({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),o=new r({name:"getLogs",call:"eth_getFilterLogs",params:1}),i=new r({name:"poll",call:"eth_getFilterChanges",params:1});return[e,n,o,i]},i=function(){var t=new r({name:"newFilter",call:"shh_newFilter",params:1}),e=new r({name:"uninstallFilter",call:"shh_uninstallFilter",params:1}),n=new r({name:"getLogs",call:"shh_getMessages",params:1}),o=new r({name:"poll",call:"shh_getFilterChanges",params:1});return[t,e,n,o]};e.exports={eth:o,shh:i}},{"../method":36}],43:[function(t,e,n){var r=t("../contracts/GlobalRegistrar.json"),o=t("../contracts/ICAPRegistrar.json"),i="0xc6d9d2cd449a754c494264e1809c50e34d64562b",a="0xa1a111bc074c9cfa781f0c38e63bd51c91b8af00";e.exports={global:{abi:r,address:i},icap:{abi:o,address:a}}},{"../contracts/GlobalRegistrar.json":1,"../contracts/ICAPRegistrar.json":2}],44:[function(t,e,n){var r=t("../utils/utils"),o=function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter,this.requestManager=null};o.prototype.setRequestManager=function(t){this.requestManager=t},o.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},o.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},o.prototype.extractCallback=function(t){return r.isFunction(t[t.length-1])?t.pop():void 0},o.prototype.attachToObject=function(t){var e={get:this.buildGet(),enumerable:!0},n=this.name.split("."),r=n[0];n.length>1&&(t[n[0]]=t[n[0]]||{},t=t[n[0]],r=n[1]),Object.defineProperty(t,r,e),t[i(r)]=this.buildAsyncGet()};var i=function(t){return"get"+t.charAt(0).toUpperCase()+t.slice(1)};o.prototype.buildGet=function(){var t=this;return function(){return t.formatOutput(t.requestManager.send({method:t.getter}))}},o.prototype.buildAsyncGet=function(){var t=this,e=function(e){t.requestManager.sendAsync({method:t.getter},function(n,r){e(n,t.formatOutput(r))})};return e.request=this.request.bind(this),e},o.prototype.request=function(){var t={method:this.getter,params:[],callback:this.extractCallback(Array.prototype.slice.call(arguments))};return t.format=this.formatOutput.bind(this),t},e.exports=o},{"../utils/utils":20}],45:[function(t,e,n){var r=t("./jsonrpc"),o=t("../utils/utils"),i=t("../utils/config"),a=t("./errors"),s=function(t){this.provider=t,this.polls={},this.timeout=null};s.prototype.send=function(t){if(!this.provider)return console.error(a.InvalidProvider()),null;var e=r.getInstance().toPayload(t.method,t.params),n=this.provider.send(e);if(!r.getInstance().isValidResponse(n))throw a.InvalidResponse(n);return n.result},s.prototype.sendAsync=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toPayload(t.method,t.params);this.provider.sendAsync(n,function(t,n){return t?e(t):r.getInstance().isValidResponse(n)?void e(null,n.result):e(a.InvalidResponse(n))})},s.prototype.sendBatch=function(t,e){if(!this.provider)return e(a.InvalidProvider());var n=r.getInstance().toBatchPayload(t);this.provider.sendAsync(n,function(t,n){return t?e(t):o.isArray(n)?void e(t,n):e(a.InvalidResponse(n))})},s.prototype.setProvider=function(t){this.provider=t},s.prototype.startPolling=function(t,e,n,r){this.polls[e]={data:t,id:e,callback:n,uninstall:r},this.timeout||this.poll()},s.prototype.stopPolling=function(t){delete this.polls[t],0===Object.keys(this.polls).length&&this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},s.prototype.reset=function(t){for(var e in this.polls)t&&-1!==e.indexOf("syncPoll_")||(this.polls[e].uninstall(),delete this.polls[e]);0===Object.keys(this.polls).length&&this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},s.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),i.ETH_POLLING_TIMEOUT),0!==Object.keys(this.polls).length){if(!this.provider)return void console.error(a.InvalidProvider());var t=[],e=[];for(var n in this.polls)t.push(this.polls[n].data),e.push(n);if(0!==t.length){var s=r.getInstance().toBatchPayload(t),c={};s.forEach(function(t,n){c[t.id]=e[n]});var u=this;this.provider.sendAsync(s,function(t,e){if(!t){if(!o.isArray(e))throw a.InvalidResponse(e);e.map(function(t){var e=c[t.id];return u.polls[e]?(t.callback=u.polls[e].callback,t):!1}).filter(function(t){return!!t}).filter(function(t){var e=r.getInstance().isValidResponse(t);return e||t.callback(a.InvalidResponse(t)),e}).forEach(function(t){t.callback(null,t.result)})}})}}},e.exports=s},{"../utils/config":18,"../utils/utils":20,"./errors":26,"./jsonrpc":35}],46:[function(t,e,n){var r=function(){this.defaultBlock="latest",this.defaultAccount=void 0};e.exports=r},{}],47:[function(t,e,n){var r=t("./formatters"),o=t("../utils/utils"),i=1,a=function(t){var e=function(e,n){return e?t.callbacks.forEach(function(t){t(e)}):(o.isObject(n)&&n.startingBlock&&(n=r.outputSyncingFormatter(n)),void t.callbacks.forEach(function(e){t.lastSyncState!==n&&(!t.lastSyncState&&o.isObject(n)&&e(null,!0),setTimeout(function(){e(null,n)},0),t.lastSyncState=n)}))};t.requestManager.startPolling({method:"eth_syncing",params:[]},t.pollId,e,t.stopWatching.bind(t))},s=function(t,e){return this.requestManager=t,this.pollId="syncPoll_"+i++,this.callbacks=[],this.addCallback(e),this.lastSyncState=!1,a(this),this};s.prototype.addCallback=function(t){return t&&this.callbacks.push(t),this},s.prototype.stopWatching=function(){this.requestManager.stopPolling(this.pollId),this.callbacks=[]},e.exports=s},{"../utils/utils":20,"./formatters":30}],48:[function(t,e,n){var r=t("./iban"),o=t("../contracts/SmartExchange.json"),i=function(t,e,n,o,i){var c=new r(n);if(!c.isValid())throw new Error("invalid iban address");if(c.isDirect())return a(t,e,c.address(),o,i);if(!i){var u=t.icapNamereg().addr(c.institution());return s(t,e,u,o,c.client())}t.icapNamereg().addr(c.institution(),function(n,r){return s(t,e,r,o,c.client(),i)})},a=function(t,e,n,r,o){return t.sendTransaction({address:n,from:e,value:r},o)},s=function(t,e,n,r,i,a){var s=o;return t.contract(s).at(n).deposit(i,{from:e,value:r},a)};e.exports=i},{"../contracts/SmartExchange.json":3,"./iban":33}],49:[function(t,e,n){},{}],50:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./enc-base64"),t("./md5"),t("./evpkdf"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./enc-base64","./md5","./evpkdf","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.BlockCipher,o=e.algo,i=[],a=[],s=[],c=[],u=[],f=[],p=[],l=[],h=[],d=[];!function(){for(var t=[],e=0;256>e;e++)128>e?t[e]=e<<1:t[e]=e<<1^283;for(var n=0,r=0,e=0;256>e;e++){var o=r^r<<1^r<<2^r<<3^r<<4;o=o>>>8^255&o^99,i[n]=o,a[o]=n;var m=t[n],y=t[m],g=t[y],v=257*t[o]^16843008*o;s[n]=v<<24|v>>>8,c[n]=v<<16|v>>>16,u[n]=v<<8|v>>>24,f[n]=v;var v=16843009*g^65537*y^257*m^16843008*n;p[o]=v<<24|v>>>8,l[o]=v<<16|v>>>16,h[o]=v<<8|v>>>24,d[o]=v,n?(n=m^t[t[t[g^m]]],r^=t[t[r]]):n=r=1}}();var m=[0,1,2,4,8,16,32,64,128,27,54],y=o.AES=r.extend({_doReset:function(){for(var t=this._key,e=t.words,n=t.sigBytes/4,r=this._nRounds=n+6,o=4*(r+1),a=this._keySchedule=[],s=0;o>s;s++)if(n>s)a[s]=e[s];else{var c=a[s-1];s%n?n>6&&s%n==4&&(c=i[c>>>24]<<24|i[c>>>16&255]<<16|i[c>>>8&255]<<8|i[255&c]):(c=c<<8|c>>>24,c=i[c>>>24]<<24|i[c>>>16&255]<<16|i[c>>>8&255]<<8|i[255&c],c^=m[s/n|0]<<24),a[s]=a[s-n]^c}for(var u=this._invKeySchedule=[],f=0;o>f;f++){var s=o-f;if(f%4)var c=a[s];else var c=a[s-4];4>f||4>=s?u[f]=c:u[f]=p[i[c>>>24]]^l[i[c>>>16&255]]^h[i[c>>>8&255]]^d[i[255&c]]}},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._keySchedule,s,c,u,f,i)},decryptBlock:function(t,e){var n=t[e+1];t[e+1]=t[e+3],t[e+3]=n,this._doCryptBlock(t,e,this._invKeySchedule,p,l,h,d,a);var n=t[e+1];t[e+1]=t[e+3],t[e+3]=n},_doCryptBlock:function(t,e,n,r,o,i,a,s){for(var c=this._nRounds,u=t[e]^n[0],f=t[e+1]^n[1],p=t[e+2]^n[2],l=t[e+3]^n[3],h=4,d=1;c>d;d++){var m=r[u>>>24]^o[f>>>16&255]^i[p>>>8&255]^a[255&l]^n[h++],y=r[f>>>24]^o[p>>>16&255]^i[l>>>8&255]^a[255&u]^n[h++],g=r[p>>>24]^o[l>>>16&255]^i[u>>>8&255]^a[255&f]^n[h++],v=r[l>>>24]^o[u>>>16&255]^i[f>>>8&255]^a[255&p]^n[h++];u=m,f=y,p=g,l=v}var m=(s[u>>>24]<<24|s[f>>>16&255]<<16|s[p>>>8&255]<<8|s[255&l])^n[h++],y=(s[f>>>24]<<24|s[p>>>16&255]<<16|s[l>>>8&255]<<8|s[255&u])^n[h++],g=(s[p>>>24]<<24|s[l>>>16&255]<<16|s[u>>>8&255]<<8|s[255&f])^n[h++],v=(s[l>>>24]<<24|s[u>>>16&255]<<16|s[f>>>8&255]<<8|s[255&p])^n[h++];t[e]=m,t[e+1]=y,t[e+2]=g,t[e+3]=v},keySize:8});e.AES=r._createHelper(y)}(),t.AES})},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],51:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){t.lib.Cipher||function(e){var n=t,r=n.lib,o=r.Base,i=r.WordArray,a=r.BufferedBlockAlgorithm,s=n.enc,c=(s.Utf8,s.Base64),u=n.algo,f=u.EvpKDF,p=r.Cipher=a.extend({cfg:o.extend(),createEncryptor:function(t,e){return this.create(this._ENC_XFORM_MODE,t,e)},createDecryptor:function(t,e){return this.create(this._DEC_XFORM_MODE,t,e)},init:function(t,e,n){this.cfg=this.cfg.extend(n),this._xformMode=t,this._key=e,this.reset()},reset:function(){a.reset.call(this),this._doReset()},process:function(t){return this._append(t),this._process()},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(){function t(t){return"string"==typeof t?k:_; +}return function(e){return{encrypt:function(n,r,o){return t(r).encrypt(e,n,r,o)},decrypt:function(n,r,o){return t(r).decrypt(e,n,r,o)}}}}()}),l=(r.StreamCipher=p.extend({_doFinalize:function(){var t=this._process(!0);return t},blockSize:1}),n.mode={}),h=r.BlockCipherMode=o.extend({createEncryptor:function(t,e){return this.Encryptor.create(t,e)},createDecryptor:function(t,e){return this.Decryptor.create(t,e)},init:function(t,e){this._cipher=t,this._iv=e}}),d=l.CBC=function(){function t(t,n,r){var o=this._iv;if(o){var i=o;this._iv=e}else var i=this._prevBlock;for(var a=0;r>a;a++)t[n+a]^=i[a]}var n=h.extend();return n.Encryptor=n.extend({processBlock:function(e,n){var r=this._cipher,o=r.blockSize;t.call(this,e,n,o),r.encryptBlock(e,n),this._prevBlock=e.slice(n,n+o)}}),n.Decryptor=n.extend({processBlock:function(e,n){var r=this._cipher,o=r.blockSize,i=e.slice(n,n+o);r.decryptBlock(e,n),t.call(this,e,n,o),this._prevBlock=i}}),n}(),m=n.pad={},y=m.Pkcs7={pad:function(t,e){for(var n=4*e,r=n-t.sigBytes%n,o=r<<24|r<<16|r<<8|r,a=[],s=0;r>s;s+=4)a.push(o);var c=i.create(a,r);t.concat(c)},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},g=(r.BlockCipher=p.extend({cfg:p.cfg.extend({mode:d,padding:y}),reset:function(){p.reset.call(this);var t=this.cfg,e=t.iv,n=t.mode;if(this._xformMode==this._ENC_XFORM_MODE)var r=n.createEncryptor;else{var r=n.createDecryptor;this._minBufferSize=1}this._mode=r.call(n,this,e&&e.words)},_doProcessBlock:function(t,e){this._mode.processBlock(t,e)},_doFinalize:function(){var t=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){t.pad(this._data,this.blockSize);var e=this._process(!0)}else{var e=this._process(!0);t.unpad(e)}return e},blockSize:4}),r.CipherParams=o.extend({init:function(t){this.mixIn(t)},toString:function(t){return(t||this.formatter).stringify(this)}})),v=n.format={},b=v.OpenSSL={stringify:function(t){var e=t.ciphertext,n=t.salt;if(n)var r=i.create([1398893684,1701076831]).concat(n).concat(e);else var r=e;return r.toString(c)},parse:function(t){var e=c.parse(t),n=e.words;if(1398893684==n[0]&&1701076831==n[1]){var r=i.create(n.slice(2,4));n.splice(0,4),e.sigBytes-=16}return g.create({ciphertext:e,salt:r})}},_=r.SerializableCipher=o.extend({cfg:o.extend({format:b}),encrypt:function(t,e,n,r){r=this.cfg.extend(r);var o=t.createEncryptor(n,r),i=o.finalize(e),a=o.cfg;return g.create({ciphertext:i,key:n,iv:a.iv,algorithm:t,mode:a.mode,padding:a.padding,blockSize:t.blockSize,formatter:r.format})},decrypt:function(t,e,n,r){r=this.cfg.extend(r),e=this._parse(e,r.format);var o=t.createDecryptor(n,r).finalize(e.ciphertext);return o},_parse:function(t,e){return"string"==typeof t?e.parse(t,this):t}}),w=n.kdf={},x=w.OpenSSL={execute:function(t,e,n,r){r||(r=i.random(8));var o=f.create({keySize:e+n}).compute(t,r),a=i.create(o.words.slice(e),4*n);return o.sigBytes=4*e,g.create({key:o,iv:a,salt:r})}},k=r.PasswordBasedCipher=_.extend({cfg:_.cfg.extend({kdf:x}),encrypt:function(t,e,n,r){r=this.cfg.extend(r);var o=r.kdf.execute(n,t.keySize,t.ivSize);r.iv=o.iv;var i=_.encrypt.call(this,t,e,o.key,r);return i.mixIn(o),i},decrypt:function(t,e,n,r){r=this.cfg.extend(r),e=this._parse(e,r.format);var o=r.kdf.execute(n,t.keySize,t.ivSize,e.salt);r.iv=o.iv;var i=_.decrypt.call(this,t,e,o.key,r);return i}})}()})},{"./core":52}],52:[function(t,e,n){!function(t,r){"object"==typeof n?e.exports=n=r():"function"==typeof define&&define.amd?define([],r):t.CryptoJS=r()}(this,function(){var t=t||function(t,e){var n={},r=n.lib={},o=r.Base=function(){function t(){}return{extend:function(e){t.prototype=this;var n=new t;return e&&n.mixIn(e),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),i=r.WordArray=o.extend({init:function(t,n){t=this.words=t||[],n!=e?this.sigBytes=n:this.sigBytes=4*t.length},toString:function(t){return(t||s).stringify(this)},concat:function(t){var e=this.words,n=t.words,r=this.sigBytes,o=t.sigBytes;if(this.clamp(),r%4)for(var i=0;o>i;i++){var a=n[i>>>2]>>>24-i%4*8&255;e[r+i>>>2]|=a<<24-(r+i)%4*8}else for(var i=0;o>i;i+=4)e[r+i>>>2]=n[i>>>2];return this.sigBytes+=o,this},clamp:function(){var e=this.words,n=this.sigBytes;e[n>>>2]&=4294967295<<32-n%4*8,e.length=t.ceil(n/4)},clone:function(){var t=o.clone.call(this);return t.words=this.words.slice(0),t},random:function(e){for(var n,r=[],o=function(e){var e=e,n=987654321,r=4294967295;return function(){n=36969*(65535&n)+(n>>16)&r,e=18e3*(65535&e)+(e>>16)&r;var o=(n<<16)+e&r;return o/=4294967296,o+=.5,o*(t.random()>.5?1:-1)}},a=0;e>a;a+=4){var s=o(4294967296*(n||t.random()));n=987654071*s(),r.push(4294967296*s()|0)}return new i.init(r,e)}}),a=n.enc={},s=a.Hex={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],o=0;n>o;o++){var i=e[o>>>2]>>>24-o%4*8&255;r.push((i>>>4).toString(16)),r.push((15&i).toString(16))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r+=2)n[r>>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new i.init(n,e/2)}},c=a.Latin1={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],o=0;n>o;o++){var i=e[o>>>2]>>>24-o%4*8&255;r.push(String.fromCharCode(i))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r++)n[r>>>2]|=(255&t.charCodeAt(r))<<24-r%4*8;return new i.init(n,e)}},u=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(c.stringify(t)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(t){return c.parse(unescape(encodeURIComponent(t)))}},f=r.BufferedBlockAlgorithm=o.extend({reset:function(){this._data=new i.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=u.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var n=this._data,r=n.words,o=n.sigBytes,a=this.blockSize,s=4*a,c=o/s;c=e?t.ceil(c):t.max((0|c)-this._minBufferSize,0);var u=c*a,f=t.min(4*u,o);if(u){for(var p=0;u>p;p+=a)this._doProcessBlock(r,p);var l=r.splice(0,u);n.sigBytes-=f}return new i.init(l,f)},clone:function(){var t=o.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),p=(r.Hasher=f.extend({cfg:o.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){f.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},blockSize:16,_createHelper:function(t){return function(e,n){return new t.init(n).finalize(e)}},_createHmacHelper:function(t){return function(e,n){return new p.HMAC.init(t,n).finalize(e)}}}),n.algo={});return n}(Math);return t})},{}],53:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.WordArray,o=e.enc;o.Base64={stringify:function(t){var e=t.words,n=t.sigBytes,r=this._map;t.clamp();for(var o=[],i=0;n>i;i+=3)for(var a=e[i>>>2]>>>24-i%4*8&255,s=e[i+1>>>2]>>>24-(i+1)%4*8&255,c=e[i+2>>>2]>>>24-(i+2)%4*8&255,u=a<<16|s<<8|c,f=0;4>f&&n>i+.75*f;f++)o.push(r.charAt(u>>>6*(3-f)&63));var p=r.charAt(64);if(p)for(;o.length%4;)o.push(p);return o.join("")},parse:function(t){var e=t.length,n=this._map,o=n.charAt(64);if(o){var i=t.indexOf(o);-1!=i&&(e=i)}for(var a=[],s=0,c=0;e>c;c++)if(c%4){var u=n.indexOf(t.charAt(c-1))<>>6-c%4*2;a[s>>>2]|=(u|f)<<24-s%4*8,s++}return r.create(a,s)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}(),t.enc.Base64})},{"./core":52}],54:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(t){return t<<8&4278255360|t>>>8&16711935}var n=t,r=n.lib,o=r.WordArray,i=n.enc;i.Utf16=i.Utf16BE={stringify:function(t){for(var e=t.words,n=t.sigBytes,r=[],o=0;n>o;o+=2){var i=e[o>>>2]>>>16-o%4*8&65535;r.push(String.fromCharCode(i))}return r.join("")},parse:function(t){for(var e=t.length,n=[],r=0;e>r;r++)n[r>>>1]|=t.charCodeAt(r)<<16-r%2*16;return o.create(n,2*e)}};i.Utf16LE={stringify:function(t){for(var n=t.words,r=t.sigBytes,o=[],i=0;r>i;i+=2){var a=e(n[i>>>2]>>>16-i%4*8&65535);o.push(String.fromCharCode(a))}return o.join("")},parse:function(t){for(var n=t.length,r=[],i=0;n>i;i++)r[i>>>1]|=e(t.charCodeAt(i)<<16-i%2*16);return o.create(r,2*n)}}}(),t.enc.Utf16})},{"./core":52}],55:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./sha1"),t("./hmac")):"function"==typeof define&&define.amd?define(["./core","./sha1","./hmac"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.Base,o=n.WordArray,i=e.algo,a=i.MD5,s=i.EvpKDF=r.extend({cfg:r.extend({keySize:4,hasher:a,iterations:1}),init:function(t){this.cfg=this.cfg.extend(t)},compute:function(t,e){for(var n=this.cfg,r=n.hasher.create(),i=o.create(),a=i.words,s=n.keySize,c=n.iterations;a.lengthf;f++)u=r.finalize(u),r.reset();i.concat(u)}return i.sigBytes=4*s,i}});e.EvpKDF=function(t,e,n){return s.create(n).compute(t,e)}}(),t.EvpKDF})},{"./core":52,"./hmac":57,"./sha1":76}],56:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,o=r.CipherParams,i=n.enc,a=i.Hex,s=n.format;s.Hex={stringify:function(t){return t.ciphertext.toString(a)},parse:function(t){var e=a.parse(t);return o.create({ciphertext:e})}}}(),t.format.Hex})},{"./cipher-core":51,"./core":52}],57:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){!function(){var e=t,n=e.lib,r=n.Base,o=e.enc,i=o.Utf8,a=e.algo;a.HMAC=r.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=i.parse(e));var n=t.blockSize,r=4*n;e.sigBytes>r&&(e=t.finalize(e)),e.clamp();for(var o=this._oKey=e.clone(),a=this._iKey=e.clone(),s=o.words,c=a.words,u=0;n>u;u++)s[u]^=1549556828,c[u]^=909522486;o.sigBytes=a.sigBytes=r,this.reset()},reset:function(){var t=this._hasher;t.reset(),t.update(this._iKey)},update:function(t){return this._hasher.update(t),this},finalize:function(t){var e=this._hasher,n=e.finalize(t);e.reset();var r=e.finalize(this._oKey.clone().concat(n));return r}})}()})},{"./core":52}],58:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./x64-core"),t("./lib-typedarrays"),t("./enc-utf16"),t("./enc-base64"),t("./md5"),t("./sha1"),t("./sha256"),t("./sha224"),t("./sha512"),t("./sha384"),t("./sha3"),t("./ripemd160"),t("./hmac"),t("./pbkdf2"),t("./evpkdf"),t("./cipher-core"),t("./mode-cfb"),t("./mode-ctr"),t("./mode-ctr-gladman"),t("./mode-ofb"),t("./mode-ecb"),t("./pad-ansix923"),t("./pad-iso10126"),t("./pad-iso97971"),t("./pad-zeropadding"),t("./pad-nopadding"),t("./format-hex"),t("./aes"),t("./tripledes"),t("./rc4"),t("./rabbit"),t("./rabbit-legacy")):"function"==typeof define&&define.amd?define(["./core","./x64-core","./lib-typedarrays","./enc-utf16","./enc-base64","./md5","./sha1","./sha256","./sha224","./sha512","./sha384","./sha3","./ripemd160","./hmac","./pbkdf2","./evpkdf","./cipher-core","./mode-cfb","./mode-ctr","./mode-ctr-gladman","./mode-ofb","./mode-ecb","./pad-ansix923","./pad-iso10126","./pad-iso97971","./pad-zeropadding","./pad-nopadding","./format-hex","./aes","./tripledes","./rc4","./rabbit","./rabbit-legacy"],o):r.CryptoJS=o(r.CryptoJS)}(this,function(t){return t})},{"./aes":50,"./cipher-core":51,"./core":52,"./enc-base64":53,"./enc-utf16":54,"./evpkdf":55,"./format-hex":56,"./hmac":57,"./lib-typedarrays":59,"./md5":60,"./mode-cfb":61,"./mode-ctr":63,"./mode-ctr-gladman":62,"./mode-ecb":64,"./mode-ofb":65,"./pad-ansix923":66,"./pad-iso10126":67,"./pad-iso97971":68,"./pad-nopadding":69,"./pad-zeropadding":70,"./pbkdf2":71,"./rabbit":73,"./rabbit-legacy":72,"./rc4":74,"./ripemd160":75,"./sha1":76,"./sha224":77,"./sha256":78,"./sha3":79,"./sha384":80,"./sha512":81,"./tripledes":82,"./x64-core":83}],59:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(){if("function"==typeof ArrayBuffer){var e=t,n=e.lib,r=n.WordArray,o=r.init,i=r.init=function(t){if(t instanceof ArrayBuffer&&(t=new Uint8Array(t)),(t instanceof Int8Array||"undefined"!=typeof Uint8ClampedArray&&t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array)&&(t=new Uint8Array(t.buffer,t.byteOffset,t.byteLength)),t instanceof Uint8Array){for(var e=t.byteLength,n=[],r=0;e>r;r++)n[r>>>2]|=t[r]<<24-r%4*8;o.call(this,n,e)}else o.apply(this,arguments)};i.prototype=r}}(),t.lib.WordArray})},{"./core":52}],60:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(e){function n(t,e,n,r,o,i,a){var s=t+(e&n|~e&r)+o+a;return(s<>>32-i)+e}function r(t,e,n,r,o,i,a){var s=t+(e&r|n&~r)+o+a;return(s<>>32-i)+e}function o(t,e,n,r,o,i,a){var s=t+(e^n^r)+o+a;return(s<>>32-i)+e}function i(t,e,n,r,o,i,a){var s=t+(n^(e|~r))+o+a;return(s<>>32-i)+e}var a=t,s=a.lib,c=s.WordArray,u=s.Hasher,f=a.algo,p=[];!function(){for(var t=0;64>t;t++)p[t]=4294967296*e.abs(e.sin(t+1))|0}();var l=f.MD5=u.extend({_doReset:function(){this._hash=new c.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(t,e){for(var a=0;16>a;a++){var s=e+a,c=t[s];t[s]=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8)}var u=this._hash.words,f=t[e+0],l=t[e+1],h=t[e+2],d=t[e+3],m=t[e+4],y=t[e+5],g=t[e+6],v=t[e+7],b=t[e+8],_=t[e+9],w=t[e+10],x=t[e+11],k=t[e+12],B=t[e+13],S=t[e+14],A=t[e+15],C=u[0],F=u[1],I=u[2],O=u[3];C=n(C,F,I,O,f,7,p[0]),O=n(O,C,F,I,l,12,p[1]),I=n(I,O,C,F,h,17,p[2]),F=n(F,I,O,C,d,22,p[3]),C=n(C,F,I,O,m,7,p[4]),O=n(O,C,F,I,y,12,p[5]),I=n(I,O,C,F,g,17,p[6]),F=n(F,I,O,C,v,22,p[7]),C=n(C,F,I,O,b,7,p[8]),O=n(O,C,F,I,_,12,p[9]),I=n(I,O,C,F,w,17,p[10]),F=n(F,I,O,C,x,22,p[11]),C=n(C,F,I,O,k,7,p[12]),O=n(O,C,F,I,B,12,p[13]),I=n(I,O,C,F,S,17,p[14]),F=n(F,I,O,C,A,22,p[15]),C=r(C,F,I,O,l,5,p[16]),O=r(O,C,F,I,g,9,p[17]),I=r(I,O,C,F,x,14,p[18]),F=r(F,I,O,C,f,20,p[19]),C=r(C,F,I,O,y,5,p[20]),O=r(O,C,F,I,w,9,p[21]),I=r(I,O,C,F,A,14,p[22]),F=r(F,I,O,C,m,20,p[23]),C=r(C,F,I,O,_,5,p[24]),O=r(O,C,F,I,S,9,p[25]),I=r(I,O,C,F,d,14,p[26]),F=r(F,I,O,C,b,20,p[27]),C=r(C,F,I,O,B,5,p[28]),O=r(O,C,F,I,h,9,p[29]),I=r(I,O,C,F,v,14,p[30]),F=r(F,I,O,C,k,20,p[31]),C=o(C,F,I,O,y,4,p[32]),O=o(O,C,F,I,b,11,p[33]),I=o(I,O,C,F,x,16,p[34]),F=o(F,I,O,C,S,23,p[35]),C=o(C,F,I,O,l,4,p[36]),O=o(O,C,F,I,m,11,p[37]),I=o(I,O,C,F,v,16,p[38]),F=o(F,I,O,C,w,23,p[39]),C=o(C,F,I,O,B,4,p[40]),O=o(O,C,F,I,f,11,p[41]),I=o(I,O,C,F,d,16,p[42]),F=o(F,I,O,C,g,23,p[43]),C=o(C,F,I,O,_,4,p[44]),O=o(O,C,F,I,k,11,p[45]),I=o(I,O,C,F,A,16,p[46]),F=o(F,I,O,C,h,23,p[47]),C=i(C,F,I,O,f,6,p[48]),O=i(O,C,F,I,v,10,p[49]),I=i(I,O,C,F,S,15,p[50]),F=i(F,I,O,C,y,21,p[51]),C=i(C,F,I,O,k,6,p[52]),O=i(O,C,F,I,d,10,p[53]),I=i(I,O,C,F,w,15,p[54]),F=i(F,I,O,C,l,21,p[55]),C=i(C,F,I,O,b,6,p[56]),O=i(O,C,F,I,A,10,p[57]),I=i(I,O,C,F,g,15,p[58]),F=i(F,I,O,C,B,21,p[59]),C=i(C,F,I,O,m,6,p[60]),O=i(O,C,F,I,x,10,p[61]),I=i(I,O,C,F,h,15,p[62]),F=i(F,I,O,C,_,21,p[63]),u[0]=u[0]+C|0,u[1]=u[1]+F|0,u[2]=u[2]+I|0,u[3]=u[3]+O|0},_doFinalize:function(){var t=this._data,n=t.words,r=8*this._nDataBytes,o=8*t.sigBytes;n[o>>>5]|=128<<24-o%32;var i=e.floor(r/4294967296),a=r;n[(o+64>>>9<<4)+15]=16711935&(i<<8|i>>>24)|4278255360&(i<<24|i>>>8),n[(o+64>>>9<<4)+14]=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8),t.sigBytes=4*(n.length+1),this._process();for(var s=this._hash,c=s.words,u=0;4>u;u++){var f=c[u];c[u]=16711935&(f<<8|f>>>24)|4278255360&(f<<24|f>>>8)}return s},clone:function(){var t=u.clone.call(this);return t._hash=this._hash.clone(),t}});a.MD5=u._createHelper(l),a.HmacMD5=u._createHmacHelper(l)}(Math),t.MD5})},{"./core":52}],61:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.mode.CFB=function(){function e(t,e,n,r){var o=this._iv;if(o){var i=o.slice(0);this._iv=void 0}else var i=this._prevBlock;r.encryptBlock(i,0);for(var a=0;n>a;a++)t[e+a]^=i[a]}var n=t.lib.BlockCipherMode.extend();return n.Encryptor=n.extend({processBlock:function(t,n){var r=this._cipher,o=r.blockSize;e.call(this,t,n,o,r),this._prevBlock=t.slice(n,n+o)}}),n.Decryptor=n.extend({processBlock:function(t,n){var r=this._cipher,o=r.blockSize,i=t.slice(n,n+o);e.call(this,t,n,o,r),this._prevBlock=i}}),n}(),t.mode.CFB})},{"./cipher-core":51,"./core":52}],62:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.mode.CTRGladman=function(){function e(t){if(255===(t>>24&255)){var e=t>>16&255,n=t>>8&255,r=255&t;255===e?(e=0,255===n?(n=0,255===r?r=0:++r):++n):++e,t=0,t+=e<<16,t+=n<<8,t+=r}else t+=1<<24;return t}function n(t){return 0===(t[0]=e(t[0]))&&(t[1]=e(t[1])),t}var r=t.lib.BlockCipherMode.extend(),o=r.Encryptor=r.extend({processBlock:function(t,e){var r=this._cipher,o=r.blockSize,i=this._iv,a=this._counter;i&&(a=this._counter=i.slice(0),this._iv=void 0),n(a);var s=a.slice(0);r.encryptBlock(s,0);for(var c=0;o>c;c++)t[e+c]^=s[c]}});return r.Decryptor=o,r}(),t.mode.CTRGladman})},{"./cipher-core":51,"./core":52}],63:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.mode.CTR=function(){var e=t.lib.BlockCipherMode.extend(),n=e.Encryptor=e.extend({processBlock:function(t,e){var n=this._cipher,r=n.blockSize,o=this._iv,i=this._counter;o&&(i=this._counter=o.slice(0),this._iv=void 0);var a=i.slice(0);n.encryptBlock(a,0),i[r-1]=i[r-1]+1|0;for(var s=0;r>s;s++)t[e+s]^=a[s]}});return e.Decryptor=n,e}(),t.mode.CTR})},{"./cipher-core":51,"./core":52}],64:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.mode.ECB=function(){var e=t.lib.BlockCipherMode.extend();return e.Encryptor=e.extend({processBlock:function(t,e){this._cipher.encryptBlock(t,e)}}),e.Decryptor=e.extend({processBlock:function(t,e){this._cipher.decryptBlock(t,e)}}),e}(),t.mode.ECB})},{"./cipher-core":51,"./core":52}],65:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.mode.OFB=function(){var e=t.lib.BlockCipherMode.extend(),n=e.Encryptor=e.extend({processBlock:function(t,e){var n=this._cipher,r=n.blockSize,o=this._iv,i=this._keystream;o&&(i=this._keystream=o.slice(0),this._iv=void 0),n.encryptBlock(i,0);for(var a=0;r>a;a++)t[e+a]^=i[a]}});return e.Decryptor=n,e}(),t.mode.OFB})},{"./cipher-core":51,"./core":52}],66:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.pad.AnsiX923={pad:function(t,e){var n=t.sigBytes,r=4*e,o=r-n%r,i=n+o-1;t.clamp(),t.words[i>>>2]|=o<<24-i%4*8,t.sigBytes+=o},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},t.pad.Ansix923})},{"./cipher-core":51,"./core":52}],67:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.pad.Iso10126={pad:function(e,n){var r=4*n,o=r-e.sigBytes%r;e.concat(t.lib.WordArray.random(o-1)).concat(t.lib.WordArray.create([o<<24],1))},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},t.pad.Iso10126})},{"./cipher-core":51,"./core":52}],68:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.pad.Iso97971={pad:function(e,n){e.concat(t.lib.WordArray.create([2147483648],1)),t.pad.ZeroPadding.pad(e,n)},unpad:function(e){t.pad.ZeroPadding.unpad(e),e.sigBytes--}},t.pad.Iso97971})},{"./cipher-core":51,"./core":52}],69:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.pad.NoPadding={pad:function(){},unpad:function(){}},t.pad.NoPadding})},{"./cipher-core":51,"./core":52}],70:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return t.pad.ZeroPadding={pad:function(t,e){var n=4*e;t.clamp(),t.sigBytes+=n-(t.sigBytes%n||n)},unpad:function(t){for(var e=t.words,n=t.sigBytes-1;!(e[n>>>2]>>>24-n%4*8&255);)n--;t.sigBytes=n+1}},t.pad.ZeroPadding})},{"./cipher-core":51,"./core":52}],71:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./sha1"),t("./hmac")):"function"==typeof define&&define.amd?define(["./core","./sha1","./hmac"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.Base,o=n.WordArray,i=e.algo,a=i.SHA1,s=i.HMAC,c=i.PBKDF2=r.extend({cfg:r.extend({keySize:4,hasher:a,iterations:1}),init:function(t){this.cfg=this.cfg.extend(t)},compute:function(t,e){for(var n=this.cfg,r=s.create(n.hasher,t),i=o.create(),a=o.create([1]),c=i.words,u=a.words,f=n.keySize,p=n.iterations;c.lengthy;y++){m=r.finalize(m),r.reset();for(var g=m.words,v=0;d>v;v++)h[v]^=g[v]}i.concat(l),u[0]++}return i.sigBytes=4*f,i}});e.PBKDF2=function(t,e,n){return c.create(n).compute(t,e)}}(),t.PBKDF2})},{"./core":52,"./hmac":57,"./sha1":76}],72:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./enc-base64"),t("./md5"),t("./evpkdf"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./enc-base64","./md5","./evpkdf","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(){for(var t=this._X,e=this._C,n=0;8>n;n++)s[n]=e[n];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(var n=0;8>n;n++){var r=t[n]+e[n],o=65535&r,i=r>>>16,a=((o*o>>>17)+o*i>>>15)+i*i,u=((4294901760&r)*r|0)+((65535&r)*r|0);c[n]=a^u}t[0]=c[0]+(c[7]<<16|c[7]>>>16)+(c[6]<<16|c[6]>>>16)|0,t[1]=c[1]+(c[0]<<8|c[0]>>>24)+c[7]|0,t[2]=c[2]+(c[1]<<16|c[1]>>>16)+(c[0]<<16|c[0]>>>16)|0,t[3]=c[3]+(c[2]<<8|c[2]>>>24)+c[1]|0,t[4]=c[4]+(c[3]<<16|c[3]>>>16)+(c[2]<<16|c[2]>>>16)|0,t[5]=c[5]+(c[4]<<8|c[4]>>>24)+c[3]|0,t[6]=c[6]+(c[5]<<16|c[5]>>>16)+(c[4]<<16|c[4]>>>16)|0,t[7]=c[7]+(c[6]<<8|c[6]>>>24)+c[5]|0}var n=t,r=n.lib,o=r.StreamCipher,i=n.algo,a=[],s=[],c=[],u=i.RabbitLegacy=o.extend({_doReset:function(){var t=this._key.words,n=this.cfg.iv,r=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],o=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]];this._b=0;for(var i=0;4>i;i++)e.call(this);for(var i=0;8>i;i++)o[i]^=r[i+4&7];if(n){var a=n.words,s=a[0],c=a[1],u=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),f=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),p=u>>>16|4294901760&f,l=f<<16|65535&u;o[0]^=u,o[1]^=p,o[2]^=f,o[3]^=l,o[4]^=u,o[5]^=p,o[6]^=f,o[7]^=l;for(var i=0;4>i;i++)e.call(this)}},_doProcessBlock:function(t,n){var r=this._X;e.call(this),a[0]=r[0]^r[5]>>>16^r[3]<<16,a[1]=r[2]^r[7]>>>16^r[5]<<16,a[2]=r[4]^r[1]>>>16^r[7]<<16,a[3]=r[6]^r[3]>>>16^r[1]<<16;for(var o=0;4>o;o++)a[o]=16711935&(a[o]<<8|a[o]>>>24)|4278255360&(a[o]<<24|a[o]>>>8),t[n+o]^=a[o]},blockSize:4,ivSize:2});n.RabbitLegacy=o._createHelper(u)}(),t.RabbitLegacy})},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],73:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./enc-base64"),t("./md5"),t("./evpkdf"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./enc-base64","./md5","./evpkdf","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(){for(var t=this._X,e=this._C,n=0;8>n;n++)s[n]=e[n];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(var n=0;8>n;n++){var r=t[n]+e[n],o=65535&r,i=r>>>16,a=((o*o>>>17)+o*i>>>15)+i*i,u=((4294901760&r)*r|0)+((65535&r)*r|0);c[n]=a^u}t[0]=c[0]+(c[7]<<16|c[7]>>>16)+(c[6]<<16|c[6]>>>16)|0,t[1]=c[1]+(c[0]<<8|c[0]>>>24)+c[7]|0,t[2]=c[2]+(c[1]<<16|c[1]>>>16)+(c[0]<<16|c[0]>>>16)|0,t[3]=c[3]+(c[2]<<8|c[2]>>>24)+c[1]|0,t[4]=c[4]+(c[3]<<16|c[3]>>>16)+(c[2]<<16|c[2]>>>16)|0,t[5]=c[5]+(c[4]<<8|c[4]>>>24)+c[3]|0,t[6]=c[6]+(c[5]<<16|c[5]>>>16)+(c[4]<<16|c[4]>>>16)|0,t[7]=c[7]+(c[6]<<8|c[6]>>>24)+c[5]|0}var n=t,r=n.lib,o=r.StreamCipher,i=n.algo,a=[],s=[],c=[],u=i.Rabbit=o.extend({_doReset:function(){for(var t=this._key.words,n=this.cfg.iv,r=0;4>r;r++)t[r]=16711935&(t[r]<<8|t[r]>>>24)|4278255360&(t[r]<<24|t[r]>>>8);var o=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],i=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]];this._b=0;for(var r=0;4>r;r++)e.call(this);for(var r=0;8>r;r++)i[r]^=o[r+4&7];if(n){var a=n.words,s=a[0],c=a[1],u=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),f=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),p=u>>>16|4294901760&f,l=f<<16|65535&u;i[0]^=u,i[1]^=p,i[2]^=f,i[3]^=l,i[4]^=u,i[5]^=p,i[6]^=f,i[7]^=l;for(var r=0;4>r;r++)e.call(this)}},_doProcessBlock:function(t,n){var r=this._X;e.call(this),a[0]=r[0]^r[5]>>>16^r[3]<<16,a[1]=r[2]^r[7]>>>16^r[5]<<16,a[2]=r[4]^r[1]>>>16^r[7]<<16,a[3]=r[6]^r[3]>>>16^r[1]<<16;for(var o=0;4>o;o++)a[o]=16711935&(a[o]<<8|a[o]>>>24)|4278255360&(a[o]<<24|a[o]>>>8),t[n+o]^=a[o]},blockSize:4,ivSize:2});n.Rabbit=o._createHelper(u)}(),t.Rabbit})},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],74:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./enc-base64"),t("./md5"),t("./evpkdf"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./enc-base64","./md5","./evpkdf","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(){for(var t=this._S,e=this._i,n=this._j,r=0,o=0;4>o;o++){e=(e+1)%256,n=(n+t[e])%256;var i=t[e];t[e]=t[n],t[n]=i,r|=t[(t[e]+t[n])%256]<<24-8*o}return this._i=e,this._j=n,r}var n=t,r=n.lib,o=r.StreamCipher,i=n.algo,a=i.RC4=o.extend({_doReset:function(){for(var t=this._key,e=t.words,n=t.sigBytes,r=this._S=[],o=0;256>o;o++)r[o]=o;for(var o=0,i=0;256>o;o++){var a=o%n,s=e[a>>>2]>>>24-a%4*8&255;i=(i+r[o]+s)%256;var c=r[o];r[o]=r[i],r[i]=c}this._i=this._j=0},_doProcessBlock:function(t,n){t[n]^=e.call(this)},keySize:8,ivSize:0});n.RC4=o._createHelper(a);var s=i.RC4Drop=a.extend({cfg:a.cfg.extend({drop:192}),_doReset:function(){a._doReset.call(this);for(var t=this.cfg.drop;t>0;t--)e.call(this)}});n.RC4Drop=o._createHelper(s)}(),t.RC4})},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],75:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(e){function n(t,e,n){return t^e^n}function r(t,e,n){return t&e|~t&n}function o(t,e,n){return(t|~e)^n}function i(t,e,n){return t&n|e&~n}function a(t,e,n){return t^(e|~n)}function s(t,e){return t<>>32-e}var c=t,u=c.lib,f=u.WordArray,p=u.Hasher,l=c.algo,h=f.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),d=f.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),m=f.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),y=f.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),g=f.create([0,1518500249,1859775393,2400959708,2840853838]),v=f.create([1352829926,1548603684,1836072691,2053994217,0]),b=l.RIPEMD160=p.extend({_doReset:function(){this._hash=f.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var c=0;16>c;c++){var u=e+c,f=t[u];t[u]=16711935&(f<<8|f>>>24)|4278255360&(f<<24|f>>>8)}var p,l,b,_,w,x,k,B,S,A,C=this._hash.words,F=g.words,I=v.words,O=h.words,N=d.words,D=m.words,P=y.words;x=p=C[0],k=l=C[1],B=b=C[2],S=_=C[3],A=w=C[4];for(var T,c=0;80>c;c+=1)T=p+t[e+O[c]]|0,T+=16>c?n(l,b,_)+F[0]:32>c?r(l,b,_)+F[1]:48>c?o(l,b,_)+F[2]:64>c?i(l,b,_)+F[3]:a(l,b,_)+F[4],T=0|T,T=s(T,D[c]),T=T+w|0,p=w,w=_,_=s(b,10),b=l,l=T,T=x+t[e+N[c]]|0,T+=16>c?a(k,B,S)+I[0]:32>c?i(k,B,S)+I[1]:48>c?o(k,B,S)+I[2]:64>c?r(k,B,S)+I[3]:n(k,B,S)+I[4],T=0|T,T=s(T,P[c]),T=T+A|0,x=A,A=S,S=s(B,10),B=k,k=T;T=C[1]+b+S|0,C[1]=C[2]+_+A|0,C[2]=C[3]+w+x|0,C[3]=C[4]+p+k|0,C[4]=C[0]+l+B|0,C[0]=T},_doFinalize:function(){var t=this._data,e=t.words,n=8*this._nDataBytes,r=8*t.sigBytes;e[r>>>5]|=128<<24-r%32,e[(r+64>>>9<<4)+14]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8),t.sigBytes=4*(e.length+1),this._process();for(var o=this._hash,i=o.words,a=0;5>a;a++){var s=i[a];i[a]=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8)}return o},clone:function(){var t=p.clone.call(this);return t._hash=this._hash.clone(),t}});c.RIPEMD160=p._createHelper(b),c.HmacRIPEMD160=p._createHmacHelper(b)}(Math),t.RIPEMD160})},{"./core":52}],76:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.WordArray,o=n.Hasher,i=e.algo,a=[],s=i.SHA1=o.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878,3285377520]); +},_doProcessBlock:function(t,e){for(var n=this._hash.words,r=n[0],o=n[1],i=n[2],s=n[3],c=n[4],u=0;80>u;u++){if(16>u)a[u]=0|t[e+u];else{var f=a[u-3]^a[u-8]^a[u-14]^a[u-16];a[u]=f<<1|f>>>31}var p=(r<<5|r>>>27)+c+a[u];p+=20>u?(o&i|~o&s)+1518500249:40>u?(o^i^s)+1859775393:60>u?(o&i|o&s|i&s)-1894007588:(o^i^s)-899497514,c=s,s=i,i=o<<30|o>>>2,o=r,r=p}n[0]=n[0]+r|0,n[1]=n[1]+o|0,n[2]=n[2]+i|0,n[3]=n[3]+s|0,n[4]=n[4]+c|0},_doFinalize:function(){var t=this._data,e=t.words,n=8*this._nDataBytes,r=8*t.sigBytes;return e[r>>>5]|=128<<24-r%32,e[(r+64>>>9<<4)+14]=Math.floor(n/4294967296),e[(r+64>>>9<<4)+15]=n,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=o.clone.call(this);return t._hash=this._hash.clone(),t}});e.SHA1=o._createHelper(s),e.HmacSHA1=o._createHmacHelper(s)}(),t.SHA1})},{"./core":52}],77:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./sha256")):"function"==typeof define&&define.amd?define(["./core","./sha256"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.lib,r=n.WordArray,o=e.algo,i=o.SHA256,a=o.SHA224=i.extend({_doReset:function(){this._hash=new r.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var t=i._doFinalize.call(this);return t.sigBytes-=4,t}});e.SHA224=i._createHelper(a),e.HmacSHA224=i._createHmacHelper(a)}(),t.SHA224})},{"./core":52,"./sha256":78}],78:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,o=r.WordArray,i=r.Hasher,a=n.algo,s=[],c=[];!function(){function t(t){for(var n=e.sqrt(t),r=2;n>=r;r++)if(!(t%r))return!1;return!0}function n(t){return 4294967296*(t-(0|t))|0}for(var r=2,o=0;64>o;)t(r)&&(8>o&&(s[o]=n(e.pow(r,.5))),c[o]=n(e.pow(r,1/3)),o++),r++}();var u=[],f=a.SHA256=i.extend({_doReset:function(){this._hash=new o.init(s.slice(0))},_doProcessBlock:function(t,e){for(var n=this._hash.words,r=n[0],o=n[1],i=n[2],a=n[3],s=n[4],f=n[5],p=n[6],l=n[7],h=0;64>h;h++){if(16>h)u[h]=0|t[e+h];else{var d=u[h-15],m=(d<<25|d>>>7)^(d<<14|d>>>18)^d>>>3,y=u[h-2],g=(y<<15|y>>>17)^(y<<13|y>>>19)^y>>>10;u[h]=m+u[h-7]+g+u[h-16]}var v=s&f^~s&p,b=r&o^r&i^o&i,_=(r<<30|r>>>2)^(r<<19|r>>>13)^(r<<10|r>>>22),w=(s<<26|s>>>6)^(s<<21|s>>>11)^(s<<7|s>>>25),x=l+w+v+c[h]+u[h],k=_+b;l=p,p=f,f=s,s=a+x|0,a=i,i=o,o=r,r=x+k|0}n[0]=n[0]+r|0,n[1]=n[1]+o|0,n[2]=n[2]+i|0,n[3]=n[3]+a|0,n[4]=n[4]+s|0,n[5]=n[5]+f|0,n[6]=n[6]+p|0,n[7]=n[7]+l|0},_doFinalize:function(){var t=this._data,n=t.words,r=8*this._nDataBytes,o=8*t.sigBytes;return n[o>>>5]|=128<<24-o%32,n[(o+64>>>9<<4)+14]=e.floor(r/4294967296),n[(o+64>>>9<<4)+15]=r,t.sigBytes=4*n.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});n.SHA256=i._createHelper(f),n.HmacSHA256=i._createHmacHelper(f)}(Math),t.SHA256})},{"./core":52}],79:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./x64-core")):"function"==typeof define&&define.amd?define(["./core","./x64-core"],o):o(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,o=r.WordArray,i=r.Hasher,a=n.x64,s=a.Word,c=n.algo,u=[],f=[],p=[];!function(){for(var t=1,e=0,n=0;24>n;n++){u[t+5*e]=(n+1)*(n+2)/2%64;var r=e%5,o=(2*t+3*e)%5;t=r,e=o}for(var t=0;5>t;t++)for(var e=0;5>e;e++)f[t+5*e]=e+(2*t+3*e)%5*5;for(var i=1,a=0;24>a;a++){for(var c=0,l=0,h=0;7>h;h++){if(1&i){var d=(1<d?l^=1<t;t++)l[t]=s.create()}();var h=c.SHA3=i.extend({cfg:i.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;25>e;e++)t[e]=new s.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var n=this._state,r=this.blockSize/2,o=0;r>o;o++){var i=t[e+2*o],a=t[e+2*o+1];i=16711935&(i<<8|i>>>24)|4278255360&(i<<24|i>>>8),a=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8);var s=n[o];s.high^=a,s.low^=i}for(var c=0;24>c;c++){for(var h=0;5>h;h++){for(var d=0,m=0,y=0;5>y;y++){var s=n[h+5*y];d^=s.high,m^=s.low}var g=l[h];g.high=d,g.low=m}for(var h=0;5>h;h++)for(var v=l[(h+4)%5],b=l[(h+1)%5],_=b.high,w=b.low,d=v.high^(_<<1|w>>>31),m=v.low^(w<<1|_>>>31),y=0;5>y;y++){var s=n[h+5*y];s.high^=d,s.low^=m}for(var x=1;25>x;x++){var s=n[x],k=s.high,B=s.low,S=u[x];if(32>S)var d=k<>>32-S,m=B<>>32-S;else var d=B<>>64-S,m=k<>>64-S;var A=l[f[x]];A.high=d,A.low=m}var C=l[0],F=n[0];C.high=F.high,C.low=F.low;for(var h=0;5>h;h++)for(var y=0;5>y;y++){var x=h+5*y,s=n[x],I=l[x],O=l[(h+1)%5+5*y],N=l[(h+2)%5+5*y];s.high=I.high^~O.high&N.high,s.low=I.low^~O.low&N.low}var s=n[0],D=p[c];s.high^=D.high,s.low^=D.low}},_doFinalize:function(){var t=this._data,n=t.words,r=(8*this._nDataBytes,8*t.sigBytes),i=32*this.blockSize;n[r>>>5]|=1<<24-r%32,n[(e.ceil((r+1)/i)*i>>>5)-1]|=128,t.sigBytes=4*n.length,this._process();for(var a=this._state,s=this.cfg.outputLength/8,c=s/8,u=[],f=0;c>f;f++){var p=a[f],l=p.high,h=p.low;l=16711935&(l<<8|l>>>24)|4278255360&(l<<24|l>>>8),h=16711935&(h<<8|h>>>24)|4278255360&(h<<24|h>>>8),u.push(h),u.push(l)}return new o.init(u,s)},clone:function(){for(var t=i.clone.call(this),e=t._state=this._state.slice(0),n=0;25>n;n++)e[n]=e[n].clone();return t}});n.SHA3=i._createHelper(h),n.HmacSHA3=i._createHmacHelper(h)}(Math),t.SHA3})},{"./core":52,"./x64-core":83}],80:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./x64-core"),t("./sha512")):"function"==typeof define&&define.amd?define(["./core","./x64-core","./sha512"],o):o(r.CryptoJS)}(this,function(t){return function(){var e=t,n=e.x64,r=n.Word,o=n.WordArray,i=e.algo,a=i.SHA512,s=i.SHA384=a.extend({_doReset:function(){this._hash=new o.init([new r.init(3418070365,3238371032),new r.init(1654270250,914150663),new r.init(2438529370,812702999),new r.init(355462360,4144912697),new r.init(1731405415,4290775857),new r.init(2394180231,1750603025),new r.init(3675008525,1694076839),new r.init(1203062813,3204075428)])},_doFinalize:function(){var t=a._doFinalize.call(this);return t.sigBytes-=16,t}});e.SHA384=a._createHelper(s),e.HmacSHA384=a._createHmacHelper(s)}(),t.SHA384})},{"./core":52,"./sha512":81,"./x64-core":83}],81:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./x64-core")):"function"==typeof define&&define.amd?define(["./core","./x64-core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(){return a.create.apply(a,arguments)}var n=t,r=n.lib,o=r.Hasher,i=n.x64,a=i.Word,s=i.WordArray,c=n.algo,u=[e(1116352408,3609767458),e(1899447441,602891725),e(3049323471,3964484399),e(3921009573,2173295548),e(961987163,4081628472),e(1508970993,3053834265),e(2453635748,2937671579),e(2870763221,3664609560),e(3624381080,2734883394),e(310598401,1164996542),e(607225278,1323610764),e(1426881987,3590304994),e(1925078388,4068182383),e(2162078206,991336113),e(2614888103,633803317),e(3248222580,3479774868),e(3835390401,2666613458),e(4022224774,944711139),e(264347078,2341262773),e(604807628,2007800933),e(770255983,1495990901),e(1249150122,1856431235),e(1555081692,3175218132),e(1996064986,2198950837),e(2554220882,3999719339),e(2821834349,766784016),e(2952996808,2566594879),e(3210313671,3203337956),e(3336571891,1034457026),e(3584528711,2466948901),e(113926993,3758326383),e(338241895,168717936),e(666307205,1188179964),e(773529912,1546045734),e(1294757372,1522805485),e(1396182291,2643833823),e(1695183700,2343527390),e(1986661051,1014477480),e(2177026350,1206759142),e(2456956037,344077627),e(2730485921,1290863460),e(2820302411,3158454273),e(3259730800,3505952657),e(3345764771,106217008),e(3516065817,3606008344),e(3600352804,1432725776),e(4094571909,1467031594),e(275423344,851169720),e(430227734,3100823752),e(506948616,1363258195),e(659060556,3750685593),e(883997877,3785050280),e(958139571,3318307427),e(1322822218,3812723403),e(1537002063,2003034995),e(1747873779,3602036899),e(1955562222,1575990012),e(2024104815,1125592928),e(2227730452,2716904306),e(2361852424,442776044),e(2428436474,593698344),e(2756734187,3733110249),e(3204031479,2999351573),e(3329325298,3815920427),e(3391569614,3928383900),e(3515267271,566280711),e(3940187606,3454069534),e(4118630271,4000239992),e(116418474,1914138554),e(174292421,2731055270),e(289380356,3203993006),e(460393269,320620315),e(685471733,587496836),e(852142971,1086792851),e(1017036298,365543100),e(1126000580,2618297676),e(1288033470,3409855158),e(1501505948,4234509866),e(1607167915,987167468),e(1816402316,1246189591)],f=[];!function(){for(var t=0;80>t;t++)f[t]=e()}();var p=c.SHA512=o.extend({_doReset:function(){this._hash=new s.init([new a.init(1779033703,4089235720),new a.init(3144134277,2227873595),new a.init(1013904242,4271175723),new a.init(2773480762,1595750129),new a.init(1359893119,2917565137),new a.init(2600822924,725511199),new a.init(528734635,4215389547),new a.init(1541459225,327033209)])},_doProcessBlock:function(t,e){for(var n=this._hash.words,r=n[0],o=n[1],i=n[2],a=n[3],s=n[4],c=n[5],p=n[6],l=n[7],h=r.high,d=r.low,m=o.high,y=o.low,g=i.high,v=i.low,b=a.high,_=a.low,w=s.high,x=s.low,k=c.high,B=c.low,S=p.high,A=p.low,C=l.high,F=l.low,I=h,O=d,N=m,D=y,P=g,T=v,E=b,R=_,M=w,H=x,j=k,q=B,L=S,z=A,U=C,W=F,J=0;80>J;J++){var G=f[J];if(16>J)var X=G.high=0|t[e+2*J],$=G.low=0|t[e+2*J+1];else{var K=f[J-15],V=K.high,Z=K.low,Y=(V>>>1|Z<<31)^(V>>>8|Z<<24)^V>>>7,Q=(Z>>>1|V<<31)^(Z>>>8|V<<24)^(Z>>>7|V<<25),tt=f[J-2],et=tt.high,nt=tt.low,rt=(et>>>19|nt<<13)^(et<<3|nt>>>29)^et>>>6,ot=(nt>>>19|et<<13)^(nt<<3|et>>>29)^(nt>>>6|et<<26),it=f[J-7],at=it.high,st=it.low,ct=f[J-16],ut=ct.high,ft=ct.low,$=Q+st,X=Y+at+(Q>>>0>$>>>0?1:0),$=$+ot,X=X+rt+(ot>>>0>$>>>0?1:0),$=$+ft,X=X+ut+(ft>>>0>$>>>0?1:0);G.high=X,G.low=$}var pt=M&j^~M&L,lt=H&q^~H&z,ht=I&N^I&P^N&P,dt=O&D^O&T^D&T,mt=(I>>>28|O<<4)^(I<<30|O>>>2)^(I<<25|O>>>7),yt=(O>>>28|I<<4)^(O<<30|I>>>2)^(O<<25|I>>>7),gt=(M>>>14|H<<18)^(M>>>18|H<<14)^(M<<23|H>>>9),vt=(H>>>14|M<<18)^(H>>>18|M<<14)^(H<<23|M>>>9),bt=u[J],_t=bt.high,wt=bt.low,xt=W+vt,kt=U+gt+(W>>>0>xt>>>0?1:0),xt=xt+lt,kt=kt+pt+(lt>>>0>xt>>>0?1:0),xt=xt+wt,kt=kt+_t+(wt>>>0>xt>>>0?1:0),xt=xt+$,kt=kt+X+($>>>0>xt>>>0?1:0),Bt=yt+dt,St=mt+ht+(yt>>>0>Bt>>>0?1:0);U=L,W=z,L=j,z=q,j=M,q=H,H=R+xt|0,M=E+kt+(R>>>0>H>>>0?1:0)|0,E=P,R=T,P=N,T=D,N=I,D=O,O=xt+Bt|0,I=kt+St+(xt>>>0>O>>>0?1:0)|0}d=r.low=d+O,r.high=h+I+(O>>>0>d>>>0?1:0),y=o.low=y+D,o.high=m+N+(D>>>0>y>>>0?1:0),v=i.low=v+T,i.high=g+P+(T>>>0>v>>>0?1:0),_=a.low=_+R,a.high=b+E+(R>>>0>_>>>0?1:0),x=s.low=x+H,s.high=w+M+(H>>>0>x>>>0?1:0),B=c.low=B+q,c.high=k+j+(q>>>0>B>>>0?1:0),A=p.low=A+z,p.high=S+L+(z>>>0>A>>>0?1:0),F=l.low=F+W,l.high=C+U+(W>>>0>F>>>0?1:0)},_doFinalize:function(){var t=this._data,e=t.words,n=8*this._nDataBytes,r=8*t.sigBytes;e[r>>>5]|=128<<24-r%32,e[(r+128>>>10<<5)+30]=Math.floor(n/4294967296),e[(r+128>>>10<<5)+31]=n,t.sigBytes=4*e.length,this._process();var o=this._hash.toX32();return o},clone:function(){var t=o.clone.call(this);return t._hash=this._hash.clone(),t},blockSize:32});n.SHA512=o._createHelper(p),n.HmacSHA512=o._createHmacHelper(p)}(),t.SHA512})},{"./core":52,"./x64-core":83}],82:[function(t,e,n){!function(r,o,i){"object"==typeof n?e.exports=n=o(t("./core"),t("./enc-base64"),t("./md5"),t("./evpkdf"),t("./cipher-core")):"function"==typeof define&&define.amd?define(["./core","./enc-base64","./md5","./evpkdf","./cipher-core"],o):o(r.CryptoJS)}(this,function(t){return function(){function e(t,e){var n=(this._lBlock>>>t^this._rBlock)&e;this._rBlock^=n,this._lBlock^=n<>>t^this._lBlock)&e;this._lBlock^=n,this._rBlock^=n<r;r++){var o=c[r]-1;n[r]=e[o>>>5]>>>31-o%32&1}for(var i=this._subKeys=[],a=0;16>a;a++){for(var s=i[a]=[],p=f[a],r=0;24>r;r++)s[r/6|0]|=n[(u[r]-1+p)%28]<<31-r%6,s[4+(r/6|0)]|=n[28+(u[r+24]-1+p)%28]<<31-r%6;s[0]=s[0]<<1|s[0]>>>31;for(var r=1;7>r;r++)s[r]=s[r]>>>4*(r-1)+3;s[7]=s[7]<<5|s[7]>>>27}for(var l=this._invSubKeys=[],r=0;16>r;r++)l[r]=i[15-r]},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._subKeys)},decryptBlock:function(t,e){this._doCryptBlock(t,e,this._invSubKeys)},_doCryptBlock:function(t,r,o){this._lBlock=t[r],this._rBlock=t[r+1],e.call(this,4,252645135),e.call(this,16,65535),n.call(this,2,858993459),n.call(this,8,16711935),e.call(this,1,1431655765);for(var i=0;16>i;i++){for(var a=o[i],s=this._lBlock,c=this._rBlock,u=0,f=0;8>f;f++)u|=p[f][((c^a[f])&l[f])>>>0];this._lBlock=c,this._rBlock=s^u}var h=this._lBlock;this._lBlock=this._rBlock,this._rBlock=h,e.call(this,1,1431655765),n.call(this,8,16711935),n.call(this,2,858993459),e.call(this,16,65535),e.call(this,4,252645135),t[r]=this._lBlock,t[r+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});r.DES=a._createHelper(h);var d=s.TripleDES=a.extend({_doReset:function(){var t=this._key,e=t.words;this._des1=h.createEncryptor(i.create(e.slice(0,2))),this._des2=h.createEncryptor(i.create(e.slice(2,4))),this._des3=h.createEncryptor(i.create(e.slice(4,6)))},encryptBlock:function(t,e){this._des1.encryptBlock(t,e),this._des2.decryptBlock(t,e),this._des3.encryptBlock(t,e)},decryptBlock:function(t,e){this._des3.decryptBlock(t,e),this._des2.encryptBlock(t,e),this._des1.decryptBlock(t,e)},keySize:6,ivSize:2,blockSize:2});r.TripleDES=a._createHelper(d)}(),t.TripleDES})},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],83:[function(t,e,n){!function(r,o){"object"==typeof n?e.exports=n=o(t("./core")):"function"==typeof define&&define.amd?define(["./core"],o):o(r.CryptoJS)}(this,function(t){return function(e){var n=t,r=n.lib,o=r.Base,i=r.WordArray,a=n.x64={};a.Word=o.extend({init:function(t,e){this.high=t,this.low=e}}),a.WordArray=o.extend({init:function(t,n){t=this.words=t||[],n!=e?this.sigBytes=n:this.sigBytes=8*t.length},toX32:function(){for(var t=this.words,e=t.length,n=[],r=0;e>r;r++){var o=t[r];n.push(o.high),n.push(o.low)}return i.create(n,this.sigBytes)},clone:function(){for(var t=o.clone.call(this),e=t.words=this.words.slice(0),n=e.length,r=0;n>r;r++)e[r]=e[r].clone();return t}})}(),t})},{"./core":52}],84:[function(t,e,n){!function(t){function r(t){for(var e,n,r=[],o=0,i=t.length;i>o;)e=t.charCodeAt(o++),e>=55296&&56319>=e&&i>o?(n=t.charCodeAt(o++),56320==(64512&n)?r.push(((1023&e)<<10)+(1023&n)+65536):(r.push(e),o--)):r.push(e);return r}function o(t){for(var e,n=t.length,r=-1,o="";++r65535&&(e-=65536,o+=v(e>>>10&1023|55296),e=56320|1023&e),o+=v(e);return o}function i(t){if(t>=55296&&57343>=t)throw Error("Lone surrogate U+"+t.toString(16).toUpperCase()+" is not a scalar value")}function a(t,e){return v(t>>e&63|128)}function s(t){if(0==(4294967168&t))return v(t);var e="";return 0==(4294965248&t)?e=v(t>>6&31|192):0==(4294901760&t)?(i(t),e=v(t>>12&15|224),e+=a(t,6)):0==(4292870144&t)&&(e=v(t>>18&7|240),e+=a(t,12),e+=a(t,6)),e+=v(63&t|128)}function c(t){for(var e,n=r(t),o=n.length,i=-1,a="";++i=y)throw Error("Invalid byte index");var t=255&m[g];if(g++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function f(){var t,e,n,r,o;if(g>y)throw Error("Invalid byte index");if(g==y)return!1;if(t=255&m[g],g++,0==(128&t))return t;if(192==(224&t)){var e=u();if(o=(31&t)<<6|e,o>=128)return o;throw Error("Invalid continuation byte")}if(224==(240&t)){if(e=u(),n=u(),o=(15&t)<<12|e<<6|n,o>=2048)return i(o),o;throw Error("Invalid continuation byte")}if(240==(248&t)&&(e=u(),n=u(),r=u(),o=(15&t)<<18|e<<12|n<<6|r,o>=65536&&1114111>=o))return o;throw Error("Invalid UTF-8 detected")}function p(t){m=r(t),y=m.length,g=0;for(var e,n=[];(e=f())!==!1;)n.push(e);return o(n)}var l="object"==typeof n&&n,h="object"==typeof e&&e&&e.exports==l&&e,d="object"==typeof global&&global;(d.global===d||d.window===d)&&(t=d);var m,y,g,v=String.fromCharCode,b={version:"2.0.0",encode:c,decode:p};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return b});else if(l&&!l.nodeType)if(h)h.exports=b;else{var _={},w=_.hasOwnProperty;for(var x in b)w.call(b,x)&&(l[x]=b[x])}else t.utf8=b}(this)},{}],"bignumber.js":[function(t,e,n){!function(n){"use strict";function r(t){function e(t,r){var o,i,a,s,c,u,f=this;if(!(f instanceof e))return W&&D(26,"constructor call without new",t),new e(t,r);if(null!=r&&J(r,2,64,E,"base")){if(r=0|r,u=t+"",10==r)return f=new e(t instanceof e?t:u),P(f,H+f.e+1,j);if((s="number"==typeof t)&&0*t!=0||!new RegExp("^-?"+(o="["+x.slice(0,r)+"]+")+"(?:\\."+o+")?$",37>r?"i":"").test(u))return m(f,u,s,r);s?(f.s=0>1/t?(u=u.slice(1),-1):1,W&&u.replace(/^0\.0*|\./,"").length>15&&D(E,w,t),s=!1):f.s=45===u.charCodeAt(0)?(u=u.slice(1),-1):1,u=n(u,10,r,f.s)}else{if(t instanceof e)return f.s=t.s,f.e=t.e,f.c=(t=t.c)?t.slice():t,void(E=0);if((s="number"==typeof t)&&0*t==0){if(f.s=0>1/t?(t=-t,-1):1,t===~~t){for(i=0,a=t;a>=10;a/=10,i++);return f.e=i,f.c=[t],void(E=0)}u=t+""}else{if(!y.test(u=t+""))return m(f,u,s);f.s=45===u.charCodeAt(0)?(u=u.slice(1),-1):1}}for((i=u.indexOf("."))>-1&&(u=u.replace(".","")),(a=u.search(/e/i))>0?(0>i&&(i=a),i+=+u.slice(a+1),u=u.substring(0,a)):0>i&&(i=u.length),a=0;48===u.charCodeAt(a);a++);for(c=u.length;48===u.charCodeAt(--c););if(u=u.slice(a,c+1))if(c=u.length,s&&W&&c>15&&D(E,w,f.s*t),i=i-a-1,i>U)f.c=f.e=null;else if(z>i)f.c=[f.e=0];else{if(f.e=i,f.c=[],a=(i+1)%B,0>i&&(a+=B),c>a){for(a&&f.c.push(+u.slice(0,a)),c-=B;c>a;)f.c.push(+u.slice(a,a+=B));u=u.slice(a),a=B-u.length}else a-=c;for(;a--;u+="0");f.c.push(+u)}else f.c=[f.e=0];E=0}function n(t,n,r,o){var a,s,c,f,l,h,d,m=t.indexOf("."),y=H,g=j;for(37>r&&(t=t.toLowerCase()),m>=0&&(c=$,$=0,t=t.replace(".",""),d=new e(r),l=d.pow(t.length-m),$=c,d.c=u(p(i(l.c),l.e),10,n),d.e=d.c.length),h=u(t,r,n),s=c=h.length;0==h[--c];h.pop());if(!h[0])return"0";if(0>m?--s:(l.c=h,l.e=s,l.s=o,l=T(l,d,y,g,n),h=l.c,f=l.r,s=l.e),a=s+y+1,m=h[a],c=n/2,f=f||0>a||null!=h[a+1],f=4>g?(null!=m||f)&&(0==g||g==(l.s<0?3:2)):m>c||m==c&&(4==g||f||6==g&&1&h[a-1]||g==(l.s<0?8:7)),1>a||!h[0])t=f?p("1",-y):"0";else{if(h.length=a,f)for(--n;++h[--a]>n;)h[a]=0,a||(++s,h.unshift(1));for(c=h.length;!h[--c];);for(m=0,t="";c>=m;t+=x.charAt(h[m++]));t=p(t,s)}return t}function h(t,n,r,o){var a,s,c,u,l;if(r=null!=r&&J(r,0,8,o,_)?0|r:j,!t.c)return t.toString();if(a=t.c[0],c=t.e,null==n)l=i(t.c),l=19==o||24==o&&q>=c?f(l,c):p(l,c);else if(t=P(new e(t),n,r),s=t.e,l=i(t.c),u=l.length,19==o||24==o&&(s>=n||q>=s)){for(;n>u;l+="0",u++);l=f(l,s)}else if(n-=c,l=p(l,s),s+1>u){if(--n>0)for(l+=".";n--;l+="0");}else if(n+=s-u,n>0)for(s+1==u&&(l+=".");n--;l+="0");return t.s<0&&a?"-"+l:l}function I(t,n){var r,o,i=0;for(c(t[0])&&(t=t[0]),r=new e(t[0]);++it||t>n||t!=l(t))&&D(r,(o||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function N(t,e,n){for(var r=1,o=e.length;!e[--o];e.pop());for(o=e[0];o>=10;o/=10,r++);return(n=r+n*B-1)>U?t.c=t.e=null:z>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function D(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",E=0,r}function P(t,e,n,r){var o,i,a,s,c,u,f,p=t.c,l=A;if(p){t:{for(o=1,s=p[0];s>=10;s/=10,o++);if(i=e-o,0>i)i+=B,a=e,c=p[u=0],f=c/l[o-a-1]%10|0;else if(u=g((i+1)/B),u>=p.length){if(!r)break t;for(;p.length<=u;p.push(0));c=f=0,o=1,i%=B,a=i-B+1}else{for(c=s=p[u],o=1;s>=10;s/=10,o++);i%=B,a=i-B+o,f=0>a?0:c/l[o-a-1]%10|0}if(r=r||0>e||null!=p[u+1]||(0>a?c:c%l[o-a-1]),r=4>n?(f||r)&&(0==n||n==(t.s<0?3:2)):f>5||5==f&&(4==n||r||6==n&&(i>0?a>0?c/l[o-a]:0:p[u-1])%10&1||n==(t.s<0?8:7)),1>e||!p[0])return p.length=0,r?(e-=t.e+1,p[0]=l[e%B],t.e=-e||0):p[0]=t.e=0,t;if(0==i?(p.length=u,s=1,u--):(p.length=u+1,s=l[B-i],p[u]=a>0?v(c/l[o-a]%l[a])*s:0),r)for(;;){if(0==u){for(i=1,a=p[0];a>=10;a/=10,i++);for(a=p[0]+=s,s=1;a>=10;a/=10,s++);i!=s&&(t.e++,p[0]==k&&(p[0]=1));break}if(p[u]+=s,p[u]!=k)break;p[u--]=0,s=1}for(i=p.length;0===p[--i];p.pop());}t.e>U?t.c=t.e=null:t.en?null!=(t=o[n++]):void 0};return a(e="DECIMAL_PLACES")&&J(t,0,F,2,e)&&(H=0|t),r[e]=H,a(e="ROUNDING_MODE")&&J(t,0,8,2,e)&&(j=0|t),r[e]=j,a(e="EXPONENTIAL_AT")&&(c(t)?J(t[0],-F,0,2,e)&&J(t[1],0,F,2,e)&&(q=0|t[0],L=0|t[1]):J(t,-F,F,2,e)&&(q=-(L=0|(0>t?-t:t)))),r[e]=[q,L],a(e="RANGE")&&(c(t)?J(t[0],-F,-1,2,e)&&J(t[1],1,F,2,e)&&(z=0|t[0],U=0|t[1]):J(t,-F,F,2,e)&&(0|t?z=-(U=0|(0>t?-t:t)):W&&D(2,e+" cannot be zero",t))),r[e]=[z,U],a(e="ERRORS")&&(t===!!t||1===t||0===t?(E=0,J=(W=!!t)?O:s):W&&D(2,e+b,t)),r[e]=W,a(e="CRYPTO")&&(t===!!t||1===t||0===t?(G=!(!t||!d||"object"!=typeof d),t&&!G&&W&&D(2,"crypto unavailable",d)):W&&D(2,e+b,t)),r[e]=G,a(e="MODULO_MODE")&&J(t,0,9,2,e)&&(X=0|t),r[e]=X,a(e="POW_PRECISION")&&J(t,0,F,2,e)&&($=0|t),r[e]=$,a(e="FORMAT")&&("object"==typeof t?K=t:W&&D(2,e+" not an object",t)),r[e]=K,r},e.max=function(){return I(arguments,R.lt)},e.min=function(){return I(arguments,R.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return v(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,o,i,a,s,c=0,u=[],f=new e(M);if(t=null!=t&&J(t,0,F,14)?0|t:H,a=g(t/B),G)if(d&&d.getRandomValues){for(r=d.getRandomValues(new Uint32Array(a*=2));a>c;)s=131072*r[c]+(r[c+1]>>>11),s>=9e15?(o=d.getRandomValues(new Uint32Array(2)),r[c]=o[0],r[c+1]=o[1]):(u.push(s%1e14),c+=2);c=a/2}else if(d&&d.randomBytes){for(r=d.randomBytes(a*=7);a>c;)s=281474976710656*(31&r[c])+1099511627776*r[c+1]+4294967296*r[c+2]+16777216*r[c+3]+(r[c+4]<<16)+(r[c+5]<<8)+r[c+6],s>=9e15?d.randomBytes(7).copy(r,c):(u.push(s%1e14),c+=7);c=a/7}else W&&D(14,"crypto unavailable",d);if(!c)for(;a>c;)s=n(),9e15>s&&(u[c++]=s%1e14);for(a=u[--c],t%=B,a&&t&&(s=A[B-t],u[c]=v(a/s)*s);0===u[c];u.pop(),c--);if(0>c)u=[i=0];else{for(i=-1;0===u[0];u.shift(),i-=B);for(c=1,s=u[0];s>=10;s/=10,c++);B>c&&(i-=B-c)}return f.e=i,f.c=u,f}}(),T=function(){function t(t,e,n){var r,o,i,a,s=0,c=t.length,u=e%C,f=e/C|0;for(t=t.slice();c--;)i=t[c]%C,a=t[c]/C|0,r=f*i+a*u,o=u*i+r%C*C+s,s=(o/n|0)+(r/C|0)+f*a,t[c]=o%n;return s&&t.unshift(s),t}function n(t,e,n,r){var o,i;if(n!=r)i=n>r?1:-1;else for(o=i=0;n>o;o++)if(t[o]!=e[o]){i=t[o]>e[o]?1:-1;break}return i}function r(t,e,n,r){for(var o=0;n--;)t[n]-=o,o=t[n]1;t.shift());}return function(i,a,s,c,u){var f,p,l,h,d,m,y,g,b,_,w,x,S,A,C,F,I,O=i.s==a.s?1:-1,N=i.c,D=a.c;if(!(N&&N[0]&&D&&D[0]))return new e(i.s&&a.s&&(N?!D||N[0]!=D[0]:D)?N&&0==N[0]||!D?0*O:O/0:NaN);for(g=new e(O),b=g.c=[],p=i.e-a.e,O=s+p+1,u||(u=k,p=o(i.e/B)-o(a.e/B),O=O/B|0),l=0;D[l]==(N[l]||0);l++);if(D[l]>(N[l]||0)&&p--,0>O)b.push(1),h=!0;else{for(A=N.length,F=D.length,l=0,O+=2,d=v(u/(D[0]+1)),d>1&&(D=t(D,d,u),N=t(N,d,u),F=D.length,A=N.length),S=F,_=N.slice(0,F),w=_.length;F>w;_[w++]=0);I=D.slice(),I.unshift(0),C=D[0],D[1]>=u/2&&C++;do{if(d=0,f=n(D,_,F,w),0>f){if(x=_[0],F!=w&&(x=x*u+(_[1]||0)),d=v(x/C),d>1)for(d>=u&&(d=u-1),m=t(D,d,u),y=m.length,w=_.length;1==n(m,_,y,w);)d--,r(m,y>F?I:D,y,u),y=m.length,f=1;else 0==d&&(f=d=1),m=D.slice(), +y=m.length;if(w>y&&m.unshift(0),r(_,m,w,u),w=_.length,-1==f)for(;n(D,_,F,w)<1;)d++,r(_,w>F?I:D,w,u),w=_.length}else 0===f&&(d++,_=[0]);b[l++]=d,_[0]?_[w++]=N[S]||0:(_=[N[S]],w=1)}while((S++=10;O/=10,l++);P(g,s+(g.e=l+p*B-1)+1,c,h)}else g.e=p,g.r=+h;return g}}(),m=function(){var t=/^(-?)0([xbo])/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,o=/^-?(Infinity|NaN)$/,i=/^\s*\+|^\s+|\s+$/g;return function(a,s,c,u){var f,p=c?s:s.replace(i,"");if(o.test(p))a.s=isNaN(p)?null:0>p?-1:1;else{if(!c&&(p=p.replace(t,function(t,e,n){return f="x"==(n=n.toLowerCase())?16:"b"==n?2:8,u&&u!=f?t:e}),u&&(f=u,p=p.replace(n,"$1").replace(r,"0.$1")),s!=p))return new e(p,f);W&&D(E,"not a"+(u?" base "+u:"")+" number",s),a.s=null}a.c=a.e=null,E=0}}(),R.absoluteValue=R.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},R.ceil=function(){return P(new e(this),this.e+1,2)},R.comparedTo=R.cmp=function(t,n){return E=1,a(this,new e(t,n))},R.decimalPlaces=R.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-o(this.e/B))*B,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},R.dividedBy=R.div=function(t,n){return E=3,T(this,new e(t,n),H,j)},R.dividedToIntegerBy=R.divToInt=function(t,n){return E=4,T(this,new e(t,n),0,1)},R.equals=R.eq=function(t,n){return E=5,0===a(this,new e(t,n))},R.floor=function(){return P(new e(this),this.e+1,3)},R.greaterThan=R.gt=function(t,n){return E=6,a(this,new e(t,n))>0},R.greaterThanOrEqualTo=R.gte=function(t,n){return E=7,1===(n=a(this,new e(t,n)))||0===n},R.isFinite=function(){return!!this.c},R.isInteger=R.isInt=function(){return!!this.c&&o(this.e/B)>this.c.length-2},R.isNaN=function(){return!this.s},R.isNegative=R.isNeg=function(){return this.s<0},R.isZero=function(){return!!this.c&&0==this.c[0]},R.lessThan=R.lt=function(t,n){return E=8,a(this,new e(t,n))<0},R.lessThanOrEqualTo=R.lte=function(t,n){return E=9,-1===(n=a(this,new e(t,n)))||0===n},R.minus=R.sub=function(t,n){var r,i,a,s,c=this,u=c.s;if(E=10,t=new e(t,n),n=t.s,!u||!n)return new e(NaN);if(u!=n)return t.s=-n,c.plus(t);var f=c.e/B,p=t.e/B,l=c.c,h=t.c;if(!f||!p){if(!l||!h)return l?(t.s=-n,t):new e(h?c:NaN);if(!l[0]||!h[0])return h[0]?(t.s=-n,t):new e(l[0]?c:3==j?-0:0)}if(f=o(f),p=o(p),l=l.slice(),u=f-p){for((s=0>u)?(u=-u,a=l):(p=f,a=h),a.reverse(),n=u;n--;a.push(0));a.reverse()}else for(i=(s=(u=l.length)<(n=h.length))?u:n,u=n=0;i>n;n++)if(l[n]!=h[n]){s=l[n]0)for(;n--;l[r++]=0);for(n=k-1;i>u;){if(l[--i]0?(c=s,r=f):(a=-a,r=u),r.reverse();a--;r.push(0));r.reverse()}for(a=u.length,n=f.length,0>a-n&&(r=f,f=u,u=r,n=a),a=0;n;)a=(u[--n]=u[n]+f[n]+a)/k|0,u[n]%=k;return a&&(u.unshift(a),++c),N(t,u,c)},R.precision=R.sd=function(t){var e,n,r=this,o=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&(W&&D(13,"argument"+b,t),t!=!!t&&(t=null)),!o)return null;if(n=o.length-1,e=n*B+1,n=o[n]){for(;n%10==0;n/=10,e--);for(n=o[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},R.round=function(t,n){var r=new e(this);return(null==t||J(t,0,F,15))&&P(r,~~t+this.e+1,null!=n&&J(n,0,8,15,_)?0|n:j),r},R.shift=function(t){var n=this;return J(t,-S,S,16,"argument")?n.times("1e"+l(t)):new e(n.c&&n.c[0]&&(-S>t||t>S)?n.s*(0>t?0:1/0):n)},R.squareRoot=R.sqrt=function(){var t,n,r,a,s,c=this,u=c.c,f=c.s,p=c.e,l=H+4,h=new e("0.5");if(1!==f||!u||!u[0])return new e(!f||0>f&&(!u||u[0])?NaN:u?c:1/0);if(f=Math.sqrt(+c),0==f||f==1/0?(n=i(u),(n.length+p)%2==0&&(n+="0"),f=Math.sqrt(n),p=o((p+1)/2)-(0>p||p%2),f==1/0?n="1e"+p:(n=f.toExponential(),n=n.slice(0,n.indexOf("e")+1)+p),r=new e(n)):r=new e(f+""),r.c[0])for(p=r.e,f=p+l,3>f&&(f=0);;)if(s=r,r=h.times(s.plus(T(c,s,l,1))),i(s.c).slice(0,f)===(n=i(r.c)).slice(0,f)){if(r.ef&&(y=_,_=w,w=y,a=f,f=h,h=a),a=f+h,y=[];a--;y.push(0));for(g=k,v=C,a=h;--a>=0;){for(r=0,d=w[a]%v,m=w[a]/v|0,c=f,s=a+c;s>a;)p=_[--c]%v,l=_[c]/v|0,u=m*p+l*d,p=d*p+u%v*v+y[s]+r,r=(p/g|0)+(u/v|0)+m*l,y[s--]=p%g;y[s]=r}return r?++i:y.shift(),N(t,y,i)},R.toDigits=function(t,n){var r=new e(this);return t=null!=t&&J(t,1,F,18,"precision")?0|t:null,n=null!=n&&J(n,0,8,18,_)?0|n:j,t?P(r,t,n):r},R.toExponential=function(t,e){return h(this,null!=t&&J(t,0,F,19)?~~t+1:null,e,19)},R.toFixed=function(t,e){return h(this,null!=t&&J(t,0,F,20)?~~t+this.e+1:null,e,20)},R.toFormat=function(t,e){var n=h(this,null!=t&&J(t,0,F,21)?~~t+this.e+1:null,e,21);if(this.c){var r,o=n.split("."),i=+K.groupSize,a=+K.secondaryGroupSize,s=K.groupSeparator,c=o[0],u=o[1],f=this.s<0,p=f?c.slice(1):c,l=p.length;if(a&&(r=i,i=a,a=r,l-=r),i>0&&l>0){for(r=l%i||i,c=p.substr(0,r);l>r;r+=i)c+=s+p.substr(r,i);a>0&&(c+=s+p.slice(r)),f&&(c="-"+c)}n=u?c+K.decimalSeparator+((a=+K.fractionGroupSize)?u.replace(new RegExp("\\d{"+a+"}\\B","g"),"$&"+K.fractionGroupSeparator):u):c}return n},R.toFraction=function(t){var n,r,o,a,s,c,u,f,p,l=W,h=this,d=h.c,m=new e(M),y=r=new e(M),g=u=new e(M);if(null!=t&&(W=!1,c=new e(t),W=l,(!(l=c.isInt())||c.lt(M))&&(W&&D(22,"max denominator "+(l?"out of range":"not an integer"),t),t=!l&&c.c&&P(c,c.e+1,1).gte(M)?c:null)),!d)return h.toString();for(p=i(d),a=m.e=p.length-h.e-1,m.c[0]=A[(s=a%B)<0?B+s:s],t=!t||c.cmp(m)>0?a>0?m:y:c,s=U,U=1/0,c=new e(p),u.c[0]=0;f=T(c,m,0,1),o=r.plus(f.times(g)),1!=o.cmp(t);)r=g,g=o,y=u.plus(f.times(o=y)),u=o,m=c.minus(f.times(o=m)),c=o;return o=T(t.minus(r),g,0,1),u=u.plus(o.times(y)),r=r.plus(o.times(g)),u.s=y.s=h.s,a*=2,n=T(y,g,a,j).minus(h).abs().cmp(T(u,r,a,j).minus(h).abs())<1?[y.toString(),g.toString()]:[u.toString(),r.toString()],U=s,n},R.toNumber=function(){var t=this;return+t||(t.s?0*t.s:NaN)},R.toPower=R.pow=function(t){var n,r,o=v(0>t?-t:+t),i=this;if(!J(t,-S,S,23,"exponent")&&(!isFinite(t)||o>S&&(t/=0)||parseFloat(t)!=t&&!(t=NaN)))return new e(Math.pow(+i,t));for(n=$?g($/B+2):0,r=new e(M);;){if(o%2){if(r=r.times(i),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(o=v(o/2),!o)break;i=i.times(i),n&&i.c&&i.c.length>n&&(i.c.length=n)}return 0>t&&(r=M.div(r)),n?P(r,$,j):r},R.toPrecision=function(t,e){return h(this,null!=t&&J(t,1,F,24,"precision")?0|t:null,e,24)},R.toString=function(t){var e,r=this,o=r.s,a=r.e;return null===a?o?(e="Infinity",0>o&&(e="-"+e)):e="NaN":(e=i(r.c),e=null!=t&&J(t,2,64,25,"base")?n(p(e,a),0|t,10,o):q>=a||a>=L?f(e,a):p(e,a),0>o&&r.c[0]&&(e="-"+e)),e},R.truncated=R.trunc=function(){return P(new e(this),this.e+1,1)},R.valueOf=R.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function o(t){var e=0|t;return t>0||t===e?e:e-1}function i(t){for(var e,n,r=1,o=t.length,i=t[0]+"";o>r;){for(e=t[r++]+"",n=B-e.length;n--;e="0"+e);i+=e}for(o=i.length;48===i.charCodeAt(--o););return i.slice(0,o+1||1)}function a(t,e){var n,r,o=t.c,i=e.c,a=t.s,s=e.s,c=t.e,u=e.e;if(!a||!s)return null;if(n=o&&!o[0],r=i&&!i[0],n||r)return n?r?0:-s:a;if(a!=s)return a;if(n=0>a,r=c==u,!o||!i)return r?0:!o^n?1:-1;if(!r)return c>u^n?1:-1;for(s=(c=o.length)<(u=i.length)?c:u,a=0;s>a;a++)if(o[a]!=i[a])return o[a]>i[a]^n?1:-1;return c==u?0:c>u^n?1:-1}function s(t,e,n){return(t=l(t))>=e&&n>=t}function c(t){return"[object Array]"==Object.prototype.toString.call(t)}function u(t,e,n){for(var r,o,i=[0],a=0,s=t.length;s>a;){for(o=i.length;o--;i[o]*=e);for(i[r=0]+=x.indexOf(t.charAt(a++));rn-1&&(null==i[r+1]&&(i[r+1]=0),i[r+1]+=i[r]/n|0,i[r]%=n)}return i.reverse()}function f(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function p(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function l(t){return t=parseFloat(t),0>t?g(t):v(t)}var h,d,m,y=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,g=Math.ceil,v=Math.floor,b=" not a boolean or binary digit",_="rounding mode",w="number type has more than 15 significant digits",x="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",k=1e14,B=14,S=9007199254740991,A=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],C=1e7,F=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!d)try{d=t("crypto")}catch(I){}}else n.BigNumber=h}(this)},{crypto:49}],web3:[function(t,e,n){var r=t("./lib/web3");"undefined"!=typeof window&&"undefined"==typeof window.Web3&&(window.Web3=r),e.exports=r},{"./lib/web3":22}]},{},["web3"]); \ No newline at end of file diff --git a/resources/web3_init.js b/resources/web3_init.js new file mode 100644 index 0000000000..11cd7342e9 --- /dev/null +++ b/resources/web3_init.js @@ -0,0 +1,65 @@ +var callbackId = 0; +var callbacks = {}; + +function httpCallback(id, data) { + var result = data; + var error = null; + + console.log(data); + + try { + result = JSON.parse(data); + } catch(e) { + error = {message: "InvalidResponse"}; + } + + + callbacks[id](error, result); +} + +var StatusHttpProvider = function (host, timeout) { + this.host = host || 'http://localhost:8545'; + this.timeout = timeout || 0; +}; + +StatusHttpProvider.prototype.send = function (payload) { + throw new Error('You tried to send "'+ payload.method +'" synchronously. Synchronous requests are not supported, sorry.'); +}; + +StatusHttpProvider.prototype.sendAsync = function (payload, callback) { + + var messageId = callbackId++; + callbacks[messageId] = callback; + if(typeof StatusBridge == "undefined") { + var data = { + payload: JSON.stringify(payload), + callbackId: JSON.stringify(messageId) + }; + + webkit.messageHandlers.sendRequest.postMessage(JSON.stringify(data)); + } else { + StatusBridge.sendRequest(JSON.stringify(messageId), JSON.stringify(payload)); + } +}; + +/** + * Synchronously tries to make Http request + * + * @method isConnected + * @return {Boolean} returns true if request haven't failed. Otherwise false + */ +StatusHttpProvider.prototype.isConnected = function() { + try { + this.sendAsync({ + id: 9999999999, + jsonrpc: '2.0', + method: 'net_listening', + params: [] + }, function(){}); + return true; + } catch(e) { + return false; + } +}; + +web3 = new Web3(new StatusHttpProvider("http://localhost:8545")); diff --git a/resources/webview.js b/resources/webview.js new file mode 100644 index 0000000000..b5c7510b74 --- /dev/null +++ b/resources/webview.js @@ -0,0 +1,26 @@ +(function () { + window.statusAPI = { + dispatch: function (event, options) { + console.log("statusAPI.dispatch: " + JSON.stringify(options)); + if (options.callback) { + console.log(options.callback); + statusAPI.callbacks[event] = options.callback; + } + var json = JSON.stringify({ + event: event, + options: options + }); + console.log("sending from webview: " + json); + WebViewBridge.send(json); + }, + callbacks: {} + }; + + WebViewBridge.onMessage = function (messageString) { + console.log("received from react-native: " + messageString); + var message = JSON.parse(messageString); + if (statusAPI.callbacks[message.event]) { + statusAPI.callbacks[message.event](message.params); + } + }; +}()); diff --git a/run-osx.sh b/run-osx.sh new file mode 100755 index 0000000000..4c76583232 --- /dev/null +++ b/run-osx.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +[ `uname -s` != "Darwin" ] && return + +function tab () { + local cmd="" + local cdto="$PWD" + local args="$@" + + if [ -d "$1" ]; then + cdto=`cd "$1"; pwd` + args="${@:2}" + fi + + if [ -n "$args" ]; then + cmd="; $args" + fi + + osascript &>/dev/null <clj]] + [status-im.utils.identicon :refer [identicon]] + [status-im.utils.random :as random] + [status-im.i18n :refer [label]] + [status-im.constants :refer [content-type-command-request]] + status-im.accounts.login.handlers + status-im.accounts.recover.handlers + [clojure.string :as str] + [status-im.utils.datetime :as time] + [status-im.utils.handlers :as u :refer [get-hashtags]] + [status-im.accounts.statuses :as statuses] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.constants :refer [console-chat-id]] + [status-im.utils.scheduler :as s] + [status-im.protocol.message-cache :as cache] + [status-im.navigation.handlers :as nav])) + + +(defn save-account + [{:keys [network]} + [_ account]] + (accounts-store/save (assoc account :network network) true)) + +(register-handler + :add-account + ((after save-account) + (fn [{:keys [network] :as db} [_ {:keys [address] :as account}]] + (let [account' (assoc account :network network)] + (update db :accounts assoc address account'))))) + +(defn account-created [result password] + (let [data (json->clj result) + public-key (:pubkey data) + address (:address data) + mnemonic (:mnemonic data) + {:keys [public private]} (protocol/new-keypair!) + account {:public-key public-key + :address address + :name (generate-gfy) + :status (rand-nth statuses/data) + :signed-up? true + :updates-public-key public + :updates-private-key private + :photo-path (identicon public-key)}] + (log/debug "account-created") + (when-not (str/blank? public-key) + (dispatch [:show-mnemonic mnemonic]) + (dispatch [:add-account account]) + (dispatch [:login-account address password true])))) + +(register-handler :create-account + (u/side-effect! + (fn [_ [_ password]] + (s/execute-later #(dispatch [:account-generation-message]) 400) + (status/create-account + password + #(account-created % password))))) + +(defn save-account! + [{:keys [current-account-id accounts network]} _] + (let [{acc-network :network :as account} + (get accounts current-account-id) + + account' (assoc account :network (or acc-network network))] + (accounts-store/save account' true))) + +(defn send-account-update + [{:keys [current-account-id current-public-key web3 accounts]} _] + (let [{:keys [name photo-path status]} (get accounts current-account-id) + {:keys [updates-public-key updates-private-key]} (accounts current-account-id)] + (protocol/broadcast-profile! + {:web3 web3 + :message {:from current-public-key + :message-id (random/id) + :keypair {:public updates-public-key + :private updates-private-key} + :payload {:profile {:name name + :status status + :profile-image photo-path}}}}))) + +(register-handler + :check-status-change + (u/side-effect! + (fn [{:keys [current-account-id accounts]} [_ status]] + (let [{old-status :status :as account} (get accounts current-account-id) + status-updated? (and (not= status nil) + (not= status old-status))] + (when status-updated? + (let [hashtags (get-hashtags status)] + (when-not (empty? hashtags) + (dispatch [:broadcast-status status hashtags])))))))) + +(register-handler + :account-update + (-> (fn [{:keys [current-account-id accounts] :as db} [_ data]] + (let [data (assoc data :last-updated (time/now-ms)) + account (merge (get accounts current-account-id) data)] + (assoc-in db [:accounts current-account-id] account))) + ((after save-account!)) + ((after send-account-update)))) + +(register-handler + :send-account-update-if-needed + (u/side-effect! + (fn [{:keys [current-account-id accounts]} _] + (let [{:keys [last-updated]} (get accounts current-account-id) + now (time/now-ms) + needs-update? (> (- now last-updated) time/week)] + (log/info "Need to send account-update: " needs-update?) + (when needs-update? + (dispatch [:account-update])))))) + +(defn set-current-account + [{:keys [accounts] :as db} [_ address]] + (let [key (:public-key (accounts address))] + (assoc db :current-account-id address + :current-public-key key))) + +(register-handler :set-current-account set-current-account) + +(defn load-accounts! [db _] + (let [accounts (->> (accounts-store/get-all) + (map (fn [{:keys [address] :as account}] + [address account])) + (into {}))] + (assoc db :accounts accounts + :view-id (if (empty? accounts) + :chat + :accounts)))) + +(register-handler :load-accounts load-accounts!) + +(defn console-create-account [db _] + (let [message-id (random/id)] + (dispatch [:received-message + {:message-id message-id + :content {:command (name :keypair) + :content (label :t/keypair-generated)} + :content-type content-type-command-request + :outgoing false + :from console-chat-id + :to "me"}]) + db)) + +(register-handler :console-create-account console-create-account) + +(register-handler + :load-processed-messages + (u/side-effect! + (fn [_] + (let [now (time/now-ms) + messages (processed-messages/get-filtered (str "ttl > " now))] + (cache/init! messages) + (processed-messages/delete (str "ttl <=" now)))))) + +(defmethod nav/preload-data! :qr-code-view + [{:keys [current-account-id] :as db} [_ _ {:keys [contact qr-source amount?]}]] + (assoc db :qr-modal {:contact (or contact + (get-in db [:accounts current-account-id])) + :qr-source qr-source + :amount? amount?})) diff --git a/src/status_im/accounts/login/handlers.cljs b/src/status_im/accounts/login/handlers.cljs new file mode 100644 index 0000000000..581bbe22c0 --- /dev/null +++ b/src/status_im/accounts/login/handlers.cljs @@ -0,0 +1,68 @@ +(ns status-im.accounts.login.handlers + (:require [re-frame.core :refer [after dispatch]] + [status-im.utils.handlers :refer [register-handler] :as u] + [taoensso.timbre :as log] + [status-im.utils.types :refer [json->clj]] + [status-im.data-store.core :as data-store] + [status-im.components.status :as status] + [status-im.constants :refer [console-chat-id]] + [status-im.navigation.handlers :as nav])) + +(defmethod nav/preload-data! :login + [db] + (update db :login dissoc :error :password)) + +(defn set-login-from-qr + [{:keys [login] :as db} [_ _ login-info]] + (assoc db :login (merge login login-info))) + +(register-handler :set-login-from-qr set-login-from-qr) + +(defn initialize-account + [address new-account?] + (dispatch [:set :login {}]) + (dispatch [:set-current-account address]) + (dispatch [:initialize-account address]) + (if new-account? + (do + (dispatch [:navigate-to-clean :chat-list]) + (dispatch [:navigate-to :chat console-chat-id])) + (do + (dispatch [:navigate-to-clean :chat-list]) + (dispatch [:navigate-to :chat-list])))) + +(register-handler + :change-account + (u/side-effect! + (fn [db [_ address new-account? callback]] + (data-store/change-account address new-account? + #(callback % address new-account?))))) + +(defn on-account-changed + [error address new-account?] + (if (nil? error) + (initialize-account address new-account?) + (log/debug "Error changing acount: " error))) + +(defn logged-in + [db address] + (let [is-login-screen? (= (:view-id db) :login) + new-account? (not is-login-screen?)] + (log/debug "Logged in: " (:view-id db) is-login-screen? new-account?) + (dispatch [:change-account address new-account? on-account-changed]))) + +(register-handler + :login-account + (after + (fn [db [_ address password]] + (status/login address password + (fn [result] + (let [data (json->clj result) + error (:error data) + success (zero? (count error))] + (log/debug "Logged in account: ") + (if success + (logged-in db address) + (dispatch [:set-in [:login :error] error]))))))) + (fn [db [_ _ _ account-creation?]] + (assoc db :account-creation? account-creation?))) diff --git a/src/status_im/accounts/login/screen.cljs b/src/status_im/accounts/login/screen.cljs new file mode 100644 index 0000000000..34fe5c178a --- /dev/null +++ b/src/status_im/accounts/login/screen.cljs @@ -0,0 +1,83 @@ +(ns status-im.accounts.login.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text + image + linear-gradient + touchable-highlight + get-dimensions]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-title-container + toolbar-title-text]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.styles :refer [color-purple + color-white + icon-search + icon-back + icon-qr + button-input]] + [status-im.i18n :refer [label]] + [status-im.accounts.login.styles :as st])) + +(defn toolbar-title [] + [view toolbar-title-container + [text {:style (merge toolbar-title-text {:color color-white}) + :font :medium} + (label :t/login)]]) + +(defview address-input [address] + [view + [text-field + {:value address + :editable false + :label (label :t/address) + :label-color "#ffffff80" + :line-color :white + :input-style st/input-style + :wrapper-style (merge button-input st/address-input-wrapper) + :on-change-text #(dispatch [:set-in [:login :address] %])}]]) + +(defview password-input [error] + [view + [text-field + {:editable true + :error (when (pos? (count error)) (label :t/wrong-password)) + :error-color :white + :label (label :t/password) + :secure-text-entry true + :label-color "#ffffff80" + :line-color :white + :input-style st/input-style + :on-change-text #(do + (dispatch [:set-in [:login :password] %]) + (dispatch [:set-in [:login :error] ""]))}]]) + +(defview login [] + [{:keys [address password error]} [:get :login] + keyboard-height [:get :keyboard-height]] + [view (st/screen-container (- (:height (get-dimensions "window")) + keyboard-height)) + [linear-gradient {:colors ["rgba(182, 116, 241, 1)" "rgba(107, 147, 231, 1)" "rgba(43, 171, 238, 1)"] + :start [0, 0] + :end [0.5, 1] + :locations [0, 0.8, 1] + :style st/gradient-background}] + [status-bar {:type :transparent}] + [toolbar {:background-color :transparent + :nav-action (act/back-white #(dispatch [:navigate-back])) + :custom-content [toolbar-title] + :actions [{:image {:style icon-search} + :handler #()}]}] + [view st/form-container + [view st/form-container-inner + [address-input (or address "")] + [password-input error]]] + [view st/bottom-actions-container + [view st/connect-button-container + [touchable-highlight {:on-press #(dispatch [:login-account address password])} + [view st/connect-button + [text {:style st/connect-button-text} + (label :t/connect)]]]]]]) diff --git a/src/status_im/accounts/login/styles.cljs b/src/status_im/accounts/login/styles.cljs new file mode 100644 index 0000000000..d3ed4c7afa --- /dev/null +++ b/src/status_im/accounts/login/styles.cljs @@ -0,0 +1,54 @@ +(ns status-im.accounts.login.styles + (:require [status-im.components.styles :refer [color-white]])) + + +(defn screen-container [height] + {:height height}) + +(def gradient-background + {:position :absolute + :top 0 + :right 0 + :bottom 0 + :left 0}) + +(def form-container + {:flex 1 + :flex-direction "row" + :align-items "center" + :justifyContent "center"}) + +(def form-container-inner + {:flex 1 + :padding-bottom 100 + :margin-left 16}) + +(def bottom-actions-container + {:position :absolute + :left 0 + :right 0 + :bottom 0}) + +(def connect-button-container + {:flex 1}) + +(def connect-button + {:backgroundColor color-white + :flex 1 + :alignItems :center + :paddingVertical 16 + :paddingHorizontal 28}) + +(def connect-button-text + {:color "#7099e6" + :fontSize 16}) + +(def input-style + {:color :white + :font-size 12}) + +(def scan-label + {:color :white}) + +(def address-input-wrapper + {}) \ No newline at end of file diff --git a/src/status_im/accounts/recover/handlers.cljs b/src/status_im/accounts/recover/handlers.cljs new file mode 100644 index 0000000000..c98f70c8d0 --- /dev/null +++ b/src/status_im/accounts/recover/handlers.cljs @@ -0,0 +1,40 @@ +(ns status-im.accounts.recover.handlers + (:require [re-frame.core :refer [register-handler after dispatch dispatch-sync]] + [status-im.components.status :as status] + [status-im.utils.types :refer [json->clj]] + [status-im.utils.identicon :refer [identicon]] + [taoensso.timbre :as log] + [clojure.string :as str] + [status-im.utils.handlers :as u] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.protocol.core :as protocol])) + +(defn account-recovered [result] + (let [_ (log/debug result) + data (json->clj result) + public-key (:pubkey data) + address (:address data) + {:keys [public private]} (protocol/new-keypair!) + account {:public-key public-key + :address address + :name (generate-gfy) + :photo-path (identicon public-key) + :updates-public-key public + :updates-private-key private + :signed-up? true}] + (log/debug "account-recovered") + (when (not (str/blank? public-key)) + (do + (dispatch [:set-in [:recover :passphrase] ""]) + (dispatch [:set-in [:recover :password] ""]) + (dispatch [:add-account account]) + (dispatch [:navigate-back]))))) + +(defn recover-account + [_ [_ passphrase password]] + (status/recover-account + passphrase + password + (fn [result] (account-recovered result)))) + +(register-handler :recover-account (u/side-effect! recover-account)) diff --git a/src/status_im/accounts/recover/screen.cljs b/src/status_im/accounts/recover/screen.cljs new file mode 100644 index 0000000000..47986bd191 --- /dev/null +++ b/src/status_im/accounts/recover/screen.cljs @@ -0,0 +1,100 @@ +(ns status-im.accounts.recover.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text + image + linear-gradient + touchable-highlight]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-gradient + toolbar-title-container + toolbar-title-text]] + [status-im.components.styles :refer [color-purple + color-white + button-input]] + [status-im.components.react :refer [linear-gradient]] + [status-im.i18n :refer [label]] + [status-im.accounts.recover.styles :as st] + [status-im.accounts.recover.validations :as v] + [cljs.spec :as s] + [clojure.string :as str] + [taoensso.timbre :as log])) + +(defn toolbar-title [] + [view toolbar-title-container + [text {:style toolbar-title-text + :font :medium} + (label :t/recover-from-passphrase)]]) + +(defview passphrase-input [passphrase] + [error [:get-in [:recover :passphrase-error]]] + (let [error (if (str/blank? passphrase) "" error) + error (if (s/valid? ::v/passphrase passphrase) + error + (label :t/enter-valid-passphrase))] + [view + [text-field + {:value passphrase + :error error + :error-color "#7099e6" + :label (label :t/passphrase) + :label-color "#838c93de" + :line-color "#0000001f" + :input-style st/input-style + :wrapper-style (merge button-input st/address-input-wrapper) + :on-change-text #(dispatch [:set-in [:recover :passphrase] %])}]])) + +(defview password-input [password] + [error [:get-in [:recover :password-error]]] + (let [error (if (str/blank? password) "" error) + error (if (s/valid? ::v/password password) + error + (label :t/enter-valid-password))] + [view + [text-field + {:value password + :error error + :error-color "#7099e6" + :label (label :t/password) + :label-color "#838c93de" + :line-color "#0000001f" + :input-style st/input-style + :on-change-text #(dispatch [:set-in [:recover :password] %])}]])) + +(defview recover [] + [{:keys [passphrase password passphrase-error password-error]} [:get :recover]] + (let [valid-form? (and + (s/valid? ::v/passphrase passphrase) + (s/valid? ::v/password password)) + gradient-colors ["rgba(24, 52, 76, 0.165)" + "rgba(24, 52, 76, 0.085)" + "rgba(24, 52, 76, 0)"] + _ (log/debug passphrase " - " password)] + [view st/screen-container + [status-bar {:type :transparent}] + [toolbar {:background-color :transparent + :nav-action (act/back #(dispatch [:navigate-back])) + :custom-content [toolbar-title]}] + [linear-gradient {:locations [0 0.6 1] + :colors gradient-colors + :style toolbar-gradient}] + [view st/recover-explain-container + [text {:style st/recover-explain-text + :font :medium} + (label :t/recover-explain)]] + [view st/form-container + [view st/form-container-inner + [passphrase-input (or passphrase "")] + [password-input (or password "")]]] + [view st/bottom-actions-container + [view st/recover-button-container + [touchable-highlight + {:on-press #(when valid-form? + (dispatch [:recover-account passphrase password]))} + [view (st/recover-button valid-form?) + [text {:style st/recover-button-text} + (label :t/recover)]]]]]])) diff --git a/src/status_im/accounts/recover/styles.cljs b/src/status_im/accounts/recover/styles.cljs new file mode 100644 index 0000000000..773dded88b --- /dev/null +++ b/src/status_im/accounts/recover/styles.cljs @@ -0,0 +1,67 @@ +(ns status-im.accounts.recover.styles + (:require [status-im.components.styles :refer [color-white]])) + + +(def screen-container + {:flex 1 + :color :white + :background-color color-white}) + +(def gradient-background + {:position :absolute + :top 0 + :right 0 + :bottom 0 + :left 0}) + +(def recover-explain-container + {:padding-horizontal 35 + :padding-top 20 + :justify-content :center}) + +(def recover-explain-text + {:color "#838c93de" + :font-size 16 + :line-height 20 + :text-align :center}) + +(def form-container + {:flex 1 + :flex-direction "row" + :justifyContent "center"}) + +(def form-container-inner + {:flex 1 + :padding-bottom 100 + :margin-left 16}) + +(def bottom-actions-container + {:position :absolute + :left 0 + :right 0 + :bottom 0}) + +(def recover-button-container + {:flex 1}) + +(defn recover-button [valid-form?] + {:backgroundColor (if valid-form? "#7099e6" :gray) + :color :white + :flex 1 + :alignItems :center + :paddingVertical 16 + :paddingHorizontal 28}) + +(def recover-button-text + {:color color-white + :fontSize 16}) + +(def input-style + {:color "#323232" + :font-size 12}) + +(def scan-label + {:color :white}) + +(def address-input-wrapper + {}) \ No newline at end of file diff --git a/src/status_im/accounts/recover/validations.cljs b/src/status_im/accounts/recover/validations.cljs new file mode 100644 index 0000000000..c2f8ee386f --- /dev/null +++ b/src/status_im/accounts/recover/validations.cljs @@ -0,0 +1,6 @@ +(ns status-im.accounts.recover.validations + (:require [cljs.spec :as s])) + +(s/def ::not-empty-string (s/and string? not-empty)) +(s/def ::passphrase ::not-empty-string) +(s/def ::password ::not-empty-string) diff --git a/src/status_im/accounts/screen.cljs b/src/status_im/accounts/screen.cljs new file mode 100644 index 0000000000..5600b9c58e --- /dev/null +++ b/src/status_im/accounts/screen.cljs @@ -0,0 +1,92 @@ +(ns status-im.accounts.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + scroll-view + text + list-view + list-item + image + linear-gradient + touchable-highlight]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.styles :refer [color-purple + color-white + icon-search + icon-back + icon-qr + icon-plus + white-form-text-input]] + [status-im.components.toolbar.styles :refer [toolbar-title-container + toolbar-title-text]] + [status-im.utils.listview :as lw] + [status-im.accounts.views.account :refer [account-view]] + [status-im.i18n :refer [label]] + [status-im.accounts.styles :as st] + [status-im.constants :refer [console-chat-id]])) + +(defn toolbar-title [] + (let [style (merge toolbar-title-text {:color color-white})] + [view toolbar-title-container + [text {:style style + :font :medium} + (label :t/switch-users)]])) + +(defn render-row [row _ _] + (list-item [account-view row])) + +(defn render-separator [_ row-id _] + (list-item [view {:style st/row-separator + :key row-id}])) + +(defn create-account [event] + (dispatch-sync [:reset-app]) + ; add accounts screen to history ( maybe there is a better way ? ) + (dispatch [:navigate-to-clean :accounts]) + (dispatch [:navigate-to :chat console-chat-id])) + +(defview accounts [] + [accounts [:get :accounts] + stack [:get :navigation-stack]] + (let [accounts (vals accounts) + show-back? (> (count stack) 1)] + [view st/screen-container + [linear-gradient {:colors ["rgba(182, 116, 241, 1)" + "rgba(107, 147, 231, 1)" + "rgba(43, 171, 238, 1)"] + :start [0, 0] + :end [0.5, 1] + :locations [0, 0.8, 1] + :style st/gradient-background} + [status-bar {:type :transparent}] + [toolbar {:background-color :transparent + :nav-action (if show-back? + (act/back-white #(dispatch [:navigate-back])) + act/nothing) + :custom-content [toolbar-title] + :actions [{:image {:style icon-search} + :handler #()}]}]] + [list-view {:dataSource (lw/to-datasource accounts) + :enableEmptySections true + :renderRow render-row + :bounces false + :style st/account-list + :contentContainerStyle (st/account-list-content (count accounts))}] + [view st/bottom-actions-container + [view st/recover-button-container + [touchable-highlight + {:on-press #(dispatch [:navigate-to :recover])} + [view st/recover-button + [text {:style st/recover-button-text} + (label :t/recover-access)]]]] + [view st/add-account-button-container + [touchable-highlight {:on-press create-account + :accessibility-label :create-account} + [view st/add-account-button + [image {:source {:uri :icon_add_white} + :style st/icon-plus}] + [text {:style st/add-account-text + :font :default} + (label :t/add-account)]]]]]])) diff --git a/src/status_im/accounts/statuses.cljs b/src/status_im/accounts/statuses.cljs new file mode 100644 index 0000000000..a9d5841404 --- /dev/null +++ b/src/status_im/accounts/statuses.cljs @@ -0,0 +1,89 @@ +(ns status-im.accounts.statuses) + +(def data + ["be the hero of your own journey" + "nature is pleased with simplicity" + "accept yourself and keep moving forward" + "only wise men look for new wisdom" + "do it or not, but do" + "any human-defined truth is ultimately relative" + "success is not an activity but a process" + "the future is forever a projection of the present" + "open source can propagate to fill all the corners of the universe" + "wisdom is unlearning what you have learnt" + "people who are afraid of the truth never find it" + "a single thought can shift your entire world" + "the man who is not permitted to own is owned" + "the wisest are the most annoyed at the loss of time" + "freedom flourishes upon the bedrock of ethics and integrity" + "speaking less and doing more says more" + "the future belongs to those who never forget to think" + "begin, be bold, and venture to be wise" + "wisdom is the reward for surviving our own stupidity" + "history is the insight to foresight" + "being surrounded by great people can get you really really far" + "cheapen words and they'll cheapen you" + "there is always exception to every rule" + "the future is certain. it is just not known" + "the future depends on what we do in the present" + "waiting leads to wondering" + "imagination is an endless possibility" + "if we don't make our own future, it will be made for us" + "i would rather risk failure than achieve it without risk" + "life would be much easier if i had the source code" + "relax and recreate yourself!" + "your history is not your future" + "wise men speak only of what they know" + "make time to read" + "we learn from failure, not from success" + "science is a way humans try to tell themselves the truths" + "success is simply the wisdom born out of so called failures" + "little decisions shape big consequences" + "it's possible to go on, no matter how impossible it seems" + "the intelligent have plans; the wise have principles" + "embrace the future and keep your back up against the past" + "knowledge is beautiful; science is amazing" + "realize that everything connects to everything else" + "wonder is the seed of knowledge" + "i want something that is nothing like the past" + "the future belongs to those who innovate" + "for the newborn and wise, everything begins small" + "your future is only as bright as your mind is open" + "all too often, the rabbit hole is as deep as you have dug it" + "ego is recessive in wisdom" + "without courage, wisdom bears no fruit" + "explore, experiment and experience" + "we are star stuff harvesting sunlight" + "he who knows all the answers has not been asked all the questions" + "the future will be different from how they have told us" + "read. you can always talk with another reader" + "the best way to predict your future is to create it" + "our thoughts create our future" + "today is the beginning of new history" + "reading & thinking breeds limitless progress" + "stuff your brain with knowledge" + "look for patterns, and then ask why those patterns exist" + "he who doesn't understand history is doomed to repeat it" + "asking why, is the root of a scientific mindset" + "thinking breeds inventions and innovations" + "every past used to be a future once upon a time" + "the future begins with hope" + "censorship exists to protect corruption" + "devote yourself to reading, learning and writing" + "the highest form of wisdom is often hidden in simplicity" + "today's science is tomorrow's technology" + "prediction is very difficult, especially about the future" + "wishes are memories coming from our future" + "fight for greater course than for greater loss" + "the life we're given is on a thread, so wear it well" + "a free thinker walks on shortcuts among wisdoms" + "free your mind from routine, keep your brain somewhere else" + "let discernment be your trustee, and mistakes your teacher" + "he who forgets the past is doomed to repeat it" + "everybody's gotta learn, nobody's born knowing" + "in a broader sense, systems thinking is a path to greater awareness" + "your choices are where your future begins" + "value your freedom or you will lose it" + "the future starts today, not tomorrow" + "our future starts in our decisions" + "there's no greater show on earth than observing human nature"]) \ No newline at end of file diff --git a/src/status_im/accounts/styles.cljs b/src/status_im/accounts/styles.cljs new file mode 100644 index 0000000000..e50cd0f9f9 --- /dev/null +++ b/src/status_im/accounts/styles.cljs @@ -0,0 +1,184 @@ +(ns status-im.accounts.styles + (:require [status-im.components.styles :refer [color-white]] + [status-im.components.react :as r])) + + +(def screen-container + {:flex 1 + :color :white}) + +(def gradient-background + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :padding-bottom 84}) + +(defn account-list-content [cnt] + (merge {:justifyContent :center} + ;; todo this will not work with landscape and looks bad + (when (< (* 69 (+ 2 cnt)) (:height (r/get-dimensions "window"))) + {:flex-grow 1}))) + +(def account-list + {:margin-top 75 + :margin-bottom 110}) + +(def row-separator + {:borderBottomWidth 1 + :borderBottomColor "#bababa"}) + +(def account-container + {:flex 1 + :flexDirection :row + :height 69 + :backgroundColor "rgba(255, 255, 255, 0.1)" + :alignItems :center + :justifyContent :center}) + +(def photo-container + {:flex 0.2 + :flexDirection :column + :alignItems :center + :justifyContent :center}) + +(def account-photo-container + {:flex 1 + :width 36 + :height 36 + :alignItems :center + :justifyContent :center}) + +(def qr-photo-container + (merge photo-container + {:margin-left 8 + :margin-right 4})) + +(defn qr-code-container [dimensions] + {:background-color "white" + :width (:width dimensions) + :align-items :center + :justify-content :center + :padding 40}) + +(def photo-image + {:width 36 + :height 36 + :border-radius 18}) + +(def name-container + {:flex 1 + :flexDirection :column}) + +(def name-text + {:color color-white + :fontSize 16}) + +(def address-text + {:color color-white + :fontSize 12}) + +(def online-container + {:flex 0.2 + :flexDirection :column + :alignItems :center + :justifyContent :center}) + +(def online-image-container + {:width 40 + :height 40 + :margin-right 4 + :align-items :center + :justify-content :center}) + +(def bottom-actions-container + {:position :absolute + :left 0 + :right 0 + :bottom 0}) + +(def add-account-button-container + {:flex 1 + :paddingVertical 16 + :paddingHorizontal 28 + :justifyContent :center + :alignItems :center}) + +(def add-account-button + {:flexDirection :row}) + +(def icon-plus + {:flexDirection :column + :paddingTop 2 + :width 20 + :height 20}) + +(def add-account-text + {:flexDirection :column + :color :white + :fontSize 16 + :marginLeft 8}) + +(def recover-button-container + {:flex 1}) + +(def recover-button + {:flex 1 + :alignItems :center + :paddingVertical 16 + :paddingHorizontal 28}) + +(def recover-button-text + {:flex 1 + :color color-white + :fontSize 16}) + +;wallet-qr-code.cljs + +(def wallet-qr-code + {:flex-grow 1 + :flex-direction :column}) + +(def account-toolbar + {:background-color "#2f3031"}) + +(def wallet-account-container + {:flex-grow 1 + :flexDirection :row + :height 69 + :alignItems :center + :justifyContent :center}) + +(def qr-code + {:background-color "#2f3031" + :flex-grow 1 + :align-items :center + :justify-content :center}) + +(def footer + {:background-color "#2f3031"}) + +(def wallet-info + {:align-items :center + :padding-bottom 20 + :padding-top 20}) + +(def wallet-name-text + {:color color-white + :padding-bottom 5}) + +(def wallet-address-text + {:color "#999999" + }) + +(def done-button + {:flex-grow 1 + :flex-direction :column + :align-items :center + :justify-content :center + :height 51 + :background-color "#7597e4"}) + +(def done-button-text + {:color color-white}) diff --git a/src/status_im/accounts/views/account.cljs b/src/status_im/accounts/views/account.cljs new file mode 100644 index 0000000000..dbe279ec89 --- /dev/null +++ b/src/status_im/accounts/views/account.cljs @@ -0,0 +1,40 @@ +(ns status-im.accounts.views.account + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [clojure.string :as s] + [status-im.resources :as res] + [status-im.components.react :refer [view + text + image + touchable-highlight]] + [re-frame.core :refer [dispatch subscribe]] + [status-im.components.styles :refer [icon-ok + icon-plus]] + [status-im.accounts.styles :as st])) + +(defn on-press [address] + (dispatch [:navigate-to-clean :accounts]) + (dispatch [:navigate-to :login address]) + (dispatch [:set-in [:login :address] address])) + +(defview account-view [{:keys [address photo-path name] :as account}] + [current-account [:get-current-account]] + [touchable-highlight + {:onPress #(on-press address)} + [view st/account-container + [view st/photo-container + [view st/account-photo-container + (if (not= address "0x0") + [image {:source {:uri (if (s/blank? photo-path) :avatar photo-path)} + :style st/photo-image}] + [image {:source {:uri :icon_plus} + :style icon-plus}])]] + [view st/name-container + [text {:style st/name-text + :numberOfLines 1} (or name address)] + (when (not= address "0x0") + [text {:style st/address-text + :numberOfLines 1} address])] + [view st/online-container + (when (= address (:address current-account)) + [image {:source {:uri :icon_ok} + :style icon-ok}])]]]) diff --git a/src/status_im/accounts/views/qr_code.cljs b/src/status_im/accounts/views/qr_code.cljs new file mode 100644 index 0000000000..c6bd278733 --- /dev/null +++ b/src/status_im/accounts/views/qr_code.cljs @@ -0,0 +1,61 @@ +(ns status-im.accounts.views.qr-code + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [status-im.components.react :refer [view + text + image + touchable-highlight + get-dimensions]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.styles :refer [icon-close]] + [status-im.components.qr-code :refer [qr-code]] + [re-frame.core :refer [dispatch subscribe]] + [status-im.accounts.styles :as st] + [status-im.i18n :refer [label]] + [clojure.string :as s] + [reagent.core :as r])) + +(defview qr-code-view [] + [{:keys [photo-path address name] :as contact} [:get-in [:qr-modal :contact]] + {:keys [qr-source amount? dimensions]} [:get :qr-modal] + {:keys [amount]} [:get :contacts-click-params]] + [view st/wallet-qr-code + [status-bar {:type :modal}] + + [view st/account-toolbar + [view st/wallet-account-container + [view st/qr-photo-container + [view st/account-photo-container + [image {:source {:uri (if (s/blank? photo-path) :avatar photo-path)} + :style st/photo-image}]]] + [view st/name-container + [text {:style st/name-text + :number-of-lines 1} name]] + [view st/online-container + [touchable-highlight {:onPress #(dispatch [:navigate-back])} + [view st/online-image-container + [image {:source {:uri :icon_close_white} + :style icon-close}]]]]]] + + [view {:style st/qr-code + :on-layout #(let [layout (.. % -nativeEvent -layout)] + (dispatch [:set-in [:qr-modal :dimensions] {:width (.-width layout) + :height (.-height layout)}]))} + (when (:width dimensions) + [view {:style (st/qr-code-container dimensions)} + [qr-code {:value (if amount? + (prn-str {:address (get contact qr-source) + :amount amount}) + (str "ethereum:" (get contact qr-source))) + :size (- (min (:width dimensions) + (:height dimensions)) + 80)}]])] + + [view st/footer + [view st/wallet-info + [text {:style st/wallet-name-text} (label :t/main-wallet)] + [text {:style st/wallet-address-text} address]] + + [touchable-highlight {:onPress #(dispatch [:navigate-back])} + [view st/done-button + [text {:style st/done-button-text} (label :t/done)]]]]]) + diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs new file mode 100644 index 0000000000..05d6af3f55 --- /dev/null +++ b/src/status_im/android/core.cljs @@ -0,0 +1,135 @@ +(ns status-im.android.core + (:require [reagent.core :as r :refer [atom]] + [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.handlers] + [status-im.subs] + [status-im.components.react :refer [app-registry + keyboard + orientation + back-android + view + modal + splash-screen]] + [status-im.components.main-tabs :refer [main-tabs]] + [status-im.contacts.search-results :refer [contacts-search-results]] + [status-im.contacts.views.contact-list :refer [contact-list]] + [status-im.contacts.views.new-contact :refer [new-contact]] + [status-im.qr-scanner.screen :refer [qr-scanner]] + [status-im.discover.search-results :refer [discover-search-results]] + [status-im.chat.screen :refer [chat]] + [status-im.accounts.login.screen :refer [login]] + [status-im.accounts.recover.screen :refer [recover]] + [status-im.accounts.screen :refer [accounts]] + [status-im.transactions.screen :refer [confirm]] + [status-im.chats-list.screen :refer [chats-list]] + [status-im.new-group.screen :refer [new-group]] + [status-im.participants.views.add :refer [new-participants]] + [status-im.participants.views.remove :refer [remove-participants]] + [status-im.group-settings.screen :refer [group-settings]] + [status-im.profile.screen :refer [profile my-profile]] + [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] + status-im.data-store.core + [taoensso.timbre :as log] + [status-im.components.status :as status] + [status-im.chat.styles.screen :as st] + [status-im.accounts.views.qr-code :refer [qr-code-view]])) + +(defn init-back-button-handler! [] + (let [new-listener (fn [] + ;; todo: it might be better always return false from + ;; this listener and handle application's closing + ;; in handlers + (let [stack (subscribe [:get :navigation-stack])] + (when (< 1 (count @stack)) + (dispatch [:navigate-back]) + true)))] + (.addEventListener back-android "hardwareBackPress" new-listener))) + +(defn orientation->keyword [o] + (keyword (.toLowerCase o))) + +(defn validate-current-view + [current-view signed-up?] + (if (or (contains? #{:login :chat :recover :accounts} current-view) + signed-up?) + current-view + :chat)) + +(defn app-root [] + (let [signed-up? (subscribe [:signed-up?]) + view-id (subscribe [:get :view-id]) + account-id (subscribe [:get :current-account-id]) + keyboard-height (subscribe [:get :keyboard-height]) + modal-view (subscribe [:get :modal])] + (log/debug "Current account: " @account-id) + (r/create-class + {:component-will-mount + (fn [] + (let [o (orientation->keyword (.getInitialOrientation orientation))] + (dispatch [:set :orientation o])) + (.addOrientationListener + orientation + #(dispatch [:set :orientation (orientation->keyword %)])) + (.lockToPortrait orientation) + (.addListener keyboard + "keyboardDidShow" + (fn [e] + (let [h (.. e -endCoordinates -height)] + (when-not (= h @keyboard-height) + (dispatch [:set :keyboard-height h]) + (dispatch [:set :keyboard-max-height h]))))) + (.addListener keyboard + "keyboardDidHide" + #(when-not (= 0 @keyboard-height) + (dispatch [:set :keyboard-height 0]))) + (.hide splash-screen)) + :render + (fn [] + (when @view-id + (let [current-view (validate-current-view @view-id @signed-up?)] + (let [component (case current-view + :discover main-tabs + :discover-search-results discover-search-results + :add-participants new-participants + :remove-participants remove-participants + :chat-list main-tabs + :new-group new-group + :group-settings group-settings + :contact-list main-tabs + :contact-list-search-results contacts-search-results + :group-contacts contact-list + :new-contact new-contact + :qr-scanner qr-scanner + :chat chat + :profile profile + :profile-photo-capture profile-photo-capture + :accounts accounts + :login login + :recover recover + :my-profile my-profile)] + [view + {:flex 1} + [component] + (when @modal-view + [view + st/chat-modal + [modal {:animation-type :slide + :transparent false + :on-request-close #(dispatch [:navigate-back])} + (let [component (case @modal-view + :qr-scanner qr-scanner + :qr-code-view qr-code-view + :confirm confirm + :contact-list-modal contact-list)] + [component])]])]))))}))) + +(defn init [& [env]] + (status/call-module status/init-jail) + (dispatch-sync [:reset-app]) + (.registerComponent app-registry "StatusIm" #(r/reactify-component app-root)) + (dispatch [:listen-to-network-status!]) + (dispatch [:initialize-crypt]) + (dispatch [:initialize-geth]) + (status/set-soft-input-mode status/adjust-resize) + (dispatch [:load-user-phone-number]) + (init-back-button-handler!)) diff --git a/src/status_im/android/platform.cljs b/src/status_im/android/platform.cljs new file mode 100644 index 0000000000..94601fd2e7 --- /dev/null +++ b/src/status_im/android/platform.cljs @@ -0,0 +1,85 @@ +(ns status-im.android.platform + (:require [status-im.components.styles :as styles] + [status-im.utils.utils :as u] + [status-im.components.toolbar.styles :refer [toolbar-background2]])) + +(def component-styles + {:status-bar {:default {:height 0 + :bar-style "dark-content" + :color styles/color-white} + :main {:height 0 + :bar-style "dark-content" + :color toolbar-background2} + :transparent {:height 20 + :bar-style "light-content" + :translucent? true + :color styles/color-transparent} + :modal {:height 0 + :bar-style "light-content" + :color styles/color-black}} + :sized-text {:margin-top 0 + :additional-height 0} + :chat {:new-message {:border-top-color styles/color-transparent + :border-top-width 0.5}} + :discover {:subtitle {:color styles/color-gray2 + :font-size 14} + :popular {:border-radius 1 + :margin-top 2 + :margin-bottom 4 + :margin-right 2 + :elevation 2} + :tag {:flex-direction "column" + :background-color "#7099e619" + :border-radius 5 + :padding 4} + :item {:status-text {:color styles/color-black + :line-height 22 + :font-size 14}}} + :contacts {:subtitle {:color styles/color-gray2 + :font-size 14}} + :bottom-gradient {:height 3} + :input-label {:left 4} + :input-error-text {:margin-left 4} + :toolbar-nav-action {:width 56 + :height 56 + :align-items :center + :justify-content :center} + :toolbar-last-activity {:color styles/text2-color + :background-color :transparent + :top 0 + :font-size 12}}) + +(def fonts + {:light {:font-family "sans-serif-light"} + :default {:font-family "sans-serif"} + :medium {:font-family "sans-serif-medium"} + + :toolbar-title {:font-family "sans-serif"}}) + + +;; Dialogs + +(def react-native-dialogs (js/require "react-native-dialogs")) + +(defn show-dialog [{:keys [title options callback]}] + (let [dialog (new react-native-dialogs)] + (.set dialog (clj->js {:title title + :items options + :itemsCallback callback})) + (.show dialog))) + + +;; Structure to be exported + +(def platform-specific + {:component-styles component-styles + :fonts fonts + :list-selection-fn show-dialog + :tabs {:tab-shadows? true} + :chats {:action-button? true + :new-chat-in-toolbar? false} + :contacts {:action-button? true + :new-contact-in-toolbar? false + :uppercase-subtitles? false + :group-block-shadows? true} + :discover {:uppercase-subtitles? false}}) diff --git a/src/status_im/chat/constants.cljs b/src/status_im/chat/constants.cljs new file mode 100644 index 0000000000..e34f89c8b0 --- /dev/null +++ b/src/status_im/chat/constants.cljs @@ -0,0 +1,16 @@ +(ns status-im.chat.constants) + +(def input-height 56) +(def max-input-height 66) +(def min-input-height 22) +(def input-spacing-top 16) +(def input-spacing-bottom 18) + +(def request-info-height 61) +(def response-height-normal 211) +(def minimum-suggestion-height (+ input-height request-info-height)) +(def suggestions-header-height 22) +(def minimum-command-suggestions-height + (+ input-height suggestions-header-height)) + +(def emoji-container-height 250) diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs new file mode 100644 index 0000000000..d2f9360062 --- /dev/null +++ b/src/status_im/chat/handlers.cljs @@ -0,0 +1,661 @@ +(ns status-im.chat.handlers + (:require-macros [cljs.core.async.macros :as am]) + (:require [re-frame.core :refer [enrich after debug dispatch]] + [status-im.models.commands :as commands] + [clojure.string :as string] + [status-im.components.styles :refer [default-chat-color]] + [status-im.chat.suggestions :as suggestions] + [status-im.protocol.core :as protocol] + [status-im.data-store.chats :as chats] + [status-im.data-store.contacts :as contacts] + [status-im.data-store.messages :as messages] + [status-im.data-store.pending-messages :as pending-messages] + [status-im.constants :refer [text-content-type + content-type-command + content-type-command-request + default-number-of-messages + console-chat-id + wallet-chat-id]] + [status-im.utils.random :as random] + [status-im.chat.sign-up :as sign-up-service] + [status-im.navigation.handlers :as nav] + [status-im.utils.handlers :refer [register-handler] :as u] + [status-im.handlers.server :as server] + [status-im.utils.phone-number :refer [format-phone-number + valid-mobile-number?]] + [status-im.components.status :as status] + [status-im.utils.types :refer [json->clj]] + status-im.chat.handlers.commands + [status-im.commands.utils :refer [command-prefix]] + [status-im.chat.utils :refer [console? not-console?]] + [status-im.utils.gfycat.core :refer [generate-gfy]] + status-im.chat.handlers.animation + status-im.chat.handlers.requests + status-im.chat.handlers.unviewed-messages + status-im.chat.handlers.send-message + status-im.chat.handlers.receive-message + status-im.chat.handlers.faucet + [cljs.core.async :as a] + status-im.chat.handlers.webview-bridge + status-im.chat.handlers.console + [taoensso.timbre :as log] + [tailrecursion.priority-map :refer [priority-map-by]])) + +(register-handler :set-chat-ui-props + (fn [db [_ ui-element value]] + (assoc-in db [:chat-ui-props ui-element] value))) + +(register-handler :toggle-chat-ui-props + (fn [{:keys [chat-ui-props] :as db} [_ ui-element]] + (assoc-in db [:chat-ui-props ui-element] (not (ui-element chat-ui-props))))) + +(register-handler :set-show-info + (fn [db [_ show-info]] + (assoc db :show-info show-info))) + +(register-handler :show-message-details + (u/side-effect! + (fn [_ [_ details]] + (dispatch [:set-chat-ui-props :show-bottom-info? true]) + (dispatch [:set-chat-ui-props :show-emoji? false]) + (dispatch [:set-chat-ui-props :bottom-info details])))) + +(register-handler :load-more-messages + (fn [{:keys [current-chat-id loading-allowed] :as db} _] + (let [all-loaded? (get-in db [:chats current-chat-id :all-loaded?])] + (if loading-allowed + (do (am/go + ( default-number-of-messages (count new-messages))] + (-> db + (assoc :loading-allowed false) + (update-in messages-path concat new-messages) + (assoc-in [:chats current-chat-id :all-loaded?] all-loaded?))))) + db)))) + +(defn safe-trim [s] + (when (string? s) + (string/trim s))) + +(register-handler :cancel-command + (fn [{:keys [current-chat-id] :as db} _] + (-> db + (dissoc :canceled-command) + (assoc-in [:chats current-chat-id :command-input] {}) + (update-in [:chats current-chat-id :input-text] safe-trim)))) + +(register-handler :start-cancel-command + (after #(dispatch [:set-soft-input-mode :resize])) + (u/side-effect! + (fn [db _] + (dispatch [:animate-cancel-command]) + (dispatch [:cancel-command])))) + +(defn update-input-text + [{:keys [current-chat-id] :as db} text] + (assoc-in db [:chats current-chat-id :input-text] text)) + +(register-handler :set-message-input [] + (fn [db [_ input]] + (assoc db :message-input input))) + +(register-handler :blur-message-input + (u/side-effect! + (fn [db _] + (when-let [message-input (:message-input db)] + (.blur message-input))))) + +(defn update-text [db [_ chat-id text]] + (assoc-in db [:chats chat-id :input-text] text)) + +(defn update-command [db [_ text]] + (if-not (commands/get-chat-command db) + (let [{:keys [command]} (suggestions/check-suggestion db text)] + (if command + (commands/set-command-input db :commands command) + db)) + db)) + +(defn set-command-suggestions + [db [_ chat-id suggestions]] + (assoc-in db [:command-suggestions chat-id] suggestions)) + +(register-handler ::set-command-suggestions set-command-suggestions) + +(defn check-suggestions + [db [_ chat-id text]] + (let [suggestions (suggestions/get-suggestions db text) + {:keys [dapp?]} (get-in db [:contacts chat-id])] + (when (and dapp? (empty? suggestions)) + (if (seq text) + (dispatch [::check-dapp-suggestions chat-id text]) + (dispatch [:clear-response-suggestions chat-id]))) + (log/debug "Suggestions: " suggestions) + (assoc-in db [:command-suggestions chat-id] suggestions))) + +(defn select-suggestion! + [db [_ chat-id text]] + (let [suggestions (get-in db [:command-suggestions chat-id])] + (if (= 1 (count suggestions)) + (dispatch [:set-chat-command (ffirst suggestions)]) + (dispatch [::set-text chat-id text])))) + +(register-handler ::check-dapp-suggestions + (u/side-effect! + (fn [db [_ chat-id text]] + (let [data (get-in db [:local-storage chat-id]) + path [:functions + :message-suggestions] + params {:parameters {:message text} + :context {:data data}}] + (status/call-jail chat-id + path + params + (fn [{:keys [result] :as data}] + (let [{:keys [returned]} result] + (log/debug "Message suggestions: " returned) + (if returned + (dispatch [:suggestions-handler {:chat-id chat-id} data]) + (dispatch [:clear-response-suggestions chat-id]))))))))) + +(register-handler :set-chat-input-text + (u/side-effect! + (fn [{:keys [current-chat-id]} [_ text]] + ;; fixes https://github.com/status-im/status-react/issues/594 + ;; todo: revisit with more clever solution + (let [text' (if (= text "! ") "!" text)] + (if (console? current-chat-id) + (dispatch [::check-input-for-commands text']) + (dispatch [::check-suggestions current-chat-id text'])))))) + +(register-handler :add-to-chat-input-text + (u/side-effect! + (fn [{:keys [chats current-chat-id]} [_ text-to-add]] + (let [input-text (get-in chats [current-chat-id :input-text])] + (dispatch [:set-chat-input-text (str input-text text-to-add)]))))) + +(def possible-commands + {[:confirmation-code :responses] #(re-matches #"^[\d]{4}$" %) + [:phone :commands] valid-mobile-number?}) + +(defn check-text-for-commands [text] + (ffirst (filter (fn [[_ f]] (f text)) possible-commands))) + +(register-handler ::check-input-for-commands + (u/side-effect! + (fn [_ [_ text]] + (if-let [[_ type :as command] (check-text-for-commands text)] + (let [text' (if (= :commands type) + (str command-prefix text) + text)] + (dispatch [::set-command-with-content command text'])) + (dispatch [::check-suggestions console-chat-id text]))))) + +(register-handler ::set-command-with-content + (u/side-effect! + (fn [_ [_ [command type] text]] + (dispatch [:set-chat-command command type]) + (dispatch [:set-chat-command-content text])))) + +(register-handler :set-message-input-view-height + (fn [{:keys [current-chat-id] :as db} [_ height]] + (assoc-in db [:chats current-chat-id :message-input-height] height))) + +(register-handler ::check-suggestions + [(after select-suggestion!) + (after #(dispatch [:animate-command-suggestions]))] + check-suggestions) + +(register-handler ::set-text update-text) + +(defn set-message-shown + [db chat-id message-id] + (update-in db [:chats chat-id :messages] (fn [messages] + (map (fn [message] + (if (= message-id (:message-id message)) + (assoc message :new? false) + message)) + messages)))) + +(register-handler :set-message-shown + (fn [db [_ {:keys [chat-id message-id]}]] + (set-message-shown db chat-id message-id))) + +(defn init-console-chat + ([{:keys [chats current-account-id] :as db} existing-account?] + (let [new-chat sign-up-service/console-chat] + (if (chats console-chat-id) + db + (do + (dispatch [:add-contacts [sign-up-service/console-contact]]) + (chats/save new-chat) + (contacts/save-all [sign-up-service/console-contact]) + (when-not current-account-id + (sign-up-service/intro)) + (when existing-account? + (sign-up-service/start-signup)) + (-> db + (assoc :new-chat new-chat) + (update :chats assoc console-chat-id new-chat) + (assoc :current-chat-id console-chat-id))))))) + +(register-handler :init-console-chat + (fn [db _] + (init-console-chat db false))) + +(register-handler :account-generation-message + (u/side-effect! + (fn [_] + (when (not (messages/get-by-id sign-up-service/passphraze-message-id)) + (sign-up-service/account-generation-message))))) + +(register-handler :show-mnemonic + (u/side-effect! + (fn [_ [_ mnemonic]] + (let [crazy-math-message? (messages/get-by-id sign-up-service/crazy-math-message)] + (sign-up-service/passphrase-messages mnemonic crazy-math-message?))))) + +(register-handler :sign-up + (after (fn [_ [_ phone-number]] + (dispatch [:account-update {:phone phone-number}]))) + (fn [db [_ phone-number message-id]] + (let [formatted (format-phone-number phone-number)] + (-> db + (assoc :user-phone-number formatted) + sign-up-service/start-listening-confirmation-code-sms + (server/sign-up formatted + message-id + sign-up-service/on-sign-up-response))))) + +(register-handler :stop-listening-confirmation-code-sms + (fn [db [_]] + (if (:confirmation-code-sms-listener db) + (sign-up-service/stop-listening-confirmation-code-sms db) + db))) + +(register-handler :sign-up-confirm + (u/side-effect! + (fn [_ [_ confirmation-code message-id]] + (server/sign-up-confirm + confirmation-code + message-id + sign-up-service/on-send-code-response)))) + +(register-handler :set-signed-up + (u/side-effect! + (fn [_ [_ signed-up]] + (dispatch [:account-update {:signed-up? signed-up}])))) + +(defn load-messages! + ([db] (load-messages! db nil)) + ([{:keys [current-chat-id] :as db} _] + (assoc db :messages (messages/get-by-chat-id current-chat-id)))) + +(defn init-chat + ([db] (init-chat db nil)) + ([{:keys [messages current-chat-id] :as db} _] + (-> db + (assoc-in [:chats current-chat-id :messages] messages) + (dissoc :messages)))) + +(defn load-commands! + [{:keys [current-chat-id]}] + (dispatch [:load-commands! current-chat-id])) + +(register-handler :init-chat + (after #(dispatch [:load-requests!])) + (-> load-messages! + ((enrich init-chat)) + ((after load-commands!)))) + +(defn compare-chats + [{timesatmp1 :timestamp} {timestamp2 :timestamp}] + (compare timestamp2 timesatmp1)) + +(defn initialize-chats + [{:keys [loaded-chats account-creation? chats] :as db} _] + (let [chats' (if account-creation? + chats + (->> loaded-chats + (map (fn [{:keys [chat-id] :as chat}] + (let [last-message (messages/get-last-message db chat-id)] + [chat-id (assoc chat :last-message last-message)]))) + (into (priority-map-by compare-chats)))) + ids (set (keys chats'))] + + (-> db + (assoc :chats chats') + (dissoc :loaded-chats) + (init-console-chat true)))) + +(defn load-chats! + [{:keys [account-creation?] :as db} _] + (if account-creation? + db + (assoc db :loaded-chats (chats/get-all)))) + +;TODO: check if its new account / signup status / create console chat +(register-handler :initialize-chats + [(after #(dispatch [:load-unviewed-messages!])) + (after #(dispatch [:load-default-contacts!]))] + ((enrich initialize-chats) load-chats!)) + +(defmethod nav/preload-data! :chat + [{:keys [current-chat-id] :as db} [_ _ id]] + (let [chat-id (or id current-chat-id) + messages (get-in db [:chats chat-id :messages]) + command? (= :command (get-in db [:edit-mode chat-id])) + db' (-> db + (assoc :current-chat-id chat-id) + (update-in [:animations :to-response-height chat-id] + #(if command? % 0))) + commands-loaded? (if js/goog.DEBUG + false + (get-in db [:chats chat-id :commands-loaded]))] + (when (= current-chat-id wallet-chat-id) + (dispatch [:cancel-command])) + (dispatch [:load-requests! chat-id]) + ;; todo rewrite this. temporary fix for https://github.com/status-im/status-react/issues/607 + (dispatch [:load-commands! chat-id]) + #_(if-not commands-loaded? + (dispatch [:load-commands! chat-id]) + (dispatch [:invoke-chat-loaded-callbacks chat-id])) + (if (and (seq messages) + (not= (count messages) 1)) + db' + (-> db' + load-messages! + init-chat)))) + +(register-handler :add-chat-loaded-callback + (fn [db [_ chat-id callback]] + (log/debug "Add chat loaded callback: " chat-id callback) + (update-in db [::chat-loaded-callbacks chat-id] conj callback))) + +(register-handler ::clear-chat-loaded-callbacks + (fn [db [_ chat-id]] + (log/debug "Clear chat loaded callback: " chat-id) + (assoc-in db [::chat-loaded-callbacks chat-id] nil))) + +(register-handler :invoke-chat-loaded-callbacks + (u/side-effect! + (fn [db [_ chat-id]] + (log/debug "Invoking chat loaded callbacks: " chat-id) + (let [callbacks (get-in db [::chat-loaded-callbacks chat-id])] + (log/debug "Invoking chat loaded callbacks: " callbacks) + (doseq [callback callbacks] + (callback)) + (dispatch [::clear-chat-loaded-callbacks chat-id]))))) + +(defn prepare-chat [{:keys [contacts]} chat-id chat] + (let [name (get-in contacts [chat-id :name])] + (merge {:chat-id chat-id + :name (or name (generate-gfy)) + :color default-chat-color + :group-chat false + :is-active true + :timestamp (.getTime (js/Date.)) + :contacts [{:identity chat-id}]} + chat))) + +(defn add-new-chat + [db [_ chat-id chat]] + (assoc db :new-chat (prepare-chat db chat-id chat))) + +(defn add-chat [{:keys [new-chat chats] :as db} [_ chat-id]] + (if-not (get chats chat-id) + (update db :chats assoc chat-id new-chat) + db)) + +(defn save-new-chat! + [{{:keys [chat-id] :as new-chat} :new-chat} _] + (when-not (chats/exists? chat-id) + (chats/save new-chat))) + +(defn open-chat! + [_ [_ chat-id _ navigation-type]] + (dispatch [(or navigation-type :navigate-to) :chat chat-id])) + +(register-handler ::start-chat! + (-> add-new-chat + ((enrich add-chat)) + ((after save-new-chat!)) + ((after open-chat!)))) + +(register-handler :start-chat + (u/side-effect! + (fn [{:keys [chats current-public-key]} + [_ contact-id options navigation-type]] + (when-not (= current-public-key contact-id) + (if (chats contact-id) + (dispatch [(or navigation-type :navigate-to) :chat contact-id]) + (dispatch [::start-chat! contact-id options navigation-type])))))) + +(register-handler :add-chat + (-> add-new-chat + ((enrich add-chat)) + ((after save-new-chat!)))) + +(defn update-chat! + [_ [_ {:keys [name] :as chat}]] + (let [chat' (if name chat (dissoc chat :name))] + (chats/save chat'))) + +(register-handler :update-chat! + (-> (fn [db [_ {:keys [chat-id name] :as chat}]] + (let [chat' (if name chat (dissoc chat :name))] + (if (get-in db [:chats chat-id]) + (update-in db [:chats chat-id] merge chat') + db))) + ((after update-chat!)))) + +(register-handler :upsert-chat! + (fn [db [_ {:keys [chat-id] :as opts}]] + (let [chat (if (chats/exists? chat-id) + (let [chat (chats/get-by-id chat-id)] + (assoc chat :timestamp (random/timestamp))) + (prepare-chat db chat-id opts))] + (chats/save chat) + (update-in db [:chats chat-id] merge chat)))) + +(register-handler :switch-command-suggestions! + (u/side-effect! + (fn [db] + (let [text (if (suggestions/typing-command? db) "" "!")] + (dispatch [:set-chat-input-text text]))))) + +(defn remove-chat + [db [_ chat-id]] + (update db :chats dissoc chat-id)) + +; todo do we really need this message? +(defn leaving-message! + [{:keys [current-chat-id]} _] + (messages/save + current-chat-id + {:from "system" + :message-id (random/id) + :content "You left this chat" + :content-type text-content-type})) + +(defn delete-messages! + [{:keys [current-chat-id]} [_ chat-id]] + (let [id (or chat-id current-chat-id)] + (messages/delete-by-chat-id id))) + +(defn delete-chat! + [_ [_ chat-id]] + (chats/delete chat-id)) + +(defn remove-pending-messages! + [_ [_ chat-id]] + (pending-messages/delete-all-by-chat-id chat-id)) + +(register-handler :leave-group-chat + ;; todo oreder of operations tbd + (after (fn [_ _] (dispatch [:navigation-replace :chat-list]))) + (u/side-effect! + (fn [{:keys [web3 current-chat-id chats current-public-key]} _] + (let [{:keys [public-key private-key]} (chats current-chat-id)] + (protocol/stop-watching-group! + {:web3 web3 + :group-id current-chat-id}) + (protocol/leave-group-chat! + {:web3 web3 + :group-id current-chat-id + :keypair {:public public-key + :private private-key} + :message {:from current-public-key + :message-id (random/id)}})) + (dispatch [::remove-chat current-chat-id])))) + +(register-handler ::remove-chat + (-> remove-chat + ;((after leaving-message!)) + ((after delete-messages!)) + ((after remove-pending-messages!)) + ((after delete-chat!)))) + +(defn edit-mode-handler [mode] + (fn [{:keys [current-chat-id] :as db} _] + (assoc-in db [:edit-mode current-chat-id] mode))) + +(register-handler :command-edit-mode + (after #(dispatch [:clear-validation-errors])) + (edit-mode-handler :command)) + +(register-handler :text-edit-mode + (after #(dispatch [:set-chat-input-text ""])) + (edit-mode-handler :text)) + +(register-handler :set-layout-height + [(after + (fn [{:keys [current-chat-id] :as db}] + (let [suggestions (get-in db [:has-suggestions? current-chat-id]) + mode (get-in db [:edit-mode current-chat-id])] + (when (and (= :command mode) suggestions) + (dispatch [:fix-response-height nil nil true]))))) + (after + (fn [{:keys [current-chat-id] :as db}] + (let [suggestions (get-in db [:command-suggestions current-chat-id]) + mode (get-in db [:edit-mode current-chat-id])] + (when (and (not= :command mode) (seq suggestions)) + (dispatch [:fix-commands-suggestions-height nil nil true])))))] + (fn [db [_ h]] + (assoc db :layout-height h))) + +(defn send-seen! + [{:keys [web3 current-public-key chats]} + [_ {:keys [from chat-id message-id]}]] + (when-not (console? chat-id) + (let [{:keys [group-chat]} (chats chat-id)] + (protocol/send-seen! {:web3 web3 + :message {:from current-public-key + :to from + :group-id (when group-chat chat-id) + :message-id message-id}})))) +(register-handler :send-seen! + [(after (fn [_ [_ {:keys [message-id]}]] + (messages/update {:message-id message-id + :message-status :seen}))) + (after (fn [_ [_ {:keys [chat-id]}]] + (dispatch [:remove-unviewed-messages chat-id])))] + (u/side-effect! send-seen!)) + +(defn send-clock-value-request! + [{:keys [web3 current-public-key]} [_ {:keys [message-id from] :as message}]] + (protocol/send-clock-value-request! {:web3 web3 + :message {:from current-public-key + :to from + :message-id message-id}})) + +(register-handler :send-clock-value-request! (u/side-effect! send-clock-value-request!)) + +(defn send-clock-value! + [{:keys [web3 current-public-key]} to message-id clock-value] + (when current-public-key + (protocol/send-clock-value! {:web3 web3 + :message {:from current-public-key + :to to + :message-id message-id + :clock-value clock-value}}))) + +(register-handler :update-clock-value! + (after (fn [db [_ to i {:keys [message-id] :as message} last-clock-value]] + (let [clock-value (+ last-clock-value i 1)] + (messages/update (assoc message :clock-value clock-value)) + (send-clock-value! db to message-id clock-value)))) + (fn [db [_ _ i {:keys [message-id] :as message} last-clock-value]] + (assoc-in db [:message-extras message-id :clock-value] (+ last-clock-value i 1)))) + +(register-handler :send-clock-value! + (u/side-effect! + (fn [db [_ to message-id]] + (let [{:keys [clock-value]} (messages/get-by-id message-id)] + (send-clock-value! db to message-id clock-value))))) + +(register-handler :set-web-view-url + (fn [{:keys [current-chat-id] :as db} [_ url]] + (assoc-in db [:web-view-url current-chat-id] url))) + +(register-handler :set-web-view-extra-js + (fn [{:keys [current-chat-id] :as db} [_ extra-js]] + (assoc-in db [:web-view-extra-js current-chat-id] extra-js))) + +(register-handler :set-soft-input-mode + (after + (fn [{:keys [current-chat-id]} [_ mode chat-id]] + (when (or (nil? chat-id) (= current-chat-id chat-id)) + (status/set-soft-input-mode (if (= :pan mode) + status/adjust-pan + status/adjust-resize))))) + (fn [db [_ chat-id mode]] + (assoc-in db [:kb-mode chat-id] mode))) + +(register-handler :check-autorun + (u/side-effect! + (fn [{:keys [current-chat-id] :as db}] + (let [autorun (get-in db [:chats current-chat-id :autorun])] + (when autorun + (am/go + ;;todo: find another way to make it work... + (a/ db + (assoc-in [:animations :command-suggestions-height chat-id] height) + (update-in [:animations :commands-height-changed] changed?))))) + +(defn get-minimum-height + [{:keys [current-chat-id] :as db}] + (let [path [:chats current-chat-id :command-input :command :type] + type (get-in db path) + command? (= :command type) + response? (not command?) + errors (get-in db [:validation-errors current-chat-id]) + validation-errors? (seq errors) + suggestion? (get-in db [:has-suggestions? current-chat-id]) + custom-errors (get-in db [:custom-validation-errors current-chat-id]) + custom-errors? (seq custom-errors) + validation-errors? (or validation-errors? custom-errors?)] + (cond-> 0 + validation-errors? (+ request-info-height) + response? (+ minimum-suggestion-height) + command? (+ input-height) + (and suggestion? command?) (+ suggestions-header-height) + ;custom-errors? (+ suggestions-header-height) + (and command? validation-errors?) (+ suggestions-header-height)))) + +(register-handler :animate-show-response + ;[(after #(dispatch [:command-edit-mode]))] + (fn [{:keys [current-chat-id] :as db}] + (let [suggestions? (get-in db [:has-suggestions? current-chat-id]) + fullscreen? (get-in db [:chats current-chat-id :command-input :command :fullscreen]) + max-height (get-in db [:layout-height]) + height (if suggestions? + (if fullscreen? + max-height + middle-height) + (get-minimum-height db))] + (assoc-in db [:animations :to-response-height current-chat-id] height)))) + +(defn fix-height + [height-key height-signal-key suggestions-key minimum] + (fn [{:keys [current-chat-id] :as db} [_ vy current no-animation]] + (let [input-margin (subscribe [:input-margin]) + max-height (- (get-in db [:layout-height]) + (get-in platform-specific [:component-styles :status-bar :default :height]) + @input-margin) + moving-down? (pos? vy) + moving-up? (not moving-down?) + under-middle-position? (<= current middle-height) + over-middle-position? (not under-middle-position?) + suggestions (get-in db [suggestions-key current-chat-id]) + old-fixed (get-in db [:animations height-key current-chat-id]) + + new-fixed (cond (not suggestions) + (minimum db) + + (and (nil? vy) (nil? current) + (> old-fixed middle-height)) + max-height + + (and (nil? vy) (nil? current) + (< old-fixed middle-height)) + (minimum db) + + (and under-middle-position? moving-up?) + middle-height + + (and over-middle-position? moving-down?) + middle-height + + (and over-middle-position? moving-up?) + max-height + + (and under-middle-position? moving-down?) + (minimum db))] + (-> db + (assoc-in [:animations height-key current-chat-id] new-fixed) + (update-in [:animations height-signal-key] inc) + (assoc-in [:animate? current-chat-id] (not no-animation)))))) + +(defn commands-min-height + [{:keys [current-chat-id] :as db}] + (let [suggestions (get-in db [:command-suggestions current-chat-id])] + (if (seq suggestions) + minimum-command-suggestions-height + input-height))) + +(register-handler :fix-commands-suggestions-height + (fix-height :command-suggestions-height + :commands-height-changed + :command-suggestions + commands-min-height)) + +(register-handler :fix-response-height + (fix-height :to-response-height + :response-height-changed + :has-suggestions? + get-minimum-height)) diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs new file mode 100644 index 0000000000..5f2d96b86d --- /dev/null +++ b/src/status_im/chat/handlers/commands.cljs @@ -0,0 +1,281 @@ +(ns status-im.chat.handlers.commands + (:require [re-frame.core :refer [enrich after dispatch]] + [status-im.utils.handlers :refer [register-handler] :as u] + [status-im.components.status :as status] + [status-im.models.commands :as commands] + [status-im.chat.utils :refer [console? not-console?]] + [clojure.string :as str] + [status-im.commands.utils :as cu] + [status-im.utils.phone-number :as pn] + [status-im.i18n :as i18n] + [status-im.utils.datetime :as time] + [status-im.utils.random :as random] + [status-im.utils.platform :as platform])) + +(defn content-by-command + [{:keys [type]} content] + (if (and (= :command type) content) + (subs content (count cu/command-prefix)) + content)) + +(defn invoke-suggestions-handler! + [{:keys [current-chat-id canceled-command] :as db} _] + (when-not canceled-command + (let [{:keys [command content params]} (get-in db [:chats current-chat-id :command-input]) + data (get-in db [:local-storage current-chat-id]) + {:keys [name type]} command + path [(if (= :command type) :commands :responses) + name + :params + 0 + :suggestions] + params {:parameters (or params {}) + :context {:data data}}] + (status/call-jail current-chat-id + path + params + #(dispatch [:suggestions-handler {:command command + :content content + :chat-id current-chat-id} %]))))) + +(defn cancel-command! + [{:keys [canceled-command]}] + (when canceled-command + (dispatch [:start-cancel-command]))) + +(defn current-command + [{:keys [current-chat-id] :as db} k] + (get-in db [:chats current-chat-id :command-input :command k])) + +(register-handler :set-chat-command-content + [(after (fn [db] + (let [trigger (keyword (current-command db :suggestions-trigger))] + (when (= :on-change trigger) + (invoke-suggestions-handler! db nil))))) + (after cancel-command!) + (after #(dispatch [:clear-validation-errors]))] + (fn [{:keys [current-chat-id] :as db} [_ content]] + (let [starts-as-command? (str/starts-with? content cu/command-prefix) + command? (= :command (current-command db :type)) + {:keys [parameter-idx command]} (commands/get-command-input db) + parameter-name (-> command :params (get parameter-idx) :name)] + (as-> db db + (commands/set-chat-command-content db content) + (commands/set-command-parameter db parameter-name content) + (assoc-in db [:chats current-chat-id :input-text] nil) + (assoc db :canceled-command (and command? (not starts-as-command?))))))) + +(register-handler :fill-chat-command-content + (u/side-effect! + (fn [db [_ content]] + (let [command? (= :command (current-command db :type))] + (dispatch + [:set-chat-command-content + (if command? + (str cu/command-prefix content) + content)]))))) + +(defn invoke-command-preview! + [db command-message [_ command-input chat-id]] + (let [{:keys [command]} command-message + {:keys [name type]} command + parameters (:params (or command-input (commands/get-command-input db))) + path [(if (= :command type) :commands :responses) + name + :preview] + params {:parameters parameters + :context {:platform platform/platform}}] + (if (and (console? chat-id) (= name "js")) + (dispatch [:send-chat-message command-message]) + (status/call-jail chat-id + path + params + #(dispatch [:command-preview command-message %]))))) + +(defn command-input + ([{:keys [current-chat-id] :as db}] + (command-input db current-chat-id)) + ([db chat-id] + (get-in db [:chats chat-id :command-input]))) + + +(register-handler ::validate! + (u/side-effect! + (fn [_ [_ command-input {:keys [chat-id handler]} {:keys [error result]}]] + ;; todo handle error + (when-not error + (let [{:keys [errors validationHandler parameters]} (:returned result)] + (cond errors + (dispatch [::add-validation-errors chat-id errors]) + + validationHandler + (dispatch [::validation-handler! + command-input + chat-id + validationHandler + parameters]) + + :else (if handler + (handler) + (dispatch [::finish-command-staging command-input chat-id])))))))) + +(register-handler :validate-command + (u/side-effect! + (fn [{:keys [current-chat-id current-account-id] :as db} [_ command-input command]] + (let [command-input (or command-input (commands/get-command-input db)) + command (or command (commands/get-chat-command db))] + (dispatch [::start-command-validation! {:command-input command-input + :command command + :chat-id current-chat-id + :address current-account-id}]))))) + +(register-handler ::finish-command-staging + [(after #(dispatch [:start-cancel-command]))] + (u/side-effect! + (fn [db [_ command-input chat-id :as parameters]] + (let [db (assoc-in db [:chats chat-id :input-text] nil) + {:keys [command to-message-id params]} (or command-input (command-input db)) + command-info {:command command + :params params + :to-message to-message-id + :created-at (time/now-ms) + :id (random/id) + :chat-id chat-id}] + (dispatch [:set-in [:command->chat (:id command-info)] chat-id]) + (invoke-command-preview! db command-info parameters))))) + +(defn set-chat-command + [{:keys [current-chat-id] :as db} [_ command-key type]] + (-> db + (commands/set-command-input (or type :commands) command-key) + (assoc-in [:chats current-chat-id :command-input :content] cu/command-prefix) + (assoc :disable-input true))) + +(register-handler :set-chat-command + [(after invoke-suggestions-handler!) + (after #(dispatch [:set-soft-input-mode :resize])) + (after #(dispatch [:command-edit-mode]))] + set-chat-command) + +(defn set-response-command [db [_ to-message-id command-key params]] + (-> db + (commands/set-command-input :responses to-message-id command-key params) + (assoc :canceled-command false))) + +(register-handler ::set-response-chat-command + [(after invoke-suggestions-handler!) + (after #(dispatch [:command-edit-mode])) + (after #(dispatch [:set-chat-input-text ""]))] + set-response-command) + +(register-handler :set-response-chat-command + (u/side-effect! + (fn [{:keys [current-chat-id] :as db} + [_ to-message-id command-key params]] + (when (get-in db [:chats current-chat-id :responses command-key]) + (dispatch [::set-response-chat-command to-message-id command-key params]))))) + +(register-handler ::add-validation-errors + (after #(dispatch [:fix-response-height])) + (fn [db [_ chat-id errors]] + (assoc-in db [:custom-validation-errors chat-id] + (map cu/generate-hiccup errors)))) + +(register-handler :clear-validation-errors + (fn [db] + (dissoc db :validation-errors :custom-validation-errors))) + +(defn dispatch-error! + [chat-id title description] + (letfn [(wrap-params [p] (if (seqable? p) p [p]))] + (dispatch [::set-validation-error + chat-id + {:title (apply i18n/label (wrap-params title)) + :description (apply i18n/label (wrap-params description))}]))) + +(def validation-handlers + {:phone (fn [command-input chat-id [number]] + (if (pn/valid-mobile-number? number) + (dispatch [::finish-command-staging command-input chat-id]) + (dispatch-error! chat-id :t/phone-number :t/invalid-phone)))}) + +(defn validator [name] + (validation-handlers (keyword name))) + +(register-handler ::validation-handler! + (u/side-effect! + (fn [_ [_ command-input chat-id name params]] + (when-let [handler (validator name)] + (handler command-input chat-id params))))) + +(register-handler ::set-validation-error + (after #(dispatch [:fix-response-height])) + (fn [db [_ chat-id error]] + (assoc-in db [:validation-errors chat-id] [error]))) + +(register-handler :invoke-commands-suggestions! + (u/side-effect! + invoke-suggestions-handler!)) + +(register-handler :send-command! + (u/side-effect! + (fn [{:keys [current-chat-id current-account-id] :as db}] + (let [{:keys [params] :as command} (commands/get-chat-command db) + {:keys [parameter-idx]} (commands/get-command-input db) + + last-parameter? (= (inc parameter-idx) (count params)) + + parameters {:command command :input command-input} + + {:keys [command content]} (command-input db) + content' (content-by-command command content)] + (dispatch [:set-command-parameter + {:value content' + :parameter (params parameter-idx)}]) + (if last-parameter? + (dispatch [:check-suggestions-trigger! parameters]) + (dispatch [::start-command-validation! + {:chat-id current-chat-id + :address current-account-id + :handler #(dispatch [:next-command-parameter])}])))))) + +(register-handler ::start-command-validation! + (u/side-effect! + (fn [db [_ {:keys [command-input chat-id address] :as data}]] + (let [command-input' (or command-input (commands/get-command-input db)) + {:keys [parameter-idx params command]} command-input' + {:keys [name type]} command + current-parameter (-> command + :params + (get parameter-idx) + :name) + to (get-in db [:contacts chat-id :address]) + context {:current-parameter current-parameter + :from address + :to to} + path [(if (= :command type) :commands :responses) + name + :validator] + parameters {:context context + :parameters params}] + (status/call-jail chat-id + path + parameters + #(dispatch [::validate! command-input data %])))))) + +(register-handler :set-command-parameter + (fn [db [_ {:keys [value parameter]}]] + (let [name (:name parameter)] + (commands/set-command-parameter db name value)))) + +(register-handler :next-command-parameter + (fn [db _] + (commands/next-command-parameter db))) + +(register-handler :check-suggestions-trigger! + (u/side-effect! + (fn [_ [_ {:keys [command]}]] + (let [suggestions-trigger (keyword (:suggestions-trigger command))] + (if (= :on-send suggestions-trigger) + (dispatch [:invoke-commands-suggestions!]) + (dispatch [:validate-command])))))) diff --git a/src/status_im/chat/handlers/console.cljs b/src/status_im/chat/handlers/console.cljs new file mode 100644 index 0000000000..b17282beb1 --- /dev/null +++ b/src/status_im/chat/handlers/console.cljs @@ -0,0 +1,80 @@ +(ns status-im.chat.handlers.console + (:require [re-frame.core :refer [dispatch dispatch-sync after]] + [status-im.utils.handlers :refer [register-handler] :as u] + [status-im.constants :refer [console-chat-id + text-content-type]] + [status-im.data-store.messages :as messages] + [taoensso.timbre :as log] + [status-im.utils.random :as random])) + +(def console-commands + {:password + (fn [params _] + (dispatch [:create-account (params "password")])) + + :phone + (fn [params id] + (dispatch [:sign-up (params "phone") id])) + + :confirmation-code + (fn [params id] + (dispatch [:sign-up-confirm (params "code") id])) + + :faucet + (fn [params id] + (dispatch [:open-faucet (params "url") id]))}) + +(def commands-names (set (keys console-commands))) + +(def commands-with-delivery-status + (disj commands-names :password :faucet)) + +(register-handler :invoke-console-command-handler! + (u/side-effect! + (fn [_ [_ {:keys [chat-id command-message] :as parameters}]] + (let [{:keys [id command params]} command-message + {:keys [name]} command] + (dispatch [:prepare-command! chat-id parameters]) + ((console-commands (keyword name)) params id))))) + +(register-handler :set-message-status + (after + (fn [_ [_ message-id status]] + (messages/update {:message-id message-id + :message-status status}))) + (fn [db [_ message-id status]] + (assoc-in db [:message-statuses message-id] {:status status}))) + +(register-handler :console-respond-command + (u/side-effect! + (fn [_ [_ {:keys [command] :as parameters}]] + (let [{:keys [command handler-data]} command] + (when command + (let [{:keys [name]} command] + (case name + "js" (let [{:keys [err data messages]} handler-data + content (if err + err + data)] + (doseq [message messages] + (let [{:keys [message type]} message] + (dispatch [:received-message + {:message-id (random/id) + :content (str type ": " message) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]))) + (when content + (dispatch [:received-message + {:message-id (random/id) + :content (str content) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]))) + (log/debug "ignoring command: " command)))))))) + + diff --git a/src/status_im/chat/handlers/faucet.cljs b/src/status_im/chat/handlers/faucet.cljs new file mode 100644 index 0000000000..f5a45f2930 --- /dev/null +++ b/src/status_im/chat/handlers/faucet.cljs @@ -0,0 +1,62 @@ +(ns status-im.chat.handlers.faucet + (:require [re-frame.core :refer [dispatch]] + [status-im.utils.handlers :refer [register-handler] :as u] + [status-im.utils.utils :refer [http-get]] + [status-im.utils.random :as random] + [status-im.constants :refer [console-chat-id + text-content-type]] + [status-im.i18n :refer [label]] + [taoensso.timbre :as log] + [goog.string :as gstring] + goog.string.format)) + +(def faucets + [{:name "http://faucet.ropsten.be:3001" + :type :api + :api-url "http://faucet.ropsten.be:3001/donate/0x%s"} + {:name "http://46.101.129.137:3001" + :type :api + :api-url "http://46.101.129.137:3001/donate/0x%s"}]) + +(defn faucet-by-name [faucet-name] + (->> faucets + (filter #(= (:name %) faucet-name)) + (first))) + +(defn received-message [content] + (dispatch [:received-message + {:message-id (random/id) + :content content + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}])) + +(defmulti open-faucet (fn [_ _ {:keys [type]}] type)) + +(defmethod open-faucet :api + [faucet-name current-address {:keys [api-url]}] + (let [api-url (gstring/format api-url current-address)] + (http-get api-url + #(received-message (label :t/faucet-success)) + #(received-message (label :t/faucet-error))))) + +(defmethod open-faucet :prefill + [faucet-name current-address {:keys [prefill-js]}] + (let [prefill-js (gstring/format prefill-js current-address) + web-view-js (gstring/format + "document.addEventListener('DOMContentLoaded', function(){ %s };" + prefill-js)] + (dispatch [:set-chat-command :browse]) + (dispatch [:fill-chat-command-content faucet-name]) + (dispatch [:set-web-view-extra-js web-view-js]) + (js/setTimeout #(dispatch [:send-command!]) 500))) + +(register-handler + :open-faucet + (u/side-effect! + (fn [{:keys [accounts current-account-id]} [_ faucet-name _]] + (if-let [faucet (faucet-by-name faucet-name)] + (let [current-address (get-in accounts [current-account-id :address])] + (open-faucet faucet-name current-address faucet)))))) diff --git a/src/status_im/chat/handlers/receive_message.cljs b/src/status_im/chat/handlers/receive_message.cljs new file mode 100644 index 0000000000..b45b680573 --- /dev/null +++ b/src/status_im/chat/handlers/receive_message.cljs @@ -0,0 +1,114 @@ +(ns status-im.chat.handlers.receive-message + (:require [status-im.utils.handlers :refer [register-handler] :as u] + [re-frame.core :refer [enrich after debug dispatch path]] + [status-im.data-store.messages :as messages] + [status-im.chat.utils :as cu] + [status-im.commands.utils :refer [generate-hiccup]] + [status-im.utils.random :as random] + [status-im.constants :refer [wallet-chat-id + content-type-command + content-type-command-request] + :as c] + [cljs.reader :refer [read-string]] + [status-im.data-store.chats :as chats] + [taoensso.timbre :as log] + [status-im.utils.scheduler :as s])) + +(defn check-preview [{:keys [content] :as message}] + (if-let [preview (:preview content)] + (let [rendered-preview (generate-hiccup (read-string preview))] + (assoc message + :preview preview + :rendered-preview rendered-preview)) + message)) + +(defn store-message [{chat-id :chat-id :as message}] + (messages/save chat-id (dissoc message :rendered-preview :new?))) + +(defn get-current-identity + [{:keys [current-account-id accounts]}] + (:public-key (accounts current-account-id))) + +(declare add-message-to-wallet) + +(defn add-message + [db {:keys [from group-id chat-id + message-id timestamp clock-value show?] + :as message + :or {clock-value 0}}] + (let [same-message (messages/get-by-id message-id) + current-identity (get-current-identity db) + chat-id' (or group-id chat-id from) + exists? (chats/exists? chat-id') + active? (chats/is-active? chat-id') + chat-clock-value (messages/get-last-clock-value chat-id') + clock-value (if (= clock-value 0) + (inc chat-clock-value) + clock-value)] + (when (and (not same-message) + (not= from current-identity) + (or (not exists?) active?)) + (let [group-chat? (not (nil? group-id)) + previous-message (messages/get-last-message db chat-id') + message' (assoc (->> message + (cu/check-author-direction previous-message) + (check-preview)) + :chat-id chat-id' + :timestamp (or timestamp (random/timestamp)) + :clock-value clock-value)] + (store-message message') + (dispatch [:upsert-chat! {:chat-id chat-id' + :group-chat group-chat?}]) + (dispatch [::add-message chat-id' message']) + (when (= (:content-type message') content-type-command-request) + (dispatch [:add-request chat-id' message'])) + (dispatch [:add-unviewed-message chat-id' message-id]) + (when-not show? + (dispatch [:send-clock-value-request! message]))) + (if (and + (= (:content-type message) content-type-command) + (not= chat-id' wallet-chat-id) + (= "send" (get-in message [:content :command]))) + (add-message-to-wallet db message))))) + +(defn add-message-to-wallet [db {:keys [content-type] :as message}] + (let [ct (if (= content-type c/content-type-command) + c/content-type-wallet-command + c/content-type-wallet-request) + message' (assoc message :clock-value 0 + :message-id (random/id) + :chat-id wallet-chat-id + :content-type ct)] + (add-message db message'))) + +(register-handler :received-protocol-message! + (u/side-effect! + (fn [_ [_ {:keys [from to payload]}]] + (dispatch [:received-message (merge payload + {:from from + :to to + :chat-id from})])))) + +(register-handler :received-message + (u/side-effect! + (fn [db [_ message]] + (add-message db message)))) + +(register-handler ::add-message + (fn [db [_ add-to-chat-id {:keys [chat-id new?] :as message}]] + (cu/add-message-to-db db add-to-chat-id chat-id message new?))) + +(defn commands-loaded? [db chat-id] + (get-in db [:chats chat-id :commands-loaded])) + +(def timeout 50) + +(register-handler :received-message-when-commands-loaded + (u/side-effect! + (fn [db [_ chat-id message]] + (if (commands-loaded? db chat-id) + (dispatch [:received-message message]) + (s/execute-later + #(dispatch [:received-message-when-commands-loaded chat-id message]) + timeout))))) + diff --git a/src/status_im/chat/handlers/requests.cljs b/src/status_im/chat/handlers/requests.cljs new file mode 100644 index 0000000000..6aa15d962b --- /dev/null +++ b/src/status_im/chat/handlers/requests.cljs @@ -0,0 +1,45 @@ +(ns status-im.chat.handlers.requests + (:require [re-frame.core :refer [after dispatch enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.data-store.requests :as requests] + [status-im.utils.handlers :refer [register-handler] :as u] + [taoensso.timbre :as log])) + +(defn store-request! + [{:keys [new-request] :as db}] + (requests/save new-request)) + +(defn add-request + [db [_ chat-id {:keys [message-id content]}]] + (let [request {:chat-id chat-id + :message-id message-id + :type (:command content) + :added (js/Date.)} + request' (update request :type keyword)] + (log/debug "Adding request: " request') + (-> db + (update-in [:chats chat-id :requests] conj request') + (assoc :new-request request)))) + +(defn load-requests! + [{:keys [current-chat-id] :as db} [_ chat-id]] + (let [chat-id' (or chat-id current-chat-id) + ;; todo maybe limit is needed + requests (requests/get-open-by-chat-id chat-id') + requests' (map #(update % :type keyword) requests)] + (assoc-in db [:chats chat-id' :requests] requests'))) + +(defn mark-request-as-answered! + [_ [_ chat-id message-id]] + (requests/mark-as-answered chat-id message-id)) + +(register-handler :add-request + (after store-request!) + add-request) + +(register-handler :load-requests! load-requests!) + +(register-handler :request-answered! + (after (fn [_ [_ chat-id]] + (dispatch [:load-requests! chat-id]))) + (u/side-effect! mark-request-as-answered!)) diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs new file mode 100644 index 0000000000..b6ae06431a --- /dev/null +++ b/src/status_im/chat/handlers/send_message.cljs @@ -0,0 +1,309 @@ +(ns status-im.chat.handlers.send-message + (:require [status-im.utils.handlers :refer [register-handler] :as u] + [clojure.string :as s] + [status-im.data-store.chats :as chats] + [status-im.data-store.messages :as messages] + [status-im.components.status :as status] + [status-im.utils.random :as random] + [status-im.utils.datetime :as time] + [re-frame.core :refer [enrich after dispatch path]] + [status-im.chat.utils :as cu] + [status-im.commands.utils :as commands-utils] + [status-im.constants :refer [console-chat-id + wallet-chat-id + text-content-type + content-type-command + content-type-command-request + default-number-of-messages] :as c] + [status-im.chat.constants :refer [input-height]] + [status-im.utils.datetime :as datetime] + [status-im.protocol.core :as protocol] + [taoensso.timbre :refer-macros [debug] :as log] + [status-im.chat.handlers.console :as console])) + +(defn prepare-command + [identity chat-id clock-value request + {:keys [id preview preview-string params command + to-message handler-data content-type]}] + (let [content (or request {:command (command :name) + :params params})] + {:message-id id + :from identity + :to chat-id + :timestamp (time/now-ms) + :content (assoc content :preview preview-string + :handler-data handler-data + :type (name (:type command))) + :content-type (or content-type + (if request + content-type-command-request + content-type-command)) + :outgoing true + :preview preview-string + :rendered-preview preview + :to-message to-message + :type (:type command) + :has-handler (:has-handler command) + :clock-value (inc clock-value) + :show? true})) + +(register-handler :send-chat-message + (u/side-effect! + (fn [{:keys [current-chat-id current-public-key current-account-id] :as db} + [_ {:keys [chat-id] :as command-message}]] + (let [text (get-in db [:chats current-chat-id :input-text]) + data {:command command-message + :message text + :chat-id (or chat-id current-chat-id) + :identity current-public-key + :address current-account-id}] + (dispatch [:clear-input current-chat-id]) + (cond + command-message + (dispatch [::check-commands-handlers! data]) + (not (s/blank? text)) + (dispatch [::prepare-message data])))))) + +(defn console-command? [chat-id command-name] + (and (= console-chat-id chat-id) + (console/commands-names (keyword command-name)))) + +(register-handler ::check-commands-handlers! + (u/side-effect! + (fn [_ [_ {:keys [command message chat-id] :as params}]] + (let [{:keys [command] :as message} command] + (let [params' (assoc params :command-message message) + command-name (:name (:command message))] + (if (:sent-to-jail? message) + ;; todo there could be other reasons for "long-running" + ;; hanling of the command besides sendTransaction + (dispatch [:navigate-to-modal :confirm]) + (cond + (console-command? chat-id command-name) + (dispatch [:invoke-console-command-handler! params']) + + (:has-handler command) + (dispatch [::invoke-command-handlers! params']) + + :else + (dispatch [:prepare-command! chat-id params']))))) + (when-not (s/blank? message) + (dispatch [::prepare-message params]))))) + +(register-handler :clear-input + (path :chats) + (fn [db [_ chat-id]] + (assoc-in db [chat-id :input-text] nil))) + +(register-handler :prepare-command! + (u/side-effect! + (fn [{:keys [current-public-key network-status] :as db} + [_ add-to-chat-id {:keys [chat-id command-message command handler-data] :as params}]] + (let [clock-value (messages/get-last-clock-value chat-id) + request (:request (:handler-data command)) + command' (->> (assoc command-message :handler-data handler-data) + (prepare-command current-public-key chat-id clock-value request) + (cu/check-author-direction db chat-id))] + (log/debug "Handler data: " request handler-data (dissoc params :commands :command-message)) + (dispatch [:update-message-overhead! chat-id network-status]) + (dispatch [::send-command! add-to-chat-id (assoc params :command command')]) + (when (cu/console? chat-id) + (dispatch `[:console-respond-command params])) + (when (and (= "send" (get-in command-message [:command :name])) + (not= add-to-chat-id wallet-chat-id)) + (let [ct (if request + c/content-type-wallet-request + c/content-type-wallet-command) + command-message' (assoc command-message :id (random/id) + :content-type ct) + params' (assoc params :command-message command-message')] + (dispatch [:prepare-command! wallet-chat-id params']))))))) + +(register-handler ::send-command! + (u/side-effect! + (fn [_ [_ add-to-chat-id params]] + (dispatch [::add-command add-to-chat-id params]) + (dispatch [::save-command! add-to-chat-id params]) + (when (not= add-to-chat-id wallet-chat-id) + (dispatch [::dispatch-responded-requests! params]) + (dispatch [::send-command-protocol! params]))))) + +(register-handler ::add-command + (after (fn [_ [_ _ {:keys [handler]}]] + (when handler (handler)))) + (fn [db [_ add-to-chat-id {:keys [chat-id command]}]] + (cu/add-message-to-db db add-to-chat-id chat-id command))) + +(register-handler ::save-command! + (u/side-effect! + (fn [_ [_ chat-id {:keys [command]}]] + (messages/save + chat-id + (dissoc command :rendered-preview :to-message :has-handler))))) + +(register-handler ::dispatch-responded-requests! + (u/side-effect! + (fn [_ [_ {:keys [command chat-id]}]] + (let [{:keys [to-message]} command] + (when to-message + (dispatch [:request-answered! chat-id to-message])))))) + +(register-handler ::invoke-command-handlers! + (u/side-effect! + (fn [db [_ {:keys [chat-id address command-message] + :as parameters}]] + (let [{:keys [id command params]} command-message + {:keys [type name]} command + path [(if (= :command type) :commands :responses) + name + :handler] + to (get-in db [:contacts chat-id :address]) + params {:parameters params + :context {:from address + :to to + :message-id id}}] + (status/call-jail + chat-id + path + params + #(dispatch [:command-handler! chat-id parameters %])))))) + +(register-handler ::prepare-message + (u/side-effect! + (fn [{:keys [network-status] :as db} [_ {:keys [chat-id identity message] :as params}]] + (let [{:keys [group-chat]} (get-in db [:chats chat-id]) + clock-value (messages/get-last-clock-value chat-id) + message' (cu/check-author-direction + db chat-id + {:message-id (random/id) + :chat-id chat-id + :content message + :from identity + :content-type text-content-type + :outgoing true + :timestamp (time/now-ms) + :clock-value (inc clock-value) + :show? true}) + message'' (if group-chat + (assoc message' :group-id chat-id :message-type :group-user-message) + (assoc message' :to chat-id :message-type :user-message)) + params' (assoc params :message message'')] + (dispatch [:update-message-overhead! chat-id network-status]) + (dispatch [::add-message params']) + (dispatch [::save-message! params']))))) + +(register-handler ::add-message + (fn [db [_ {:keys [chat-id message]}]] + (cu/add-message-to-db db chat-id chat-id message))) + +(register-handler ::save-message! + (after (fn [_ [_ params]] + (dispatch [::send-message! params]))) + (u/side-effect! + (fn [_ [_ {:keys [chat-id message]}]] + (messages/save chat-id message)))) + +(register-handler :clear-response-suggestions + (fn [db [_ chat-id]] + (-> db + (update-in [:suggestions] dissoc chat-id) + (update-in [:has-suggestions?] dissoc chat-id) + (assoc-in [:animations :to-response-height chat-id] input-height)))) + +(register-handler ::send-dapp-message + (u/side-effect! + (fn [db [_ chat-id {:keys [content] :as message}]] + (let [data (get-in db [:local-storage chat-id]) + path [:functions + :message-handler] + params {:parameters {:message content} + :context {:data data}}] + (dispatch [:clear-response-suggestions chat-id]) + (status/call-jail chat-id + path + params + (fn [{:keys [result]}] + (log/debug "Message handler result: " result) + (dispatch [::received-dapp-message chat-id result]))))))) + +(register-handler ::received-dapp-message + (u/side-effect! + (fn [{:keys [current-chat-id] :as db} [_ chat-id {:keys [returned] :as message}]] + (let [{:keys [data messages err]} returned + content (if err + err + data)] + (doseq [message messages] + (let [{:keys [message type]} message] + (dispatch [:received-message + {:message-id (random/id) + :content (str type ": " message) + :content-type text-content-type + :outgoing false + :chat-id chat-id + :from chat-id + :to "me"}]))) + (when content + (dispatch [:received-message + {:message-id (random/id) + :content (str content) + :content-type text-content-type + :outgoing false + :chat-id chat-id + :from chat-id + :to "me"}])))))) + +(register-handler ::send-message! + (u/side-effect! + (fn [{:keys [web3 chats network-status] + :as db} [_ {{:keys [message-type] + :as message} :message + chat-id :chat-id}]] + (let [{:keys [dapp?] :as contact} (get-in db [:contacts chat-id])] + (if dapp? + (dispatch [::send-dapp-message chat-id message]) + (when message + (let [message' (select-keys message [:from :message-id]) + payload (select-keys message [:timestamp :content :content-type + :clock-value :show?]) + payload (if (= network-status :offline) + (assoc payload :show? false) + payload) + options {:web3 web3 + :message (assoc message' :payload payload)}] + (if (= message-type :group-user-message) + (let [{:keys [public-key private-key]} (chats chat-id)] + (protocol/send-group-message! (assoc options + :group-id chat-id + :keypair {:public public-key + :private private-key}))) + (protocol/send-message! (assoc-in options + [:message :to] (:to message))))))))))) + +(register-handler ::send-command-protocol! + (u/side-effect! + (fn [{:keys [web3 current-public-key chats network-status] :as db} + [_ {:keys [chat-id command]}]] + (log/debug "sending command: " command) + (when (cu/not-console? chat-id) + (let [{:keys [public-key private-key]} (chats chat-id) + {:keys [group-chat]} (get-in db [:chats chat-id]) + + payload (-> command + (select-keys [:content :content-type + :clock-value :show?]) + (assoc :timestamp (datetime/now-ms))) + payload (if (= network-status :offline) + (assoc payload :show? false) + payload) + options {:web3 web3 + :message {:from current-public-key + :message-id (:message-id command) + :payload payload}}] + (if group-chat + (protocol/send-group-message! (assoc options + :group-id chat-id + :keypair {:public public-key + :private private-key})) + (protocol/send-message! (assoc-in options + [:message :to] chat-id)))))))) diff --git a/src/status_im/chat/handlers/unviewed_messages.cljs b/src/status_im/chat/handlers/unviewed_messages.cljs new file mode 100644 index 0000000000..45196cf146 --- /dev/null +++ b/src/status_im/chat/handlers/unviewed_messages.cljs @@ -0,0 +1,37 @@ +(ns status-im.chat.handlers.unviewed-messages + (:require [re-frame.core :refer [after enrich path dispatch]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.data-store.messages :as messages])) + +(defn set-unviewed-messages [db] + (let [messages (->> (::raw-unviewed-messages db) + (group-by :chat-id) + (map (fn [[id messages]] + [id {:messages-ids (map :message-id messages) + :count (count messages)}])) + (into {}))] + (-> db + (assoc :unviewed-messages messages) + (dissoc ::raw-unviewed-messages)))) + +(defn load-messages! [db] + (let [messages (messages/get-unviewed)] + (assoc db ::raw-unviewed-messages messages))) + +(register-handler ::set-unviewed-messages set-unviewed-messages) + +(register-handler :load-unviewed-messages! + (after #(dispatch [::set-unviewed-messages])) + load-messages!) + +(register-handler :add-unviewed-message + (path :unviewed-messages) + (fn [db [_ chat-id message-id]] + (-> db + (update-in [chat-id :messages-ids] conj message-id) + (update-in [chat-id :count] inc)))) + +(register-handler :remove-unviewed-messages + (path :unviewed-messages) + (fn [db [_ chat-id]] + (dissoc db chat-id))) \ No newline at end of file diff --git a/src/status_im/chat/handlers/webview_bridge.cljs b/src/status_im/chat/handlers/webview_bridge.cljs new file mode 100644 index 0000000000..b59c96b70a --- /dev/null +++ b/src/status_im/chat/handlers/webview_bridge.cljs @@ -0,0 +1,129 @@ +(ns status-im.chat.handlers.webview-bridge + (:require [re-frame.core :refer [after dispatch enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.utils.handlers :as u] + [status-im.utils.types :as t] + [status-im.i18n :refer [label]] + [taoensso.timbre :as log] + [status-im.models.commands :as commands] + [status-im.commands.utils :as cu] + [status-im.components.status :as s] + [status-im.constants :as c] + [cljs.reader :refer [read-string]] + [status-im.navigation.handlers :as nav])) + +(def web3 (js/require "web3")) + +(defn by-public-key [public-key contacts] + (when-let [{:keys [address]} (contacts public-key)] + (when address {:address address}))) + +(defn scan-qr-handler + [{:keys [contacts]} [_ _ data]] + (let [data' (read-string data) + data'' (cond + (map? data') data' + (.isAddress web3.prototype data') {:address data'} + (string? data') (by-public-key data' contacts) + :else nil)] + (when data'' + (dispatch [:send-to-webview-bridge + {:params data'' + :event (name :webview-send-transaction)}])))) + +(register-handler :webview-address-from-qr + (u/side-effect! scan-qr-handler)) + +(register-handler :set-webview-bridge + (fn [db [_ bridge]] + (assoc db :webview-bridge bridge))) + +(defn contacts-click-handler [{:keys [whisper-identity] :as contact} action params] + (dispatch [:navigate-back]) + (when action + (if (= contact :qr-scan) + (if (= action :send) + (dispatch [:show-scan-qr :webview-address-from-qr]) + (dispatch [:navigate-to-modal :qr-code-view {:qr-source :whisper-identity + :amount? true}])) + (dispatch [:chat-with-command whisper-identity action params])))) + +(register-handler ::send-command + (u/side-effect! + (fn [db [_ command-key params]] + (let [command (commands/get-response-or-command :commands db command-key) + command-input {:content (str cu/command-prefix "0") + :command command + :parameter-idx 0 + :params {"amount" (:amount params)} + :to-message-id nil}] + (dispatch [:validate-command command-input command]))))) + + +(defn chat-with-command + [_ [_ whisper-identity command-key params]] + (dispatch [:remove-contacts-click-handler]) + (dispatch [:add-chat-loaded-callback whisper-identity + #(dispatch [::send-command command-key params])]) + (dispatch [:start-chat whisper-identity])) + +(register-handler :chat-with-command + (u/side-effect! chat-with-command)) + +(register-handler :webview-bridge-message + (u/side-effect! + (fn [_ [_ message-string]] + (let [{:keys [event options] :as message} (t/json->clj message-string) + event' (keyword event) + params (:data options)] + (log/debug (str "message from webview: " message)) + (case event' + :webview-send-transaction + (dispatch [:navigate-to-modal + :contact-list-modal + {:handler contacts-click-handler + :action :send + :params params}]) + :webview-receive-transaction + (dispatch [:navigate-to-modal + :contact-list-modal + {:handler contacts-click-handler + :action :request + :params params}]) + :webview-scan-qr (dispatch [:show-scan-qr :webview-address-from-qr]) + :webview-send-eth (dispatch [:webview-send-eth! params]) + (log/error (str "Unknown event: " event'))))))) + +(defmethod nav/preload-data! :contact-list-modal + [db [_ _ {:keys [handler action params]}]] + (assoc db :contacts-click-handler handler + :contacts-click-action action + :contacts-click-params params + :contacts-filter #(not (nil? (:address %))))) + +(def qr-context {:toolbar-title (label :t/address)}) + +(register-handler :show-scan-qr + (after #(dispatch [:navigate-to-modal :qr-scanner qr-context])) + (fn [db [_ click-handler]] + (assoc-in db [:qr-codes qr-context] click-handler))) + +(register-handler :send-to-webview-bridge + (u/side-effect! + (fn [{:keys [webview-bridge]} [_ data]] + (when webview-bridge + (.sendToBridge webview-bridge (t/clj->json data)))))) + +(register-handler :webview-send-eth! + (u/side-effect! + (fn [{:keys [current-account-id]} [_ {:keys [amount address]}]] + (let [context {:from current-account-id} + path [:functions :send] + parameters {:context context + :parameters {:amount amount + :address address}}] + (s/call-jail c/wallet-chat-id + path + parameters + (fn [data] + (log/debug :webview-send-eth-callback data))))))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs new file mode 100644 index 0000000000..97961baa86 --- /dev/null +++ b/src/status_im/chat/screen.cljs @@ -0,0 +1,214 @@ +(ns status-im.chat.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + animated-view + text + icon + modal + touchable-highlight + list-view + list-item]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.chat-icon.screen :refer [chat-icon-view-action + chat-icon-view-menu-item]] + [status-im.chat.styles.screen :as st] + [status-im.utils.listview :refer [to-datasource-inverted]] + [status-im.utils.utils :refer [truncate-str]] + [status-im.utils.datetime :as time] + [status-im.utils.platform :refer [platform-specific]] + [status-im.components.invertible-scroll-view :refer [invertible-scroll-view]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.chat.views.message :refer [chat-message]] + [status-im.chat.views.datemark :refer [chat-datemark]] + [status-im.chat.views.response :refer [response-view]] + [status-im.chat.views.new-message :refer [chat-message-input-view]] + [status-im.chat.views.actions :refer [actions-view]] + [status-im.chat.views.emoji :refer [emoji-view]] + [status-im.chat.views.bottom-info :refer [bottom-info-view]] + [status-im.chat.views.toolbar-content :refer [toolbar-content-view]] + [status-im.chat.views.suggestions :refer [suggestion-container]] + [status-im.i18n :refer [label label-pluralize]] + [status-im.components.animation :as anim] + [status-im.components.sync-state.offline :refer [offline-view]] + [status-im.constants :refer [content-type-status]] + [reagent.core :as r])) + +(defn contacts-by-identity [contacts] + (->> contacts + (map (fn [{:keys [identity] :as contact}] + [identity contact])) + (into {}))) + +(defn add-message-color [{:keys [from] :as message} contact-by-identity] + (if (= "system" from) + (assoc message :text-color :#4A5258 + :background-color :#D3EEEF) + (let [{:keys [text-color background-color]} (get contact-by-identity from)] + (assoc message :text-color text-color + :background-color background-color)))) + +(defview chat-icon [] + [chat-id [:chat :chat-id] + group-chat [:chat :group-chat] + name [:chat :name] + color [:chat :color]] + ;; TODO stub data ('online' property) + [chat-icon-view-action chat-id group-chat name color true]) + +(defn typing [member] + [view st/typing-view + [view st/typing-background + [text {:style st/typing-text + :font :default} + (str member " " (label :t/is-typing))]]]) + +(defn typing-all [] + [view st/typing-all + ;; TODO stub data + (for [member ["Geoff" "Justas"]] + ^{:key member} [typing member])]) + +(defmulti message-row (fn [{{:keys [type]} :row}] type)) + +(defmethod message-row :datemark + [{{:keys [value]} :row}] + (list-item [chat-datemark value])) + +(defmethod message-row :default + [{:keys [contact-by-identity group-chat messages-count row index]}] + (let [message (-> row + (add-message-color contact-by-identity) + (assoc :group-chat group-chat) + (assoc :messages-count messages-count) + (assoc :index index) + (assoc :last-message (= (js/parseInt index) (dec messages-count))))] + (list-item [chat-message message]))) + +(defn toolbar-action [] + (let [show-actions (subscribe [:chat-ui-props :show-actions?])] + (fn [] + (if @show-actions + [touchable-highlight + {:on-press #(dispatch [:set-chat-ui-props :show-actions? false])} + [view st/action + [icon :up st/up-icon]]] + [touchable-highlight + {:on-press #(dispatch [:set-chat-ui-props :show-actions? true])} + [view st/action + [chat-icon]]])))) + +(defview add-contact-bar [] + [pending-contact? [:chat :pending-contact?] + chat-id [:get :current-chat-id]] + (when pending-contact? + [touchable-highlight + {:on-press #(dispatch [:add-pending-contact chat-id])} + [view st/add-contact + [text {:style st/add-contact-text} + (label :t/add-to-contacts)]]])) + +(defview chat-toolbar [] + [show-actions? [:chat-ui-props :show-actions?] + accounts [:get :accounts]] + [view + [status-bar] + [toolbar {:hide-nav? (or (empty? accounts) show-actions?) + :custom-content [toolbar-content-view] + :custom-action [toolbar-action] + :style (get-in platform-specific [:component-styles :toolbar])}] + [add-contact-bar]]) + +(defn get-intro-status-message [all-messages] + (let [{:keys [timestamp content-type] :as last-message} (last all-messages)] + (when (not= content-type content-type-status) + {:message-id "intro-status" + :content-type content-type-status + :timestamp (or timestamp (time/now-ms))}))) + +(defn messages-with-timemarks [all-messages extras] + (let [status-message (get-intro-status-message all-messages) + all-messages (if status-message + (concat all-messages [status-message]) + all-messages) + messages (->> all-messages + (map #(merge % (get extras (:message-id %)))) + (remove #(false? (:show? %))) + (sort-by :clock-value >) + (map #(assoc % :datemark (time/day-relative (:timestamp %)))) + (group-by :datemark) + (map (fn [[k v]] [v {:type :datemark :value k}])) + (flatten)) + remove-last? (some (fn [{:keys [content-type]}] + (= content-type content-type-status)) + messages)] + (if remove-last? + (drop-last messages) + messages))) + +(defview messages-view [group-chat] + [messages [:chat :messages] + contacts [:chat :contacts] + message-extras [:get :message-extras] + loaded? [:all-messages-loaded?]] + (let [contacts' (contacts-by-identity contacts) + messages (messages-with-timemarks messages message-extras)] + [list-view {:renderRow (fn [row _ index] + (message-row {:contact-by-identity contacts' + :group-chat group-chat + :messages-count (count messages) + :row row + :index index})) + :renderScrollComponent #(invertible-scroll-view (js->clj %)) + :onEndReached (when-not loaded? #(dispatch [:load-more-messages])) + :enableEmptySections true + :keyboardShouldPersistTaps true + :dataSource (to-datasource-inverted messages)}])) + +(defn messages-container-animation-logic + [{:keys [offset val]}] + (fn [_] + (anim/start (anim/spring val {:toValue @offset})))) + +(defview messages-container [messages] + [offset [:messages-offset] + messages-offset (anim/create-value 0) + context {:offset offset + :val messages-offset} + on-update (messages-container-animation-logic context)] + {:component-did-mount on-update + :component-did-update on-update} + [animated-view + {:style (st/messages-container messages-offset)} + messages]) + +(defview chat [] + [group-chat [:chat :group-chat] + show-actions? [:chat-ui-props :show-actions?] + show-bottom-info? [:chat-ui-props :show-bottom-info?] + show-emoji? [:chat-ui-props :show-emoji?] + command? [:command?] + layout-height [:get :layout-height]] + {:component-did-mount #(dispatch [:check-autorun])} + [view {:style st/chat-view + :onLayout (fn [event] + (let [height (.. event -nativeEvent -layout -height)] + (when (not= height layout-height) + (dispatch [:set-layout-height height]))))} + [chat-toolbar] + [messages-container + [messages-view group-chat]] + ;; todo uncomment this + #_(when @group-chat [typing-all]) + (when-not command? + [suggestion-container]) + [response-view] + (when show-emoji? + [emoji-view]) + [chat-message-input-view] + (when show-actions? + [actions-view]) + (when show-bottom-info? + [bottom-info-view]) + [offline-view {:top (get-in platform-specific + [:component-styles :status-bar :default :height])}]]) diff --git a/src/status_im/chat/sign_up.cljs b/src/status_im/chat/sign_up.cljs new file mode 100644 index 0000000000..960f189c94 --- /dev/null +++ b/src/status_im/chat/sign_up.cljs @@ -0,0 +1,192 @@ +(ns status-im.chat.sign-up + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.styles :refer [default-chat-color]] + [status-im.utils.utils :refer [http-post]] + [status-im.utils.random :as random] + [status-im.utils.sms-listener :refer [add-sms-listener + remove-sms-listener]] + [status-im.utils.phone-number :refer [format-phone-number]] + [status-im.constants :refer [console-chat-id + text-content-type + content-type-command + content-type-command-request + content-type-status]] + [status-im.i18n :refer [label]] + [clojure.string :as s])) + +(defn send-console-message [text] + {:message-id (random/id) + :from "me" + :to console-chat-id + :content text + :content-type text-content-type + :outgoing true}) + +; todo fn name is not too smart, but... +(defn command-content + [command content] + {:command (name command) + :content content}) + +;; -- Send phone number ---------------------------------------- +(defn on-sign-up-response [& [message]] + (let [message-id (random/id)] + (dispatch [:received-message + {:message-id message-id + :content (command-content + :confirmation-code + (or message (label :t/confirmation-code))) + :content-type content-type-command-request + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]))) + +(defn handle-sms [{body :body}] + (when-let [matches (re-matches #"(\d{4})" body)] + (dispatch [:sign-up-confirm (second matches)]))) + +(defn start-listening-confirmation-code-sms [db] + (if (not (:confirmation-code-sms-listener db)) + (assoc db :confirmation-code-sms-listener (add-sms-listener handle-sms)) + db)) + +(defn stop-listening-confirmation-code-sms [db] + (when-let [listener (:confirmation-code-sms-listener db)] + (remove-sms-listener listener) + (dissoc db :confirmation-code-sms-listener))) + +;; -- Send confirmation code and synchronize contacts--------------------------- +(defn on-sync-contacts [] + (dispatch [:received-message + {:message-id (random/id) + :content (label :t/contacts-syncronized) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]) + (dispatch [:set-signed-up true])) + +(defn sync-contacts [] + ;; TODO 'on-sync-contacts' is never called + (dispatch [:sync-contacts on-sync-contacts])) + +(defn on-send-code-response [body] + (dispatch [:received-message + {:message-id (random/id) + :content (:message body) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]) + (let [status (keyword (:status body))] + (when (= :confirmed status) + (do + (dispatch [:stop-listening-confirmation-code-sms]) + (sync-contacts) + ;; TODO should be called after sync-contacts? + (dispatch [:set-signed-up true]))) + (when (= :failed status) + (on-sign-up-response (label :t/incorrect-code))))) + +(defn start-signup [] + (let [message-id (random/id)] + (dispatch [:received-message + {:message-id message-id + :content (command-content + :phone + (label :t/phone-number-required)) + :content-type content-type-command-request + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}])) + (dispatch [:received-message + {:message-id (random/id) + :content (label :t/shake-your-phone) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}])) + +(def crazy-math-message "crazy-math-message") +;; -- Saving password ---------------------------------------- +(defn account-generation-message [] + (dispatch [:received-message + {:message-id crazy-math-message + :content (label :t/account-generation-message) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}])) + +(def passphraze-message-id "passphraze-message") + +(defn passphrase-messages [mnemonic crazy-math-message?] + (dispatch [:received-message + {:message-id passphraze-message-id + :content (if crazy-math-message? + (label :t/phew-here-is-your-passphrase) + (label :t/here-is-your-passphrase)) + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]) + (dispatch [:received-message + {:message-id (random/id) + :content mnemonic + :content-type text-content-type + :outgoing false + :chat-id console-chat-id + :from console-chat-id + :to "me"}]) + (start-signup)) + +(def intro-status + {:message-id "intro-status" + :content (label :t/intro-status) + :from console-chat-id + :chat-id console-chat-id + :content-type content-type-status + :outgoing false + :to "me"}) + +(defn intro [] + (dispatch [:received-message intro-status]) + (dispatch [:received-message-when-commands-loaded + console-chat-id + {:chat-id console-chat-id + :message-id "intro-message1" + :content (command-content + :password + (label :t/intro-message1)) + :content-type content-type-command-request + :outgoing false + :from console-chat-id + :to "me"}])) + +(def console-chat + {:chat-id console-chat-id + :name (s/capitalize console-chat-id) + :color default-chat-color + :group-chat false + :is-active true + :timestamp (.getTime (js/Date.)) + :photo-path console-chat-id + :contacts [{:identity console-chat-id + :text-color "#FFFFFF" + :background-color "#AB7967"}]}) + +(def console-contact + {:whisper-identity console-chat-id + :name (s/capitalize console-chat-id) + :photo-path console-chat-id + :dapp? true + ; todo remove/change dapp config fot console + :dapp-url "http://localhost:8185/resources" + :dapp-hash 858845357}) diff --git a/src/status_im/chat/styles/command_pill.cljs b/src/status_im/chat/styles/command_pill.cljs new file mode 100644 index 0000000000..5d6b0e9163 --- /dev/null +++ b/src/status_im/chat/styles/command_pill.cljs @@ -0,0 +1,15 @@ +(ns status-im.chat.styles.command-pill + (:require [status-im.utils.platform :as p] + [status-im.components.styles :refer [color-white]])) + +(defn pill [command] + {:backgroundColor (:color command) + :height 24 + :borderRadius 50 + :padding-top (if p/ios? 4 3) + :paddingHorizontal 12 + :text-align :left}) + +(def pill-text + {:fontSize 12 + :color color-white}) diff --git a/src/status_im/chat/styles/command_validation.cljs b/src/status_im/chat/styles/command_validation.cljs new file mode 100644 index 0000000000..341d243f55 --- /dev/null +++ b/src/status_im/chat/styles/command_validation.cljs @@ -0,0 +1,17 @@ +(ns status-im.chat.styles.command-validation + (:require [status-im.components.styles :as st] + [status-im.chat.constants :as constants])) + +(def messages-container + {:background-color :#d50000 + :height constants/request-info-height + :padding-left 16 + :padding-top 12}) + +(def title + {:color :white + :font-size 14}) + +(def description + (assoc title :opacity 0.9 + :font-size 12)) diff --git a/src/status_im/chat/styles/content_suggestions.cljs b/src/status_im/chat/styles/content_suggestions.cljs new file mode 100644 index 0000000000..869c0b68b5 --- /dev/null +++ b/src/status_im/chat/styles/content_suggestions.cljs @@ -0,0 +1,44 @@ +(ns status-im.chat.styles.content-suggestions + (:require [status-im.components.styles :refer [color-light-blue-transparent + color-white + color-black + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text2-color + text3-color]])) + +(def suggestion-height 56) + +(def suggestion-container + {:paddingLeft 16 + :backgroundColor color-white}) + +(def suggestion-sub-container + {:height suggestion-height + :borderBottomWidth 1 + :borderBottomColor separator-color}) + +(def value-text + {:marginTop 9 + :fontSize 14 + :color text1-color}) + +(def description-text + {:marginTop 1.5 + :fontSize 14 + :color text2-color}) + +(defn suggestions-container [suggestions-count] + {:flex 1 + :marginVertical 1 + :marginHorizontal 0 + :height (min 150 (* suggestion-height suggestions-count)) + :backgroundColor color-white + :borderRadius 5}) + +(def container + {:backgroundColor color-white}) diff --git a/src/status_im/chat/styles/datemark.cljs b/src/status_im/chat/styles/datemark.cljs new file mode 100644 index 0000000000..c311ef4467 --- /dev/null +++ b/src/status_im/chat/styles/datemark.cljs @@ -0,0 +1,19 @@ +(ns status-im.chat.styles.datemark) + +(def datemark-wrapper + {:flex 1 + :align-items :center}) + +(def datemark + {:background-color "#bbc4cb33" + :padding-left 12 + :padding-right 12 + :margin-top 8 + :margin-bottom 8 + :border-radius 12 + :height 24}) + +(def datemark-text + {:color "#838c93" + :top 4 + :font-size 12}) diff --git a/src/status_im/chat/styles/dragdown.cljs b/src/status_im/chat/styles/dragdown.cljs new file mode 100644 index 0000000000..d4d974ac21 --- /dev/null +++ b/src/status_im/chat/styles/dragdown.cljs @@ -0,0 +1,12 @@ +(ns status-im.chat.styles.dragdown + (:require [status-im.components.styles :refer [color-white]])) + +(def drag-down-touchable + {:height 22 + :background-color color-white + :alignItems :center + :justifyContent :center}) + +(def drag-down-icon + {:width 16 + :height 16}) diff --git a/src/status_im/chat/styles/emoji.cljs b/src/status_im/chat/styles/emoji.cljs new file mode 100644 index 0000000000..0c17d7236b --- /dev/null +++ b/src/status_im/chat/styles/emoji.cljs @@ -0,0 +1,23 @@ +(ns status-im.chat.styles.emoji + (:require [status-im.components.styles :refer [color-white]] + [status-im.chat.constants :refer [emoji-container-height]] + [taoensso.timbre :as log])) + +(def container-height emoji-container-height) + +(defn container [height] + {:flexDirection :column + :position :absolute + :left 0 + :right 0 + :bottom 0 + :height (or height emoji-container-height) + :backgroundColor color-white + :elevation 5}) + +(def emoji-container + {:flex 1}) + +(def emoji-picker + {:flex 1 + :background-color color-white}) diff --git a/src/status_im/chat/styles/input.cljs b/src/status_im/chat/styles/input.cljs new file mode 100644 index 0000000000..f50a2c4e92 --- /dev/null +++ b/src/status_im/chat/styles/input.cljs @@ -0,0 +1,72 @@ +(ns status-im.chat.styles.input + (:require [status-im.components.styles :refer [color-white + color-blue + text1-color + text2-color + chat-background + color-black]])) + +(def command-input-and-suggestions-container + {:flexDirection :column}) + +(def command-input-container + {:flexDirection :row + :height 56 + :backgroundColor color-white}) + +(def command-container + {:left 0 + :backgroundColor :white + :position :absolute}) + +(defn command-text-container + [{:keys [color]}] + {:flexDirection :column + :marginTop 16 + :marginBottom 16 + :marginLeft 16 + :marginRight 8 + :backgroundColor color + :height 24 + :borderRadius 50}) + +(def command-text + {:marginTop 3 + :marginHorizontal 12 + :fontSize 12 + :color color-white}) + +(def command-input + {:flex 1 + :marginLeft 8 + :marginTop -2 + :padding 0 + :fontSize 14 + :color text1-color}) + +(def send-container + {:marginTop 10 + :marginRight 10 + :width 36 + :height 36 + :borderRadius 50 + :backgroundColor color-blue}) + +(def send-icon + {:marginTop 10.5 + :marginLeft 12 + :width 15 + :height 15}) + +(def cancel-container + {:marginTop 10 + :marginRight 10 + :width 36 + :height 36}) + +(def cancel-icon + {:marginTop 10.5 + :marginLeft 12 + :width 12 + :height 12}) + diff --git a/src/status_im/chat/styles/message.cljs b/src/status_im/chat/styles/message.cljs new file mode 100644 index 0000000000..9823cc765c --- /dev/null +++ b/src/status_im/chat/styles/message.cljs @@ -0,0 +1,273 @@ +(ns status-im.chat.styles.message + (:require [status-im.components.styles :refer [color-light-blue-transparent + color-white + color-black + color-blue + selected-message-color + online-color + text1-color + text2-color]] + [status-im.utils.platform :as p] + [status-im.constants :refer [text-content-type + content-type-command]])) + +(def style-message-text + {:fontSize 14 + :lineHeight 21 + :color text1-color}) + +(def style-sub-text + {:top -2 + :fontSize 12 + :color text2-color + :lineHeight 14 + :height 16}) + +(defn message-padding-top + [{:keys [new-day same-author same-direction]}] + (cond + new-day 0 + same-author 4 + same-direction 20 + :else 10)) + +(defn last-message-padding + [{:keys [last-message typing]}] + (when (and last-message (not typing)) + {:paddingBottom 20})) + +(def message-datemark + {:margin-top 10 + :height 34}) + +(def message-empty-spacing + {:height 16}) + +(def message-body-base + {:padding-right 8 + :padding-left 8}) + +(defn message-body + [{:keys [outgoing] :as message}] + (let [align (if outgoing :flex-end :flex-start)] + (merge message-body-base + {:flexDirection :column + :width 260 + :paddingTop (message-padding-top message) + :alignSelf align + :alignItems align} + (last-message-padding message)))) + +(defn incoming-group-message-body-st + [message] + (merge message-body-base + {:flexDirection :row + :alignSelf :flex-start + :marginTop (message-padding-top message) + :paddingRight 8 + :paddingLeft 8} + (last-message-padding message))) + +(def selected-message + {:marginTop 18 + :marginLeft 40 + :fontSize 12 + :color text2-color}) + +(def group-message-wrapper + {:flexDirection :column}) + +(def group-message-view + {:flexDirection :column + :width 260 + :paddingLeft 8 + :alignItems :flex-start}) + +(def message-author {:width 24}) + +(def photo-view {:borderRadius 12}) +(def photo + {:borderRadius 12 + :width 24 + :height 24}) + +(def delivery-view + {:flexDirection :row + :marginTop 2}) + +(def delivery-image + {:marginTop 6 + :width 9 + :height 7}) + +(def delivery-text + {:fontSize 12 + :color text2-color + :marginLeft 5}) + +(defn text-message + [{:keys [outgoing group-chat incoming-group]}] + (merge style-message-text + {:marginTop (if incoming-group + 4 + 0)} + (when (and outgoing group-chat) + {:color color-white}))) + +(defn message-view + [{:keys [content-type outgoing group-chat selected]}] + (merge {:borderRadius 14 + :padding 12 + :backgroundColor color-white} + (when (= content-type content-type-command) + {:paddingTop 10 + :paddingBottom 14}) + (if outgoing + (when (and group-chat (= content-type text-content-type)) + {:backgroundColor color-blue}) + (when selected + {:backgroundColor selected-message-color})))) + +(def comand-request-view + {:paddingRight 16}) + +(def command-request-message-view + {:borderRadius 14 + :padding 12 + :paddingRight 28 + :backgroundColor color-white}) + +(def command-request-from-text + (merge style-sub-text {:marginBottom 2})) + +(defn command-request-image-touchable [top-offset?] + {:position :absolute + :top (if top-offset? 4 -1) + :right -8 + :alignItems :center + :justifyContent :center + :width 48 + :height 48}) + +(defn command-request-image-view [command scale] + {:width 32 + :height 32 + :borderRadius 16 + :backgroundColor (:color command) + :transform [{:scale scale}]}) + +(def command-image-view + {:position :absolute + :top 0 + :right 0 + :width 24 + :height 24 + :alignItems :center}) + +(def command-request-image + {:position :absolute + :top 9 + :left 10 + :width 12 + :height 13}) + +(def command-request-text-view + {:marginTop 4 + :height 14}) + +(def content-command-view + {:flexDirection :column}) + +(def command-container + {:flexDirection :row + :marginRight 32}) + +(def command-image + {:margin-top 5 + :width 12 + :height 13 + :tint-color :#a9a9a9cc}) + +(def command-text + (merge style-message-text + {:marginTop 8 + :marginHorizontal 0})) + +(def audio-container + {:flexDirection :row + :alignItems :center}) + +(def play-view + {:width 33 + :height 33 + :borderRadius 16 + :elevation 1}) + +(def play-image + {:width 33 + :height 33}) + +(def track-container + {:marginTop 10 + :marginLeft 10 + :width 120 + :height 26 + :elevation 1}) + +(def track + {:position :absolute + :top 4 + :width 120 + :height 2 + :backgroundColor :#EC7262}) + +(def track-mark + {:position :absolute + :left 0 + :top 0 + :width 2 + :height 10 + :backgroundColor :#4A5258}) + +(def track-duration-text + {:position :absolute + :left 1 + :top 11 + :fontSize 11 + :color :#4A5258 + :letterSpacing 1 + :lineHeight 15}) + +(def status-container + {:flex 1 + :alignSelf :center + :alignItems :center + :width 249 + :padding-bottom 16}) + +(def status-image-view + {:marginTop 20}) + +(def status-from + {:marginTop 20 + :fontSize 18 + :color text1-color}) + +(def status-text + {:marginTop 10 + :fontSize 14 + :lineHeight 20 + :textAlign :center + :color text2-color}) + +(defn message-animated-container [height] + {:height height}) + +(defn message-container [window-width] + {:position :absolute + :width window-width}) + +(defn new-message-container [margin on-top?] + {:background-color color-white + :margin-bottom margin + :elevation (if on-top? 6 5)}) diff --git a/src/status_im/chat/styles/message_input.cljs b/src/status_im/chat/styles/message_input.cljs new file mode 100644 index 0000000000..711480f515 --- /dev/null +++ b/src/status_im/chat/styles/message_input.cljs @@ -0,0 +1,49 @@ +(ns status-im.chat.styles.message-input + (:require [status-im.components.styles :refer [color-white + color-blue]] + [status-im.chat.constants :refer [max-input-height + min-input-height + input-spacing-top + input-spacing-bottom]] + [status-im.utils.platform :as p])) + +(def input-container + {:flex-direction :column}) + +(defn input-view [content-height] + {:flex-direction :row + :align-items :center + :justify-content :center + :height (+ (min (max min-input-height content-height) max-input-height) + input-spacing-top + input-spacing-bottom) + :background-color color-white}) + +(defn message-input-container [content-height] + {:height (min (max min-input-height content-height) max-input-height) + :margin-top input-spacing-top + :margin-bottom input-spacing-bottom + :flex 1 + :flex-direction "column" + :margin-right 0}) + +(def send-wrapper + {:margin-top 10 + :margin-right 10 + :width 36 + :flex 1 + :flex-direction "column" + :align-items :center + :justify-content :flex-start}) + +(def send-container + {:width 36 + :height 36 + :border-radius 18 + :background-color color-blue}) + +(def send-icon + {:margin-top 10.5 + :margin-left 12 + :width 15 + :height 15}) \ No newline at end of file diff --git a/src/status_im/chat/styles/plain_message.cljs b/src/status_im/chat/styles/plain_message.cljs new file mode 100644 index 0000000000..4d44152937 --- /dev/null +++ b/src/status_im/chat/styles/plain_message.cljs @@ -0,0 +1,58 @@ +(ns status-im.chat.styles.plain-message + (:require [status-im.components.styles :refer [text1-color]] + [status-im.chat.constants :refer [max-input-height + min-input-height]] + [status-im.utils.platform :as p])) + +(defn message-input-button-touchable [width content-height] + {:width width + :flex 1}) + +(defn message-input-button [scale margin-top] + {:transform [{:scale scale}] + :width 24 + :height 24 + :margin-top margin-top + :margin-left 16 + :align-items :center + :justify-content :center}) + +(def list-icon + {:margin-left 4 + :margin-top 5.5 + :margin-bottom 5.5 + :margin-right 4 + :width 16 + :height 13}) + +(def requests-icon-container + {:width 12 + :height 12 + :border-radius 12 + :left -1 + :top -1 + :background-color :white + :position :absolute}) + +(def requests-icon + {:background-color :#7099e6 + :margin 2 + :width 8 + :height 8 + :border-radius 8}) + +(def close-icon + {:width 12 + :height 12}) + +(def message-input + {:flex 1 + :padding 0 + :font-size 14 + :line-height 20 + :color text1-color + :padding-top 0}) + +(def smile-icon + {:width 20 + :height 20}) diff --git a/src/status_im/chat/styles/response.cljs b/src/status_im/chat/styles/response.cljs new file mode 100644 index 0000000000..453bc43589 --- /dev/null +++ b/src/status_im/chat/styles/response.cljs @@ -0,0 +1,92 @@ +(ns status-im.chat.styles.response + (:require [status-im.components.styles :refer [color-white + color-blue + text1-color + text2-color + chat-background + color-black]] + [status-im.chat.constants :refer [input-height + request-info-height + response-height-normal]] + [status-im.utils.platform :as p])) + +(def drag-container + {:height 16 + :alignItems :center + :justifyContent :center}) + +(def drag-icon + {:width 14 + :height 3}) + +(def command-icon-container + {:marginTop 1 + :marginLeft 12 + :width 32 + :height 32 + :alignItems :center + :justifyContent :center + :borderRadius 16 + :backgroundColor color-white}) + +(defn command-icon [color] + {:width 13 + :height 13 + :tint-color color}) + +(def info-container + {:flex 1 + :marginLeft 12}) + +(def command-name + {:marginTop 0 + :fontSize 12 + :color color-white}) + +(def message-info + {:marginTop 1 + :fontSize 12 + :opacity 0.69 + :color color-white}) + +(defn response-view [height input-margin] + {:flexDirection :column + :position :absolute + :elevation 4 + :left 0 + :right 0 + :bottom input-margin + :height height + :backgroundColor color-white}) + +(def input-placeholder + {:height input-height}) + +(defn request-info [color] + {:flexDirection :column + :height request-info-height + :backgroundColor color}) + +(def inner-container + {:flexDirection :row + :height 45}) + +(def cancel-container + {:marginTop 2.5 + :marginRight 16 + :width 24 + :height 24}) + +(def cancel-icon + {:marginTop 6 + :marginLeft 6 + :width 12 + :height 12}) + +(defn command-input [ml disable?] + {:flex 1 + :margin-right 16 + :margin-left (- ml 5) + :padding 0 + :font-size 14 + :color (if disable? color-white text1-color)}) diff --git a/src/status_im/chat/styles/screen.cljs b/src/status_im/chat/styles/screen.cljs new file mode 100644 index 0000000000..95d138bb26 --- /dev/null +++ b/src/status_im/chat/styles/screen.cljs @@ -0,0 +1,212 @@ +(ns status-im.chat.styles.screen + (:require [status-im.components.styles :refer [color-white + color-black + chat-background + online-color + selected-message-color + separator-color + text1-color + text2-color]] + [status-im.components.toolbar.styles :refer [toolbar-background1]])) + +(def chat-view + {:flex 1 + :background-color chat-background}) + +(def toolbar-container + {}) + +(defn messages-container [bottom] + {:flex 1 + :padding-bottom bottom + :margin-bottom 0}) + +(def toolbar-view + {:flexDirection :row + :height 56 + :backgroundColor toolbar-background1 + :elevation 2}) + +(def action + {:width 56 + :height 56 + :top 0 + :alignItems :center + :justifyContent :center}) + +(def icon-view + {:width 56 + :height 56}) + +(def back-icon + {:marginTop 21 + :marginLeft 23 + :width 8 + :height 14}) + +(defn chat-name-view [show-actions] + {:flex 1 + :margin-bottom 2 + :margin-left (if show-actions 16 0) + :align-items :flex-start + :justify-content :center}) + +(def chat-name-text + {:color text1-color + :margin-top 2 + :fontSize 16}) + +(def group-icon + {:marginTop 4 + :width 14 + :height 9}) + +(def up-icon + {:width 14 + :height 8}) + +(def members + {:marginTop -0.5 + :marginLeft 4 + :fontSize 12 + :color text2-color}) + +(def last-activity + {:height 18}) + +(defn actions-wrapper [status-bar-height] + {:backgroundColor toolbar-background1 + :elevation 2 + :position :absolute + :top (+ 55 status-bar-height) + :left 0 + :right 0}) + +(def actions-separator + {:marginLeft 16 + :height 1.5 + :backgroundColor separator-color}) + +(def actions-view + {:marginVertical 10}) + +(def action-icon-row + {:flexDirection :row + :height 56}) + +(def action-icon-view + (merge icon-view + {:alignItems :center + :justifyContent :center})) + +(def action-view + {:flex 1 + :alignItems :flex-start + :justifyContent :center}) + +(def action-title + {:margin-top -2.5 + :color text1-color + :font-size 14}) + +(def action-subtitle + {:margin-top 1 + :color text2-color + :font-size 12}) + +(def actions-overlay + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0}) + +(def typing-all + {:marginBottom 20}) + +(def typing-view + {:width 260 + :marginTop 10 + :paddingLeft 8 + :paddingRight 8 + :alignItems :flex-start + :alignSelf :flex-start}) + +(def typing-background + {:borderRadius 14 + :padding 12 + :height 38 + :backgroundColor selected-message-color}) + +(def typing-text + {:marginTop -2 + :fontSize 12 + :color text2-color}) + +(def overlay-highlight + {:flex 1}) + +;; this map looks a bit strange +;; but this way of setting elevation seems to be the only way to set z-index (in RN 0.30) +(def bottom-info-overlay + {:position :absolute + :top -16 + :bottom -16 + :left -16 + :right -16 + :background-color "#00000055" + :elevation 8}) + +(defn bottom-info-container [height] + {:backgroundColor toolbar-background1 + :elevation 2 + :position :absolute + :bottom 16 + :left 16 + :right 16 + :height height}) + +(def bottom-info-list-container + {:padding-left 16 + :padding-right 16 + :padding-top 8 + :padding-bottom 8}) + +(def item-height 60) + +(def bottom-info-row + {:flex-direction "row" + :padding-top 4 + :padding-bottom 4}) + +(def bottom-info-row-photo + {:width 42 + :height 42 + :borderRadius 21}) + +(def bottom-info-row-text-container + {:margin-left 16 + :margin-right 16}) + +(def bottom-info-row-text1 + {:color "black"}) + +(def bottom-info-row-text2 + {:color "#888888"}) + +(def chat-modal + {:position :absolute + :left 0 + :top 0 + :right 0 + :bottom 0}) + +(def add-contact + {:height 35 + :background-color :white + :justify-content :center}) + +(def add-contact-text + {:text-align :center + :text-align-vertical :center + :color :#7099e6}) diff --git a/src/status_im/chat/styles/suggestions.cljs b/src/status_im/chat/styles/suggestions.cljs new file mode 100644 index 0000000000..1f8c9d01df --- /dev/null +++ b/src/status_im/chat/styles/suggestions.cljs @@ -0,0 +1,119 @@ +(ns status-im.chat.styles.suggestions + (:require [status-im.components.styles :refer [color-light-blue-transparent + color-white + color-black + color-gray + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text2-color + text3-color]] + [taoensso.timbre :as log])) + +(def suggestion-height 60) + +(def suggestion-highlight + {:margin-left 57}) + +(def suggestion-container + {:backgroundColor color-white}) + +(def suggestion-sub-container + {:height suggestion-height + :borderBottomWidth 1 + :borderBottomColor separator-color + :flex-direction :row}) + +(def command-description-container + {:flex 0.6}) + +(def command-label-container + {:flex 0.4 + :flex-direction :column + :align-items :flex-end + :margin-right 16}) + +(defn suggestion-background + [{:keys [color]}] + {:marginTop 10 + :height 24 + :backgroundColor color + :borderRadius 50}) + +(def suggestion-text + {:marginTop 2.5 + :marginHorizontal 12 + :fontSize 12 + :color color-white}) + +(def title-container + {:margin-left 57 + :margin-bottom 16}) + +(def title-text + {:font-size 13 + :color :#8f838c93}) + +(def value-text + {:marginTop 6 + :fontSize 14 + :color text1-color}) + +(def description-text + {:marginTop 2 + :fontSize 12 + :color text2-color}) + +(defn container [height input-margin] + {:flexDirection :column + :position :absolute + :left 0 + :right 0 + :bottom input-margin + :height height + :backgroundColor color-white + :elevation 4}) + +(def request-container + {:height 56 + :flex-direction :row}) + +(def request-icon-container + {:height 56 + :width 57 + :align-items :center + :justifyContent :center}) + +(defn request-icon-background [color] + {:height 32 + :width 32 + :border-radius 32 + :background-color color + :align-items :center + :justifyContent :center}) + +(def request-icon + {:width 12 + :height 12}) + +(def request-info-container + {:height 56 + :padding-top 10}) + +(def request-info-description + {:font-size 12 + :color color-black}) + +(def request-message-info + {:font-size 12 + :margin-top 2 + :color color-gray}) + +(def header-icon + {:background-color :#838c93 + :width 14 + :border-radius 1 + :height 3}) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs new file mode 100644 index 0000000000..24e97eafe9 --- /dev/null +++ b/src/status_im/chat/subs.cljs @@ -0,0 +1,269 @@ +(ns status-im.chat.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub dispatch subscribe path]] + [status-im.utils.platform :refer [ios?]] + [status-im.models.commands :as commands] + [status-im.data-store.chats :as chats] + [status-im.constants :refer [response-suggesstion-resize-duration]] + [status-im.chat.constants :as c] + [status-im.chat.views.plain-message :as plain-message] + [status-im.chat.views.command :as command] + [status-im.constants :refer [content-type-status]] + [status-im.utils.platform :refer [platform-specific]])) + +(register-sub :chat-properties + (fn [db [_ properties]] + (->> properties + (map (fn [k] + [k (-> @db + (get-in [:chats (:current-chat-id @db) k]) + (reaction))])) + (into {})))) + +(register-sub + :chat-ui-props + (fn [db [_ ui-element]] + (reaction (get-in @db [:chat-ui-props ui-element])))) + +(register-sub :chat + (fn [db [_ k]] + (-> @db + (get-in [:chats (:current-chat-id @db) k]) + (reaction)))) + +(register-sub :get-current-chat-id + (fn [db _] + (reaction (:current-chat-id @db)))) + +(register-sub :get-suggestions + (fn [db _] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:command-suggestions @chat-id]))))) + +(register-sub :get-commands + (fn [db [_ chat-id]] + (let [current-chat (or chat-id (@db :current-chat-id))] + (reaction (or (get-in @db [:chats current-chat :commands]) {}))))) + +(register-sub :get-chat-by-id + (fn [_ [_ chat-id]] + (reaction (chats/get-by-id chat-id)))) + +(register-sub :get-responses + (fn [db [_ chat-id]] + (let [current-chat (or chat-id (@db :current-chat-id))] + (reaction (or (get-in @db [:chats current-chat :responses]) {}))))) + +(register-sub :get-commands-and-responses + (fn [db [_ chat-id]] + (reaction (or (->> (get-in @db [:chats chat-id]) + ((juxt :commands :responses)) + (apply merge)) + (->> (get @db :all-commands) + ((juxt :commands :responses)) + (apply merge)))))) + +(register-sub :get-chat-input-text + (fn [db _] + (->> [:chats (:current-chat-id @db) :input-text] + (get-in @db) + (reaction)))) + +(register-sub :get-message-input-view-height + (fn [db _] + (reaction (get-in @db [:chats (:current-chat-id @db) :message-input-height])))) + +(register-sub :valid-plain-message? + (fn [_ _] + (let [input-message (subscribe [:get-chat-input-text])] + (reaction + (plain-message/message-valid? @input-message))))) + +(register-sub :valid-command? + (fn [_ [_ validator]] + (let [input (subscribe [:get-chat-command-content])] + (reaction (command/valid? @input validator))))) + +(register-sub :get-chat-command + (fn [db _] + (reaction (commands/get-chat-command @db)))) + +(register-sub :get-command-parameter + (fn [db] + (let [command (subscribe [:get-chat-command]) + chat-id (subscribe [:get-current-chat-id])] + (reaction + (let [parameter-index (commands/get-command-parameter-index @db @chat-id)] + (when parameter-index (nth (:params @command) parameter-index))))))) + +(register-sub :get-chat-command-content + (fn [db _] + (reaction (commands/get-chat-command-content @db)))) + +(register-sub :get-chat-command-to-message-id + (fn [db _] + (reaction (commands/get-chat-command-to-message-id @db)))) + +(register-sub :chat-command-request + (fn [db _] + (reaction (commands/get-chat-command-request @db)))) + +(register-sub :get-current-chat + (fn [db _] + (let [current-chat-id (:current-chat-id @db)] + (reaction (get-in @db [:chats current-chat-id]))))) + +(register-sub :get-chat + (fn [db [_ chat-id]] + (reaction (get-in @db [:chats chat-id])))) + +(register-sub :get-content-suggestions + (fn [db _] + (reaction (get-in @db [:suggestions (:current-chat-id @db)])))) + +(register-sub :command? + (fn [db] + (->> (get-in @db [:edit-mode (:current-chat-id @db)]) + (= :command) + (reaction)))) + +(register-sub :command-type + (fn [] + (let [command (subscribe [:get-chat-command])] + (reaction (:type @command))))) + +(register-sub :messages-offset + (fn [] + (let [command? (subscribe [:command?]) + type (subscribe [:command-type]) + command-suggestions (subscribe [:get-content-suggestions])] + (reaction + (cond (and @command? (= @type :response)) + c/request-info-height + + (and @command? (= @type :command) (seq @command-suggestions)) + c/suggestions-header-height + + :else 0))))) + +(register-sub :command-icon-width + (fn [] + (let [width (subscribe [:get :command-icon-width]) + type (subscribe [:command-type])] + (reaction (if (= :command @type) + @width + 0))))) + +(register-sub :get-requests + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:chats @chat-id :requests]))))) + +(register-sub :get-requests-map + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (->> (get-in @db [:chats @chat-id :requests]) + (map #(vector (:message-id %) %)) + (into {})))))) + +(register-sub :get-request + (fn [_ [_ message-id]] + (let [requests (subscribe [:get-requests-map])] + (reaction (get @requests message-id))))) + +(register-sub :get-current-request + (fn [] + (let [requests (subscribe [:get-requests-map]) + message-id (subscribe [:get-chat-command-to-message-id])] + (reaction (@requests @message-id))))) + +(register-sub :get-response + (fn [db [_ n]] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:chats @chat-id :responses n]))))) + +(register-sub :is-request-answered? + (fn [_ [_ message-id]] + (let [requests (subscribe [:get-requests])] + (reaction (not (some #(= message-id (:message-id %)) @requests)))))) + +(register-sub :validation-errors + (fn [db] + (let [current-chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:validation-errors @current-chat-id]))))) + +(register-sub :custom-validation-errors + (fn [db] + (let [current-chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:custom-validation-errors @current-chat-id]))))) + +(register-sub :unviewed-messages-count + (fn [db [_ chat-id]] + (reaction (get-in @db [:unviewed-messages chat-id :count])))) + +(register-sub :command-suggestions-height + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction + (get-in @db [:animations :command-suggestions-height @chat-id]))))) + +(register-sub :response-height + (fn [db [_ status-bar]] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction + (min (get-in @db [:animations :to-response-height @chat-id]) + (if (> (:layout-height @db) 0) + (- (:layout-height @db) + (get-in platform-specific [:component-styles :status-bar status-bar :height])) + 0)))))) + +(register-sub :web-view-url + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:web-view-url @chat-id]))))) + +(register-sub :web-view-extra-js + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:web-view-extra-js @chat-id]))))) + +(register-sub :animate? + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:animate? @chat-id]))))) + +(register-sub :kb-mode + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:kb-mode @chat-id]))))) + +(register-sub :input-margin + (fn [] + (let [kb-height (subscribe [:get :keyboard-height]) + focused (subscribe [:get :focused]) + mode (subscribe [:kb-mode]) + kb-max (subscribe [:get :keyboard-max-height]) + show-emoji? (subscribe [:chat-ui-props :show-emoji?])] + (reaction + (cond @show-emoji? (or @kb-max c/emoji-container-height) + ios? @kb-height + (and @focused (= :pan @mode) (pos? @kb-height)) 20 + :else 0))))) + +(register-sub :max-layout-height + (fn [db [_ status-bar]] + (let [layout-height (subscribe [:get :layout-height]) + input-margin (subscribe [:input-margin]) + status-bar-height (get-in platform-specific [:component-styles :status-bar status-bar :height])] + (reaction + (- @layout-height @input-margin status-bar-height))))) + +(register-sub :all-messages-loaded? + (fn [db] + (let [chat-id (subscribe [:get-current-chat-id])] + (reaction (get-in @db [:chats @chat-id :all-loaded?]))))) + +(register-sub :photo-path + (fn [_ [_ id]] + (let [contacts (subscribe [:get :contacts])] + (reaction (:photo-path (@contacts id)))))) diff --git a/src/status_im/chat/suggestions.cljs b/src/status_im/chat/suggestions.cljs new file mode 100644 index 0000000000..1de4d86970 --- /dev/null +++ b/src/status_im/chat/suggestions.cljs @@ -0,0 +1,52 @@ +(ns status-im.chat.suggestions + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.db :as db] + [status-im.models.commands :refer [get-commands + get-chat-command-request + get-chat-command-to-message-id]] + [status-im.utils.utils :refer [log http-get]] + [clojure.string :as s])) + +(defn suggestion? [text] + (= (get text 0) "!")) + +(defn can-be-suggested? [text] + (fn [{:keys [name]}] + (.startsWith (str "!" name) text))) + +(defn get-suggestions + [{:keys [current-chat-id] :as db} text] + (let [commands (get-in db [:chats current-chat-id :commands])] + (if (suggestion? text) + (filter (fn [[_ v]] ((can-be-suggested? text) v)) commands) + []))) + +(defn get-command [db text] + (when (suggestion? text) + ;; TODO change 'commands' to 'suggestions' + (first (filter #(= (:text %) text) (get-commands db))))) + +(defn handle-command [db command-key content] + (when-let [command-handler (get-chat-command-request db)] + (let [to-message-id (get-chat-command-to-message-id db)] + (command-handler to-message-id command-key content))) + db) + +(defn get-command-handler [db command-key content] + (when-let [command-handler (get-chat-command-request db)] + (let [to-message-id (get-chat-command-to-message-id db)] + (fn [] + (command-handler to-message-id command-key content))))) + +(defn check-suggestion [db message] + (when-let [suggestion-text (when (string? message) + (re-matches #"^![^\s]+\s" message))] + (let [suggestion-text' (s/trim suggestion-text)] + (->> (get-commands db) + (filter #(= suggestion-text' (->> % second :name (str "!")))) + first)))) + +(defn typing-command? [db] + (-> db + (get-in [:chats (:current-chat-id db) :input-text]) + suggestion?)) diff --git a/src/status_im/chat/suggestions_responder.cljs b/src/status_im/chat/suggestions_responder.cljs new file mode 100644 index 0000000000..6d760c155a --- /dev/null +++ b/src/status_im/chat/suggestions_responder.cljs @@ -0,0 +1,30 @@ +(ns status-im.chat.suggestions-responder + (:require [status-im.components.drag-drop :as drag] + [status-im.components.animation :as anim] + [status-im.components.react :as react] + [re-frame.core :refer [dispatch]])) + +;; todo bad name. Ideas? +(defn enough-dy [gesture] + (> (Math/abs (.-dy gesture)) 10)) + +(defn on-move [response-height layout-height] + (fn [_ gesture] + (when (enough-dy gesture) + (let [to-value (- @layout-height (.-moveY gesture))] + (anim/start + (anim/spring response-height {:toValue to-value})))))) + +(defn on-release [response-height handler-name] + (fn [_ gesture] + (when (enough-dy gesture) + (dispatch [handler-name + (.-vy gesture) + ;; todo access to "private" property + ;; better to find another way... + (.-_value response-height)])))) + +(defn pan-responder [response-height layout-height handler-name] + (drag/create-pan-responder + {:on-move (on-move response-height layout-height) + :on-release (on-release response-height handler-name)})) diff --git a/src/status_im/chat/utils.cljs b/src/status_im/chat/utils.cljs new file mode 100644 index 0000000000..54e7be2c32 --- /dev/null +++ b/src/status_im/chat/utils.cljs @@ -0,0 +1,38 @@ +(ns status-im.chat.utils + (:require [status-im.constants :refer [console-chat-id + wallet-chat-id]] + [taoensso.timbre :as log])) + +(defn console? [s] + (= console-chat-id s)) + +(def not-console? + (complement console?)) + +(defn wallet? [s] + (= wallet-chat-id s)) + +(defn add-message-to-db + ([db add-to-chat-id chat-id message] (add-message-to-db db add-to-chat-id chat-id message true)) + ([db add-to-chat-id chat-id message new?] + (let [messages [:chats add-to-chat-id :messages]] + (update-in db messages conj (assoc message :chat-id chat-id + :new? (if (nil? new?) + true + new?)))))) + +(defn- check-message [previous-message {:keys [from outgoing] :as message}] + (merge message + {:same-author (if previous-message + (= (:from previous-message) from) + true) + :same-direction (if previous-message + (= (:outgoing previous-message) outgoing) + true)})) + +(defn check-author-direction + ([previous-message message] + (check-message previous-message message)) + ([db chat-id message] + (let [previous-message (first (get-in db [:chats chat-id :messages]))] + (check-message previous-message message)))) diff --git a/src/status_im/chat/views/actions.cljs b/src/status_im/chat/views/actions.cljs new file mode 100644 index 0000000000..7e4ecd6664 --- /dev/null +++ b/src/status_im/chat/views/actions.cljs @@ -0,0 +1,134 @@ +(ns status-im.chat.views.actions + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as s] + [status-im.components.react :refer [view + animated-view + text + icon + touchable-highlight + list-view + list-item]] + [status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]] + [status-im.chat.styles.screen :as st] + [status-im.i18n :refer [label label-pluralize]] + [status-im.utils.platform :refer [platform-specific]])) + +(defview menu-item-icon-profile [] + [chat-id [:chat :chat-id] + group-chat [:chat :group-chat] + name [:chat :name] + color [:chat :color]] + ;; TODO stub data ('online' property) + [chat-icon-view-menu-item chat-id group-chat name color true]) + +(defn- members-text [members] + (str (s/join ", " (map #(:name %) members)) + " " + (label :t/and-you))) + +(defn item-members [members] + {:title (label :t/members-title) + :subtitle (members-text members) + :icon :menu_group + :icon-style {:width 25 + :height 19} + ;; TODO not implemented: action Members + :handler nil}) + +(defn item-user [chat-id] + {:title (label :t/profile) + :custom-icon [menu-item-icon-profile] + :icon :menu_group + :icon-style {:width 25 + :height 19} + :handler #(dispatch [:show-profile chat-id])}) + +(def item-search + {:title (label :t/search-chat) + :subtitle (label :t/not-implemented) + :icon :search_gray_copy + :icon-style {:width 17 + :height 17} + ;; TODO not implemented: action Search chat + :handler nil}) + +(def item-notifications + {:title (label :t/notifications-title) + :subtitle (label :t/not-implemented) + ;;:subtitle "Chat muted" + :icon :muted + :icon-style {:width 18 + :height 21} + ;; TODO not implemented: action Notifications + :handler nil}) + +(def item-settings + {:title (label :t/settings) + :icon :settings + :icon-style {:width 20 + :height 13} + :handler #(dispatch [:show-group-settings])}) + +(defn group-chat-items [members] + [(item-members members) + item-search + item-notifications + item-settings]) + +(defn user-chat-items [chat-id] + [(item-user chat-id) + item-search + item-notifications]) + +(defn overlay [{:keys [on-click-outside]} items] + [view st/actions-overlay + [touchable-highlight {:on-press on-click-outside + :style st/overlay-highlight} + [view nil]] + items]) + +(defn action-view [{:keys [icon-style + custom-icon + handler + title + subtitle] + icon-name :icon}] + [touchable-highlight {:on-press (fn [] + (dispatch [:set-chat-ui-props :show-actions? false]) + (when handler + (handler)))} + [view st/action-icon-row + [view st/action-icon-view + (or custom-icon + [icon icon-name icon-style])] + [view st/action-view + [text {:style st/action-title + :number-of-lines 1 + :font :medium} + title] + (when-let [subtitle subtitle] + [text {:style st/action-subtitle + :number-of-lines 1 + :font :default} + subtitle])]]]) + +(defn actions-list-view [] + (let [{:keys [group-chat chat-id]} + (subscribe [:chat-properties [:group-chat :chat-id]]) + members (subscribe [:current-chat-contacts]) + status-bar-height (get-in platform-specific [:component-styles :status-bar :default :height])] + (when-let [actions (if @group-chat + (group-chat-items @members) + (user-chat-items @chat-id))] + [view (-> (st/actions-wrapper status-bar-height) + (merge (get-in platform-specific [:component-styles :actions-list-view]))) + [view st/actions-separator] + [view st/actions-view + (for [action actions] + (if action + ^{:key action} [action-view action]))]]))) + +(defn actions-view [] + [overlay {:on-click-outside #(dispatch [:set-chat-ui-props :show-actions? false])} + [actions-list-view]]) diff --git a/src/status_im/chat/views/bottom_info.cljs b/src/status_im/chat/views/bottom_info.cljs new file mode 100644 index 0000000000..72aec7eaf4 --- /dev/null +++ b/src/status_im/chat/views/bottom_info.cljs @@ -0,0 +1,86 @@ +(ns status-im.chat.views.bottom-info + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + image + text + icon + touchable-highlight + list-view + list-item]] + [status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]] + [status-im.chat.styles.screen :as st] + [status-im.i18n :refer [label label-pluralize message-status-label]] + [status-im.components.animation :as anim] + [status-im.utils.utils :refer [truncate-str]] + [status-im.utils.identicon :refer [identicon]] + [status-im.utils.listview :as lw] + [clojure.string :as str])) + +(defn- container-animation-logic [{:keys [to-value val]}] + (fn [_] + (anim/start + (anim/spring val {:toValue to-value + :friction 6 + :tension 40})))) + +(defn overlay [{:keys [on-click-outside]} items] + [view {:style st/bottom-info-overlay} + [touchable-highlight {:on-press on-click-outside + :style st/overlay-highlight} + [view nil]] + items]) + +(defn container [height & _] + (let [anim-value (anim/create-value 1) + context {:to-value height + :val anim-value} + on-update (container-animation-logic context)] + (r/create-class + {:component-did-update + on-update + :reagent-render + (fn [height & children] + [animated-view {:style (st/bottom-info-container height)} + (into [view] children)])}))) + +(defn message-status-row [{:keys [photo-path name]} {:keys [whisper-identity status]}] + [view st/bottom-info-row + [image {:source {:uri (or photo-path (identicon whisper-identity))} + :style st/bottom-info-row-photo}] + [view st/bottom-info-row-text-container + [text {:style st/bottom-info-row-text1 + :number-of-lines 1} + (truncate-str (if-not (str/blank? name) + name + whisper-identity) 30)] + [text {:style st/bottom-info-row-text2 + :number-of-lines 1} + (message-status-label (or status :sending))]]]) + +(defn render-row [contacts] + (fn [{:keys [whisper-identity] :as row} _ _] + (let [contact (get contacts whisper-identity)] + (list-item [message-status-row contact row])))) + +(defn bottom-info-view [] + (let [bottom-info (subscribe [:chat-ui-props :bottom-info]) + contacts (subscribe [:get-contacts])] + (r/create-class + {:reagent-render + (fn [] + (let [{:keys [user-statuses message-status participants]} @bottom-info + participants (->> participants + (map (fn [{:keys [identity]}] + [identity {:whisper-identity identity + :status message-status}])) + (into {})) + statuses (vals (merge participants user-statuses))] + [overlay {:on-click-outside #(dispatch [:set-chat-ui-props :show-bottom-info? false])} + [container (* st/item-height (count statuses)) + [list-view {:dataSource (lw/to-datasource statuses) + :enableEmptySections true + :renderRow (render-row @contacts) + :contentContainerStyle st/bottom-info-list-container}]]]))}))) diff --git a/src/status_im/chat/views/command.cljs b/src/status_im/chat/views/command.cljs new file mode 100644 index 0000000000..5b26a80aef --- /dev/null +++ b/src/status_im/chat/views/command.cljs @@ -0,0 +1,26 @@ +(ns status-im.chat.views.command + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + icon + text + touchable-highlight]] + [status-im.chat.styles.input :as st])) + +(defn set-input-message [message] + (dispatch [:set-chat-command-content message])) + +(defn valid? [message validator] + (if validator + (validator message) + (pos? (count message)))) + +(defview command-icon [command] + [icon-width [:get :command-icon-width]] + [view st/command-container + [view {:style (st/command-text-container command) + :onLayout (fn [event] + (let [width (.. event -nativeEvent -layout -width)] + (when (not= icon-width width) + (dispatch [:set :command-icon-width width]))))} + [text {:style st/command-text} (str "!" (:name command))]]]) diff --git a/src/status_im/chat/views/command_validation.cljs b/src/status_im/chat/views/command_validation.cljs new file mode 100644 index 0000000000..8b69b69bcd --- /dev/null +++ b/src/status_im/chat/views/command_validation.cljs @@ -0,0 +1,30 @@ +(ns status-im.chat.views.command-validation + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [status-im.components.react :as c] + [status-im.chat.styles.command-validation :as st] + [status-im.utils.listview :as lw])) + +(defn message [{:keys [title description]}] + (c/list-item + [c/view + [c/text {:style st/title} title] + [c/text {:style st/description} description]])) + +(defn messages-list [errors] + [c/list-view {:renderRow message + :keyboardShouldPersistTaps true + :dataSource (lw/to-datasource errors) + :style st/messages-container}]) + +(defview validation-messages [] + [validation-messages [:validation-errors] + custom-errors [:custom-validation-errors] + command? [:command?]] + [c/view + (cond (seq custom-errors) + (vec (concat [c/view] custom-errors)) + + (seq validation-messages) + [messages-list validation-messages] + + :else nil)]) diff --git a/src/status_im/chat/views/datemark.cljs b/src/status_im/chat/views/datemark.cljs new file mode 100644 index 0000000000..527c6d91b3 --- /dev/null +++ b/src/status_im/chat/views/datemark.cljs @@ -0,0 +1,13 @@ +(ns status-im.chat.views.datemark + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text]] + [clojure.string :as str] + [status-im.i18n :refer [label]] + [status-im.chat.styles.datemark :as st])) + +(defn chat-datemark [value] + [view st/datemark-wrapper + [view st/datemark + [text {:style st/datemark-text} + (str/capitalize (or value (label :t/datetime-today)))]]]) \ No newline at end of file diff --git a/src/status_im/chat/views/emoji.cljs b/src/status_im/chat/views/emoji.cljs new file mode 100644 index 0000000000..fb7b07c752 --- /dev/null +++ b/src/status_im/chat/views/emoji.cljs @@ -0,0 +1,21 @@ +(ns status-im.chat.views.emoji + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + text + icon + emoji-picker]] + [status-im.chat.styles.emoji :as st] + [status-im.components.animation :as anim] + [status-im.chat.suggestions-responder :as resp] + [status-im.chat.constants :as c] + [status-im.i18n :refer [label]])) + +(defview emoji-view [] + [keyboard-max-height [:get :keyboard-max-height]] + [view {:style (st/container keyboard-max-height)} + [view st/emoji-container + [emoji-picker {:style st/emoji-picker + :hideClearButton true + :onEmojiSelected #(dispatch [:add-to-chat-input-text %])}]]]) diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs new file mode 100644 index 0000000000..c23decb890 --- /dev/null +++ b/src/status_im/chat/views/message.cljs @@ -0,0 +1,389 @@ +(ns status-im.chat.views.message + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [clojure.string :as s] + [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.i18n :refer [message-status-label]] + [status-im.components.react :refer [view + text + image + icon + animated-view + touchable-highlight + get-dimensions]] + [status-im.components.animation :as anim] + [status-im.chat.views.request-message :refer [message-content-command-request]] + [status-im.chat.styles.message :as st] + [status-im.chat.styles.command-pill :as pill-st] + [status-im.chat.views.datemark :refer [chat-datemark]] + [status-im.models.commands :refer [parse-command-message-content + parse-command-request]] + [status-im.resources :as res] + [status-im.utils.datetime :as time] + [status-im.constants :refer [console-chat-id + wallet-chat-id + text-content-type + content-type-status + content-type-command + content-type-command-request] :as c] + [status-im.components.chat-icon.screen :refer [chat-icon-message-status]] + [status-im.utils.identicon :refer [identicon]] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.i18n :refer [label + get-contact-translated]] + [status-im.chat.utils :as cu] + [clojure.string :as str] + [status-im.chat.handlers.console :as console] + [taoensso.timbre :as log])) + +(def window-width (:width (get-dimensions "window"))) + +(defn message-content-status [_] + (let [{:keys [chat-id group-chat name color]} (subscribe [:chat-properties [:chat-id :group-chat :name :color]]) + members (subscribe [:current-chat-contacts])] + (fn [{:keys [messages-count content datemark]}] + (let [{:keys [photo-path + status + last-online]} (if @group-chat + {:photo-path nil + :status nil + :last-online 0} + (first @members))] + [view st/status-container + [chat-icon-message-status @chat-id @group-chat @name @color false] + [text {:style st/status-from + :font :default + :number-of-lines 1} + (if (str/blank? @name) + (generate-gfy) + (or (get-contact-translated @chat-id :name @name) + (label :t/chat-name)))] + (when (or status content) + [text {:style st/status-text + :font :default} + (or status content)]) + (if (> messages-count 1) + [view st/message-datemark + [chat-datemark datemark]] + [view st/message-empty-spacing])])))) + +(defn message-content-audio [_] + [view st/audio-container + [view st/play-view + [image {:source res/play + :style st/play-image}]] + [view st/track-container + [view st/track] + [view st/track-mark] + [text {:style st/track-duration-text + :font :default} + "03:39"]]]) + +(defn wallet-command-preview + [{{:keys [name]} :contact-chat + :keys [contact-address params outgoing? current-chat-id]}] + (let [amount (if (= 1 (count params)) + (first (vals params)) + (str params))] + [text {:style st/command-text + :font :default} + (if (= current-chat-id wallet-chat-id) + (let [label-val (if outgoing? :t/chat-send-eth-to :t/chat-send-eth-from)] + (label label-val {:amount amount + :chat-name (or name contact-address)})) + (label :t/chat-send-eth {:amount amount}))])) + +(defn wallet-command? [content-type] + (#{c/content-type-wallet-command c/content-type-wallet-request} content-type)) + +(defn command-preview + [{:keys [params preview content-type] :as message}] + (cond + (wallet-command? content-type) + (wallet-command-preview message) + + preview preview + + :else + [text {:style st/command-text + :font :default} + (if (= 1 (count params)) + (first (vals params)) + (str params))])) + +(defview message-content-command + [{:keys [content content-type rendered-preview chat-id to from outgoing] :as message}] + [commands [(if (= (:type content) "response") + :get-responses + :get-commands) + chat-id] + current-chat-id [:get-current-chat-id] + contact-chat [:get-in [:chats (if outgoing to from)]]] + (let [{:keys [command params]} (parse-command-message-content commands content) + {:keys [name type] + icon-path :icon} command] + [view st/content-command-view + [view st/command-container + [view (pill-st/pill command) + [text {:style pill-st/pill-text + :font :default} + (str (if (= :command type) "!" "?") name)]]] + (when icon-path + [view st/command-image-view + [icon icon-path st/command-image]]) + [command-preview {:command (:name command) + :content-type content-type + :params params + :outgoing? outgoing + :preview rendered-preview + :contact-chat contact-chat + :contact-address (if outgoing to from) + :current-chat-id current-chat-id}]])) + +(defn message-view + [message content] + [view (st/message-view message) + #_(when incoming-group + [text {:style message-author-text} + "Justas"]) + content]) + +(defmulti message-content (fn [_ message _] + (message :content-type))) + +(defmethod message-content content-type-command-request + [wrapper message] + [wrapper message [message-content-command-request message]]) + +(defmethod message-content c/content-type-wallet-request + [wrapper message] + [wrapper message [message-content-command-request message]]) + +(def replacements + {"\\*[^*]+\\*" {:font-weight :bold} + "~[^~]+~" {:font-style :italic}}) + +(def regx (re-pattern (s/join "|" (map first replacements)))) + +(defn get-style [string] + (->> replacements + (into [] (comp + (map first) + (map #(vector % (re-pattern %))) + (drop-while (fn [[_ regx]] (not (re-matches regx string)))) + (take 1))) + ffirst + replacements)) + +;; todo rewrite this, naive implementation +(defn- parse-text [string] + (if (string? string) + (let [general-text (s/split string regx) + general-text' (if (zero? (count general-text)) + [nil] + general-text) + styled-text (vec (map-indexed + (fn [idx string] + (let [style (get-style string)] + [text + {:key (str idx "_" string) + :style style} + (subs string 1 (- (count string) 1))])) + (re-seq regx string))) + styled-text' (if (> (count general-text) + (count styled-text)) + (conj styled-text nil) + styled-text)] + (mapcat vector general-text' styled-text')) + (str string))) + +(defn text-message + [{:keys [content] :as message}] + [message-view message + [text {:style (st/text-message message) + :font :default} + (parse-text content)]]) + +(defmethod message-content text-content-type + [wrapper message] + [wrapper message [text-message message]]) + +(defmethod message-content content-type-status + [_ message] + [message-content-status message]) + +(defmethod message-content content-type-command + [wrapper message] + [wrapper message + [message-view message [message-content-command message]]]) + +(defmethod message-content c/content-type-wallet-command + [wrapper message] + [wrapper message + [message-view message [message-content-command message]]]) + +(defmethod message-content :default + [wrapper {:keys [content-type content] :as message}] + [wrapper message + [message-view message + [message-content-audio {:content content + :content-type content-type}]]]) + +(defview group-message-delivery-status [{:keys [message-id group-id message-status user-statuses] :as msg}] + [app-db-message-user-statuses [:get-in [:message-user-statuses message-id]] + app-db-message-status-value [:get-in [:message-statuses message-id :status]] + chat [:get-chat-by-id group-id] + contacts [:get-contacts]] + (let [status (or message-status app-db-message-status-value :sending) + user-statuses (merge user-statuses app-db-message-user-statuses) + participants (:contacts chat) + seen-by-everyone? (and (= (count user-statuses) (count participants)) + (every? (fn [[_ {:keys [status]}]] + (= (keyword status) :seen)) user-statuses))] + (if (or (zero? (count user-statuses)) + seen-by-everyone?) + [view st/delivery-view + [image {:source (case status + :seen {:uri :icon_ok_small} + :failed res/delivery-failed-icon + nil) + :style st/delivery-image}] + [text {:style st/delivery-text + :font :default} + (message-status-label + (if seen-by-everyone? + :seen-by-everyone + status))]] + [touchable-highlight + {:on-press (fn [] + (dispatch [:show-message-details {:message-status status + :user-statuses user-statuses + :participants participants}]))} + [view st/delivery-view + (for [[_ {:keys [whisper-identity]}] (take 3 user-statuses)] + ^{:key whisper-identity} + [image {:source {:uri (or (get-in contacts [whisper-identity :photo-path]) + (identicon whisper-identity))} + :style {:width 16 + :height 16 + :borderRadius 8}}]) + (if (> (count user-statuses) 3) + [text {:style st/delivery-text + :font :default} + (str "+ " (- (count user-statuses) 3))])]]))) + +(defview message-delivery-status + [{:keys [message-id chat-id message-status user-statuses content]}] + [app-db-message-status-value [:get-in [:message-statuses message-id :status]]] + (let [delivery-status (get-in user-statuses [chat-id :status]) + command-name (keyword (:command content)) + status (cond (and (not (console/commands-with-delivery-status command-name)) + (cu/console? chat-id)) + :seen + + (cu/wallet? chat-id) + :sent + + :else + (or delivery-status message-status app-db-message-status-value :sending))] + [view st/delivery-view + [image {:source (case status + :seen {:uri :icon_ok_small} + :failed res/delivery-failed-icon + nil) + :style st/delivery-image}] + [text {:style st/delivery-text + :font :default} + (message-status-label status)]])) + +(defview member-photo [from] + [photo-path [:photo-path from]] + [view st/photo-view + [image {:source {:uri (if (s/blank? photo-path) + (identicon from) + photo-path)} + :style st/photo}]]) + +(defn incoming-group-message-body + [{:keys [selected same-author from index] :as message} content] + (let [delivery-status :seen-by-everyone] + [view st/group-message-wrapper + (when selected + [text {:style st/selected-message + :font :default} + "Mar 7th, 15:22"]) + [view (st/incoming-group-message-body-st message) + [view st/message-author + (when (or (= index 1) + (not same-author)) [member-photo from])] + [view st/group-message-view + content + ;; TODO show for last or selected + (when (and selected delivery-status) + [message-delivery-status message])]]])) + +(defn message-body + [{:keys [outgoing message-type] :as message} content] + [view (st/message-body message) + content + (when outgoing + (if (= (keyword message-type) :group-user-message) + [group-message-delivery-status message] + [message-delivery-status message]))]) + +(defn message-container-animation-logic [{:keys [to-value val callback]}] + (fn [_] + (let [to-value @to-value] + (when (< 0 to-value) + (anim/start + (anim/timing val {:toValue to-value + :duration 250}) + (fn [arg] + (when (.-finished arg) + (callback)))))))) + +(defn message-container [message & children] + (if (:new? message) + (let [layout-height (r/atom 0) + anim-value (anim/create-value 1) + anim-callback #(dispatch [:set-message-shown message]) + context {:to-value layout-height + :val anim-value + :callback anim-callback} + on-update (message-container-animation-logic context)] + (r/create-class + {:component-did-update + on-update + :reagent-render + (fn [message & children] + @layout-height + [animated-view {:style (st/message-animated-container anim-value)} + (into [view {:style (st/message-container window-width) + :onLayout (fn [event] + (let [height (.. event -nativeEvent -layout -height)] + (reset! layout-height height)))}] + children)])})) + (into [view] children))) + +(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from]}] + (let [my-identity (subscribe [:get :current-public-key]) + status (subscribe [:get-in [:message-user-statuses message-id my-identity]])] + (r/create-class + {:component-did-mount + (fn [] + (when (and (not outgoing) + (not= :seen (keyword @status)) + (not= :seen (keyword (get-in user-statuses [@my-identity :status])))) + (dispatch [:send-seen! {:chat-id chat-id + :from from + :message-id message-id}]))) + :reagent-render + (fn [{:keys [outgoing group-chat clock-value] :as message}] + [message-container message + [view + (let [incoming-group (and group-chat (not outgoing))] + [message-content + (if incoming-group + incoming-group-message-body + message-body) + (merge message {:incoming-group incoming-group})])]])}))) diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs new file mode 100644 index 0000000000..49ad30f116 --- /dev/null +++ b/src/status_im/chat/views/message_input.cljs @@ -0,0 +1,115 @@ +(ns status-im.chat.views.message-input + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + animated-view + icon + touchable-highlight + text-input]] + [status-im.chat.views.plain-message :as plain-message] + [status-im.chat.views.command :as command] + [status-im.chat.styles.message-input :as st] + [status-im.chat.styles.plain-message :as st-message] + [status-im.chat.styles.response :as st-response] + [reagent.core :as r] + [clojure.string :as str])) + +(defn send-button [{:keys [on-press accessibility-label]}] + [touchable-highlight {:on-press on-press + :accessibility-label accessibility-label} + [view st/send-wrapper + [view st/send-container + [icon :send st/send-icon]]]]) + +(defn plain-input-options [disable?] + {:style st-message/message-input + :on-change-text (when-not disable? plain-message/set-input-message) + :editable (not disable?) + :on-submit-editing plain-message/send}) + +(defn on-press-commands-handler + [{:keys [suggestions-trigger]}] + #(dispatch [:send-command!])) + +(defn command-input-options [command icon-width disable?] + {:style (st-response/command-input icon-width disable?) + :on-change-text (when-not disable? command/set-input-message) + :on-submit-editing (on-press-commands-handler command)}) + +(defview message-input [input-options set-layout-size] + [input-message [:get-chat-input-text] + disable? [:get :disable-input] + active? [:chat :is-active]] + [text-input (merge + (plain-input-options (or disable? (not active?))) + {:placeholder-text-color :#c0c5c9 + :auto-focus false + :blur-on-submit true + :multiline true + :on-content-size-change #(let [size (-> (.-nativeEvent %) + (.-contentSize) + (.-height))] + (set-layout-size size)) + :accessibility-label :input + :on-focus #(do (dispatch [:set :focused true]) + (dispatch [:set-chat-ui-props :show-emoji? false])) + :on-blur #(do (dispatch [:set :focused false]) + (set-layout-size 0)) + :default-value (or input-message "")} + input-options)]) + +(defview command-input [input-options {:keys [fullscreen] :as command}] + [input-command [:get-chat-command-content] + icon-width [:command-icon-width] + disable? [:get :disable-input]] + [text-input (merge + (command-input-options command icon-width disable?) + {:auto-focus (not fullscreen) + :blur-on-submit false + :accessibility-label :input + :on-focus #(dispatch [:set :focused true]) + :on-blur #(dispatch [:set :focused false]) + :default-value (or input-command "")} + input-options)]) + +(defn plain-message-get-initial-state [_] + {:height 0}) + +(defn plain-message-input-view [_] + (let [command? (subscribe [:command?]) + command (subscribe [:get-chat-command]) + input-command (subscribe [:get-chat-command-content]) + input-message (subscribe [:get-chat-input-text]) + valid-plain-message? (subscribe [:valid-plain-message?]) + component (r/current-component) + set-layout-size #(r/set-state component {:height %})] + (r/create-class + {:get-initial-state + plain-message-get-initial-state + :component-will-update + (fn [_] + (when (or (and @command? (str/blank? @input-command)) + (and (not @command?) (not @input-message))) + (set-layout-size 0))) + :reagent-render + (fn [{:keys [input-options]}] + (let [{:keys [height]} (r/state component)] + [view st/input-container + [view (st/input-view height) + [plain-message/commands-button height #(set-layout-size 0)] + [view (st/message-input-container height) + (if @command? + [command-input input-options @command] + [message-input input-options set-layout-size])] + [plain-message/smile-button height] + (when (or (and @command? (not (str/blank? @input-command))) + @valid-plain-message?) + (let [on-press (if @command? + (on-press-commands-handler @command) + plain-message/send)] + [send-button {:on-press #(do (dispatch [:set-chat-ui-props :show-emoji? false]) + (on-press %))}])) + (when (and @command? (= :command (:type @command))) + [command/command-icon @command])]]))}))) + diff --git a/src/status_im/chat/views/new_message.cljs b/src/status_im/chat/views/new_message.cljs new file mode 100644 index 0000000000..2b17865be2 --- /dev/null +++ b/src/status_im/chat/views/new_message.cljs @@ -0,0 +1,47 @@ +(ns status-im.chat.views.new-message + (:require-macros [status-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [dispatch subscribe]] + [status-im.components.react :refer [view + scroll-view]] + [status-im.chat.views.message-input :refer [plain-message-input-view]] + [status-im.chat.constants :refer [input-height]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.chat.styles.message :as st] + [taoensso.timbre :as log])) + +(defn get-height [event] + (.-height (.-layout (.-nativeEvent event)))) + +(defn get-options [{:keys [type placeholder]} command-type] + (let [options (case (keyword type) + :phone {:input-options {:keyboard-type "phone-pad"}} + :password {:input-options {:secure-text-entry true}} + :number {:input-options {:keyboard-type "numeric"}} + ;; todo maybe nil is fine for now :) + nil #_(throw (js/Error. "Uknown command type")))] + (if (= :response command-type) + (if placeholder + (assoc-in options [:input-options :placeholder] placeholder) + options) + (assoc-in options [:input-options :placeholder] "")))) + +(defview chat-message-input-view [] + [margin [:input-margin] + command? [:command?] + response-height [:response-height] + parameter [:get-command-parameter] + type [:command-type] + suggestions [:get-suggestions] + message-input-height [:get-message-input-view-height]] + (let [on-top? (or (and (not (empty? suggestions)) + (not command?)) + (not= response-height input-height)) + style (get-in platform-specific [:component-styles :chat :new-message])] + [view {:style (merge (st/new-message-container margin on-top?) style) + :on-layout (fn [event] + (let [height (get-height event)] + (when (not= height message-input-height) + (dispatch [:set-message-input-view-height height]))))} + [plain-message-input-view + (when command? (get-options parameter type))]])) diff --git a/src/status_im/chat/views/plain_message.cljs b/src/status_im/chat/views/plain_message.cljs new file mode 100644 index 0000000000..df4d6f5cef --- /dev/null +++ b/src/status_im/chat/views/plain_message.cljs @@ -0,0 +1,106 @@ +(ns status-im.chat.views.plain-message + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + icon + text + touchable-highlight + dismiss-keyboard!]] + [status-im.components.animation :as anim] + [status-im.chat.styles.plain-message :as st] + [status-im.constants :refer [response-input-hiding-duration]])) + +(defn set-input-message [message] + (dispatch [:set-chat-input-text message])) + +(defn send [] + (dispatch [:send-chat-message])) + +(defn message-valid? [ message] + (and (pos? (count message)) + (not= "!" message))) + +(defn button-animation-logic [{:keys [command? val]}] + (fn [_] + (let [to-scale (if @command? 0 1)] + (anim/start (anim/spring val {:toValue to-scale + :tension 30}))))) + +(defn list-container [min] + (fn [{:keys [command? width]}] + (let [n-width (if @command? min 56) + delay (if @command? 100 0)] + (anim/start (anim/timing width {:toValue n-width + :duration response-input-hiding-duration + :delay delay}) + #(dispatch [:set :disable-input false]))))) + +(defn commands-button [height on-press] + (let [command? (subscribe [:command?]) + requests (subscribe [:get-requests]) + suggestions (subscribe [:get-suggestions]) + buttons-scale (anim/create-value (if @command? 1 0)) + container-width (anim/create-value (if @command? 20 56)) + context {:command? command? + :val buttons-scale + :width container-width} + on-update (fn [_] + ((button-animation-logic context)) + ((list-container 20) context))] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [height on-press] + [touchable-highlight {:on-press #(do (dispatch [:set-chat-ui-props :show-emoji? false]) + (dispatch [:switch-command-suggestions!]) + (on-press)) + :disabled @command?} + [animated-view {:style (st/message-input-button-touchable container-width height)} + (when-not @command? + [animated-view {:style (st/message-input-button + buttons-scale + (if (seq @suggestions) 16 17))} + (if (seq @suggestions) + [icon :close_gray st/close-icon] + [icon :input_list st/list-icon]) + (when (and (seq @requests) + (not (seq @suggestions))) + [view st/requests-icon-container + [view st/requests-icon]])])]])}))) + +(defn smile-animation-logic [{:keys [command? val width]}] + (fn [_] + (let [to-scale (if @command? 0 1)] + (when-not @command? (anim/set-value width 56)) + (anim/start (anim/spring val {:toValue to-scale + :tension 30}) + (fn [e] + (when (and @command? (.-finished e)) + (anim/set-value width 0.1))))))) + +(defn smile-button [height] + (let [command? (subscribe [:command?]) + buttons-scale (anim/create-value (if @command? 1 0)) + container-width (anim/create-value (if @command? 0.1 56)) + context {:command? command? + :val buttons-scale + :width container-width} + on-update (smile-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [] + [touchable-highlight {:on-press #(do (dispatch [:toggle-chat-ui-props :show-emoji?]) + (dismiss-keyboard!)) + :disabled @command?} + [animated-view {:style (st/message-input-button-touchable container-width height)} + [animated-view {:style (st/message-input-button buttons-scale 16)} + [icon :smile st/smile-icon]]]])}))) diff --git a/src/status_im/chat/views/request_message.cljs b/src/status_im/chat/views/request_message.cljs new file mode 100644 index 0000000000..77acdd7b43 --- /dev/null +++ b/src/status_im/chat/views/request_message.cljs @@ -0,0 +1,109 @@ +(ns status-im.chat.views.request-message + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + text + image + icon + touchable-highlight]] + [status-im.chat.styles.message :as st] + [status-im.models.commands :refer [parse-command-request]] + [status-im.components.animation :as anim])) + +(def request-message-icon-scale-delay 600) + +(defn set-chat-command [message-id command] + (let [command-key (keyword (:name command)) + params (:set-params command)] + (dispatch [:set-response-chat-command message-id command-key params]))) + +(defn label [command] + (when command + (->> (name (:name command)) + (str "request-")))) + +(def min-scale 1) +(def max-scale 1.3) + +(defn button-animation [val to-value loop? answered?] + (anim/anim-sequence + [(anim/anim-delay + (if (and @loop? (not @answered?)) + request-message-icon-scale-delay + 0)) + (anim/spring val {:toValue to-value})])) + +(defn request-button-animation-logic + [{:keys [to-value val loop? answered?] :as context}] + (anim/start + (button-animation val to-value loop? answered?) + #(if (and @loop? (not @answered?)) + (let [new-value (if (= to-value min-scale) max-scale min-scale) + context' (assoc context :to-value new-value)] + (request-button-animation-logic context')) + (anim/start + (button-animation val min-scale loop? answered?))))) + +(defn request-button [message-id command status-initialized? top-offset?] + (let [scale-anim-val (anim/create-value min-scale) + answered? (subscribe [:is-request-answered? message-id]) + loop? (r/atom true) + context {:to-value max-scale + :val scale-anim-val + :answered? answered? + :loop? loop?}] + (r/create-class + {:component-did-mount + (if @answered? (fn []) #(request-button-animation-logic context)) + :component-will-unmount + #(reset! loop? false) + :reagent-render + (fn [message-id {command-icon :icon :as command} status-initialized?] + (when command + [touchable-highlight + {:on-press (when (and (not @answered?) status-initialized?) + #(set-chat-command message-id command)) + :style (st/command-request-image-touchable top-offset?) + :accessibility-label (label command)} + [animated-view {:style (st/command-request-image-view command scale-anim-val)} + (when command-icon + [icon command-icon st/command-request-image])]]))}))) + +(defn message-content-command-request + [{:keys [message-id content from incoming-group]}] + (let [top-offset (r/atom {:specified? false}) + commands-atom (subscribe [:get-responses]) + answered? (subscribe [:is-request-answered? message-id]) + status-initialized? (subscribe [:get :status-module-initialized?])] + (fn [{:keys [message-id content from incoming-group]}] + (let [commands @commands-atom + params (:params content) + {:keys [command content]} (parse-command-request commands content) + command (if (and params command) + (merge command {:set-params params}) + command)] + [view st/comand-request-view + [touchable-highlight + {:on-press (when (and (not @answered?) @status-initialized?) + #(set-chat-command message-id command))} + [view st/command-request-message-view + (when incoming-group + [text {:style st/command-request-from-text + :font :default} + from]) + [text {:style st/style-message-text + :on-layout #(reset! top-offset {:specified? true + :value (-> (.-nativeEvent %) + (.-layout) + (.-height) 
 + (> 25))}) + :font :default} + content]]] + (when (:request-text command) + [view st/command-request-text-view + [text {:style st/style-sub-text + :font :default} + (:request-text command)]]) + (when (:specified? @top-offset) + [request-button message-id command @status-initialized? (:value @top-offset)])])))) diff --git a/src/status_im/chat/views/response.cljs b/src/status_im/chat/views/response.cljs new file mode 100644 index 0000000000..3ae0a3f7ad --- /dev/null +++ b/src/status_im/chat/views/response.cljs @@ -0,0 +1,159 @@ +(ns status-im.chat.views.response + (:require-macros [reagent.ratom :refer [reaction]] + [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + icon + image + text + touchable-highlight + web-view + scroll-view]] + [status-im.components.drag-drop :as drag] + [status-im.chat.styles.response :as st] + [status-im.chat.styles.dragdown :as ddst] + [status-im.components.animation :as anim] + [status-im.chat.suggestions-responder :as resp] + [status-im.chat.constants :as c] + [status-im.chat.views.command-validation :as cv] + [status-im.utils.platform :refer [ios?]] + [status-im.components.webview-bridge :refer [webview-bridge]] + [status-im.i18n :refer [label]] + [status-im.utils.datetime :as dt] + [taoensso.timbre :as log] + [status-im.utils.name :refer [shortened-name]] + [status-im.utils.js-resources :as js-res] + [status-im.commands.utils :as cu])) + +(defn drag-icon [] + [view st/drag-container + [icon :drag_white st/drag-icon]]) + +(defn command-icon [{icon-path :icon + color :color}] + [view st/command-icon-container + (when icon-path + [icon icon-path (st/command-icon color)])]) + +(defn request-info-text [name chat-id added] + (let [name' (shortened-name (or name chat-id) 20)] + (str "By " name' ", " + (dt/format-date "MMM" added) + " " + (dt/get-ordinal-date added) + " at " + (dt/format-date "HH:mm" added)))) + +(defview info-container + [command] + [{:keys [name chat-id]} [:get-current-chat] + {:keys [added]} [:get-current-request]] + [view st/info-container + [text {:style st/command-name} + (str (:description command) " " (label :t/request))] + (when added + [text {:style st/message-info} (request-info-text name chat-id added)])]) + +(defn request-info [response-height] + (let [layout-height (subscribe [:max-layout-height :default]) + pan-responder (resp/pan-responder response-height + layout-height + :fix-response-height) + command (subscribe [:get-chat-command])] + (fn [response-height] + (if (= :response (:type @command)) + [view (merge (drag/pan-handlers pan-responder) + {:style (st/request-info (:color @command))}) + [drag-icon] + [view st/inner-container + [command-icon @command] + [info-container @command] + [touchable-highlight {:on-press #(dispatch [:start-cancel-command])} + [view st/cancel-container + [icon :close_white st/cancel-icon]]]]] + [view (merge (drag/pan-handlers pan-responder) + {:style ddst/drag-down-touchable}) + [icon :drag_down ddst/drag-down-icon]])))) + +(defn container-animation-logic [{:keys [to-value val animate?]}] + (when-let [to-value @to-value] + (let [max-layout-height (subscribe [:max-layout-height :default]) + to-value (min to-value (max 0 @max-layout-height))] + (when-not (= to-value (.-_value val)) + (if (or (nil? @animate?) @animate?) + (anim/start (anim/timing val {:toValue to-value + :duration 300})) + (anim/set-value val to-value)))))) + +(defn container [response-height & children] + (let [;; todo to-response-height, cur-response-height must be specific + ;; for each chat + to-response-height (subscribe [:response-height :default]) + input-margin (subscribe [:input-margin]) + changed (subscribe [:animations :response-height-changed]) + animate? (subscribe [:animate?]) + context {:to-value to-response-height + :val response-height + :animate? animate?} + on-update #(container-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [response-height & children] + @to-response-height @changed + (into [animated-view {:style (st/response-view + response-height + @input-margin)}] + children))}))) + +(defn on-navigation-change + [event] + (let [{:strs [loading url]} (js->clj event)] + (when-not (= "about:blank" url) + (if loading + (dispatch [:set-web-view-url url]) + (dispatch [:set-chat-command-content (str cu/command-prefix url)]))))) + +(defn web-view-error [] + (r/as-element + [view {:justify-content :center + :align-items :center + :flex-direction :row} + [text (label :t/web-view-error)]])) + +(defview suggestions-web-view [] + [url [:web-view-url] + extra-js [:web-view-extra-js]] + (when url + [webview-bridge + {:ref #(dispatch [:set-webview-bridge %]) + :on-bridge-message #(dispatch [:webview-bridge-message %]) + :source {:uri url} + :render-error web-view-error + :java-script-enabled true + :injected-on-start-loading-java-script (str js-res/web3 js-res/web3-init) + :injected-java-script (str js-res/webview-js extra-js) + :bounces false + :on-navigation-state-change on-navigation-change + :local-storage-enabled true}])) + +(defn placeholder [] + [view st/input-placeholder]) + +(defview response-suggestions-view [] + [suggestions [:get-content-suggestions]] + (when (seq suggestions) suggestions)) + +(defn response-view [] + (let [response-height (anim/create-value c/input-height)] + [container response-height + [request-info response-height] + [suggestions-web-view] + [response-suggestions-view] + [cv/validation-messages] + [placeholder]])) diff --git a/src/status_im/chat/views/suggestions.cljs b/src/status_im/chat/views/suggestions.cljs new file mode 100644 index 0000000000..32353a36c6 --- /dev/null +++ b/src/status_im/chat/views/suggestions.cljs @@ -0,0 +1,131 @@ +(ns status-im.chat.views.suggestions + (:require-macros [status-im.utils.views :refer [defview]] + [clojure.string :as str]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + scroll-view + text + icon + image + touchable-highlight + list-view + list-item + animated-view]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.chat.styles.suggestions :as st] + [status-im.chat.styles.dragdown :as ddst] + [reagent.core :as r] + [status-im.components.animation :as anim] + [status-im.components.drag-drop :as drag] + [status-im.utils.platform :refer [ios?]] + [status-im.chat.suggestions-responder :as resp] + [status-im.chat.constants :as c] + [status-im.i18n :refer [label]] + [status-im.chat.views.response :as response])) + +(defn set-command-input [command] + (dispatch [:set-chat-command command])) + +(defview request-item [{:keys [type message-id]}] + [{:keys [color description] + icon-path :icon + :as response} [:get-response type] + {:keys [name chat-id]} [:get-current-chat] + {:keys [added]} [:get-request message-id]] + [touchable-highlight + {:on-press #(dispatch [:set-response-chat-command message-id type])} + [view st/request-container + [view st/request-icon-container + [view (st/request-icon-background color) + (when icon-path + [icon icon-path st/request-icon])]] + [view st/request-info-container + [text {:style st/request-info-description} description] + (when added + [text {:style st/request-message-info} + (response/request-info-text name chat-id added)])]]]) + +(defn suggestion-list-item + [[command {:keys [title description] + name :name + :as suggestion}]] + (let [label (str "!" name)] + [touchable-highlight + {:onPress #(set-command-input command) + :style st/suggestion-highlight} + [view st/suggestion-container + [view st/suggestion-sub-container + [view st/command-description-container + [text {:style st/value-text} title] + [text {:style st/description-text} description]] + [view st/command-label-container + [view (st/suggestion-background suggestion) + [text {:style st/suggestion-text} label]]]]]])) + +(defn title [s] + [view st/title-container + [text {:style st/title-text} s]]) + +(defview suggestions-view [] + [suggestions [:get-suggestions] + requests [:get-requests]] + [view {:flex 1} + [scroll-view {:keyboardShouldPersistTaps true} + (when (seq requests) + [title (label :t/suggestions-requests)]) + (when (seq requests) + (for [{:keys [chat-id message-id] :as request} requests] + ^{:key [chat-id message-id]} + [request-item request])) + [title (label :t/suggestions-commands)] + (for [suggestion (remove #(nil? (:title (second %))) suggestions)] + ^{:key (first suggestion)} + [suggestion-list-item suggestion])]]) + +(defn header [h] + (let [layout-height (subscribe [:max-layout-height :default]) + pan-responder (resp/pan-responder h + layout-height + :fix-commands-suggestions-height)] + (fn [_] + [view + (merge (drag/pan-handlers pan-responder) + {:style ddst/drag-down-touchable}) + [view st/header-icon]]))) + +(defn container-animation-logic [{:keys [to-value val animate?]}] + (when-let [to-value @to-value] + (let [max-layout-height (subscribe [:max-layout-height :default]) + to-value (min to-value (max 0 @max-layout-height))] + (when-not (= to-value (.-_value val)) + (if (or (nil? @animate?) @animate?) + (anim/start (anim/spring val {:toValue to-value})) + (anim/set-value val to-value)))))) + +(defn container [h & elements] + (let [;; todo to-response-height, cur-response-height must be specific + ;; for each chat + to-response-height (subscribe [:command-suggestions-height]) + input-margin (subscribe [:input-margin]) + changed (subscribe [:animations :commands-height-changed]) + animate? (subscribe [:animate?]) + context {:to-value to-response-height + :val h + :animate? animate?} + on-update #(container-animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [h & elements] + @to-response-height @changed + (into [animated-view {:style (st/container h @input-margin)}] elements))}))) + +(defview suggestion-container [] + (let [h (anim/create-value c/input-height)] + [container h + [header h] + [suggestions-view] + [view {:height c/input-height}]])) diff --git a/src/status_im/chat/views/toolbar_content.cljs b/src/status_im/chat/views/toolbar_content.cljs new file mode 100644 index 0000000000..bcabd4ea8d --- /dev/null +++ b/src/status_im/chat/views/toolbar_content.cljs @@ -0,0 +1,86 @@ +(ns status-im.chat.views.toolbar-content + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] + [cljs-time.core :as t] + [status-im.components.react :refer [view + text + icon]] + [status-im.i18n :refer [get-contact-translated + label + label-pluralize]] + [status-im.chat.styles.screen :as st] + [status-im.components.refreshable-text.view :refer [refreshable-text]] + [status-im.utils.datetime :as time] + [status-im.utils.platform :refer [platform-specific]] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.constants :refer [console-chat-id wallet-chat-id]])) + +(defn online-text [contact chat-id] + (cond + (#{console-chat-id wallet-chat-id} chat-id) (label :t/available) + contact (let [last-online (get contact :last-online) + last-online-date (time/to-date last-online) + now-date (t/now)] + (if (and (> last-online 0) + (<= last-online-date now-date)) + (time/time-ago last-online-date) + (label :t/active-unknown))) + :else (label :t/active-unknown))) + +(defn in-progress-text [{:keys [highestBlock currentBlock startBlock]}] + (let [total (- highestBlock startBlock) + ready (- currentBlock startBlock) + percentage (if (zero? ready) + 0 + (->> (/ ready total) + (* 100) + (.round js/Math)))] + + (str (label :t/sync-in-progress) " " percentage "% " currentBlock))) + +(defview last-activity [{:keys [online-text sync-state]}] + [state [:get :sync-data]] + [refreshable-text {:style st/last-activity + :text-style (get-in platform-specific [:component-styles :toolbar-last-activity]) + :font :default + :value (case sync-state + :in-progress (in-progress-text state) + :synced (label :t/sync-synced) + online-text)}]) + +(defn group-last-activity [{:keys [contacts sync-state]}] + (if (or (= sync-state :in-progress) + (= sync-state :synced)) + [last-activity {:sync-state sync-state}] + [view {:flex-direction :row} + [icon :group st/group-icon] + [text {:style st/members + :font :medium} + (let [cnt (inc (count contacts))] + (label-pluralize cnt :t/members-active))]])) + +(defn toolbar-content-view [] + (let [{:keys [group-chat + name + contacts + chat-id]} (subscribe [:chat-properties [:group-chat :name :contacts :chat-id]]) + show-actions? (subscribe [:chat-ui-props :show-actions?]) + accounts (subscribe [:get :accounts]) + contact (subscribe [:get-in [:contacts @chat-id]]) + sync-state (subscribe [:get :sync-state])] + (fn [] + [view (st/chat-name-view (or (empty? @accounts) + @show-actions?)) + [text {:style st/chat-name-text + :number-of-lines 1 + :font :toolbar-title} + (if (str/blank? @name) + (generate-gfy) + (or (get-contact-translated @chat-id :name @name) + (label :t/chat-name)))] + (if @group-chat + [group-last-activity {:contacts @contacts + :sync-state @sync-state}] + [last-activity {:online-text (online-text @contact @chat-id) + :sync-state @sync-state}])]))) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs new file mode 100644 index 0000000000..073a8109d2 --- /dev/null +++ b/src/status_im/chats_list/screen.cljs @@ -0,0 +1,84 @@ +(ns status-im.chats-list.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [list-view + list-item + view + animated-view + text + image + touchable-highlight]] + [status-im.components.action-button :refer [action-button + action-button-item]] + [status-im.components.drawer.view :refer [open-drawer]] + [status-im.components.styles :refer [color-blue]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar-with-search]] + [status-im.components.toolbar.actions :as act] + [status-im.components.icons.custom-icons :refer [ion-icon]] + [status-im.components.react :refer [linear-gradient]] + [status-im.components.sync-state.offline :refer [offline-view]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.chats-list.views.chat-list-item :refer [chat-list-item]] + [status-im.i18n :refer [label]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.chats-list.styles :as st] + [status-im.components.tabs.styles :refer [tabs-height]])) + +(defview toolbar-view [] + [chats-scrolled? [:get :chats-scrolled?]] + (let [new-chat? (get-in platform-specific [:chats :new-chat-in-toolbar?]) + actions (if new-chat? + [(act/add #(dispatch [:navigate-to :group-contacts :people]))])] + [toolbar-with-search + {:show-search? false + :search-key :chat-list + :title (label :t/chats) + :search-placeholder (label :t/search-for) + :nav-action (act/hamburger open-drawer) + :actions actions + :style (st/toolbar chats-scrolled?)}])) + +(defn chats-action-button [] + [view {:style (st/action-buttons-container false 0) + :pointerEvents :box-none} + [action-button {:button-color color-blue + :offset-x 16 + :offset-y -2 + :hide-shadow true + :spacing 13} + [action-button-item + {:title (label :t/new-chat) + :buttonColor :#9b59b6 + :onPress #(dispatch [:navigate-to :group-contacts :people])} + [ion-icon {:name :md-create + :style st/create-icon}]] + [action-button-item + {:title (label :t/new-group-chat) + :buttonColor :#1abc9c + :onPress #(dispatch [:navigate-to :new-group])} + [ion-icon {:name :md-person + :style st/person-stalker-icon}]]]]) + +(defn chat-shadow-item [] + [view {:height 3} + [linear-gradient {:style {:height 3} + :colors st/gradient-top-bottom-shadow}]]) + +(defview chats-list [] + [chats [:get :chats]] + [view st/chats-container + [toolbar-view] + [list-view {:dataSource (to-datasource chats) + :renderRow (fn [[id :as row] _ _] + (list-item ^{:key id} [chat-list-item row])) + :renderFooter #(list-item [chat-shadow-item]) + :renderSeparator #(list-item + (when (< %2 (- (count chats) 1)) + ^{:key (str "separator-" %2)} + [view st/chat-separator-wrapper + [view st/chat-separator-item]])) + :style st/list-container}] + (when (get-in platform-specific [:chats :action-button?]) + [chats-action-button]) + [offline-view]]) diff --git a/src/status_im/chats_list/styles.cljs b/src/status_im/chats_list/styles.cljs new file mode 100644 index 0000000000..b89ab110e5 --- /dev/null +++ b/src/status_im/chats_list/styles.cljs @@ -0,0 +1,138 @@ +(ns status-im.chats-list.styles + (:require [status-im.components.styles :refer [color-white + color-light-gray + color-separator + color-blue + text1-color + text2-color + new-messages-count-color]] + [status-im.components.tabs.styles :refer [tabs-height]] + [status-im.components.toolbar.styles :refer [toolbar-background1 + toolbar-background2]] + [status-im.utils.platform :as p])) + +(defn toolbar [chats-scrolled?] + (merge {:background-color (if chats-scrolled? + toolbar-background1 + toolbar-background2)} + (get-in p/platform-specific [:component-styles :toolbar]))) + +(def gradient-top-bottom-shadow + ["rgba(24, 52, 76, 0.165)" + "rgba(24, 52, 76, 0.03)" + "rgba(24, 52, 76, 0.01)"]) + +(def chat-separator-wrapper + {:background-color color-white + :height 0.5 + :padding-left 74}) + +(def chat-separator-item + {:border-bottom-width 0.5 + :border-bottom-color color-separator}) + +(def chat-container + {:flex-direction :row + :background-color color-white + :height 94}) + +(def chat-icon-container + {:margin-top -2 + :margin-left -4 + :padding 16 + :width 48 + :height 48}) + +(def item-container + {:flex-direction :column + :margin-left 30 + :padding-top 16 + :padding-right 16 + :flex 1}) + +(def name-view + {:flex-direction :row}) + +(def name-text + {:color text1-color + :font-size 14}) + +(def group-icon + {:margin-top 5 + :margin-left 8 + :width 14 + :height 9}) + +(def memebers-text + {:marginTop 2 + :marginLeft 4 + :fontSize 12 + :color text2-color}) + +(def last-message-text + {:margin-top 5 + :margin-right 40 + :color text1-color + :fontSize 14 + :lineHeight 20}) + +(def last-message-text-no-messages + (merge last-message-text + {:color text2-color})) + +(def status-container + {:flex-direction :row + :top 18 + :right 16}) + +(def status-image + {:marginTop 4 + :width 9 + :height 7}) + +(def datetime-text + {:fontSize 12 + :color text2-color + :marginLeft 5}) + +(def new-messages-container + {:position :absolute + :top 54 + :right 16 + :width 24 + :height 24 + :backgroundColor new-messages-count-color + :borderRadius 50}) + +(def new-messages-text + {:top 5 + :left 0 + :fontSize 10 + :color color-blue + :textAlign :center}) + +(def chats-container + {:flex 1}) + +(def list-container + {:background-color color-light-gray + :margin-bottom (when p/ios? 72)}) + +(def create-icon + {:fontSize 20 + :height 22 + :color color-white}) + +(def person-stalker-icon + {:fontSize 20 + :height 22 + :color color-white}) + +(defn action-buttons-container [animation? offset-y] + ;; todo fix overlaying of parent view + {:position :absolute + :right 0 + :height 230 + :width 230 + :bottom 24 + :transform [{:translateY (if animation? offset-y 1)}]}) diff --git a/src/status_im/chats_list/views/chat_list_item.cljs b/src/status_im/chats_list/views/chat_list_item.cljs new file mode 100644 index 0000000000..d2a5fff6e8 --- /dev/null +++ b/src/status_im/chats_list/views/chat_list_item.cljs @@ -0,0 +1,13 @@ +(ns status-im.chats-list.views.chat-list-item + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text + image + touchable-highlight]] + [status-im.chats-list.views.inner-item :refer [chat-list-item-inner-view]] + [taoensso.timbre :as log])) + +(defn chat-list-item [[chat-id chat]] + [touchable-highlight {:on-press #(dispatch [:navigate-to :chat chat-id])} + [view + [chat-list-item-inner-view (assoc chat :chat-id chat-id)]]]) diff --git a/src/status_im/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs new file mode 100644 index 0000000000..70ab294202 --- /dev/null +++ b/src/status_im/chats_list/views/inner_item.cljs @@ -0,0 +1,106 @@ +(ns status-im.chats-list.views.inner-item + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] + [status-im.components.react :refer [view image icon text]] + [status-im.components.chat-icon.screen :refer [chat-icon-view-chat-list]] + [status-im.models.commands :refer [parse-command-message-content]] + [status-im.chats-list.styles :as st] + [status-im.utils.utils :refer [truncate-str]] + [status-im.i18n :refer [get-contact-translated label label-pluralize]] + [status-im.utils.datetime :as time] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.constants :refer [console-chat-id + content-type-command + content-type-command-request] :as c] + [taoensso.timbre :as log])) + +(defmulti message-content (fn [{:keys [content-type] :as message}] content-type)) + +(defn command-content + [{{:keys [command params]} :content}] + (let [kw (keyword (str "t/command-text-" (name command)))] + (label kw params))) + +(defmethod message-content content-type-command + [message] + (command-content message)) + +(defmethod message-content c/content-type-wallet-command + [message] + (command-content message)) + +(defmethod message-content content-type-command-request + [{{:keys [content]} :content}] + content) + +(defmethod message-content :default + [{:keys [content]}] + content) + +(defn message-content-text [message] + (let [content (message-content message)] + (if (str/blank? content) + [text {:style st/last-message-text-no-messages} + (label :t/no-messages)] + [text {:style st/last-message-text + :number-of-lines 2} + content]))) + +(defview message-status [{:keys [chat-id contacts]} + {:keys [message-id message-status user-statuses message-type outgoing] :as msg}] + [app-db-message-status-value [:get-in [:message-statuses message-id :status]]] + (let [delivery-status (get-in user-statuses [chat-id :status])] + (when (and outgoing + (or (some #(= (keyword %) :seen) [delivery-status + message-status + app-db-message-status-value]) + (and (= (keyword message-type) :group-user-message) + (and (= (count user-statuses) (count contacts)) + (every? (fn [[_ {:keys [status]}]] + (= (keyword status) :seen)) user-statuses))) + (= chat-id console-chat-id))) + [image {:source {:uri :icon_ok_small} + :style st/status-image}]))) + +(defn message-timestamp [{:keys [timestamp]}] + (when timestamp + [text {:style st/datetime-text} + (time/to-short-str timestamp)])) + +(defview unviewed-indicator [chat-id] + [unviewed-messages [:unviewed-messages-count chat-id]] + (when (pos? unviewed-messages) + [view st/new-messages-container + [text {:style st/new-messages-text + :font :medium} + unviewed-messages]])) + +(defn chat-list-item-inner-view [{:keys [chat-id name color last-message + online group-chat contacts] :as chat}] + (let [last-message (or (first (sort-by :clock-value > (:messages chat))) + last-message) + name (or (get-contact-translated chat-id :name name) + (generate-gfy))] + [view st/chat-container + [view st/chat-icon-container + [chat-icon-view-chat-list chat-id group-chat name color online]] + [view st/item-container + [view st/name-view + [text {:style st/name-text + :font :medium} + (if (str/blank? name) + (generate-gfy) + (truncate-str name 30))] + (when group-chat + [icon :group st/group-icon]) + (when group-chat + [text {:style st/memebers-text} + (label-pluralize (inc (count contacts)) :t/members)])] + [message-content-text last-message]] + [view + (when last-message + [view st/status-container + [message-status chat last-message] + [message-timestamp last-message]]) + [unviewed-indicator chat-id]]])) diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs new file mode 100644 index 0000000000..151012900f --- /dev/null +++ b/src/status_im/commands/handlers/jail.cljs @@ -0,0 +1,111 @@ +(ns status-im.commands.handlers.jail + (:require [re-frame.core :refer [after dispatch subscribe trim-v debug]] + [status-im.utils.handlers :as u] + [status-im.utils.utils :refer [http-get show-popup]] + [status-im.components.status :as status] + [status-im.utils.types :refer [json->clj]] + [status-im.commands.utils :refer [generate-hiccup reg-handler]] + [clojure.string :as s] + [status-im.components.react :as r] + [status-im.models.commands :as cm] + [status-im.constants :refer [console-chat-id]] + [status-im.i18n :refer [get-contact-translated]] + [taoensso.timbre :as log])) + +(defn render-command + [db [chat-id message-id markup]] + (let [hiccup (generate-hiccup markup)] + (assoc-in db [:rendered-commands chat-id message-id] hiccup))) + +(defn command-handler! + [_ [chat-id + {:keys [command-message] :as parameters} + {:keys [result error]}]] + (let [{:keys [context returned]} result + {handler-error :error} returned] + (log/debug "command handler: " result error parameters) + (cond + handler-error + (log/debug :error-from-handler handler-error + :chat-id chat-id + :command command-message) + + result + (let [command' (assoc command-message :handler-data returned) + parameters' (assoc parameters :command command')] + (if (:eth_sendTransaction context) + (dispatch [:wait-for-transaction (:id command-message) parameters']) + (dispatch [:prepare-command! chat-id parameters']))) + + (not (or error handler-error)) + (dispatch [:prepare-command! chat-id parameters]) + + :else nil))) + +(defn suggestions-handler! + [{:keys [contacts] :as db} [{:keys [chat-id]} {:keys [result]}]] + (let [{:keys [markup webViewUrl]} (:returned result) + {:keys [dapp? dapp-url]} (get contacts chat-id) + hiccup (generate-hiccup markup) + web-view-url (if (and (= webViewUrl "dapp-url") dapp? dapp-url) + (get-contact-translated chat-id :dapp-url dapp-url) + webViewUrl)] + (-> db + (assoc-in [:suggestions chat-id] hiccup) + (assoc-in [:web-view-url chat-id] web-view-url) + (assoc-in [:has-suggestions? chat-id] (or hiccup web-view-url))))) + +(defn suggestions-events-handler! + [{:keys [current-chat-id] :as db} [[n data]]] + (log/debug "Suggestion event: " data) + (let [{:keys [dapp?] :as contact} (get-in db [:contacts current-chat-id]) + command? (= :command (:type (cm/get-chat-command db)))] + (case (keyword n) + :set-value (if command? + (dispatch [:fill-chat-command-content data]) + (when dapp? + (dispatch [:set-chat-input-text data]))) + ;; todo show error? + nil))) + +(defn command-preview + [_ [command-message {:keys [result]}]] + (let [result' (:returned result)] + (dispatch [:send-chat-message + (if result' + (assoc command-message + :preview (generate-hiccup result') + :preview-string (str result')) + command-message)]))) + +(defn print-error-message! [message] + (fn [_ params] + (when (:error (last params)) + (show-popup "Error" (s/join "\n" [message params])) + (log/debug message params)))) + +(reg-handler ::render-command render-command) + +(reg-handler :command-handler! + (after (print-error-message! "Error on command handling")) + (u/side-effect! command-handler!)) + +(reg-handler :suggestions-handler + [(after #(dispatch [:animate-show-response])) + (after (print-error-message! "Error on param suggestions")) + (after (fn [_ [{:keys [command]}]] + (when (= :on-send (keyword (:suggestions-trigger command))) + #_(when (:webViewUrl (:returned result)) + (dispatch [:set-soft-input-mode :pan])) + (r/dismiss-keyboard!))))] + suggestions-handler!) +(reg-handler :suggestions-event! (u/side-effect! suggestions-events-handler!)) + +(reg-handler :command-preview + (after (print-error-message! "Error on command preview")) + (u/side-effect! command-preview)) + +(reg-handler :set-local-storage + (fn [{:keys [current-chat-id] :as db} [{:keys [data] :as event}]] + (log/debug "Got event: " event) + (assoc-in db [:local-storage current-chat-id] data))) diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs new file mode 100644 index 0000000000..17179b3cb7 --- /dev/null +++ b/src/status_im/commands/handlers/loading.cljs @@ -0,0 +1,183 @@ +(ns status-im.commands.handlers.loading + (:require [re-frame.core :refer [path after dispatch subscribe trim-v debug]] + [status-im.utils.handlers :as u] + [status-im.utils.utils :refer [http-get show-popup]] + [clojure.string :as s] + [status-im.data-store.commands :as commands] + [status-im.components.status :as status] + [status-im.utils.types :refer [json->clj]] + [status-im.commands.utils :refer [reg-handler]] + [status-im.constants :refer [console-chat-id wallet-chat-id]] + [taoensso.timbre :as log] + [status-im.utils.homoglyph :as h] + [status-im.utils.js-resources :as js-res] + [clojure.string :as str])) + +(def commands-js "commands.js") + +(defn load-commands! + [{:keys [current-chat-id contacts]} [identity]] + (let [identity (or identity current-chat-id) + contact (or (get contacts identity) + {:whisper-identity identity})] + (when identity + (dispatch [::fetch-commands! contact]))) + ;; todo uncomment + #_(if-let [{:keys [file]} (commands/get-by-chat-id identity)] + (dispatch [::parse-commands! identity file]) + (dispatch [::fetch-commands! identity]))) + +(defn fetch-commands! + [db [{:keys [whisper-identity dapp? dapp-url]}]] + (when true + ;-let [url (get-in db [:chats identity :dapp-url])] + (cond + (= console-chat-id whisper-identity) + (dispatch [::validate-hash whisper-identity js-res/console-js]) + + (= wallet-chat-id whisper-identity) + (dispatch [::validate-hash whisper-identity js-res/wallet-js]) + + (and dapp? dapp-url) + (dispatch [::validate-hash whisper-identity js-res/dapp-js]) + + :else + (dispatch [::validate-hash whisper-identity js-res/commands-js]) + #_(http-get (s/join "/" [url commands-js]) + + #(dispatch [::validate-hash identity %]) + #(dispatch [::loading-failed! identity ::file-was-not-found]))))) + +(defn dispatch-loaded! + [db [identity file]] + (if (::valid-hash db) + (dispatch [::parse-commands! identity file]) + (dispatch [::loading-failed! identity ::wrong-hash]))) + +(defn get-hash-by-identity + [db identity] + (get-in db [:contacts identity :dapp-hash])) + +(defn get-hash-by-file + [file] + ;; todo tbd hashing algorithm + (hash file)) + +(defn parse-commands! [_ [identity file]] + (status/parse-jail identity file + (fn [result] + (let [{:keys [error result]} (json->clj result)] + (log/debug "Error parsing commands: " error result) + (if error + (dispatch [::loading-failed! identity ::error-in-jail error]) + (if identity + (dispatch [::add-commands identity file result]) + (dispatch [::add-all-commands result]))))))) + +(defn validate-hash + [db [identity file]] + (let [valid? true + ;; todo check + #_(= (get-hash-by-identity db identity) + (get-hash-by-file file))] + (assoc db ::valid-hash valid?))) + +(defn mark-as [as coll] + (->> coll + (map (fn [[k v]] [k (assoc v :type as)])) + (into {}))) + +(defn filter-forbidden-names [account id commands] + (->> commands + (remove (fn [[_ {:keys [registered-only]}]] + (and (not (:address account)) + registered-only))) + (remove (fn [[n]] + (and + (not (= console-chat-id id)) + (h/matches (name n) "password")))) + (into {}))) + +(defn add-commands + [db [id _ {:keys [commands responses autorun]}]] + (let [account @(subscribe [:get-current-account]) + commands' (filter-forbidden-names account id commands) + responses' (filter-forbidden-names account id responses)] + (-> db + (assoc-in [id :commands] (mark-as :command commands')) + (assoc-in [id :responses] (mark-as :response responses')) + (assoc-in [id :commands-loaded] true) + (assoc-in [id :autorun] autorun)))) + +(defn save-commands-js! + [_ [id file]] + (commands/save {:chat-id id :file file})) + +(defn loading-failed! + [db [id reason details]] + (let [url (get-in db [:chats id :dapp-url])] + (let [m (s/join "\n" ["commands.js loading failed" + url + id + (name reason) + details])] + (show-popup "Error" m) + (log/debug m)))) + +(reg-handler :load-commands! (u/side-effect! load-commands!)) +(reg-handler ::fetch-commands! (u/side-effect! fetch-commands!)) + +(reg-handler ::validate-hash + (after dispatch-loaded!) + validate-hash) + +(reg-handler ::parse-commands! (u/side-effect! parse-commands!)) + +(reg-handler ::add-commands + [(path :chats) + (after save-commands-js!) + (after #(dispatch [:check-autorun])) + (after (fn [_ [id]] + (dispatch [:invoke-commands-loading-callbacks id]) + (dispatch [:invoke-chat-loaded-callbacks id])))] + add-commands) + +(reg-handler ::add-all-commands + (fn [db [{:keys [commands responses]}]] + (assoc db :all-commands {:commands (mark-as :command commands) + :responses (mark-as :response responses)}))) + +(reg-handler ::loading-failed! (u/side-effect! loading-failed!)) + +(reg-handler :add-commands-loading-callback + (fn [db [chat-id callback]] + (update-in db [::commands-callbacks chat-id] conj callback))) + +(reg-handler :invoke-commands-loading-callbacks + (u/side-effect! + (fn [db [chat-id]] + (let [callbacks (get-in db [::commands-callbacks chat-id])] + (doseq [callback callbacks] + (callback)) + (dispatch [::clear-commands-callbacks chat-id]))))) + +(reg-handler ::clear-commands-callbacks + (fn [db [chat-id]] + (assoc-in db [::commands-callbacks chat-id] nil))) + +(reg-handler :load-default-contacts! + (u/side-effect! + (fn [{:keys [chats]}] + (doseq [[id {:keys [name photo-path public-key add-chat? + dapp? dapp-url dapp-hash] :as contact}] js-res/default-contacts] + (let [id (clojure.core/name id)] + (when-not (chats id) + (when add-chat? + (dispatch [:add-chat id {:name (:en name)}])) + (dispatch [:add-contacts [{:whisper-identity id + :name (:en name) + :photo-path photo-path + :public-key public-key + :dapp? dapp? + :dapp-url (:en dapp-url) + :dapp-hash dapp-hash}]]))))))) diff --git a/src/status_im/commands/utils.cljs b/src/status_im/commands/utils.cljs new file mode 100644 index 0000000000..6ed45cd2bf --- /dev/null +++ b/src/status_im/commands/utils.cljs @@ -0,0 +1,50 @@ +(ns status-im.commands.utils + (:require [clojure.set :as set] + [clojure.walk :as w] + [status-im.components.react :refer [text scroll-view view web-view + image touchable-highlight]] + [re-frame.core :refer [dispatch trim-v debug]] + [status-im.utils.handlers :refer [register-handler]])) + +(def command-prefix "c ") + +(defn json->clj [json] + (when-not (= json "undefined") + (js->clj (.parse js/JSON json) :keywordize-keys true))) + +(def elements + {:text text + :view view + :scroll-view scroll-view + :web-view web-view + :image image + :touchable touchable-highlight}) + +(defn get-element [n] + (elements (keyword (.toLowerCase n)))) + +(def events #{:onPress}) + +(defn wrap-event [event] + #(dispatch [:suggestions-event! event])) + +(defn check-events [m] + (let [ks (set (keys m)) + evs (set/intersection ks events)] + (reduce #(update %1 %2 wrap-event) m evs))) + +(defn generate-hiccup [markup] + ;; todo implement validation + (w/prewalk + (fn [el] + (if (and (vector? el) (string? (first el))) + (-> el + (update 0 get-element) + (update 1 check-events)) + el)) + markup)) + +(defn reg-handler + ([name handler] (reg-handler name nil handler)) + ([name middleware handler] + (register-handler name [trim-v middleware] handler))) diff --git a/src/status_im/components/action_button.cljs b/src/status_im/components/action_button.cljs new file mode 100644 index 0000000000..6ee896d1ed --- /dev/null +++ b/src/status_im/components/action_button.cljs @@ -0,0 +1,7 @@ +(ns status-im.components.action-button + (:require [reagent.core :as r])) + +(def class (js/require "react-native-action-button")) + +(def action-button (r/adapt-react-class (.-default class))) +(def action-button-item (r/adapt-react-class (.. class -default -Item))) diff --git a/src/status_im/components/animation.cljs b/src/status_im/components/animation.cljs new file mode 100644 index 0000000000..21a6cac5c1 --- /dev/null +++ b/src/status_im/components/animation.cljs @@ -0,0 +1,57 @@ +(ns status-im.components.animation + (:require [status-im.components.react :refer [animated]])) + +(defn start + ([anim] (.start anim)) + ([anim callback] (.start anim callback))) + +(defn timing [anim-value config] + (.timing animated anim-value (clj->js config))) + +(defn spring [anim-value config] + (.spring animated anim-value (clj->js config))) + +(defn decay [anim-value config] + (.decay animated anim-value (clj->js config))) + +(defn anim-sequence [animations] + (.sequence animated (clj->js animations))) + +(defn parallel [animations] + (.parallel animated (clj->js animations))) + +(defn anim-delay [duration] + (.delay animated duration)) + +(defn event [config] + (.event animated (clj->js [nil, config]))) + +(defn add-listener [anim-value listener] + (.addListener anim-value listener)) + +(defn remove-all-listeners [anim-value] + (.removeAllListeners anim-value)) + +(defn stop-animation [anim-value] + (.stopAnimation anim-value)) + +(defn value [anim-value] + (.-value anim-value)) + +(defn set-value [anim-value value] + (.setValue anim-value value)) + +(defn create-value [value] + (js/ReactNative.Animated.Value. value)) + +(defn x [value-xy] + (.-x value-xy)) + +(defn y [value-xy] + (.-y value-xy)) + +(defn get-layout [value-xy] + (js->clj (.getLayout value-xy))) + +(defn create-value-xy [x y] + (js/ReactNative.Animated.ValueXY. (clj->js {:x x, :y y}))) diff --git a/src/status_im/components/camera.cljs b/src/status_im/components/camera.cljs new file mode 100644 index 0000000000..d49fb7caf0 --- /dev/null +++ b/src/status_im/components/camera.cljs @@ -0,0 +1,16 @@ +(ns status-im.components.camera + (:require [reagent.core :as r] + [clojure.walk :refer [keywordize-keys]])) + +(def camera-class (js/require "react-native-camera")) + +(defn constants [t] + (-> (aget camera-class "default" "constants" t) + (js->clj) + (keywordize-keys))) + +(def aspects (constants "Aspect")) +(def capture-targets (constants "CaptureTarget")) + +(defn camera [props] + (r/create-element (.-default camera-class) (clj->js (merge {:inverted true} props)))) diff --git a/src/status_im/components/carousel/carousel.cljs b/src/status_im/components/carousel/carousel.cljs new file mode 100644 index 0000000000..5d6681d1b0 --- /dev/null +++ b/src/status_im/components/carousel/carousel.cljs @@ -0,0 +1,208 @@ +(ns status-im.components.carousel.carousel + (:require [reagent.impl.component :as rc] + [status-im.components.react :refer [view + scroll-view + touchable-without-feedback + text]] + [status-im.components.carousel.styles :as st] + [taoensso.timbre :as log] + [status-im.components.react :as r])) + + +(defn window-page-width [] + (.-width (.get (.. r/react-native -Dimensions) "window"))) + +(def defaults {:gap 8 + :sneak 8 + :pageStyle {} + :scrollThreshold 20}) + +(defn get-active-page [data] + (get data :activePage 0)) + +(defn get-sneak [{:keys [sneak gap count]}] + (if (> (or count 2) 1) + (or sneak (:sneak defaults)) + gap)) + +(defn get-gap [data] + (get data :gap (:gap defaults))) + +(defn get-count [data] + (get data :count)) + +(defn compute-page-width + ([gap sneak] + (compute-page-width (window-page-width) gap sneak)) + ([window-page-width gap sneak] + (- window-page-width (+ (* 2 gap) (* 2 sneak))))) + +(defn get-page-width [data] + (get data :pageWidth (compute-page-width (get-gap data) (get-sneak data)))) + +(defn get-page-style [data] + (let [data-style (get data :pageStyle {})] + (merge (:pageStyle defaults) data-style))) + +(defn get-scroll-threshold [data] + (get data :scrollThreshold (:scrollThreshold defaults))) + +(defn apply-props [component props] + (let [sneak (get-sneak props) + page-width (get-page-width props) + style (get-page-style props) + gap (quot (- (window-page-width) + (* 2 sneak) + page-width) + 2) + count (get-count props)] + (reagent.core/set-state component {:sneak sneak + :pageWidth page-width + :pageStyle style + :gap gap + :count count}))) + +(defn scroll-to [component x y] + (.scrollTo (.-scrollView component) (clj->js {:y y + :x x}))) + +(defn get-current-position [event] + (.-x (.-contentOffset (.-nativeEvent event)))) + +(defn get-page-position [state page] + (let [page-width (get-page-width state) + gap (get-gap state) + sneak (get-sneak state) + count (get-count state) + addition (condp = page + 0 gap + (dec count) (- (* 3 gap) (* sneak 2)) + (- sneak (* gap 2)))] + (+ (* page page-width) + (* (dec page) gap) + addition))) + +(defn go-to-page [component page] + (let [props (reagent.core/props component) + state (reagent.core/state component) + page-width (get-page-width state) + gap (get-gap state) + page-position (get-page-position state page)] + (log/debug "go-to-page: props-page-width=" page-width "; gap=" gap + "; page-position=" page-position "; page: " page) + (reagent.core/set-state component {:scrolling? true}) + (js/setTimeout #(reagent.core/set-state component {:scrolling? false}) 200) + (scroll-to component page-position 0) + (reagent.core/set-state component {:activePage page}) + (when (:onPageChange props) + ((:onPageChange props) page)))) + +(defn on-scroll-end [event component starting-position] + (let [props (reagent.core/props component) + state (reagent.core/state component) + scroll-threshold (get-scroll-threshold props) + current-page (get-active-page state) + current-position (get-current-position event) + page-count (get-count state) + direction (cond + (> current-position (+ starting-position scroll-threshold)) 1 + (< current-position (- starting-position scroll-threshold)) -1 + :else 0) + new-page (+ current-page direction)] + (log/debug state "on-scroll-end: starting position=" starting-position + "; current-position=" current-position "; direction=" direction + "; current-page=" current-page "; new-page=" new-page) + (if (and (not= current-page new-page) + (< -1 new-page page-count)) + (go-to-page component new-page) + (scroll-to component starting-position 0)))) + +(defn component-will-mount [component new-args] + (let [props (reagent.core/props component)] + (log/debug "component-will-mount: new-args=" new-args) + (apply-props component props))) + +(defn component-did-mount [component] + (let [props (reagent.core/props component) + initial-page (.-initialPage props)] + (log/debug "component-did-mount: initial-page="initial-page) + (when (pos? initial-page) + (go-to-page component initial-page)))) + +(defn component-will-update [component new-argv] + (log/debug "component-will-update: ")) + +(defn component-did-update [component old-argv] + (log/debug "component-did-update")) + +(defn component-will-receive-props [component new-argv] + (let [props (rc/extract-props new-argv)] + (log/debug "component-will-receive-props: props=" props) + (apply-props component props))) + +(defn get-event-width [event] + (.-width (.-layout (.-nativeEvent event)))) + +(defn on-layout-change [event component] + (let [state (reagent.core/state component) + page-width (compute-page-width (get-event-width event) + (get-gap state) + (get-sneak state)) + state-page-width (get-page-width state) + active-page (get-active-page state) + gap (get-gap state) + page-position (* active-page (+ page-width gap))] + (log/debug "Layout changed: " " page-width=" page-width "; state-page-width=" state-page-width) + (if (not= page-width state-page-width) + (do + (reagent.core/set-state component {:pageWidth page-width}) + (.setState component {:layout (.-layout (.-nativeEvent event))})) + (scroll-to component page-position 0)))) + +(defn get-pages [component data children] + (let [page-width (get-page-width data) + page-style (get-page-style data) + gap (get-gap data) + sneak (get-sneak data) + count (get-count data)] + (doall (map-indexed (fn [index child] + (let [page-index index + touchable-data {:key index + :onPress #(go-to-page component page-index)}] + [touchable-without-feedback touchable-data + [view {:style [(st/page index count page-width gap) + page-style] + :onLayout #(log/debug "view onLayout" %)} + + child]])) children)))) + +(defn reagent-render [data children] + (let [starting-position (atom 0) + component (reagent.core/current-component) + state (reagent.core/state component) + sneak (get-sneak state) + gap (get-gap state)] + (log/debug "reagent-render: " data state) + [view {:style st/scroll-view-container} + [scroll-view {:contentContainerStyle (st/content-container sneak gap) + :automaticallyAdjustContentInsets false + :bounces false + :decelerationRate 0.9 + :horizontal true + :onLayout #(on-layout-change % component) + :scrollEnabled (not (get state :scrolling?)) + :onScrollBeginDrag #(reset! starting-position (get-current-position %)) + :onScrollEndDrag #(on-scroll-end % component @starting-position) + :showsHorizontalScrollIndicator false + :ref #(set! (.-scrollView component) %)} + (get-pages component state children)]])) + +(defn carousel [data children] + (let [component-data {:component-did-mount component-did-mount + :component-will-mount component-will-mount + :component-will-receive-props component-will-receive-props + :component-will-update component-will-update + :component-did-update component-did-update + :display-name "carousel" + :reagent-render reagent-render}] + (reagent.core/create-class component-data))) diff --git a/src/status_im/components/carousel/styles.cljs b/src/status_im/components/carousel/styles.cljs new file mode 100644 index 0000000000..4c4e84cabd --- /dev/null +++ b/src/status_im/components/carousel/styles.cljs @@ -0,0 +1,19 @@ +(ns status-im.components.carousel.styles) + +(def scroll-view-container + {:flex 1}) + +(defn content-container [sneak gap] + {:paddingLeft (quot gap 2) + :paddingRight (quot gap 2)}) + +(defn page [index count page-width gap] + (let [margin (quot gap 2) + left-spacing (if (zero? index) gap 0) + right-spacing (if (and (= index (dec count)) + (> count 1)) + gap 0)] + {:width page-width + :justifyContent :center + :marginLeft (+ margin left-spacing) + :marginRight (+ margin right-spacing)})) diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs new file mode 100644 index 0000000000..8e4797da40 --- /dev/null +++ b/src/status_im/components/chat_icon/screen.cljs @@ -0,0 +1,178 @@ +(ns status-im.components.chat-icon.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + image + icon]] + [taoensso.timbre :as log] + [status-im.components.icons.custom-icons :refer [oct-icon]] + [status-im.components.chat-icon.styles :as st] + [status-im.components.styles :refer [default-chat-color]] + [status-im.resources :as resources] + [status-im.constants :refer [console-chat-id]] + [clojure.string :as s])) + +(defn default-chat-icon [name styles] + [view (:default-chat-icon styles) + [text {:style (:default-chat-icon-text styles)} + (first name)]]) + +(defn chat-icon [photo-path {:keys [size]}] + (let [photo (if (s/starts-with? photo-path "contacts://") + (->> (s/replace photo-path #"contacts://" "") + (keyword) + (get resources/contacts)) + {:uri photo-path})] + [image {:source photo + :style (merge st/default-image-style + (st/image-style size))}])) + +(defn dapp-badge [styles] + [view (:online-view-wrapper styles) + [view (:online-view styles) + [view + [view (:online-dot-left styles)] + [view (:online-dot-right styles)]]]]) + +(defn contact-badge [type styles] + (when (= type :edit) + [view (:online-view styles) + (case type + :online [view + [view (:online-dot-left styles)] + [view (:online-dot-right styles)]] + :edit [view + [oct-icon {:name :pencil + :style st/photo-pencil}]])])) + +(defview pending-contact-badge + [chat-id {:keys [pending-wrapper pending-outer-circle pending-inner-circle]}] + [pending-contact? [:get-in [:chats chat-id :pending-contact?]]] + (when pending-contact? + [view pending-wrapper + [view pending-outer-circle + [view pending-inner-circle]]])) + +(defview chat-icon-view [chat-id group-chat name online styles] + [photo-path [:chat-photo chat-id] + dapp? [:get-in [:contacts chat-id :dapp?]]] + [view (:container styles) + (if-not (s/blank? photo-path) + [chat-icon photo-path styles] + [default-chat-icon name styles]) + (when dapp? + [dapp-badge styles]) + [pending-contact-badge chat-id styles]]) + +(defn chat-icon-view-chat-list [chat-id group-chat name color online] + [chat-icon-view chat-id group-chat name online + {:container st/container-chat-list + :online-view-wrapper st/online-view-wrapper + :online-view st/online-view + :online-dot-left st/online-dot-left + :online-dot-right st/online-dot-right + :pending-wrapper st/pending-wrapper + :pending-outer-circle st/pending-outer-circle + :pending-inner-circle st/pending-inner-circle + :size 40 + :chat-icon st/chat-icon-chat-list + :default-chat-icon (st/default-chat-icon-chat-list color) + :default-chat-icon-text st/default-chat-icon-text}]) + +(defn chat-icon-view-action [chat-id group-chat name color online] + ^{:key chat-id} + [chat-icon-view chat-id group-chat name online + {:container st/container + :online-view-wrapper st/online-view-wrapper + :online-view st/online-view + :online-dot-left st/online-dot-left + :online-dot-right st/online-dot-right + :pending-wrapper st/pending-wrapper + :pending-outer-circle st/pending-outer-circle + :pending-inner-circle st/pending-inner-circle + :size 36 + :chat-icon st/chat-icon-view-action + :default-chat-icon (st/default-chat-icon-view-action color) + :default-chat-icon-text st/default-chat-icon-text}]) + +(defn chat-icon-view-menu-item [chat-id group-chat name color online] + ^{:key chat-id} + [chat-icon-view chat-id group-chat name online + {:container st/container-menu-item + :online-view-wrapper st/online-view-menu-wrapper + :online-view st/online-view-menu-item + :online-dot-left st/online-dot-left-menu-item + :online-dot-right st/online-dot-right-menu-item + :pending-wrapper st/pending-view-menu-wrapper + :pending-outer-circle st/pending-outer-circle + :pending-inner-circle st/pending-inner-circle + :size 24 + :chat-icon st/chat-icon-menu-item + :default-chat-icon (st/default-chat-icon-view-action color) + :default-chat-icon-text st/default-chat-icon-text}]) + +(defn chat-icon-message-status [chat-id group-chat name color online] + ^{:key chat-id} + [chat-icon-view chat-id group-chat name online + {:container st/container-message-status + :online-view-wrapper st/online-view-wrapper + :online-view st/online-view + :online-dot-left st/online-dot-left + :online-dot-right st/online-dot-right + :pending-wrapper st/pending-wrapper + :pending-outer-circle st/pending-outer-circle + :pending-inner-circle st/pending-inner-circle + :size 64 + :chat-icon st/chat-icon-message-status + :default-chat-icon (st/default-chat-icon-message-status color) + :default-chat-icon-text st/message-status-icon-text}]) + +(defn contact-icon-view [contact styles] + (let [photo-path (:photo-path contact) + ;; TODO: stub + type :online] + [view (:container styles) + (if-not (s/blank? photo-path) + [chat-icon photo-path styles] + [default-chat-icon (:name contact) styles]) + [contact-badge type styles]])) + +(defn contact-icon-contacts-tab [contact] + [contact-icon-view contact + {:container st/container-chat-list + :online-view st/online-view + :online-dot-left st/online-dot-left + :online-dot-right st/online-dot-right + :size 40 + :chat-icon st/chat-icon-chat-list + :default-chat-icon (st/default-chat-icon-chat-list default-chat-color) + :default-chat-icon-text st/default-chat-icon-text}]) + +(defn profile-icon-view [photo-path name color badge-type] + (let [styles {:container st/container-profile + :online-view st/online-view-profile + :online-dot-left st/online-dot-left-profile + :online-dot-right st/online-dot-right-profile + :size 64 + :chat-icon st/chat-icon-profile + :default-chat-icon (st/default-chat-icon-profile color) + :default-chat-icon-text st/default-chat-icon-text}] + [view (:container styles) + (if (and photo-path (not (empty? photo-path))) + [chat-icon photo-path styles] + [default-chat-icon name styles]) + [contact-badge badge-type styles]])) + +(defview profile-icon [] + [contact [:contact]] + (let [;; TODO: stub + type :online + color default-chat-color] + [profile-icon-view (:photo-path @contact) (:name @contact) color type])) + +(defn my-profile-icon [{{:keys [photo-path name]} :account + edit? :edit?}] + (let [type (if edit? :edit :blank) + color default-chat-color] + [profile-icon-view photo-path name color type])) diff --git a/src/status_im/components/chat_icon/styles.cljs b/src/status_im/components/chat_icon/styles.cljs new file mode 100644 index 0000000000..9f30e1159d --- /dev/null +++ b/src/status_im/components/chat_icon/styles.cljs @@ -0,0 +1,239 @@ +(ns status-im.components.chat-icon.styles + (:require [status-im.components.styles :refer [color-white + online-color]])) + +(defn default-chat-icon [color] + {:margin 4 + :width 40 + :height 40 + :alignItems :center + :justifyContent :center + :borderRadius 20 + :backgroundColor color}) + +(defn default-chat-icon-chat-list [color] + (merge (default-chat-icon color) + {:width 40 + :height 40 + :border-radius 20})) + +(defn default-chat-icon-menu-item [color] + (merge (default-chat-icon color) + {:width 24 + :height 24 + :border-radius 12})) + +(defn default-chat-icon-profile [color] + (merge (default-chat-icon color) + {:width 64 + :height 64 + :border-radius 32})) + +(defn default-chat-icon-view-action [color] + (merge (default-chat-icon color) + {:width 36 + :height 36 + :border-radius 18})) + +(defn default-chat-icon-message-status [color] + (merge (default-chat-icon color) + {:width 64 + :height 64 + :border-radius 32})) + +(def default-chat-icon-text + {:marginTop -2 + :color color-white + :fontSize 16 + :lineHeight 20}) + +(def message-status-icon-text + {:marginTop -2 + :color color-white + :fontSize 24}) + +(def chat-icon + {:margin 4 + :border-radius 20 + :width 40 + :height 40}) + +(def chat-icon-chat-list + (merge chat-icon + {:width 40 + :height 40 + :margin 0})) + +(def chat-icon-menu-item + {:width 24 + :height 24 + :border-radius 12}) + +(def chat-icon-profile + (merge chat-icon + {:width 64 + :height 64 + :border-radius 32})) + +(def chat-icon-view-action + (merge chat-icon + {:width 36 + :height 36 + :border-radius 18 + :margin 0})) + +(def chat-icon-message-status + {:border-radius 32 + :width 64 + :height 64}) + +(def online-view-wrapper + {:position :absolute + :bottom -1 + :right 0 + :width 22 + :height 22 + :border-radius 11 + :background-color :white}) + +(def online-view-menu-wrapper + {:position :absolute + :bottom 0 + :right -1 + :width 16 + :height 16 + :border-radius 8 + :background-color :white}) + +(def online-view + {:position :absolute + :bottom 2 + :right 2 + :width 18 + :height 18 + :border-radius 9 + :background-color online-color}) + +(def online-view-menu-item + (merge online-view + {:width 14 + :height 14 + :border-radius 7 + :bottom 1 + :right 1})) + +(def online-view-profile + (merge online-view + {:width 24 + :height 24 + :border-radius 12})) + +(def online-dot + {:position :absolute + :top 7 + :width 4 + :height 4 + :border-radius 2 + :background-color color-white}) +(def online-dot-left (merge online-dot {:left 4})) +(def online-dot-right (merge online-dot {:left 10})) + +(def photo-pencil + {:margin-left 8 + :margin-right 2 + :margin-top 6 + :font-size 12 + :color :white}) + +(def online-dot-menu-item + (merge online-dot + {:top 4 + :width 3 + :height 3})) +(def online-dot-left-menu-item + (merge online-dot-menu-item {:left 2.5 + :top 5})) +(def online-dot-right-menu-item + (merge online-dot-menu-item {:left 8 + :top 5})) + +(def online-dot-profile + (merge online-dot + {:top 8 + :width 4 + :height 4})) +(def online-dot-left-profile + (merge online-dot-profile {:left 5})) +(def online-dot-right-profile + (merge online-dot-profile {:left 11})) + +(def container + {:width 44 + :height 44}) + +(def container-chat-list + {:width 48 + :height 48}) + +(def container-menu-item + {:width 32 + :height 32}) + +(def container-profile + {:width 72 + :height 72}) + +(def container-message-status + {:margin-top 20}) + +(def default-image-style + {:margin 4}) + +(defn border-style [size] + {:width size + :height size + :border-radius (/ size 2) + :background-color :#b9c8d6 + :padding 0.5}) + +(defn image-style [size] + (let [image-size (dec size)] + {:width image-size + :height image-size + :border-radius (/ image-size 2)})) + +(def pending-wrapper + {:position :absolute + :bottom 4 + :right 5 + :width 12 + :height 12 + :border-radius 6 + :background-color :white + :align-items :center + :justify-content :center}) + +(def pending-view-menu-wrapper + {:position :absolute + :bottom 2 + :right 2 + :width 12 + :height 12 + :border-radius 6 + :background-color :white + :align-items :center + :justify-content :center}) + +(def pending-outer-circle + {:background-color online-color + :width 8 + :height 8 + :border-radius 4 + :align-items :center + :justify-content :center}) + +(def pending-inner-circle + {:background-color :white + :width 4 + :height 4 + :border-radius 2}) diff --git a/src/status_im/components/drag_drop.cljs b/src/status_im/components/drag_drop.cljs new file mode 100644 index 0000000000..708efdfb12 --- /dev/null +++ b/src/status_im/components/drag_drop.cljs @@ -0,0 +1,12 @@ +(ns status-im.components.drag-drop + (:require [status-im.components.react :refer [animated pan-responder]] + [status-im.components.animation :as anim])) + +(defn pan-handlers [pan-responder] + (js->clj (.-panHandlers pan-responder))) + +(defn create-pan-responder [{:keys [on-move on-release]}] + (.create pan-responder + (clj->js {:onStartShouldSetPanResponder (fn [] true) + :onPanResponderMove on-move + :onPanResponderRelease on-release}))) diff --git a/src/status_im/components/drawer/styles.cljs b/src/status_im/components/drawer/styles.cljs new file mode 100644 index 0000000000..d2723dbd59 --- /dev/null +++ b/src/status_im/components/drawer/styles.cljs @@ -0,0 +1,99 @@ +(ns status-im.components.drawer.styles + (:require [status-im.components.styles :refer [color-light-blue-transparent + color-white + color-black + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text2-color + text3-color + color-red]] + [status-im.utils.platform :as p])) + +(def drawer-menu + {:flex 1 + :background-color color-white + :flex-direction :column}) + +(def user-photo-container + {:margin-top 40 + :align-items :center + :justify-content :center}) + +(def user-photo + {:border-radius 32 + :width 64 + :height 64}) + +(def name-container + {:margin-top (if p/ios? -13 -19) + :margin-bottom -16 + :margin-left 16 + :margin-right 16}) + +(def name-input-wrapper + {}) + +(defn name-input-text [valid?] + {:color (if valid? text1-color + color-red) + :text-align :center}) + +(def status-container + {:margin-left 16 + :margin-right 16 + :flex-direction :row + :margin-top 5 + :justify-content :center}) + +(def status-view + {:min-height 56 + :width 200 + :font-size 14 + :text-align :center + :text-align-vertical :top + :color text2-color}) + +(def status-input + (merge status-view + {:padding-left 4 + :padding-top (if p/ios? 0 5)})) + +(def status-text + (merge status-view + {:padding-left 0 + :padding-top 5})) + +(def menu-items-container + {:flex 1 + :margin-top 20 + :align-items :stretch + :flex-direction :column}) + +(def menu-item-touchable + {:height 48 + :paddingLeft 16 + :paddingTop 14}) + +(def menu-item-text + {:font-size 14 + :line-height 21 + :color text1-color}) + +(def name-text + {:color text1-color + :font-size 16}) + +(def switch-users-container + {:padding-vertical 36 + :align-items :center}) + +(def switch-users-text + {:font-size 14 + :line-height 21 + :color text3-color}) + +(def feedback {:text-align :center}) diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs new file mode 100644 index 0000000000..4a8a7ebbb5 --- /dev/null +++ b/src/status_im/components/drawer/view.cljs @@ -0,0 +1,135 @@ +(ns status-im.components.drawer.view + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [reagent.core :as r] + [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [clojure.string :as str] + [cljs.spec :as s] + [status-im.components.react :refer [view + text + text-input + image + drawer-layout + touchable-without-feedback + touchable-opacity]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.status-view.view :refer [status-view]] + [status-im.components.drawer.styles :as st] + [status-im.profile.validations :as v] + [status-im.resources :as res] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.utils.utils :refer [clean-text]] + [status-im.i18n :refer [label]] + [status-im.components.react :refer [dismiss-keyboard!]] + [clojure.string :as str] + [cljs.spec :as s] + [status-im.components.chat-icon.screen :as ci] + [taoensso.timbre :as log])) + +(defonce drawer-atom (atom)) + +(defn open-drawer [] + (.openDrawer @drawer-atom)) + +(defn close-drawer [] + (.closeDrawer @drawer-atom)) + +(defn menu-item [{:keys [name handler]}] + [touchable-opacity {:style st/menu-item-touchable + :onPress (fn [] + (close-drawer) + (handler))} + [text {:style st/menu-item-text + :font :default} + name]]) + +(defn- update-status [new-status] + (when-not (str/blank? new-status) + (dispatch [:check-status-change new-status]) + (dispatch [:account-update {:status new-status}]) + (dispatch [:set-in [:profile-edit :status] new-status]))) + +(defn drawer-menu [] + (let + [account (subscribe [:get-current-account]) + profile (subscribe [:get :profile-edit]) + keyboard-height (subscribe [:get :keyboard-height]) + placeholder (generate-gfy) + status-edit? (r/atom false) + status-text (r/atom nil)] + (fn [] + (let [{:keys [name photo-path status]} @account + {new-name :name + new-status :status} @profile] + [view st/drawer-menu + [touchable-without-feedback {:on-press #(dismiss-keyboard!)} + [view st/drawer-menu + [touchable-opacity {:on-press #(dispatch [:navigate-to :my-profile])} + [view st/user-photo-container + [ci/chat-icon photo-path {:size 64}]]] + [view st/name-container + [text-field + {:line-color :white + :focus-line-color :white + :placeholder placeholder + :editable true + :input-style (st/name-input-text (s/valid? ::v/name (or new-name name))) + :wrapper-style st/name-input-wrapper + :value name + :on-change-text #(dispatch [:set-in [:profile-edit :name] %]) + :on-end-editing #(when (and new-name (not (str/blank? new-name))) + (dispatch [:account-update {:name (clean-text new-name)}]))}]] + [view st/status-container + (if @status-edit? + [text-input {:style st/status-input + :editable true + :multiline true + :auto-focus true + :focus status-edit? + :max-length 140 + :accessibility-label :input + :placeholder (label :t/profile-no-status) + :default-value status + :on-blur #(do + (reset! status-edit? false) + (update-status @status-text)) + :on-change-text #(let [status (clean-text %)] + (reset! status-text status) + (if (str/includes? % "\n") + (do + (reset! status-edit? false) + (update-status status)) + (dispatch [:set-in [:profile-edit :status] status])))}] + [status-view {:style st/status-text + :on-press #(reset! status-edit? true) + :number-of-lines 3 + :status status}])] + [view st/menu-items-container + [menu-item {:name (label :t/profile) + :handler #(dispatch [:navigate-to :my-profile])}] + [menu-item {:name (label :t/settings) + :handler (fn [] + ;; TODO not implemented + )}] + [menu-item {:name (label :t/discover) + :handler #(dispatch [:navigate-to-tab :discover])}] + [menu-item {:name (label :t/contacts) + :handler #(dispatch [:navigate-to-tab :contact-list])}]] + (when (zero? @keyboard-height) + [text {:style st/feedback + :font :default} (label :t/feedback)]) + (when (zero? @keyboard-height) + [view st/switch-users-container + [touchable-opacity {:onPress (fn [] + (close-drawer) + (dispatch [:navigate-to :accounts]))} + [text {:style st/switch-users-text + :font :default} + (label :t/switch-users)]]])]]])))) + +(defn drawer-view [items] + [drawer-layout {:drawerWidth 260 + :renderNavigationView #(r/as-element [drawer-menu]) + :onDrawerSlide dismiss-keyboard! + :ref (fn [drawer] + (reset! drawer-atom drawer))} + items]) diff --git a/src/status_im/components/icons/custom_icons.cljs b/src/status_im/components/icons/custom_icons.cljs new file mode 100644 index 0000000000..8ce0b571ae --- /dev/null +++ b/src/status_im/components/icons/custom_icons.cljs @@ -0,0 +1,8 @@ +(ns status-im.components.icons.custom-icons + (:require [reagent.core :as r])) + +(def ion-icon + (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons"))) + +(def oct-icon + (r/adapt-react-class (js/require "react-native-vector-icons/Octicons"))) \ No newline at end of file diff --git a/src/status_im/components/image_button/styles.cljs b/src/status_im/components/image_button/styles.cljs new file mode 100644 index 0000000000..75782a54aa --- /dev/null +++ b/src/status_im/components/image_button/styles.cljs @@ -0,0 +1,28 @@ +(ns status-im.components.image-button.styles) + +(def image-button + {:position :absolute + :bottom 2 + :right 16 + :flex 1 + :height 50 + :alignItems :center}) + +(def image-button-content + {:flex 1 + :flexDirection :row + :height 50 + :alignItems :center + :alignSelf :center}) + +(def image-button-text + {:flex 1 + :flexDirection :column + :letter-spacing -0.3 + :margin-left 8}) + +(def scan-button-text + (merge image-button-text {:color "#7099e6"})) + +(def show-qr-button-text + (merge image-button-text {:color "#838c93"})) \ No newline at end of file diff --git a/src/status_im/components/image_button/view.cljs b/src/status_im/components/image_button/view.cljs new file mode 100644 index 0000000000..d95f9a3b17 --- /dev/null +++ b/src/status_im/components/image_button/view.cljs @@ -0,0 +1,31 @@ +(ns status-im.components.image-button.view + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text + image + touchable-highlight]] + [status-im.components.styles :refer [icon-scan]] + [status-im.i18n :refer [label]] + [status-im.components.image-button.styles :as st])) + +(defn image-button [{:keys [value style icon handler]}] + [view st/image-button + [touchable-highlight {:on-press handler} + [view st/image-button-content + [image {:source {:uri (or icon :scan_blue)} + :style icon-scan}] + (when text + [text {:style style} value])]]]) + +(defn scan-button [{:keys [show-label? handler]}] + [image-button {:icon :scan_blue + :value (if show-label? + (label :t/scan-qr)) + :style st/scan-button-text + :handler handler}]) + +(defn show-qr-button [{:keys [handler]}] + [image-button {:icon :icon_qr_gray + :value (label :t/show-qr) + :style st/show-qr-button-text + :handler handler}]) \ No newline at end of file diff --git a/src/status_im/components/invertible_scroll_view.cljs b/src/status_im/components/invertible_scroll_view.cljs new file mode 100644 index 0000000000..429d66d6ed --- /dev/null +++ b/src/status_im/components/invertible_scroll_view.cljs @@ -0,0 +1,8 @@ +(ns status-im.components.invertible-scroll-view + (:require [reagent.core :as r])) + +(def class (js/require "react-native-invertible-scroll-view")) + +(defn invertible-scroll-view [props] + (r/create-element class (clj->js (merge {:inverted true} props)))) + diff --git a/src/status_im/components/item_checkbox.cljs b/src/status_im/components/item_checkbox.cljs new file mode 100644 index 0000000000..45c667141d --- /dev/null +++ b/src/status_im/components/item_checkbox.cljs @@ -0,0 +1,5 @@ +(ns status-im.components.item-checkbox + (:require [reagent.core :as r])) + +(def item-checkbox (r/adapt-react-class (js/require "react-native-circle-checkbox"))) + diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs new file mode 100644 index 0000000000..bb5564e0d4 --- /dev/null +++ b/src/status_im/components/main_tabs.cljs @@ -0,0 +1,134 @@ +(ns status-im.components.main-tabs + (:require-macros [reagent.ratom :refer [reaction]] + [status-im.utils.views :refer [defview]] + [cljs.core.async.macros :as am]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [reagent.core :as r] + [status-im.components.react :refer [view + animated-view + text + image + touchable-highlight + get-dimensions + swiper]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.drawer.view :refer [drawer-view]] + [status-im.components.animation :as anim] + [status-im.components.tabs.bottom-shadow :refer [bottom-shadow-view]] + [status-im.chats-list.screen :refer [chats-list]] + [status-im.discover.screen :refer [discover]] + [status-im.contacts.screen :refer [contact-list]] + [status-im.components.tabs.tabs :refer [tabs]] + [status-im.components.tabs.styles :as st] + [status-im.components.styles :as common-st] + [status-im.i18n :refer [label]] + [cljs.core.async :as a])) + +(def tab-list + [{:view-id :chat-list + :title (label :t/chats) + :screen chats-list + :icon :icon_tab_chats + :index 0} + {:view-id :discover + :title (label :t/discover) + :screen discover + :icon :icon_tab_discover + :index 1} + {:view-id :contact-list + :title (label :t/contacts) + :screen contact-list + :icon :icon_tab_contacts + :index 2}]) + +(defn animation-logic [{:keys [offsets val tab-id to-tab-id]}] + (fn [_] + (when-let [offsets @offsets] + (let [from-value (:from offsets) + to-value (:to offsets) + to-tab-id @to-tab-id] + (anim/set-value val from-value) + (when to-value + (anim/start + (anim/timing val {:toValue to-value + :duration 300}) + (when (= tab-id to-tab-id) + (fn [arg] + (when (.-finished arg) + (dispatch [:on-navigated-to-tab])))))))))) + +(def tab->index {:chat-list 0 + :discover 1 + :contact-list 2}) + +(def index->tab (clojure.set/map-invert tab->index)) + +(defn get-tab-index [view-id] + (get tab->index view-id 0)) + +(defn scroll-to [prev-view-id view-id] + (let [p (get-tab-index prev-view-id) + n (get-tab-index view-id)] + (- n p))) + +(defonce scrolling? (atom false)) + +(defn on-scroll-end [swiped? scroll-ended view-id] + (fn [_ state] + (when @scrolling? + (a/put! scroll-ended true)) + (let [{:strs [index]} (js->clj state) + new-view-id (index->tab index)] + (when-not (= view-id new-view-id) + (reset! swiped? true) + (dispatch [:navigate-to-tab new-view-id]))))) + +(defn start-scrolling-loop + "Loop that synchronizes tabs scrolling to avoid an inconsistent state." + [scroll-start scroll-ended] + (am/go-loop [[swiper to] (a/js (merge {:inverted true} props)))) \ No newline at end of file diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs new file mode 100644 index 0000000000..841c86c13c --- /dev/null +++ b/src/status_im/components/react.cljs @@ -0,0 +1,135 @@ +(ns status-im.components.react + (:require [reagent.core :as r] + [status-im.components.styles :as st] + [status-im.utils.utils :as u + :refer [get-react-property get-class adapt-class]] + [status-im.utils.platform :refer [platform-specific]])) + +(def react-native (js/require "react-native")) +(def native-modules (.-NativeModules react-native)) +(def device-event-emitter (.-DeviceEventEmitter react-native)) + +(def linear-gradient-module (js/require "react-native-linear-gradient")) +(def dismiss-keyboard! (js/require "dismissKeyboard")) +(def orientation (js/require "react-native-orientation")) +(def back-android (get-react-property "BackAndroid")) +(def drawer (js/require "react-native-drawer-layout")) + +(def splash-screen (.-SplashScreen native-modules)) + +;; React Components + +(def app-registry (get-react-property "AppRegistry")) +(def navigator (get-class "Navigator")) +(def view (get-class "View")) +(def linear-gradient-class (adapt-class linear-gradient-module)) + +(def status-bar (get-class "StatusBar")) +(def drawer-layout (adapt-class drawer)) + +(def list-view-class (get-class "ListView")) +(def scroll-view (get-class "ScrollView")) +(def web-view (get-class "WebView")) + +(def text-class (get-class "Text")) +(def text-input-class (get-class "TextInput")) +(def image (get-class "Image")) + +(def touchable-without-feedback (get-class "TouchableWithoutFeedback")) +(def touchable-highlight-class (get-class "TouchableHighlight")) +(def touchable-opacity (get-class "TouchableOpacity")) + +(def modal (get-class "Modal")) +(def picker (get-class "Picker")) + +(def pan-responder (.-PanResponder js/ReactNative)) +(def animated (.-Animated js/ReactNative)) +(def animated-view (r/adapt-react-class (.-View animated))) +(def animated-text (r/adapt-react-class (.-Text animated))) + +(def dimensions (.-Dimensions js/ReactNative)) +(def keyboard (.-Keyboard react-native)) + +;; Accessor methods for React Components + +(defn text + ([t] + (r/as-element [text-class t])) + ([{:keys [style font uppercase?] :as opts + :or {font :default}} t & ts] + (r/as-element + (let [font (get-in platform-specific [:fonts (keyword font)]) + ts (cond->> (conj ts t) + uppercase? (map clojure.string/upper-case))] + (vec (concat + [text-class + (-> opts + (dissoc :font) + (assoc :style (merge style font)))] + ts)))))) + +(defn text-input [{:keys [font style] :as opts + :or {font :default}} text] + (let [font (get-in platform-specific [:fonts (keyword font)])] + [text-input-class (merge + {:underline-color-android :transparent + :placeholder-text-color st/text2-color + :placeholder "Type" + :value text} + (-> opts + (dissoc :font) + (assoc :style (merge style font))))])) + +(defn icon + ([n] (icon n {})) + ([n style] + [image {:source {:uri (keyword (str "icon_" (name n)))} + :style style}])) + +(defn list-view [props] + [list-view-class (merge {:enableEmptySections true} props)]) + +(defn touchable-highlight [props content] + [touchable-highlight-class + (merge {:underlay-color :transparent} props) + content]) + +(def picker-item + (when-let [picker (get-react-property "Picker")] + (adapt-class (.-Item picker)))) + +(defn get-dimensions [name] + (js->clj (.get dimensions name) :keywordize-keys true)) + +(defn linear-gradient + [props & children] + (vec (concat [linear-gradient-class (merge {:inverted true} props)] children))) + +(defn list-item [component] + (r/as-element component)) + +;; Image picker + +(def image-picker-class (js/require "react-native-image-crop-picker")) + +(defn show-image-picker [images-fn] + (let [image-picker (.-default image-picker-class)] + (-> image-picker + (.openPicker (clj->js {:multiple false})) + (.then images-fn)))) + +(def swiper (adapt-class (js/require "react-native-swiper"))) + +;; Clipboard + +(defn copy-to-clipboard [text] + (.setString (.-Clipboard react-native) text)) + + +;; Emoji + +(def emoji-picker-class (js/require "react-native-emoji-picker")) + +(def emoji-picker + (let [emoji-picker (.-default emoji-picker-class)] + (r/adapt-react-class emoji-picker))) diff --git a/src/status_im/components/refreshable_text/view.cljs b/src/status_im/components/refreshable_text/view.cljs new file mode 100644 index 0000000000..348b34e94d --- /dev/null +++ b/src/status_im/components/refreshable_text/view.cljs @@ -0,0 +1,67 @@ +(ns status-im.components.refreshable-text.view + (:require [reagent.core :as r] + [reagent.impl.component :as rc] + [status-im.components.react :refer [view + animated-view + text]] + [status-im.components.animation :as anim])) + +(defn start-animation [{:keys [old-value-top + new-value-top + old-value-opacity + new-value-opacity]}] + (anim/start + (anim/timing old-value-top {:toValue 10 + :duration 300})) + (anim/start + (anim/timing new-value-top {:toValue 0 + :duration 300})) + (anim/start + (anim/timing old-value-opacity {:toValue 0 + :duration 300})) + (anim/start + (anim/timing new-value-opacity {:toValue 1.0 + :duration 300}))) + +(defn refreshable-text [{:keys [value]}] + (let [old-value-top (anim/create-value 0) + new-value-top (anim/create-value 0) + old-value-opacity (anim/create-value 0) + new-value-opacity (anim/create-value 1.0) + context {:old-value-top old-value-top + :new-value-top new-value-top + :old-value-opacity old-value-opacity + :new-value-opacity new-value-opacity}] + (r/create-class + {:get-initial-state + (fn [] + {:old-value nil + :value value}) + :component-will-update + (fn [component props] + (let [{new-value :value} (rc/extract-props props) + {old-value :value} (r/props component)] + (r/set-state component {:old-value old-value + :value new-value}) + (anim/set-value old-value-top 0) + (anim/set-value new-value-top -10) + (anim/set-value old-value-opacity 1.0) + (anim/set-value new-value-opacity 0.0) + (start-animation context))) + :reagent-render + (fn [{:keys [style text-style font]}] + (let [component (r/current-component) + {:keys [old-value value]} (r/state component)] + [view style + [animated-view {:style {:position :absolute + :margin-top old-value-top + :opacity old-value-opacity}} + [text {:style text-style + :font font} + old-value]] + [animated-view {:style {:position :absolute + :margin-top new-value-top + :opacity new-value-opacity}} + [text {:style text-style + :font font} + value]]]))}))) diff --git a/src/status_im/components/selectable_field/styles.cljs b/src/status_im/components/selectable_field/styles.cljs new file mode 100644 index 0000000000..98b4e02894 --- /dev/null +++ b/src/status_im/components/selectable_field/styles.cljs @@ -0,0 +1,38 @@ +(ns status-im.components.selectable-field.styles + (:require [status-im.utils.platform :refer [platform-specific]])) + + +(def selectable-field-container + {}) + +(def label-container + {:margin-bottom 13}) + +(def label + {:color "#838c93" + :background-color :transparent + :font-size 14}) + +(def text-container + {:padding 0 + :margin-bottom 18 + :margin 0}) + +(def text + {:font-size 16 + :color "#555555" + :margin-right 16 + :text-align-vertical :top}) + +(defn sized-text + [height] + (let [{:keys [additional-height + margin-top]} (get-in platform-specific [:component-styles :sized-text])] + (merge text {:height (+ additional-height height) + :margin-bottom 0 + :margin-top margin-top + :padding-top 0 + :padding-left 0 + :margin-left 0 + :padding-bottom 0}))) + diff --git a/src/status_im/components/selectable_field/view.cljs b/src/status_im/components/selectable_field/view.cljs new file mode 100644 index 0000000000..64598295dc --- /dev/null +++ b/src/status_im/components/selectable_field/view.cljs @@ -0,0 +1,63 @@ +(ns status-im.components.selectable-field.view + (:require [status-im.components.react :refer [view + text-input + text]] + [reagent.core :as r] + [status-im.components.selectable-field.styles :as st] + [status-im.i18n :refer [label]] + [status-im.utils.platform :as p] + [taoensso.timbre :as log])) + +(defn- on-press-default + [event component] + (log/debug "Pressed " event component) + (r/set-state component {:focused? true})) + +(defn- on-selection-change + [event component] + (let [selection (.-selection (.-nativeEvent event)) + start (.-start selection) + end (.-end selection)] + (log/debug "Selection changed: " start end))) + +(defn- on-layout-text + [event component] + (let [height (.-height (.-layout (.-nativeEvent event))) + {:keys [full-height]} (r/state component)] + (when (and (pos? height) (not full-height)) + (r/set-state component {:full-height height + :measured? true})))) + +(defn- reagent-render + [{:keys [label value editable? props on-press] :as data}] + (let [component (r/current-component) + {:keys [focused? measured? full-height]} (r/state component)] + (log/debug "reagent-render: " data focused? measured? full-height) + [view st/selectable-field-container + [view st/label-container + [text {:style st/label + :font :medium} (or label "")]] + [view st/text-container + (if focused? + [text-input {:style (st/sized-text full-height) + :multiline true + :selectTextOnFocus true + :editable (if p/android? true editable?) + :auto-focus true + :on-selection-change #(on-selection-change % component) + :on-focus #(log/debug "Focused" %) + :on-blur #(r/set-state component {:focused? false}) + :value value}] + [text (merge {:style st/text + :on-press (or on-press + #(on-press-default % component)) + :onLayout #(on-layout-text % component) + :font :default + :ellipsizeMode :middle + :number-of-lines (if measured? 1 0)} (or props {})) (or value "")])]])) + +(defn selectable-field [_] + (let [component-data {:display-name "selectable-field" + :reagent-render reagent-render}] + (reagent.core/create-class component-data))) + diff --git a/src/status_im/components/share.cljs b/src/status_im/components/share.cljs new file mode 100644 index 0000000000..f10be26ffb --- /dev/null +++ b/src/status_im/components/share.cljs @@ -0,0 +1,7 @@ +(ns status-im.components.share + (:require [status-im.utils.platform :as p])) + +(def class (js/require "react-native-share")) + +(defn open [opts] + (.open class (clj->js opts))) \ No newline at end of file diff --git a/src/status_im/components/status.cljs b/src/status_im/components/status.cljs new file mode 100644 index 0000000000..689f87f711 --- /dev/null +++ b/src/status_im/components/status.cljs @@ -0,0 +1,155 @@ +(ns status-im.components.status + (:require-macros + [cljs.core.async.macros :refer [go-loop go]]) + (:require [status-im.components.react :as r] + [status-im.utils.types :as t] + [re-frame.core :refer [dispatch]] + [taoensso.timbre :as log] + [cljs.core.async :refer [json [data] + (.stringify js/JSON (clj->js data))) + +(defn call-jail [chat-id path params callback] + (when status + (call-module + #(do + (log/debug :chat-id chat-id) + (log/debug :path path) + (log/debug :params params) + (let [params' (update params :context assoc + :debug js/goog.DEBUG + :locale i/i18n.locale) + cb (fn [r] + (let [{:keys [result] :as r'} (t/json->clj r) + {:keys [messages]} result] + (log/debug r') + (doseq [{:keys [type message]} messages] + (log/debug (str "VM console(" type ") - " message))) + (callback r')))] + (.callJail status chat-id (cljs->json path) (cljs->json params') cb)))))) + + +(defn set-soft-input-mode [mode] + (when status + (call-module #(.setSoftInputMode status mode)))) + +(def adjust-resize 16) +(def adjust-pan 32) diff --git a/src/status_im/components/status_bar.cljs b/src/status_im/components/status_bar.cljs new file mode 100644 index 0000000000..e34ce4f0ba --- /dev/null +++ b/src/status_im/components/status_bar.cljs @@ -0,0 +1,16 @@ +(ns status-im.components.status-bar + (:require [status-im.components.react :as ui] + [status-im.utils.platform :refer [platform-specific]])) + +(defn status-bar [{type :type + :or {type :default}}] + (let [{:keys [height + bar-style + translucent? + color]} (get-in platform-specific [:component-styles :status-bar type])] + [ui/view + [ui/status-bar {:background-color color + :translucent translucent? + :bar-style bar-style}] + [ui/view {:style {:height height + :background-color color}}]])) \ No newline at end of file diff --git a/src/status_im/components/status_view/view.cljs b/src/status_im/components/status_view/view.cljs new file mode 100644 index 0000000000..7fb752da85 --- /dev/null +++ b/src/status_im/components/status_view/view.cljs @@ -0,0 +1,28 @@ +(ns status-im.components.status-view.view + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] + [status-im.components.react :refer [view text]] + [status-im.utils.platform :refer [platform-specific]])) + +(defn tag-view [tag] + [text {:style {:color "#7099e6"} + :font :medium} + (str tag " ")]) + +(defn status-view [{:keys [style + message-id + status + on-press + number-of-lines] + :or {message-id "msg"}}] + [text {:style style + :on-press on-press + :number-of-lines number-of-lines + :font :default} + (for [[i status] (map-indexed vector (str/split status #" "))] + (if (.startsWith status "#") + ^{:key (str "item-" message-id "-" i)} + [tag-view status] + ^{:key (str "item-" message-id "-" i)} + (str status " ")))]) \ No newline at end of file diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs new file mode 100644 index 0000000000..05dea54728 --- /dev/null +++ b/src/status_im/components/styles.cljs @@ -0,0 +1,82 @@ +(ns status-im.components.styles) + +(def color-transparent "transparent") +(def color-blue "#7099e6") +(def color-blue-transparent "#7099e632") +(def color-black "#000000de") +(def color-purple "#a187d5") +(def color-gray "#838c93de") +(def color-gray2 "#8f838c93") +(def color-gray3 "#00000040") +(def color-steel "#838b91") +(def color-white "white") +(def color-light-blue-transparent "#bbc4cb32") +(def color-light-gray "#EEF2F5") +(def color-red "red") +(def color-separator "#D6D6D6") + +(def text1-color color-black) +(def text1-disabled-color "#555555") +(def text2-color color-gray) +(def text3-color color-blue) +(def text4-color color-white) +(def text5-color "#838c938f") +(def online-color color-blue) +(def new-messages-count-color color-blue-transparent) +(def chat-background color-light-gray) +(def selected-message-color "#E4E9ED") +(def separator-color "#0000001f") +(def default-chat-color color-purple) + +(def flex + {:flex 1}) + +(def icon-search + {:width 17 + :height 17}) + +(def create-icon + {:fontSize 20 + :height 22 + :color :white}) + +(def icon-back + {:width 8 + :height 14}) + +(def icon-add + {:width 14 + :height 14}) + +(def icon-ok + {:width 18 + :height 14}) + +(def icon-qr + {:width 23 + :height 22}) + +(def icon-scan + {:width 18 + :height 18}) + +(def icon-plus + {:width 18 + :height 18}) + +(def icon-close + {:width 12 + :height 12}) + +(def white-form-text-input + {:marginLeft -4 + :fontSize 14 + :color color-white}) + +(def button-input-container + {:flex 1 + :flexDirection :row}) + +(def button-input + {:flex 1 + :flexDirection :column}) diff --git a/src/status_im/components/sync_state/gradient.cljs b/src/status_im/components/sync_state/gradient.cljs new file mode 100644 index 0000000000..c05f90795b --- /dev/null +++ b/src/status_im/components/sync_state/gradient.cljs @@ -0,0 +1,119 @@ +(ns status-im.components.sync-state.gradient + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + text + animated-view + linear-gradient + get-dimensions]] + [status-im.components.sync-state.styles :as st] + [status-im.components.animation :as anim] + [taoensso.timbre :as log])) + +(def gradient-animation-duration 700) +(def synced-disappear-delay 2500) +(def gradient-width 250) +(def in-progress-animation-delay 1500) + +(def window-width (:width (get-dimensions "window"))) + +(declare start-gradient-reverse-animation) + +(defn- start-gradient-animation [{:keys [gradient-position sync-state] :as context}] + (when (= @sync-state :in-progress) + (anim/start + (anim/timing gradient-position + {:toValue (- window-width (/ gradient-width 3)) + :duration gradient-animation-duration}) + (fn [_] + (start-gradient-reverse-animation context))))) + +(defn- start-gradient-reverse-animation [{:keys [gradient-position sync-state] :as context}] + (when (= @sync-state :in-progress) + (anim/start + (anim/timing gradient-position + {:toValue (- 0 (* 2 (/ gradient-width 3))) + :duration gradient-animation-duration}) + (fn [_] + (start-gradient-animation context))))) + +(defn- start-synced-animation [{:keys [sync-state-opacity in-progress-opacity synced-opacity]}] + (anim/start + (anim/timing in-progress-opacity {:toValue 0.0 + :duration 250})) + (anim/start + (anim/timing synced-opacity {:toValue 1.0 + :duration 250}) + (fn [_] + (anim/start + (anim/timing sync-state-opacity {:toValue 0.0 + :duration 250 + :delay synced-disappear-delay}) + (fn [_] + (dispatch [:set :sync-state :done])))))) + +(defn start-in-progress-animation [component] + (r/set-state component + {:pending? true + :animation (js/setTimeout + (fn [] + (dispatch [:set :sync-state :in-progress]) + (r/set-state component {:pending? false})) + in-progress-animation-delay)})) + +(defn start-offline-animation [{:keys [sync-state-opacity]}] + (anim/start + (anim/timing sync-state-opacity {:toValue 0.0 + :duration 250}))) + +(defn clear-pending-animation [component] + (let [{:keys [pending? animation]} (r/state component)] + (when pending? + (r/set-state component {:pending? false}) + (js/clearTimeout animation)))) + + +(defn sync-state-gradient-view [] + (let [sync-state (subscribe [:get :sync-state]) + gradient-position (anim/create-value 0) + sync-state-opacity (anim/create-value 0.0) + in-progress-opacity (anim/create-value 0.0) + synced-opacity (anim/create-value 0.0) + + context {:sync-state sync-state + :gradient-position gradient-position + + :sync-state-opacity sync-state-opacity + :in-progress-opacity in-progress-opacity + :synced-opacity synced-opacity} + on-update (fn [component _] + (case @sync-state + :pending (start-in-progress-animation component) + :in-progress (do + (anim/set-value gradient-position 0) + (anim/set-value sync-state-opacity 1) + (anim/set-value in-progress-opacity 1) + (anim/set-value synced-opacity 0) + (start-gradient-animation context)) + :synced (start-synced-animation context) + :done (clear-pending-animation component) + :offline (do (clear-pending-animation component) + (start-offline-animation context)) + (log/debug "Sync state:" @sync-state)))] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [] + [view st/sync-style-gradient + [animated-view {:style (st/loading-wrapper sync-state-opacity)} + [animated-view {:style (st/gradient-wrapper in-progress-opacity gradient-position)} + [linear-gradient {:colors ["#89b1fe" "#8b5fe4" "#8b5fe4" "#89b1fe"] + :start [0, 1] + :end [1, 1] + :locations [0 0.3 0.7 1] + :style (st/gradient gradient-width)}]] + (when (not= @sync-state :in-progress) + [animated-view {:style (st/synced-wrapper synced-opacity window-width)}])]])}))) \ No newline at end of file diff --git a/src/status_im/components/sync_state/offline.cljs b/src/status_im/components/sync_state/offline.cljs new file mode 100644 index 0000000000..885abbb8fc --- /dev/null +++ b/src/status_im/components/sync_state/offline.cljs @@ -0,0 +1,42 @@ +(ns status-im.components.sync-state.offline + (:require [re-frame.core :refer [subscribe dispatch]] + [reagent.core :as r] + [status-im.components.react :refer [view + text + animated-view + linear-gradient + get-dimensions]] + [status-im.components.sync-state.styles :as st] + [status-im.components.animation :as anim] + [status-im.i18n :refer [label]])) + +(def window-width (:width (get-dimensions "window"))) + +(defn start-offline-animation [offline-opacity] + (anim/start + (anim/timing offline-opacity {:toValue 1.0 + :duration 250}))) + +(defn offline-view [_] + (let [sync-state (subscribe [:get :sync-state]) + network-status (subscribe [:get :network-status]) + offline-opacity (anim/create-value 0.0) + on-update (fn [_ _] + (anim/set-value offline-opacity 0) + (when (or (= @network-status :offline) (= @sync-state :offline)) + (start-offline-animation offline-opacity))) + pending-contact? (subscribe [:chat :pending-contact?]) + view-id (subscribe [:get :view-id])] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [{:keys [top]}] + (when (or (= @network-status :offline) (= @sync-state :offline)) + (let [pending? (and @pending-contact? (= :chat @view-id))] + [animated-view {:style (st/offline-wrapper top offline-opacity window-width pending?)} + [view + [text {:style st/offline-text} + (label :t/offline)]]])))}))) diff --git a/src/status_im/components/sync_state/styles.cljs b/src/status_im/components/sync_state/styles.cljs new file mode 100644 index 0000000000..b6b036597d --- /dev/null +++ b/src/status_im/components/sync_state/styles.cljs @@ -0,0 +1,41 @@ +(ns status-im.components.sync-state.styles) + +(def sync-style-gradient + {:position :relative + :height 0 + :top -2}) + +(defn loading-wrapper [opacity] + {:background-color "#89b1fe" + :opacity opacity + :height 2}) + +(defn gradient-wrapper [in-progress-opacity position] + {:position :absolute + :left position + :opacity in-progress-opacity}) + +(defn gradient [width] + {:width width + :height 2}) + +(defn synced-wrapper [opacity window-width] + {:opacity opacity + :position :absolute + :width window-width + :background-color "#5fc48d" + :height 2}) + +(defn offline-wrapper [top opacity window-width pending?] + {:opacity opacity + :width window-width + :top (+ (+ 56 top) (if pending? 35 0)) + :position :absolute + :background-color "#828b92cc" + :height 35}) + +(def offline-text + {:text-align :center + :color :white + :font-size 14 + :top 8}) diff --git a/src/status_im/components/tabs/bottom_shadow.cljs b/src/status_im/components/tabs/bottom_shadow.cljs new file mode 100644 index 0000000000..c6e3a836a9 --- /dev/null +++ b/src/status_im/components/tabs/bottom_shadow.cljs @@ -0,0 +1,12 @@ +(ns status-im.components.tabs.bottom-shadow + (:require [status-im.components.tabs.styles :as st] + [status-im.components.react :refer [linear-gradient]] + [status-im.utils.platform :refer [platform-specific]])) + +(defn bottom-shadow-view [] + (if (get-in platform-specific [:tabs :tab-shadows?]) + [linear-gradient {:locations [0 0.8 1] + :colors ["rgba(24, 52, 76, 0)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"] + :style (merge + st/bottom-gradient + (get-in platform-specific [:component-styles :bottom-gradient]))}])) \ No newline at end of file diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs new file mode 100644 index 0000000000..0040b76099 --- /dev/null +++ b/src/status_im/components/tabs/styles.cljs @@ -0,0 +1,71 @@ +(ns status-im.components.tabs.styles + (:require [status-im.components.styles :refer [color-white]])) + +(def tabs-height 56) +(def tab-height (- tabs-height 1)) + +(def bottom-gradient + {:position :absolute + :bottom 55 + :left 0 + :right 0}) + +(defn tabs-container [hidden?] + {:position :absolute + :bottom 0 + :left 0 + :right 0 + :height tabs-height + :background-color color-white + :margin-bottom (if hidden? (- tabs-height) 0) + :transform [{:translateY 1}]}) + +(def tabs-container-line + {:border-top-width 1 + :border-top-color "#D7D7D7"}) + +(def tabs-inner-container + {:flexDirection :row + :height tab-height + :opacity 1 + :justifyContent :center + :alignItems :center}) + +(def tab + {:flex 1 + :height tab-height + :justifyContent :center + :alignItems :center}) + +(def tab-title + {:font-size 12 + :height 16 + :min-width 60 + :text-align :center + :color "#6e93d8"}) + +(def tab-icon + {:width 24 + :height 24 + :marginBottom 1 + :align-self :center}) + +(defn tab-container [active?] + {:flex 1 + :height tab-height + :justifyContent :center + :alignItems :center + :padding-top (if active? 0 16)}) + +(defn animated-offset [value] + {:top value + :justifyContent :center + :alignItems :center}) + +(def main-swiper + {:position :absolute + :top 0 + :left 0 + :right 0 + :bottom tabs-height + :shows-pagination false}) diff --git a/src/status_im/components/tabs/tab.cljs b/src/status_im/components/tabs/tab.cljs new file mode 100644 index 0000000000..8474a91256 --- /dev/null +++ b/src/status_im/components/tabs/tab.cljs @@ -0,0 +1,58 @@ +(ns status-im.components.tabs.tab + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + animated-view + text + image + touchable-highlight]] + [reagent.core :as r] + [status-im.components.tabs.styles :as st] + [status-im.components.animation :as anim])) + +(defn animation-logic [val to-value] + (fn [] + (anim/start (anim/spring val {:toValue to-value + :tension 40})))) + +(defview tab [_] + (let [icon-anim-value (anim/create-value 0) + text-anim-value (anim/create-value 0) + icon-reverse-anim-value (anim/create-value 0) + text-reverse-anim-value (anim/create-value 0) + on-update (comp (animation-logic icon-anim-value 0) + (animation-logic text-anim-value 0) + (animation-logic icon-reverse-anim-value 0) + (animation-logic text-reverse-anim-value 30))] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :component-will-receive-props + (fn [] + (anim/set-value icon-anim-value 8) + (anim/set-value text-anim-value 30) + (anim/set-value icon-reverse-anim-value -8) + (anim/set-value text-reverse-anim-value -8)) + :reagent-render + (fn [{:keys [view-id title icon selected-view-id prev-view-id]}] + [touchable-highlight {:style st/tab + :disabled (= view-id selected-view-id) + :onPress #(dispatch [:navigate-to-tab view-id])} + [view {:style (st/tab-container (= selected-view-id view-id))} + [animated-view {:style (st/animated-offset (cond + (= selected-view-id view-id) icon-anim-value + (= prev-view-id view-id) icon-reverse-anim-value + :else 0))} + [image {:source {:uri icon} + :style st/tab-icon}]] + [animated-view {:style (st/animated-offset (cond + (= selected-view-id view-id) text-anim-value + (= prev-view-id view-id) text-reverse-anim-value + :else 0))} + [text {:style st/tab-title} + (if (or (= selected-view-id view-id) + (= prev-view-id view-id)) + title + " ")]]]])}))) diff --git a/src/status_im/components/tabs/tabs.cljs b/src/status_im/components/tabs/tabs.cljs new file mode 100644 index 0000000000..27e7310aab --- /dev/null +++ b/src/status_im/components/tabs/tabs.cljs @@ -0,0 +1,58 @@ +(ns status-im.components.tabs.tabs + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + animated-view + text-input + text + image + touchable-highlight + linear-gradient]] + [reagent.core :as r] + [status-im.components.tabs.styles :as st] + [status-im.components.tabs.tab :refer [tab]] + [status-im.components.animation :as anim] + [status-im.utils.platform :refer [platform-specific]])) + +(defn create-tab [index data selected-view-id prev-view-id] + (let [data (merge data {:key index + :index index + :selected-view-id selected-view-id + :prev-view-id prev-view-id})] + [tab data])) + +(defn animation-logic [{:keys [hidden? val]}] + (let [was-hidden? (atom (not @hidden?))] + (fn [_] + (when (not= @was-hidden? @hidden?) + (let [to-value (if @hidden? 0 (- st/tabs-height))] + (swap! was-hidden? not) + (anim/start + (anim/timing val {:toValue to-value + :duration 300}))))))) + +(defn tabs-container [& children] + (let [chats-scrolled? (subscribe [:get :chats-scrolled?]) + tabs-bar-value (subscribe [:animations :tabs-bar-value]) + shadows? (get-in platform-specific [:tabs :tab-shadows?]) + context {:hidden? chats-scrolled? + :val @tabs-bar-value} + on-update (animation-logic context)] + (anim/set-value @tabs-bar-value 0) + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [& children] + @chats-scrolled? + (into [animated-view {:style (merge (st/tabs-container @chats-scrolled?) + (if-not shadows? st/tabs-container-line)) + :pointerEvents (if @chats-scrolled? :none :auto)}] + children))}))) + +(defn tabs [{:keys [tab-list selected-view-id prev-view-id swiper]}] + [tabs-container + [view st/tabs-inner-container + (doall (map-indexed #(create-tab %1 %2 selected-view-id prev-view-id) tab-list))]]) diff --git a/src/status_im/components/text_field/styles.cljs b/src/status_im/components/text_field/styles.cljs new file mode 100644 index 0000000000..99f8a8ae71 --- /dev/null +++ b/src/status_im/components/text_field/styles.cljs @@ -0,0 +1,45 @@ +(ns status-im.components.text-field.styles + (:require [status-im.utils.platform :refer [platform-specific]])) + + +(def text-field-container + {:position :relative + :height 72 + :padding-top 30 + :padding-bottom 34}) + +(def text-input + {:font-size 16 + :height 34 + :line-height 34 + :padding-bottom 5 + :text-align-vertical :top}) + +(defn label [top font-size color] + (let [input-label-style (get-in platform-specific [:component-styles :input-label])] + (merge input-label-style + {:position :absolute + :top top + :color color + :font-size font-size + :background-color :transparent}))) + +(def label-float + {}) + +(defn underline-container [background-color] + {:background-color background-color + :height 1 + :align-items :center}) + +(defn underline [background-color width] + {:background-color background-color + :height 1 + :width width}) + +(defn error-text [color] + (let [input-error-text-style (get-in platform-specific [:component-styles :input-error-text])] + (merge input-error-text-style + {:color color + :background-color :transparent + :font-size 12}))) diff --git a/src/status_im/components/text_field/view.cljs b/src/status_im/components/text_field/view.cljs new file mode 100644 index 0000000000..3a9c6402f6 --- /dev/null +++ b/src/status_im/components/text_field/view.cljs @@ -0,0 +1,203 @@ +(ns status-im.components.text-field.view + (:require [clojure.string :as s] + [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [reagent.core :as r] + [status-im.components.react :refer [react-native + view + text + animated-text + animated-view + text-input + touchable-opacity]] + [status-im.components.text-field.styles :as st] + [status-im.i18n :refer [label]] + [status-im.components.animation :as anim] + [taoensso.timbre :as log])) + + +(def config {:label-top 16 + :label-bottom 37 + :label-font-large 16 + :label-font-small 13 + :label-animation-duration 200}) + +(def default-props {:wrapper-style {} + :input-style {} + :line-style {} + :editable true + :label-color "#838c93" + :line-color "#0000001f" + :focus-line-color "#0000001f" + :error-color "#d50000" + :secure-text-entry false + :on-focus #() + :on-blur #() + :on-change-text #() + :on-change #()}) + +(defn field-animation [{:keys [top to-top font-size to-font-size + line-width to-line-width]}] + (let [duration (:label-animation-duration config) + animation (anim/parallel [(anim/timing top {:toValue to-top + :duration duration}) + (anim/timing font-size {:toValue to-font-size + :duration duration}) + (anim/timing line-width {:toValue to-line-width + :duration duration})])] + (anim/start animation (fn [arg] + (when (.-finished arg) + (log/debug "Field animation finished")))))) + +; Invoked once before the component is mounted. The return value will be used +; as the initial value of this.state. +(defn get-initial-state [component] + {:has-focus false + :float-label? false + :label-top 0 + :label-font-size 0 + :line-width (anim/create-value 0) + :max-line-width 100}) + +; Invoked once, both on the client and server, immediately before the initial +; rendering occurs. If you call setState within this method, render() will see +; the updated state and will be executed only once despite the state change. +(defn component-will-mount [component] + (let [{:keys [value] :as props} (r/props component) + data {:label-top (anim/create-value (if (s/blank? value) + (:label-bottom config) + (:label-top config))) + :label-font-size (anim/create-value (if (s/blank? value) + (:label-font-large config) + (:label-font-small config))) + :float-label? (if (s/blank? value) false true)}] + ;(log/debug "component-will-mount") + (r/set-state component data))) + +; Invoked once, only on the client (not on the server), immediately after the +; initial rendering occurs. At this point in the lifecycle, you can access any +; refs to your children (e.g., to access the underlying DOM representation). +; The componentDidMount() method of child components is invoked before that of +; parent components. +(defn component-did-mount [component] + (let [props (r/props component)] + ;(log/debug "component-did-mount:") + )) + +; Invoked when a component is receiving new props. This method is not called for +; the initial render. Use this as an opportunity to react to a prop transition +; before render() is called by updating the state using this.setState(). +; The old props can be accessed via this.props. Calling this.setState() within +; this function will not trigger an additional render. +(defn component-will-receive-props [component new-props] + ;(log/debug "component-will-receive-props: new-props=" new-props) + ) + +; Invoked before rendering when new props or state are being received. This method +; is not called for the initial render or when forceUpdate is used. Use this as +; an opportunity to return false when you're certain that the transition to the +; new props and state will not require a component update. +; If shouldComponentUpdate returns false, then render() will be completely skipped +; until the next state change. In addition, componentWillUpdate and +; componentDidUpdate will not be called. +(defn should-component-update [component next-props next-state] + ;(log/debug "should-component-update: " next-props next-state) + true) + +; Invoked immediately before rendering when new props or state are being received. +; This method is not called for the initial render. Use this as an opportunity +; to perform preparation before an update occurs. +(defn component-will-update [component next-props next-state] + ;(log/debug "component-will-update: " next-props next-state) + ) + +; Invoked immediately after the component's updates are flushed to the DOM. +; This method is not called for the initial render. Use this as an opportunity +; to operate on the DOM when the component has been updated. +(defn component-did-update [component prev-props prev-state] + ;(log/debug "component-did-update: " prev-props prev-state) + ) + +(defn on-input-focus [{:keys [component animation onFocus]}] + (do + (log/debug "input focused") + (r/set-state component {:has-focus true + :float-label? true}) + (field-animation animation) + (when onFocus (onFocus)))) + +(defn on-input-blur [{:keys [component value animation onBlur]}] + (log/debug "Input blurred") + (r/set-state component {:has-focus false + :float-label? (if (s/blank? value) false true)}) + (when (s/blank? value) + (field-animation animation)) + (when onBlur (onBlur))) + +(defn get-width [event] + (.-width (.-layout (.-nativeEvent event)))) + +(defn reagent-render [data children] + (let [component (r/current-component) + {:keys [has-focus + float-label? + label-top + label-font-size + line-width + current-value + max-line-width]} (r/state component) + {:keys [wrapper-style input-style label-hidden? line-color focus-line-color secure-text-entry + label-color error-color error label value on-focus on-blur + on-change-text on-change on-end-editing editable placeholder]} (merge default-props (r/props component)) + line-color (if error error-color line-color) + focus-line-color (if error error-color focus-line-color) + label-color (if (and error (not float-label?)) error-color label-color) + label (when-not label-hidden? + (if error (str label " *") label))] + [view (merge st/text-field-container wrapper-style) + (when-not label-hidden? + [animated-text {:style (st/label label-top label-font-size label-color)} label]) + [text-input {:style (merge st/text-input input-style) + :placeholder (or placeholder "") + :editable editable + :secure-text-entry secure-text-entry + :on-focus #(on-input-focus {:component component + :animation {:top label-top + :to-top (:label-top config) + :font-size label-font-size + :to-font-size (:label-font-small config) + :line-width line-width + :to-line-width max-line-width} + :onFocus on-focus}) + :on-blur #(on-input-blur {:component component + :value (or current-value value) + :animation {:top label-top + :to-top (:label-bottom config) + :font-size label-font-size + :to-font-size (:label-font-large config) + :line-width line-width + :to-line-width 0} + :onBlur on-blur}) + :on-change-text (fn [text] + (r/set-state component {:current-value text}) + (on-change-text text)) + :on-change #(on-change %) + :default-value value + :on-end-editing (when on-end-editing + on-end-editing)}] + [view {:style (st/underline-container line-color) + :onLayout #(r/set-state component {:max-line-width (get-width %)})} + [animated-view {:style (st/underline focus-line-color line-width)}]] + [text {:style (st/error-text error-color)} error]])) + +(defn text-field [data children] + (let [component-data {:get-initial-state get-initial-state + :component-will-mount component-will-mount + :component-did-mount component-did-mount + :component-will-receive-props component-will-receive-props + :should-component-update should-component-update + :component-will-update component-will-update + :component-did-update component-did-update + :display-name "text-field" + :reagent-render reagent-render}] + ;(log/debug "Creating text-field component: " data) + (r/create-class component-data))) diff --git a/src/status_im/components/toolbar/actions.cljs b/src/status_im/components/toolbar/actions.cljs new file mode 100644 index 0000000000..d38c3bcf39 --- /dev/null +++ b/src/status_im/components/toolbar/actions.cljs @@ -0,0 +1,31 @@ +(ns status-im.components.toolbar.actions + (:require [status-im.components.toolbar.styles :as st])) + +(def nothing + {:image {:source nil + :style st/action-search}}) + +(defn hamburger [handler] + {:image {:source {:uri :icon_hamburger} + :style st/action-hamburger} + :handler handler}) + +(defn add [handler] + {:image {:source {:uri :icon_add} + :style st/action-add} + :handler handler}) + +(defn search [handler] + {:image {:source {:uri :icon_search} + :style st/action-search} + :handler handler}) + +(defn back [handler] + {:image {:source {:uri :icon_back} + :style st/action-back} + :handler handler}) + +(defn back-white [handler] + {:image {:source {:uri :icon_back_white} + :style st/action-back} + :handler handler}) diff --git a/src/status_im/components/toolbar/styles.cljs b/src/status_im/components/toolbar/styles.cljs new file mode 100644 index 0000000000..0e4a57acc1 --- /dev/null +++ b/src/status_im/components/toolbar/styles.cljs @@ -0,0 +1,102 @@ +(ns status-im.components.toolbar.styles + (:require [status-im.components.styles :refer [text1-color + color-white + color-light-gray]])) + +(def toolbar-background1 color-white) +(def toolbar-background2 color-light-gray) + +(def toolbar-height 56) +(def toolbar-icon-width 32) +(def toolbar-icon-spacing 8) + +(def toolbar-gradient + {:height 4}) + +(defn toolbar-wrapper [background-color] + {:backgroundColor (or background-color toolbar-background1) + :elevation 2}) + +(def toolbar + {:flex-direction :row + :height toolbar-height}) + +(defn toolbar-nav-actions-container [actions] + {:width (when (and actions (> (count actions) 0)) + (-> (+ toolbar-icon-width toolbar-icon-spacing) + (* (count actions)) + (+ toolbar-icon-spacing))) + :flex-direction "row"}) + +(def toolbar-nav-action + {:width toolbar-height + :height toolbar-height + :align-items :center + :justify-content :center + :padding-right 12}) + +(def toolbar-title-container + {:flex 1 + :alignItems :center + :justifyContent :center}) + +(def toolbar-title-text + {:margin-top 0 + :color text1-color + :font-size 16}) + +(defn toolbar-actions-container [actions-count custom] + (merge {:flex-direction "row" + :margin-left toolbar-icon-spacing} + (when (and (= actions-count 0) + (not custom)) + {:width (+ toolbar-icon-width toolbar-icon-spacing)}))) + +(def toolbar-action + {:width toolbar-icon-width + :height toolbar-height + :margin-right toolbar-icon-spacing + :align-items :center + :justify-content :center}) + +(def toolbar-with-search + {:background-color toolbar-background2 + :elevation 0}) + +(def toolbar-with-search-content + {:flex 1 + :align-items :center + :justify-content :center}) + +(def toolbar-search-input + {:flex 1 + :align-self :stretch + :margin-left 18 + :margin-top 2 + :font-size 14 + :color "#7099e6"}) + +(def toolbar-with-search-title + {:color "#000000de" + :align-self :center + :text-align :center + :font-size 16}) + + +;; Specific actions + +(def action-hamburger + {:width 16 + :height 12}) + +(def action-add + {:width 17 + :height 17}) + +(def action-search + {:width 17 + :height 17}) + +(def action-back + {:width 8 + :height 14}) diff --git a/src/status_im/components/toolbar/view.cljs b/src/status_im/components/toolbar/view.cljs new file mode 100644 index 0000000000..3e16b1c35a --- /dev/null +++ b/src/status_im/components/toolbar/view.cljs @@ -0,0 +1,93 @@ +(ns status-im.components.toolbar.view + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + icon + text + text-input + image + touchable-highlight]] + [status-im.components.sync-state.gradient :refer [sync-state-gradient-view]] + [status-im.components.styles :refer [icon-back + icon-search]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :as st] + [status-im.utils.platform :refer [platform-specific]])) + +(defn toolbar [{title :title + nav-action :nav-action + hide-nav? :hide-nav? + actions :actions + custom-action :custom-action + background-color :background-color + custom-content :custom-content + style :style}] + (let [style (merge (st/toolbar-wrapper background-color) style)] + [view {:style style} + [view st/toolbar + [view (st/toolbar-nav-actions-container actions) + (when (not hide-nav?) + (if nav-action + [touchable-highlight {:on-press (:handler nav-action)} + [view (get-in platform-specific [:component-styles :toolbar-nav-action]) + [image (:image nav-action)]]] + [touchable-highlight {:on-press #(dispatch [:navigate-back]) + :accessibility-label :navigate-back} + [view (get-in platform-specific [:component-styles :toolbar-nav-action]) + [image {:source {:uri :icon_back} + :style icon-back}]]]))] + (or custom-content + [view {:style st/toolbar-title-container} + [text {:style st/toolbar-title-text + :font :toolbar-title} + title]]) + [view (st/toolbar-actions-container (count actions) custom-action) + (if actions + (for [{action-image :image + action-handler :handler} actions] + ^{:key (str "action-" action-image)} + [touchable-highlight {:on-press action-handler} + [view st/toolbar-action + [image action-image]]]) + custom-action)]] + [sync-state-gradient-view]])) + +(defn- toolbar-search-submit [on-search-submit] + (let [text @(subscribe [:get-in [:toolbar-search :text]])] + (on-search-submit text) + (dispatch [:set-in [:toolbar-search :text] nil]))) + +(defn- toolbar-with-search-content [{:keys [show-search? + search-key + search-placeholder + title + on-search-submit]}] + [view st/toolbar-with-search-content + (if show-search? + [text-input + {:style st/toolbar-search-input + :auto-focus true + :placeholder search-placeholder + :on-change-text #(dispatch [:set-in [:toolbar-search :text] %]) + :on-submit-editing #(toolbar-search-submit on-search-submit)}] + [view + [text {:style st/toolbar-with-search-title + :font :toolbar-title} + title]])]) + +(defn toolbar-with-search [{:keys [show-search? + search-key + nav-action + actions + style + on-search-submit] + :as opts}] + (let [toggle-search-fn #(dispatch [:set-in [:toolbar-search :show] %]) + actions (if show-search? + [(act/search #(toolbar-search-submit on-search-submit))] + (into actions [(act/search #(toggle-search-fn search-key))]))] + [toolbar {:style (merge st/toolbar-with-search style) + :nav-action (if show-search? + (act/back #(toggle-search-fn nil)) + nav-action) + :custom-content [toolbar-with-search-content opts] + :actions actions}])) diff --git a/src/status_im/components/webview_bridge.cljs b/src/status_im/components/webview_bridge.cljs new file mode 100644 index 0000000000..2b0ae6787b --- /dev/null +++ b/src/status_im/components/webview_bridge.cljs @@ -0,0 +1,10 @@ +(ns status-im.components.webview-bridge + (:require [status-im.utils.utils :as u] + [reagent.core :as r] + [status-im.utils.platform :as p])) + +(def webview-bridge-class + (r/adapt-react-class (.-default (js/require "react-native-webview-bridge")))) + +(defn webview-bridge [opts] + [webview-bridge-class opts]) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs new file mode 100644 index 0000000000..afceed1d1d --- /dev/null +++ b/src/status_im/constants.cljs @@ -0,0 +1,27 @@ +(ns status-im.constants) + +(def ethereum-rpc-url "http://localhost:8545") + +(def server-address "http://api.status.im/") +;; (def server-address "http://10.0.3.2:3000/") +;; (def server-address "http://localhost:3000/") + +(def text-content-type "text/plain") +(def content-type-command "command") +(def content-type-command-request "command-request") +(def content-type-wallet-command "wallet-command") +(def content-type-wallet-request "wallet-request") +(def content-type-status "status") + +(def max-chat-name-length 20) + +(def response-input-hiding-duration 100) +(def response-suggesstion-resize-duration 100) + +(def default-number-of-messages 20) +(def blocks-per-hour 120) + +(def default-number-of-discover-search-results 20) + +(def console-chat-id "console") +(def wallet-chat-id "wallet") diff --git a/src/status_im/contacts/default_contacts.cljs b/src/status_im/contacts/default_contacts.cljs new file mode 100644 index 0000000000..dc6e976f9d --- /dev/null +++ b/src/status_im/contacts/default_contacts.cljs @@ -0,0 +1,17 @@ +(ns status-im.contacts.default-contacts + (:require [status-im.constants :refer [wallet-chat-id]] + [clojure.string :as s])) + +(def contacts + [{:whisper-identity wallet-chat-id + :name (s/capitalize wallet-chat-id) + :photo-path :icon_wallet_avatar + :dapp? true + :add-chat? true} + + {:whisper-identity "dapp-auction" + :name "Auction House" + :photo-path "http://auctionhouse.dappbench.com/images/auctionhouse.png" + :dapp? true + :dapp-url "http://auctionhouse.dappbench.com"} + ]) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs new file mode 100644 index 0000000000..10b8a5369b --- /dev/null +++ b/src/status_im/contacts/handlers.cljs @@ -0,0 +1,278 @@ +(ns status-im.contacts.handlers + (:require [re-frame.core :refer [after dispatch]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.data-store.contacts :as contacts] + [status-im.utils.crypt :refer [encrypt]] + [clojure.string :as s] + [status-im.protocol.core :as protocol] + [status-im.utils.utils :refer [http-post]] + [status-im.utils.phone-number :refer [format-phone-number]] + [status-im.utils.handlers :as u] + [status-im.utils.utils :refer [require]] + [status-im.navigation.handlers :as nav] + [status-im.utils.random :as random] + [status-im.i18n :refer [label]] + [taoensso.timbre :as log] + [cljs.reader :refer [read-string]])) + + +(defmethod nav/preload-data! :group-contacts + [db [_ _ group]] + (dissoc + (if group + (assoc db :contacts-group group) + db) + :contacts-filter)) + +(defmethod nav/preload-data! :new-group + [db _] + (-> db + (assoc :new-group #{}) + (assoc :new-chat-name nil))) + +(defmethod nav/preload-data! :contact-list + [db [_ _ click-handler]] + (-> db + (assoc-in [:toolbar-search :show] nil) + (assoc :contacts-click-handler click-handler + :contacts-filter nil))) + +(register-handler :remove-contacts-click-handler + (fn [db] + (dissoc db + :contacts-click-handler + :contacts-click-action))) + +(defn save-contact + [_ [_ contact]] + (contacts/save contact)) + +(defn watch-contact + [{:keys [web3]} [_ {:keys [whisper-identity public-key private-key]}]] + (when (and public-key private-key) + (protocol/watch-user! {:web3 web3 + :identity whisper-identity + :keypair {:public public-key + :private private-key} + :callback #(dispatch [:incoming-message %1 %2])}))) + +(register-handler :watch-contact (u/side-effect! watch-contact)) + +(defn stop-watching-contact + [{:keys [web3]} [_ {:keys [whisper-identity]}]] + (protocol/stop-watching-user! {:web3 web3 + :identity whisper-identity})) + +(register-handler :stop-watching-contact (u/side-effect! stop-watching-contact)) + +(defn send-contact-request + [{:keys [current-public-key web3 current-account-id accounts]} [_ contact]] + (let [{:keys [whisper-identity]} contact + {:keys [name photo-path updates-public-key updates-private-key status]} + (get accounts current-account-id)] + (protocol/contact-request! + {:web3 web3 + :message {:from current-public-key + :to whisper-identity + :message-id (random/id) + :payload {:contact {:name name + :profile-image photo-path + :address current-account-id + :status status} + :keypair {:public updates-public-key + :private updates-private-key}}}}))) + +(register-handler :send-contact-request! (u/side-effect! send-contact-request)) + +(register-handler :update-contact! + (fn [db [_ {:keys [whisper-identity] :as contact}]] + (if (contacts/exists? whisper-identity) + (do + (contacts/save contact) + (update-in db [:contacts whisper-identity] merge contact)) + db))) + +(defn load-contacts! [db _] + (let [contacts (->> (contacts/get-all) + (map (fn [{:keys [whisper-identity] :as contact}] + [whisper-identity contact])) + (into {}))] + (doseq [[_ contact] contacts] + (dispatch [:watch-contact contact])) + (assoc db :contacts contacts))) + +(register-handler :load-contacts load-contacts!) + +;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 +(def react-native-contacts (js/require "react-native-contacts")) + +(defn contact-name [contact] + (->> contact + ((juxt :givenName :middleName :familyName)) + (remove s/blank?) + (s/join " "))) + +(defn normalize-phone-contacts [contacts] + (let [contacts' (js->clj contacts :keywordize-keys true)] + (map (fn [{:keys [thumbnailPath phoneNumbers] :as contact}] + {:name (contact-name contact) + :photo-path thumbnailPath + :phone-numbers phoneNumbers}) contacts'))) + +(defn fetch-contacts-from-phone! + [_ _] + (.getAll react-native-contacts + (fn [error contacts] + (if error + (log/debug :error-on-fetching-loading error) + (let [contacts' (normalize-phone-contacts contacts)] + (dispatch [:get-contacts-identities contacts'])))))) + +(register-handler :sync-contacts + (u/side-effect! fetch-contacts-from-phone!)) + +(defn get-contacts-by-hash [contacts] + (->> contacts + (mapcat (fn [{:keys [phone-numbers] :as contact}] + (map (fn [{:keys [number]}] + (let [number' (format-phone-number number)] + [(encrypt number') + (-> contact + (assoc :phone-number number') + (dissoc :phone-numbers))])) + phone-numbers))) + (into {}))) + +(defn add-identity [contacts-by-hash contacts] + (map (fn [{:keys [phone-number-hash whisper-identity address]}] + (let [contact (contacts-by-hash phone-number-hash)] + (assoc contact :whisper-identity whisper-identity + :address address))) + (js->clj contacts))) + +(defn request-stored-contacts [contacts] + (let [contacts-by-hash (get-contacts-by-hash contacts) + data (or (keys contacts-by-hash) ())] + (http-post "get-contacts" {:phone-number-hashes data} + (fn [{:keys [contacts]}] + (let [contacts' (add-identity contacts-by-hash contacts)] + (dispatch [:add-contacts contacts'])))))) + +(defn get-identities-by-contacts! [_ [_ contacts]] + (request-stored-contacts contacts)) + +(register-handler :get-contacts-identities + (u/side-effect! get-identities-by-contacts!)) + +(defn save-contacts! [{:keys [new-contacts]} _] + (contacts/save-all new-contacts)) + +(defn update-pending-status [old-contacts {:keys [whisper-identity pending] :as contact}] + (let [{old-pending :pending + :as old-contact} (get old-contacts whisper-identity)] + (if old-contact + (assoc contact :pending (and old-pending pending)) + (assoc contact :pending pending)))) + +(defn add-new-contacts + [{:keys [contacts] :as db} [_ new-contacts]] + (let [identities (set (map :whisper-identity contacts)) + new-contacts' (->> new-contacts + (map #(update-pending-status contacts %)) + (remove #(identities (:whisper-identity %))) + (map #(vector (:whisper-identity %) %)) + (into {}))] + (-> db + (update :contacts merge new-contacts') + (assoc :new-contacts (vals new-contacts'))))) + +(register-handler :add-contacts + (after save-contacts!) + add-new-contacts) + +(defn add-new-contact [db [_ {:keys [whisper-identity] :as contact}]] + (-> db + (update :contacts assoc whisper-identity contact) + (assoc :new-contact-identity ""))) + +(register-handler :add-new-contact + (u/side-effect! + (fn [_ [_ {:keys [whisper-identity] :as contact}]] + (when-not (contacts/get-by-id whisper-identity) + (dispatch [::prepare-contact contact]) + (dispatch [:start-chat whisper-identity {} :navigation-replace]))))) + +(register-handler ::prepare-contact + (-> add-new-contact + ((after save-contact)) + ((after send-contact-request)))) + +(register-handler ::update-pending-contact + (-> add-new-contact + ((after save-contact)))) + +(register-handler :add-pending-contact + (u/side-effect! + (fn [{:keys [chats contacts]} [_ chat-id]] + (let [contact (if-let [contact-info (get-in chats [chat-id :contact-info])] + (read-string contact-info) + (-> (get contacts chat-id) + (assoc :pending false)))] + (dispatch [::prepare-contact contact]) + (dispatch [:update-chat! {:chat-id chat-id + :pending-contact? false}]) + (dispatch [:watch-contact contact]) + (dispatch [:discoveries-send-portions chat-id]))))) + +(defn set-contact-identity-from-qr + [db [_ _ contact-identity]] + (assoc db :new-contact-identity contact-identity)) + +(register-handler :set-contact-identity-from-qr set-contact-identity-from-qr) + +(register-handler :contact-update-received + (u/side-effect! + (fn [{:keys [chats current-public-key] :as db} [_ {:keys [from payload]}]] + (when (not= current-public-key from) + (let [{:keys [content timestamp]} payload + {:keys [status name profile-image]} (:profile content) + prev-last-updated (get-in db [:contacts from :last-updated])] + (if (<= prev-last-updated timestamp) + (let [contact {:whisper-identity from + :name name + :photo-path profile-image + :status status + :last-updated timestamp}] + (dispatch [:update-contact! contact]) + (when (chats from) + (dispatch [:update-chat! {:chat-id from + :name name}]))))))))) + +(register-handler :contact-online-received + (u/side-effect! + (fn [db [_ {:keys [from] + {:keys [timestamp]} :payload}]] + (let [prev-last-online (get-in db [:contacts from :last-online])] + (when (< prev-last-online timestamp) + (protocol/reset-pending-messages! from) + (dispatch [:update-contact! {:whisper-identity from + :last-online timestamp}])))))) + +(register-handler :remove-contact + (-> (u/side-effect! + (fn [_ [_ {:keys [whisper-identity] :as contact}]] + (dispatch [:update-chat! {:chat-id whisper-identity + :pending-contact? true}]) + (dispatch [:update-contact! (assoc contact :pending true)]))) + ((after stop-watching-contact)))) + +(register-handler :open-contact-menu + (u/side-effect! + (fn [_ [_ list-selection-fn {:keys [name] :as contact}]] + (list-selection-fn {:title name + :options [(label :t/remove-contact)] + :callback (fn [index] + (case index + 0 (dispatch [:remove-contact contact]) + :default)) + :cancel-text (label :t/cancel)})))) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs new file mode 100644 index 0000000000..5187116b74 --- /dev/null +++ b/src/status_im/contacts/screen.cljs @@ -0,0 +1,158 @@ +(ns status-im.contacts.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [reagent.core :as r] + [clojure.string :as str] + [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text + image + touchable-highlight + linear-gradient + scroll-view + list-view + list-item] :as react] + [status-im.components.action-button :refer [action-button + action-button-item]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar-with-search]] + [status-im.components.toolbar.actions :as act] + [status-im.components.drawer.view :refer [open-drawer]] + [status-im.components.icons.custom-icons :refer [ion-icon]] + [status-im.contacts.views.contact :refer [contact-view]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.i18n :refer [label]] + [status-im.contacts.styles :as st] + [status-im.components.styles :refer [color-blue + create-icon + icon-search]])) + +(def contacts-limit 50) + +(defn toolbar-view [show-search?] + (let [new-contact? (get-in platform-specific [:contacts :new-contact-in-toolbar?]) + actions (if new-contact? + [(act/add #(dispatch [:navigate-to :new-contact]))])] + (toolbar-with-search + {:show-search? show-search? + :search-key :contact-list + :title (label :t/contacts) + :search-placeholder (label :t/search-for) + :nav-action (act/hamburger open-drawer) + :actions actions + :on-search-submit (fn [text] + (when-not (str/blank? text) + (dispatch [:set :contacts-filter #(let [name (-> (or (:name %) "") + (str/lower-case)) + text (str/lower-case text)] + (not= (.indexOf name text) -1))]) + (dispatch [:set :contact-list-search-text text]) + (dispatch [:navigate-to :contact-list-search-results])))}))) + +(defn subtitle-view [subtitle contacts-count] + [view st/contact-group-header-inner + [text {:style (merge st/contact-group-subtitle + (get-in platform-specific [:component-styles :contacts :subtitle])) + :uppercase? (get-in platform-specific [:contacts :uppercase-subtitles?]) + :font :medium} + subtitle] + [text {:style (merge st/contact-group-count + (get-in platform-specific [:component-styles :contacts :subtitle])) + :uppercase? (get-in platform-specific [:contacts :uppercase-subtitles?]) + :font :medium} + (str contacts-count)]]) + +(defn group-top-view [] + [linear-gradient {:style st/contact-group-header-gradient-bottom + :colors st/contact-group-header-gradient-bottom-colors}]) + +(defn group-bottom-view [] + [linear-gradient {:style st/contact-group-header-gradient-top + :colors st/contact-group-header-gradient-top-colors}]) + +(defn line-view [] + [view {:style {:background-color "#D7D7D7" + :height 1}}]) + +(defn contact-group-view [contacts contacts-count subtitle group click-handler] + (let [shadows? (get-in platform-specific [:contacts :group-block-shadows?])] + [view st/contact-group + [view st/contact-group-header + [subtitle-view subtitle contacts-count]] + (if shadows? + [group-top-view] + [line-view]) + [view + (doall + (map (fn [contact] + ^{:key contact} + [contact-view {:contact contact + :extended? true + :on-click click-handler + :more-on-click nil}]) + contacts))] + (when (<= contacts-limit (count contacts)) + [view st/show-all + [touchable-highlight {:on-press #(dispatch [:navigate-to :group-contacts group])} + [view + [text {:style st/show-all-text + :font :medium} + (label :t/show-all)]]]]) + (if shadows? + [group-bottom-view] + [line-view])])) + +(defn contacts-action-button [] + [view st/buttons-container + [action-button {:button-color color-blue + :offset-x 16 + :offset-y -2 + :hide-shadow true + :spacing 13} + [action-button-item + {:title (label :t/new-contact) + :buttonColor :#9b59b6 + :onPress #(dispatch [:navigate-to :new-contact])} + [ion-icon {:name :md-create + :style create-icon}]]]]) + +(defn contact-list [_] + (let [peoples (subscribe [:get-added-people-with-limit contacts-limit]) + dapps (subscribe [:get-added-dapps-with-limit contacts-limit]) + people-count (subscribe [:added-people-count]) + dapps-count (subscribe [:added-dapps-count]) + click-handler (subscribe [:get :contacts-click-handler]) + show-search (subscribe [:get-in [:toolbar-search :show]]) + show-toolbar-shadow? (r/atom false)] + (fn [current-view?] + [view st/contacts-list-container + [toolbar-view (and current-view? + (= @show-search :contact-list))] + [view {:style st/toolbar-shadow} + (when @show-toolbar-shadow? + [linear-gradient {:style st/contact-group-header-gradient-bottom + :colors st/contact-group-header-gradient-bottom-colors}])] + (if (pos? (+ @dapps-count @people-count)) + [scroll-view {:style st/contact-groups + :onScroll (fn [e] + (let [offset (.. e -nativeEvent -contentOffset -y)] + (reset! show-toolbar-shadow? + (<= st/contact-group-header-height offset))))} + (when (pos? @dapps-count) + [contact-group-view + @dapps + @dapps-count + (label :t/contacts-group-dapps) + :dapps + @click-handler]) + (when (pos? @people-count) + [contact-group-view + @peoples + @people-count + (label :t/contacts-group-people) + :people + @click-handler])] + [view st/empty-contact-groups + [react/icon :group_big st/empty-contacts-icon] + [text {:style st/empty-contacts-text} (label :t/no-contacts)]]) + (when (get-in platform-specific [:contacts :action-button?]) + [contacts-action-button])]))) diff --git a/src/status_im/contacts/search_results.cljs b/src/status_im/contacts/search_results.cljs new file mode 100644 index 0000000000..55e47fb96d --- /dev/null +++ b/src/status_im/contacts/search_results.cljs @@ -0,0 +1,36 @@ +(ns status-im.contacts.search-results + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.react :refer [view + text + icon + list-view + list-item]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.contacts.views.contact :refer [contact-view]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.i18n :refer [label]] + [status-im.contacts.styles :as st])) + +(defview contacts-search-results [] + [search-text [:get :contact-list-search-text] + contacts [:contacts-with-letters]] + [view st/search-container + [status-bar] + [toolbar {:nav-action (act/back #(dispatch [:navigate-back])) + :title search-text + :style (get-in platform-specific [:component-styles :toolbar])}] + (if (empty? contacts) + [view st/search-empty-view + ;; todo change icon + [icon :group_big st/empty-contacts-icon] + [text {:style st/empty-contacts-text} + "No contacts found"]] + [list-view {:dataSource (to-datasource contacts) + :renderRow (fn [row _ _] + (list-item [contact-view {:contact row + :letter? true + :extended? true}]))}])]) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs new file mode 100644 index 0000000000..100b7da558 --- /dev/null +++ b/src/status_im/contacts/styles.cljs @@ -0,0 +1,240 @@ +(ns status-im.contacts.styles + (:require [status-im.components.styles :refer [text1-color + text2-color + text3-color + text5-color + color-white + color-black + color-light-gray + color-separator + color-gray2]] + [status-im.components.toolbar.styles :refer [toolbar-background2]])) + +;; Contacts list + +(def toolbar-shadow + {:height 2 + :background-color toolbar-background2}) + +(def contact-groups + {:flex 1 + :background-color toolbar-background2}) + +(def contacts-list-container + {:flex 1}) + +(def empty-contact-groups + (merge contact-groups + {:align-items :center + :justify-content :center})) + +(def empty-contacts-icon + {:height 62 + :width 62}) + +(def empty-contacts-text + {:margin-top 12 + :font-size 16 + :color color-gray2}) + +(def contacts-list + {:backgroundColor color-white}) + +(def contact-group + {:flex-direction :column}) + +(def contact-group-header + {:flex-direction :column + :background-color toolbar-background2}) + +(def contact-group-header-inner + {:flexDirection :row + :alignItems :center + :height 48 + :backgroundColor toolbar-background2}) + +(def contact-group-subtitle + {:flex 1 + :margin-left 16}) + +(def contact-group-count + {:margin-right 14}) + +(def contact-group-header-gradient-top + {:flexDirection :row + :height 3 + :backgroundColor toolbar-background2}) + +(def contact-group-header-gradient-top-colors + ["rgba(24, 52, 76, 0.165)" + "rgba(24, 52, 76, 0.03)" + "rgba(24, 52, 76, 0.01)"]) + +(def contact-group-header-gradient-bottom + {:flexDirection :row + :height 2 + :backgroundColor toolbar-background2}) + +(def contact-group-header-gradient-bottom-colors + ["rgba(24, 52, 76, 0.01)" + "rgba(24, 52, 76, 0.05)"]) + +(def contact-group-header-height (+ (:height contact-group-header-inner) + (:height contact-group-header-gradient-bottom))) + +(def show-all + {:flexDirection :row + :alignItems :center + :height 56 + :backgroundColor color-white}) + +(def show-all-text + {:marginLeft 72 + :fontSize 14 + :color text3-color + ;; ios only: + :letterSpacing 0.5}) + +(def contact-container + {:flex-direction :row + :background-color color-white}) + +(def letter-container + {:paddingTop 11 + :paddingLeft 20 + :width 56}) + +(def letter-text + {:fontSize 24 + :color text3-color}) + +(def contact-photo-container + {:marginTop 4 + :marginLeft 12}) + +(def option-inner-container + {:flex 1 + :flex-direction :row + :height 56 + :background-color color-white + :border-bottom-color color-separator + :border-bottom-width 0.5}) + +(def option-inner + {:width 48 + :height 48 + :margin-top 4 + :margin-left 12}) + +(def option-inner-image + {:width 24 + :height 18 + :top 16 + :left 13}) + +(def spacing-top + {:background-color color-white + :height 8}) + +(def spacing-bottom + {:background-color color-white + :height 8}) + +(def contact-inner-container + {:flex 1 + :flexDirection :row + :height 56 + :margin-right 16 + :backgroundColor color-white}) + +(def info-container + {:flex 1 + :flexDirection :column + :margin-left 12 + :justifyContent :center}) + +(def name-text + {:fontSize 15 + :color text1-color}) + +(def info-text + {:marginTop 1 + :fontSize 12 + :color text2-color}) + +(def more-btn + {:width 56 + :height 56 + :alignItems :center + :justifyContent :center}) + +(def more-btn-icon + {:width 4 + :height 16}) + +; New contact + +(def contact-form-container + {:flex 1 + :color :white + :backgroundColor :white}) + +(def gradient-background + {:position :absolute + :top 0 + :right 0 + :bottom 0 + :left 0}) + +(def form-container + {:margin-left 16 + :margin-top 8 + :height 72}) + +(def address-explication-container + {:flex 1 + :margin-top 30 + :paddingLeft 16 + :paddingRight 16}) + +(def address-explication + {:textAlign :center + :color "#838c93de"}) + +(def buttons-container + {:position :absolute + :bottom 24 + :right 0 + :width 220 + :height 170}) + +(def qr-input + {:margin-right 42}) + +(def enter-address-icon + {:margin-left 21 + :margin-right 21 + :margin-top 19 + :width 20 + :height 18}) + +(def scan-qr-icon + {:margin-left 21 + :margin-right 20 + :margin-top 18 + :width 20 + :height 20}) + +;; Contacts search + +(def search-container + {:flex 1 + :background-color color-white}) + +(def search-empty-view + {:flex 1 + :background-color color-white + :align-items :center + :justify-content :center}) + + diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs new file mode 100644 index 0000000000..0bc89845cf --- /dev/null +++ b/src/status_im/contacts/subs.cljs @@ -0,0 +1,129 @@ +(ns status-im.contacts.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub subscribe]] + [clojure.string :as str] + [status-im.utils.identicon :refer [identicon]] + [taoensso.timbre :as log])) + +(register-sub :get-contacts + (fn [db _] + (let [contacts (reaction (:contacts @db))] + (reaction @contacts)))) + +(defn sort-contacts [contacts] + (->> (vals contacts) + (sort (fn [c1 c2] + (let [name1 (or (:name c1) (:address c1) (:whisper-identity c1)) + name2 (or (:name c2) (:address c2) (:whisper-identity c2))] + (compare (clojure.string/lower-case name1) + (clojure.string/lower-case name2))))))) + +(register-sub :all-added-contacts + (fn [db _] + (let [contacts (reaction (:contacts @db))] + (->> (remove #(true? (:pending (second %))) @contacts) + (sort-contacts) + (reaction))))) + +(register-sub :all-added-people + (fn [] + (let [contacts (subscribe [:all-added-contacts])] + (reaction (remove :dapp? @contacts))))) + +(register-sub :all-added-dapps + (fn [] + (let [contacts (subscribe [:all-added-contacts])] + (reaction (filter :dapp? @contacts))))) + +(register-sub :get-added-people-with-limit + (fn [_ [_ limit]] + (let [contacts (subscribe [:all-added-people])] + (reaction (take limit @contacts))))) + +(register-sub :get-added-dapps-with-limit + (fn [_ [_ limit]] + (let [contacts (subscribe [:all-added-dapps])] + (reaction (take limit @contacts))))) + +(register-sub :added-people-count + (fn [_ _] + (let [contacts (subscribe [:all-added-people])] + (reaction (count @contacts))))) + +(register-sub :added-dapps-count + (fn [_ _] + (let [contacts (subscribe [:all-added-dapps])] + (reaction (count @contacts))))) + +(defn get-contact-letter [contact] + (when-let [letter (first (:name contact))] + (clojure.string/upper-case letter))) + +(register-sub :contacts-with-letters + (fn [db _] + (let [contacts (reaction (:contacts @db)) + pred (subscribe [:get :contacts-filter])] + (reaction + (let [ordered (sort-contacts @contacts) + ordered (if @pred (filter @pred ordered) ordered)] + (reduce (fn [prev cur] + (let [prev-letter (get-contact-letter (last prev)) + cur-letter (get-contact-letter cur)] + (conj prev + (if (not= prev-letter cur-letter) + (assoc cur :letter cur-letter) + cur)))) + [] ordered)))))) + +(defn contacts-by-chat [fn db chat-id] + (let [chat (reaction (get-in @db [:chats chat-id])) + contacts (reaction (:contacts @db))] + (reaction + (when @chat + (let [current-participants (->> @chat + :contacts + (map :identity) + set)] + (fn #(current-participants (:whisper-identity %)) + (vals @contacts))))))) + +(defn contacts-by-current-chat [fn db] + (let [current-chat-id (:current-chat-id @db)] + (contacts-by-chat fn db current-chat-id))) + +(register-sub :contact + (fn [db _] + (let [identity (:contact-identity @db)] + (reaction (get-in @db [:contacts identity]))))) + +(register-sub :contact-by-identity + (fn [db [_ identity]] + (reaction (get-in @db [:contacts identity])))) + +(register-sub :all-new-contacts + (fn [db _] + (contacts-by-current-chat remove db))) + +(register-sub :current-chat-contacts + (fn [db _] + (contacts-by-current-chat filter db))) + +(register-sub :chat-photo + (fn [db [_ chat-id]] + (let [chat-id (or chat-id (:current-chat-id @db)) + chat (reaction (get-in @db [:chats chat-id])) + contacts (contacts-by-chat filter db chat-id)] + (reaction + (when @chat + (if (:group-chat @chat) + ;; TODO return group chat icon + nil + (cond + (:photo-path @chat) + (:photo-path @chat) + + (pos? (count @contacts)) + (:photo-path (first @contacts)) + + :else + (identicon chat-id)))))))) diff --git a/src/status_im/contacts/validations.cljs b/src/status_im/contacts/validations.cljs new file mode 100644 index 0000000000..ce256e057d --- /dev/null +++ b/src/status_im/contacts/validations.cljs @@ -0,0 +1,32 @@ +(ns status-im.contacts.validations + (:require [cljs.spec :as s] + [status-im.data-store.contacts :as contacts])) + +(def web3 (js/require "web3")) + +(defn is-address? [s] + (.isAddress web3.prototype s)) + +(defn contact-can-be-added? [identity] + (if (contacts/exists? identity) + (-> (contacts/get-by-id identity) + (get :pending)) + true)) + +(defn valid-length? [identity] + (let [length (count identity)] + (or + (= 130 length) + (= 132 length) + (is-address? identity)))) + +(s/def ::identity-length valid-length?) +(s/def ::contact-can-be-added contact-can-be-added?) +(s/def ::not-empty-string (s/and string? not-empty)) +(s/def ::name ::not-empty-string) +(s/def ::whisper-identity (s/and ::not-empty-string + ::contact-can-be-added + ::identity-length)) + +(s/def ::contact (s/keys :req-un [::name ::whisper-identity] + :opt-un [::phone ::photo-path ::address])) diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs new file mode 100644 index 0000000000..3096290342 --- /dev/null +++ b/src/status_im/contacts/views/contact.cljs @@ -0,0 +1,34 @@ +(ns status-im.contacts.views.contact + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [status-im.components.react :refer [view text icon touchable-highlight]] + [re-frame.core :refer [dispatch]] + [status-im.contacts.styles :as st] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]] + [status-im.utils.platform :refer [platform-specific]])) + +(defn- on-press [{:keys [whisper-identity] :as contact}] + (dispatch [:send-contact-request! contact]) + (dispatch [:start-chat whisper-identity {} :navigation-replace])) + +(defn- more-on-press [contact] + (dispatch [:open-contact-menu (:list-selection-fn platform-specific) contact])) + +(defn letter-view [letter] + [view st/letter-container + (when letter + [text {:style st/letter-text} letter])]) + +(defview contact-view [{{:keys [whisper-identity letter dapp?] :as contact} :contact + :keys [extended? letter? on-click more-on-click info]}] + [chat [:get-chat whisper-identity]] + [touchable-highlight + {:on-press #((or on-click on-press) contact)} + [view st/contact-container + (when letter? + [letter-view letter]) + [contact-inner-view contact info] + (when (and extended? (not dapp?)) + [touchable-highlight + {:on-press #((or more-on-click more-on-press) contact)} + [view st/more-btn + [icon :more_vertical st/more-btn-icon]]])]]) diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs new file mode 100644 index 0000000000..38af0e85c6 --- /dev/null +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -0,0 +1,28 @@ +(ns status-im.contacts.views.contact-inner + (:require [clojure.string :as s] + [status-im.components.react :refer [view image text]] + [status-im.components.chat-icon.screen :refer [contact-icon-contacts-tab]] + [status-im.contacts.styles :as st] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.i18n :refer [get-contact-translated label]])) + +(defn contact-photo [contact] + [view st/contact-photo-container + [contact-icon-contacts-tab contact]]) + +(defn contact-inner-view + ([contact] + (contact-inner-view contact nil)) + ([{:keys [whisper-identity name] :as contact} info] + [view st/contact-inner-container + [contact-photo contact] + [view st/info-container + [text {:style st/name-text + :number-of-lines 1} + (if (pos? (count (:name contact))) + (get-contact-translated whisper-identity :name name) + ;; todo is this correct behaviour? + (generate-gfy))] + (when info + [text {:style st/info-text} + info])]])) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs new file mode 100644 index 0000000000..d7477a8fe5 --- /dev/null +++ b/src/status_im/contacts/views/contact_list.cljs @@ -0,0 +1,116 @@ +(ns status-im.contacts.views.contact-list + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view text + image + touchable-highlight + list-view + list-item]] + [status-im.contacts.views.contact :refer [contact-view]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-background1]] + [status-im.components.drawer.view :refer [drawer-view open-drawer]] + [status-im.components.styles :refer [icon-search + icon-back + button-input-container + button-input]] + [status-im.components.image-button.view :refer [scan-button]] + [status-im.contacts.styles :as st] + [status-im.utils.listview :as lw] + [status-im.i18n :refer [label]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]] + [reagent.core :as r] + [clojure.string :as str])) + +(defn new-group-chat-view [] + [touchable-highlight + {:on-press #(dispatch [:navigate-to :new-group])} + [view st/contact-container + [view st/option-inner-container + [view st/option-inner + [image {:source {:uri :icon_menu_group} + :style st/option-inner-image}]] + [view st/info-container + [text {:style st/name-text} + (label :t/new-group-chat)]]]]]) + +(defn render-row [chat-modal click-handler action params] + (fn [row _ _] + (list-item + [contact-view {:contact row + :letter? chat-modal + :on-click (if click-handler + #(click-handler row action params))}]))) + +(defn contact-list-entry [{:keys [click-handler icon icon-style label]}] + [touchable-highlight + {:on-press click-handler} + [view st/contact-container + [view st/contact-inner-container + [image {:source {:uri icon} + :style icon-style}] + [view st/info-container + [text {:style st/name-text + :number-of-lines 1} + label]]]]]) + +(defview contact-list-toolbar [] + [group [:get :contacts-group] + modal [:get :modal]] + [view + [status-bar] + [toolbar {:title (label (if-not group + :t/contacts + (if (= group :dapps) + :t/contacts-group-dapps + :t/contacts-group-new-chat))) + :nav-action (when modal + (act/back #(dispatch [:navigate-back]))) + :background-color toolbar-background1 + :style (get-in platform-specific [:component-styles :toolbar]) + :actions [(act/search #())]}]]) + +(defview contact-list [] + [contacts [:contacts-with-letters] + group [:get :contacts-group] + modal [:get :modal] + click-handler [:get :contacts-click-handler] + action [:get :contacts-click-action] + params [:get :contacts-click-params]] + (let [show-new-group-chat? (and (= group :people) + (get-in platform-specific [:chats :new-chat-in-toolbar?]))] + [drawer-view + [view st/contacts-list-container + [contact-list-toolbar] + ;; todo add stub + (when modal + [view + [contact-list-entry {:click-handler #(do + (dispatch [:send-to-webview-bridge + {:event (name :webview-send-transaction)}]) + (dispatch [:navigate-back])) + :icon :icon_enter_address + :icon-style st/enter-address-icon + :label (label :t/enter-address)}] + [contact-list-entry {:click-handler #(click-handler :qr-scan action) + :icon :icon_scan_q_r + :icon-style st/scan-qr-icon + :label (label (if (= :request action) + :t/show-qr + :t/scan-qr))}]]) + (when contacts + [list-view {:dataSource (lw/to-datasource contacts) + :enableEmptySections true + :renderRow (render-row modal click-handler action params) + :bounces false + :renderHeader #(list-item + [view + (if show-new-group-chat? + [new-group-chat-view]) + [view st/spacing-top]]) + :renderFooter #(list-item [view st/spacing-bottom]) + :style st/contacts-list}])]])) diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs new file mode 100644 index 0000000000..170d5b1b28 --- /dev/null +++ b/src/status_im/contacts/views/new_contact.cljs @@ -0,0 +1,120 @@ +(ns status-im.contacts.views.new-contact + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [clojure.string :as str] + [status-im.components.react :refer [view + text + image + linear-gradient + touchable-highlight]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.utils.identicon :refer [identicon]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-title-container + toolbar-title-text + toolbar-background1]] + [status-im.utils.utils :refer [log http-post]] + [status-im.components.styles :refer [icon-ok + icon-back + button-input-container + button-input]] + [status-im.components.image-button.view :refer [scan-button]] + [status-im.i18n :refer [label]] + [cljs.spec :as s] + [status-im.contacts.validations :as v] + [status-im.contacts.styles :as st] + [status-im.data-store.contacts :as contacts] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.utils.hex :refer [normalize-hex]] + [status-im.utils.platform :refer [platform-specific]])) + + +(def toolbar-title + [view toolbar-title-container + [text {:style toolbar-title-text} + (label :t/add-new-contact)]]) + +(defn on-add-contact [id] + (if (v/is-address? id) + (http-post "get-contacts-by-address" {:addresses [id]} + (fn [{:keys [contacts]}] + (if (> (count contacts) 0) + (let [{:keys [whisper-identity]} (first contacts) + contact {:name (generate-gfy) + :address id + :photo-path (identicon whisper-identity) + :whisper-identity whisper-identity}] + (if (contacts/exists? whisper-identity) + (dispatch [:add-pending-contact whisper-identity]) + (dispatch [:add-new-contact contact]))) + (dispatch [:set :new-contact-public-key-error (label :t/unknown-address)])))) + (if (contacts/exists? id) + (dispatch [:add-pending-contact id]) + (dispatch [:add-new-contact {:name (generate-gfy) + :photo-path (identicon id) + :whisper-identity id}])))) + +(defn- validation-error-message + [whisper-identity {:keys [address public-key]} error] + (cond + (#{(normalize-hex address) (normalize-hex public-key)} + (normalize-hex whisper-identity)) + (label :t/can-not-add-yourself) + + (not (s/valid? ::v/contact-can-be-added whisper-identity)) + (label :t/contact-already-added) + + (not (s/valid? ::v/whisper-identity whisper-identity)) + (label :t/enter-valid-public-key) + + :else error)) + +(defn toolbar-actions [new-contact-identity account error] + (let [error-message (validation-error-message new-contact-identity account error)] + [{:image {:source {:uri (if (str/blank? error-message) + :icon_ok_blue + :icon_ok_disabled)} + :style icon-ok} + :handler #(when (str/blank? error-message) + (on-add-contact new-contact-identity))}])) + +(defview contact-whisper-id-input [whisper-identity error] + [current-account [:get-current-account]] + (let [error (when-not (str/blank? whisper-identity) + (validation-error-message whisper-identity current-account error))] + [view button-input-container + [text-field + {:error error + :error-color "#7099e6" + :input-style st/qr-input + :value whisper-identity + :wrapper-style button-input + :label (label :t/public-key) + :on-change-text #(do + (dispatch [:set-in [:new-contact-identity] %]) + (dispatch [:set :new-contact-public-key-error nil]))}] + [scan-button {:show-label? (zero? (count whisper-identity)) + :handler #(dispatch [:scan-qr-code + {:toolbar-title (label :t/new-contact)} + :set-contact-identity-from-qr])}]])) + + +(defview new-contact [] + [new-contact-identity [:get :new-contact-identity] + error [:get :new-contact-public-key-error] + account [:get-current-account]] + [view st/contact-form-container + [status-bar] + [toolbar {:background-color toolbar-background1 + :style (get-in platform-specific [:component-styles :toolbar]) + :nav-action (act/back #(dispatch [:navigate-back])) + :title (label :t/add-new-contact) + :actions (toolbar-actions new-contact-identity account error)}] + [view st/form-container + [contact-whisper-id-input new-contact-identity error]] + [view st/address-explication-container + [text {:style st/address-explication + :font :default} + (label :t/address-explication)]]]) diff --git a/src/status_im/data_store/accounts.cljs b/src/status_im/data_store/accounts.cljs new file mode 100644 index 0000000000..fd018e6757 --- /dev/null +++ b/src/status_im/data_store/accounts.cljs @@ -0,0 +1,14 @@ +(ns status-im.data-store.accounts + (:require [status-im.data-store.realm.accounts :as data-store])) + +(defn get-all [] + (data-store/get-all-as-list)) + +(defn get-by-address [address] + (data-store/get-by-address address)) + +(defn save [account update?] + (data-store/save account update?)) + +(defn save-all [accounts update?] + (data-store/save-all accounts update?)) \ No newline at end of file diff --git a/src/status_im/data_store/chats.cljs b/src/status_im/data_store/chats.cljs new file mode 100644 index 0000000000..5845b2dc8c --- /dev/null +++ b/src/status_im/data_store/chats.cljs @@ -0,0 +1,95 @@ +(ns status-im.data-store.chats + (:require [status-im.data-store.realm.chats :as data-store] + [re-frame.core :refer [dispatch]]) + (:refer-clojure :exclude [exists?])) + +(defn- normalize-contacts + [chats] + (map #(update % :contacts vals) chats)) + +(defn get-all + [] + (-> (data-store/get-all-active) + normalize-contacts)) + +(defn get-by-id + [id] + (data-store/get-by-id id)) + +(defn exists? + [chat-id] + (data-store/exists? chat-id)) + +(defn save + [{:keys [last-message-id chat-id] :as chat}] + (let [chat (assoc chat :last-message-id (or last-message-id ""))] + (data-store/save chat (data-store/exists? chat-id)))) + +(defn delete + [chat-id] + (data-store/delete chat-id)) + +(defn get-contacts + [chat-id] + (data-store/get-contacts chat-id)) + +(defn has-contact? + [chat-id identity] + (data-store/has-contact? chat-id identity)) + +(defn add-contacts + [chat-id identities] + (data-store/add-contacts chat-id identities) + ; TODO: move this somewhere else + ; TODO: temp. Update chat in db atom + (dispatch [:initialize-chats])) + +(defn remove-contacts + [chat-id identities] + (data-store/remove-contacts chat-id identities)) + +(defn save-property + [chat-id property-name value] + (data-store/save-property chat-id property-name value)) + +(defn get-property + [chat-id property-name] + (data-store/get-property chat-id property-name)) + +(defn is-active? + [chat-id] + (get-property chat-id :is-active)) + +(defn removed-at + [chat-id] + (get-property chat-id :removed-at)) + +(defn get-message-overhead + [chat-id] + (get-property chat-id :message-overhead)) + +(defn get-active-group-chats + [] + (data-store/get-active-group-chats)) + +(defn set-active + [chat-id active?] + (save-property chat-id :is-active active?)) + +(defn inc-message-overhead + [chat-id] + (save-property chat-id :message-overhead (inc (get-message-overhead chat-id)))) + +(defn reset-message-overhead + [chat-id] + (save-property chat-id :message-overhead 0)) + +(defn new-update? + [timestamp chat-id] + (let + [{:keys [added-to-at removed-at removed-from-at added-at]} + (get-by-id chat-id)] + (and (> timestamp added-to-at) + (> timestamp removed-at) + (> timestamp removed-from-at) + (> timestamp added-at)))) diff --git a/src/status_im/data_store/commands.cljs b/src/status_im/data_store/commands.cljs new file mode 100644 index 0000000000..9a7a04cf7e --- /dev/null +++ b/src/status_im/data_store/commands.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.commands + (:require [status-im.data-store.realm.commands :as data-store])) + +(defn get-by-chat-id + [chat-id] + (data-store/get-by-chat-id chat-id)) + +(defn save + [command] + (data-store/save command)) diff --git a/src/status_im/data_store/contacts.cljs b/src/status_im/data_store/contacts.cljs new file mode 100644 index 0000000000..1fc3d271d0 --- /dev/null +++ b/src/status_im/data_store/contacts.cljs @@ -0,0 +1,33 @@ +(ns status-im.data-store.contacts + (:require [status-im.data-store.realm.contacts :as data-store] + [taoensso.timbre :as log]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [] + (data-store/get-all-as-list)) + +(defn get-by-id + [whisper-identity] + (data-store/get-by-id-cljs whisper-identity)) + +(defn save + [{:keys [whisper-identity pending] :as contact}] + (let [{pending-db :pending + :as contact-db} (data-store/get-by-id whisper-identity) + contact (assoc contact :pending + (boolean (if contact-db + (if (nil? pending) pending-db pending) + pending)))] + (data-store/save contact (if contact-db true false)))) + +(defn save-all + [contacts] + (mapv save contacts)) + +(defn delete [contact] + (data-store/delete contact)) + +(defn exists? + [whisper-identity] + (data-store/exists? whisper-identity)) diff --git a/src/status_im/data_store/core.cljs b/src/status_im/data_store/core.cljs new file mode 100644 index 0000000000..21a948bc3d --- /dev/null +++ b/src/status_im/data_store/core.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.core + (:require [taoensso.timbre :as log] + [status-im.data-store.realm.core :as data-source])) + + +(defn init [] + (data-source/reset-account)) + +(defn change-account [address new-account? handler] + (data-source/change-account address new-account? handler)) diff --git a/src/status_im/data_store/discover.cljs b/src/status_im/data_store/discover.cljs new file mode 100644 index 0000000000..23108fd516 --- /dev/null +++ b/src/status_im/data_store/discover.cljs @@ -0,0 +1,28 @@ +(ns status-im.data-store.discover + (:require [status-im.data-store.realm.discover :as data-store]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [ordering] + (->> (data-store/get-all-as-list ordering) + (mapv #(update % :tags vals)))) + +(defn save + [discover] + (data-store/save discover)) + +(defn exists? + [message-id] + (data-store/exists? message-id)) + +(defn save-all + [discoveries] + (data-store/save-all discoveries)) + +(defn delete + [by ordering critical-count to-delete-count] + (data-store/delete by ordering critical-count to-delete-count)) + +(defn get-all-tags + [] + (data-store/get-all-tags)) diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs new file mode 100644 index 0000000000..d7a8b62220 --- /dev/null +++ b/src/status_im/data_store/messages.cljs @@ -0,0 +1,134 @@ +(ns status-im.data-store.messages + (:require [status-im.data-store.realm.messages :as data-store] + [clojure.string :refer [join split]] + [status-im.utils.random :refer [timestamp]] + [clojure.walk :refer [stringify-keys keywordize-keys]] + [status-im.commands.utils :refer [generate-hiccup]] + [cljs.reader :refer [read-string]] + [status-im.constants :as c] + [taoensso.timbre :as log]) + (:refer-clojure :exclude [update])) + +(defn- map-to-str + [m] + (join ";" (map #(join "=" %) (stringify-keys m)))) + +(defn- str-to-map + [s] + (->> (keywordize-keys (apply hash-map (split s #"[;=]"))) + (map (fn [[k v]] + [k (if (= k :params) + (keywordize-keys (read-string v)) + v)])) + (into {}))) + +(defn- user-statuses-to-map + [user-statuses] + (->> (vals user-statuses) + (mapv (fn [{:keys [whisper-identity] :as status}] + [whisper-identity status])) + (into {}))) + +(defn- command-type? + [type] + (contains? + #{c/content-type-command c/content-type-command-request + c/content-type-wallet-request c/content-type-wallet-command} + type)) + +(def default-values + {:outgoing false + :to nil + :same-author false + :same-direction false + :preview nil}) + +(defn get-by-id + [message-id] + (some-> (data-store/get-by-id message-id) + (clojure.core/update :user-statuses user-statuses-to-map))) + +(defn get-messages + [messages] + (->> messages + (mapv #(clojure.core/update % :user-statuses user-statuses-to-map)) + (into '()) + reverse + (keep (fn [{:keys [content-type preview] :as message}] + (if (command-type? content-type) + (-> message + (clojure.core/update :content str-to-map) + (assoc :rendered-preview + (when preview + (generate-hiccup (read-string preview))))) + message))))) + +(defn get-count-by-chat-id + [chat-id] + (data-store/get-count-by-chat-id chat-id)) + +(defn get-by-chat-id + ([chat-id] + (get-by-chat-id chat-id 0)) + ([chat-id from] + (->> (data-store/get-by-chat-id chat-id from c/default-number-of-messages) + (mapv #(clojure.core/update % :user-statuses user-statuses-to-map)) + (into '()) + reverse + (keep (fn [{:keys [content-type preview] :as message}] + (if (command-type? content-type) + (-> message + (clojure.core/update :content str-to-map) + (assoc :rendered-preview + (when preview + (generate-hiccup (read-string preview))))) + message)))))) + +(defn get-last-message + [db chat-id] + (if-let [{:keys [content-type] :as message} (data-store/get-last-message chat-id)] + (if (command-type? content-type) + (clojure.core/update message :content str-to-map) + message))) + +(defn get-last-outgoing + [chat-id number-of-messages] + (data-store/get-by-fields {:chat-id chat-id + :outgoing true} + 0 + number-of-messages)) + +(defn get-last-clock-value + [chat-id] + (if-let [message (data-store/get-last-message chat-id)] + (:clock-value message) + 0)) + +(defn get-unviewed + [] + (data-store/get-unviewed)) + +(defn save + ;; todo remove chat-id parameter + [chat-id {:keys [message-id content] + :as message}] + (when-not (data-store/exists? message-id) + (let [content' (if (string? content) + content + (map-to-str content)) + message' (merge default-values + message + {:chat-id chat-id + :content content' + :timestamp (timestamp)})] + (data-store/save message')))) + +(defn update + [{:keys [message-id] :as message}] + (when (data-store/exists? message-id) + (let [message (clojure.core/update message :user-statuses vals)] + (data-store/save message)))) + +(defn delete-by-chat-id [chat-id] + (data-store/delete-by-chat-id chat-id)) + diff --git a/src/status_im/data_store/pending_messages.cljs b/src/status_im/data_store/pending_messages.cljs new file mode 100644 index 0000000000..6bf7b19c09 --- /dev/null +++ b/src/status_im/data_store/pending_messages.cljs @@ -0,0 +1,48 @@ +(ns status-im.data-store.pending-messages + (:require [status-im.data-store.realm.pending-messages :as data-store] + [clojure.string :as str] + [status-im.utils.hex :as i])) + +(defn- get-id + [message-id to] + (let [to' (i/normalize-hex to) + to'' (when to' (subs to' 0 7)) + id' (if to'' + (str message-id "-" (subs to'' 0 7)) + message-id)] + id')) + +(defn get-all + [] + (data-store/get-all-as-list)) + +(defn get-by-chat-id + [chat-id] + (data-store/get-by-chat-id chat-id)) + +(defn get-by-message-id + [message-id] + (data-store/get-by-message-id message-id)) + +(defn save + [{:keys [id to group-id message] :as pending-message}] + (let [{:keys [from topics payload]} message + id' (get-id id to) + chat-id (or group-id to) + message' (-> pending-message + (assoc :id id' + :from from + :message-id id + :chat-id chat-id + :payload payload + :topics (prn-str topics)) + (dissoc :message))] + (data-store/save message'))) + +(defn delete + [pending-message] + (data-store/delete pending-message)) + +(defn delete-all-by-chat-id + [chat-id] + (data-store/delete-all-by-chat-id chat-id)) \ No newline at end of file diff --git a/src/status_im/data_store/processed_messages.cljs b/src/status_im/data_store/processed_messages.cljs new file mode 100644 index 0000000000..d934614417 --- /dev/null +++ b/src/status_im/data_store/processed_messages.cljs @@ -0,0 +1,15 @@ +(ns status-im.data-store.processed-messages + (:require [status-im.data-store.realm.processed-messages :as data-store] + [taoensso.timbre :as log]) + (:refer-clojure :exclude [exists?])) + +(defn get-filtered + [condition] + (data-store/get-filtered-as-list condition)) + +(defn save + [processed-message] + (data-store/save processed-message)) + +(defn delete [condition] + (data-store/delete condition)) diff --git a/src/status_im/data_store/realm/accounts.cljs b/src/status_im/data_store/realm/accounts.cljs new file mode 100644 index 0000000000..44bd4ad0a3 --- /dev/null +++ b/src/status_im/data_store/realm/accounts.cljs @@ -0,0 +1,21 @@ +(ns status-im.data-store.realm.accounts + (:require [status-im.data-store.realm.core :as realm])) + +(defn get-all [] + (realm/get-all realm/base-realm :account)) + +(defn get-all-as-list [] + (-> (get-all) + realm/realm-collection->list)) + +(defn get-by-address [address] + (realm/get-one-by-field-clj realm/base-realm :account :address address)) + +(defn save [account update?] + (realm/write realm/base-realm + #(realm/create realm/base-realm :account account update?))) + +(defn save-all [accounts update?] + (realm/write realm/base-realm + (fn [] + (mapv #(realm/create realm/base-realm :account % update?) accounts)))) \ No newline at end of file diff --git a/src/status_im/data_store/realm/chats.cljs b/src/status_im/data_store/realm/chats.cljs new file mode 100644 index 0000000000..d90b04e6e5 --- /dev/null +++ b/src/status_im/data_store/realm/chats.cljs @@ -0,0 +1,111 @@ +(ns status-im.data-store.realm.chats + (:require [status-im.data-store.realm.core :as realm] + [status-im.utils.random :refer [timestamp]]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [] + (-> (realm/get-all @realm/account-realm :chat) + (realm/sorted :timestamp :desc))) + +(defn get-all-as-list + [] + (-> (get-all) + realm/realm-collection->list)) + +(defn get-all-active + [] + (-> (realm/get-by-field @realm/account-realm :chat :is-active true) + (realm/sorted :timestamp :desc) + realm/realm-collection->list)) + +(defn- groups + [active?] + (realm/filtered (get-all) + (str "group-chat = true && is-active = " + (if active? "true" "false")))) + +(defn get-active-group-chats + [] + (map + (fn [{:keys [chat-id public-key private-key]}] + {:chat-id chat-id + :keypair {:private private-key + :public public-key}}) + (realm/realm-collection->list (groups true)))) + +(defn get-by-id + [chat-id] + (-> (realm/get-one-by-field-clj @realm/account-realm :chat :chat-id chat-id) + (realm/list->array :contacts))) + +(defn save + [chat update?] + (realm/save @realm/account-realm :chat chat update?)) + +(defn exists? + [chat-id] + (realm/exists? @realm/account-realm :chat {:chat-id chat-id})) + +(defn delete + [chat-id] + (when-let [chat (get-by-id chat-id)] + (realm/write @realm/account-realm + (fn [] + (doto chat + (aset "is-active" false) + (aset "removed-at" timestamp)))))) + +(defn get-contacts + [chat-id] + (-> (realm/get-one-by-field @realm/account-realm :chat :chat-id chat-id) + (aget "contacts"))) + +(defn has-contact? + [chat-id identity] + (let [contacts (get-contacts chat-id) + contact (.find contacts (fn [object index collection] + (= identity (aget object "identity"))))] + (if contact true false))) + +(defn- save-contacts + [identities contacts added-at] + (doseq [contact-identity identities] + (if-let [contact (.find contacts (fn [object index collection] + (= contact-identity (aget object "identity"))))] + (doto contact + (aset "is-in-chat" true) + (aset "added-at" added-at)) + (.push contacts (clj->js {:identity contact-identity + :added-at added-at}))))) + +(defn add-contacts + [chat-id identities] + (let [contacts (get-contacts chat-id) + added-at (timestamp)] + (realm/write @realm/account-realm + #(save-contacts identities contacts added-at)))) + +(defn- delete-contacts + [identities contacts] + (doseq [contact-identity identities] + (when-let [contact (.find contacts (fn [object index collection] + (= contact-identity (aget object "identity"))))] + (realm/delete @realm/account-realm contact)))) + +(defn remove-contacts + [chat-id identities] + (let [contacts (get-contacts chat-id)] + (delete-contacts identities contacts))) + +(defn save-property + [chat-id property-name value] + (realm/write @realm/account-realm + (fn [] + (-> (realm/get-one-by-field @realm/account-realm :chat :chat-id chat-id) + (aset (name property-name) value))))) + +(defn get-property + [chat-id property] + (when-let [chat (realm/get-one-by-field @realm/account-realm :chat :chat-id chat-id)] + (aget chat (name property)))) diff --git a/src/status_im/data_store/realm/commands.cljs b/src/status_im/data_store/realm/commands.cljs new file mode 100644 index 0000000000..187da5172f --- /dev/null +++ b/src/status_im/data_store/realm/commands.cljs @@ -0,0 +1,11 @@ +(ns status-im.data-store.realm.commands + (:require [status-im.data-store.realm.core :as realm])) + +(defn get-by-chat-id + [chat-id] + (realm/get-one-by-field-clj @realm/account-realm :command + :chat-id identity)) + +(defn save + [command] + (realm/save @realm/account-realm :command command true)) diff --git a/src/status_im/data_store/realm/contacts.cljs b/src/status_im/data_store/realm/contacts.cljs new file mode 100644 index 0000000000..7efe73eee4 --- /dev/null +++ b/src/status_im/data_store/realm/contacts.cljs @@ -0,0 +1,34 @@ +(ns status-im.data-store.realm.contacts + (:require [status-im.data-store.realm.core :as realm]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [] + (-> (realm/get-all @realm/account-realm :contact) + (realm/sorted :name :asc))) + +(defn get-all-as-list + [] + (-> (get-all) + realm/realm-collection->list)) + +(defn get-by-id + [whisper-identity] + (realm/get-one-by-field @realm/account-realm :contact :whisper-identity whisper-identity)) + +(defn get-by-id-cljs + [whisper-identity] + (some-> (get-by-id whisper-identity) + (js->clj :keywordize-keys true))) + +(defn save + [contact update?] + (realm/save @realm/account-realm :contact contact update?)) + +(defn delete + [{:keys [whisper-identity]}] + (realm/delete @realm/account-realm (get-by-id whisper-identity))) + +(defn exists? + [whisper-identity] + (realm/exists? @realm/account-realm :contact {:whisper-identity whisper-identity})) diff --git a/src/status_im/data_store/realm/core.cljs b/src/status_im/data_store/realm/core.cljs new file mode 100644 index 0000000000..db218023a3 --- /dev/null +++ b/src/status_im/data_store/realm/core.cljs @@ -0,0 +1,188 @@ +(ns status-im.data-store.realm.core + (:require [status-im.utils.utils :as u] + [status-im.utils.types :refer [to-string]] + [status-im.data-store.realm.schemas.account.core :as account] + [status-im.data-store.realm.schemas.base.core :as base] + [taoensso.timbre :as log] + [status-im.utils.fs :as fs] + [clojure.string :as str]) + (:refer-clojure :exclude [exists?])) + +(def realm-class (js/require "realm")) + +(defn realm-version + [file-name] + (.schemaVersion realm-class file-name)) + +(defn open-realm + [options file-name] + (let [options (merge options {:path file-name})] + (when (cljs.core/exists? js/window) + (realm-class. (clj->js options))))) + +(defn close [realm] + (.close realm)) + +(defn migrate [file-name schemas] + (let [current-version (realm-version file-name)] + (doseq [schema schemas + :when (> (:schemaVersion schema) current-version) + :let [migrated-realm (open-realm schema file-name)]] + (close migrated-realm)))) + +(defn open-migrated-realm + [file-name schemas] + (migrate file-name schemas) + (open-realm (last schemas) file-name)) + +(def new-account-filename "new-account") +(def new-accout-realm-file (str new-account-filename ".realm")) + +(def base-realm (open-migrated-realm (.-defaultPath realm-class) base/schemas)) + +(def account-realm (atom (open-migrated-realm new-account-filename account/schemas))) + +(defn close-account-realm [] + (close @account-realm) + (reset! account-realm nil)) + +(defn reset-account [] + (when @account-realm + (close @account-realm)) + (reset! account-realm (open-migrated-realm new-account-filename account/schemas)) + (.write @account-realm #(.deleteAll @account-realm))) + +(defn move-file-handler [address err handler] + (log/debug "Moved file with error: " err address) + (if err + (log/error "Error moving account realm: " (.-message err)) + (reset! account-realm (open-migrated-realm address account/schemas))) + (handler err)) + +(defn change-account [address new-account? handler] + (let [path (.-path @account-realm)] + (log/debug "closing account realm: " path) + (close-account-realm) + (log/debug "is new account? " new-account?) + (if new-account? + (let [new-path (str/replace path new-account-filename address)] + (log/debug "Moving file " path " to " new-path) + (fs/move-file path new-path #(move-file-handler address % handler))) + (do + (reset! account-realm (open-migrated-realm address account/schemas)) + (handler nil))))) + +; realm functions + +(defn and-query [queries] + (str/join " and " queries)) + +(defn or-query [queries] + (str/join " or " queries)) + +(defn write [realm f] + (.write realm f)) + +(defn create + ([realm schema-name obj] + (create realm schema-name obj false)) + ([realm schema-name obj update?] + (.create realm (to-string schema-name) (clj->js obj) update?))) + +(defn save + ([realm schema-name obj] + (save realm schema-name obj false)) + ([realm schema-name obj update?] + (write realm #(create realm schema-name obj update?)))) + +(defn save-all + ([realm schema-name objs] + (save-all realm schema-name objs false)) + ([realm schema-name objs update?] + (write realm (fn [] + (mapv #(save realm schema-name % update?) objs))))) + +(defn delete [realm obj] + (write realm #(.delete realm obj))) + +(defn get-all [realm schema-name] + (.objects realm (to-string schema-name))) + +(defn sorted [results field-name order] + (.sorted results (to-string field-name) (if (= order :asc) + false + true))) + +(defn get-count [objs] + (.-length objs)) + +(defn page [results from to] + (js/Array.prototype.slice.call results from to)) + +(defn filtered [results filter-query] + (.filtered results filter-query)) + +(defn realm-collection->list [collection] + (-> (.map collection (fn [object _ _] object)) + (js->clj :keywordize-keys true))) + +(defn list->array [record list-field] + (update-in record [list-field] (comp vec vals))) + +(defn single [result] + (-> (aget result 0))) + +(defn single-cljs [result] + (some-> (aget result 0) + (js->clj :keywordize-keys true))) + +(defn get-by-filter [realm schema-name filter] + (-> (.objects realm (name schema-name)) + (.filtered filter))) + +(defn- get-schema-by-name [opts] + (->> opts + (mapv (fn [{:keys [name] :as schema}] + [(keyword name) schema])) + (into {}))) + +(defn- field-type [realm schema-name field] + (let [schema-by-name (get-schema-by-name (js->clj (.-schema realm) :keywordize-keys true)) + field-def (get-in schema-by-name [schema-name :properties field])] + (if (map? field-def) + (:type field-def) + field-def))) + +(defmulti to-query (fn [realm schema-name operator field value] + operator)) + +(defmethod to-query :eq [schema schema-name operator field value] + (let [value (to-string value) + field-type (field-type schema schema-name field) + escaped-value (when value (str/replace (str value) #"\"" "\\\"")) + query (str (name field) "=" (if (= "string" (name field-type)) + (str "\"" escaped-value "\"") + value))] + query)) + +(defn get-by-field [realm schema-name field value] + (let [q (to-query realm schema-name :eq field value)] + (.filtered (.objects realm (name schema-name)) q))) + +(defn get-one-by-field [realm schema-name field value] + (single (get-by-field realm schema-name field value))) + +(defn get-one-by-field-clj [realm schema-name field value] + (single-cljs (get-by-field realm schema-name field value))) + +(defn get-by-fields [realm schema-name op fields] + (let [queries (map (fn [[k v]] + (to-query realm schema-name :eq k v)) + fields)] + (.filtered (.objects realm (name schema-name)) + (case op + :and (and-query queries) + :or (or-query queries))))) + +(defn exists? [realm schema-name fields] + (pos? (.-length (get-by-fields realm schema-name :and fields)))) diff --git a/src/status_im/data_store/realm/discover.cljs b/src/status_im/data_store/realm/discover.cljs new file mode 100644 index 0000000000..104477989c --- /dev/null +++ b/src/status_im/data_store/realm/discover.cljs @@ -0,0 +1,78 @@ +(ns status-im.data-store.realm.discover + (:require [status-im.data-store.realm.core :as realm] + [taoensso.timbre :as log]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [ordering] + (-> (realm/get-all @realm/account-realm :discover) + (realm/sorted :created-at ordering))) + +(defn get-all-as-list + [ordering] + (-> (get-all ordering) + realm/realm-collection->list)) + +(defn get-tag-by-name [tag] + (log/debug "Getting tag: " tag) + (realm/get-one-by-field-clj @realm/account-realm :tag :name tag)) + +(defn- update-tag-counter [func tag] + (let [tag (:name tag) + tag-object (get-tag-by-name tag)] + (if tag-object + (realm/create @realm/account-realm :tag + {:name tag + :count (func (:count tag-object))} + true)))) + +(defn- update-tags-counter [func tags] + (doseq [tag (distinct tags)] + (update-tag-counter func tag))) + +(defn- get-tags + [message-id] + (-> (realm/get-one-by-field-clj @realm/account-realm :discover :message-id message-id) + (:tags) + (vals))) + +(defn- upsert-discover [{:keys [message-id tags] :as discover}] + (log/debug "Creating/updating discover with tags: " tags) + (let [prev-tags (get-tags message-id)] + (when prev-tags + (update-tags-counter dec prev-tags)) + (realm/create @realm/account-realm :discover discover true) + (update-tags-counter inc tags))) + +(defn exists? + [message-id] + (realm/exists? @realm/account-realm :discover {:message-id message-id})) + +(defn save + [discover] + (realm/write @realm/account-realm + #(upsert-discover discover))) + +(defn save-all + [discoveries] + (realm/write @realm/account-realm + (fn [] + (doseq [discover discoveries] + (upsert-discover discover))))) + +(defn delete + [by ordering critical-count to-delete-count] + (let [discoveries (realm/get-all @realm/account-realm :discover) + count (realm/get-count discoveries)] + (if (> count critical-count) + (let [to-delete (-> (realm/sorted discoveries by ordering) + (realm/page 0 to-delete-count))] + (realm/write @realm/account-realm + (fn [] + (log/debug (str "Deleting " (realm/get-count to-delete) " discoveries")) + (realm/delete @realm/account-realm to-delete))))))) + +(defn get-all-tags [] + (-> (realm/get-all @realm/account-realm :tag) + (realm/sorted :count :desc) + realm/realm-collection->list)) diff --git a/src/status_im/data_store/realm/messages.cljs b/src/status_im/data_store/realm/messages.cljs new file mode 100644 index 0000000000..5e53ddcb6d --- /dev/null +++ b/src/status_im/data_store/realm/messages.cljs @@ -0,0 +1,65 @@ +(ns status-im.data-store.realm.messages + (:require [status-im.data-store.realm.core :as realm]) + (:refer-clojure :exclude [exists?])) + + +(defn get-all + [] + (realm/get-all @realm/account-realm :message)) + +(defn get-all-as-list + [] + (-> (get-all) + realm/realm-collection->list)) + +(defn get-by-id + [message-id] + (realm/get-one-by-field-clj @realm/account-realm :message :message-id message-id)) + +(defn get-by-chat-id + ([chat-id] + (realm/get-by-field @realm/account-realm :message :chat-id chat-id)) + ([chat-id number-of-messages] + (get-by-chat-id chat-id 0 number-of-messages)) + ([chat-id from number-of-messages] + (-> (realm/get-by-field @realm/account-realm :message :chat-id chat-id) + (realm/sorted :timestamp :desc) + (realm/page from (+ from number-of-messages)) + (realm/realm-collection->list)))) + +(defn get-count-by-chat-id + [chat-id] + (-> (get-by-chat-id chat-id) + (realm/get-count))) + +(defn get-by-fields + [fields from number-of-messages] + (-> (realm/get-by-fields @realm/account-realm :message :and fields) + (realm/sorted :timestamp :desc) + (realm/page from (+ from number-of-messages)) + (realm/realm-collection->list))) + +(defn get-last-message + [chat-id] + (-> (realm/get-by-field @realm/account-realm :message :chat-id chat-id) + (realm/sorted :clock-value :desc) + (realm/single-cljs))) + +(defn get-unviewed + [] + (-> (realm/get-by-fields @realm/account-realm :message :and {:outgoing false + :message-status nil}) + (realm/realm-collection->list))) + +(defn exists? + [message-id] + (realm/exists? @realm/account-realm :message {:message-id message-id})) + +(defn save + [message] + (realm/save @realm/account-realm :message message true)) + +(defn delete-by-chat-id + [chat-id] + (realm/delete @realm/account-realm + (get-by-chat-id chat-id))) diff --git a/src/status_im/data_store/realm/pending_messages.cljs b/src/status_im/data_store/realm/pending_messages.cljs new file mode 100644 index 0000000000..23a7af8cac --- /dev/null +++ b/src/status_im/data_store/realm/pending_messages.cljs @@ -0,0 +1,35 @@ +(ns status-im.data-store.realm.pending-messages + (:require [status-im.data-store.realm.core :as realm] + [cljs.reader :refer [read-string]])) + +(defn get-all + [] + (realm/get-all @realm/account-realm :pending-message)) + +(defn get-all-as-list + [] + (->> (get-all) + realm/realm-collection->list + (map (fn [message] + (update message :topics read-string))))) + +(defn get-by-message-id + [message-id] + (realm/get-by-field @realm/account-realm :pending-message :message-id message-id)) + +(defn get-by-chat-id + [chat-id] + (realm/get-by-field @realm/account-realm :pending-message :chat-id chat-id)) + +(defn save + [pending-message] + (realm/save @realm/account-realm :pending-message pending-message true)) + +(defn delete + [{{:keys [message-id ack-of-message]} :payload}] + (let [message-id (or ack-of-message message-id)] + (realm/delete @realm/account-realm (get-by-message-id message-id)))) + +(defn delete-all-by-chat-id + [chat-id] + (realm/delete @realm/account-realm (get-by-chat-id chat-id))) diff --git a/src/status_im/data_store/realm/processed_messages.cljs b/src/status_im/data_store/realm/processed_messages.cljs new file mode 100644 index 0000000000..917f807d29 --- /dev/null +++ b/src/status_im/data_store/realm/processed_messages.cljs @@ -0,0 +1,25 @@ +(ns status-im.data-store.realm.processed-messages + (:require [status-im.data-store.realm.core :as realm]) + (:refer-clojure :exclude [exists?])) + +(defn get-all + [] + (-> (realm/get-all @realm/account-realm :processed-message) + (realm/sorted :ttl :asc))) + +(defn get-filtered + [condition] + (realm/filtered (get-all) condition)) + +(defn get-filtered-as-list + [condition] + (-> (get-filtered condition) + realm/realm-collection->list)) + +(defn save + [processed-message] + (realm/save @realm/account-realm :processed-message processed-message)) + +(defn delete + [condition] + (realm/delete @realm/account-realm (get-filtered condition))) diff --git a/src/status_im/data_store/realm/requests.cljs b/src/status_im/data_store/realm/requests.cljs new file mode 100644 index 0000000000..d87c1bbc77 --- /dev/null +++ b/src/status_im/data_store/realm/requests.cljs @@ -0,0 +1,40 @@ +(ns status-im.data-store.realm.requests + (:require [status-im.data-store.realm.core :as realm])) + +(defn get-all + [] + (realm/get-all @realm/account-realm :request)) + +(defn get-all-as-list + [] + (-> (get-all) + realm/realm-collection->list)) + +(defn get-open-by-chat-id + [chat-id] + (-> (realm/get-by-fields @realm/account-realm :request :and [[:chat-id chat-id] + [:status "open"]]) + (realm/sorted :added :desc) + (realm/realm-collection->list))) + +(defn save + [request] + (realm/save @realm/account-realm :request request true)) + +(defn save-all + [requests] + (realm/save-all @realm/account-realm :request requests true)) + +(defn- get-by-message-id + [chat-id message-id] + (-> (realm/get-by-fields @realm/account-realm :request :and [[:chat-id chat-id] + [:message-id message-id]]) + (realm/single))) + +(defn mark-as-answered + [chat-id message-id] + (realm/write @realm/account-realm + (fn [] + (-> (get-by-message-id chat-id message-id) + (.-status) + (set! "answered"))))) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs new file mode 100644 index 0000000000..393dd465ca --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -0,0 +1,7 @@ +(ns status-im.data-store.realm.schemas.account.core + (:require [status-im.data-store.realm.schemas.account.v1.core :as v1])) + +; put schemas ordered by version +(def schemas [{:schema v1/schema + :schemaVersion 1 + :migration v1/migration}]) diff --git a/src/status_im/data_store/realm/schemas/account/v1/chat.cljs b/src/status_im/data_store/realm/schemas/account/v1/chat.cljs new file mode 100644 index 0000000000..6223b65c64 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/chat.cljs @@ -0,0 +1,40 @@ +(ns status-im.data-store.realm.schemas.account.v1.chat + (:require [taoensso.timbre :as log] + [status-im.components.styles :refer [default-chat-color]])) + +(def schema {:name :chat + :primaryKey :chat-id + :properties {:chat-id :string + :name :string + :color {:type :string + :default default-chat-color} + :group-chat {:type :bool + :indexed true} + :group-admin {:type :string + :optional true} + :is-active :bool + :timestamp :int + :contacts {:type :list + :objectType :chat-contact} + :removed-at {:type :int + :optional true} + :removed-from-at {:type :int + :optional true} + :added-to-at {:type :int + :optional true} + :updated-at {:type :int + :optional true} + :last-message-id :string + :message-overhead {:type :int + :default 0} + :public-key {:type :string + :optional true} + :private-key {:type :string + :optional true} + :pending-contact? {:type :bool + :default false} + :contact-info {:type :string + :optional true}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating chat schema")) diff --git a/src/status_im/data_store/realm/schemas/account/v1/chat_contact.cljs b/src/status_im/data_store/realm/schemas/account/v1/chat_contact.cljs new file mode 100644 index 0000000000..49fccbe81f --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/chat_contact.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.account.v1.chat-contact + (:require [taoensso.timbre :as log])) + +(def schema {:name :chat-contact + :properties {:identity "string" + :is-in-chat {:type "bool" + :default true}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating chat-contact schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/command.cljs b/src/status_im/data_store/realm/schemas/account/v1/command.cljs new file mode 100644 index 0000000000..b731766c0c --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/command.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.account.v1.command + (:require [taoensso.timbre :as log])) + +(def schema {:name :command + :primaryKey :chat-id + :properties {:chat-id "string" + :file "string"}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating command schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/contact.cljs b/src/status_im/data_store/realm/schemas/account/v1/contact.cljs new file mode 100644 index 0000000000..d49edcbf74 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/contact.cljs @@ -0,0 +1,26 @@ +(ns status-im.data-store.realm.schemas.account.v1.contact + (:require [taoensso.timbre :as log])) + +(def schema {:name :contact + :primaryKey :whisper-identity + :properties {:address {:type "string" :optional true} + :whisper-identity "string" + :name {:type "string" :optional true} + :photo-path {:type "string" :optional true} + :last-updated {:type "int" :default 0} + :last-online {:type "int" :default 0} + :pending {:type "bool" :default false} + :status {:type "string" :optional true} + :public-key {:type :string + :optional true} + :private-key {:type :string + :optional true} + :dapp? {:type :bool + :default false} + :dapp-url {:type :string + :optional true} + :dapp-hash {:type :int + :optional true}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating contact schema")) diff --git a/src/status_im/data_store/realm/schemas/account/v1/core.cljs b/src/status_im/data_store/realm/schemas/account/v1/core.cljs new file mode 100644 index 0000000000..6e4732349c --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/core.cljs @@ -0,0 +1,42 @@ +(ns status-im.data-store.realm.schemas.account.v1.core + (:require [status-im.data-store.realm.schemas.account.v1.chat :as chat] + [status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact] + [status-im.data-store.realm.schemas.account.v1.command :as command] + [status-im.data-store.realm.schemas.account.v1.contact :as contact] + [status-im.data-store.realm.schemas.account.v1.discover :as discover] + [status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store] + [status-im.data-store.realm.schemas.account.v1.message :as message] + [status-im.data-store.realm.schemas.account.v1.pending-message :as pending-message] + [status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message] + [status-im.data-store.realm.schemas.account.v1.request :as request] + [status-im.data-store.realm.schemas.account.v1.tag :as tag] + [status-im.data-store.realm.schemas.account.v1.user-status :as user-status] + [taoensso.timbre :as log])) + +(def schema [chat/schema + chat-contact/schema + command/schema + contact/schema + discover/schema + kv-store/schema + message/schema + pending-message/schema + processed-message/schema + request/schema + tag/schema + user-status/schema]) + +(defn migration [old-realm new-realm] + (log/debug "migrating v1 account database: " old-realm new-realm) + (chat/migration old-realm new-realm) + (chat-contact/migration old-realm new-realm) + (command/migration old-realm new-realm) + (contact/migration old-realm new-realm) + (discover/migration old-realm new-realm) + (kv-store/migration old-realm new-realm) + (message/migration old-realm new-realm) + (pending-message/migration old-realm new-realm) + (processed-message/migration old-realm new-realm) + (request/migration old-realm new-realm) + (tag/migration old-realm new-realm) + (user-status/migration old-realm new-realm)) diff --git a/src/status_im/data_store/realm/schemas/account/v1/discover.cljs b/src/status_im/data_store/realm/schemas/account/v1/discover.cljs new file mode 100644 index 0000000000..7f96cc846c --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/discover.cljs @@ -0,0 +1,16 @@ +(ns status-im.data-store.realm.schemas.account.v1.discover + (:require [taoensso.timbre :as log])) + +(def schema {:name :discover + :primaryKey :message-id + :properties {:message-id "string" + :name {:type "string" :optional true} + :status "string" + :whisper-id "string" + :photo-path {:type "string" :optional true} + :tags {:type "list" + :objectType "tag"} + :created-at {:type "int" :default 0}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating discover schema")) diff --git a/src/status_im/data_store/realm/schemas/account/v1/kv_store.cljs b/src/status_im/data_store/realm/schemas/account/v1/kv_store.cljs new file mode 100644 index 0000000000..011a1ac552 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/kv_store.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.account.v1.kv-store + (:require [taoensso.timbre :as log])) + +(def schema {:name :kv-store + :primaryKey :key + :properties {:key "string" + :value "string"}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating kv-store schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/message.cljs b/src/status_im/data_store/realm/schemas/account/v1/message.cljs new file mode 100644 index 0000000000..c724cdc9a4 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/message.cljs @@ -0,0 +1,36 @@ +(ns status-im.data-store.realm.schemas.account.v1.message + (:require [taoensso.timbre :as log])) + +(def schema {:name :message + :primaryKey :message-id + :properties {:message-id "string" + :from "string" + :to {:type "string" + :optional true} + :group-id {:type "string" + :optional true} + :content "string" ;; TODO make it ArrayBuffer + :content-type "string" + :timestamp "int" + :chat-id {:type "string" + :indexed true} + :outgoing "bool" + :retry-count {:type :int + :default 0} + :same-author "bool" + :same-direction "bool" + :preview {:type :string + :optional true} + :message-type {:type :string + :optional true} + :message-status {:type :string + :optional true} + :user-statuses {:type :list + :objectType "user-status"} + :clock-value {:type :int + :default 0} + :show? {:type :bool + :default true}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating message schema")) diff --git a/src/status_im/data_store/realm/schemas/account/v1/pending_message.cljs b/src/status_im/data_store/realm/schemas/account/v1/pending_message.cljs new file mode 100644 index 0000000000..de046ef935 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/pending_message.cljs @@ -0,0 +1,22 @@ +(ns status-im.data-store.realm.schemas.account.v1.pending-message + (:require [taoensso.timbre :as log])) + +(def schema {:name :pending-message + :primaryKey :id + :properties {:id :string + :message-id :string + :chat-id {:type :string + :optional true} + :ack? :bool + :requires-ack? :bool + :from :string + :to {:type :string + :optional true} + :payload :string + :type :string + :topics :string + :attempts :int + :was-sent? :bool}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating pending-message schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/processed_message.cljs b/src/status_im/data_store/realm/schemas/account/v1/processed_message.cljs new file mode 100644 index 0000000000..a6ce431abf --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/processed_message.cljs @@ -0,0 +1,13 @@ +(ns status-im.data-store.realm.schemas.account.v1.processed-message + (:require [taoensso.timbre :as log])) + +(def schema {:name :processed-message + :primaryKey :id + :properties {:id :string + :message-id :string + :type {:type "string" + :optional true} + :ttl :int}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating processed-message schema")) diff --git a/src/status_im/data_store/realm/schemas/account/v1/request.cljs b/src/status_im/data_store/realm/schemas/account/v1/request.cljs new file mode 100644 index 0000000000..832b79d9dc --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/request.cljs @@ -0,0 +1,13 @@ +(ns status-im.data-store.realm.schemas.account.v1.request + (:require [taoensso.timbre :as log])) + +(def schema {:name :request + :properties {:message-id :string + :chat-id :string + :type :string + :status {:type :string + :default "open"} + :added :date}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating request schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/tag.cljs b/src/status_im/data_store/realm/schemas/account/v1/tag.cljs new file mode 100644 index 0000000000..6f081b9df6 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/tag.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.account.v1.tag + (:require [taoensso.timbre :as log])) + +(def schema {:name :tag + :primaryKey :name + :properties {:name "string" + :count {:type "int" :optional true :default 0}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating tag schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/account/v1/user_status.cljs b/src/status_im/data_store/realm/schemas/account/v1/user_status.cljs new file mode 100644 index 0000000000..ae388e4b56 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v1/user_status.cljs @@ -0,0 +1,12 @@ +(ns status-im.data-store.realm.schemas.account.v1.user-status + (:require [taoensso.timbre :as log])) + +(def schema {:name :user-status + :primaryKey :id + :properties {:id "string" + :whisper-identity {:type "string" + :default ""} + :status "string"}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating user-status schema")) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/base/core.cljs b/src/status_im/data_store/realm/schemas/base/core.cljs new file mode 100644 index 0000000000..34bd4bc171 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/core.cljs @@ -0,0 +1,7 @@ +(ns status-im.data-store.realm.schemas.base.core + (:require [status-im.data-store.realm.schemas.base.v1.core :as v1])) + +; put schemas ordered by version +(def schemas [{:schema v1/schema + :schemaVersion 1 + :migration v1/migration}]) diff --git a/src/status_im/data_store/realm/schemas/base/v1/account.cljs b/src/status_im/data_store/realm/schemas/base/v1/account.cljs new file mode 100644 index 0000000000..ddd68ba18f --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v1/account.cljs @@ -0,0 +1,23 @@ +(ns status-im.data-store.realm.schemas.base.v1.account + (:require [taoensso.timbre :as log])) + +(def schema {:name :account + :primaryKey :address + :properties {:address :string + :public-key :string + :updates-public-key {:type :string + :optional true} + :updates-private-key {:type :string + :optional true} + :name {:type :string :optional true} + :phone {:type :string :optional true} + :email {:type :string :optional true} + :status {:type :string :optional true} + :photo-path :string + :last-updated {:type :int :default 0} + :signed-up? {:type :bool + :default false} + :network :string}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating account schema")) diff --git a/src/status_im/data_store/realm/schemas/base/v1/core.cljs b/src/status_im/data_store/realm/schemas/base/v1/core.cljs new file mode 100644 index 0000000000..6addb40b3f --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v1/core.cljs @@ -0,0 +1,12 @@ +(ns status-im.data-store.realm.schemas.base.v1.core + (:require [status-im.data-store.realm.schemas.base.v1.account :as account] + [status-im.data-store.realm.schemas.base.v1.kv-store :as kv-store] + [taoensso.timbre :as log])) + +(def schema [account/schema + kv-store/schema]) + +(defn migration [old-realm new-realm] + (log/debug "migrating v1 base database: " old-realm new-realm) + (account/migration old-realm new-realm) + (kv-store/migration old-realm new-realm)) \ No newline at end of file diff --git a/src/status_im/data_store/realm/schemas/base/v1/kv_store.cljs b/src/status_im/data_store/realm/schemas/base/v1/kv_store.cljs new file mode 100644 index 0000000000..8bc17ec68c --- /dev/null +++ b/src/status_im/data_store/realm/schemas/base/v1/kv_store.cljs @@ -0,0 +1,10 @@ +(ns status-im.data-store.realm.schemas.base.v1.kv-store + (:require [taoensso.timbre :as log])) + +(def schema {:name :kv-store + :primaryKey :key + :properties {:key "string" + :value "string"}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating kv-store schema")) \ No newline at end of file diff --git a/src/status_im/data_store/requests.cljs b/src/status_im/data_store/requests.cljs new file mode 100644 index 0000000000..fd6a25239f --- /dev/null +++ b/src/status_im/data_store/requests.cljs @@ -0,0 +1,22 @@ +(ns status-im.data-store.requests + (:require [status-im.data-store.realm.requests :as data-store])) + +(defn get-all + [] + (data-store/get-all-as-list)) + +(defn get-open-by-chat-id + [chat-id] + (data-store/get-open-by-chat-id chat-id)) + +(defn save + [request] + (data-store/save request)) + +(defn save-all + [requests] + (data-store/save-all requests)) + +(defn mark-as-answered + [chat-id message-id] + (data-store/mark-as-answered chat-id message-id)) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs new file mode 100644 index 0000000000..f5d1776a3e --- /dev/null +++ b/src/status_im/db.cljs @@ -0,0 +1,60 @@ +(ns status-im.db + (:require [status-im.components.react :refer [animated]] + [status-im.components.animation :as anim] + [status-im.constants :refer [console-chat-id]] + [status-im.utils.platform :as p])) + +;; initial state of app-db +(def app-db {:identity-password "replace-me-with-user-entered-password" + :identity "me" + :current-public-key "me" + + :accounts {} + :current-account-id nil + + :profile-edit {:edit? false + :name nil + :email nil + :status nil + :photo-path nil} + + :new-contact-identity "" + :contacts {} + :discoveries {} + :discover-search-tags [] + :tags {} + + :chats {} + :chat {:command nil + :last-message nil} + :current-chat-id console-chat-id + + :contacts-ids #{} + :selected-contacts #{} + :chats-updated-signal 0 + :chat-ui-props {:show-actions? false + :show-bottom-info? false} + :selected-participants #{} + :view-id nil + :navigation-stack '() + :current-tag nil + :qr-codes {} + :keyboard-height 0 + :animations {;; todo clear this + :tabs-bar-value (anim/create-value 0)} + :loading-allowed true + + :sync-state :done + :sync-listener nil + :status-module-initialized? (or p/ios? js/goog.DEBUG) + :edit-mode {} + :network :testnet}) + +(defn chat-command-path [chat-id] + [:chats chat-id :command-input :command]) +(defn chat-command-to-message-id-path [chat-id] + [:chats chat-id :command-input :to-message-id]) +(defn chat-command-content-path [chat-id] + [:chats chat-id :command-input :content]) +(defn chat-command-request-path [chat-id message-id] + [:chats chat-id :command-requests message-id]) diff --git a/src/status_im/discover/handlers.cljs b/src/status_im/discover/handlers.cljs new file mode 100644 index 0000000000..fc483e140f --- /dev/null +++ b/src/status_im/discover/handlers.cljs @@ -0,0 +1,142 @@ +(ns status-im.discover.handlers + (:require [re-frame.core :refer [after dispatch enrich]] + [status-im.utils.utils :refer [first-index]] + [status-im.utils.handlers :refer [register-handler get-hashtags]] + [status-im.protocol.core :as protocol] + [status-im.navigation.handlers :as nav] + [status-im.data-store.discover :as discoveries] + [status-im.utils.handlers :as u] + [status-im.utils.datetime :as time] + [status-im.utils.random :as random])) + +(def request-discoveries-interval-s 600) + +(register-handler :init-discoveries + (fn [db _] + (-> db + (assoc :tags []) + (assoc :discoveries {})))) + +(defn identities [contacts] + (->> (map second contacts) + (remove (fn [{:keys [dapp? pending]}] + (or pending dapp?))) + (map :whisper-identity))) + +(defmethod nav/preload-data! :discover + [db _] + (-> db + (assoc-in [:toolbar-search :show] nil) + (assoc :tags (discoveries/get-all-tags)) + (assoc :discoveries (->> (discoveries/get-all :desc) + (map (fn [{:keys [message-id] :as discover}] + [message-id discover])) + (into {}))))) + +(register-handler :broadcast-status + (u/side-effect! + (fn [{:keys [current-public-key web3 current-account-id accounts contacts]} + [_ status hashtags]] + (let [{:keys [name photo-path]} (get accounts current-account-id) + message-id (random/id) + message {:message-id message-id + :from current-public-key + :payload {:message-id message-id + :status status + :hashtags (vec hashtags) + :profile {:name name + :profile-image photo-path}}}] + (doseq [id (identities contacts)] + (protocol/send-status! + {:web3 web3 + :message (assoc message :to id)})) + (dispatch [:status-received message]))))) + +(register-handler :status-received + (u/side-effect! + (fn [{:keys [discoveries] :as db} [_ {:keys [from payload]}]] + (when (and (not (discoveries/exists? (:message-id payload))) + (not (get discoveries (:message-id payload)))) + (let [{:keys [message-id status hashtags profile]} payload + {:keys [name profile-image]} profile + discover {:message-id message-id + :name name + :photo-path profile-image + :status status + :whisper-id from + :tags (map #(hash-map :name %) hashtags) + :created-at (time/now-ms)}] + (dispatch [:add-discover discover])))))) + +(register-handler :start-requesting-discoveries + (fn [{:keys [request-discoveries-timer] :as db}] + (when request-discoveries-timer + (js/clearInterval request-discoveries-timer)) + (dispatch [:request-discoveries]) + (assoc db :request-discoveries-timer + (js/setInterval #(dispatch [:request-discoveries]) + (* request-discoveries-interval-s 1000))))) + +(register-handler :request-discoveries + (u/side-effect! + (fn [{:keys [current-public-key web3 contacts]}] + (doseq [id (identities contacts)] + (when-not (protocol/message-pending? web3 :discoveries-request id) + (protocol/send-discoveries-request! + {:web3 web3 + :message {:from current-public-key + :to id + :message-id (random/id)}})))))) + +(register-handler :discoveries-send-portions + (u/side-effect! + (fn [{:keys [current-public-key contacts web3]} [_ to]] + (when (get contacts to) + (protocol/send-discoveries-response! + {:web3 web3 + :discoveries (discoveries/get-all :asc) + :message {:from current-public-key + :to to}}))))) + +(register-handler :discoveries-request-received + (u/side-effect! + (fn [_ [_ {:keys [from]}]] + (dispatch [:discoveries-send-portions from])))) + +(register-handler :discoveries-response-received + (u/side-effect! + (fn [{:keys [discoveries contacts]} [_ {:keys [payload from]}]] + (when (get contacts from) + (when-let [data (:data payload)] + (doseq [{:keys [message-id] :as discover} data] + (when (and (not (discoveries/exists? message-id)) + (not (get discoveries message-id))) + (let [discover (assoc discover :created-at (time/now-ms))] + (dispatch [:add-discover discover]))))))))) + +(defn add-discover + [db [_ discover]] + (assoc db :new-discover discover)) + +(defn save-discover! + [{:keys [new-discover]} _] + (discoveries/save new-discover)) + +(defn reload-tags! + [db _] + (assoc db :tags (discoveries/get-all-tags) + :discoveries (->> (discoveries/get-all :desc) + (map (fn [{:keys [message-id] :as discover}] + [message-id discover])) + (into {})))) + +(register-handler :add-discover + (-> add-discover + ((after save-discover!)) + ((enrich reload-tags!)))) + +(register-handler + :remove-old-discoveries! + (u/side-effect! + (fn [_ _] + (discoveries/delete :created-at :asc 1000 200)))) diff --git a/src/status_im/discover/screen.cljs b/src/status_im/discover/screen.cljs new file mode 100644 index 0000000000..300d78cf43 --- /dev/null +++ b/src/status_im/discover/screen.cljs @@ -0,0 +1,93 @@ +(ns status-im.discover.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require + [reagent.core :as r] + [re-frame.core :refer [dispatch subscribe]] + [clojure.string :as str] + [status-im.components.react :refer [view + scroll-view + text + text-input + icon]] + [status-im.components.toolbar.view :refer [toolbar-with-search]] + [status-im.components.toolbar.actions :as act] + [status-im.components.drawer.view :refer [open-drawer]] + [status-im.components.carousel.carousel :refer [carousel]] + [status-im.discover.views.popular-list :refer [discover-popular-list]] + [status-im.discover.views.discover-list-item :refer [discover-list-item]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.i18n :refer [label]] + [status-im.discover.styles :as st] + [status-im.contacts.styles :as contacts-st])) + +(defn get-hashtags [status] + (let [hashtags (map #(str/lower-case (str/replace % #"#" "")) (re-seq #"[^ !?,;:.]+" status))] + (or hashtags []))) + +(defn toolbar-view [show-search?] + [toolbar-with-search + {:show-search? show-search? + :search-key :discover + :title (label :t/discover) + :search-placeholder (label :t/search-tags) + :nav-action (act/hamburger open-drawer) + :on-search-submit (fn [text] + (when-not (str/blank? text) + (let [hashtags (get-hashtags text)] + (dispatch [:set :discover-search-tags hashtags]) + (dispatch [:navigate-to :discover-search-results]))))}]) + +(defn title [label-kw spacing?] + [view st/section-spacing + [text {:style (merge (get-in platform-specific [:component-styles :discover :subtitle]) + (when spacing? {:margin-top 16})) + :uppercase? (get-in platform-specific [:discover :uppercase-subtitles?]) + :font :medium} + (label label-kw)]]) + +(defview discover-popular [{:keys [contacts current-account]}] + [popular-tags [:get-popular-tags 10]] + [view st/popular-container + [title :t/popular-tags false] + (if (pos? (count popular-tags)) + [carousel {:pageStyle st/carousel-page-style + :gap 8 + :sneak 16 + :count (count popular-tags)} + (for [{:keys [name]} popular-tags] + [discover-popular-list {:tag name + :contacts contacts + :current-account current-account}])] + [text (label :t/none)])]) + +(defview discover-recent [{:keys [current-account]}] + [discoveries [:get-recent-discoveries]] + (when (seq discoveries) + [view st/recent-container + [title :t/recent true] + [view st/recent-list + (let [discoveries (map-indexed vector discoveries)] + (for [[i {:keys [message-id] :as message}] discoveries] + ^{:key (str "message-recent-" message-id)} + [discover-list-item {:message message + :show-separator? (not= (inc i) (count discoveries)) + :current-account current-account}]))]])) + +(defview discover [current-view?] + [show-search [:get-in [:toolbar-search :show]] + contacts [:get :contacts] + current-account [:get-current-account] + discoveries [:get-recent-discoveries]] + [view st/discover-container + [toolbar-view (and current-view? + (= show-search :discover))] + (if discoveries + [scroll-view st/scroll-view-container + [discover-popular {:contacts contacts + :current-account current-account}] + [discover-recent {:current-account current-account}]] + [view contacts-st/empty-contact-groups + ;; todo change icon + [icon :group_big contacts-st/empty-contacts-icon] + [text {:style contacts-st/empty-contacts-text} + (label :t/no-statuses-discovered)]])]) diff --git a/src/status_im/discover/search_results.cljs b/src/status_im/discover/search_results.cljs new file mode 100644 index 0000000000..5275eeeff6 --- /dev/null +++ b/src/status_im/discover/search_results.cljs @@ -0,0 +1,62 @@ +(ns status-im.discover.search-results + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.react :refer [view + text + icon + list-view + list-item + scroll-view]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.discover.views.discover-list-item :refer [discover-list-item]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.i18n :refer [label]] + [status-im.discover.styles :as st] + [status-im.contacts.styles :as contacts-st] + [taoensso.timbre :as log])) + +(defn render-separator [_ row-id _] + (list-item [view {:style st/row-separator + :key row-id}])) + +(defn title-content [tags] + [scroll-view {:horizontal true + :bounces false + :flex 1 + :contentContainerStyle st/tag-title-scroll} + [view st/tag-title-container + (for [tag (take 3 tags)] + ^{:key (str "tag-" tag)} + [view (merge (get-in platform-specific [:component-styles :discover :tag]) + {:margin-left 2 :margin-right 2}) + [text {:style st/tag-title + :font :default} + (str " #" tag)]])]]) + +(defview discover-search-results [] + [discoveries [:get-popular-discoveries 250] + tags [:get :discover-search-tags] + current-account [:get-current-account]] + (let [discoveries (:discoveries discoveries) + datasource (to-datasource discoveries)] + [view st/discover-tag-container + [status-bar] + [toolbar {:nav-action (act/back #(dispatch [:navigate-back])) + :custom-content (title-content tags) + :style st/discover-tag-toolbar}] + (if (empty? discoveries) + [view st/empty-view + ;; todo change icon + [icon :group_big contacts-st/empty-contacts-icon] + [text {:style contacts-st/empty-contacts-text} + (label :t/no-statuses-found)]] + [list-view {:dataSource datasource + :renderRow (fn [row _ _] + (list-item [discover-list-item + {:message row + :current-account current-account}])) + :renderSeparator render-separator + :style st/recent-list}])])) diff --git a/src/status_im/discover/styles.cljs b/src/status_im/discover/styles.cljs new file mode 100644 index 0000000000..acc19e856d --- /dev/null +++ b/src/status_im/discover/styles.cljs @@ -0,0 +1,146 @@ +(ns status-im.discover.styles + (:require [status-im.components.styles :refer [color-gray2 + color-white + color-light-gray]] + [status-im.components.toolbar.styles :refer [toolbar-background2]])) + +;; Common + +(def row-separator + {:border-bottom-width 1 + :border-bottom-color "#eff2f3"}) + +(def row + {:flex-direction :row + :margin-bottom 10}) + +(def column + {:flex-direction :column}) + +(def empty-view + {:flex 1 + :background-color color-white + :align-items :center + :justify-content :center}) + +(def section-spacing + {:padding 16}) + +(def scroll-view-container + {:bounces false}) + +;; Popular + +(def popular-container + {:background-color toolbar-background2}) + +(def carousel-page-style + {}) + +(def tag-name + {:color "#7099e6" + :font-size 14 + :padding-right 5 + :padding-bottom 2 + :align-items :center + :justify-content :center}) + +(def tag-count + {:color "#838c93" + :font-size 12 + :padding-right 6 + :padding-bottom 2 + :align-items :center + :justify-content :center}) + +(def tag-count-container + {:flex 0.2 + :flex-direction "column" + :align-items "flex-end" + :padding-top 6 + :padding-right 9}) + +(def separator + {:background-color "rgb(200, 199, 204)" + :height 0.5}) + +;; Popular list item + +(def popular-list-container + {:flex 1 + :background-color :white + :padding-top 18 + :padding-left 16}) + +(def popular-list-item + {:flex-direction :row + :padding-bottom 16 + :top 1}) + +(def popular-list-item-name + {:color "black" + :font-size 15 + :padding-bottom 4}) + +(def popular-list-item-name-container + {:flex 0.8 + :flex-direction "column" + :padding-top 16}) + +(def popular-list-item-avatar-container + {:flex 0.2 + :flex-direction "column" + :align-items :center + :padding-top 16}) + +;; discover_recent + +(def recent-container + {:background-color toolbar-background2}) + +(def recent-list + {:background-color :white + :padding-left 16}) + +;; Discover tag + +(def discover-tag-toolbar + {:border-bottom-color "#D7D7D7" + :border-bottom-width 1}) + +(def discover-tag-container + {:flex 1 + :backgroundColor color-light-gray}) + +(def tag-title-scroll + {:flex 1 + :alignItems "center" + :justifyContent "center"}) + +(def tag-title-container + {:flex 1 + :alignItems "center" + :justifyContent "center" + :flex-direction "row"}) + +(def tag-title + {:color "#7099e6" + :font-size 14 + :padding-right 5 + :padding-bottom 2}) + +(def icon-back + {:width 8 + :height 14}) + +(def icon-search + {:width 17 + :height 17}) + +(def discover-container + {:flex 1 + :backgroundColor color-white}) + +(def search-icon + {:width 17 + :height 17}) diff --git a/src/status_im/discover/subs.cljs b/src/status_im/discover/subs.cljs new file mode 100644 index 0000000000..6849df82ec --- /dev/null +++ b/src/status_im/discover/subs.cljs @@ -0,0 +1,54 @@ +(ns status-im.discover.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [status-im.utils.datetime :as time])) + +(defn- calculate-priority [{:keys [chats contacts current-public-key]} + {:keys [whisper-id created-at]}] + (let [contact (get contacts whisper-id) + chat (get chats whisper-id) + seen-online-recently? (< (- (time/now-ms) (get contact :last-online)) + time/hour) + me? (= current-public-key whisper-id)] + (+ created-at ;; message is newer => priority is higher + (if (or me? contact) time/day 0) ;; user exists in contact list => increase priority + (if (or me? chat) time/day 0) ;; chat with this user exists => increase priority + (if (or me? seen-online-recently?) time/hour 0) ;; the user was online recently => increase priority + ))) + +(defn- get-discoveries-by-tags [discoveries current-tag tags] + (let [tags' (or tags [current-tag])] + (filter #(every? (->> (map :name (:tags %)) + (into (hash-set))) + tags') + (vals discoveries)))) + +(register-sub :get-popular-discoveries + (fn [db [_ limit tags]] + (let [discoveries (reaction (:discoveries @db)) + current-tag (reaction (:current-tag @db)) + search-tags (reaction (:discover-search-tags @db)) + discoveries (->> (get-discoveries-by-tags @discoveries @current-tag (or tags @search-tags)) + (map #(assoc % :priority (calculate-priority db %))) + (sort-by :priority >))] + (reaction {:discoveries (take limit discoveries) + :total (count discoveries)})))) + +(register-sub :get-recent-discoveries + (fn [db] + (->> (:discoveries @db) + (vals) + (reaction)))) + +(register-sub :get-popular-tags + (fn [db [_ limit]] + (-> (take limit (:tags @db)) + (reaction)))) + +(register-sub :get-discover-search-results + (fn [db _] + (let [discoveries (reaction (:discoveries @db)) + current-tag (reaction (:current-tag @db)) + tags (reaction (:discover-search-tags @db))] + (-> (get-discoveries-by-tags @discoveries @current-tag @tags) + (reaction))))) diff --git a/src/status_im/discover/views/discover_list_item.cljs b/src/status_im/discover/views/discover_list_item.cljs new file mode 100644 index 0000000000..8804f0d1e1 --- /dev/null +++ b/src/status_im/discover/views/discover_list_item.cljs @@ -0,0 +1,52 @@ +(ns status-im.discover.views.discover-list-item + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] + [status-im.components.react :refer [view text image touchable-highlight]] + [status-im.discover.styles :as st] + [status-im.components.status-view.view :refer [status-view]] + [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.utils.identicon :refer [identicon]] + [status-im.components.chat-icon.screen :as ci] + [status-im.utils.platform :refer [platform-specific]])) + +(defview discover-list-item [{{:keys [name + photo-path + whisper-id + message-id + status] + :as message} :message + show-separator? :show-separator? + {account-photo-path :photo-path + account-address :public-key + account-name :name + :as current-account} :current-account}] + [{contact-name :name + contact-photo-path :photo-path} [:get-in [:contacts whisper-id]]] + (let [item-style (get-in platform-specific [:component-styles :discover :item])] + [view + [view st/popular-list-item + [view st/popular-list-item-name-container + [text {:style st/popular-list-item-name + :font :medium + :number-of-lines 1} + (cond + (= account-address whisper-id) account-name + (not (str/blank? contact-name)) contact-name + (not (str/blank? name)) name + :else (generate-gfy))] + [status-view {:id message-id + :style (:status-text item-style) + :status status}]] + [view (merge st/popular-list-item-avatar-container + (:icon item-style)) + [touchable-highlight {:on-press #(dispatch [:start-chat whisper-id])} + [view + [ci/chat-icon (cond + (= account-address whisper-id) account-photo-path + (not (str/blank? contact-photo-path)) contact-photo-path + (not (str/blank? photo-path)) photo-path + :else (identicon whisper-id)) + {:size 36}]]]]] + (when show-separator? + [view st/separator])])) diff --git a/src/status_im/discover/views/popular_list.cljs b/src/status_im/discover/views/popular_list.cljs new file mode 100644 index 0000000000..17e6224c88 --- /dev/null +++ b/src/status_im/discover/views/popular_list.cljs @@ -0,0 +1,36 @@ +(ns status-im.discover.views.popular-list + (:require-macros [status-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + list-view + list-item + touchable-highlight + text]] + [status-im.discover.styles :as st] + [status-im.utils.listview :refer [to-datasource]] + [status-im.discover.views.discover-list-item :refer [discover-list-item]] + [status-im.utils.platform :refer [platform-specific]])) + +(defview discover-popular-list [{:keys [tag contacts current-account]}] + [discoveries [:get-popular-discoveries 3 [tag]]] + [view (merge st/popular-list-container + (get-in platform-specific [:component-styles :discover :popular])) + [view st/row + [view (get-in platform-specific [:component-styles :discover :tag]) + [touchable-highlight {:on-press #(do (dispatch [:set :discover-search-tags [tag]]) + (dispatch [:navigate-to :discover-search-results]))} + [view + [text {:style st/tag-name + :font :medium} + (str " #" (name tag))]]]] + [view st/tag-count-container + [text {:style st/tag-count + :font :default} + (:total discoveries)]]] + (let [discoveries (map-indexed vector (:discoveries discoveries))] + (for [[i {:keys [message-id] :as discover}] discoveries] + ^{:key (str "message-popular-" message-id)} + [discover-list-item {:message discover + :show-separator? (not= (inc i) (count discoveries)) + :current-account current-account}]))]) diff --git a/src/status_im/group_settings/handlers.cljs b/src/status_im/group_settings/handlers.cljs new file mode 100644 index 0000000000..de0ecaa99d --- /dev/null +++ b/src/status_im/group_settings/handlers.cljs @@ -0,0 +1,210 @@ +(ns status-im.group-settings.handlers + (:require [re-frame.core :refer [debug dispatch after enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.chat.handlers :refer [delete-messages!]] + [status-im.protocol.core :as protocol] + [status-im.utils.random :as random] + [status-im.data-store.contacts :as contacts] + [status-im.data-store.messages :as messages] + [status-im.data-store.chats :as chats] + [status-im.constants :refer [text-content-type]] + [status-im.utils.handlers :as u] + [status-im.navigation.handlers :as nav])) + +(defmethod nav/preload-data! :group-settings + [db _] + (assoc db :selected-participants #{})) + +(defn save-property! + [current-chat-id property-name value] + (chats/save-property current-chat-id property-name value)) + +(defn save-chat-property! + [db-name property-name] + (fn [{:keys [current-chat-id] :as db} _] + (let [property (db-name db)] + (save-property! current-chat-id property-name property)))) + +(defn update-chat-property + [db-name property-name] + (fn [{:keys [current-chat-id] :as db} _] + (let [property (db-name db)] + (assoc-in db [:chats current-chat-id property-name] property)))) + +(defn prepare-chat-settings + [{:keys [current-chat-id] :as db} _] + (let [{:keys [name color]} (-> db + (get-in [:chats current-chat-id]) + (select-keys [:name :color]))] + (-> db + (assoc :new-chat-name name + :new-chat-color color + :group-settings {})))) + +(register-handler :show-group-settings + (after (fn [_ _] (dispatch [:navigate-to :group-settings]))) + prepare-chat-settings) + +(register-handler :set-chat-name + (after (save-chat-property! :new-chat-name :name)) + (update-chat-property :new-chat-name :name)) + +(register-handler :set-chat-color + (after (fn [{:keys [current-chat-id]} [_ color]] + (save-property! current-chat-id :color (name color)))) + (fn [{:keys [current-chat-id] :as db} [_ color]] + (assoc-in db [:chats current-chat-id :color] color))) + +(defn clear-messages + [{:keys [current-chat-id] :as db} _] + (assoc-in db [:chats current-chat-id :messages] '())) + +(register-handler :clear-history + (after delete-messages!) + clear-messages) + +(register-handler :group-settings + (fn [db [_ k v]] + (assoc-in db [:group-settings k] v))) + +(defn remove-identities [collection identities] + (remove #(identities (:identity %)) collection)) + +(defn remove-members + [{:keys [current-chat-id selected-participants] :as db} _] + (update-in db [:chats current-chat-id :contacts] + remove-identities selected-participants)) + +(defn remove-members-from-chat! + [{:keys [current-chat-id selected-participants] :as db} _] + (chats/remove-contacts current-chat-id selected-participants)) + +(defn notify-about-removing! + [{:keys [web3 current-chat-id selected-participants chats current-public-key]} _] + (let [{:keys [private public] :as new-keypair} (protocol/new-keypair!) + {:keys [name private-key public-key] + :as chat} (get chats current-chat-id) + old-keypair {:private private-key + :public public-key} + contacts (get chat :contacts) + identities (-> (map :identity contacts) + set + (clojure.set/difference selected-participants))] + (dispatch [:update-chat! {:chat-id current-chat-id + :private-key private + :public-key public}]) + (doseq [participant selected-participants] + (let [id (random/id)] + (doseq [keypair [old-keypair new-keypair]] + (protocol/remove-from-group! + {:web3 web3 + :group-id current-chat-id + :identity participant + :keypair keypair + :message {:from current-public-key + :message-id id}})))) + (protocol/start-watching-group! + {:web3 web3 + :group-id current-chat-id + :identity current-public-key + :keypair new-keypair + :callback #(dispatch [:incoming-message %1 %2])}) + (protocol/update-group! + {:web3 web3 + :group {:id current-chat-id + :name name + :contacts (conj identities current-public-key) + :admin current-public-key + :keypair new-keypair} + :identities identities + :message {:from current-public-key + :message-id (random/id)}}))) + +(defn system-message [message-id content] + {:from "system" + :message-id message-id + :content content + :content-type text-content-type}) + +(defn removed-participant-message [chat-id identity] + (let [contact-name (:name (contacts/get-by-id identity))] + (->> (str "You've removed " (or contact-name identity)) + (system-message (random/id)) + (messages/save chat-id)))) + +(defn create-removing-messages! + [{:keys [current-chat-id selected-participants]} _] + (doseq [participant selected-participants] + (removed-participant-message current-chat-id participant))) + +(defn deselect-members [db _] + (assoc db :selected-participants #{})) + +(register-handler :remove-participants + ;; todo check if user have rights to add/remove participants + ;; todo order of operations tbd + (-> remove-members + ;; todo shouldn't this be done only after receiving of the "ack message" + ;; about the api call that removes participants from the group? + ((after remove-members-from-chat!)) + ;; todo uncomment + ((after notify-about-removing!)) + ((after create-removing-messages!)) + ((enrich deselect-members)) + debug)) + +(defn add-memebers + [{:keys [current-chat-id selected-participants] :as db} _] + (let [new-identities (map #(hash-map :identity %) selected-participants)] + (update db [:chats current-chat-id :contacts] concat new-identities))) + +(defn add-members-to-chat! + [{:keys [current-chat-id selected-participants]} _] + (chats/add-contacts current-chat-id selected-participants)) + +(defn notify-about-new-members! + [{:keys [current-chat-id selected-participants + current-public-key chats web3]} _] + (let [{:keys [name contacts]} (chats current-chat-id) + identities (map :identity contacts) + + {:keys [public private] + :as new-keypair} (protocol/new-keypair!) + + group-message {:web3 web3 + :group {:id current-chat-id + :name name + :contacts (conj identities current-public-key) + :admin current-public-key} + :message {:from current-public-key + :message-id (random/id)}}] + (dispatch [:update-chat! {:chat-id current-chat-id + :public-key public + :private-key private}]) + (protocol/start-watching-group! {:web3 web3 + :group-id current-chat-id + :identity current-public-key + :keypair new-keypair + :callback #(dispatch [:incoming-message %1 %2])}) + (protocol/invite-to-group! + (-> group-message + (assoc-in [:group :keypair] new-keypair) + (assoc :identities selected-participants))) + (protocol/update-group! + (-> group-message + (assoc-in [:group :keypair] new-keypair) + (assoc :identities identities))) + (doseq [identity selected-participants] + (protocol/add-to-group! {:web3 web3 + :group-id current-chat-id + :identity identity + :keypair new-keypair + :message {:from current-public-key + :message-id (random/id)}})))) + +(register-handler :add-new-participants + ;; todo order of operations tbd + (-> add-memebers + ((after add-members-to-chat!)) + ((after notify-about-new-members!)) + ((enrich deselect-members)))) diff --git a/src/status_im/group_settings/screen.cljs b/src/status_im/group_settings/screen.cljs new file mode 100644 index 0000000000..ae733b9642 --- /dev/null +++ b/src/status_im/group_settings/screen.cljs @@ -0,0 +1,173 @@ +(ns status-im.group-settings.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text-input + text + image + icon + modal + picker + picker-item + scroll-view + touchable-highlight]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.chat-icon.screen :refer [chat-icon-view-action]] + [status-im.group-settings.styles.group-settings :as st] + [status-im.group-settings.views.member :refer [member-view]] + [status-im.i18n :refer [label]] + [status-im.group-settings.views.color-settings :refer [color-settings]])) + +(defn remove-member [] + (dispatch [:remove-participants])) + +(defn close-member-menu [] + (dispatch [:set :selected-participants #{}])) + +;; TODO not in design +(defview member-menu [] + [{:keys [name] :as participant} [:selected-participant]] + (when participant + [modal {:animationType :none + :transparent false + :onRequestClose close-member-menu} + [touchable-highlight {:style st/modal-container + :on-press close-member-menu} + [view st/modal-inner-container + [text {:style st/modal-member-name} name] + [touchable-highlight {:on-press remove-member} + [view + [text {:style st/modal-remove-text} + (label :t/remove)]]]]]])) + +(defview chat-members [] + [members [:current-chat-contacts]] + [view st/chat-members-container + (for [member members] + ^{:key member} [member-view member])]) + +(defn setting-view [{:keys [icon-style custom-icon handler title subtitle] + icon-name :icon}] + [touchable-highlight {:on-press handler} + [view st/setting-row + [view st/setting-icon-view + (or custom-icon + [icon icon-name icon-style])] + [view st/setting-view + [text {:style st/setting-title} title] + (when-let [subtitle subtitle] + [text {:style st/setting-subtitle} + subtitle])]]]) + +(defview chat-color-icon [] + [chat-color [:chat :color]] + [view {:style (st/chat-color-icon chat-color)}]) + +(defn show-chat-color-picker [] + (dispatch [:group-settings :show-color-picker true])) + +(defn settings-view [] + (let [settings [{:custom-icon [chat-color-icon] + :title (label :t/change-color) + :handler show-chat-color-picker} + ;; TODO not implemented: Notifications + (merge {:title (label :t/notifications-title) + :subtitle (label :t/not-implemented) + :handler nil} + (if true + {:icon :notifications-on + :icon-style {:width 16 + :height 21}} + {:icon :muted + :icon-style {:width 18 + :height 21}})) + {:icon :close-gray + :icon-style {:width 12 + :height 12} + :title (label :t/clear-history) + ;; TODO show confirmation dialog? + :handler #(dispatch [:clear-history])} + {:icon :bin + :icon-style {:width 12 + :height 18} + :title (label :t/delete-and-leave) + ;; TODO show confirmation dialog? + :handler #(dispatch [:leave-group-chat])}]] + [view st/settings-container + (for [setting settings] + ^{:key setting} [setting-view setting])])) + +(defview chat-icon [] + [chat-id [:chat :chat-id] + group-chat [:chat :group-chat] + name [:chat :name] + color [:chat :color]] + [view st/action + [chat-icon-view-action chat-id group-chat name color false]]) + +(defn new-group-toolbar [] + [view + [status-bar] + [toolbar {:title (label :t/chat-settings) + :custom-action [chat-icon]}]]) + +(defn focus [] + (dispatch [:set ::name-input-focused true])) + +(defn blur [] + (dispatch [:set ::name-input-focused false])) + +(defn save [] + (dispatch [:set-chat-name])) + +(defview chat-name [] + [name [:chat :name] + new-name [:get :new-chat-name] + validation-messages [:new-chat-name-validation-messages] + focused? [:get ::name-input-focused] + valid? [:new-chat-name-valid?]] + [view + [text {:style st/chat-name-text} (label :t/chat-name)] + [view (st/chat-name-value-container focused?) + [text-input {:style st/chat-name-value + :ref #(when (and % focused?) (.focus %)) + :on-change-text #(dispatch [:set :new-chat-name %]) + :on-focus focus + :on-blur blur} + name] + (if (or focused? (not= name new-name)) + [touchable-highlight {:style (st/chat-name-btn-edit-container valid?) + :on-press save} + [view [icon :ok_purple st/add-members-icon]]] + [touchable-highlight {:style (st/chat-name-btn-edit-container true) + :on-press focus} + [view [text {:style st/chat-name-btn-edit-text} (label :t/edit)]]])] + (when (pos? (count validation-messages)) + [text {:style st/chat-name-validation-message} (first validation-messages)])]) + +(defview members [] + [current-pk [:get :current-public-key] + group-admin [:chat :group-admin]] + (when (= current-pk group-admin) + [view + [text {:style st/members-text} (label :t/members-title)] + [touchable-highlight {:on-press #(dispatch [:navigate-to :add-participants])} + ;; TODO add participants view is not in design + [view st/add-members-container + [icon :add_gray st/add-members-icon] + [text {:style st/add-members-text} + (label :t/add-members)]]] + [chat-members]])) + +(defn group-settings [] + [view st/group-settings + [new-group-toolbar] + [scroll-view st/body + [chat-name] + [members] + [text {:style st/settings-text} + (label :t/settings)] + [settings-view]] + [color-settings] + [member-menu]]) diff --git a/src/status_im/group_settings/styles/color_settings.cljs b/src/status_im/group_settings/styles/color_settings.cljs new file mode 100644 index 0000000000..1180edfe55 --- /dev/null +++ b/src/status_im/group_settings/styles/color_settings.cljs @@ -0,0 +1,78 @@ +(ns status-im.group-settings.styles.color-settings) + +(def color-highlight + {:flex 0.25}) + +(def color-icon-comtainer + {:justify-content :center + :align-items :center}) + +(defn color-item [color] + {:width 70 + :height 70 + :border-radius 70 + :background-color color + :justify-content :center + :align-items :center}) + +(def icon-ok + {:width 17.5 + :height 13.5}) + +(def container-height 225) + +(def label-container-top-margin 16) +(def label-container-bottom-margin 8) +(def font-size 16) + +(def color-settings-container + {:position :absolute + :right 0 + :left 0 + :bottom 0 + :height container-height + :background-color :white + :border-top-color :#0000001f + :border-top-width 1 + :align-items :stretch}) + +(def label-container + {:margin-top label-container-top-margin + :align-items :center + :margin-bottom 8}) + +(def label + {:font-size 16 + :color :black}) + +(def close-highlight + {:position :absolute + :bottom (- container-height 15 + label-container-top-margin + label-container-bottom-margin) + :right 18}) + +(def close-container-size 24) +(def close-settings-container + {:width close-container-size + :height close-container-size + :justify-content :center + :align-items :center}) + +(def close-icon + {:width 12 + :height 12}) + +(def all-colors-container + {:flex-direction :column + :align-items :stretch + :height (- container-height + label-container-top-margin + font-size + close-container-size)}) + +(def color-container + {:flex 0.5 + :flex-direction :row + :align-items :center + :justify-content :center}) diff --git a/src/status_im/group_settings/styles/group_settings.cljs b/src/status_im/group_settings/styles/group_settings.cljs new file mode 100644 index 0000000000..3e98cf9547 --- /dev/null +++ b/src/status_im/group_settings/styles/group_settings.cljs @@ -0,0 +1,163 @@ +(ns status-im.group-settings.styles.group-settings + (:require [status-im.components.styles :refer [color-white + color-purple + separator-color + text1-color + text2-color]])) + +(def modal-container + {:flex 1 + :justifyContent :center + :padding 20}) + +(def modal-inner-container + {:borderRadius 10 + :alignItems :center + :padding 5 + :backgroundColor color-white}) + +(def modal-member-name + {:color text2-color + :fontSize 14 + :lineHeight 20}) + +(def modal-remove-text + {:margin 10 + :color text1-color + :fontSize 14 + :lineHeight 20}) + +(def modal-color-picker-inner-container + {:borderRadius 10 + :padding 5 + :backgroundColor color-white}) + +(def modal-color-picker-save-btn-text + {:margin 10 + :alignSelf :center + :color text1-color + :fontSize 14 + :lineHeight 20}) + +(def chat-members-container + {:marginBottom 10}) + +(def action + {:width 56 + :height 56 + :alignItems :center + :justifyContent :center}) + +(def group-settings + {:flex 1 + :flexDirection :column + :backgroundColor color-white}) + +(def body + {:flex 1 + :flexDirection :column}) + +(def chat-name-text + {:marginTop 24 + :marginLeft 16 + :marginBottom 16 + :color text2-color + :fontSize 14 + :lineHeight 20}) + +(defn chat-name-value-container [focused?] + {:flexDirection :row + :marginLeft 16 + :height 56 + :alignItems :center + :justifyContent :center + :borderBottomWidth 2 + :borderBottomColor (if focused? color-purple separator-color)}) + +(def chat-name-value + {:flex 1 + :fontSize 16 + :color text1-color}) + +(def chat-name-validation-message + {:marginTop 8 + :marginLeft 16 + :color :red}) + +(defn chat-name-btn-edit-container [enabled?] + {:padding 16 + :justifyContent :center + :opacity (if enabled? 1 0.3)}) + +(def chat-name-btn-edit-text + {:color text2-color + :fontSize 16 + :lineHeight 20}) + +(def members-text + {:marginTop 24 + :marginLeft 16 + :marginBottom 16 + :color text2-color + :fontSize 14 + :lineHeight 20}) + +(def add-members-icon + {:marginVertical -1 + :marginLeft 19 + :marginHorizontal 3 + :width 17 + :height 17}) + +(def add-members-container + {:flexDirection :row}) + +(def add-members-text + {:marginTop 18 + :marginLeft 32 + :color text2-color + :fontSize 16 + :lineHeight 20}) + +(def settings-text + {:marginTop 24 + :marginLeft 16 + :marginBottom 16 + :color text2-color + :fontSize 14 + :lineHeight 20}) + +(def settings-container + {:flexDirection :column}) + +(def setting-row + {:flexDirection :row + :height 56}) + +(def setting-icon-view + {:width 56 + :height 56 + :alignItems :center + :justifyContent :center}) + +(def setting-view + {:flex 1 + :marginLeft 16 + :alignItems :flex-start + :justifyContent :center}) + +(def setting-title + {:marginTop -2.5 + :color text1-color + :fontSize 16}) + +(def setting-subtitle + {:marginTop 1 + :color text2-color + :fontSize 12}) + +(defn chat-color-icon [color] + {:borderRadius 12 + :width 24 + :height 24 + :backgroundColor color}) diff --git a/src/status_im/group_settings/subs.cljs b/src/status_im/group_settings/subs.cljs new file mode 100644 index 0000000000..c1bb17da4d --- /dev/null +++ b/src/status_im/group_settings/subs.cljs @@ -0,0 +1,31 @@ +(ns status-im.group-settings.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [status-im.constants :refer [max-chat-name-length]])) + +(register-sub :selected-participant + (fn [db _] + (reaction + (let [identity (first (:selected-participants @db))] + (get-in @db [:contacts identity]))))) + +(register-sub :group-settings + (fn [db [_ k]] + (reaction (get-in @db [:group-settings k])))) + +(defn get-chat-name-validation-messages [chat-name] + (filter some? + (list (when (zero? (count chat-name)) + "Chat name can't be empty") + (when (< max-chat-name-length (count chat-name)) + "Chat name is too long")))) + +(register-sub :new-chat-name-validation-messages + (fn [db [_]] + (let [chat-name (reaction (:new-chat-name @db))] + (reaction (get-chat-name-validation-messages @chat-name))))) + +(register-sub :new-chat-name-valid? + (fn [db [_]] + (let [chat-name (reaction (:new-chat-name @db))] + (reaction (zero? (count (get-chat-name-validation-messages @chat-name))))))) diff --git a/src/status_im/group_settings/views/color_settings.cljs b/src/status_im/group_settings/views/color_settings.cljs new file mode 100644 index 0000000000..7c305f8b87 --- /dev/null +++ b/src/status_im/group_settings/views/color_settings.cljs @@ -0,0 +1,48 @@ +(ns status-im.group-settings.views.color-settings + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [dispatch]] + [status-im.components.react :refer [view + text + icon + touchable-highlight]] + [status-im.i18n :refer [label]] + [status-im.group-settings.styles.color-settings :as st])) + +(defn close-chat-color-picker [] + (dispatch [:group-settings :show-color-picker false])) + +(def all-colors + (->> [:#a187d5 :#7099e6 :#424874 :#96c0b7 + :#d3b99f :#eb6464 :#6d98ba :#c17767] + (partition 4) + (map-indexed vector))) + +(defn color-icon [current-color color] + (let [selected-color? (= (keyword current-color) color)] + [touchable-highlight + {:on-press #(dispatch [:set-chat-color color]) + :style st/color-highlight} + [view st/color-icon-comtainer + [view (st/color-item color) + (when selected-color? + [icon :ok st/icon-ok])]]])) + +(defview color-settings [] + [show-color-picker [:group-settings :show-color-picker] + current-color [:chat :color]] + (when show-color-picker + [view st/color-settings-container + [view st/label-container + [text st/label (label :t/change-color)]] + [touchable-highlight + {:on-press close-chat-color-picker + :style st/close-highlight} + [view st/close-settings-container + [icon :close_gray st/close-icon]]] + [view st/all-colors-container + (for [[idx colors] all-colors] + ^{:key idx} + [view st/color-container + (for [color (take-last 4 colors)] + ^{:key color} + [color-icon current-color color])])]])) diff --git a/src/status_im/group_settings/views/member.cljs b/src/status_im/group_settings/views/member.cljs new file mode 100644 index 0000000000..e2edc020d7 --- /dev/null +++ b/src/status_im/group_settings/views/member.cljs @@ -0,0 +1,12 @@ +(ns status-im.group-settings.views.member + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.contacts.views.contact :refer [contact-view]] + [status-im.i18n :refer [label]])) + +(defn member-view [{:keys [whisper-identity role] :as contact}] + ;; TODO implement :role property for group chat contact + [contact-view + {:contact contact + :extended? true + :info role + :on-click #(dispatch [:set :selected-participants #{whisper-identity}])}]) diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs new file mode 100644 index 0000000000..48337c6507 --- /dev/null +++ b/src/status_im/handlers.cljs @@ -0,0 +1,148 @@ +(ns status-im.handlers + (:require + [re-frame.core :refer [after dispatch dispatch-sync debug]] + [status-im.db :refer [app-db]] + [status-im.data-store.core :as data-store] + [taoensso.timbre :as log] + [status-im.utils.crypt :refer [gen-random-bytes]] + [status-im.components.status :as status] + [status-im.utils.handlers :refer [register-handler] :as u] + status-im.chat.handlers + status-im.group-settings.handlers + status-im.navigation.handlers + status-im.contacts.handlers + status-im.discover.handlers + status-im.new-group.handlers + status-im.participants.handlers + status-im.profile.handlers + status-im.commands.handlers.loading + status-im.commands.handlers.jail + status-im.qr-scanner.handlers + status-im.accounts.handlers + status-im.protocol.handlers + status-im.transactions.handlers + status-im.network.handlers + [status-im.utils.types :as t] + [status-im.constants :refer [console-chat-id]] + [status-im.utils.ethereum-network :as enet])) + +;; -- Common -------------------------------------------------------------- + +(defn set-el [db [_ k v]] + (assoc db k v)) + +(register-handler :set set-el) + +(defn set-in [db [_ path v]] + (assoc-in db path v)) + +(register-handler :set-in set-in) + +(register-handler :set-animation + (fn [db [_ k v]] + (assoc-in db [:animations k] v))) + +(register-handler :initialize-db + (fn [{:keys [status-module-initialized? network-status network]} _] + (data-store/init) + (cond-> (assoc app-db :current-account-id nil + :network-status network-status) + + status-module-initialized? + (assoc :status-module-initialized? true) + + true + (assoc :network (or network :testnet))))) + +(register-handler :initialize-account-db + (fn [db _] + (-> db + (assoc :current-chat-id console-chat-id) + (dissoc :edit-mode + :transactions + :transactions-queue + :new-contact-identity)))) + +(register-handler :initialize-account + (u/side-effect! + (fn [_ [_ address]] + (dispatch [:initialize-account-db]) + (dispatch [:load-processed-messages]) + (dispatch [:initialize-protocol address]) + (dispatch [:initialize-sync-listener]) + (dispatch [:initialize-chats]) + (dispatch [:load-contacts]) + (dispatch [:init-chat]) + (dispatch [:init-discoveries]) + (dispatch [:send-account-update-if-needed]) + (dispatch [:start-requesting-discoveries]) + (dispatch [:remove-old-discoveries!])))) + +(register-handler :reset-app + (u/side-effect! + (fn [_ _] + (dispatch [:initialize-db]) + (dispatch [:load-accounts]) + (dispatch [:init-console-chat]) + (dispatch [:load-default-contacts!]) + (dispatch [:load-commands! console-chat-id]) + (dispatch [:load-commands!])))) + +(def ecc (js/require "eccjs")) + +(register-handler :initialize-crypt + (u/side-effect! + (fn [_ _] + (log/debug "initializing crypt") + (gen-random-bytes 1024 (fn [{:keys [error buffer]}] + (if error + (do + (log/error "Failed to generate random bytes to initialize sjcl crypto") + (dispatch [:notify-user {:type :error + :error error}])) + (do + (->> (.toString buffer "hex") + (.toBits (.. ecc -sjcl -codec -hex)) + (.addEntropy (.. ecc -sjcl -random))) + (dispatch [:crypt-initialized])))))))) + +(defn node-started [db result] + (log/debug "Started Node") + (enet/get-network #(dispatch [:set :network %]))) + +(register-handler :initialize-geth + (u/side-effect! + (fn [db _] + (log/debug "Starting node") + (status/start-node (fn [result] (node-started db result)))))) + +(register-handler :signal-event + (u/side-effect! + (fn [_ [_ event-str]] + (log/debug :event-str event-str) + (let [{:keys [type event]} (t/json->clj event-str)] + (case type + "transaction.queued" (dispatch [:transaction-queued event]) + "transaction.failed" (dispatch [:transaction-failed event]) + "node.started" (log/debug "Event *node.started* received") + "module.initialized" (dispatch [:status-module-initialized!]) + "local_storage.set" (dispatch [:set-local-storage event]) + (log/debug "Event " type " not handled")))))) + +(register-handler :status-module-initialized! + (after (u/side-effect! + (fn [db] + (status/module-initialized!)))) + (fn [db] + (assoc db :status-module-initialized? true))) + +(register-handler :crypt-initialized + (u/side-effect! + (fn [_ _] + (log/debug "crypt initialized")))) + +;; -- User data -------------------------------------------------------------- +(register-handler :load-user-phone-number + (fn [db [_]] + ;; todo fetch phone number from db + (assoc db :user-phone-number "123"))) diff --git a/src/status_im/handlers/server.cljs b/src/status_im/handlers/server.cljs new file mode 100644 index 0000000000..9703da34e6 --- /dev/null +++ b/src/status_im/handlers/server.cljs @@ -0,0 +1,36 @@ +(ns status-im.handlers.server + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.utils.utils :refer [http-post]] + [taoensso.timbre :as log] + [status-im.utils.scheduler :as sch] + [status-im.data-store.messages :as messages])) + +(defn sign-up + [db phone-number message-id handler] + (let [current-account-id (get db :current-account-id) + {:keys [public-key address]} (get-in db [:accounts current-account-id])] + (log/debug "signing up with public-key" public-key "and phone " phone-number) + (http-post "sign-up" {:phone-number phone-number + :whisper-identity public-key + :address address} + (fn [body] + (log/debug body) + (dispatch [:set-message-status message-id :seen]) + (handler)) + (fn [_] + (sch/execute-later + #(dispatch [:sign-up phone-number message-id]) + (sch/s->ms 1)))) + db)) + +(defn sign-up-confirm + [confirmation-code message-id handler] + (http-post "sign-up-confirm" + {:code confirmation-code} + (fn [body] + (dispatch [:set-message-status message-id :seen]) + (handler body)) + (fn [_] + (sch/execute-later + #(dispatch [:sign-up-confirm confirmation-code message-id]) + (sch/s->ms 1))))) diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs new file mode 100644 index 0000000000..5070abe0e1 --- /dev/null +++ b/src/status_im/i18n.cljs @@ -0,0 +1,103 @@ +(ns status-im.i18n + (:require + [status-im.translations.af :as af] + [status-im.translations.ar :as ar] + [status-im.translations.de :as de] + [status-im.translations.de-ch :as de-ch] + [status-im.translations.en :as en] + [status-im.translations.es :as es] + [status-im.translations.es-ar :as es-ar] + [status-im.translations.fr :as fr] + [status-im.translations.fr-ch :as fr-ch] + [status-im.translations.hi :as hi] + [status-im.translations.hu :as hu] + [status-im.translations.it :as it] + [status-im.translations.it-ch :as it-ch] + [status-im.translations.ja :as ja] + [status-im.translations.ko :as ko] + [status-im.translations.nl :as nl] + [status-im.translations.pl :as pl] + [status-im.translations.pt-br :as pt-br] + [status-im.translations.pt-pt :as pt-pt] + [status-im.translations.ro :as ro] + [status-im.translations.ru :as ru] + [status-im.translations.sl :as sl] + [status-im.translations.sv :as sv] + [status-im.translations.sw :as sw] + [status-im.translations.th :as th] + [status-im.translations.tr :as tr] + [status-im.translations.uk :as uk] + [status-im.translations.ur :as ur] + [status-im.translations.vi :as vi] + [status-im.translations.zh-hans :as zh-hans] + [status-im.translations.zh-hant :as zh-hant] + [status-im.translations.zh-wuu :as zh-wuu] + [status-im.translations.zh-yue :as zh-yue] + [status-im.utils.utils :as u] + [status-im.utils.js-resources :refer [default-contacts]])) + +(def i18n (js/require "react-native-i18n")) +(set! (.-fallbacks i18n) true) +(set! (.-defaultSeparator i18n) "/") + +(set! (.-translations i18n) (clj->js {:af af/translations + :ar ar/translations + :de de/translations + :de-ch de-ch/translations + :en en/translations + :es es/translations + :es-ar es-ar/translations + :fr fr/translations + :fr-ch fr-ch/translations + :hi hi/translations + :hu hu/translations + :it it/translations + :it-ch it-ch/translations + :ja ja/translations + :ko ko/translations + :nl nl/translations + :pl pl/translations + :pt-br pt-br/translations + :pt-pt pt-pt/translations + :ro ro/translations + :ru ru/translations + :sl sl/translations + :sv sv/translations + :sw sw/translations + :th th/translations + :tr tr/translations + :uk uk/translations + :ur ur/translations + :vi vi/translations + :zh-hans zh-hans/translations + :zh-hant zh-hant/translations + :zh-wuu zh-wuu/translations + :zh-yue zh-yue/translations})) + +(defn label + ([path] (label path {})) + ([path options] + (if (exists? i18n.t) + (.t i18n (name path) (clj->js options)) + (name path)))) + +(defn label-pluralize [count path & options] + (if (exists? i18n.t) + (.p i18n count (name path) (clj->js options)) + (name path))) + +(defn message-status-label [status] + (->> status + (name) + (str "t/status-") + (keyword) + (label))) + +(def locale + (.-locale i18n)) + +(defn get-contact-translated [contact-id key fallback] + (let [translation #(get-in default-contacts [(keyword contact-id) key (keyword %)])] + (or (translation locale) + (translation (subs locale 0 2)) + fallback))) diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs new file mode 100644 index 0000000000..01888221d6 --- /dev/null +++ b/src/status_im/ios/core.cljs @@ -0,0 +1,121 @@ +(ns status-im.ios.core + (:require [reagent.core :as r :refer [atom]] + [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.handlers] + [status-im.subs] + [status-im.components.react :refer [view + modal + app-registry + keyboard + orientation + splash-screen]] + [status-im.components.main-tabs :refer [main-tabs]] + [status-im.contacts.search-results :refer [contacts-search-results]] + [status-im.contacts.views.contact-list :refer [contact-list]] + [status-im.contacts.views.new-contact :refer [new-contact]] + [status-im.qr-scanner.screen :refer [qr-scanner]] + [status-im.discover.search-results :refer [discover-search-results]] + [status-im.chat.screen :refer [chat]] + [status-im.accounts.login.screen :refer [login]] + [status-im.accounts.recover.screen :refer [recover]] + [status-im.accounts.screen :refer [accounts]] + [status-im.transactions.screen :refer [confirm]] + [status-im.chats-list.screen :refer [chats-list]] + [status-im.new-group.screen :refer [new-group]] + [status-im.participants.views.add :refer [new-participants]] + [status-im.participants.views.remove :refer [remove-participants]] + [status-im.group-settings.screen :refer [group-settings]] + [status-im.profile.screen :refer [profile my-profile]] + [status-im.profile.photo-capture.screen :refer [profile-photo-capture]] + status-im.data-store.core + [taoensso.timbre :as log] + [status-im.chat.styles.screen :as st] + [status-im.accounts.views.qr-code :refer [qr-code-view]] + [status-im.components.status :as status])) + +(defn orientation->keyword [o] + (keyword (.toLowerCase o))) + +(defn validate-current-view + [current-view signed-up?] + (if (or (contains? #{:login :chat :recover :accounts} current-view) + signed-up?) + current-view + :chat)) + +(defn app-root [] + (let [signed-up? (subscribe [:signed-up?]) + modal-view (subscribe [:get :modal]) + view-id (subscribe [:get :view-id]) + account-id (subscribe [:get :current-account-id]) + keyboard-height (subscribe [:get :keyboard-height])] + (log/debug "Current account: " @account-id) + (r/create-class + {:component-will-mount + (fn [] + (let [o (orientation->keyword (.getInitialOrientation orientation))] + (dispatch [:set :orientation o])) + (.addOrientationListener + orientation + #(dispatch [:set :orientation (orientation->keyword %)])) + (.lockToPortrait orientation) + (.addListener keyboard + "keyboardWillShow" + (fn [e] + (let [h (.. e -endCoordinates -height)] + (when-not (= h @keyboard-height) + (dispatch [:set :keyboard-height h]) + (dispatch [:set :keyboard-max-height h]))))) + (.addListener keyboard + "keyboardWillHide" + #(when-not (= 0 @keyboard-height) + (dispatch [:set :keyboard-height 0]))) + (.hide splash-screen)) + :render + (fn [] + (when @view-id + (let [current-view (validate-current-view @view-id @signed-up?)] + (let [component (case current-view + :discover main-tabs + :discover-search-results discover-search-results + :add-participants new-participants + :remove-participants remove-participants + :chat-list main-tabs + :new-group new-group + :group-settings group-settings + :contact-list main-tabs + :contact-list-search-results contacts-search-results + :group-contacts contact-list + :new-contact new-contact + :qr-scanner qr-scanner + :chat chat + :profile profile + :profile-photo-capture profile-photo-capture + :accounts accounts + :login login + :recover recover + :my-profile my-profile)] + [view + {:flex 1} + [component] + (when @modal-view + [view + st/chat-modal + [modal {:animation-type :slide + :transparent false + :on-request-close #(dispatch [:navigate-back])} + (let [component (case @modal-view + :qr-scanner qr-scanner + :qr-code-view qr-code-view + :confirm confirm + :contact-list-modal contact-list)] + [component])]])]))))}))) + +(defn init [] + (status/call-module status/init-jail) + (dispatch-sync [:reset-app]) + (dispatch [:listen-to-network-status!]) + (dispatch [:initialize-crypt]) + (dispatch [:initialize-geth]) + (dispatch [:load-user-phone-number]) + (.registerComponent app-registry "StatusIm" #(r/reactify-component app-root))) diff --git a/src/status_im/ios/platform.cljs b/src/status_im/ios/platform.cljs new file mode 100644 index 0000000000..6c49098f30 --- /dev/null +++ b/src/status_im/ios/platform.cljs @@ -0,0 +1,93 @@ +(ns status-im.ios.platform + (:require [status-im.components.styles :as styles] + [status-im.utils.utils :as u] + [status-im.components.toolbar.styles :refer [toolbar-background2]])) + +(def component-styles + {:status-bar {:default {:height 20 + :bar-style "default" + :color styles/color-white} + :main {:height 20 + :bar-style "default" + :color toolbar-background2} + :transparent {:height 20 + :bar-style "light-content" + :color styles/color-transparent} + :modal {:height 20 + :bar-style "light-content" + :color "#2f3031"}} + :toolbar {:border-bottom-color styles/color-gray3 + :border-bottom-width 0.5} + :sized-text {:margin-top -5 + :additional-height 5} + :actions-list-view {:border-bottom-color styles/color-gray3 + :border-bottom-width 0.5} + :chat {:new-message {:border-top-color styles/color-gray3 + :border-top-width 0.5}} + :discover {:subtitle {:color styles/color-steel + :font-size 13 + :letter-spacing 1} + :popular {:border-radius 3 + :border-width 1 + :border-color "#D7D7D7"} + :tag {:flex-direction "column" + :background-color "rgb(227, 235, 250)" + :border-radius 4 + :border-width 1 + :border-color "rgba(112, 153, 230, 0.31)" + :padding 6} + :item {:status-text {:color styles/color-steel + :font-size 14 + :letter-spacing -0.1} + :icon {:padding-top 0 + :bottom -4 + :justify-content :flex-end}}} + :contacts {:subtitle {:color styles/color-steel + :font-size 13 + :letter-spacing 1}} + :bottom-gradient {:height 1} + :input-label {:left 0} + :input-error-text {:margin-left 0} + :toolbar-nav-action {:width 46 + :height 56 + :align-items :center + :justify-content :center} + :toolbar-last-activity {:color styles/text2-color + :background-color :transparent + :top 0 + :font-size 14}}) + +(def fonts + {:light {:font-family "SFUIText-Light"} + :default {:font-family "SFUIText-Regular"} + :medium {:font-family "SFUIText-Medium"} + :bold {:font-family "SFUIText-Bold"} + + :toolbar-title {:font-family "SFUIText-Medium"}}) + +;; Dialogs + +(def react-native (js/require "react-native")) + +(defn show-action-sheet [{:keys [options callback cancel-text]}] + (.showActionSheetWithOptions (.-ActionSheetIOS react-native) + (clj->js {:options (conj options cancel-text) + :cancelButtonIndex (count options)}) + callback)) + + +;; Structure to be exported + +(def platform-specific + {:component-styles component-styles + :fonts fonts + :list-selection-fn show-action-sheet + :tabs {:tab-shadows? false} + :chats {:action-button? false + :new-chat-in-toolbar? true} + :contacts {:action-button? false + :new-contact-in-toolbar? true + :uppercase-subtitles? true + :group-block-shadows? false} + :discover {:uppercase-subtitles? true}}) + diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs new file mode 100644 index 0000000000..9ac9fab2ee --- /dev/null +++ b/src/status_im/models/commands.cljs @@ -0,0 +1,88 @@ +(ns status-im.models.commands + (:require [status-im.db :as db] + [tailrecursion.priority-map :refer [priority-map-by]])) + +(defn get-commands [{:keys [current-chat-id] :as db}] + (or (get-in db [:chats current-chat-id :commands]) {})) + +(defn get-response-or-command + [type {:keys [current-chat-id] :as db} command-key] + ((or (get-in db [:chats current-chat-id type]) {}) command-key)) + +(defn get-chat-command-content + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-content-path current-chat-id))) + +(defn set-chat-command-content + [{:keys [current-chat-id] :as db} content] + (assoc-in db (db/chat-command-content-path current-chat-id) content)) + +(defn set-command-parameter + [{:keys [current-chat-id] :as db} name value] + (assoc-in db [:chats current-chat-id :command-input :params name] value)) + +(defn get-chat-command + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-path current-chat-id))) + +(defn get-command-input [{:keys [current-chat-id] :as db}] + (get-in db [:chats current-chat-id :command-input])) + +(defn add-params [command params] + (let [command-params (:params command) + command-params (vec (map (fn [param] + (let [param-key (keyword (:name param)) + value (get params param-key)] + (assoc param :value value))) command-params))] + (assoc command :params command-params))) + +(defn set-command-input + ([db type command-key] + (set-command-input db type nil command-key)) + ([db type message-id command-key] + (set-command-input db type message-id command-key nil)) + ([{:keys [current-chat-id] :as db} type message-id command-key params] + (let [command (-> (get-response-or-command type db command-key) + (add-params params)) + first-parameter (get (:params command) 0) + value (:value first-parameter)] + (update-in db [:chats current-chat-id :command-input] merge + {:content value + :command command + :parameter-idx 0 + :params params + :to-message-id message-id})))) + +(defn get-command-parameter-index + ([{:keys [current-chat-id] :as db}] + (get-command-parameter-index db current-chat-id)) + ([db chat-id] + (get-in db [:chats chat-id :command-input :parameter-idx]))) + +(defn next-command-parameter + [{:keys [current-chat-id] :as db}] + (let [parameter-index (get-command-parameter-index db) + command (get-chat-command db) + next-parameter (get (:params command) (inc parameter-index)) + value (:value next-parameter)] + (-> db + (update-in [:chats current-chat-id :command-input :parameter-idx] inc) + (set-chat-command-content value)))) + + +(defn get-chat-command-to-message-id + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-to-message-id-path current-chat-id))) + +(defn get-chat-command-request + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-request-path current-chat-id + (get-chat-command-to-message-id db)))) + +(defn parse-command-message-content [commands content] + (if (map? content) + (update content :command #((keyword %) commands)) + content)) + +(defn parse-command-request [commands content] + (update content :command #((keyword %) commands))) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs new file mode 100644 index 0000000000..92a9b22f79 --- /dev/null +++ b/src/status_im/navigation/handlers.cljs @@ -0,0 +1,98 @@ +(ns status-im.navigation.handlers + (:require [re-frame.core :refer [dispatch debug enrich after]] + [status-im.utils.handlers :refer [register-handler]] + [taoensso.timbre :as log])) + +(defn push-view [db view-id] + (-> db + (update :navigation-stack conj view-id) + (assoc :view-id view-id))) + +(defn replace-top-element [stack view-id] + (let [stack' (if (pos? (count stack)) + (pop stack) + stack)] + (conj stack' view-id))) + +(defn replace-view [db view-id] + (-> db + (update :navigation-stack replace-top-element view-id) + (assoc :view-id view-id))) + +(defmulti preload-data! + (fn [db [_ view-id]] (or view-id (:view-id db)))) + +(defmethod preload-data! :default [db _] db) + +(defn -preload-data! [{:keys [was-modal?] :as db} & args] + (if was-modal? + (dissoc db :was-modal) + (apply preload-data! db args))) + +(register-handler :navigate-forget + (enrich preload-data!) + (fn [db [_ new-view-id]] + (assoc db :view-id new-view-id))) + +(register-handler :navigate-to + (enrich preload-data!) + (fn [{:keys [view-id] :as db} [_ new-view-id]] + (if (= view-id new-view-id) + db + (push-view db new-view-id)))) + +(register-handler :navigate-to-modal + (enrich preload-data!) + (fn [db [_ modal-view]] + (assoc db :modal modal-view))) + +(register-handler :navigation-replace + (enrich preload-data!) + (fn [db [_ view-id]] + (replace-view db view-id))) + +(register-handler :navigate-back + (enrich -preload-data!) + (fn [{:keys [navigation-stack view-id modal] :as db} _] + (cond + modal (assoc db :modal nil + :was-modal? true) + (>= 1 (count navigation-stack)) db + + :else + (let [[previous-view-id :as navigation-stack'] (pop navigation-stack) + first-in-stack (first navigation-stack)] + (if (= view-id first-in-stack) + (-> db + (assoc :view-id previous-view-id) + (assoc :navigation-stack navigation-stack')) + (assoc db :view-id first-in-stack)))))) + +(register-handler :navigate-to-tab + (enrich preload-data!) + (fn [db [_ view-id]] + (-> db + (assoc :prev-tab-view-id (:view-id db)) + (assoc :prev-view-id (:view-id db)) + (push-view view-id)))) + +(register-handler :on-navigated-to-tab + (enrich preload-data!) + (fn [db [_]] + (assoc db :prev-tab-view-id nil))) + +(defn show-profile + [db [_ identity]] + (-> db + (assoc :contact-identity identity) + (push-view :profile))) + +(register-handler :show-profile show-profile) + +(defn navigate-to-clean + [db [_ view-id]] + (-> db + (assoc :navigation-stack (list)) + (push-view view-id))) + +(register-handler :navigate-to-clean navigate-to-clean) diff --git a/src/status_im/network/handlers.cljs b/src/status_im/network/handlers.cljs new file mode 100644 index 0000000000..d43c72a878 --- /dev/null +++ b/src/status_im/network/handlers.cljs @@ -0,0 +1,18 @@ +(ns status-im.network.handlers + (:require [re-frame.core :refer [dispatch debug enrich after]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.utils.handlers :as u] + [status-im.network.net-info :as ni] + [clojure.string :as s])) + +(register-handler :listen-to-network-status! + (u/side-effect! + (fn [] + (let [handler #(dispatch [:update-network-status %])] + (ni/init handler) + (ni/add-listener handler))))) + +(register-handler :update-network-status + (fn [db [_ is-connected?]] + (let [status (if is-connected? :online :offline)] + (assoc db :network-status status)))) diff --git a/src/status_im/network/net_info.cljs b/src/status_im/network/net_info.cljs new file mode 100644 index 0000000000..86bfcd6d0c --- /dev/null +++ b/src/status_im/network/net_info.cljs @@ -0,0 +1,16 @@ +(ns status-im.network.net-info + (:require [status-im.utils.utils :as u] + [taoensso.timbre :as log])) + +(def net-info (u/get-react-property "NetInfo")) + +(defn init [callback] + (when net-info + (.then (.fetch (.-isConnected net-info)) + (fn [is-connected?] + (log/debug "Is connected?" is-connected?) + (callback is-connected?))))) + +(defn add-listener [listener] + (when net-info + (.addEventListener (.-isConnected net-info) "change" listener))) diff --git a/src/status_im/new_group/handlers.cljs b/src/status_im/new_group/handlers.cljs new file mode 100644 index 0000000000..810699dcfa --- /dev/null +++ b/src/status_im/new_group/handlers.cljs @@ -0,0 +1,124 @@ +(ns status-im.new-group.handlers + (:require [status-im.protocol.core :as protocol] + [re-frame.core :refer [after dispatch debug enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.components.styles :refer [default-chat-color]] + [status-im.data-store.chats :as chats] + [clojure.string :as s] + [status-im.utils.handlers :as u] + [status-im.utils.random :as random] + [taoensso.timbre :refer-macros [debug]])) + +(defn deselect-contact + [db [_ id]] + (update db :selected-contacts disj id)) + +(register-handler :deselect-contact deselect-contact) + +(defn select-contact + [db [_ id]] + (update db :selected-contacts conj id)) + +(register-handler :select-contact select-contact) + +(defn group-name-from-contacts + [{:keys [contacts selected-contacts username]}] + (->> (select-keys contacts selected-contacts) + vals + (map :name) + (cons username) + (s/join ", "))) + +(defn prepare-chat + [{:keys [selected-contacts current-public-key] :as db} [_ group-name]] + (let [contacts (mapv #(hash-map :identity %) selected-contacts) + chat-name (if-not (s/blank? group-name) + group-name + (group-name-from-contacts db)) + {:keys [public private]} (protocol/new-keypair!)] + (assoc db :new-chat {:chat-id (random/id) + :public-key public + :private-key private + :name chat-name + :color default-chat-color + :group-chat true + :group-admin current-public-key + :is-active true + :timestamp (.getTime (js/Date.)) + :contacts contacts}))) + +(defn add-chat + [{:keys [new-chat] :as db} _] + (-> db + (assoc-in [:chats (:chat-id new-chat)] new-chat) + (assoc :selected-contacts #{}))) + +(defn create-chat! + [{:keys [new-chat]} _] + (chats/save new-chat)) + +(defn show-chat! + [{:keys [new-chat]} _] + (dispatch [:navigation-replace :chat (:chat-id new-chat)])) + +(defn start-listen-group! + [{:keys [new-chat web3 current-public-key]}] + (let [{:keys [chat-id public-key private-key contacts name]} new-chat + identities (mapv :identity contacts)] + (protocol/invite-to-group! + {:web3 web3 + :group {:id chat-id + :name name + :contacts (conj identities current-public-key) + :admin current-public-key + :keypair {:public public-key + :private private-key}} + :identities identities + :message {:from current-public-key + :message-id (random/id)}}) + (protocol/start-watching-group! + {:web3 web3 + :group-id chat-id + :identity current-public-key + :keypair {:public public-key + :private private-key} + :callback #(dispatch [:incoming-message %1 %2])}))) + +(register-handler :create-new-group + (-> prepare-chat + ((enrich add-chat)) + ((after create-chat!)) + ((after show-chat!)) + ((after start-listen-group!)))) + +(register-handler :group-chat-invite-received + (u/side-effect! + (fn [{:keys [current-public-key web3]} + [_ {:keys [from] + {:keys [group-id group-name contacts keypair timestamp]} :payload}]] + (let [{:keys [private public]} keypair] + (let [contacts' (keep (fn [ident] + (when (not= ident current-public-key) + {:identity ident})) contacts) + chat {:chat-id group-id + :name group-name + :group-chat true + :group-admin from + :public-key public + :private-key private + :contacts contacts' + :added-to-at timestamp + :timestamp timestamp + :is-active true} + + exists? (chats/exists? group-id)] + (when (or (not exists?) (chats/new-update? timestamp group-id)) + (if exists? + (dispatch [:update-chat! chat]) + (dispatch [:add-chat group-id chat])) + (protocol/start-watching-group! + {:web3 web3 + :group-id group-id + :identity current-public-key + :keypair keypair + :callback #(dispatch [:incoming-message %1 %2])}))))))) diff --git a/src/status_im/new_group/screen.cljs b/src/status_im/new_group/screen.cljs new file mode 100644 index 0000000000..29a537df75 --- /dev/null +++ b/src/status_im/new_group/screen.cljs @@ -0,0 +1,73 @@ +(ns status-im.new-group.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.resources :as res] + [status-im.components.react :refer [view + text + image + icon + touchable-highlight + list-view + list-item]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.styles :refer [color-purple]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.new-group.views.contact :refer [new-group-contact]] + [status-im.new-group.styles :as st] + [status-im.new-group.validations :as v] + [status-im.i18n :refer [label]] + [cljs.spec :as s])) + +(defview new-group-toolbar [] + [new-chat-name [:get :new-chat-name]] + (let [create-btn-enabled? (s/valid? ::v/name new-chat-name)] + [view + [status-bar] + [toolbar + {:title (label :t/new-group-chat) + :actions [{:image {:source res/v ;; {:uri "icon_search"} + :style (st/toolbar-icon create-btn-enabled?)} + :handler (when create-btn-enabled? + #(dispatch [:create-new-group new-chat-name]))}]}]])) + +(defview group-name-input [] + [new-chat-name [:get :new-chat-name]] + [view + [text-field + {:error (cond + (not (s/valid? ::v/not-empty-string new-chat-name)) + (label :t/empty-group-chat-name) + (not (s/valid? ::v/not-illegal-name new-chat-name)) + (label :t/illegal-group-chat-name)) + :wrapper-style st/group-chat-name-wrapper + :error-color "#7099e6" + :line-color "#0000001f" + :label-hidden? true + :input-style st/group-chat-name-input + :auto-focus true + :on-change-text #(dispatch [:set :new-chat-name %]) + :value new-chat-name}]]) + +(defview new-group [] + [contacts [:all-added-contacts]] + [view st/new-group-container + [new-group-toolbar] + [view st/chat-name-container + [text {:style st/members-text + :font :medium} + (label :t/group-chat-name)] + [group-name-input] + [text {:style st/members-text + :font :medium} + (label :t/members-title)] + #_[touchable-highlight {:on-press (fn [])} + [view st/add-container + [icon :add_gray st/add-icon] + [text {:style st/add-text} (label :t/add-members)]]] + [list-view + {:dataSource (to-datasource contacts) + :renderRow (fn [row _ _] + (list-item [new-group-contact row])) + :style st/contacts-list}]]]) diff --git a/src/status_im/new_group/styles.cljs b/src/status_im/new_group/styles.cljs new file mode 100644 index 0000000000..e18311b155 --- /dev/null +++ b/src/status_im/new_group/styles.cljs @@ -0,0 +1,65 @@ +(ns status-im.new-group.styles + (:require [status-im.components.styles :refer [color-white + color-blue + text1-color + text2-color]])) + +(defn toolbar-icon [enabled?] + {:width 20 + :height 18 + :opacity (if enabled? 1 0.3)}) + +(def new-group-container + {:flex 1 + :flex-direction :column + :background-color color-white}) + +(def chat-name-container + {:margin-left 16}) + +(def group-chat-name-input + {:font-size 14 + :color text1-color}) + +(def group-chat-name-wrapper + {:padding-top 0}) + +(def members-text + {:margin-top 24 + :margin-bottom 8 + :color text2-color + :font-size 14 + :line-height 20}) + +(def add-container + {:flex-direction :row + :margin-bottom 16}) + +(def add-icon + {:margin-vertical 18 + :margin-horizontal 3 + :width 17 + :height 17}) + +(def add-text + {:margin-top 16 + :margin-left 32 + :color text2-color + :font-size 14 + :line-height 20}) + +(def contacts-list + {:background-color :white}) + +(def contact-container + {:flex-direction :row + :justify-content :center + :align-items :center + :height 56}) + +(def contact-item-checkbox + {:outer-size 20 + :filter-size 16 + :inner-size 12 + :outer-color color-blue + :inner-color color-blue}) diff --git a/src/status_im/new_group/subs.cljs b/src/status_im/new_group/subs.cljs new file mode 100644 index 0000000000..6753a5eacb --- /dev/null +++ b/src/status_im/new_group/subs.cljs @@ -0,0 +1,7 @@ +(ns status-im.new-group.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [status-im.utils.subs :as u])) + +(register-sub :is-contact-selected? + (u/contains-sub :selected-contacts)) diff --git a/src/status_im/new_group/validations.cljs b/src/status_im/new_group/validations.cljs new file mode 100644 index 0000000000..304dfbb7f8 --- /dev/null +++ b/src/status_im/new_group/validations.cljs @@ -0,0 +1,17 @@ +(ns status-im.new-group.validations + (:require [cljs.spec :as s] + [status-im.utils.phone-number :refer [valid-mobile-number?]] + [status-im.constants :refer [console-chat-id wallet-chat-id]] + [clojure.string :as str] + [status-im.utils.homoglyph :as h])) + +(defn not-illegal-name? [username] + (let [username (some-> username (str/trim))] + (and (not (h/matches username console-chat-id)) + (not (h/matches username wallet-chat-id))))) + +(s/def ::not-empty-string (s/and string? not-empty)) +(s/def ::not-illegal-name not-illegal-name?) + +(s/def ::name (s/and ::not-empty-string + ::not-illegal-name)) diff --git a/src/status_im/new_group/views/contact.cljs b/src/status_im/new_group/views/contact.cljs new file mode 100644 index 0000000000..be080bda05 --- /dev/null +++ b/src/status_im/new_group/views/contact.cljs @@ -0,0 +1,20 @@ +(ns status-im.new-group.views.contact + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view]] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]] + [status-im.components.item-checkbox :refer [item-checkbox]] + [status-im.new-group.styles :as st])) + +(defn on-toggle [whisper-identity] + (fn [checked?] + (let [action (if checked? :select-contact :deselect-contact)] + (dispatch [action whisper-identity])))) + +(defview new-group-contact [{:keys [whisper-identity] :as contact}] + [checked [:is-contact-selected? whisper-identity]] + [view st/contact-container + [item-checkbox (merge {:on-toggle (on-toggle whisper-identity) + :checked checked} + st/contact-item-checkbox)] + [contact-inner-view contact]]) diff --git a/src/status_im/participants/handlers.cljs b/src/status_im/participants/handlers.cljs new file mode 100644 index 0000000000..a6fb532355 --- /dev/null +++ b/src/status_im/participants/handlers.cljs @@ -0,0 +1,24 @@ +(ns status-im.participants.handlers + (:require [status-im.navigation.handlers :as nav] + [re-frame.core :refer [debug]] + [status-im.utils.handlers :refer [register-handler]])) + +(defmethod nav/preload-data! :add-participants + [db _] + (assoc db :selected-participants #{})) + +(defmethod nav/preload-data! :remove-participants + [db _] + (assoc db :selected-participants #{})) + +(defn deselect-participant + [db [_ id]] + (update db :selected-participants disj id)) + +(register-handler :deselect-participant deselect-participant) + +(defn select-participant + [db [_ id]] + (update db :selected-participants conj id)) + +(register-handler :select-participant (debug select-participant)) diff --git a/src/status_im/participants/styles.cljs b/src/status_im/participants/styles.cljs new file mode 100644 index 0000000000..20436fb19a --- /dev/null +++ b/src/status_im/participants/styles.cljs @@ -0,0 +1,24 @@ +(ns status-im.participants.styles) + +(def participants-container + {:flex 1 + :backgroundColor :white}) + +(def participants-list + {:backgroundColor :white}) + +(def new-participant-image + {:width 20 + :height 18}) + +(def remove-participants-image + {:width 22 + :height 30}) + +(def participant-container + {:flexDirection :row + :marginTop 5 + :marginBottom 5 + :paddingLeft 15 + :paddingRight 15 + :height 75}) diff --git a/src/status_im/participants/subs.cljs b/src/status_im/participants/subs.cljs new file mode 100644 index 0000000000..d351667a8a --- /dev/null +++ b/src/status_im/participants/subs.cljs @@ -0,0 +1,7 @@ +(ns status-im.participants.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [status-im.utils.subs :as u])) + +(register-sub :is-participant-selected? + (u/contains-sub :selected-participants)) diff --git a/src/status_im/participants/views/add.cljs b/src/status_im/participants/views/add.cljs new file mode 100644 index 0000000000..2ed209da3e --- /dev/null +++ b/src/status_im/participants/views/add.cljs @@ -0,0 +1,35 @@ +(ns status-im.participants.views.add + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.resources :as res] + [status-im.components.react :refer [view list-view list-item]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.participants.views.contact :refer [participant-contact]] + [reagent.core :as r] + [status-im.participants.styles :as st] + [status-im.i18n :refer [label]] + [status-im.components.styles :as cst])) + +(defn new-participants-toolbar [] + [view + [status-bar] + [toolbar + {:title (label :t/add-participants) + :actions [{:image {:source res/v ;; {:uri "icon_search"} + :style st/new-participant-image} + :handler #(do (dispatch [:add-new-participants]) + (dispatch [:navigate-back]))}]}]]) + +(defn new-participants-row + [row _ _] + (list-item [participant-contact row])) + +(defview new-participants [] + [contacts [:all-new-contacts]] + [view st/participants-container + [new-participants-toolbar] + [list-view {:dataSource (to-datasource contacts) + :renderRow new-participants-row + :style st/participants-list}]]) diff --git a/src/status_im/participants/views/contact.cljs b/src/status_im/participants/views/contact.cljs new file mode 100644 index 0000000000..0311510e9b --- /dev/null +++ b/src/status_im/participants/views/contact.cljs @@ -0,0 +1,23 @@ +(ns status-im.participants.views.contact + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view]] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]] + [status-im.components.item-checkbox :refer [item-checkbox]] + [reagent.core :as r] + [status-im.participants.styles :as st])) + +;; todo duplication +(defn on-toggle [whisper-identity] + (fn [checked?] + (let [action (if checked? :select-participant :deselect-participant)] + (dispatch [action whisper-identity])))) + +(defview participant-contact + [{:keys [whisper-identity] :as contact}] + [checked [:is-participant-selected? whisper-identity]] + [view st/participant-container + [item-checkbox {:onToggle (on-toggle whisper-identity) + :checked checked + :size 30}] + [contact-inner-view contact]]) diff --git a/src/status_im/participants/views/remove.cljs b/src/status_im/participants/views/remove.cljs new file mode 100644 index 0000000000..cd03d93715 --- /dev/null +++ b/src/status_im/participants/views/remove.cljs @@ -0,0 +1,41 @@ +(ns status-im.participants.views.remove + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.resources :as res] + [status-im.components.react :refer [view + text + image + touchable-highlight + list-view + list-item]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.participants.views.contact + :refer [participant-contact]] + [reagent.core :as r] + [status-im.participants.styles :as st] + [status-im.i18n :refer [label]] + [status-im.components.styles :as cst])) + +(defn remove-participants-toolbar [] + [view + [status-bar] + [toolbar + {:title (label :t/remove-participants) + :actions [{:handler #(do (dispatch [:remove-participants]) + (dispatch [:navigate-back])) + :image {:source res/trash-icon ;; {:uri "icon_search"} + :style st/remove-participants-image}}]}]]) + +(defn remove-participants-row + [row _ _] + (r/as-element [participant-contact row])) + +(defview remove-participants [] + [contacts [:current-chat-contacts]] + [view st/participants-container + [remove-participants-toolbar] + [list-view {:dataSource (to-datasource contacts) + :renderRow remove-participants-row + :style st/participants-list}]]) diff --git a/src/status_im/profile/handlers.cljs b/src/status_im/profile/handlers.cljs new file mode 100644 index 0000000000..a52180213d --- /dev/null +++ b/src/status_im/profile/handlers.cljs @@ -0,0 +1,54 @@ +(ns status-im.profile.handlers + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.components.react :refer [show-image-picker + copy-to-clipboard]] + [status-im.components.share :refer [open]] + [status-im.utils.image-processing :refer [img->base64]] + [status-im.i18n :refer [label]] + [status-im.utils.handlers :as u :refer [get-hashtags]] + [clojure.string :as str] + [status-im.profile.validations :as v] + [cljs.spec :as s] + [taoensso.timbre :as log])) + +(defn message-user [identity] + (when identity + (dispatch [:navigate-to :chat identity]))) + +(register-handler :open-image-picker + (u/side-effect! + (fn [_ _] + (show-image-picker + (fn [image] + (let [path (get (js->clj image) "path") + _ (log/debug path) + on-success (fn [base64] + (dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)])) + on-error (fn [type error] + (.log js/console type error))] + (img->base64 path on-success on-error))))))) + +(register-handler :open-image-source-selector + (u/side-effect! + (fn [_ [_ list-selection-fn]] + (list-selection-fn {:title (label :t/image-source-title) + :options [(label :t/image-source-make-photo) (label :t/image-source-gallery)] + :callback (fn [index] + (case index + 0 (dispatch [:navigate-to :profile-photo-capture]) + 1 (dispatch [:open-image-picker]) + :default)) + :cancel-text (label :t/image-source-cancel)})))) + +(register-handler :open-sharing + (u/side-effect! + (fn [_ [_ list-selection-fn text dialog-title]] + (list-selection-fn {:title dialog-title + :options [(label :t/sharing-copy-to-clipboard) (label :t/sharing-share)] + :callback (fn [index] + (case index + 0 (copy-to-clipboard text) + 1 (open {:message text}) + :default)) + :cancel-text (label :t/sharing-cancel)})))) diff --git a/src/status_im/profile/photo_capture/screen.cljs b/src/status_im/profile/photo_capture/screen.cljs new file mode 100644 index 0000000000..0c74dc8592 --- /dev/null +++ b/src/status_im/profile/photo_capture/screen.cljs @@ -0,0 +1,56 @@ +(ns status-im.profile.photo-capture.screen + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [clojure.walk :refer [keywordize-keys]] + [status-im.components.react :refer [view + text + image + touchable-highlight]] + [status-im.components.camera :refer [camera + aspects + capture-targets]] + [status-im.components.styles :refer [icon-back]] + [status-im.components.icons.custom-icons :refer [ion-icon]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-background1]] + [status-im.utils.image-processing :refer [img->base64]] + [status-im.profile.photo-capture.styles :as st] + [status-im.i18n :refer [label]] + [reagent.core :as r] + [taoensso.timbre :as log])) + +(defn image-captured [data] + (let [path (.-path data) + _ (log/debug "Captured image: " path) + on-success (fn [base64] + (log/debug "Captured success: " base64) + (dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)]) + (dispatch [:navigate-back])) + on-error (fn [type error] + (log/debug type error))] + (img->base64 path on-success on-error))) + +(defn profile-photo-capture [] + (let [camera-ref (r/atom nil)] + [view st/container + [status-bar] + [toolbar {:title (label :t/image-source-title) + :nav-action (act/back #(dispatch [:navigate-back])) + :background-color toolbar-background1}] + [camera {:style {:flex 1} + :aspect (:fill aspects) + :captureTarget (:disk capture-targets) + :type "front" + :ref #(reset! camera-ref %)}] + [view {:style {:padding 10 + :background-color toolbar-background1}} + [touchable-highlight {:style {:align-self "center"} + :on-press (fn [] + (let [camera @camera-ref] + (-> (.capture camera) + (.then image-captured) + (.catch #(log/debug "Error capturing image: " %)))))} + [view + [ion-icon {:name :md-camera + :style {:font-size 36}}]]]]])) diff --git a/src/status_im/profile/photo_capture/styles.cljs b/src/status_im/profile/photo_capture/styles.cljs new file mode 100644 index 0000000000..efd151170c --- /dev/null +++ b/src/status_im/profile/photo_capture/styles.cljs @@ -0,0 +1,5 @@ +(ns status-im.profile.photo-capture.styles) + +(def container + {:flex 1 + :background-color :white}) \ No newline at end of file diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs new file mode 100644 index 0000000000..b1e208635b --- /dev/null +++ b/src/status_im/profile/screen.cljs @@ -0,0 +1,242 @@ +(ns status-im.profile.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [clojure.string :as str] + [cljs.spec :as s] + [reagent.core :as r] + [status-im.components.react :refer [view + text + text-input + image + icon + modal + scroll-view + touchable-highlight + touchable-opacity + touchable-without-feedback + show-image-picker + dismiss-keyboard!]] + [status-im.components.icons.custom-icons :refer [oct-icon]] + [status-im.components.chat-icon.screen :refer [my-profile-icon]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.components.selectable-field.view :refer [selectable-field]] + [status-im.components.status-view.view :refer [status-view]] + [status-im.components.share :as share] + [status-im.utils.phone-number :refer [format-phone-number]] + [status-im.utils.image-processing :refer [img->base64]] + [status-im.utils.platform :refer [platform-specific]] + [status-im.profile.handlers :refer [message-user]] + [status-im.profile.validations :as v] + [status-im.profile.styles :as st] + [status-im.utils.random :refer [id]] + [status-im.utils.utils :refer [clean-text]] + [status-im.components.image-button.view :refer [show-qr-button]] + [status-im.i18n :refer [label + get-contact-translated]])) + +(defn share [text dialog-title] + (let [list-selection-fn (:list-selection-fn platform-specific)] + (dispatch [:open-sharing list-selection-fn text dialog-title]))) + +(defn toolbar [{:keys [account edit?]}] + (let [profile-edit-data-valid? (s/valid? ::v/profile account)] + [view + [touchable-highlight {:style st/back-btn-touchable + :on-press (fn [] + (dispatch [:set-in [:profile-edit :edit?] false]) + (dispatch [:navigate-back]))} + [view st/back-btn-container + [icon :back st/back-btn-icon]]] + [touchable-highlight {:style st/actions-btn-touchable + :on-press (fn [] + (if edit? + (when profile-edit-data-valid? + (dismiss-keyboard!) + (dispatch [:check-status-change (:status account)]) + (dispatch [:account-update account]) + (dispatch [:set-in [:profile-edit :edit?] false])) + (dispatch [:set :profile-edit (merge account {:edit? true})])))} + [view st/actions-btn-container + (if edit? + [oct-icon {:name :check + :style (st/ok-btn-icon profile-edit-data-valid?)}] + [icon :dots st/edit-btn-icon])]]])) + +(defn status-image-view [_] + (let [component (r/current-component) + just-opened? (r/atom true) + input-ref (r/atom nil) + set-status-height #(let [height (-> (.-nativeEvent %) + (.-contentSize) + (.-height))] + (r/set-state component {:height height}))] + (r/create-class + {:reagent-render + (fn [{{:keys [whisper-identity + name + status + photo-path]} :account + edit? :edit?}] + [view st/status-block + [view st/user-photo-container + + (if edit? + [touchable-highlight {:on-press (fn [] + (let [list-selection-fn (:list-selection-fn platform-specific)] + (dispatch [:open-image-source-selector list-selection-fn])))} + [view + [my-profile-icon {:account {:photo-path photo-path + :name name} + :edit? edit?}]]] + [my-profile-icon {:account {:photo-path photo-path + :name name} + :edit? edit?}])] + [text-field + {:line-color :white + :focus-line-color :white + :editable edit? + :input-style (st/username-input edit? (s/valid? ::v/name name)) + :wrapper-style st/username-wrapper + :value (get-contact-translated whisper-identity :name name) + :on-change-text #(dispatch [:set-in [:profile-edit :name] %])}] + (if (or edit? @just-opened?) + [text-input {:ref #(reset! input-ref %) + :style (st/status-input (:height (r/state component))) + :multiline true + :editable true + :on-content-size-change #(do (set-status-height %) + (reset! just-opened? false)) + :max-length 140 + :placeholder (label :t/profile-no-status) + :on-change-text #(let [status (clean-text %)] + (if (str/includes? % "\n") + (.blur @input-ref) + (dispatch [:set-in [:profile-edit :status] status]))) + :default-value status}] + [status-view {:style (st/status-text (:height (r/state component))) + :status status}])])}))) + +(defview profile [] + [{whisper-identity :whisper-identity + address :address + username :name + photo-path :photo-path + phone :phone + status :status + :as contact} [:contact]] + [scroll-view {:style st/profile} + [status-bar] + [view + [touchable-highlight {:style st/back-btn-touchable + :on-press (fn [] + (dispatch [:navigate-back]))} + [view (get-in platform-specific [:component-styles :toolbar-nav-action]) + [icon :back st/back-btn-icon]]] + ;; TODO not implemented + #_[touchable-highlight {:style st/actions-btn-touchable + :on-press (fn [] + (.log js/console "Dots pressed!"))} + [view st/actions-btn-container + [icon :dots st/edit-btn-icon]]]] + + [status-image-view {:account contact + :photo-path photo-path + :edit? false}] + + [scroll-view (merge st/profile-properties-container {:keyboardShouldPersistTaps true + :bounces false}) + + [view st/status-block + [view st/btns-container + [touchable-highlight {:onPress #(message-user whisper-identity)} + [view st/message-btn + [text {:style st/message-btn-text} (label :t/message)]]] + ;; TODO not implemented + #_[touchable-highlight {:onPress #(.log js/console "Not yet implemented")} + [view st/more-btn + [icon :more_vertical_blue st/more-btn-image]]]]] + + [view st/profile-property-with-top-spacing + [selectable-field {:label (label :t/phone-number) + :editable? false + :value (if (and phone (not (str/blank? phone))) + (format-phone-number phone) + (label :t/not-specified))}] + [view st/underline-container]] + + (when address + [view st/profile-property + [view st/profile-property-row + [view st/profile-property-field + [selectable-field {:label (label :t/address) + :editable? false + :value address + :on-press #(share address (label :t/address))}]] + [show-qr-button {:handler #(dispatch [:navigate-to-modal :qr-code-view {:contact contact + :qr-source :whisper-identity}])}]] + [view st/underline-container]]) + + [view st/profile-property + [view st/profile-property-row + [view st/profile-property-field + [selectable-field {:label (label :t/public-key) + :editable? false + :value whisper-identity + :on-press #(share whisper-identity (label :t/public-key))}]] + [show-qr-button {:handler #(dispatch [:navigate-to-modal :qr-code-view {:contact contact + :qr-source :public-key}])}]]] + + [view st/underline-container]]]) + +(defview my-profile [] + [edit? [:get-in [:profile-edit :edit?]] + qr [:get-in [:profile-edit :qr-code]] + current-account [:get-current-account] + changed-account [:get :profile-edit]] + (let [{:keys [phone + address + public-key] + :as account} (if edit? + changed-account + current-account)] + [scroll-view {:style st/profile + :bounces false} + [status-bar] + [toolbar {:account account + :edit? edit?}] + + [status-image-view {:account account + :edit? edit?}] + + [scroll-view (merge st/my-profile-properties-container {:bounces false}) + [view st/profile-property + [selectable-field {:label (label :t/phone-number) + :editable? edit? + :value (if (and phone (not (str/blank? phone))) + (format-phone-number phone) + (label :t/not-specified))}] + [view st/underline-container]] + + [view st/profile-property + [view st/profile-property-row + [view st/profile-property-field + [selectable-field {:label (label :t/address) + :editable? edit? + :value address + :on-press #(share address (label :t/address))}]] + [show-qr-button {:handler #(dispatch [:navigate-to-modal :qr-code-view {:contact account + :qr-source :address}])}]] + [view st/underline-container]] + + [view st/profile-property + [view st/profile-property-row + [view st/profile-property-field + [selectable-field {:label (label :t/public-key) + :editable? edit? + :value public-key + :on-press #(share public-key (label :t/public-key))}]] + [show-qr-button {:handler #(dispatch [:navigate-to-modal :qr-code-view {:contact account + :qr-source :public-key}])}]]] + + [view st/underline-container]]])) diff --git a/src/status_im/profile/styles.cljs b/src/status_im/profile/styles.cljs new file mode 100644 index 0000000000..545e99022b --- /dev/null +++ b/src/status_im/profile/styles.cljs @@ -0,0 +1,171 @@ +(ns status-im.profile.styles + (:require [status-im.components.styles :refer [color-light-blue-transparent + color-white + color-gray + color-black + color-blue + color-blue-transparent + selected-message-color + online-color + separator-color + text1-color + text1-disabled-color + text2-color + color-red]] + [status-im.utils.platform :as p])) + +(def profile + {:flex 1 + :background-color color-white + :flex-direction :column}) + +(def back-btn-touchable + {:position :absolute}) + +(def back-btn-container + {:width 46 + :height 56 + :align-items :center + :justify-content :center}) + +(def back-btn-icon + {:width 8 + :height 14}) + +(def actions-btn-touchable + {:position :absolute + :right 0}) + +(def actions-btn-container + {:width 56 + :height 56 + :align-items :center + :justify-content :center}) + +(def edit-btn-icon + {:width 4 + :height 16}) + +(defn ok-btn-icon [enabled?] + {:font-size 22 + :color (if enabled? color-black color-gray)}) + +(def user-photo-container + {:margin-top 22}) + +(def username-wrapper + {:width 300 + :margin-top (if p/ios? -18 -22) + :margin-bottom -16}) + +(defn username-input [edit? valid?] + {:font-size 18 + :text-align :center + :color (if edit? + (if valid? text1-color color-red) + text1-disabled-color)}) + +(def status-block + {:flex-direction "column" + :align-items "center" + :justifyContent "center" + :margin-left 55 + :margin-right 55}) + +(defn status-view [height] + {:align-self "stretch" + :font-size 14 + :min-height height + :text-align "center" + :color text2-color}) + +(defn status-input [height] + (merge (status-view height) + {:margin-left (if p/ios? 21 16) + :margin-right 16 + :margin-top (if p/ios? 6 1)})) + +(defn status-text [height] + (merge (status-view (- height (if p/ios? 5 10))) + {:margin-left 18 + :margin-right 18 + :margin-top 11 + :margin-bottom 0})) + +(def btns-container + {:margin-top 0 + :flex-direction :row}) + +(def message-btn + {:height 40 + :justify-content :center + :background-color color-blue + :padding-left 25 + :padding-right 25 + :border-radius 20}) + +(def message-btn-text + {:margin-top -2.5 + :font-size 14 + :color color-white}) + +(def more-btn + {:margin-left 10 + :width 40 + :height 40 + :align-items :center + :justify-content :center + :background-color color-blue-transparent + :padding 8 + :border-radius 20}) + +(def more-btn-image + {:width 4 + :height 16}) + +(def profile-properties-container + {:align-items :stretch + :flex-firection :column + :margin-top 16}) + +(def my-profile-properties-container + {:align-items :stretch + :flex-firection :column + :margin-top 32}) + +(def profile-property + {:margin-left 16}) + +(def profile-property-with-top-spacing + {:margin-top 32 + :margin-left 16}) + +(def profile-property-row + {:flex 1 + :flex-direction :row}) + +(def profile-property-field + {:margin-right 96 + :flex 1}) + +(def report-user-text + {:font-size 14 + :line-height 21 + :color text2-color + ;; IOS: + :letter-spacing 0.5}) + +(def qr-code + {:width 250 + :height 250 + :background-color "white" + :border-radius 4 + :align-items :center + :padding-top 15 + :elevation 4}) + +(def underline-container + {:background-color "#0000001f" + :margin-bottom 18 + :height 1 + :align-items :center}) diff --git a/src/status_im/profile/validations.cljs b/src/status_im/profile/validations.cljs new file mode 100644 index 0000000000..ea1542b14d --- /dev/null +++ b/src/status_im/profile/validations.cljs @@ -0,0 +1,20 @@ +(ns status-im.profile.validations + (:require [cljs.spec :as s] + [status-im.constants :refer [console-chat-id wallet-chat-id]] + [clojure.string :as str] + [status-im.utils.homoglyph :as h])) + +(defn correct-name? [username] + (let [username (some-> username (str/trim))] + (and (not (h/matches username console-chat-id)) + (not (h/matches username wallet-chat-id))))) + +(defn correct-email? [email] + (let [pattern #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"] + (or (str/blank? email) + (and (string? email) (re-matches pattern email))))) + +(s/def ::name correct-name?) +(s/def ::email correct-email?) + +(s/def ::profile (s/keys :req-un [::name])) diff --git a/src/status_im/protocol/ack.cljs b/src/status_im/protocol/ack.cljs new file mode 100644 index 0000000000..c77320c448 --- /dev/null +++ b/src/status_im/protocol/ack.cljs @@ -0,0 +1,24 @@ +(ns status-im.protocol.ack + (:require [status-im.protocol.web3.delivery :as d] + [status-im.protocol.web3.filtering :as f] + [status-im.utils.random :as random])) + +(defn check-ack! + [web3 + from + {:keys [type requires-ack? message-id ack? group-id ack-of-message]} + identity] + (when (and requires-ack? (not ack?)) + (let [message {:from identity + :to from + :message-id (random/id) + :topics [f/status-topic] + :type type + :ack? true + :payload {:type type + :ack? true + :ack-of-message message-id + :group-id group-id}}] + (d/add-pending-message! web3 message))) + (when ack? + (d/remove-pending-message! web3 ack-of-message from))) diff --git a/src/status_im/protocol/chat.cljs b/src/status_im/protocol/chat.cljs new file mode 100644 index 0000000000..a126becf5e --- /dev/null +++ b/src/status_im/protocol/chat.cljs @@ -0,0 +1,65 @@ +(ns status-im.protocol.chat + (:require [cljs.spec :as s] + [status-im.protocol.message :as m] + [status-im.protocol.web3.filtering :as f] + [status-im.protocol.web3.delivery :as d] + [taoensso.timbre :refer-macros [debug]] + [status-im.protocol.validation :refer-macros [valid?]])) + +(def message-defaults + {:topics [f/status-topic]}) + +(s/def ::timestamp int?) +(s/def ::user-message + (s/merge + :protocol/message + (s/keys :req-un [:message/to :chat-message/payload]))) + +(defn send! + [{:keys [web3 message]}] + {:pre [(valid? ::user-message message)]} + (let [message' (merge message-defaults + (assoc message + :type :message + :requires-ack? true))] + (debug :send-user-message message') + (d/add-pending-message! web3 message'))) + +(s/def ::seen-message + (s/merge :protocol/message (s/keys :req-un [:message/to]))) + +(defn send-seen! + [{:keys [web3 message]}] + {:pre [(valid? ::seen-message message)]} + (debug :send-seen message) + (d/add-pending-message! + web3 + (merge message-defaults + (-> message + (assoc + :type :seen + :requires-ack? false) + (assoc-in [:payload :group-id] (:group-id message)) + (dissoc :group-id))))) + +(defn send-clock-value-request! + [{:keys [web3 message]}] + (debug :send-clock-value-request message) + (d/add-pending-message! + web3 + (merge message-defaults + (-> message + (assoc + :type :clock-value-request + :requires-ack? false))))) + +(defn send-clock-value! + [{:keys [web3 message]}] + (debug :send-clock-value message) + (d/add-pending-message! + web3 + (merge message-defaults + (-> message + (assoc + :type :clock-value + :requires-ack? false))))) diff --git a/src/status_im/protocol/core.cljs b/src/status_im/protocol/core.cljs new file mode 100644 index 0000000000..438affbddf --- /dev/null +++ b/src/status_im/protocol/core.cljs @@ -0,0 +1,109 @@ +(ns status-im.protocol.core + (:require status-im.protocol.message + [status-im.protocol.web3.utils :as u] + [status-im.protocol.web3.filtering :as f] + [status-im.protocol.web3.delivery :as d] + [taoensso.timbre :refer-macros [debug]] + [status-im.protocol.validation :refer-macros [valid?]] + [status-im.protocol.web3.utils :as u] + [status-im.protocol.chat :as chat] + [status-im.protocol.group :as group] + [status-im.protocol.listeners :as l] + [status-im.protocol.encryption :as e] + [status-im.protocol.discoveries :as discoveries] + [cljs.spec :as s] + [status-im.utils.random :as random])) + +;; user +(def send-message! chat/send!) +(def send-seen! chat/send-seen!) +(def send-clock-value-request! chat/send-clock-value-request!) +(def send-clock-value! chat/send-clock-value!) +(def reset-pending-messages! d/reset-pending-messages!) + +;; group +(def start-watching-group! group/start-watching-group!) +(def stop-watching-group! group/stop-watching-group!) +(def send-group-message! group/send!) +(def invite-to-group! group/invite!) +(def update-group! group/update-group!) +(def remove-from-group! group/remove-identity!) +(def add-to-group! group/add-identity!) +(def leave-group-chat! group/leave!) + +;; encryption +;; todo move somewhere, encryption functions shouldn't be there +(def new-keypair! e/new-keypair!) + +;; discoveries +(def watch-user! discoveries/watch-user!) +(def stop-watching-user! discoveries/stop-watching-user!) +(def contact-request! discoveries/contact-request!) +(def broadcast-profile! discoveries/broadcast-profile!) +(def send-status! discoveries/send-status!) +(def send-discoveries-request! discoveries/send-discoveries-request!) +(def send-discoveries-response! discoveries/send-discoveries-response!) + +(def message-pending? d/message-pending?) + +;; initialization +(s/def ::rpc-url string?) +(s/def ::identity string?) +(s/def :message/chat-id string?) +(s/def ::group (s/keys :req-un [:message/chat-id :message/keypair])) +(s/def ::groups (s/* ::group)) +(s/def ::callback fn?) +(s/def ::contact (s/keys :req-un [::identity :message/keypair])) +(s/def ::contacts (s/* ::contact)) +(s/def ::profile-keypair :message/keypair) +(s/def ::options + (s/merge + (s/keys :req-un [::rpc-url ::identity ::groups ::profile-keypair + ::callback :discoveries/hashtags ::contacts]) + ::d/delivery-options)) + +(def stop-watching-all! f/remove-all-filters!) +(def reset-all-pending-messages! d/reset-all-pending-messages!) + +(defn init-whisper! + [{:keys [rpc-url identity groups callback + contacts profile-keypair pending-messages] + :as options}] + {:pre [(valid? ::options options)]} + (debug :init-whisper) + (stop-watching-all!) + (d/reset-all-pending-messages!) + (let [web3 (u/make-web3 rpc-url) + listener-options {:web3 web3 + :identity identity}] + ;; start listening to groups + (doseq [{:keys [chat-id keypair]} groups] + (f/add-filter! + web3 + {:topics [chat-id]} + (l/message-listener (assoc listener-options :callback callback + :keypair keypair)))) + ;; start listening to user's inbox + (f/add-filter! + web3 + {:to identity + :topics [f/status-topic]} + (l/message-listener (assoc listener-options :callback callback))) + ;; start listening to profiles + (doseq [{:keys [identity keypair]} contacts] + (watch-user! {:web3 web3 + :identity identity + :keypair keypair + :callback callback})) + (d/set-pending-mesage-callback! callback) + (let [online-message #(discoveries/send-online! + {:web3 web3 + :message {:from identity + :message-id (random/id) + :keypair profile-keypair}})] + (d/run-delivery-loop! + web3 + (assoc options :online-message online-message))) + (doseq [pending-message pending-messages] + (d/add-prepeared-pending-message! web3 pending-message)) + web3)) diff --git a/src/status_im/protocol/discoveries.cljs b/src/status_im/protocol/discoveries.cljs new file mode 100644 index 0000000000..2432f44988 --- /dev/null +++ b/src/status_im/protocol/discoveries.cljs @@ -0,0 +1,143 @@ +(ns status-im.protocol.discoveries + (:require + [taoensso.timbre :refer-macros [debug]] + [status-im.protocol.web3.utils :as u] + [status-im.protocol.web3.delivery :as d] + [status-im.protocol.web3.filtering :as f] + [status-im.protocol.listeners :as l] + [cljs.spec :as s] + [status-im.protocol.validation :refer-macros [valid?]] + [status-im.utils.random :as random])) + +(def discover-topic-prefix "status-discover-") +(def discover-hashtag-prefix "status-hashtag-") + +(defn- make-discover-topic [identity] + (str discover-topic-prefix identity)) + +(s/def :send-online/message + (s/merge :protocol/message + (s/keys :req-un [:message/keypair]))) +(s/def :send-online/options + (s/keys :req-un [:options/web3 :send-online/message])) + +(defn send-online! + [{:keys [web3 message] :as options}] + {:pre [(valid? :send-online/options options)]} + (debug :send-online) + (let [message' (merge + message + {:requires-ack? false + :type :online + :payload {:timestamp (u/timestamp)} + :topics [(make-discover-topic (:from message))]})] + (d/add-pending-message! web3 message'))) + +(s/def ::identity :message/from) +(s/def :watch-user/options + (s/keys :req-un [:options/web3 :message/keypair ::identity ::callback])) + +(defn watch-user! + [{:keys [web3 identity] :as options}] + {:pre [(valid? :watch-user/options options)]} + (f/add-filter! + web3 + {:from identity + :topics [(make-discover-topic identity)]} + (l/message-listener (dissoc options :identity)))) + +(defn stop-watching-user! + [{:keys [web3 identity] :as options}] + (f/remove-filter! + web3 + {:from identity + :topics [(make-discover-topic identity)]})) + +(s/def :contact-request/contact map?) + +(s/def :contact-request/payload + (s/merge :message/payload + (s/keys :req-un [:contact-request/contact :message/keypair]))) + +(s/def :contact-request/message + (s/merge :protocol/message + (s/keys :req-un [:message/to :contact-request/payload]))) + +(defn contact-request! + [{:keys [web3 message]}] + {:pre [(valid? :contact-request/message message)]} + (debug :send-command-request!) + (d/add-pending-message! + web3 + (assoc message :type :contact-request + :requires-ack? true + :topics [f/status-topic]))) + +(defonce watched-hashtag-topics (atom nil)) + +(s/def :discoveries/hashtags (s/every string? :kind-of set?)) + +(s/def ::callback fn?) +(s/def :watch-hashtags/options + (s/keys :req-un [:options/web3 :discoveries/hashtags ::callback])) + +(s/def ::status (s/nilable string?)) +(s/def ::profile (s/keys :req-un [::status])) +(s/def :profile/payload + (s/merge :message/payload (s/keys :req-un [::profile]))) +(s/def :profile/message + (s/merge :protocol/message (s/keys :req-un [:message/keypair + :profile/payload]))) +(s/def :broadcast-profile/options + (s/keys :req-un [:profile/message :options/web3])) + +(defn broadcast-profile! + [{:keys [web3 message] :as options}] + {:pre [(valid? :broadcast-profile/options options)]} + (debug :broadcasting-status) + (d/add-pending-message! + web3 + (-> message + (assoc :type :profile + :topics [(make-discover-topic (:from message))]) + (assoc-in [:payload :timestamp] (u/timestamp)) + (assoc-in [:payload :content :profile] + (get-in message [:payload :profile])) + (update :payload dissoc :profile)))) + +(s/def :status/payload + (s/merge :message/payload (s/keys :req-un [::status]))) +(s/def :status/message + (s/merge :protocol/message (s/keys :req-un [:status/payload]))) +(s/def :broadcast-hasthags/options + (s/keys :req-un [:discoveries/hashtags :status/message :options/web3])) + +(defn send-status! + [{:keys [web3 message]}] + (debug :broadcasting-status) + (let [message (-> message + (assoc :type :discover + :topics [(make-discover-topic (:from message))]))] + (d/add-pending-message! web3 message))) + +(defn send-discoveries-request! + [{:keys [web3 message]}] + (debug :sending-discoveries-request) + (d/add-pending-message! + web3 + (-> message + (assoc :type :discoveries-request + :topics [(make-discover-topic (:from message))])))) + +(defn send-discoveries-response! + [{:keys [web3 discoveries message]}] + (debug :sending-discoveries-response) + (doseq [portion (->> (take 100 discoveries) + (partition 10 10 nil))] + (d/add-pending-message! + web3 + (-> message + (assoc :type :discoveries-response + :topics [(make-discover-topic (:from message))] + :message-id (random/id) + :payload {:data (into [] portion)}))))) diff --git a/src/status_im/protocol/encryption.cljs b/src/status_im/protocol/encryption.cljs new file mode 100644 index 0000000000..13043b7656 --- /dev/null +++ b/src/status_im/protocol/encryption.cljs @@ -0,0 +1,21 @@ +(ns status-im.protocol.encryption) + +(def default-curve 384) + +(def ecc (js/require "eccjs")) + +(defn new-keypair! + "Returns {:private \"private key\" :public \"public key\"" + [] + (let [{:keys [enc dec]} + (-> (.generate ecc (.-ENC_DEC ecc) default-curve) + (js->clj :keywordize-keys true))] + {:private dec + :public enc})) + +(defn encrypt [public-key content] + (.encrypt ecc public-key content)) + +(defn decrypt [private-key content] + (.decrypt ecc private-key content)) + diff --git a/src/status_im/protocol/group.cljs b/src/status_im/protocol/group.cljs new file mode 100644 index 0000000000..1cf9342bf0 --- /dev/null +++ b/src/status_im/protocol/group.cljs @@ -0,0 +1,119 @@ +(ns status-im.protocol.group + (:require + [status-im.protocol.message :as m] + [status-im.protocol.web3.delivery :as d] + [status-im.protocol.web3.utils :as u] + [cljs.spec :as s] + [taoensso.timbre :refer-macros [debug]] + [status-im.protocol.validation :refer-macros [valid?]] + [status-im.protocol.web3.filtering :as f] + [status-im.protocol.listeners :as l])) + +(defn prepare-mesage + [{:keys [message group-id keypair new-keypair type]}] + (let [message' (-> message + (update :payload assoc + :group-id group-id + :type type + :timestamp (u/timestamp)) + (assoc :topics [group-id] + :requires-ack? true + :keypair keypair + :type type))] + (if new-keypair + (assoc message' :new-keypair keypair) + message'))) + +(defn- send-group-message! + [{:keys [web3] :as opts} type] + (let [message (-> opts + (assoc :type type) + (prepare-mesage))] + (debug :send-group-message message) + (d/add-pending-message! web3 message))) + +(s/def ::group-message + (s/merge :protocol/message (s/keys :req-un [:chat-message/payload]))) + +(defn send! + [{:keys [keypair message] :as options}] + {:pre [(valid? :message/keypair keypair) + (valid? ::group-message message)]} + (send-group-message! options :group-message)) + +(defn leave! + [options] + (send-group-message! options :leave-group)) + +(defn add-identity! + [{:keys [identity] :as options}] + {:pre [(valid? :message/to identity)]} + (let [options' (assoc-in options + [:message :payload :identity] + identity)] + (send-group-message! options' :add-group-identity))) + +(defn remove-identity! + [{:keys [identity] :as options}] + {:pre [(valid? :message/to identity)]} + (let [options' (assoc-in options + [:message :payload :identity] + identity)] + (send-group-message! options' :remove-group-identity))) + +(s/def :group/admin :message/from) +(s/def ::identities (s/* string?)) + +(s/def :group/name string?) +(s/def :group/id string?) +(s/def :group/admin string?) +(s/def :group/contacts (s/* string?)) +(s/def ::group + (s/keys :req-un + [:group/name :group/id :group/contacts :message/keypair :group/admin])) +(s/def :invite/options + (s/keys :req-un [:options/web3 :protocol/message ::group ::identities])) + +(defn- notify-about-group! + [type {:keys [web3 message identities group] + :as options}] + {:pre [(valid? :invite/options options)]} + (let [{:keys [id admin name keypair contacts]} group + message' (-> message + (assoc :topics [f/status-topic] + :requires-ack? true + :type type) + (update :payload assoc + :timestamp (u/timestamp) + :group-id id + :group-admin admin + :group-name name + :keypair keypair + :contacts contacts + :type type))] + (doseq [identity identities] + (d/add-pending-message! web3 (assoc message' :to identity))))) + +(defn invite! + [options] + (notify-about-group! :group-invitation options)) + +;; todo notify users about keypair change when someone leaves group (from admin) +(defn update-group! + [options] + (notify-about-group! :update-group options)) + +(defn stop-watching-group! + [{:keys [web3 group-id]}] + {:pre [(valid? :message/chat-id group-id)]} + (f/remove-filter! web3 {:topics [group-id]})) + +(defn start-watching-group! + [{:keys [web3 group-id keypair callback identity]}] + (f/add-filter! + web3 + {:topics [group-id]} + (l/message-listener {:web3 web3 + :identity identity + :callback callback + :keypair keypair}))) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs new file mode 100644 index 0000000000..b16ad3c288 --- /dev/null +++ b/src/status_im/protocol/handlers.cljs @@ -0,0 +1,414 @@ +(ns status-im.protocol.handlers + (:require [status-im.utils.handlers :as u] + [re-frame.core :refer [dispatch after]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.data-store.contacts :as contacts] + [status-im.data-store.messages :as messages] + [status-im.data-store.pending-messages :as pending-messages] + [status-im.data-store.processed-messages :as processed-messages] + [status-im.data-store.chats :as chats] + [status-im.protocol.core :as protocol] + [status-im.constants :refer [text-content-type + blocks-per-hour]] + [status-im.i18n :refer [label]] + [status-im.utils.random :as random] + [status-im.protocol.message-cache :as cache] + [status-im.utils.datetime :as dt] + [taoensso.timbre :as log :refer-macros [debug]] + [status-im.constants :as c] + [status-im.components.status :as status])) + +(register-handler :initialize-protocol + (fn [db [_ current-account-id]] + (let [{:keys [public-key status updates-public-key + updates-private-key]} + (get-in db [:accounts current-account-id])] + (if public-key + (let [groups (chats/get-active-group-chats) + w3 (protocol/init-whisper! + {:rpc-url c/ethereum-rpc-url + :identity public-key + :groups groups + :callback #(dispatch [:incoming-message %1 %2]) + :ack-not-received-s-interval 125 + :default-ttl 120 + :send-online-s-interval 180 + :ttl {} + :max-attempts-number 3 + :delivery-loop-ms-interval 500 + :profile-keypair {:public updates-public-key + :private updates-private-key} + :hashtags (u/get-hashtags status) + :pending-messages (pending-messages/get-all) + :contacts (keep (fn [{:keys [whisper-identity + public-key + private-key]}] + (when (and public-key private-key) + {:identity whisper-identity + :keypair {:public public-key + :private private-key}})) + (contacts/get-all)) + :post-error-callback #(dispatch [::post-error %])})] + (assoc db :web3 w3)) + db)))) + +(register-handler :update-sync-state + (u/side-effect! + (fn [{:keys [sync-state sync-data]} [_ error sync]] + (let [{:keys [highestBlock currentBlock] :as state} + (js->clj sync :keywordize-keys true) + syncing? (> (- highestBlock currentBlock) blocks-per-hour) + new-state (cond + error :offline + syncing? (if (= sync-state :done) + :pending + :in-progress) + :else (if (or (= sync-state :done) + (= sync-state :pending)) + :done + :synced))] + (when (and (not= sync-data state) (= :in-progress new-state)) + (dispatch [:set :sync-data state])) + (when (not= sync-state new-state) + (dispatch [:set :sync-state new-state])))))) + +(register-handler :initialize-sync-listener + (fn [{:keys [web3 sync-listener] :as db} _] + (if-not sync-listener + (->> (.isSyncing (.-eth web3) + (fn [error sync] + (dispatch [:update-sync-state error sync]))) + (assoc db :sync-listener)) + db))) + +(register-handler :incoming-message + (u/side-effect! + (fn [_ [_ type {:keys [payload ttl id] :as message}]] + (let [message-id (or id (:message-id payload))] + (when-not (cache/exists? message-id type) + (let [ttl-s (* 1000 (or ttl 120)) + processed-message {:id (random/id) + :message-id message-id + :type type + :ttl (+ (dt/now-ms) ttl-s)}] + (cache/add! processed-message) + (processed-messages/save processed-message)) + (case type + :message (dispatch [:received-protocol-message! message]) + :group-message (dispatch [:received-protocol-message! message]) + :ack (if (#{:message :group-message} (:type payload)) + (dispatch [:message-delivered message]) + (dispatch [:pending-message-remove message])) + :seen (dispatch [:message-seen message]) + :clock-value-request (dispatch [:message-clock-value-request message]) + :clock-value (dispatch [:message-clock-value message]) + :group-invitation (dispatch [:group-chat-invite-received message]) + :update-group (dispatch [:update-group-message message]) + :add-group-identity (dispatch [:participant-invited-to-group message]) + :remove-group-identity (dispatch [:participant-removed-from-group message]) + :leave-group (dispatch [:participant-left-group message]) + :contact-request (dispatch [:contact-request-received message]) + :discover (dispatch [:status-received message]) + :discoveries-request (dispatch [:discoveries-request-received message]) + :discoveries-response (dispatch [:discoveries-response-received message]) + :profile (dispatch [:contact-update-received message]) + :online (dispatch [:contact-online-received message]) + :pending (dispatch [:pending-message-upsert message]) + :sent (let [{:keys [to id group-id]} message + message' {:from to + :payload {:message-id id + :group-id group-id}}] + (dispatch [:message-sent message'])) + (debug "Unknown message type" type))))))) + +(defn system-message + ([message-id timestamp content] + {:from "system" + :message-id message-id + :timestamp timestamp + :content content + :content-type text-content-type})) + +(defn joined-chat-message [chat-id from message-id] + (let [contact-name (:name (contacts/get-by-id from))] + (messages/save chat-id {:from "system" + :message-id (str message-id "_" from) + :content (str (or contact-name from) " " (label :t/received-invitation)) + :content-type text-content-type}))) + +(defn participant-invited-to-group-message [chat-id current-identity identity from message-id timestamp] + (let [inviter-name (:name (contacts/get-by-id from)) + invitee-name (if (= identity current-identity) + (label :t/You) + (:name (contacts/get-by-id identity)))] + {:from "system" + :group-id chat-id + :timestamp timestamp + :message-id message-id + :content (str (or inviter-name from) " " (label :t/invited) " " (or invitee-name identity)) + :content-type text-content-type})) + +(defn participant-removed-from-group-message + [identity from {:keys [message-id timestamp]}] + (let [remover-name (:name (contacts/get-by-id from)) + removed-name (:name (contacts/get-by-id identity))] + (->> (str (or remover-name from) " " (label :t/removed) " " (or removed-name identity)) + (system-message message-id timestamp)))) + +(defn you-removed-from-group-message + [from {:keys [message-id timestamp]}] + (let [remover-name (:name (contacts/get-by-id from))] + (->> (str (or remover-name from) " " (label :t/removed-from-chat)) + (system-message message-id timestamp)))) + +(defn participant-left-group-message + [chat-id from {:keys [message-id timestamp]}] + (let [left-name (:name (contacts/get-by-id from))] + (->> (str (or left-name from) " " (label :t/left)) + (system-message message-id timestamp) + (messages/save chat-id)))) + +(register-handler :group-chat-invite-acked + (u/side-effect! + (fn [_ [action from group-id ack-message-id]] + (log/debug action from group-id ack-message-id) + #_(joined-chat-message group-id from ack-message-id)))) + +(register-handler :participant-removed-from-group + (u/side-effect! + (fn [{:keys [current-public-key chats]} + [_ {:keys [from] + {:keys [group-id identity message-id] :as payload} :payload + :as message}]] + (when-not (messages/get-by-id message-id) + (let [admin (get-in chats [group-id :group-admin])] + (when (= admin from) + (if (= current-public-key identity) + (dispatch [::you-removed-from-group message]) + (let [message + (assoc + (participant-removed-from-group-message identity from payload) + :group-id group-id)] + (chats/remove-contacts group-id [identity]) + (dispatch [:received-message message]))))))))) + +(register-handler ::you-removed-from-group + (u/side-effect! + (fn [{:keys [web3]} + [_ {:keys [from] + {:keys [group-id timestamp] :as payload} :payload}]] + (when (chats/new-update? timestamp group-id) + (let [message + (-> (you-removed-from-group-message from payload) + (assoc :group-id group-id))] + (dispatch [:received-message message])) + (protocol/stop-watching-group! {:web3 web3 + :group-id group-id}) + (dispatch [:update-chat! {:chat-id group-id + :removed-from-at timestamp + :is-active false}]))))) + +(register-handler :participant-left-group + (u/side-effect! + (fn [{:keys [current-public-key]} + [_ {:keys [from] + {:keys [group-id timestamp] :as payload} :payload}]] + (when (and (not= current-public-key from) + (chats/is-active? group-id) + (> timestamp (chats/get-property group-id :timestamp))) + (participant-left-group-message group-id from payload) + (dispatch [::remove-identity-from-chat group-id from]) + (dispatch [::remove-identity-from-chat! group-id from]))))) + +(register-handler ::remove-identity-from-chat + (fn [db [_ chat-id id]] + (update-in db [:chats chat-id :contacts] + #(remove (fn [{:keys [identity]}] + (= identity id)) %)))) + +(register-handler ::remove-identity-from-chat! + (u/side-effect! + (fn [_ [_ group-id identity]] + (chats/remove-contacts group-id [identity])))) + +(register-handler :participant-invited-to-group + (u/side-effect! + (fn [{:keys [current-public-key chats]} + [_ {:keys [from] + {:keys [group-id identity message-id timestamp]} :payload}]] + (let [admin (get-in chats [group-id :group-admin])] + (when (= from admin) + (dispatch + [:received-message + (participant-invited-to-group-message group-id current-public-key identity from message-id timestamp)]) + (when-not (= current-public-key identity) + (dispatch [:add-contact-to-group! group-id identity]))))))) + +(register-handler :add-contact-to-group! + (u/side-effect! + (fn [_ [_ group-id identity]] + (when-not (chats/has-contact? group-id identity) + (dispatch [::add-contact group-id identity]) + (dispatch [::store-contact! group-id identity]))))) + +(register-handler ::add-contact + (fn [db [_ group-id identity]] + (update-in db [:chats group-id :contacts] conj {:identity identity}))) + +(register-handler ::store-contact! + (u/side-effect! + (fn [_ [_ group-id identity]] + (chats/add-contacts group-id [identity])))) + +(defn save-message-status! [status] + (fn [_ [_ + {:keys [from] + {:keys [message-id ack-of-message group-id]} :payload}]] + (let [message-id' (or ack-of-message message-id)] + (when-let [{:keys [message-status] :as message} (messages/get-by-id message-id')] + (when-not (= (keyword message-status) :seen) + (let [group? (boolean group-id) + message (if (and group? (not= status :sent)) + (update-in message + [:user-statuses from] + (fn [{old-status :status}] + {:id (random/id) + :whisper-identity from + :status (if (= (keyword old-status) :seen) + old-status + status)})) + (assoc message :message-status status))] + (messages/update message))))))) + +(defn save-message-clock-value! + [{:keys [message-extras] :as db} + [_ {:keys [from] + {:keys [message-id clock-value]} :payload}]] + (when-let [{old-clock-value :clock-value + :as message} (merge (messages/get-by-id message-id) + (get message-extras message-id))] + (if (>= clock-value old-clock-value) + (messages/update (assoc message :clock-value clock-value :show? true))))) + +(defn update-message-status [status] + (fn [db + [_ {:keys [from] + {:keys [message-id ack-of-message group-id]} :payload}]] + (if (chats/is-active? (or group-id from)) + (let [message-id' (or ack-of-message message-id) + group? (boolean group-id) + status-path (if (and group? (not= status :sent)) + [:message-user-statuses message-id' from] + [:message-statuses message-id']) + {current-status :status} (get-in db status-path)] + (if-not (= :seen current-status) + (assoc-in db status-path {:whisper-identity from + :status status}) + db)) + db))) + +(defn remove-pending-message + [_ [_ message]] + (dispatch [:pending-message-remove message])) + +(register-handler :message-delivered + [(after (save-message-status! :delivered)) + (after remove-pending-message)] + (update-message-status :delivered)) + +(register-handler :message-failed + (after (save-message-status! :failed)) + (update-message-status :failed)) + +(register-handler :message-sent + (after (save-message-status! :sent)) + (update-message-status :sent)) + +(register-handler :message-seen + [(after (save-message-status! :seen))] + (update-message-status :seen)) + +(register-handler :message-clock-value-request + (u/side-effect! + (fn [db [_ {:keys [from] {:keys [message-id]} :payload}]] + (let [{:keys [chat-id]} (messages/get-by-id message-id) + message-overhead (chats/get-message-overhead chat-id) + last-clock-value (messages/get-last-clock-value chat-id)] + (if (> message-overhead 0) + (let [last-outgoing (->> (messages/get-last-outgoing chat-id message-overhead) + (reverse) + (map-indexed vector))] + (chats/reset-message-overhead chat-id) + (doseq [[i message] last-outgoing] + (dispatch [:update-clock-value! from i message (+ last-clock-value 100)]))) + (dispatch [:send-clock-value! from message-id])))))) + +(register-handler :message-clock-value + (after save-message-clock-value!) + (fn [{:keys [message-extras] :as db} + [_ {:keys [from] + {:keys [message-id clock-value]} :payload}]] + (if-let [{old-clock-value :clock-value + :as message} (merge (messages/get-by-id message-id) + (get message-extras message-id))] + (if (> clock-value old-clock-value) + (assoc-in db [:message-extras message-id] {:clock-value clock-value + :show? true}) + db) + db))) + +(register-handler :pending-message-upsert + (after + (fn [_ [_ {:keys [type id] :as pending-message}]] + (pending-messages/save pending-message) + (when (#{:message :group-message} type) + (messages/update {:message-id id + :delivery-status :pending})))) + (fn [db [_ {:keys [type id to group-id]}]] + (if (#{:message :group-message} type) + (let [chat-id (or group-id to) + current-status (get-in db [:message-status chat-id id])] + (if-not (= :seen current-status) + (assoc-in db [:message-status chat-id id] :pending) + db)) + db))) + +(register-handler :pending-message-remove + (u/side-effect! + (fn [_ [_ message]] + (pending-messages/delete message)))) + +(register-handler :contact-request-received + (u/side-effect! + (fn [{:keys [contacts]} [_ {:keys [from payload]}]] + (when from + (let [{{:keys [name profile-image address status]} :contact + {:keys [public private]} :keypair} payload + + contact {:whisper-identity from + :public-key public + :private-key private + :address address + :status status + :photo-path profile-image + :name name} + contact-exists? (get contacts from) + chat {:name name + :chat-id from + :contact-info (prn-str contact) + :pending-contact? true}] + (if contact-exists? + (do + (dispatch [:update-contact! contact]) + (dispatch [:watch-contact contact])) + (dispatch [:add-chat from chat]))))))) + +(register-handler ::post-error + (u/side-effect! + (fn [_ [_ error]] + (.log js/console error) + (let [message (.-message error)] + (when (or (re-find (re-pattern "Could not connect to the server.") message) + (re-find (re-pattern "Failed to connect") message)) + (status/restart-rpc) + (dispatch [:load-commands!])))))) diff --git a/src/status_im/protocol/listeners.cljs b/src/status_im/protocol/listeners.cljs new file mode 100644 index 0000000000..535f520e86 --- /dev/null +++ b/src/status_im/protocol/listeners.cljs @@ -0,0 +1,53 @@ +(ns status-im.protocol.listeners + (:require [cljs.reader :as r] + [status-im.protocol.ack :as ack] + [status-im.protocol.web3.utils :as u] + [status-im.protocol.encryption :as e] + [taoensso.timbre :refer-macros [debug] :as log] + [status-im.utils.hex :as i])) + +(defn- parse-payload [payload] + (debug :parse-payload) + (r/read-string (u/to-utf8 payload))) + +(defn- decrypt [key content] + (try + {:content (r/read-string (e/decrypt key content))} + (catch :default err + (debug :decrypt-error err) + {:error err}))) + +(defn- parse-content [key {:keys [content]} was-encrypted?] + (debug :parse-content + "Key exitsts:" (not (nil? key)) + "Content exists:" (not (nil? content))) + (if (and (not was-encrypted?) key content) + (decrypt key content) + {:content content})) + +(defn message-listener + [{:keys [web3 identity callback keypair]}] + (fn [error js-message] + ;; todo handle error + (when error + (debug :listener-error error)) + (when-not error + (debug :message-received) + (let [{:keys [from payload to] :as message} + (js->clj js-message :keywordize-keys true) + + {:keys [type ack?] :as payload'} + (parse-payload payload)] + (when (or (not= (i/normalize-hex identity) + (i/normalize-hex from)) + ;; allow user to receive his own discoveries + (= type :discover)) + (let [{:keys [content error]} (parse-content (:private keypair) + payload' + (not= "0x0" to))] + (if error + (debug :failed-to-handle-message error) + (let [payload'' (assoc payload' :content content) + message' (assoc message :payload payload'')] + (callback (if ack? :ack type) message') + (ack/check-ack! web3 from payload'' identity))))))))) diff --git a/src/status_im/protocol/message.cljs b/src/status_im/protocol/message.cljs new file mode 100644 index 0000000000..501b3be60d --- /dev/null +++ b/src/status_im/protocol/message.cljs @@ -0,0 +1,47 @@ +(ns status-im.protocol.message + (:require [cljs.spec :as s])) + +(s/def :message/ttl (s/and int? pos?)) +(s/def :message/from string?) +(s/def :message/to (s/nilable string?)) +(s/def :message/message-id string?) +(s/def :message/requires-ack? boolean?) +(s/def :keypair/private string?) +(s/def :keypair/public string?) +(s/def :message/keypair (s/keys :req-un [:keypair/private + :keypair/public])) +(s/def :message/topics (s/* string?)) + +(s/def :payload/content (s/or :string-message string? + :command map?)) +(s/def :payload/content-type string?) +(s/def :payload/timestamp (s/and int? pos?)) +(s/def :payload/new-keypair :message/keypair) + +(s/def :group-message/type + #{:group-message :group-invitation :add-group-identity + :remove-group-identity :leave-group :update-group}) + +(s/def :discover-message/type #{:online :status :discover :contact-request}) + +(s/def :message/type + (s/or :group :group-message/type + :discover :discover-message/type + :user #{:message})) + +(s/def :message/payload + (s/keys :opt-un [:message/type + :payload/content + :payload/content-type + :payload/new-keypair + :payload/timestamp])) + +(s/def :protocol/message + (s/keys :req-un [:message/from :message/message-id] + :opt-un [:message/to :message/topics :message/requires-ack? + :message/keypair :message/ttl :message/payload])) + +(s/def :chat-message/payload + (s/keys :req-un [:payload/content :payload/content-type :payload/timestamp])) + +(s/def :options/web3 #(not (nil? %))) diff --git a/src/status_im/protocol/message_cache.cljs b/src/status_im/protocol/message_cache.cljs new file mode 100644 index 0000000000..969ae7041b --- /dev/null +++ b/src/status_im/protocol/message_cache.cljs @@ -0,0 +1,22 @@ +(ns status-im.protocol.message-cache + (:refer-clojure :exclude [exists?])) + +(defonce messages-set (atom #{})) +(defonce messages-map (atom {})) + +(defn init! + [messages] + (reset! messages-set (into #{} messages)) + (reset! messages-map (->> messages + (map (fn [{:keys [message-id type] :as message}] + [[message-id type] message])) + (into {})))) + +(defn add! + [{:keys [message-id type] :as message}] + (swap! messages-set conj message) + (swap! messages-map conj [[message-id type] message])) + +(defn exists? + [message-id type] + (get @messages-map [message-id type])) diff --git a/src/status_im/protocol/validation.clj b/src/status_im/protocol/validation.clj new file mode 100644 index 0000000000..c9d472fd10 --- /dev/null +++ b/src/status_im/protocol/validation.clj @@ -0,0 +1,12 @@ +(ns status-im.protocol.validation) + +(defn- fline [and-form] (:line (meta and-form))) + +(defmacro valid? [spec x] + `(let [v?# (cljs.spec/valid? ~spec ~x)] + (when-not v?# + (let [explanation# (cljs.spec/explain-str ~spec ~x)] + (taoensso.timbre/log! :error :p + [explanation#] + ~{:?line (fline &form)}))) + v?#)) diff --git a/src/status_im/protocol/validation.cljs b/src/status_im/protocol/validation.cljs new file mode 100644 index 0000000000..44ba173656 --- /dev/null +++ b/src/status_im/protocol/validation.cljs @@ -0,0 +1,2 @@ +(ns status-im.protocol.validation + (:require-macros [status-im.protocol.validation :as macros])) diff --git a/src/status_im/protocol/web3/delivery.cljs b/src/status_im/protocol/web3/delivery.cljs new file mode 100644 index 0000000000..862ee9d8d1 --- /dev/null +++ b/src/status_im/protocol/web3/delivery.cljs @@ -0,0 +1,231 @@ +(ns status-im.protocol.web3.delivery + (:require-macros [cljs.core.async.macros :refer [go-loop go]]) + (:require [cljs.core.async :refer [ message + (select-keys [:message-id :requires-ack? :type :clock-value]) + (merge payload) + (assoc :content content') + prn-str + u/from-utf8)] + (-> message + (select-keys [:from :to :topics :ttl]) + (assoc :payload payload')))) + +(s/def :shh/pending-message + (s/keys :req-un [:message/from :shh/payload :message/topics] + :opt-un [:message/ttl :message/to])) + +(defonce pending-mesage-callback (atom nil)) +(defonce recipient->pending-message (atom {})) + +(defn set-pending-mesage-callback! + [callback] + (reset! pending-mesage-callback callback)) + +(defn add-pending-message! + [web3 {:keys [type message-id requires-ack? to ack?] :as message}] + {:pre [(valid? :protocol/message message)]} + (go + (debug :add-pending-message!) + ;; encryption can take some time, better to run asynchronously + (let [message' (prepare-message message)] + (when (valid? :shh/pending-message message') + (let [group-id (get-in message [:payload :group-id]) + pending-message {:id message-id + :ack? (boolean ack?) + :message message' + :to to + :type type + :group-id group-id + :requires-ack? (boolean requires-ack?) + :attempts 0 + :was-sent? false}] + (when (and @pending-mesage-callback requires-ack?) + (@pending-mesage-callback :pending pending-message)) + (swap! messages assoc-in [web3 message-id to] pending-message) + (when to + (swap! recipient->pending-message + update to set/union #{[web3 message-id to]}))))))) + +(s/def :delivery/pending-message + (s/keys :req-un [:message/from :message/to :shh/payload + :message/requires-ack? :payload/ack? ::id :message/topics + ::attempts ::was-sent?])) + +(defn add-prepeared-pending-message! + [web3 {:keys [message-id to] :as pending-message}] + {:pre [(valid? :delivery/pending-message pending-message)]} + (debug :add-prepeared-pending-message!) + (let [message (select-keys pending-message [:from :to :topics :payload]) + pending-message' (assoc pending-message :message message + :id message-id)] + (swap! messages assoc-in [web3 message-id to] pending-message') + (when to + (swap! recipient->pending-message + update to set/union #{[web3 message-id to]})))) + +(defn remove-pending-message! [web3 id to] + (swap! messages update web3 + (fn [messages] + (when messages + (let [message (messages id) + ;; Message that is send without specified "from" option + ;; is stored in pending "messages" map as + ;; {message-id {nil message}}. + ;; When we receive the first ack for such message it is + ;; removed from pending messages adding of the nil key + ;; to the next dissoc form + ;; todo rewrite handling of ack message in more clear way + message' (dissoc message to nil)] + (if (seq message') + (assoc messages id message') + (dissoc messages id)))))) + (when to + (swap! recipient->pending-message + update to set/difference #{[web3 id to]}))) + +(defn message-was-sent! [web3 id to] + (let [messages' (swap! messages update web3 + (fn [messages] + (let [message (get-in messages [id to]) + message' (when message + (assoc message :was-sent? true + :attempts 1))] + (if message' + (assoc-in messages [id to] message') + messages))))] + (when @pending-mesage-callback + (let [message (get-in messages' [web3 id to])] + (when message + (@pending-mesage-callback :sent message)))))) + +(defn attempt-was-made! [web3 id to] + (debug :attempt-was-made id) + (swap! messages update-in [web3 id to] + (fn [{:keys [attempts] :as data}] + (assoc data :attempts (inc attempts) + :last-attempt (u/timestamp))))) + +(defn delivery-callback + [web3 post-error-callback {:keys [id requires-ack? to]}] + (fn [error _] + (when error + (log/warn :shh-post-error error) + (when post-error-callback + (post-error-callback error))) + (when-not error + (debug :delivery-callback) + (message-was-sent! web3 id to) + (when-not requires-ack? + (remove-pending-message! web3 id to))))) + +(s/def ::pos-int (s/and pos? int?)) +(s/def ::delivery-loop-ms-interval ::pos-int) +(s/def ::ack-not-received-s-interval ::pos-int) +(s/def ::max-attempts-number ::pos-int) +(s/def ::default-ttl ::pos-int) +(s/def ::send-online-s-interval ::pos-int) +(s/def ::online-message fn?) +(s/def ::post-error-callback fn?) + +(s/def ::delivery-options + (s/keys :req-un [::delivery-loop-ms-interval ::ack-not-received-s-interval + ::max-attempts-number ::default-ttl ::send-online-s-interval + ::post-error-callback] + :opt-un [::online-message])) + +(defn should-be-retransmitted? + "Checks if messages should be transmitted again." + [{:keys [ack-not-received-s-interval max-attempts-number]} + {:keys [was-sent? attempts last-attempt]}] + (if-not was-sent? + ;; message was not sent succesfully via web3.shh, but maybe + ;; better to do this only when we receive error from shh.post + ;; todo add some notification about network issues + (<= attempts (* 5 max-attempts-number)) + (and + ;; if message was not send less then max-attempts-number times + ;; continue attempts + (<= attempts max-attempts-number) + ;; check retransmition interval + (<= (+ last-attempt (* 1000 ack-not-received-s-interval)) (u/timestamp))))) + +(defn- check-ttl + [message message-type ttl-config default-ttl] + (update message :ttl #(or % ((keyword message-type) ttl-config) default-ttl))) + +(defn message-pending? + [web3 required-type required-to] + (some (fn [[_ messages]] + (some (fn [[_ {:keys [type to]}]] + (and (= type required-type) + (= to required-to))) + messages)) + (@messages web3))) + +(defn run-delivery-loop! + [web3 {:keys [delivery-loop-ms-interval default-ttl ttl-config + send-online-s-interval online-message post-error-callback] + :as options}] + {:pre [(valid? ::delivery-options options)]} + (debug :run-delivery-loop!) + (let [previous-stop-flag @loop-state + stop? (atom false)] + ;; stop previous delivery loop if it exists + (when previous-stop-flag + (reset! previous-stop-flag true)) + ;; reset stop flag for a new loop + (reset! loop-state stop?) + ;; go go!!! + (debug :init-loop) + (go-loop [_ nil] + (doseq [[_ messages] (@messages web3)] + (doseq [[_ {:keys [id message to type] :as data}] messages] + ;; check each message asynchronously + (go + (when (should-be-retransmitted? options data) + (try + (let [message' (check-ttl message type ttl-config default-ttl) + callback (delivery-callback web3 post-error-callback data)] + (t/post-message! web3 message' callback)) + (catch :default err + (log/error :post-message-error err)) + (finally + (attempt-was-made! web3 id to))))))) + (when-not @stop? + (recur (pending-message to)] + (when (get-in @messages key) + (swap! messages #(update-in % key assoc + :last-attempt 0 + :attempts 0))))) + +(defn reset-all-pending-messages! [] + (reset! messages {})) diff --git a/src/status_im/protocol/web3/filtering.cljs b/src/status_im/protocol/web3/filtering.cljs new file mode 100644 index 0000000000..d4f3bfa49c --- /dev/null +++ b/src/status_im/protocol/web3/filtering.cljs @@ -0,0 +1,29 @@ +(ns status-im.protocol.web3.filtering + (:require [status-im.protocol.web3.utils :as u] + [cljs.spec :as s] + [taoensso.timbre :refer-macros [debug]])) + +(def status-topic "status-dapp-topic") +(defonce filters (atom {})) + +(s/def ::options (s/keys :opt-un [:message/to :message/topics])) + +(defn remove-filter! [web3 options] + (when-let [filter (get-in @filters [web3 options])] + (.stopWatching filter) + (debug :stop-watching options) + (swap! filters update web3 dissoc options))) + +(defn add-filter! + [web3 options callback] + (remove-filter! web3 options) + (debug :add-filter options) + (let [filter (.filter (u/shh web3) + (clj->js options) + callback)] + (swap! filters assoc-in [web3 options] filter))) + +(defn remove-all-filters! [] + (doseq [[web3 filters] @filters] + (doseq [options (keys filters)] + (remove-filter! web3 options)))) diff --git a/src/status_im/protocol/web3/transport.cljs b/src/status_im/protocol/web3/transport.cljs new file mode 100644 index 0000000000..d913869370 --- /dev/null +++ b/src/status_im/protocol/web3/transport.cljs @@ -0,0 +1,17 @@ +(ns status-im.protocol.web3.transport + (:require [status-im.protocol.web3.utils :as u] + [cljs.spec :as s] + [status-im.protocol.validation :refer-macros [valid?]] + [taoensso.timbre :refer-macros [debug]])) + +(s/def :shh/payload string?) +(s/def :shh/message + (s/keys + :req-un [:shh/payload :message/ttl :message/from :message/topics] + :opt-un [:message/to])) + +(defn post-message! + [web3 message callback] + {:pre [(valid? :shh/message message)]} + (debug :post-message message) + (.post (u/shh web3) (clj->js message) callback)) diff --git a/src/status_im/protocol/web3/utils.cljs b/src/status_im/protocol/web3/utils.cljs new file mode 100644 index 0000000000..487d763b03 --- /dev/null +++ b/src/status_im/protocol/web3/utils.cljs @@ -0,0 +1,23 @@ +(ns status-im.protocol.web3.utils + (:require [cljs-time.core :refer [now]] + [cljs-time.coerce :refer [to-long]])) + +(def web3 (js/require "web3")) + +(def status-app-topic "status-app") + +(defn from-utf8 [s] + (.fromUtf8 web3.prototype s)) + +(defn to-utf8 [s] + (.toUtf8 web3.prototype s)) + +(defn shh [web3] + (.-shh web3)) + +(defn make-web3 [rpc-url] + (->> (web3.providers.HttpProvider. rpc-url) + (web3.))) + +(defn timestamp [] + (to-long (now))) diff --git a/src/status_im/qr_scanner/handlers.cljs b/src/status_im/qr_scanner/handlers.cljs new file mode 100644 index 0000000000..6a6452ffd1 --- /dev/null +++ b/src/status_im/qr_scanner/handlers.cljs @@ -0,0 +1,41 @@ +(ns status-im.qr-scanner.handlers + (:require [re-frame.core :refer [after dispatch debug enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.navigation.handlers :as nav] + [status-im.utils.handlers :as u])) + +(defmethod nav/preload-data! :qr-scanner + [db [_ _ identifier]] + (assoc db :current-qr-context identifier)) + +(defn set-current-identifier [db [_ identifier handler]] + (assoc-in db [:qr-codes identifier] handler)) + +(defn navigate-to-scanner + [_ [_ identifier]] + (dispatch [:navigate-to :qr-scanner identifier])) + +(register-handler :scan-qr-code + (after navigate-to-scanner) + set-current-identifier) + +(register-handler :clear-qr-code + (fn [db [_ identifier]] + (update db :qr-codes dissoc identifier))) + +(defn handle-qr-request + [db [_ context data]] + (when-let [handler (get-in db [:qr-codes context])] + (dispatch [handler context data]))) + +(defn clear-qr-request [db [_ context]] + (-> db + (update :qr-codes dissoc context) + (dissoc :current-qr-context))) + +(register-handler :set-qr-code + (-> (u/side-effect! handle-qr-request) + ((enrich clear-qr-request)) + ((after (fn [{:keys [view-id]}] + (when (= :qr-scanner view-id) + (dispatch [:navigate-back]))))))) diff --git a/src/status_im/qr_scanner/screen.cljs b/src/status_im/qr_scanner/screen.cljs new file mode 100644 index 0000000000..2a95f8a768 --- /dev/null +++ b/src/status_im/qr_scanner/screen.cljs @@ -0,0 +1,46 @@ +(ns status-im.qr-scanner.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + image]] + [status-im.components.camera :refer [camera]] + [status-im.components.styles :refer [icon-search + icon-back]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.actions :as act] + [status-im.components.toolbar.styles :refer [toolbar-background1]] + [status-im.qr-scanner.styles :as st] + [status-im.utils.types :refer [json->clj]] + [clojure.string :as str])) + +(defview qr-scanner-toolbar [title] + [modal [:get :modal]] + [view + [status-bar] + [toolbar {:title title + :background-color toolbar-background1 + :nav-action (when modal + (act/back #(dispatch [:navigate-back])))}]]) + +(defview qr-scanner [] + [identifier [:get :current-qr-context]] + [view st/barcode-scanner-container + [qr-scanner-toolbar (:toolbar-title identifier)] + [camera {:onBarCodeRead (fn [code] + (let [data (-> (.-data code) + (str/replace #"ethereum:" ""))] + (dispatch [:set-qr-code identifier data]))) + ;:barCodeTypes [:qr] + :captureAudio false + :style st/barcode-scanner}] + [view st/rectangle-container + [view st/rectangle + [image {:source {:uri :corner_left_top} + :style st/corner-left-top}] + [image {:source {:uri :corner_right_top} + :style st/corner-right-top}] + [image {:source {:uri :corner_right_bottom} + :style st/corner-right-bottom}] + [image {:source {:uri :corner_left_bottom} + :style st/corner-left-bottom}]]]]) diff --git a/src/status_im/qr_scanner/styles.cljs b/src/status_im/qr_scanner/styles.cljs new file mode 100644 index 0000000000..7e92540003 --- /dev/null +++ b/src/status_im/qr_scanner/styles.cljs @@ -0,0 +1,76 @@ +(ns status-im.qr-scanner.styles + (:require [status-im.components.styles :refer [color-white]] + [status-im.components.toolbar.styles :refer [toolbar-height]])) + +(def barcode-scanner-container + {:flex 1 + :backgroundColor :white}) + +(def barcode-scanner + {:flex 1 + :justifyContent :flex-end + :alignItems :center}) + +(def rectangle-container + {:position :absolute + :left 0 + :top toolbar-height + :bottom 0 + :right 0 + :flex 1 + :alignItems :center + :justifyContent :center + :backgroundColor :transparent}) + +(def rectangle + {:height 250 + :width 250 + :backgroundColor :transparent}) + +(def corner-left-top + {:position :absolute + :left 0 + :top 0 + :width 56 + :height 56}) + +(def corner-right-top + {:position :absolute + :right 0 + :top 0 + :width 56 + :height 56}) + +(def corner-right-bottom + {:position :absolute + :right 0 + :bottom 0 + :width 56 + :height 56}) + +(def corner-left-bottom + {:position :absolute + :left 0 + :bottom 0 + :width 56 + :height 56}) + +(def import-button + {:position :absolute + :right 16 + :flex 1 + :height 50 + :alignItems :center}) + +(def import-button-content + {:flex 1 + :flexDirection :row + :height 50 + :alignItems :center + :alignSelf :center}) + +(def import-text + {:flex 1 + :flexDirection :column + :color color-white + :margin-left 8}) \ No newline at end of file diff --git a/src/syng_im/resources.cljs b/src/status_im/resources.cljs similarity index 64% rename from src/syng_im/resources.cljs rename to src/status_im/resources.cljs index 956fd46c28..9ebd25505c 100644 --- a/src/syng_im/resources.cljs +++ b/src/status_im/resources.cljs @@ -1,18 +1,26 @@ -(ns syng-im.resources) +(ns status-im.resources) +(def add-icon (js/require "./images/add.png")) +(def att (js/require "./images/att.png")) +(def chat-icon (js/require "./images/chat.png")) +(def delivered-icon (js/require "./images/delivered.png")) +(def delivery-failed-icon (js/require "./images/deliveryfailed.png")) +(def icon-close-gray (js/require "./images/icon_close_gray.png")) +(def leave-icon (js/require "./images/leave.png")) (def logo-icon (js/require "./images/logo.png")) +(def mic (js/require "./images/mic.png")) (def nav-back-icon (js/require "./images/nav-back.png")) (def user-no-photo (js/require "./images/no-photo.png")) (def online-icon (js/require "./images/online.png")) -(def seen-icon (js/require "./images/seen.png")) -(def delivered-icon (js/require "./images/delivered.png")) -(def delivery-failed-icon (js/require "./images/deliveryfailed.png")) -(def chat-icon (js/require "./images/chat.png")) (def play (js/require "./images/play.png")) -(def mic (js/require "./images/mic.png")) +(def seen-icon (js/require "./images/seen.png")) (def smile (js/require "./images/smile.png")) -(def att (js/require "./images/att.png")) -(def v (js/require "./images/v.png")) -(def add-icon (js/require "./images/add.png")) (def trash-icon (js/require "./images/trash.png")) -(def leave-icon (js/require "./images/leave.png")) +(def v (js/require "./images/v.png")) + +(def contacts + {:auction-house (js/require "./images/contacts/auction-house.png") + :mkr-market (js/require "./images/contacts/mkr-market.png") + :oaken-water-meter (js/require "./images/contacts/oaken-water-meter.png") + :flight-delays-suck (js/require "./images/contacts/flight-delays-suck.png") + :jarrad (js/require "./images/contacts/jarrad.png")}) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs new file mode 100644 index 0000000000..a4b068540a --- /dev/null +++ b/src/status_im/subs.cljs @@ -0,0 +1,34 @@ +(ns status-im.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub subscribe]] + status-im.chat.subs + status-im.group-settings.subs + status-im.discover.subs + status-im.contacts.subs + status-im.new-group.subs + status-im.participants.subs + status-im.transactions.subs)) + +(register-sub :get + (fn [db [_ k]] + (reaction (k @db)))) + +(register-sub :get-current-account + (fn [db [_ _]] + (reaction (let [current-account-id (:current-account-id @db)] + (get-in @db [:accounts current-account-id]))))) + +(register-sub :get-in + (fn [db [_ path]] + (reaction (get-in @db path)))) + +(register-sub :animations + (fn [db [_ k]] + (reaction (get-in @db [:animations k])))) + +(register-sub :signed-up? + (fn [] + (let [account-id (subscribe [:get :current-account-id]) + accounts (subscribe [:get :accounts])] + (reaction (when (and @accounts @account-id) + (get-in @accounts [@account-id :signed-up?])))))) diff --git a/src/status_im/transactions/handlers.cljs b/src/status_im/transactions/handlers.cljs new file mode 100644 index 0000000000..e270a54cd0 --- /dev/null +++ b/src/status_im/transactions/handlers.cljs @@ -0,0 +1,204 @@ +(ns status-im.transactions.handlers + (:require [re-frame.core :refer [after dispatch debug enrich]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.navigation.handlers :as nav] + [status-im.utils.handlers :as u] + [status-im.utils.types :as t] + [status-im.components.status :as status] + [clojure.string :as s] + [taoensso.timbre :as log])) + +;; flow: +;; :accept-transactions +;; ↓ +;; :transaction-completed +;; ↓ +;; ::remove-transaction && [:set :wrong-password? false] <- on error +;; ::remove-transaction <- when transaction is +;; not from the jail +;; ::add-transactions-hash +;; && ::check-completed-transaction! +;; && :navigation-replace <- on success + + +(defmethod nav/preload-data! :confirm + [{:keys [transactions-queue] :as db} _] + (-> db + (assoc :transactions transactions-queue + :wrong-password-counter 0 + :wrong-password? false) + (assoc-in [:confirm-transactions :password] 0))) + +(defn on-unlock + [ids password] + (dispatch [:set :wrong-password? false]) + (doseq [id ids] + (status/complete-transaction + id + password + #(dispatch [:transaction-completed + {:id id + :response %}])))) + +(register-handler :accept-transactions + (u/side-effect! + (fn [{:keys [transactions]} [_ password]] + (let [ids (keys transactions)] + (on-unlock ids password))))) + +(register-handler :deny-transactions + (u/side-effect! + (fn [{:keys [transactions]}] + (let [transactions' (vals transactions) + messages-ids (map :message-id transactions') + ids (map :id transactions')] + (dispatch [::remove-pending-messages messages-ids]) + (dispatch [::remove-transactions ids]) + (doseq [id ids] + (dispatch [::discard-transaction id])) + (dispatch [:navigate-back]))))) + +(register-handler :deny-transaction + (u/side-effect! + (fn [{:keys [transactions]} [_ id]] + (let [{:keys [message-id] :as transaction} (get transactions id)] + (when transaction + (dispatch [::remove-pending-message message-id]) + (dispatch [::remove-transaction id]) + (dispatch [::discard-transaction id])))))) + +(register-handler ::discard-transaction + (u/side-effect! + (fn [db [_ id]] + (status/discard-transaction id)))) + +(register-handler ::remove-transactions + (fn [db [_ hashes]] + (-> db + (dissoc :transactions) + (update :transactions-queue #(apply dissoc % hashes))))) + +(register-handler ::remove-transaction + (after (fn [{:keys [modal]}] + (when (= :confirm modal) + (dispatch [:navigate-back])))) + (fn [db [_ hash]] + (-> db + (update :transactions dissoc hash) + (update :transactions-queue dissoc hash)))) + +(register-handler :wait-for-transaction + (after (fn [_ [_ message-id]] + (dispatch [::check-completed-transaction! + {:message-id message-id}]))) + (fn [db [_ message-id params]] + (assoc-in db [:transaction-subscribers message-id] params))) + +(defn remove-pending-message + [{:keys [command->chat] :as db} message-id] + (let [chat-id (get command->chat message-id)] + (if chat-id + (update db :transaction-subscribers dissoc message-id) + db))) + +(register-handler ::remove-pending-messages + (fn [db [_ ids]] + (log/debug :message-ids ids) + (reduce remove-pending-message db ids))) + +(register-handler ::remove-pending-message + (fn [db [_ message-id]] + (remove-pending-message db message-id))) + +(register-handler :transaction-queued + (u/side-effect! + (fn [_ [_ {:keys [id args] :as transaction}]] + (if (:to args) + (dispatch [::transaction-queued transaction]) + (status/discard-transaction id))))) + +(register-handler ::transaction-queued + (after #(dispatch [:navigate-to-modal :confirm])) + (fn [db [_ {:keys [id message_id args]}]] + (let [{:keys [from to value]} args + transaction {:id id + :from from + :to to + :value (.toDecimal js/Web3.prototype value) + :message-id message_id}] + (assoc-in db [:transactions-queue id] transaction)))) + +(register-handler :transaction-completed + (u/side-effect! + (fn [{:keys [transactions]} [_ {:keys [id response]}]] + (let [{:keys [hash error] :as parsed-response} (t/json->clj response) + {:keys [message-id]} (transactions id)] + (log/debug :parsed-response parsed-response) + (when-not (and error (string? error) (not (s/blank? error))) + (if (and message-id (not (s/blank? message-id))) + (do (dispatch [::add-transactions-hash {:id id + :hash hash + :message-id message-id}]) + (dispatch [::check-completed-transaction! + {:message-id message-id}])) + (dispatch [::remove-transaction id]))))))) + +(register-handler ::add-transactions-hash + (fn [db [_ {:keys [id hash message-id]}]] + (-> db + (assoc-in [:transactions id :hash] hash) + (assoc-in [:message-id->transaction-id message-id] id)))) + +(register-handler ::send-pending-message + (u/side-effect! + (fn [{:keys [transaction-subscribers]} [_ message-id hash]] + (when-let [{:keys [chat-id] :as params} (transaction-subscribers message-id)] + (let [params' (assoc-in params [:handler-data :transaction-hash] hash)] + (dispatch [:prepare-command! chat-id params'])) + (dispatch [::remove-transaction-subscriber message-id]))))) + +(register-handler ::remove-transaction-subscriber + (fn [db [_ old-hash]] + (update db :transaction-subscribers dissoc old-hash))) + +(register-handler ::check-completed-transaction! + (u/side-effect! + (fn [{:keys [message-id->transaction-id transactions transaction-subscribers]} + [_ {:keys [message-id]}]] + (let [id (get message-id->transaction-id message-id) + {:keys [hash]} (get transactions id) + pending-message (get transaction-subscribers message-id)] + (when (and pending-message id hash) + (dispatch [::send-pending-message message-id hash]) + (dispatch [::remove-transaction id])))))) + +(def wrong-password-code "2") +(def discard-code "4") + +(register-handler :transaction-failed + (u/side-effect! + (fn [_ [_ {:keys [id message_id error_code]}]] + (cond + + (= error_code wrong-password-code) + (dispatch [:set-wrong-password!]) + + (not= discard-code error_code) + (do (when message_id + (dispatch [::remove-pending-message message_id])) + (dispatch [::remove-transaction id])) + + :else nil)))) + +(def attempts-limit 3) + +(register-handler :set-wrong-password! + (after (fn [{:keys [wrong-password-counter]}] + (when (>= wrong-password-counter attempts-limit) + (dispatch [:set :wrong-password? false]) + (dispatch [:set :wrong-password-counter 0]) + (dispatch [:set-in [:confirm-transactions :password] ""])))) + (fn [db] + (-> db + (assoc :wrong-password? true) + (update :wrong-password-counter (fnil inc 0))))) diff --git a/src/status_im/transactions/screen.cljs b/src/status_im/transactions/screen.cljs new file mode 100644 index 0000000000..25374611f5 --- /dev/null +++ b/src/status_im/transactions/screen.cljs @@ -0,0 +1,61 @@ +(ns status-im.transactions.screen + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] + [status-im.components.styles :refer [icon-ok + icon-close]] + [status-im.components.carousel.carousel :refer [carousel]] + [status-im.components.status-bar :refer [status-bar]] + [status-im.components.toolbar.view :refer [toolbar]] + [status-im.components.toolbar.styles :refer [toolbar-title-container]] + [status-im.components.text-field.view :refer [text-field]] + [status-im.transactions.views.transaction-page :refer [transaction-page]] + [status-im.transactions.styles :as st] + [status-im.i18n :refer [label label-pluralize]] + [clojure.string :as s])) + + +(defview confirm [] + [transactions [:transactions] + {:keys [password]} [:get :confirm-transactions] + wrong-password? [:wrong-password?]] + [view st/transactions-screen + [status-bar {:type :transparent}] + [toolbar + {:style st/transactions-toolbar + :nav-action {:image {:source {:uri :icon_close_white} + :style icon-close} + :handler #(dispatch [:deny-transactions])} + :custom-content [view {:style toolbar-title-container} + [text {:style st/toolbar-title-text} + (label-pluralize (count transactions) :t/confirm-transactions)]] + :actions [{:image {:source {:uri (if-not (s/blank? password) + :icon_ok + :icon_ok_disabled_inversed)} + :style icon-ok} + :handler #(dispatch [:accept-transactions password])}]}] + [view st/carousel-container + [carousel {:pageStyle st/carousel-page-style + :gap 8 + :sneak 16 + :count (count transactions)} + (when transactions + (for [transaction transactions] + [transaction-page transaction]))]] + [view st/form-container + [text-field + {:editable true + :error (when wrong-password? (label :t/wrong-password)) + :error-color :#ffffff80 #_:#7099e6 + :label (label :t/password) + :secure-text-entry true + :label-color :#ffffff80 + :line-color :white + :input-style st/password-style + :on-change-text #(dispatch [:set-in [:confirm-transactions :password] %])}]]]) diff --git a/src/status_im/transactions/styles.cljs b/src/status_im/transactions/styles.cljs new file mode 100644 index 0000000000..f904fbe31e --- /dev/null +++ b/src/status_im/transactions/styles.cljs @@ -0,0 +1,103 @@ +(ns status-im.transactions.styles) + +(def transactions-screen + {:flex 1 + :backgroundColor "#828b92"}) + +(def transactions-toolbar + {:backgroundColor "#828b92" + :elevation 0}) + +(def toolbar-title-text + {:color :white + :fontSize 16}) + +(def carousel-page-style + {}) + +(def form-container + {:flex 1 + :paddingLeft 16}) + +(def password-style + {:color :white + :font-size 12}) + +;transaction-page + +(def transaction-page + {:flex 1 + :backgroundColor "#f3f4f4"}) + +(def title-bar + {:backgroundColor :white + :height 39 + :justifyContent :center}) + +(def title-bar-text + {:color "#838c93" + :font-size 13 + :margin-left 12 + :margin-right 30}) + +(def icon-close-container + {:position :absolute + :right 12 + :top 13}) + +(def icon-close + {:width 12 + :height 12}) + +(def transaction-info-container + {:flex 1 + :paddingTop 6}) + +(def scroll-view-container + {:flex 1}) + +(def scroll-view + {:flex 1 + :height 175}) + +(def scroll-view-content + {:paddingVertical 6}) + +(def transaction-info-row + {:flex 1 + :flexDirection :row}) + +(def transaction-info-column-title + {:flex 0.4 + :flexDirection :column + :paddingHorizontal 6}) + +(def transaction-info-column-value + {:flex 0.6 + :flexDirection :column + :paddingHorizontal 6}) + +(def transaction-info-item + {:flex 1 + :padding 6}) + +(def transaction-info-title + {:textAlign :right + :color "#838c93de" + :fontSize 14 + :lineHeight 20}) + +(def transaction-info-value + {:color "#000000de" + :fontSize 14 + :lineHeight 20 +}) + +(def scroll-view-item + {:flex 1 + :height 20 + :padding 6 }) + +(def carousel-container + {:min-height 215 + :flex 1}) diff --git a/src/status_im/transactions/subs.cljs b/src/status_im/transactions/subs.cljs new file mode 100644 index 0000000000..43c1e7c8ea --- /dev/null +++ b/src/status_im/transactions/subs.cljs @@ -0,0 +1,27 @@ +(ns status-im.transactions.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub subscribe]] + [clojure.string :as s] + [status-im.utils.hex :as i])) + +(register-sub :transactions + (fn [db] + (reaction (vals (:transactions @db))))) + +(register-sub :contacts-by-address + (fn [db] + (reaction (into {} (map (fn [[_ {:keys [address] :as contact}]] + [address contact]) + + (:contacts @db) + ))))) + +(register-sub :contact-by-address + (fn [_ [_ address]] + (let [contacts (subscribe [:contacts-by-address]) + address' (when address + (i/normalize-hex address))] + (reaction (@contacts address'))))) + +(register-sub :wrong-password? + (fn [db] (reaction (:wrong-password? @db)))) diff --git a/src/status_im/transactions/views/transaction_page.cljs b/src/status_im/transactions/views/transaction_page.cljs new file mode 100644 index 0000000000..7176103083 --- /dev/null +++ b/src/status_im/transactions/views/transaction_page.cljs @@ -0,0 +1,52 @@ +(ns status-im.transactions.views.transaction-page + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.components.react :refer [view + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] + [status-im.components.styles :refer [icon-ok + icon-close]] + [status-im.transactions.styles :as st] + [status-im.i18n :refer [label label-pluralize]] + [taoensso.timbre :as log])) + +(defn title-bar [title id] + [view st/title-bar + [text {:style st/title-bar-text + :font :medium + :number-of-lines 1} + title] + [touchable-highlight {:style st/icon-close-container + :on-press #(dispatch [:deny-transaction id])} + [view [image {:source {:uri :icon_close_gray} + :style st/icon-close}]]]]) + +(defn transaction-info [index [name value]] + [view {:style st/transaction-info-item + :key index} + [view {:style st/transaction-info-row} + [view st/transaction-info-column-title + [text {:style st/transaction-info-title} name]] + [view st/transaction-info-column-value + [text {:style st/transaction-info-value} value]]]]) + +(defview transaction-page [{:keys [id from to value] :as transaction}] + [{:keys [name] :as contact} [:contact-by-address to]] + (let [eth-value (.fromWei js/Web3.prototype value "ether") + title (str eth-value " ETH to " (or name to)) + transactions-info [[(label :t/status) (label :t/pending-confirmation)] + [(label :t/recipient) (or name to)] + [(label :t/value) (str eth-value " ETH")]]] + [view {:style st/transaction-page + :key id} + [title-bar title id] + [view st/scroll-view-container + [scroll-view {:style st/scroll-view + :contentContainerStyle st/scroll-view-content + :showsVerticalScrollIndicator true + :scrollEnabled true} + (map-indexed transaction-info transactions-info)]]])) diff --git a/src/status_im/translations/af.cljs b/src/status_im/translations/af.cljs new file mode 100644 index 0000000000..497333133d --- /dev/null +++ b/src/status_im/translations/af.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.af) + +(def translations + { + ;common + :members-title "lede" + :not-implemented "!nie geimplimenteer nie" + :chat-name "Bynaam" + :notifications-title "Kennisgewings en klanke" + :offline "Aflyn" + + ;drawer + :invite-friends "Nooi vriende" + :faq "Vrae" + :switch-users "Verander gebruikers" + + ;chat + :is-typing "is besig om te tik" + :and-you "en jy" + :search-chat "Deursoek geselsies" + :members {:one "1 lid" + :other "{{count}} lede" + :zero "geen lede"} + :members-active {:one "1 lid, 1 aktief" + :other "{{count}} lede, {{count}} aktief" + :zero "geen lede"} + :active-online "Aanlyn" + :active-unknown "Onbekend" + :available "Beskikbaar" + :no-messages "Geen boodskappe" + :suggestions-requests "Versoeke" + :suggestions-commands "Opdragte" + + ;sync + :sync-in-progress "Besig om te sinchroniseer..." + :sync-synced "gesinchroniseer" + + ;messages + :status-sending "Besig om te stuur" + :status-pending "Hangende" + :status-sent "Gestuur" + :status-seen-by-everyone "Deur almal gesien" + :status-seen "Gesien" + :status-delivered "Afgelewer" + :status-failed "Gefaal" + + ;datetime + :datetime-second {:one "sekonde" + :other "sekondes"} + :datetime-minute {:one "minuut" + :other "minute"} + :datetime-hour {:one "uur" + :other "ure"} + :datetime-day {:one "dag" + :other "dae"} + :datetime-multiple "e" ; TODO probably wrong + :datetime-ago "gelede" + :datetime-yesterday "gister" + :datetime-today "vandag" + + ;profile + :profile "Profiel" + :report-user "RAPPORTEER GEBRUIKER" + :message "Boodskap" + :username "Gebruikernaam" + :not-specified "Nie gespesifiseer nie" + :public-key "Openbare sleutel" + :phone-number "Telefoonnommer" + :email "Epos" + :profile-no-status "Geen status" + :add-to-contacts "Voeg by kontakte" + :error-incorrect-name "Kies asseblief 'n ander naam" + :error-incorrect-email "Verkeerde epos" + + ;;make_photo + :image-source-title "Profielbeeld" + :image-source-make-photo "Opname" + :image-source-gallery "Kies uit galery" + :image-source-cancel "Kanselleer" + + ;sign-up + :contacts-syncronized "U kontakte is gesinchroniseer" + :confirmation-code (str "Dankie! Ons het vir u 'n SMS gestuur met 'n bevestigings " + "kode. Verskaf asseblief daardie kode om u telefoonnommer te bevestig") + :incorrect-code (str "Jammer, die kode was nie korrek nie, voer asseblief weer in") + :generate-passphrase (str "Ek sal 'n tydelike wagwoord vir jou skep sodat jy jou " + "toegang kan herstel of van 'n ander toestel af kan aanteken") + :phew-here-is-your-passphrase "*Sjoe* dit was moeilik, hier is jou tydelike wagwoord, *skryf dit neer en hou dit veilig!* Jy sal dit nodig hê om jou rekening te herwin." + :here-is-your-passphrase "Hier is jou tydelike wagwoord, *skryf dit neer en hou dit veilig!* Jy sal dit nodig hê om jou rekening te herwin." + :written-down "Maak seker dat jy dit veilig neergeskryf het" + :phone-number-required "Tik hier om jou telefoonnommer in te voer & ek sal jou vriende opspoor" + :intro-status "Gesels met my om jou rekening op te stel en jou stellings te verander!" + :intro-message1 "Welkom by Status\nTik tik hierdie boodskap om jou wagwoord te stel & laat ons begin!" + :account-generation-message "Net 'n oomblik, ek moet mal somme doen om jou rekening te skep!" + + ;chats + :chats "Geselsies" + :new-chat "Nuwe geselsie" + :new-group-chat "Nuwe groepgeselsie" + + ;discover + :discover "Ontdekking" + :none "Geen" + :search-tags "Tik jou soek-oortjies hier in" + :popular-tags "Gewilde oortjies" + :recent "Onlangs" + :no-statuses-discovered "Geen statusse gevind nie" + + ;settings + :settings "Stellings" + + ;contacts + :contacts "Kontakte" + :new-contact "Nuwe kontak" + :show-all "WYS ALMAL" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Mense" + :contacts-group-new-chat "Begin nuwe geselsie" + :no-contacts "Nog geen kontakte nie" + :show-qr "Wys QR" + + ;group-settings + :remove "Verwyder" + :save "Stoor" + :change-color "Verander kleur" + :clear-history "Maak geskiedenis skoon" + :delete-and-leave "Vee uit en gaan uit" + :chat-settings "Geselsie-stellings" + :edit "Wysig" + :add-members "Voeg lede by" + :blue "Blou" + :purple "Pers" + :green "Groen" + :red "Rooi" + + ;commands + :money-command-description "Stuur geld" + :location-command-description "Stuur ligging" + :phone-command-description "Stuur telefoonnommer" + :phone-request-text "Telefoonnommer-versoek" + :confirmation-code-command-description "Stuur bevestigingskode" + :confirmation-code-request-text "Bevestigingskode-versoek" + :send-command-description "Stuur ligging" + :request-command-description "Stuur versoek" + :keypair-password-command-description "" + :help-command-description "Help" + :request "Versoek" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH aan {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH van {{chat-name}}" + :command-text-location "Ligging {{address}}" + :command-text-browse "Webblaaier-blad: {{webpage}}" + :command-text-send "Transaksie: {{amount}} ETH" + :command-text-help "Help" + + ;new-group + :group-chat-name "Bynaam" + :empty-group-chat-name "Voer assseblief 'n naam in" + :illegal-group-chat-name "Kies asseblief 'n ander naam" + + ;participants + :add-participants "Voeg deelnemers by" + :remove-participants "Verwyder deelnemers" + + ;protocol + :received-invitation "geselsie-uitnodiging ontvang" + :removed-from-chat "het jou van groepsgeselsie verwyder" + :left "uitgegaan" + :invited "uitgenooi" + :removed "verwyder" + :You "Jou" + + ;new-contact + :add-new-contact "Voeg nuwe kontak by" + :import-qr "Voer in" + :scan-qr "Skandeer QR" + :name "Naam" + :whisper-identity "Whisper-identiteit" + :address-explication "Miskien moet hier 'n bietjie teks wees wat verduidelik wat 'n adres is en waar om daarvoor te soek." + :enter-valid-address "Voer asseblief 'n geldige adres in of skandeer 'n QR-kode" + :contact-already-added "Die kontak is alreeds bygevoeg" + :can-not-add-yourself "Jy kan nie jouself byvoeg nie" + :unknown-address "Onbekende adres" + + + ;login + :connect "Konnekteer" + :address "Adres" + :password "Wagwoord" + :login "Teken aan" + :wrong-password "Verkeerde wagwoord" + + ;recover + :recover-from-passphrase "Herstel van tydelike wagwoord" + :recover-explain "Voer asseblief die tydelike wagwoord vir jou wagwoord in om toegang te herstel" + :passphrase "Tydelike wagwoord" + :recover "Herstel" + :enter-valid-passphrase "Voer asseblief 'n tydelike wagwoord in" + :enter-valid-password "Voer asseblief 'n wagwoord in" + + ;accounts + :recover-access "Herwin toegang" + :add-account "Voeg rekening by" + + ;wallet-qr-code + :done "Gedoen" + :main-wallet "Hoofbeursie" + + ;validation + :invalid-phone "Ongeldige telefoonnommer" + :amount "Bedrag" + :not-enough-eth (str "Nie genoeg ETH in die rekening nie " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Bevestig transaksie" + :other "Bevestig {{count}} transaksies" + :zero "Geen transaksies"} + :status "Status" + :pending-confirmation "Bevestiging hangende" + :recipient "Ontvanger" + :one-more-item "Nog een item" + :fee "Fooi" + :value "Waarde" + + ;:webview + :web-view-error "oepsie, fout"}) diff --git a/src/status_im/translations/ar.cljs b/src/status_im/translations/ar.cljs new file mode 100644 index 0000000000..1f571a7f4e --- /dev/null +++ b/src/status_im/translations/ar.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.ar) + +(def translations + { + ;common + :members-title "الأعضاء" + :not-implemented "!غير مُطَبّق" + :chat-name "اسم الدردشة" + :notifications-title "الإخطارات والأصوات" + :offline "غير متصل" + + ;drawer + :invite-friends "دعوة الأصدقاء" + :faq "الأسئلة الشائعة" + :switch-users "التبديل بين المستخدمين" + + ;chat + :is-typing "يكتب" + :and-you "وأنت أيضاً" + :search-chat "البحث في الدردشة" + :members {:one "1 عضو" + :other "{{count}} أعضاء" + :zero "لا يوجد أعضاء"} + :members-active {:one "1 عضو، 1 نشط" + :other "{{count}} أعضاء، {{count}} نشط" + :zero "لا يوجد أعضاء"} + :active-online "متصل بالانترنت" + :active-unknown "غير معروف" + :available "متاح" + :no-messages "لا توجد رسائل" + :suggestions-requests "الطلبات" + :suggestions-commands "الأوامر" + + ;sync + :sync-in-progress "قيد المزامنة..." + :sync-synced "بالتزامن" + + ;messages + :status-sending "إرسال" + :status-pending "ريثما" + :status-sent "تم الإرسال" + :status-seen-by-everyone "شوهد من قبل الجميع" + :status-seen "شوهد" + :status-delivered "تم الاستلام" + :status-failed "فشل" + + ;datetime + :datetime-second {:one "ثانية" + :other "ثوان"} + :datetime-minute {:one "دقيقة" + :other "دقائق"} + :datetime-hour {:one "ساعة" + :other "ساعات"} + :datetime-day {:one "يوم" + :other "أيام"} + :datetime-multiple "ث" ; TODO + :datetime-ago "منذ" + :datetime-yesterday "الأمس" + :datetime-today "اليوم" + + ;profile + :profile "الملف الشخصي" + :report-user "الإبلاغ عن مستخدم" + :message "الرسالة" + :username "اسم المستخدم" + :not-specified "غير محدد" + :public-key "المفتاح العمومي" + :phone-number "رقم الهاتف" + :email "البريد الإلكتروني" + :profile-no-status "لا توجد حالة" + :add-to-contacts "أضف إلى جهات الاتصال" + :error-incorrect-name "الرجاء اختيار اسم آخر" + :error-incorrect-email "بريد إلكتروني غير صحيح" + + ;;make_photo + :image-source-title "الصورة الشخصية" + :image-source-make-photo "التقاط صورة" + :image-source-gallery "الاختيار من معرض الصور" + :image-source-cancel "إلغاء" + + ;sign-up + :contacts-syncronized "تمت مزامنة جهات الاتصال الخاصة بك" + :confirmation-code (str "شكراً! لقد أرسلنا لك رسالة نصية تتضمن رمز تأكيد" + "يرجى إدخال هذا الرمز لتأكيد رقم هاتفك") + :incorrect-code (str "عذراً الرمز غير صحيح، يرجى إدخاله مرة أخرى") + :generate-passphrase (str "سوف أقوم بإنشاء عبارة مرور لك حتى تتمكن من " + "الوصول أو الدخول ثانية من جهاز آخر") + :phew-here-is-your-passphrase "*أف* لقد كان أمراً شاقاً، إليك عبارة المرور الخاصة بك، * قم بتسجيلها واحتفظ بها في مكان آمن!* سوف تحتاج إليها لاسترداد حسابك." + :here-is-your-passphrase "إليك عبارة المرور الخاصة بك، * قم بتسجيلها واحتفظ بها في مكان آمن! *سوف تحتاج إليها لاسترداد حسابك" + :written-down "تأكد بأنك قد دونتها بشكل آمن" + :phone-number-required "اضغط هنا لإدخال رقم الهاتف الخاص بك وسوف تعثر على أصدقائك" + :intro-status "دردش معي لتثبيت حسابك وقم بتغيير الإعدادات الخاصة بك!" + :intro-message1 "مرحبا بك في Status \n اضغط على هذه الرسالة لتعيين كلمة المرور الخاصة بك وابدأ!" + :account-generation-message "امنحني ثانية واحدة، سوف أحتاج إلى إجراء بعض الحسابات الرياضية المجنونة لإنتاج الحساب الخاص بك!" + + ;chats + :chats "الدردشات" + :new-chat "دردشة جديدة" + :new-group-chat "مجموعة دردشة جديدة" + + ;discover + :discover "اكتشاف" + :none "لا شيء" + :search-tags "اكتب بيانات بحثك هنا" + :popular-tags "العلامات المشهورة" + :recent "حديثة" + :no-statuses-discovered "لم يتم الكشف عن حالات" + + ;settings + :settings "الإعدادات" + + ;contacts + :contacts "جهات الاتصال" + :new-contact "جهة اتصال جديدة" + :show-all "عرض الكل" + :contacts-group-dapps "ÐApps" + :contacts-group-people "الناس" + :contacts-group-new-chat "ابدأ دردشة جديدة" + :no-contacts "لا توجد جهات اتصال بعد" + :show-qr "عرض شفرة التعريف" + + ;group-settings + :remove "إزالة" + :save "حفظ" + :change-color "تغيير اللون" + :clear-history "حذف التاريخ" + :delete-and-leave "الحذف والمغادرة" + :chat-settings "إعدادات الدردشة" + :edit "تحرير" + :add-members "إضافة أعضاء" + :blue "أزرق" + :purple "أرجواني" + :green "أخضر" + :red "أحمر" + + ;commands + :money-command-description "إرسال الأموال" + :location-command-description "إرسال الموقع" + :phone-command-description "إرسال رقم الهاتف" + :phone-request-text "طلب رقم الهاتف" + :confirmation-code-command-description "إرسال رمز التأكيد" + :confirmation-code-request-text "طلب رمز التأكيد" + :send-command-description "إرسال الموقع" + :request-command-description "إرسال طلب" + :keypair-password-command-description "" + :help-command-description "المساعدة" + :request "طلب" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH إلى {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH من {{chat-name}}" + :command-text-location "الموقع {{address}}" + :command-text-browse "تصفح صفحة الويب {{webpage}}" + :command-text-send "المعاملة: {{amount}} ETH" + :command-text-help "المساعدة" + + ;new-group + :group-chat-name "اسم الدردشة" + :empty-group-chat-name "الرجاء إدخال اسم" + :illegal-group-chat-name "الرجاء اختيار اسم آخر" + + ;participants + :add-participants "إضافة مشاركين" + :remove-participants "إزالة مشاركين" + + ;protocol + :received-invitation "تسلم دعوة الدردشة" + :removed-from-chat "قام بإزالتك من مجموعة الدردشة" + :left "غادر" + :invited "مَدْعُوّ" + :removed "مُسْتَبْعَد" + :You "أنت" + + ;new-contact + :add-new-contact "إضافة جهة اتصال جديدة" + :import-qr "جلب" + :scan-qr "مسح شفرة التعريف" + :name "اسم" + :whisper-identity "Whisper Identity" + :address-explication "ربما يتعين أن تتوافر هنا بعض النصوص التي تشرح ما هو العنوان وأين تبحث عنه" + :enter-valid-address "الرجاء إدخال عنوان صحيح أو قم بمسح شفرة التعريف" + :contact-already-added "تمت إضافة جهة الاتصال" + :can-not-add-yourself "لا يمكنك إضافة نفسك" + :unknown-address "عنوان غير معروف" + + + ;login + :connect "اتصال" + :address "عنوان" + :password "كلمة مرور" + :login "تسجيل الدخول" + :wrong-password "كلمة مرور خاطئة" + + ;recover + :recover-from-passphrase "استرداد بواسطة عبارة المرور" + :recover-explain "الرجاء إدخال عبارة المرور الخاصة بكلمة المرور الخاصة بك لاستعادة إمكانية الوصول" + :passphrase "عبارة المرور" + :recover "استعادة" + :enter-valid-passphrase "الرجاء إدخال عبارة المرور" + :enter-valid-password "الرجاء إدخال كلمة المرور" + + ;accounts + :recover-access "استعادة إمكانية الوصول" + :add-account "إضافة حساب" + + ;wallet-qr-code + :done "تم التنفيذ" + :main-wallet "المحفظة الرئيسية" + + ;validation + :invalid-phone "رقم هاتف غير صحيح" + :amount "الكمية" + :not-enough-eth (str "لا يوجدETH كافي بالحساب " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "تأكيد المعاملة" + :other "تأكيد {{count}} معاملات" + :zero "لا توجد معاملات"} + :status "الحالة" + :pending-confirmation "في انتظار التأكيد" + :recipient "المستلم" + :one-more-item "بند واحد آخر" + :fee "الرسوم" + :value "القيمة" + + ;:webview + :web-view-error "عفواً، حدث خطأ ما"}) diff --git a/src/status_im/translations/de.cljs b/src/status_im/translations/de.cljs new file mode 100644 index 0000000000..458e2ea51a --- /dev/null +++ b/src/status_im/translations/de.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.de) + +(def translations + { + ;common + :members-title "Mitglieder" + :not-implemented "!nicht implementiert" + :chat-name "Chatname" + :notifications-title "Benachrichtigungen und Sounds" + :offline "Offline" + + ;drawer + :invite-friends "Freunde einladen" + :faq "FAQ" + :switch-users "Benutzer wechseln" + + ;chat + :is-typing "gibt ein" + :and-you "und Sie" + :search-chat "Chat durchsuchen" + :members {:one "1 Mitglied" + :other "{{count}} Mitglieder" + :zero "keine Mitglieder"} + :members-active {:one "1 Mitglied, 1 aktiv" + :other "{{count}} Mitglieder, {{count}} aktiv" + :zero "keine Mitglieder"} + :active-online "Online" + :active-unknown "Unbekannt" + :available "Verfügbar" + :no-messages "Keine Nachrichten" + :suggestions-requests "Anfragen" + :suggestions-commands "Befehle" + + ;sync + :sync-in-progress "Synchronisiere..." + :sync-synced "Synchronisiert" + + ;messages + :status-sending "Absenden" + :status-pending "Anhängig" + :status-sent "Verschickt" + :status-seen-by-everyone "Von allen gesehen" + :status-seen "Gesehen" + :status-delivered "Zugestellt" + :status-failed "Fehlgeschlagen" + + ;datetime + :datetime-second {:one "Sekunde" + :other "Sekunden"} + :datetime-minute {:one "Minute" + :other "Minuten"} + :datetime-hour {:one "Stunde" + :other "Stunden"} + :datetime-day {:one "Tag" + :other "Tage"} + :datetime-multiple "s" + :datetime-ago "vor" + :datetime-yesterday "Gestern" + :datetime-today "Heute" + + ;profile + :profile "Profil" + :report-user "BENUTZER MELDEN" + :message "Nachricht" + :username "Benutzername" + :not-specified "Nicht angegeben" + :public-key "Öffentlicher Schlüssel" + :phone-number "Telefonnumer" + :email "E-Mail" + :profile-no-status "Kein Status" + :add-to-contacts "Zu Kontakten hinzufügen" + :error-incorrect-name "Bitte wählen Sie einen anderen Namen" + :error-incorrect-email "Inkorrekte E-Mail" + + ;;make_photo + :image-source-title "Profilfoto" + :image-source-make-photo "Fotografieren" + :image-source-gallery "Aus Galarie auswählen" + :image-source-cancel "Abbrechen" + + ;sign-up + :contacts-syncronized "Ihre Kontakte wurden synchronisiert" + :confirmation-code (str "Danke! Wir haben Ihnen eine Textnachricht mit einer Bestätigungscode" + "geschickt. Bitte geben Sie den Code ein, damit wir Ihre Telefonnummer verifizieren können") + :incorrect-code (str "Tut uns leid, der Code war nicht korrekt, bitte erneut eingeben") + :generate-passphrase (str "Ich werde eine Passphrase für Sie generieren, damit Sie Ihren" + "Zugriff wiederherstellen oder sich von einem anderen Gerät aus einloggen können") + :phew-here-is-your-passphrase "*Puh* das war schwer, hier ist Ihr Passphrase, *bitte aufschreiben und sicher aufbewahren!*Sie benötigen dies, um Ihren Account wiederherzustellen." + :here-is-your-passphrase "Hier ist Ihr Passphrase, *bitte aufschreiben und sicher verwahren!*Sie benötigen dies, um Ihren Account wiederherzustellen." + :written-down "Stellen Sie sicher, dass Sie es sicher aufgeschrieben haben" + :phone-number-required "Tippen Sie hier, um Ihre Telefonnummer einzugeben & ich werde Ihre Freunde finden" + :intro-status "Chatten Sie mit mir, um Ihren Account einzurichten und Ihre Einstellungen zu ändern!" + :intro-message1 "Willkommen beim Status\nTippen Sie auf diese nachricht, um Ihr Passwort einzurichten und loszulegen!" + :account-generation-message "Eine Sekunde, ich muss wahnsinnig schwere Mathematik machen, um Ihr Konto zu generieren!" + + ;chats + :chats "Chats" + :new-chat "Neuer Chat" + :new-group-chat "Neuer Gruppenchat" + + ;discover + :discover "Entdeckung" + :none "Nichts" + :search-tags "Geben Sie Ihre Suchbegriffe hier ein" + :popular-tags "Beliebte Begriffe" + :recent "Kürzlich" + :no-statuses-discovered "Kein Status gefunden" + + ;settings + :settings "Einstellungen" + + ;contacts + :contacts "Kontakte" + :new-contact "Neuer Kontakt" + :show-all "ALLE ANZEIGEN" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Leute" + :contacts-group-new-chat "Neuen Chat beginnen" + :no-contacts "Noch keine Kontakte" + :show-qr "QR anzeigen" + + ;group-settings + :remove "Entfernen" + :save "Speichern" + :change-color "Farbe ändern" + :clear-history "Verlauf löschen" + :delete-and-leave "Löschen und verlassen" + :chat-settings "Chateinstellungen" + :edit "Bearbeiten" + :add-members "Mitglieder hinzufügen" + :blue "Blau" + :purple "Lila" + :green "Grün" + :red "Rot" + + ;commands + :money-command-description "Geld senden" + :location-command-description "Position senden" + :phone-command-description "Telefonnummer senden" + :phone-request-text "Telefonnummer anfragen" + :confirmation-code-command-description "Bestätitungscode senden" + :confirmation-code-request-text "Bestätigungscode anfragen" + :send-command-description "Position senden" + :request-command-description "Anfrage senden" + :keypair-password-command-description "" + :help-command-description "Hilfe" + :request "Anfrage" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH an {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH von {{chat-name}}" + :command-text-location "Position: {{address}}" + :command-text-browse "Auf Webseite: {{webpage}}" + :command-text-send "Transaktion: {{amount}} ETH" + :command-text-help "Hilfe" + + ;new-group + :group-chat-name "Chatname" + :empty-group-chat-name "Bitte geben Sie einen Namen ein" + :illegal-group-chat-name "Bitte wählen Sie einen anderen Namen" + + ;participants + :add-participants "Teilnehmer hinzufügen" + :remove-participants "Teilnehmer entfernen" + + ;protocol + :received-invitation "Chateinladung erhalten" + :removed-from-chat "Aus dem Gruppenchat entfernt" + :left "Links" + :invited "Eingeladen" + :removed "Entfernt" + :You "Sie" + + ;new-contact + :add-new-contact "Neuen Kontakt hinzufügen" + :import-qr "Import" + :scan-qr "QR-Code scannen" + :name "Name" + :whisper-identity "Identität Flüstern" + :address-explication "Vielleicht sollte hier ein Text stehen, der erklärt, was eine Adresse ist und wo man danach sucht" + :enter-valid-address "Bitte geben Sie eine gültige Adresse ein oder scannen Sie einen QR-Code" + :contact-already-added "Dieser Kontakt wurde bereits hinzugefügt" + :can-not-add-yourself "Sie können sich selbst nicht hinzufügen" + :unknown-address "Unbekannte Adresse" + + + ;login + :connect "Verbinden" + :address "Adresse" + :password "Passwort" + :login "Login" + :wrong-password "Falsches Passwort" + + ;recover + :recover-from-passphrase "Via Passphrase wiederherstellen" + :recover-explain "Bitte geben Sie den Passphrase für Ihr Passwort ein, um den Zugriff wiederherzustellen" + :passphrase "Passphrase" + :recover "Wiederherstellen" + :enter-valid-passphrase "Bitte geben Sie einen Passphrase ein" + :enter-valid-password "Bitte geben Sie ein Passwort ein" + + ;accounts + :recover-access "Zugriff wiederherstellen" + :add-account "Konto hinzufügen" + + ;wallet-qr-code + :done "Fertig" + :main-wallet "Haupt-Konto" + + ;validation + :invalid-phone "Ungültige Telefonnummer" + :amount "Betrag" + :not-enough-eth (str "Nicht genug EHT auf dem Konto" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Transaktion bestätigen" + :other "{{count}} Transaktionen bestätigen" + :zero "Keine Transaktionen"} + :status "Status" + :pending-confirmation "Bestätigung ausstehend" + :recipient "Empfänger" + :one-more-item "Noch ein Objekt" + :fee "Gebühr" + :value "Wert" + + ;:webview + :web-view-error "Ups, Fehler"}) diff --git a/src/status_im/translations/de_ch.cljs b/src/status_im/translations/de_ch.cljs new file mode 100644 index 0000000000..a9a74d2f08 --- /dev/null +++ b/src/status_im/translations/de_ch.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.de-ch) + +(def translations + { + ;common + :members-title "Mitglieder" + :not-implemented "!nicht implementiert" + :chat-name "Chat Name" + :notifications-title "Notifikationen and Klänge" + :offline "Offline" + + ;drawer + :invite-friends "Lade Freunde ein" + :faq "FAQ" + :switch-users "Benutzer wechseln" + + ;chat + :is-typing "tippt" + :and-you "und du" + :search-chat "Suche Chat" + :members {:one "1 Mitglied" + :other "{{count}} Mitglieder" + :zero "Keine Mitglieder"} + :members-active {:one "1 Mitglied, 1 aktiv" + :other "{{count}} Mitglieder, {{count}} aktiv" + :zero "Keine Mitglieder"} + :active-online "Online" + :active-unknown "Unbekannt" + :available "Verfügbar" + :no-messages "Keine Nachrichten" + :suggestions-requests "Fragt an" + :suggestions-commands "Kommandiert" + + ;sync + :sync-in-progress "Synchronisierung..." + :sync-synced "Synchron" + + ;messages + :status-sending "Senden" + :status-pending "Anhängig" + :status-sent "Gesendet" + :status-seen-by-everyone "Von allen gesehen" + :status-seen "Gesehen" + :status-delivered "Geliefert" + :status-failed "Fehlgeschlagen" + + ;datetime + :datetime-second {:one "Sekunde" + :other "Sekunden"} + :datetime-minute {:one "Minute" + :other "Minuten"} + :datetime-hour {:one "Stunde" + :other "Stunden"} + :datetime-day {:one "Tag" + :other "Tage"} + :datetime-multiple "s" + :datetime-ago "vor" + :datetime-yesterday "gestern" + :datetime-today "heute" + + ;profile + :profile "Profil" + :report-user "BENUTZER MELDEN" + :message "Nachricht" + :username "Benutzername" + :not-specified "Nicht spezifiziert" + :public-key "Öffentlicher Schlüssel" + :phone-number "Telefonnummer" + :email "Email" + :profile-no-status "Kein Status" + :add-to-contacts "Zu den Kontakten hinzufügen" + :error-incorrect-name "Bitte wähle einen anderen Namen" + :error-incorrect-email "Inkorrekte E-mail" + + ;;make_photo + :image-source-title "Profilbild" + :image-source-make-photo "Erfassen" + :image-source-gallery "Aus der Galerie auswählen" + :image-source-cancel "Stornieren" + + ;sign-up + :contacts-syncronized "Deine Kontakte wurden synchronisiert" + :confirmation-code (str "Vielen Dank! Wir haben Dir eine SMS mit einer Bestätigung zugesandt" + "code. Bitte gebe diesen Code zur Bestätigung deiner Telefonnummer an") + :incorrect-code (str "Leider ist der Code nicht korrekt. Bitte versuche es nochmals") + :generate-passphrase (str "Ich generiere eine Passphrase für dich, so dass du deine wiederherstellen kannst " + "Zugriff oder Anmelden von einem anderen Gerät") + :phew-here-is-your-passphrase "*Phew* das war hart, hier ist deine Passphrase, *Schreibe diese auf und bewahre diese sicher auf!* Du benötigst diese, um dein Konto wiederherzustellen." + :here-is-your-passphrase "Hier ist deine Passphrase, *Schreibe diese auf und bewahre diese sicher auf!* Du benötigst diese, um dein Konto wiederherzustellen." + :written-down "Stelle sicher, dass du diese aufgeschrieben hast!" + :phone-number-required "Klicke hier, um deine Telefonnummer einzugeben & Ich finde deine Freunde für dich" + :intro-status "Chat mit mir, damit ich dein Konto einrichten und deine Einstellungen ändern kann!" + :intro-message1 "Willkommen zu Status\nTap diese Nachricht um Passwort einzurichten & loszulegen!" + :account-generation-message "Gib mir eine Sekunde, Ich muss wie verrückt etwas berechnen um dein Konto zu generieren!" + + ;chats + :chats "Chats" + :new-chat "Neuer Chat" + :new-group-chat "Neuer Gruppenchat" + + ;discover + :discover "Entdeckung" + :none "Keine" + :search-tags "Gebe hier deine Suchbegriffe ein" + :popular-tags "Beliebte Suchbegriffe" + :recent "Kürzlich" + :no-statuses-discovered "Es wurden keine Status gefunden" + + ;settings + :settings "Einstellungen" + + ;contacts + :contacts "Kontakte" + :new-contact "Neuer Kontakt" + :show-all "ZEIGE ALLES" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Leute" + :contacts-group-new-chat "Starte neuer Chat" + :no-contacts "Noch keine Kontakte" + :show-qr "Zeige QR" + + ;group-settings + :remove "Entferne" + :save "Speichern" + :change-color "Farbe ändern" + :clear-history "Verlauf löschen" + :delete-and-leave "Löschen und Verlassen" + :chat-settings "Chat Einstellungen" + :edit "Bearbeiten" + :add-members "Mitglieder hinzufügen" + :blue "Blau" + :purple "Lila" + :green "Grün" + :red "Rot" + + ;commands + :money-command-description "Geld senden" + :location-command-description "Standort senden" + :phone-command-description "Telefonnummer senden" + :phone-request-text "Telefonnummer anfordern" + :confirmation-code-command-description "Bestätigungscode senden" + :confirmation-code-request-text "Bestätigungscode anfordern" + :send-command-description "Standort senden" + :request-command-description "Anforderung senden" + :keypair-password-command-description "" + :help-command-description "Hilfe" + :request "Anforderung" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH zu {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH von {{chat-name}}" + :command-text-location "Standort: {{address}}" + :command-text-browse "Durchsuchen der Website: {{webpage}}" + :command-text-send "Transaktion: {{amount}} ETH" + :command-text-help "Hilfe" + + ;new-group + :group-chat-name "Chat Name" + :empty-group-chat-name "Bitte gib einen Namen ein" + :illegal-group-chat-name "Bitte wähle einen anderen Namen aus" + + ;participants + :add-participants "Teilnehmer hinzufügen" + :remove-participants "Teilnehmer entfernen" + + ;protocol + :received-invitation "Chat Einladung erhalten" + :removed-from-chat "du wurdest vom Gruppenchat entfernt" + :left "verlassen" + :invited "eingeladen" + :removed "entfernt" + :You "Du" + + ;new-contact + :add-new-contact "Teilnehmer hinzufügen" + :import-qr "Importieren" + :scan-qr "QR scannen" + :name "Name" + :whisper-identity "Identität flüstern" + :address-explication "Vielleicht sollte hier ein Text stehen der erklärt, was eine Adresse ist und wo man suchen soll" + :enter-valid-address "Bitte gebe eine gültige Adresse ein oder scannen den QR-Code" + :contact-already-added "Der Kontakt wurde bereits hinzugefügt" + :can-not-add-yourself "Du kannst dich nicht selbst hinzufügen" + :unknown-address "Unbekannte Adresse" + + + ;login + :connect "Verbinden" + :address "Adresse" + :password "Passwort" + :login "Login" + :wrong-password "Falsches Passwort" + + ;recover + :recover-from-passphrase "Wiederherstellung von Passphrase" + :recover-explain "Bitte gebe die Passphrase für dein Passwort ein, um den Zugang wiederherzustellen" + :passphrase "Passphrase" + :recover "Wiederherstellen" + :enter-valid-passphrase "Bitte Passphrase eingeben" + :enter-valid-password "Bitte Passwort eingeben" + + ;accounts + :recover-access "Zugriff wiederherstellen" + :add-account "Konto hinzufügen" + + ;wallet-qr-code + :done "Erledigt" + :main-wallet "Haupt-Portemonnaie" + + ;validation + :invalid-phone "Ungültige Telefonnummer" + :amount "Betrag" + :not-enough-eth (str "Nicht genug ETH zur Verfügung " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Transaktion bestätigen" + :other "Bestätige {{count}} Transaktion" + :zero "Keine Transaktion"} + :status "Status" + :pending-confirmation "Konfirmation ausstehend" + :recipient "Empfänger" + :one-more-item "Noch ein Element" + :fee "Gebühr" + :value "Valuta" + + ;:webview + :web-view-error "oops, Fehler"}) \ No newline at end of file diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs new file mode 100644 index 0000000000..035517fe6d --- /dev/null +++ b/src/status_im/translations/en.cljs @@ -0,0 +1,243 @@ +(ns status-im.translations.en) + +(def translations + { + ;common + :members-title "Members" + :not-implemented "!not implemented" + :chat-name "Chat name" + :notifications-title "Notifications and sounds" + :offline "Offline" + :search-for "Search for..." + :cancel "Cancel" + + ;drawer + :invite-friends "Invite friends" + :faq "FAQ" + :switch-users "Switch users" + :feedback "Got Feedback?\nShake your phone!" + + ;chat + :is-typing "is typing" + :and-you "and you" + :search-chat "Search chat" + :members {:one "1 member" + :other "{{count}} members" + :zero "no members"} + :members-active {:one "1 member, 1 active" + :other "{{count}} members, {{count}} active" + :zero "no members"} + :active-online "Online" + :active-unknown "Unknown" + :available "Available" + :no-messages "No messages" + :suggestions-requests "Requests" + :suggestions-commands "Commands" + :faucet-success "Faucet request has been received" + :faucet-error "Faucet request error" + + ;sync + :sync-in-progress "Syncing..." + :sync-synced "In sync" + + ;messages + :status-sending "Sending" + :status-pending "Pending" + :status-sent "Sent" + :status-seen-by-everyone "Seen by everyone" + :status-seen "Seen" + :status-delivered "Delivered" + :status-failed "Failed" + + ;datetime + :datetime-second {:one "second" + :other "seconds"} + :datetime-minute {:one "minute" + :other "minutes"} + :datetime-hour {:one "hour" + :other "hours"} + :datetime-day {:one "day" + :other "days"} + :datetime-multiple "s" + :datetime-ago "ago" + :datetime-yesterday "yesterday" + :datetime-today "today" + + ;profile + :profile "Profile" + :report-user "REPORT USER" + :message "Message" + :username "Username" + :not-specified "Not specified" + :public-key "Public Key" + :phone-number "Phone number" + :email "Email" + :profile-no-status "No status" + :add-to-contacts "Add to contacts" + :error-incorrect-name "Please select another name" + :error-incorrect-email "Incorrect e-mail" + + ;;make_photo + :image-source-title "Profile image" + :image-source-make-photo "Capture" + :image-source-gallery "Select from gallery" + :image-source-cancel "Cancel" + + ;;sharing + :sharing-copy-to-clipboard "Copy to clipboard" + :sharing-share "Share..." + :sharing-cancel "Cancel" + + ;sign-up + :contacts-syncronized "Your contacts have been synchronized" + :confirmation-code (str "Thanks! We've sent you a text message with a confirmation " + "code. Please provide that code to confirm your phone number") + :incorrect-code (str "Sorry the code was incorrect, please enter again") + :generate-passphrase (str "I'll generate a passphrase for you so you can restore your " + "access or log in from another device") + :phew-here-is-your-passphrase "*Phew* that was hard, here is your passphrase, *write this down and keep this safe!* You will need it to recover your account." + :here-is-your-passphrase "Here is your passphrase, *write this down and keep this safe!* You will need it to recover your account." + :written-down "Make sure you had securely written it down" + :phone-number-required "Tap here to enter your phone number & I'll find your friends" + :shake-your-phone "Find a bug or have a suggestion? Just ~shake~ your phone!" + :intro-status "Chat with me to setup your account and change your settings!" + :intro-message1 "Welcome to Status\nTap this message to set your password & get started!" + :account-generation-message "Gimmie a sec, I gotta do some crazy math to generate your account!" + + ;chats + :chats "Chats" + :new-chat "New chat" + :new-group-chat "New group chat" + + ;discover + :discover "Discover" + :none "None" + :search-tags "Type your search tags here" + :popular-tags "Popular tags" + :recent "Recent" + :no-statuses-discovered "No statuses discovered" + :no-statuses-found "No statuses found" + + ;settings + :settings "Settings" + + ;contacts + :contacts "Contacts" + :new-contact "New Contact" + :remove-contact "Remove contact" + :show-all "SHOW ALL" + :contacts-group-dapps "ÐApps" + :contacts-group-people "People" + :contacts-group-new-chat "Start new chat" + :no-contacts "No contacts yet" + :show-qr "Show QR" + :enter-address "Enter address" + + ;group-settings + :remove "Remove" + :save "Save" + :change-color "Change color" + :clear-history "Clear history" + :delete-and-leave "Delete and leave" + :chat-settings "Chat settings" + :edit "Edit" + :add-members "Add Members" + :blue "Blue" + :purple "Purple" + :green "Green" + :red "Red" + + ;commands + :money-command-description "Send money" + :location-command-description "Send location" + :phone-command-description "Send phone number" + :phone-request-text "Phone number request" + :confirmation-code-command-description "Send confirmation code" + :confirmation-code-request-text "Confirmation code request" + :send-command-description "Send location" + :request-command-description "Send request" + :keypair-password-command-description "" + :help-command-description "Help" + :request "Request" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH to {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH from {{chat-name}}" + :command-text-location "Location: {{address}}" + :command-text-browse "Browsing webpage: {{webpage}}" + :command-text-send "Transaction: {{amount}} ETH" + :command-text-help "Help" + :command-text-faucet "Faucet: {{url}}" + + ;new-group + :group-chat-name "Chat name" + :empty-group-chat-name "Please enter a name" + :illegal-group-chat-name "Please select another name" + + ;participants + :add-participants "Add Participants" + :remove-participants "Remove Participants" + + ;protocol + :received-invitation "received chat invitation" + :removed-from-chat "removed you from group chat" + :left "left" + :invited "invited" + :removed "removed" + :You "You" + + ;new-contact + :add-new-contact "Add new contact" + :import-qr "Import" + :scan-qr "Scan QR" + :swow-qr "Show QR" + :name "Name" + :whisper-identity "Whisper Identity" + :address-explication "Maybe here should be some text explaining what an address is and where to look for it" + :enter-valid-address "Please enter a valid address or scan a QR code" + :enter-valid-public-key "Please enter a valid public key or scan a QR code" + :contact-already-added "The contact has already been added" + :can-not-add-yourself "You can't add yourself" + :unknown-address "Unknown address" + + + ;login + :connect "Connect" + :address "Address" + :password "Password" + :login "Login" + :wrong-password "Wrong password" + + ;recover + :recover-from-passphrase "Recover from passphrase" + :recover-explain "Please enter the passphrase for your password to recover access" + :passphrase "Passphrase" + :recover "Recover" + :enter-valid-passphrase "Please enter a passphrase" + :enter-valid-password "Please enter a password" + + ;accounts + :recover-access "Recover access" + :add-account "Add account" + + ;wallet-qr-code + :done "Done" + :main-wallet "Main Wallet" + + ;validation + :invalid-phone "Invalid phone number" + :amount "Amount" + :not-enough-eth (str "Not enough ETH on balance " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirm transaction" + :other "Confirm {{count}} transactions" + :zero "No transactions"} + :status "Status" + :pending-confirmation "Pending confirmation" + :recipient "Recipient" + :one-more-item "One more item" + :fee "Fee" + :value "Value" + + ;:webview + :web-view-error "oops, error"}) diff --git a/src/status_im/translations/es.cljs b/src/status_im/translations/es.cljs new file mode 100644 index 0000000000..a57f7637d0 --- /dev/null +++ b/src/status_im/translations/es.cljs @@ -0,0 +1,231 @@ +(ns status-im.translations.es) + +(def translations + { + ;common + :members-title "Miembros" + :not-implemented "¡No se ha implementado!" + :chat-name "Nombre del chat" + :notifications-title "Notificaciones y sonidos" + :offline "Desconectado" + + ;drawer + :invite-friends "Invitar a los amigos" + :faq "Preguntas más frecuentes" + :switch-users "Cambiar usuarios" + + ;chat + :is-typing "está escribiendo" + :and-you "y tú" + :search-chat "Buscar chat" + :members {:one "1 miembro" + :other "{{count}} miembros" + :zero "no hay miembros"} + :members-active {:one "1 miembro, 1 activo" + :other "{{count}} miembros, {{count}} activos" + :zero " no hay miembros"} + :active-online "En línea" + :active-unknown "Desconocido" + :available "Disponible" + :no-messages "No hay mensajes" + :suggestions-requests "Peticiones" + :suggestions-commands "Comandos" + + ;sync + :sync-in-progress "Sincronización..." + :sync-synced "Sincronizados" + + ;messages + :status-sending "Enviando" + :status-pending "Pendiente" + :status-sent "Enviado" + :status-seen-by-everyone "Visto por todos" + :status-seen "Visto" + :status-delivered "Entregado" + :status-failed "Fallido" + + ;datetime + :datetime-second {:one "segundo" + :other "segundos"} + :datetime-minute {:one "minuto" + :other "minutos"} + :datetime-hour {:one "hora" + :other "horas"} + :datetime-day {:one "día" + :other "días"} + :datetime-multiple "s" + :datetime-ago "hace" + :datetime-yesterday "ayer" + :datetime-today "hoy" + + ;profile + :profile "Perfil" + :report-user "DENUNCIAR AL USUARIO" + :message "Mensaje" + :username "Nombre de usuario" + :not-specified "No especificado" + :public-key "Clave pública" + :phone-number "Número de teléfono" + :email "Correo electrónico" + :profile-no-status "Sin estado" + :add-to-contacts "Añadir a contactos" + :error-incorrect-name "Por favor, selecciona otro nombre" + :error-incorrect-email "Correo electrónico incorrecto" + + ;;make_photo + :image-source-title "Imagen de perfil" + :image-source-make-photo "Captura" + :image-source-gallery "Seleccionar de la galería" + :image-source-cancel "Cancelar" + + ;;sharing + :sharing-copy-to-clipboard "Copiar" + :sharing-share "Compartir..." + :sharing-cancel "Cancelar" + + ;sign-up + :contacts-syncronized "Se han sincronizado tus contactos" + :confirmation-code (str "¡Gracias! Te hemos enviado un mensaje de texto con un código" + "de confirmación. Por favor, escribe ese código para confirmar tu número de teléfono") + :incorrect-code (str "Lo sentimos, el código era incorrecto. Escríbalo de nuevo.") + :generate-passphrase (str "Generaré una frase de contraseña para que puedas restaurar tu contraseña." + "Acceder o iniciar sesión desde otro dispositivo") + :phew-here-is-your-passphrase "*Uf* ha sido difícil, aquí está tu contraseña, * escríbela y ¡guárdala en un sitio seguro! * La necesitarás para recuperar tu cuenta." + :here-is-your-passphrase "Aquí está tu frase de contraseña, *escríbela y ¡guárdala en un sitio seguro! *La necesitarás para recuperar tu cuenta." + :written-down "Asegúrate de haberla escrito de manera segura" + :phone-number-required "Toca aquí para escribir tu número de teléfono y encontraré a tus amigos" + :intro-status "¡Habla conmigo para configurar tu cuenta y cambiar tu configuración!" + :intro-message1 "Bienvenido a estados\n¡Toca este mensaje para configurar tu contraseña y empezar!" + :account-generation-message "Dame un segundo, ¡tengo que hacer un poco de Matemáticas locas para generar tu cuenta!" + + ;chats + :chats "Chats" + :new-chat "Un nuevo chat" + :new-group-chat "Un nuevo grupo de chat" + + ;discover + :discover "Descubrir" + :none "Ninguno" + :search-tags "Escribe tus etiquetas de búsqueda aquí" + :popular-tags "Etiquetas populares" + :recent "Reciente" + :no-statuses-discovered "No se han encontrado estados" + + ;settings + :settings "Ajustes" + + ;contacts + :contacts "Contactos" + :new-contact "Un nuevo contacto" + :show-all "MOSTRAR TODOS" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Gente" + :contacts-group-new-chat "Empezar un Nuevo chat" + :no-contacts "No hay contactos todavía" + :show-qr "Mostrar QR" + + ;group-settings + :remove "Eliminar" + :save "Guardar" + :change-color "Cambiar el color" + :clear-history "Borrar el historial" + :delete-and-leave "Borrar y salir" + :chat-settings "Ajustes del chat" + :edit "Editar" + :add-members "Añadir miembros" + :blue "Azul" + :purple "Púrpura" + :green "Verde" + :red "Rojo" + + ;commands + :money-command-description "Enviar dinero" + :location-command-description "Enviar ubicación" + :phone-command-description "Enviar número de teléfono" + :phone-request-text "Solicitud de número de teléfono" + :confirmation-code-command-description "Enviar código de confirmación" + :confirmation-code-request-text "Solicitud de código de confirmación" + :send-command-description "Enviar ubicación" + :request-command-description "Enviar solicitud" + :keypair-password-command-description "" + :help-command-description "Ayuda" + :request "Solicitar" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH a {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de{{chat-name}}" + :command-text-location "Ubicación: {{address}}" + :command-text-browse "Navegación en la página web: {{webpage}}" + :command-text-send "Transacción: {{amount}} ETH" + :command-text-help "Ayuda" + + ;new-group + :group-chat-name "Nombre del chat" + :empty-group-chat-name "Por favor, escribe un nombre" + :illegal-group-chat-name "Por favor, selecciona otro nombre" + + ;participants + :add-participants "Agregar participantes" + :remove-participants "Eliminar participantes" + + ;protocol + :received-invitation "invitación de chat recibida" + :removed-from-chat "te ha eliminado del grupo de chat" + :left "salió" + :invited "invitado" + :removed "eliminado" + :You "Tú" + + ;new-contact + :add-new-contact "Añadir un nuevo contacto" + :import-qr "Importar" + :scan-qr "Escanear QR" + :name "Nombre" + :whisper-identity "Identidad de susurro" + :address-explication "Tal vez aquí debería haber algún texto explicando qué es una dirección y dónde buscarla" + :enter-valid-address "Por favor, escribe una dirección válida o escanea un código QR " + :contact-already-added "Ya se ha añadido el contacto" + :can-not-add-yourself "No puedes agregarte" + :unknown-address "Dirección desconocida" + + + ;login + :connect "Conectar" + :address "Dirección" + :password "Contraseña" + :login "Iniciar sesión" + :wrong-password "Contraseña incorrecta" + + ;recover + :recover-from-passphrase "Recuperar de la frase de contraseña" + :recover-explain "Por favor, escribe la frase de contraseña a modo de contraseña para poder entrar" + :passphrase "Frase de contraseña" + :recover "Recuperar" + :enter-valid-passphrase "Por favor, escribe la frase de contraseña" + :enter-valid-password "Por favor, escribe la contraseña" + + ;accounts + :recover-access "Recuperar el acceso" + :add-account "Añadir cuenta" + + ;wallet-qr-code + :done "Hecho" + :main-wallet "Cartera principal" + + ;validation + :invalid-phone "El número de teléfono no es válido" + :amount "Cantidad" + :not-enough-eth (str "No hay suficiente ETH en conjunto " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmar transacción" + :other "Confirmar {{count}} transacciones" + :zero "No hay transacciones"} + :status "Estado" + :pending-confirmation "Confirmación pendiente" + :recipient "Beneficiario" + :one-more-item "Un elemento más" + :fee "Cuota" + :value "Valor" + + ;:webview + :web-view-error "oops, error"}) diff --git a/src/status_im/translations/es_ar.cljs b/src/status_im/translations/es_ar.cljs new file mode 100644 index 0000000000..1e197e9351 --- /dev/null +++ b/src/status_im/translations/es_ar.cljs @@ -0,0 +1,231 @@ +(ns status-im.translations.es-ar) + +(def translations + { + ;common + :members-title "Miembros" + :not-implemented "!no implementado" + :chat-name "Nombre del chat" + :notifications-title "Notificaciones y sonidos" + :offline "Fuera de línea" + + ;drawer + :invite-friends "Invitar amigos" + :faq "Preguntas frecuentes" + :switch-users "Cambiar de usuario" + + ;chat + :is-typing "está escribiendo" + :and-you "y tú" + :search-chat "Buscar chat" + :members {:one "1 miembro" + :other "{{count}} miembros" + :zero "no hay miembros"} + :members-active {:one "1 miembro, 1 activo" + :other "{{count}} miembros, {{count}} activo(s)" + :zero "no hay miembros"} + :active-online "En línea" + :active-unknown "Desconocido" + :available "Disponible" + :no-messages "No hay mensajes" + :suggestions-requests "Solicitudes" + :suggestions-commands "Comandos" + + ;sync + :sync-in-progress "Sincronizando..." + :sync-synced "Sincronizado" + + ;messages + :status-sending "Enviando" + :status-pending "Pendiente" + :status-sent "Enviado" + :status-seen-by-everyone "Visto por todos" + :status-seen "Visto" + :status-delivered "Enviado" + :status-failed "Fallido" + + ;datetime + :datetime-second {:one "segundo" + :other "segundos"} + :datetime-minute {:one "minuto" + :other "minutos"} + :datetime-hour {:one "hora" + :other "horas"} + :datetime-day {:one "día" + :other "días"} + :datetime-multiple "s" + :datetime-ago "atrás" + :datetime-yesterday "ayer" + :datetime-today "hoy" + + ;profile + :profile "Perfil" + :report-user "REPORTAR USUARIO" + :message "Mensaje" + :username "Nombre de usuario" + :not-specified "No especificado" + :public-key "Clave pública" + :phone-number "Número telefónico" + :email "Correo electrónico" + :profile-no-status "Sin estatus" + :add-to-contacts "Agregar a contactos" + :error-incorrect-name "Elecciona otro nombre" + :error-incorrect-email "Correo electrónico incorrecto" + + ;;make_photo + :image-source-title "Imagen de perfil" + :image-source-make-photo "Capturar" + :image-source-gallery "Seleccionar de la galería" + :image-source-cancel "Cancelar" + + ;;sharing + :sharing-copy-to-clipboard "Copiar" + :sharing-share "Compartir..." + :sharing-cancel "Cancelar" + + ;sign-up + :contacts-syncronized "Tus contactos se han sincronizado" + :confirmation-code (str "¡Gracias! Te hemos enviado un código de confirmación por mensaje de texto. " + "Ingresa este código para confirmar tu número telefónico") + :incorrect-code (str "Lo siento, el código no era correcto; ingrésalo de nuevo") + :generate-passphrase (str "Voy a generar una frase de contraseña para que puedas restablecer tu " + "acceso o iniciar sesión desde otro dispositivo") + :phew-here-is-your-passphrase "*Wow* eso estuvo difícil, aquí tienes tu contraseña, *¡anótala y mantenla segura!* La necesitarás para recuperar tu cuenta." + :here-is-your-passphrase "Aquí tienes tu frase de contraseña, *¡Anótala y mantenga segura!* La necesitarás para recuperar tu cuenta." + :written-down "Asegúrate de haberla anotado en un lugar seguro" + :phone-number-required "Pulsa aquí para ingresar tu número telefónico y yo encontraré a tus amigos" + :intro-status "Chatea conmigo para establecer tu cuenta y cambiar tu configuración!" + :intro-message1 "Bienvenido(a) a Status\n¡Pulsa en este mensaje para establecer tu contraseña y comenzar!" + :account-generation-message "¡Dame un segundo, necesito realizar un súper cálculo para generar tu cuenta!" + + ;chats + :chats "Chats" + :new-chat "Nuevo chat" + :new-group-chat "Nuevo chat de grupo" + + ;discover + :discover "Descubrimiento" + :none "Ninguno" + :search-tags "Ingresa aquí tus etiquetas de búsqueda" + :popular-tags "Etiquetas populares" + :recent "Reciente" + :no-statuses-discovered "No se encontró ningún estatus" + + ;settings + :settings "Configuración" + + ;contacts + :contacts "Contactos" + :new-contact "Nuevo contacto" + :show-all "MOSTRAR TODOS" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Gente" + :contacts-group-new-chat "Iniciar nuevo chat" + :no-contacts "No hay contactos todavía" + :show-qr "Mostrar QR" + + ;group-settings + :remove "Eliminar" + :save "Guardar" + :change-color "Cambiar color" + :clear-history "Borrar historial" + :delete-and-leave "Suprimir y dejar" + :chat-settings "Configuración del chat" + :edit "Editar" + :add-members "Agregar miembros" + :blue "Azul" + :purple "Púrpura" + :green "Verde" + :red "Rojo" + + ;commands + :money-command-description "Enviar dinero" + :location-command-description "Enviar ubicación" + :phone-command-description "Enviar número telefónico" + :phone-request-text "Solicitud de número telefónico" + :confirmation-code-command-description "Enviar código de confirmación" + :confirmation-code-request-text "Solicitud de código de confirmación" + :send-command-description "Enviar ubicación" + :request-command-description "Enviar solicitud" + :keypair-password-command-description "" + :help-command-description "Ayuda" + :request "Solicitud" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH a {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de {{chat-name}}" + :command-text-location "Ubicación: {{address}}" + :command-text-browse "Navegando la página web: {{webpage}}" + :command-text-send "Transacción: {{amount}} ETH" + :command-text-help "Ayuda" + + ;new-group + :group-chat-name "Nombre del chat" + :empty-group-chat-name "Ingresa un nombre" + :illegal-group-chat-name "Selecciona otro nombre" + + ;participants + :add-participants "Agregar participantes" + :remove-participants "Retirar participantes" + + ;protocol + :received-invitation "recibió invitación a chat" + :removed-from-chat "te retiró del grupo de chat" + :left "restantes" + :invited "invitado" + :removed "retirado" + :You "Tú" + + ;new-contact + :add-new-contact "Agregar nuevo contacto" + :import-qr "Importar" + :scan-qr "Escanear QR" + :name "Nombre" + :whisper-identity "Identidad de Whisper" + :address-explication "Tal vez, aquí debería haber alguna indicación explicando qué es una dirección y dónde buscarla" + :enter-valid-address "Ingresa una dirección válida o escanea un código QR" + :contact-already-added "El contacto ya ha sido agregado" + :can-not-add-yourself "No puedes agregarte a ti mismo(a)" + :unknown-address "Dirección desconocida" + + + ;login + :connect "Conectar" + :address "Dirección" + :password "Contraseña" + :login "Inicio de sesión" + :wrong-password "Contraseña incorrecta" + + ;recover + :recover-from-passphrase "Recuperar con frase de contraseña" + :recover-explain "Ingresa la frase de contraseña para tu contraseña para recuperar el acceso" + :passphrase "Frase de contraseña" + :recover "Recuperar" + :enter-valid-passphrase "Ingresa una frase de contraseña" + :enter-valid-password "Ingresa una contraseña" + + ;accounts + :recover-access "Recuperar acceso" + :add-account "Agregar cuenta" + + ;wallet-qr-code + :done "Completado" + :main-wallet "Cartera principal" + + ;validation + :invalid-phone "Número telefónico inválido" + :amount "Monto" + :not-enough-eth (str "No hay suficiente ETH en tu saldo " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmar transacción" + :other "Confirmar {{count}} transacciones" + :zero "No hay transacciones"} + :status "Estatus" + :pending-confirmation "Esperando confirmación" + :recipient "Destinatario" + :one-more-item "Un artículo más" + :fee "Tarifa" + :value "Valor" + + ;:webview + :web-view-error "oops, error"}) diff --git a/src/status_im/translations/fr.cljs b/src/status_im/translations/fr.cljs new file mode 100644 index 0000000000..aaf97fff1c --- /dev/null +++ b/src/status_im/translations/fr.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.fr) + +(def translations + { + ;common + :members-title "Membres" + :not-implemented "! non mis en œuvre" + :chat-name "Pseudo" + :notifications-title "Notifications et sons" + :offline "Hors ligne" + + ;drawer + :invite-friends "Inviter des amis" + :faq "FAQ" + :switch-users "Changer d'utilisateur" + + ;chat + :is-typing "écrit..." + :and-you "et vous" + :search-chat "Rechercher un chat" + :members {:one "1 membre" + :other "{{count}} membres" + :zero "Aucun membre"} + :members-active {:one "1 membre, 1 actif" + :other "{{count}} membres, {{count}} actifs" + :zero "Aucun membre"} + :active-online "En ligne" + :active-unknown "Inconnu" + :available "Disponible" + :no-messages "Pas de messages" + :suggestions-requests "Demandes" + :suggestions-commands "Commandes" + + ;sync + :sync-in-progress "Synchronisation en cours..." + :sync-synced "En synchronisation" + + ;messages + :status-sending "Envoi en cours..." + :status-pending "En attendant..." + :status-sent "Envoyé" + :status-seen-by-everyone "Vu par tout le monde" + :status-seen "Vu" + :status-delivered "Livré" + :status-failed "Échec" + + ;datetime + :datetime-second {:one "seconde" + :other "secondes"} + :datetime-minute {:one "minute" + :other "minutes"} + :datetime-hour {:one "heure" + :other "heures"} + :datetime-day {:one "jour" + :other "jours"} + :datetime-multiple "s" + :datetime-ago "il y a" + :datetime-yesterday "hier" + :datetime-today "aujourd'hui" + + ;profile + :profile "Profil" + :report-user "DÉNONCER UN UTILISATEUR" + :message "Message" + :username "Nom d'utilisateur" + :not-specified "Non spécifié" + :public-key "Clé publique" + :phone-number "Numéro de téléphone" + :email "Adresse e-mail" + :profile-no-status "Pas de statut" + :add-to-contacts "Ajouter aux contacts" + :error-incorrect-name "Veuillez choisir un autre nom" + :error-incorrect-email "Adresse e-mail incorrecte" + + ;;make_photo + :image-source-title "Photo de profil" + :image-source-make-photo "Capturer" + :image-source-gallery "Sélectionner dans la galerie" + :image-source-cancel "Annuler" + + ;sign-up + :contacts-syncronized "Vos contacts ont été synchronisés" + :confirmation-code (str "Merci ! Nous vous avons envoyé un message texte contenant le code " + "de confirmation. Veuillez fournir ce code pour confirmer votre numéro de téléphone") + :incorrect-code (str "Désolé, le code est incorrect. Veuillez le saisir à nouveau.") + :generate-passphrase (str "Je vais créer une phrase de passe pour vous permettre de restaurer votre " + "accès ou de vous connecter depuis un autre périphérique") + :phew-here-is-your-passphrase "*Ouf* c'était dur, voici votre phrase secrète, *écrivez-la et gardez-la en lieu sûr !* Vous en aurez besoin pour récupérer votre compte." + :here-is-your-passphrase "Voici votre phrase secrète, *écrivez-la et gardez-la en lieu sûr !* Vous en aurez besoin pour récupérer votre compte." + :written-down "Assurez-vous de l'avoir bien écrite" + :phone-number-required "Appuyez ici pour entrer votre numéro de téléphone et je vais trouver vos amis" + :intro-status "Chattez avec moi pour configurer votre compte et modifier vos paramètres !" + :intro-message1 "Bienvenue dans Status\nAppuyez sur ce message pour définir votre mot de passe et commencer !" + :account-generation-message "Donnez-moi une seconde, je dois faire quelques calculs de ouf pour générer votre compte !" + + ;chats + :chats "Chats" + :new-chat "Nouveau chat" + :new-group-chat "Nouveau chat de groupe" + + ;discover + :discover "Découverte" + :none "Aucun" + :search-tags "Entrez vos mots clés de" + :popular-tags "Mots-clés populaires" + :recent "Récent" + :no-statuses-discovered "Aucun statut découvert" + + ;settings + :settings "Paramètres" + + ;contacts + :contacts "Contacts" + :new-contact "Nouveau contact" + :show-all "TOUT AFFICHER" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Personnes" + :contacts-group-new-chat "Démarrer un nouveau chat" + :no-contacts "Pas de contacts encore" + :show-qr "Afficher le QR" + + ;group-settings + :remove "Retirer" + :save "Sauvegarder" + :change-color "Changer la couleur" + :clear-history "Effacer l'historique" + :delete-and-leave "Supprimer et quitter" + :chat-settings "Paramètres de chat" + :edit "Modifier" + :add-members "Ajouter des membres" + :blue "Bleu" + :purple "Violet" + :green "Vert" + :red "Rouge" + + ;commands + :money-command-description "Envoyer de l'argent" + :location-command-description "Envoyer l'emplacement" + :phone-command-description "Envoyer le numéro de téléphone" + :phone-request-text "Demande de numéro de téléphone" + :confirmation-code-command-description "Envoyer le code de confirmation" + :confirmation-code-request-text "Demande de code de confirmation" + :send-command-description "Envoyer l'emplacement" + :request-command-description "Envoyer une demande" + :keypair-password-command-description "" + :help-command-description "Aide" + :request "Demande" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH à {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de {{chat-name}}" + :command-text-location "Emplacement : {{address}}" + :command-text-browse "Page de navigation : {{webpage}}" + :command-text-send "Transaction : {{amount}} ETH" + :command-text-help "Aide" + + ;new-group + :group-chat-name "Pseudo" + :empty-group-chat-name "Veuillez saisir un nom" + :illegal-group-chat-name "Veuillez choisir un autre nom" + + ;participants + :add-participants "Ajouter des participants" + :remove-participants "Supprimer des participants" + + ;protocol + :received-invitation "invitation de chat reçue" + :removed-from-chat "vous a retiré du chat de groupe" + :left "est parti(e)" + :invited "invité" + :removed "supprimé" + :You "Vous" + + ;new-contact + :add-new-contact "Ajouter un nouveau contact" + :import-qr "Importer" + :scan-qr "Scanner le QR" + :name "Nom" + :whisper-identity "Murmurer l'identité" + :address-explication "Peut-être qu'ici devrait se trouver un texte expliquant ce qu'est une adresse et où la chercher" + :enter-valid-address "Veuillez saisir une adresse valide ou scanner un code QR" + :contact-already-added "Le contact a déjà été ajouté" + :can-not-add-yourself "Vous ne pouvez pas vous ajouter vous-même" + :unknown-address "Adresse inconnue" + + + ;login + :connect "Connecter" + :address "Adresse" + :password "Mot de passe" + :login "Connexion" + :wrong-password "Mot de passe incorrect" + + ;recover + :recover-from-passphrase "Récupérer avec la phrase secrète" + :recover-explain "Veuillez saisir la phrase secrète de votre mot de passe pour restaurer votre accès" + :passphrase "Phrase secrète" + :recover "Récupérer" + :enter-valid-passphrase "Veuillez saisir une phrase secrète" + :enter-valid-password "Veuillez saisir un mot de passe" + + ;accounts + :recover-access "Récupérer l'accès" + :add-account "Ajouter un compte" + + ;wallet-qr-code + :done "Terminé" + :main-wallet "Portefeuille principal" + + ;validation + :invalid-phone "Numéro de téléphone invalide" + :amount "Montant" + :not-enough-eth (str "Pas assez d'ETH sur le solde " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmer la transaction" + :other "Confirmer {{count}} transactions" + :zero "Aucune transaction"} + :status "Statut" + :pending-confirmation "Confirmation en attente" + :recipient "Destinataire" + :one-more-item "Encore un élément" + :fee "Frais" + :value "Valeur" + + ;:webview + :web-view-error "Oops, erreur"}) diff --git a/src/status_im/translations/fr_ch.cljs b/src/status_im/translations/fr_ch.cljs new file mode 100644 index 0000000000..13e7914dfe --- /dev/null +++ b/src/status_im/translations/fr_ch.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.fr-ch) + +(def translations + { + ;common + :members-title "Membres" + :not-implemented "!pas mis en place" + :chat-name "Nom de chat" + :notifications-title "Notifications et sons" + :offline "Hors ligne" + + ;drawer + :invite-friends "Inviter des amis" + :faq "FAQ" + :switch-users "Changer d'utilisateur" + + ;chat + :is-typing "est en train de taper" + :and-you "et vous" + :search-chat "Chercher dans une conversation" + :members {:one "1 membre" + :other "{{count}} membres" + :zero "pas de membres"} + :members-active {:one "1 membre, 1 actif" + :other "{{count}} membres, {{count}} actif(s)" + :zero "pas de membres"} + :active-online "En ligne" + :active-unknown "Inconnu" + :available "Disponible" + :no-messages "Pas de messages" + :suggestions-requests "Demandes" + :suggestions-commands "Commandes" + + ;sync + :sync-in-progress "En cours de synchronisation..." + :sync-synced "Synchronisé" + + ;messages + :status-sending "Envoi en cours" + :status-pending "En attendant" + :status-sent "Envoyé" + :status-seen-by-everyone "Vu par tous" + :status-seen "Vu" + :status-delivered "Livré" + :status-failed "Echoué" + + ;datetime + :datetime-second {:one "seconde" + :other "secondes"} + :datetime-minute {:one "minute" + :other "minutes"} + :datetime-hour {:one "heure" + :other "heures"} + :datetime-day {:one "jour" + :other "jours"} + :datetime-multiple "s" + :datetime-ago "avant" + :datetime-yesterday "hier" + :datetime-today "aujourd'hui" + + ;profile + :profile "Profil" + :report-user "SIGNALER UTILISATEUR" + :message "Message" + :username "Nom d'utilisateur" + :not-specified "Non spécifié" + :public-key "Clé publique" + :phone-number "Numéro de téléphone" + :email "E-mail" + :profile-no-status "Pas de statut" + :add-to-contacts "Ajouter aux contacts" + :error-incorrect-name "Veuillez sélectionner un autre nom" + :error-incorrect-email "E-mail incorrect" + + ;;make_photo + :image-source-title "Image du profil" + :image-source-make-photo "Capture" + :image-source-gallery "Sélectionner dans la galerie" + :image-source-cancel "Annuler" + + ;sign-up + :contacts-syncronized "Vos contacts ont été synchronisés" + :confirmation-code (str "Merci! Nous vous avons envoyé un message de texte avec un code " + "de confirmation. Veuillez fournir ce code pour confirmer votre numéro de téléphone") + :incorrect-code (str "Désolé, le code est incorrect, veuillez réessayer") + :generate-passphrase (str "Je vais générer une phrase de passe pour vous afin que vous puissiez " + "rétablir l'accès ou vous connecter depuis un autre appareil") + :phew-here-is-your-passphrase "*Pffff* c'était dur, voici votre phrase de passe, *notez-la et gardez-la en sécurité!* Vous en aurez besoin pour récupérer votre compte." + :here-is-your-passphrase "Voici votre phrase de passe, *notez-la et gardez-la en sécurité!* Vous en aurez besoin pour récupérer votre compte." + :written-down "Assurez-vous de l'avoir notée de manière sûre" + :phone-number-required "Tapez ici pour saisir votre numéro de téléphone et je trouverai vos amis" + :intro-status "Chattez avec moi pour configurer votre compte et changer vos paramètres !" + :intro-message1 "Bienvenue dans le statut\nTapez ce message pour établir votre mot de passe et vous lancer !" + :account-generation-message "Une seconde svp, je dois lancer quelques formules magiques pour générer votre compte !" + + ;chats + :chats "Chats" + :new-chat "Nouveau chat" + :new-group-chat "Nouveau chat de groupe" + + ;discover + :discover "Découverte" + :none "Aucun" + :search-tags "Tapez vos clés de recherche ici" + :popular-tags "Clés populaires" + :recent "Récent" + :no-statuses-discovered "Aucun statut trouvé" + + ;settings + :settings "Paramètres" + + ;contacts + :contacts "Contacts" + :new-contact "Nouveau Contact" + :show-all "MONTRER TOUS" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Personnes" + :contacts-group-new-chat "Lancer un nouveau chat" + :no-contacts "Pas encore de contacts" + :show-qr "Montrer QR" + + ;group-settings + :remove "Supprimer" + :save "Sauvegarder" + :change-color "Changer la couleur" + :clear-history "Effacer l'historique" + :delete-and-leave "Supprimer et quitter" + :chat-settings "Paramètres de chat" + :edit "Modifier" + :add-members "Ajouter des membres" + :blue "Bleu" + :purple "Violet" + :green "Vert" + :red "Rouge" + + ;commands + :money-command-description "Envoyer de l'argent" + :location-command-description "Envoyer un emplacement" + :phone-command-description "Envoyer un numéro de téléphone" + :phone-request-text "Demande de numéro de téléphone" + :confirmation-code-command-description "Envoyer un code de confirmation" + :confirmation-code-request-text "Demande de code de confirmation" + :send-command-description "Envoyer un emplacement" + :request-command-description "Envoyer une demande" + :keypair-password-command-description "" + :help-command-description "Aide" + :request "Demande" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH pour {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de {{chat-name}}" + :command-text-location "Emplacement: {{address}}" + :command-text-browse "Site web de navigation: {{webpage}}" + :command-text-send "Transaction: {{amount}} ETH" + :command-text-help "Aide" + + ;new-group + :group-chat-name "Nom du chat" + :empty-group-chat-name "Veuillez entrer un nom" + :illegal-group-chat-name "Veuillez sélectionner un autre nom" + + ;participants + :add-participants "Ajouter des participants" + :remove-participants "Supprimer des participants" + + ;protocol + :received-invitation "reçu une invitation à un chat" + :removed-from-chat "vous a supprimé du chat de groupe" + :left "restant" + :invited "invité" + :removed "supprimé" + :You "Vous" + + ;new-contact + :add-new-contact "Ajouter un nouveau contact" + :import-qr "Importer" + :scan-qr "Scannner QR" + :name "Nom" + :whisper-identity "Murmurer l'identité" + :address-explication "On pourrait mettre ici un texte qui explique ce qu'est une adresse et comment la rechercher" + :enter-valid-address "Veuillez saisir une adresse valable ou scanner un code QR" + :contact-already-added "Le contact a déjà été ajouté" + :can-not-add-yourself "Vous ne pouvez pas vous ajouter vous-même" + :unknown-address "Adresse inconnue" + + + ;login + :connect "Se connecter" + :address "Adresse" + :password "Mot de passe" + :login "Nom d'utilisateur" + :wrong-password "Mauvais mot de passe" + + ;recover + :recover-from-passphrase "Restaurer à partir d'une phrase de passe" + :recover-explain "Veuillez saisir la phrase de passe pour votre mot de passe afin de récupérer l'accès" + :passphrase "Phrase de passe" + :recover "Restaurer" + :enter-valid-passphrase "Veuillez taper une phrase de passe" + :enter-valid-password "Veuillez taper un mot de passe" + + ;accounts + :recover-access "Récupérer l'accès" + :add-account "Ajouter un compte" + + ;wallet-qr-code + :done "Terminé" + :main-wallet "Portefeuille principal" + + ;validation + :invalid-phone "Numéro de téléphone non valable" + :amount "Montant" + :not-enough-eth (str "Pas assez d'ETH sur le solde " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmer la transaction" + :other "Confirmer {{count}} transactions" + :zero "Pas de transactions"} + :status "Statut" + :pending-confirmation "Confirmation en attente" + :recipient "Destinataire" + :one-more-item "Encore un objet" + :fee "Tarif" + :value "Valeur" + + ;:webview + :web-view-error "oups, erreur"}) diff --git a/src/status_im/translations/hi.cljs b/src/status_im/translations/hi.cljs new file mode 100644 index 0000000000..45d0cd9d86 --- /dev/null +++ b/src/status_im/translations/hi.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.hi) + +(def translations + { + ;common + :members-title "सदस्य" + :not-implemented "!कार्यान्वित नहीं" + :chat-name "चैट नाम" + :notifications-title "सूचनाएं और आवाज" + :offline "ऑफ़लाइन" + + ;drawer + :invite-friends "दोस्तों को आमंत्रित करें" + :faq "अक्सर पूछे जाने वाले प्रश्न" + :switch-users "उपयोगकर्ताओं को स्विच करें" + + ;chat + :is-typing "टाइप कर रहा है" + :and-you "और आप" + :search-chat "चैट खोजें" + :members {:one "1 सदस्य" + :other "{{count}} सदस्य" + :zero "कोई सदस्य नहीं"} + :members-active {:one "1 सदस्य, 1 सक्रिय" + :other "{{count}} सदस्य, {{count}} सक्रिय" + :zero "कोई सदस्य नहीं"} + :active-online "ऑनलाइन" + :active-unknown "अनजान" + :available "उपलब्ध" + :no-messages "कोई संदेश नहीं" + :suggestions-requests "अनुरोध" + :suggestions-commands "कमांड" + + ;sync + :sync-in-progress "सिंक किया जा रहा है..." + :sync-synced "सिंक में" + + ;messages + :status-sending "भेज रहे हैं" + :status-pending "विचाराधीन" + :status-sent "भेज दिया" + :status-seen-by-everyone "हर किसी ने देख लिया" + :status-seen "देखा गया" + :status-delivered "सौंप दिया" + :status-failed "विफल रहा" + + ;datetime + :datetime-second {:one "सेकंड" + :other "सेकंड"} + :datetime-minute {:one "मिनट" + :other "मिनट"} + :datetime-hour {:one "घंटा" + :other "घंटे"} + :datetime-day {:one "दिन" + :other "दिन"} + :datetime-multiple "एस" + :datetime-ago "पहले" + :datetime-yesterday "बीता हुआ कल" + :datetime-today "आज" + + ;profile + :profile "प्रोफाइल" + :report-user "उपयोगकर्ता को रिपोर्ट करें" + :message "संदेश" + :username "उपयोगकर्ता नाम" + :not-specified "निर्दिष्ट नहीं" + :public-key "सार्वजनिक कुंजी" + :phone-number "फ़ोन नंबर" + :email "ईमेल" + :profile-no-status "कोई स्थिति नहीं" + :add-to-contacts "संपर्क में जोड़ें" + :error-incorrect-name "कृपया दूसरा नाम चयन करें" + :error-incorrect-email "गलत ई-मेल" + + ;;make_photo + :image-source-title "प्रोफ़ाइल छवि" + :image-source-make-photo "प्राप्त करें" + :image-source-gallery "गैलरी से चयन करें" + :image-source-cancel "रद्द करें" + + ;sign-up + :contacts-syncronized "आपके संपर्कों को सिंक्रनाइज़ किया गया है" + :confirmation-code (str "धन्यवाद! हमने आपको \"पुष्टि कोड\" के साथ एक टेक्स्ट संदेश भेजा है " + "कृपया अपने फोन नंबर की पुष्टि करने के लिए वह कोड डालें।") + :incorrect-code (str "क्षमा करें, कोड गलत था, कृपया फिर से डालें") + :generate-passphrase (str "मैं आपके लिए एक पासफ्रेज जनरेट करूंगा ताकि आप अपना पासफ्रेज बहाल कर सकें " + "अन्य डिवाइस से उपयोग या लॉगिन करें") + :phew-here-is-your-passphrase "*उफ़्फ़* यह बहुत मुश्किल था, यह रहा आपका पासफ्रेज, *इसे लिख लें और सुरक्षित रखें! * अपने खाते को ठीक करने के लिए आपको इसकी आवश्यकता होगी।" + :here-is-your-passphrase "यह रहा आपका पासफ्रेज, *इसे लिख लें और सुरक्षित रखें! * अपने खाते को ठीक करने के लिए आपको इसकी आवश्यकता होगी।" + :written-down "पक्का करें कि आपने इसे सुरक्षित रूप से लिख लिया था" + :phone-number-required "अपना फोन नंबर दर्ज करने के लिए यहां टैप करें और मैं आपके दोस्तों को ढूंढ निकालूंगा" + :intro-status "अपना खाता सेट करने और अपनी सेटिंग्स बदलने के लिए मेरे साथ चैट करें!" + :intro-message1 "स्टेटस में आपका स्वागत है\n अपना पासवर्ड सेट करने और शुरुआत करने के लिए इस संदेश पर करें!" + :account-generation-message "मुझे एक सेकंड का समय दें, आपका खाता जनरेट करने के लिए मुझे कुछ क्रेजी गणित का प्रयोग करना होगा!" + + ;chats + :chats "चैट" + :new-chat "नई चैट" + :new-group-chat "नई ग्रुप चैट" + + ;discover + :discover "खोज" + :none "कोई नहीं" + :search-tags "अपने खोज टैग यहां टाइप करें" + :popular-tags "लोकप्रिय टैग" + :recent "नया" + :no-statuses-discovered "कोई स्टेटस नहीं मिला" + + ;settings + :settings "सेटिंग्स" + + ;contacts + :contacts "संपर्क" + :new-contact "नया संपर्क" + :show-all "सभी दिखाएं" + :contacts-group-dapps "ÐApps" + :contacts-group-people "लोग" + :contacts-group-new-chat "नई चैट शुरू करें" + :no-contacts "अभी तक कोई संपर्क नहीं" + :show-qr "QR दिखाएं" + + ;group-settings + :remove "निकालें" + :save "सहेजें" + :change-color "रंग बदलें" + :clear-history "इतिहास साफ़ करें" + :delete-and-leave "हटाएं और छोड़ें" + :chat-settings "चैट सेटिंग्स" + :edit "संपादित करें" + :add-members "सदस्य जोड़ें" + :blue "नीला" + :purple "बैंगनी" + :green "हरा" + :red "लाल" + + ;commands + :money-command-description "पैसे भेजें" + :location-command-description "स्थान भेजें" + :phone-command-description "फोन नंबर भेजें" + :phone-request-text "फ़ोन नंबर अनुरोध" + :confirmation-code-command-description "पुष्टि कोड भेजें" + :confirmation-code-request-text "पुष्टि कोड अनुरोध" + :send-command-description "स्थान भेजें" + :request-command-description "अनुरोध भेजें" + :keypair-password-command-description "" + :help-command-description "मदद करें" + :request "अनुरोध करें" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} {{chat-name}} को ETH" + :chat-send-eth-from "{{amount}} {{chat-name}} से ETH" + :command-text-location "स्थान: {{address}}" + :command-text-browse "वेबपेज ब्राउजिंग: {{webpage}}" + :command-text-send "लेनदेन: {{amount}} ETH" + :command-text-help "मदद करें" + + ;new-group + :group-chat-name "चैट नाम" + :empty-group-chat-name "कोई नाम दर्ज करें" + :illegal-group-chat-name "कृपया दूसरा नाम चयन करें" + + ;participants + :add-participants "प्रतिभागियों को जोड़ें" + :remove-participants "प्रतिभागियों को निकालें" + + ;protocol + :received-invitation "चैट आमंत्रण प्राप्त हुआ" + :removed-from-chat "आपको ग्रुप चैट से निकाल दिया" + :left "बाएं" + :invited "आमंत्रित" + :removed "निकाल दिया" + :You "आप" + + ;new-contact + :add-new-contact "नया संपर्क जोड़ें" + :import-qr "आयात करें" + :scan-qr "QR स्कैन करें" + :name "नाम" + :whisper-identity "पहचान बताएं" + :address-explication "संभवतः यहाँ कुछ टेक्स्ट विवरण होना चाहिए कि पता क्या है और इसे कहाँ खोजा जाए" + :enter-valid-address "कृपया एक वैध पता दर्ज करें या एक QR कोड स्कैन करें" + :contact-already-added "संपर्क पहले से जोड़ लिया गया है" + :can-not-add-yourself "आप अपने आपको नहीं जोड़ सकते" + :unknown-address "अज्ञात पता" + + + ;login + :connect "कनेक्ट करें" + :address "पता" + :password "पासवर्ड" + :login "लॉगिन करें" + :wrong-password "गलत पासवर्ड" + + ;recover + :recover-from-passphrase "पासफ्रेज से ठीक करें" + :recover-explain "पहुंच को ठीक करने के लिए कृपया अपने पासवर्ड का पासफ्रेज दर्ज करें" + :passphrase "पासफ्रेज" + :recover "ठीक करें" + :enter-valid-passphrase "कृपया एक पासफ्रेज दर्ज करें" + :enter-valid-password "कृपया पासवर्ड दर्ज करें" + + ;accounts + :recover-access "पहुंच को ठीक करें" + :add-account "खाता जोड़ें" + + ;wallet-qr-code + :done "पूरा हो गया" + :main-wallet "मुख्य वॉलेट" + + ;validation + :invalid-phone "अमान्य फोन नंबर" + :amount "राशि" + :not-enough-eth (str "बैलेंस पर पर्याप्त ETH नहीं " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "लेनदेन की पुष्टि करें" + :other "{{count}} लेनदेनों की पुष्टि करें" + :zero "कोई लेनदेन नहीं"} + :status "स्टेटस" + :pending-confirmation "लंबित पुष्टि" + :recipient "प्राप्तकर्ता" + :one-more-item "एक अन्य आइटम" + :fee "शुल्क" + :value "मूल्य" + + ;:webview + :web-view-error "ओह, त्रुटि"}) diff --git a/src/status_im/translations/hu.cljs b/src/status_im/translations/hu.cljs new file mode 100644 index 0000000000..62d3d5ca53 --- /dev/null +++ b/src/status_im/translations/hu.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.hu) + +(def translations + { + ;common + :members-title "Tagok" + :not-implemented "!nem végrehajtott" + :chat-name "Csevegés neve" + :notifications-title "Értesítések és hangok" + :offline "Offline" + + ;drawer + :invite-friends "Barátok meghívása" + :faq "GYIK" + :switch-users "Felhasználók váltása" + + ;chat + :is-typing "gépel" + :and-you "és te" + :search-chat "Csevegés keresése" + :members {:one "1 tag" + :other "{{count}} tag" + :zero "nincsenek tagok"} + :members-active {:one "1 tag, 1 aktív" + :other "{{count}} tag, {{count}} aktív" + :zero "nincsenek tagok"} + :active-online "Online" + :active-unknown "Ismeretlen" + :available "Elérhető" + :no-messages "Nincsenek üzenetek" + :suggestions-requests "Kérések" + :suggestions-commands "Parancsok" + + ;sync + :sync-in-progress "Szinkronizálás..." + :sync-synced "Szinkronizálás folyamatban" + + ;messages + :status-sending "Küldés" + :status-pending "Függőben levő" + :status-sent "Elküldve" + :status-seen-by-everyone "Látva mindenki által" + :status-seen "Látva" + :status-delivered "Kézbesítve" + :status-failed "Nem sikerült" + + ;datetime + :datetime-second {:one "másodperc" + :other "másodperc"} + :datetime-minute {:one "perc" + :other "perc"} + :datetime-hour {:one "óra" + :other "óra"} + :datetime-day {:one "nap" + :other "nap"} + :datetime-multiple "k" + :datetime-ago "ezelőtt" + :datetime-yesterday "tegnap" + :datetime-today "ma" + + ;profile + :profile "Profil" + :report-user "FELHASZNÁLÓ JELENTÉSE" + :message "Üzenet" + :username "Felhasználónév" + :not-specified "Nincs megadva" + :public-key "Nyilvános kulcs" + :phone-number "Telefonszám" + :email "E-mail" + :profile-no-status "Nincs állapot" + :add-to-contacts "Hozzáadás a kapcsolatokhoz" + :error-incorrect-name "Kérjük, válassz ki másik nevet" + :error-incorrect-email "Hibás e-mail" + + ;;make_photo + :image-source-title "Profilkép" + :image-source-make-photo "Rögzítés" + :image-source-gallery "Kiválasztás a galériából" + :image-source-cancel "Mégsem" + + ;sign-up + :contacts-syncronized "Kapcsolataid szinkronizálásra kerültek" + :confirmation-code (str "Köszönjük! Küldtünk neked egy szöveges üzenetet megerősítési " + "kóddal. Kérjük, add meg a kódot telefonszámod megerősítése érdekében") + :incorrect-code (str "Sajnáljuk, hibás kód, kérjük, add meg újból") + :generate-passphrase (str "Generálok neked egy jelmondatot hozzáférésed helyreállításához " + "vagy egy másik eszközről történő bejelentkezéshez") + :phew-here-is-your-passphrase "*Hűha* ez nehéz volt, de kész a jelmondatod, *írd fel valahova és vigyázz rá!* Szükséged lesz hozzá felhasználói fiókod helyreállításához." + :here-is-your-passphrase "Kész a jelmondatod, *írd fel valahová és őrizd meg!* Szükséged lesz hozzá felhasználói fiókod helyreállításához." + :written-down "Bizonyosodj meg arról, hogy biztonságos helyen tárolod" + :phone-number-required "Érints ide telefonszámod megadásához és megtalálom a barátaidat" + :intro-status "Csevegj velem felhasználói fiókod létrehozásáról és beállításaid megváltoztatásáról!" + :intro-message1 "Üdv az Állapotnál\nÉrints erre a üzenetre, állítsd be a jelszavad és fogj hozzá!" + :account-generation-message "Adj egy percet, varázsolok egy kicsit és létre is hozom a felhasználói fiókodat!" + + ;chats + :chats "Csevegések" + :new-chat "Új csevegés" + :new-group-chat "Új csoportos csevegés" + + ;discover + :discover "Felfedezés" + :none "Semmi" + :search-tags "Add meg keresési címkéidet itt" + :popular-tags "Népszerű címkék" + :recent "Legutóbbi" + :no-statuses-discovered "Nincsenek felfedezett állapotok" + + ;settings + :settings "Beállítások" + + ;contacts + :contacts "Kapcsolatok" + :new-contact "Új kapcsolat" + :show-all "ÖSSZES MUTATÁSA" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Emberek" + :contacts-group-new-chat "Új csevegés indítása" + :no-contacts "Még nincsenek kapcsolatok" + :show-qr "QR mutatása" + + ;group-settings + :remove "Eltávolítás" + :save "Mentés" + :change-color "Szín megváltoztatása" + :clear-history "Előzmények törlése" + :delete-and-leave "Törlés és kilépés" + :chat-settings "Csevegés beállítások" + :edit "Szerkesztés" + :add-members "Tagok hozzáadása" + :blue "Kék" + :purple "Lila" + :green "Zöld" + :red "Piros" + + ;commands + :money-command-description "Pénz küldése" + :location-command-description "Halyszín küldése" + :phone-command-description "Telefonszám küldése" + :phone-request-text "Telefonszám irénylése" + :confirmation-code-command-description "Megerősítési kód küldése" + :confirmation-code-request-text "Megerősítési kód igénylése" + :send-command-description "Helyszín küldése" + :request-command-description "Küldési igény" + :keypair-password-command-description "" + :help-command-description "Segítség" + :request "Kérés" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH ide {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH innen {{chat-name}}" + :command-text-location "Helyszín: {{address}}" + :command-text-browse "Böngészési weboldal: {{webpage}}" + :command-text-send "Tranzakció: {{amount}} ETH" + :command-text-help "Segítség" + + ;new-group + :group-chat-name "Csevegés neve" + :empty-group-chat-name "Kérjük, add meg egy új nevet" + :illegal-group-chat-name "Kérjük, válassz egy új nevet" + + ;participants + :add-participants "Résztvevők hozzáadása" + :remove-participants "Résztvevők eltávolítása" + + ;protocol + :received-invitation "csevegési meghívásban részesült" + :removed-from-chat "eltávolítva a csoportos csevegésből" + :left "maradt" + :invited "meghívott" + :removed "eltávolított" + :You "Te" + + ;new-contact + :add-new-contact "Új kapcsolat hozzáadása" + :import-qr "Importálás" + :scan-qr "QR beolvasása" + :name "Név" + :whisper-identity "Whisper személyazonosság" + :address-explication "Itt talán szükség lenne egy kis szövegre, ami elmagyarázná, mi is az a cím és hol lehet megtalálni" + :enter-valid-address "Kérjük, adj meg egy helyes címet vagy olvass be egy QR kódot" + :contact-already-added "A kapcsolat már hozzáadásra került" + :can-not-add-yourself "Magadat nem adhatod hozzá" + :unknown-address "Ismeretlen cím" + + + ;login + :connect "Kapcsolódás" + :address "Cím" + :password "Jelszó" + :login "Bejelentkezés" + :wrong-password "Hibás jelszó" + + ;recover + :recover-from-passphrase "Visszaállítás jelmondatból" + :recover-explain "Kérjük, adj meg egy jelmondatot a jelszavaddal történő hozzáférés helyreállításához" + :passphrase "Jelmondat" + :recover "Visszaállítás" + :enter-valid-passphrase "Kérjük, adj meg egy jelmondatote" + :enter-valid-password "Kérjük, adj meg egy jelszót" + + ;accounts + :recover-access "Hozzáférés helyreállítása" + :add-account "Felhasználói fiók hozzáadása" + + ;wallet-qr-code + :done "Kész" + :main-wallet "Fő zseb" + + ;validation + :invalid-phone "Hibás telefonszám" + :amount "Összeg" + :not-enough-eth (str "Nincs elég ETH a számlán " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Tranzakció megerősítése" + :other "{{count}} tranzakció megerősítése" + :zero "Nincsenek tranzakciók"} + :status "Állapot" + :pending-confirmation "Függőben lévő megerősítés" + :recipient "Címzett" + :one-more-item "Még egy tétel" + :fee "Díj" + :value "Érték" + + ;:webview + :web-view-error "hoppá, hiba"}) diff --git a/src/status_im/translations/it.cljs b/src/status_im/translations/it.cljs new file mode 100644 index 0000000000..b6089f2c95 --- /dev/null +++ b/src/status_im/translations/it.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.it) + +(def translations + { + ;common + :members-title "Membri" + :not-implemented "!non implementato" + :chat-name "Nome chat" + :notifications-title "Notifiche e suoni" + :offline "Offline" + + ;drawer + :invite-friends "Invita amici" + :faq "FAQ" + :switch-users "Cambia utente" + + ;chat + :is-typing "sta scrivendo" + :and-you "e tu" + :search-chat "Cerca chat" + :members {:one "1 membro" + :other "{{count}} membri" + :zero "nessun membro"} + :members-active {:one "1 membro, 1 attivo" + :other "{{count}} membri, {{count}} attivi" + :zero "nessun membro"} + :active-online "Online" + :active-unknown "Sconosciuto" + :available "Disponibile" + :no-messages "Nessun messaggio" + :suggestions-requests "Richieste" + :suggestions-commands "Comandi" + + ;sync + :sync-in-progress "Sincronizzazione in corso..." + :sync-synced "Sincronizzato" + + ;messages + :status-sending "Invio" + :status-pending "In attesa di" + :status-sent "Inviato" + :status-seen-by-everyone "Visto da tutti" + :status-seen "Visto" + :status-delivered "Consegnato" + :status-failed "Fallito" + + ;datetime + :datetime-second {:one "secondo" + :other "secondi"} + :datetime-minute {:one "minuto" + :other "minuti"} + :datetime-hour {:one "ora" + :other "ore"} + :datetime-day {:one "giorno" + :other "giorni"} + :datetime-multiple "s" + :datetime-ago "fa" + :datetime-yesterday "ieri" + :datetime-today "oggi" + + ;profile + :profile "Profilo" + :report-user "SEGNALA UTENTE" + :message "Messaggio" + :username "Username" + :not-specified "Non specificato" + :public-key "Chiave pubblica" + :phone-number "Numero di telefono" + :email "Email" + :profile-no-status "Nessuno stato" + :add-to-contacts "Aggiunto ai contatti" + :error-incorrect-name "Seleziona un altro nome" + :error-incorrect-email "e-mail non valida" + + ;;make_photo + :image-source-title "Immagine del profilo" + :image-source-make-photo "Acquisisci" + :image-source-gallery "Scegli dalla galleria" + :image-source-cancel "Elimina" + + ;sign-up + :contacts-syncronized "I tuoi contatti sono stati sincronizzati" + :confirmation-code (str "Grazie! Ti abbiamo inviato un messaggio con un codice di conferma " + "codice. Fornisci questo codice per confermare il tuo numero di telefono") + :incorrect-code (str "Spiacenti, il codice era errato, reinseriscilo") + :generate-passphrase (str "Genereremo una passphrase da utilizzare per riattivare il tuo " + "accesso o accedere da un altro dispositivo") + :phew-here-is-your-passphrase "*Uff* è stato difficile, ecco la tua passphrase, *scrivila e tienila al sicuro!* Ti servirà per poter accedere al tuo account." + :here-is-your-passphrase "Questa è la tua passphrase, *scrivila e tienila al sicuro!* Ti servirà per poter accedere al tuo account." + :written-down "Assicurati di averla scritta e conservata" + :phone-number-required "Tocca qui per inserire il tuo numero di telefono e cercheremo i tuoi amici" + :intro-status "Fai una chat con me per impostare il tuo account e cambiare le tue impostazioni!" + :intro-message1 "Benvenuto su Status\nTocca questo messaggio per impostare la tua password e poter cominciare!" + :account-generation-message "Dammi un secondo, devo fare dei calcoli complicati per generare il tuo account!" + + ;chats + :chats "Chat" + :new-chat "Nuova chat" + :new-group-chat "Nuova chat di gruppo" + + ;discover + :discover "Scopri" + :none "Nessuno" + :search-tags "Digita qui le tue etichette di ricerca" + :popular-tags "Tag popolari" + :recent "Recenti" + :no-statuses-discovered "Nessuno stato trovato" + + ;settings + :settings "Impostazioni" + + ;contacts + :contacts "Contatti" + :new-contact "Nuovo contatto" + :show-all "MOSTRA TUTTI" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Persone" + :contacts-group-new-chat "Comincia una nuova chat" + :no-contacts "Ancora nessun contatto" + :show-qr "Mostra QR" + + ;group-settings + :remove "Rimuovi" + :save "Salva" + :change-color "Cambia colore" + :clear-history "Cancella cronologia" + :delete-and-leave "Cancella e lascia" + :chat-settings "Impostazioni chat" + :edit "Modifica" + :add-members "Aggiungi membri" + :blue "Blu" + :purple "Viola" + :green "Verde" + :red "Rosso" + + ;commands + :money-command-description "Invia denaro" + :location-command-description "Invia posizione" + :phone-command-description "Invia numero di telefono" + :phone-request-text "Richiesta numero di telefono" + :confirmation-code-command-description "Invia codice di conferma" + :confirmation-code-request-text "Codice di conferma richiesto" + :send-command-description "Invia posizione" + :request-command-description "Invia richiesta" + :keypair-password-command-description "" + :help-command-description "Aiuto" + :request "Richiesta" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH per {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH da {{chat-name}}" + :command-text-location "Posizione: {{address}}" + :command-text-browse "Naviga la pagina web: {{webpage}}" + :command-text-send "Transazione: {{amount}} ETH" + :command-text-help "Aiuto" + + ;new-group + :group-chat-name "Nome chat" + :empty-group-chat-name "Inserisci un nome" + :illegal-group-chat-name "Scegli un altro nome" + + ;participants + :add-participants "Aggiungi partecipanti" + :remove-participants "Rimuovi partecipanti" + + ;protocol + :received-invitation "invito alla chat ricevuto" + :removed-from-chat "sei stato rimosso dalla chat di gruppo" + :left "lasciato" + :invited "invitato" + :removed "rimosso" + :You "Tu" + + ;new-contact + :add-new-contact "Aggiungi un nuovo contatto" + :import-qr "Importa" + :scan-qr "Scansiona codice QR" + :name "Nome" + :whisper-identity "Sussurra identità" + :address-explication "Forse qui bisognerebbe inserire un testo che spieghi cos'è un indirizzo e dove cercarlo" + :enter-valid-address "Inserisci un indirizzo valido o scansiona un codice QR" + :contact-already-added "Il contatto è stato già aggiunto" + :can-not-add-yourself "Non puoi aggiungere te stesso" + :unknown-address "Indirizzo sconosciuto" + + + ;login + :connect "Connetti" + :address "Indirizzo" + :password "Password" + :login "Accedi" + :wrong-password "Password errata" + + ;recover + :recover-from-passphrase "Recupera dalla passphrase" + :recover-explain "Inserisci la passphrase per poter accedere alla tua password" + :passphrase "Passphrase" + :recover "Recupera" + :enter-valid-passphrase "Inserisci una passphrase" + :enter-valid-password "Inserisci una password" + + ;accounts + :recover-access "Recupera accessi" + :add-account "Aggiungi account" + + ;wallet-qr-code + :done "Fatto" + :main-wallet "Portafogli principale" + + ;validation + :invalid-phone "Numero di telefono non valido" + :amount "Ammontare" + :not-enough-eth (str "ETH sul bilancio insufficiente " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Conferma transazione" + :other "Conferma {{count}} transazioni" + :zero "Nessuna transazione"} + :status "Stato" + :pending-confirmation "Conferma in attesa" + :recipient "Ricevente" + :one-more-item "Un altro oggetto" + :fee "Commissione" + :value "Valore" + + ;:webview + :web-view-error "ops, errore"}) diff --git a/src/status_im/translations/it_ch.cljs b/src/status_im/translations/it_ch.cljs new file mode 100644 index 0000000000..fe90f0c4be --- /dev/null +++ b/src/status_im/translations/it_ch.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.it-ch) + +(def translations + { + ;common + :members-title "Membri" + :not-implemented "!non implementato" + :chat-name "Nome chat" + :notifications-title "Notifiche e suoni" + :offline "Offline" + + ;drawer + :invite-friends "Invita amici" + :faq "FAQ" + :switch-users "Cambia utenti" + + ;chat + :is-typing "sta scrivendo" + :and-you "e tu" + :search-chat "Cerca chat" + :members {:one "1 membro" + :other "{{count}} membri" + :zero "nessun membro"} + :members-active {:one "1 membro, 1 attivo" + :other "{{count}} membri, {{count}} attivi" + :zero "nessun membro"} + :active-online "Online" + :active-unknown "Sconosciuto" + :available "Disponibile" + :no-messages "Nessun messaggio" + :suggestions-requests "Richieste" + :suggestions-commands "Comandi" + + ;sync + :sync-in-progress "Sincronizzazione in corso..." + :sync-synced "Sincronizzato" + + ;messages + :status-sending "Invio in corso" + :status-pending "In attesa di" + :status-sent "Inviato" + :status-seen-by-everyone "Visto da tutti" + :status-seen "Visto" + :status-delivered "Consegnato" + :status-failed "Invio fallito" + + ;datetime + :datetime-second {:one "secondo" + :other "secondi"} + :datetime-minute {:one "minuto" + :other "minuti"} + :datetime-hour {:one "ora" + :other "ore"} + :datetime-day {:one "giorno" + :other "giorni"} + :datetime-multiple "s" + :datetime-ago "fa" + :datetime-yesterday "ieri" + :datetime-today "oggi" + + ;profile + :profile "Profilo" + :report-user "SEGNALA UTENTE" + :message "Messaggio" + :username "Nome utente" + :not-specified "Non specificato" + :public-key "Chiave pubblica" + :phone-number "Numero di telefono" + :email "Email" + :profile-no-status "Nessuno stato" + :add-to-contacts "Aggiungi ai contatti" + :error-incorrect-name "Seleziona un altro nome" + :error-incorrect-email "Email errata" + + ;;make_photo + :image-source-title "Immagine profilo" + :image-source-make-photo "Scatta" + :image-source-gallery "Seleziona dalla galleria immagini" + :image-source-cancel "Annulla" + + ;sign-up + :contacts-syncronized "I tuoi contatti sono stati sincronizzati" + :confirmation-code (str "Grazie! Ti abbiamo inviato un messaggio con un codice di " + "conferma. Utilizza tale codice per confermare il tuo numero di telefono") + :incorrect-code (str "Il codice inserito è errato, riprova") + :generate-passphrase (str "Provvederò a generare una passphrase così potrai ripristinare il tuo " + "accesso o effettuare il login da un altro dispositivo") + :phew-here-is-your-passphrase "*Wow* È stato difficile, ecco qui la tua passphrase, *prendi nota e conservala in un luogo sicuro!* Ti servirà per ripristinare il tuo conto." + :here-is-your-passphrase "Ecco qui la tua passphrase, *prendi nota e conservala in un luogo sicuro!* Ti servirà per ripristinare il tuo conto." + :written-down "Assicurati di averla scritta correttamente" + :phone-number-required "Clicca qui per inserire il tuo numero di telefono e trovare i tuoi amici" + :intro-status "Avvia una conversazione con me per impostare il tuo conto e modificare le tue impostazioni!" + :intro-message1 "Benvenuto su Status\nTocca questo messaggio per impostare la tua password e iniziare!" + :account-generation-message "Dammi un secondo, devo eseguire dei calcoli matematici complessi per generare il tuo conto!" + + ;chats + :chats "Conversazioni" + :new-chat "Nuova conversazione" + :new-group-chat "Nuova conversazione di gruppo" + + ;discover + :discover "Scoperta" + :none "Nessuna" + :search-tags "Inserisci qui i tag di ricerca" + :popular-tags "Tag popolari" + :recent "Recente" + :no-statuses-discovered "Nessuno stato identificato" + + ;settings + :settings "Impostazioni" + + ;contacts + :contacts "Contatti" + :new-contact "Nuovo contatto" + :show-all "MOSTRA TUTTI" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Persone" + :contacts-group-new-chat "Inizia una nuova conversazione" + :no-contacts "Nessun contatto registrato" + :show-qr "Mostra QR" + + ;group-settings + :remove "Rimuovi" + :save "Salva" + :change-color "Cambia colore" + :clear-history "Cancella cronologia" + :delete-and-leave "Elimina ed esci" + :chat-settings "Impostazioni conversazioni" + :edit "Modifica" + :add-members "Aggiungi membri" + :blue "Blu" + :purple "Viola" + :green "Verde" + :red "Rosso" + + ;commands + :money-command-description "Invia denaro" + :location-command-description "Invia posizione" + :phone-command-description "Invia numero di telefono" + :phone-request-text "Richiesta numero di telefono" + :confirmation-code-command-description "Invia codice di conferma" + :confirmation-code-request-text "Richiesta codice di conferma" + :send-command-description "Invia posizione" + :request-command-description "Invia richiesta" + :keypair-password-command-description "" + :help-command-description "Aiuto" + :request "Richiedi" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH a {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH da {{chat-name}}" + :command-text-location "Posizione: {{address}}" + :command-text-browse "Pagina web: {{webpage}}" + :command-text-send "Transazione: {{amount}} ETH" + :command-text-help "Aiuto" + + ;new-group + :group-chat-name "Nome conversazione" + :empty-group-chat-name "Inserire un nome" + :illegal-group-chat-name "Selezionare un altro nome" + + ;participants + :add-participants "Aggiungi partecipanti" + :remove-participants "Rimuovi partecipanti" + + ;protocol + :received-invitation "ha ricevuto un invito di conversazione" + :removed-from-chat "ti ha rimosso dalla conversazione di gruppo" + :left "È uscito" + :invited "È stato invitato" + :removed "È stato rimosso" + :You "Tu" + + ;new-contact + :add-new-contact "Aggiungi nuovo contatto" + :import-qr "Importa" + :scan-qr "Scansiona QR" + :name "Nome" + :whisper-identity "Whisper Identity" + :address-explication "Forse qui dovremmo spiegare cos'è un indirizzo e dove cercarlo" + :enter-valid-address "Inserire un indirizzo valido oppure effettuare la scansione del codice QR" + :contact-already-added "Il contatto è già stato aggiunto" + :can-not-add-yourself "Non puoi aggiungere te stesso" + :unknown-address "Indirizzo sconosciuto" + + + ;login + :connect "Effettua connessione" + :address "Indirizzo" + :password "Password" + :login "Login" + :wrong-password "Password errata" + + ;recover + :recover-from-passphrase "Ripristina tramite passphrase" + :recover-explain "Inserire la passphrase per ripristinare la password di accesso" + :passphrase "Passphrase" + :recover "Ripristina" + :enter-valid-passphrase "Inserire una passphrase" + :enter-valid-password "Inserire una password" + + ;accounts + :recover-access "Ripristina l'accesso" + :add-account "Aggiungi conto" + + ;wallet-qr-code + :done "OK" + :main-wallet "Wallet principale" + + ;validation + :invalid-phone "Numero di telefono non valido" + :amount "Saldo" + :not-enough-eth (str "Saldo ETH non sufficiente " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Conferma transazione" + :other "Conferma {{count}} transazioni" + :zero "Nessuna transazione"} + :status "Stato" + :pending-confirmation "Conferma pendente" + :recipient "Beneficiario" + :one-more-item "Ancora un elemento" + :fee "Commissione" + :value "Valore" + + ;:webview + :web-view-error "Ops, si è verificato un errore"}) diff --git a/src/status_im/translations/ja.cljs b/src/status_im/translations/ja.cljs new file mode 100644 index 0000000000..0c55053afb --- /dev/null +++ b/src/status_im/translations/ja.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.ja) + +(def translations + { + ;common + :members-title "メンバー" + :not-implemented "!実装されていません" + :chat-name "チャット名" + :notifications-title "通知と音声" + :offline "オフライン" + + ;drawer + :invite-friends "友だちを招待" + :faq "よくある質問" + :switch-users "ユーザーを切り替え" + + ;chat + :is-typing "タイプ中です" + :and-you "そしてあなた" + :search-chat "チャットを検索" + :members {:one "1人のメンバー" + :other "{{count}}人のメンバー" + :zero "メンバーがいません"} + :members-active {:one "1人のメンバー、1人がアクティブ" + :other "{{count}}人のメンバー、{{count}}人がアクティブ" + :zero "メンバーがいません"} + :active-online "オンライン" + :active-unknown "不明" + :available "利用可能" + :no-messages "メッセージがありません" + :suggestions-requests "リクエスト" + :suggestions-commands "コマンド" + + ;sync + :sync-in-progress "同期中..." + :sync-synced "同期しています" + + ;messages + :status-sending "送信中" + :status-pending "保留中" + :status-sent "送信済み" + :status-seen-by-everyone "全員が閲覧" + :status-seen "閲覧" + :status-delivered "配達済み" + :status-failed "失敗しました" + + ;datetime + :datetime-second {:one "秒" + :other "秒"} + :datetime-minute {:one "分" + :other "分"} + :datetime-hour {:one "時間" + :other "時間"} + :datetime-day {:one "日" + :other "日"} + :datetime-multiple "秒" + :datetime-ago "前" + :datetime-yesterday "昨日" + :datetime-today "今日" + + ;profile + :profile "プロフィール" + :report-user "ユーザーを通報" + :message "メッセージ" + :username "ユーザー名" + :not-specified "特定されていません" + :public-key "公開鍵" + :phone-number "電話番号" + :email "メールアドレス" + :profile-no-status "ステータスがありません" + :add-to-contacts "連絡先に追加" + :error-incorrect-name "別の名前を選択してください" + :error-incorrect-email "間違ったメールアドレス" + + ;;make_photo + :image-source-title "プロフィール画像" + :image-source-make-photo "キャプチャ" + :image-source-gallery "ギャラリーから選択" + :image-source-cancel "キャンセル" + + ;sign-up + :contacts-syncronized "連絡先が同期されました" + :confirmation-code (str "ありがとうございます! 確認コードが記載されたメッセージが" + "送信されました。電話番号を確認するにはそのコードを入力してください") + :incorrect-code (str "申し訳ありませんがコードが間違っています。もう一度入力してください") + :generate-passphrase (str "パスフレーズを生成してアクセスを復元したり、" + "別のデバイスからログインしたりすることができます") + :phew-here-is-your-passphrase "*お疲れ様でした。*これがパスフレーズです。*書き留めて安全な場所に保管してください!*アカウントの復元に必要になります。" + :here-is-your-passphrase "これがパスフレーズです。*書き留めて安全な場所に保管してください!*アカウントの復元に必要になります。" + :written-down "書き留めて安全な場所に保管してください。" + :phone-number-required "ここをタップして電話番号を入力するとお友達を検索します。" + :intro-status "チャットしてアカウントを設定し、設定を変更してください!" + :intro-message1 "ステータスにようこそ\nこのメッセージをタップしてパスワードを設定して開始しましょう!" + :account-generation-message "少々お待ちください。アカウントを生成するには複雑な計算が必要です!" + + ;chats + :chats "チャット" + :new-chat "新規チャット" + :new-group-chat "新規グループチャット" + + ;discover + :discover "発見" + :none "なし" + :search-tags "ここに検索タグを入力してください" + :popular-tags "人気のタグ" + :recent "最近" + :no-statuses-discovered "ステータスが見つかりませんでした" + + ;settings + :settings "設定" + + ;contacts + :contacts "連絡先" + :new-contact "新規連絡先" + :show-all "全て表示" + :contacts-group-dapps "ÐApps" + :contacts-group-people "人々" + :contacts-group-new-chat "新規チャットを開始" + :no-contacts "まだ連絡先がありません" + :show-qr "QRを表示" + + ;group-settings + :remove "削除" + :save "保存" + :change-color "色を変更" + :clear-history "履歴を消去" + :delete-and-leave "削除して閉じる" + :chat-settings "チャット設定" + :edit "編集" + :add-members "メンバーを追加" + :blue "ブルー" + :purple "パープル" + :green "グリーン" + :red "レッド" + + ;commands + :money-command-description "送金" + :location-command-description "位置を送信" + :phone-command-description "電話番号を送信" + :phone-request-text "電話番号をリクエスト" + :confirmation-code-command-description "確認コードを送信" + :confirmation-code-request-text "確認コードリクエスト" + :send-command-description "位置を送信" + :request-command-description "リクエストを送信" + :keypair-password-command-description "" + :help-command-description "ヘルプ" + :request "リクエスト" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETHを{{chat-name}}に" + :chat-send-eth-from "{{amount}} ETHを{{chat-name}}から" + :command-text-location "位置: {{address}}" + :command-text-browse "ウェブページ閲覧: {{webpage}}" + :command-text-send "トランザクション: {{amount}} ETH" + :command-text-help "ヘルプ" + + ;new-group + :group-chat-name "チャット名" + :empty-group-chat-name "名前を入力してください" + :illegal-group-chat-name "別の名前を選択してください" + + ;participants + :add-participants "参加者を追加" + :remove-participants "参加者を削除" + + ;protocol + :received-invitation "チャット招待状を受信しました" + :removed-from-chat "グループチャットから削除されました" + :left "残り" + :invited "招待済み" + :removed "削除済み" + :You "あなた" + + ;new-contact + :add-new-contact "新規連絡先を追加" + :import-qr "インポート" + :scan-qr "QRをスキャン" + :name "名前" + :whisper-identity "Whisper Identity" + :address-explication "ここにアドレスについての説明やどこで見つけられるのかを入力してください" + :enter-valid-address "有効なアドレスを入力するかQRコードをスキャンしてください" + :contact-already-added "連絡先はすでに追加されています" + :can-not-add-yourself "自分自身を追加することはできません" + :unknown-address "不明のアドレス" + + + ;login + :connect "接続" + :address "アドレス" + :password "パスワード" + :login "ログイン" + :wrong-password "パスワードが間違っています" + + ;recover + :recover-from-passphrase "パスフレーズから復元" + :recover-explain "パスワードのためにパスフレーズを入力してアクセスを復元してください" + :passphrase "パスフレーズ" + :recover "復元" + :enter-valid-passphrase "パスフレーズを入力してください" + :enter-valid-password "パスワードを入力してください" + + ;accounts + :recover-access "アクセスを復元" + :add-account "アカウントを追加" + + ;wallet-qr-code + :done "完了" + :main-wallet "主要のウォレット" + + ;validation + :invalid-phone "無効な電話番号" + :amount "金額" + :not-enough-eth (str "十分なETH残高がありません" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "トランザクションを確認" + :other "{{count}}トランザクションを確認" + :zero "トランザクションがありません"} + :status "ステータス" + :pending-confirmation "確認が保留中です" + :recipient "受信者" + :one-more-item "さらに一つの項目" + :fee "手数料" + :value "値" + + ;:webview + :web-view-error "おっと、エラーが発生しました。"}) diff --git a/src/status_im/translations/ko.cljs b/src/status_im/translations/ko.cljs new file mode 100644 index 0000000000..c32e7359c7 --- /dev/null +++ b/src/status_im/translations/ko.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.ko) + +(def translations + { + ;common + :members-title "회원" + :not-implemented "!구현되지 않음" + :chat-name "채팅 이름" + :notifications-title "알림 및 소리" + :offline "오프라인" + + ;drawer + :invite-friends "친구 초대" + :faq "자주묻는질문" + :switch-users "사용자 전환" + + ;chat + :is-typing "이(가) 입력 중" + :and-you "그리고 귀하" + :search-chat "채팅 검색" + :members {:one "회원 1명" + :other "회원 {{count}}명" + :zero "회원 없음"} + :members-active {:one "회원 1명, 활성 1명" + :other "회원 {{count}}명, 활성 {{count}}명" + :zero "회원 없음"} + :active-online "온라인" + :active-unknown "알 수 없음" + :available "이용 가능" + :no-messages "메시지 없음" + :suggestions-requests "요청" + :suggestions-commands "명령" + + ;sync + :sync-in-progress "동기화 중..." + :sync-synced "동기화됨" + + ;messages + :status-sending "보내는 중" + :status-pending "계류중인" + :status-sent "전송됨" + :status-seen-by-everyone "모든 사람이 조회함" + :status-seen "조회함" + :status-delivered "배달됨" + :status-failed "실패" + + ;datetime + :datetime-second {:one "초" + :other "초"} + :datetime-minute {:one "분" + :other "분"} + :datetime-hour {:one "시간" + :other "시간"} + :datetime-day {:one "일" + :other "일"} + :datetime-multiple "초" + :datetime-ago "전" + :datetime-yesterday "어제" + :datetime-today "오늘" + + ;profile + :profile "프로필" + :report-user "사용자 신고하기" + :message "메시지" + :username "사용자명" + :not-specified "지정되지 않음" + :public-key "공용 키" + :phone-number "전화번호" + :email "이메일" + :profile-no-status "상태 정보 없음" + :add-to-contacts "연락처에 추가" + :error-incorrect-name "다음 이름을 선택해 주세요" + :error-incorrect-email "잘못된 이메일" + + ;;make_photo + :image-source-title "프로필 이미지" + :image-source-make-photo "캡쳐" + :image-source-gallery "갤러리에서 선택하기" + :image-source-cancel "취소" + + ;sign-up + :contacts-syncronized "연락처가 동기화되었습니다" + :confirmation-code (str "감사합니다! 귀하께 확인 문자 메시지를 보내드렸습니다 " + "코드. 귀하의 전화번호를 확인하려면 해당 코드를 입력해 주세요") + :incorrect-code (str "죄송하지만 잘못된 코드입니다. 다시 입력해 주세요") + :generate-passphrase (str "액세스를 복원하거나 다른 기기에서 로그인할 수 있도록 " + "암호문을 생성해 드리겠습니다") + :phew-here-is-your-passphrase "*휴~*어려운 작업이었네요, 여기 암호문이 있습니다. *이걸 적어두시고 안전하게 보관하세요!* 계정을 복구할 때 필요합니다." + :here-is-your-passphrase "여기 암호문이 있습니다. *이걸 적어두시고 안전하게 보관하세요!* 계정을 복구할 때 필요합니다." + :written-down "암호문을 안전하게 적어 두었는지 확인해 주세요" + :phone-number-required "여기를 탭하여 귀하의 전화번호를 입력하시면 친구들을 찾아드리겠습니다" + :intro-status "계정을 설정하고, 설정을 변경하려면 저와 채팅하세요!" + :intro-message1 "'상태'에 오신 것을 환영합니다\n이 메시지를 탭하여 비밀번호를 설정하고 시작하세요!" + :account-generation-message "잠깐만요, 귀하의 계정 생성을 위한 수학 연산 작업 좀 할게요!" + + ;chats + :chats "채팅" + :new-chat "새 채팅" + :new-group-chat "새 그룹 채팅" + + ;discover + :discover "발견" + :none "없음" + :search-tags "여기에 검색 태그를 입력하세요" + :popular-tags "인기 태그" + :recent "최근" + :no-statuses-discovered "발견된 상태 정보가 없습니다" + + ;settings + :settings "설정" + + ;contacts + :contacts "연락처" + :new-contact "새 연락처" + :show-all "모두 표시" + :contacts-group-dapps "ÐApps" + :contacts-group-people "사람들" + :contacts-group-new-chat "새 채팅 시작하기" + :no-contacts "연락처가 아직 없습니다" + :show-qr "QR 표시" + + ;group-settings + :remove "제거" + :save "저장" + :change-color "색상 변경" + :clear-history "내역 삭제" + :delete-and-leave "삭제하고 나가기" + :chat-settings "채팅 설정" + :edit "편집" + :add-members "회원 추가" + :blue "파란색" + :purple "자주색" + :green "녹색" + :red "빨간색" + + ;commands + :money-command-description "송금하기" + :location-command-description "위치정보 보내기" + :phone-command-description "전화번호 보내기" + :phone-request-text "전화번호 요청" + :confirmation-code-command-description "확인 코드 보내기" + :confirmation-code-request-text "확인 코드 요청" + :send-command-description "위치정보 보내기" + :request-command-description "요청 보내기" + :keypair-password-command-description "" + :help-command-description "도움말" + :request "요청" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH 송금인: {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH 수금인: {{chat-name}}" + :command-text-location "위치: {{address}}" + :command-text-browse "열람중인 웹페이지: {{webpage}}" + :command-text-send "거래: {{amount}} ETH" + :command-text-help "도움말" + + ;new-group + :group-chat-name "채팅 이름" + :empty-group-chat-name "이름을 입력해 주세요" + :illegal-group-chat-name "다른 이름을 선택해 주세요" + + ;participants + :add-participants "참가자 추가" + :remove-participants "참가자 제거" + + ;protocol + :received-invitation "채팅 초대를 받았습니다" + :removed-from-chat "귀하는 그룹 채팅에서 제거되었습니다" + :left "나감" + :invited "초대됨" + :removed "제거됨" + :You "귀하" + + ;new-contact + :add-new-contact "새 연락처 추가하기" + :import-qr "가져오기" + :scan-qr "QR코드 스캔" + :name "이름" + :whisper-identity "Whisper 아이디" + :address-explication "여기에는 주소가 어떻게 되는지, 그리고 해당 주소를 어디에서 찾을지를 설명하는 글이 있어야 합니다" + :enter-valid-address "유효한 주소를 입력하거나 QR 코드를 스캔해 주세요" + :contact-already-added "해당 연락처는 이미 추가되었습니다" + :can-not-add-yourself "나 자신을 추가할 수는 없습니다" + :unknown-address "알 수 없는 주소" + + + ;login + :connect "연결" + :address "주소" + :password "비밀번호" + :login "로그인" + :wrong-password "잘못된 비밀번호" + + ;recover + :recover-from-passphrase "암호문에서 복구하기" + :recover-explain "액세스를 복구하려면 비밀번호에 대한 암호문을 입력해 주세요" + :passphrase "암호문" + :recover "복구하기" + :enter-valid-passphrase "암호문을 입력해 주세요" + :enter-valid-password "비밀번호를 입력해 주세요" + + ;accounts + :recover-access "액세스 복구하기" + :add-account "계정 추가" + + ;wallet-qr-code + :done "완료" + :main-wallet "주요 지갑" + + ;validation + :invalid-phone "잘못된 전화번호" + :amount "금액" + :not-enough-eth (str "ETH 잔고가 부족합니다 " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "거래 확인" + :other "{{count}}개의 거래 확인" + :zero "거래 없음"} + :status "상태" + :pending-confirmation "보류 중인 확인" + :recipient "수취인" + :one-more-item "항목 하나 더" + :fee "요금" + :value "값" + + ;:webview + :web-view-error "이런, 오류입니다"}) diff --git a/src/status_im/translations/nl.cljs b/src/status_im/translations/nl.cljs new file mode 100644 index 0000000000..ce5bf20061 --- /dev/null +++ b/src/status_im/translations/nl.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.nl) + +(def translations + { + ;common + :members-title "Leden" + :not-implemented "!niet geïmplementeerd" + :chat-name "Chatnaam" + :notifications-title "Meldingen en geluiden" + :offline "Offline" + + ;drawer + :invite-friends "Nodig vrienden uit" + :faq "FAQ" + :switch-users "Schakel tussen gebruikers" + + ;chat + :is-typing "typt" + :and-you "en jij" + :search-chat "Zoek in de chat" + :members {:one "1 lid" + :other "{{count}} leden" + :zero "geen leden"} + :members-active {:one "1 lid, 1 actieve" + :other "{{count}} leden, {{count}} actieve" + :zero "geen leden"} + :active-online "Online" + :active-unknown "Onbekend" + :available "Beschikbaar" + :no-messages "Geen berichten" + :suggestions-requests "Aanvragen" + :suggestions-commands "Commando’s" + + ;sync + :sync-in-progress "Wordt gesynchroniseerd..." + :sync-synced "Gesynchroniseerd" + + ;messages + :status-sending "Wordt verstuurd" + :status-pending "Hangende" + :status-sent "Verstuurd" + :status-seen-by-everyone "Door iedereen gezien" + :status-seen "Gezien" + :status-delivered "Bezorgd" + :status-failed "Mislukt" + + ;datetime + :datetime-second {:one "seconde" + :other "seconden"} + :datetime-minute {:one "minuut" + :other "minuten"} + :datetime-hour {:one "uur" + :other "uren"} + :datetime-day {:one "dag" + :other "dagen"} + :datetime-multiple "s" + :datetime-ago "geleden" + :datetime-yesterday "gisteren" + :datetime-today "vandaag" + + ;profile + :profile "Profiel" + :report-user "MELD GEBRUIKER" + :message "Bericht" + :username "Gebruikersnaam" + :not-specified "Niet opgegeven" + :public-key "Openbare sleutel" + :phone-number "Telefoonnummer" + :email "E-mailadres" + :profile-no-status "Geen status" + :add-to-contacts "Aan contactpersonen toevoegen" + :error-incorrect-name "Kies een andere naam" + :error-incorrect-email "Onjuist e-mailadres" + + ;;make_photo + :image-source-title "Profielfoto" + :image-source-make-photo "Foto nemen" + :image-source-gallery "Kies uit galerij" + :image-source-cancel "Annuleren" + + ;sign-up + :contacts-syncronized "Jouw contactpersonen zijn gesynchroniseerd" + :confirmation-code (str "Bedankt! We hebben je een sms gestuurd met een bevestigingscode" + ". Geef die code op om jouw telefoonnummer te bevestigen") + :incorrect-code (str "Sorry, de code was onjuist, voer hem opnieuw in") + :generate-passphrase (str "Ik zal een wachtzin maken, zodat je jouw" + "toegang kunt herstellen of vanaf een ander apparaat kunt inloggen") + :phew-here-is-your-passphrase "*Foei* dat was moeilijk, hier is jouw wachtzin, *schrijf deze op en bewaar hem goed!* Je zult hem nodig hebben om jouw account te herstellen." + :here-is-your-passphrase "Hier is jouw wachtzin, * schrijf deze op en bewaar hem goed!* Je zult hem nodig hebben om jouw account te herstellen." + :written-down "Zorg ervoor dat je hem veilig hebt opgeschreven" + :phone-number-required "Tik hier om je telefoonnummer in te voeren, dan zoek ik jouw vrienden" + :intro-status "Chat met me om jouw account in te stellen en jouw instellingen te wijzigen!" + :intro-message1 "Welkom bij Status\nTik op dit bericht om jouw wachtwoord in te stellen en aan de slag te gaan!" + :account-generation-message "Geef me een momentje, ik moet wat ingewikkelde berekeningen doen om jouw account aan te maken!" + + ;chats + :chats "Chats" + :new-chat "Nieuwe chat" + :new-group-chat "Nieuwe groepchat" + + ;discover + :discover "Ontdekking" + :none "Geen" + :search-tags "Typ hier jouw zoektags" + :popular-tags "Populaire tags" + :recent "Recent" + :no-statuses-discovered "Geen statussen ontdekt" + + ;settings + :settings "Instellingen" + + ;contacts + :contacts "Contactpersonen" + :new-contact "Nieuwe contactpersonen" + :show-all "TOON ALLES" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Mensen" + :contacts-group-new-chat "Start nieuwe chat" + :no-contacts "Nog geen contactpersonen" + :show-qr "Toon QR" + + ;group-settings + :remove "Verwijderen" + :save "Opslaan" + :change-color "Wijzig kleur" + :clear-history "Wis geschiedenis" + :delete-and-leave "Verwijderen en afsluiten" + :chat-settings "Chatinstellingen" + :edit "Bewerken" + :add-members "Voeg leden toe" + :blue "Blauw" + :purple "Paars" + :green "Groen" + :red "Rood" + + ;commands + :money-command-description "Stuur geld" + :location-command-description "Stuur locatie" + :phone-command-description "Stuur telefoonnummer" + :phone-request-text "Telefoonnummer aanvraag" + :confirmation-code-command-description "Stuur bevestigingscode" + :confirmation-code-request-text "Bevestigingscode aanvraag" + :send-command-description "Stuur locatie" + :request-command-description "Stuur aanvraag" + :keypair-password-command-description "" + :help-command-description "Help" + :request "Aanvraag" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH naar {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH van {{chat-name}}" + :command-text-location "Locatie: {{address}}" + :command-text-browse "Browsen op internetpagina: {{webpage}}" + :command-text-send "Transactie: {{amount}} ETH" + :command-text-help "Help" + + ;new-group + :group-chat-name "Chatnaam" + :empty-group-chat-name "Voer een naam in" + :illegal-group-chat-name "Kies een andere naam" + + ;participants + :add-participants "Voeg deelnemers toe" + :remove-participants "Verwijder deelnemers" + + ;protocol + :received-invitation "ontving chatuitnodiging" + :removed-from-chat "verwijderde jou van groepchat" + :left "ging weg" + :invited "uitgenodigd" + :removed "verwijderd" + :You "Jou" + + ;new-contact + :add-new-contact "Voeg nieuwe contactpersoon toe" + :import-qr "Importeren" + :scan-qr "QR scannen" + :name "Naam" + :whisper-identity "Fluister identiteit" + :address-explication "Misschien zou hier wat tekst moeten staan waarin wordt uitgelegd wat een adres is en waar je deze kunt vinden" + :enter-valid-address "Voer een geldig adres in of scan een QR-code" + :contact-already-added "De contactpersoon is al toegevoegd" + :can-not-add-yourself "Je kunt niet zelf toevoegen" + :unknown-address "Onbekend adres" + + + ;login + :connect "Verbinden" + :address "Adres" + :password "Wachtwoord" + :login "Inloggen" + :wrong-password "Verkeerd wachtwoord" + + ;recover + :recover-from-passphrase "Herstellen met wachtzin" + :recover-explain "Voer de wachtzin in voor jouw wachtwoord om toegang te herstellen" + :passphrase "Wachtzin" + :recover "Herstellen" + :enter-valid-passphrase "Voer een wachtzin in" + :enter-valid-password "Voer een wachtwoord in" + + ;accounts + :recover-access "Toegang herstellen" + :add-account "Voeg account toe" + + ;wallet-qr-code + :done "Klaar" + :main-wallet "Hoofdportemonnee" + + ;validation + :invalid-phone "Ongeldig telefoonnummer" + :amount "Bedrag" + :not-enough-eth (str "Niet genoeg ETH op saldo" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Bevestig transactie" + :other "Bevestig {{count}} transacties" + :zero "Geen transacties"} + :status "Status" + :pending-confirmation "In afwachting van bevestiging" + :recipient "Ontvanger" + :one-more-item "Nog één artikel" + :fee "Kosten" + :value "Waarde" + + ;:webview + :web-view-error "oeps, fout"}) diff --git a/src/status_im/translations/pl.cljs b/src/status_im/translations/pl.cljs new file mode 100644 index 0000000000..cd175f28f4 --- /dev/null +++ b/src/status_im/translations/pl.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.pl) + +(def translations + { + ;common + :members-title "Użytkownicy" + :not-implemented "nie wprowadzono" + :chat-name "Nazwa czatu" + :notifications-title "Powiadomienia i dźwięki" + :offline "Offline" + + ;drawer + :invite-friends "Zaproś znajomych" + :faq "FAQ" + :switch-users "Przełącz użytkowników" + + ;chat + :is-typing "wpisuje tekst" + :and-you "i ty" + :search-chat "Przeszukaj czat" + :members {:one "1 użytkownik" + :other "Użytkownicy: {{count}}" + :zero "brak użytkowników "} + :members-active {:one "1 użytkownik, 1 aktywny" + :other "Użytkownicy:{{count}}, aktywni:{{count}}" + :zero "brak użytkowników"} + :active-online "Online" + :active-unknown "Nieznany" + :available "Dostępny" + :no-messages "Brak wiadomości" + :suggestions-requests "Żądania" + :suggestions-commands "Polecenia" + + ;sync + :sync-in-progress "Synchronizacja..." + :sync-synced "W synchronizacji" + + ;messages + :status-sending "Wysyłanie" + :status-pending "W oczekiwaniu" + :status-sent "Wysłano" + :status-seen-by-everyone "Przeczytane przez wszystkich" + :status-seen "Przeczytane" + :status-delivered "Dostarczono" + :status-failed "Niepowodzenie" + + ;datetime + :datetime-second {:one "sekunda" + :other "sekund(y)"} + :datetime-minute {:one "minuta" + :other "minut(y)"} + :datetime-hour {:one "godzina" + :other "godziny(y)"} + :datetime-day {:one "dzień" + :other "dni"} + :datetime-multiple "s" + :datetime-ago "temu" + :datetime-yesterday "wczoraj" + :datetime-today "dzisiaj" + + ;profile + :profile "Profil" + :report-user "ZGŁOŚ UŻYTOWNIKA" + :message "Wiadomość" + :username "Nazwa użytkownika" + :not-specified "Nie określono" + :public-key "Klucz publiczny" + :phone-number "Numer telefonu " + :email "E-mail" + :profile-no-status "Brak statusu" + :add-to-contacts "Dodaj do kontaktów" + :error-incorrect-name "Wybierz inną nazwę " + :error-incorrect-email "Nieprawidłowy e-mail" + + ;;make_photo + :image-source-title "Zdjęcie profilowe " + :image-source-make-photo "Przechwyć" + :image-source-gallery "Wybierz z galerii " + :image-source-cancel "Anuluj" + + ;sign-up + :contacts-syncronized "Twoje kontakty zostały zsynchronizowane" + :confirmation-code (str "Dziękujemy! Wysłaliśmy ci SMS-a z kodem" + "potwierdzającym. Prosimy o podanie kodu w celu zweryfikowania swojego numeru telefonu") + :incorrect-code (str "Przepraszamy, kod jest nieprawidłowy. Prosimy wprowadzić kod ponownie") + :generate-passphrase (str "Wygenerujemy specjalne hasło, dzięki któremu będzie można przywrócić " + "dostęp lub zalogować się z innego urządzenia") + :phew-here-is-your-passphrase "*Uff*, to nie było łatwe. Oto twoje specjalne hasło, *zapisz je i przechowuj w bezpiecznym miejscu!* Będzie ci potrzebne podczas procedury odzyskiwania konta." + :here-is-your-passphrase "Oto twoje specjalne hasło, *zapisz je i przechowuj w bezpiecznym miejscu!* Będzie ci potrzebne podczas procedury odzyskiwania konta." + :written-down "Upewnij się, że je zapisałeś i przechowujesz w bezpiecznym miejscu" + :phone-number-required "Dotknij tutaj, aby wprowadzić swój numer telefonu, a my znajdziemy twoich znajomych " + :intro-status "Porozmawiaj ze mną na czacie, aby skonfigurować swoje konto i zmienić ustawienia!" + :intro-message1 "Witamy w sekcji Status\nWybierz tę wiadomość, aby ustawić hasło i rozpocząć!" + :account-generation-message "Daj mi chwilkę, muszę wykonać szalone obliczenia, żeby utworzyć dla Ciebie konto!" + + ;chats + :chats "Czaty" + :new-chat "Nowy czat" + :new-group-chat "Nowy czat grupowy" + + ;discover + :discover "Odkryte" + :none "Brak" + :search-tags "Tutaj wpisz swoje tagi wyszukiwania" + :popular-tags "Popularne tagi" + :recent "Najnowsze" + :no-statuses-discovered "Nie odkryto statusów" + + ;settings + :settings "Ustawienia" + + ;contacts + :contacts "Kontakty" + :new-contact "Nowy kontakt" + :show-all "POKAŻ WSZYSTKIE" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Ludzie" + :contacts-group-new-chat "Rozpocznij nowy czat" + :no-contacts "W tej chwili brak kontaktów" + :show-qr "Pokaż QR" + + ;group-settings + :remove "Usuń" + :save "Zapisz" + :change-color "Zmień kolor" + :clear-history "Wyczyść historię" + :delete-and-leave "Usuń i wyjdź" + :chat-settings "Ustawienia czatu" + :edit "Edytuj" + :add-members "Dodaj użytkowników" + :blue "Niebieski" + :purple "Fioletowy" + :green "Zielony" + :red "Czerwony" + + ;commands + :money-command-description "Wyślij pieniądze" + :location-command-description "Wyślij lokalizację" + :phone-command-description "Wyślij numer telefonu" + :phone-request-text "Prośba o numer telefonu" + :confirmation-code-command-description "Wyślij kod potwierdzający" + :confirmation-code-request-text "Prośba o kod potwierdzający" + :send-command-description "Wyślij lokalizację" + :request-command-description "Wyślij prośbę" + :keypair-password-command-description "" + :help-command-description "Pomoc" + :request "Prośba" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH do {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH od {{chat-name}}" + :command-text-location "Lokalizacja: {{address}}" + :command-text-browse "Przeglądana strona: {{webpage}}" + :command-text-send "Transakcja: {{amount}} ETH" + :command-text-help "Pomoc" + + ;new-group + :group-chat-name "Nazwa czatu" + :empty-group-chat-name "Wprowadź nazwę" + :illegal-group-chat-name "Wybierz inną nazwę" + + ;participants + :add-participants "Dodaj uczestników" + :remove-participants "Usuń uczestników" + + ;protocol + :received-invitation "otrzymano zaproszenie na czat" + :removed-from-chat "usunięto z czatu grupowego" + :left "pozostało" + :invited "zaproszeni" + :removed "usunięci" + :You "Ty" + + ;new-contact + :add-new-contact "Dodaj nowy kontakt" + :import-qr "Importuj" + :scan-qr "Skanuj QR" + :name "Nazwa" + :whisper-identity "Sekretna tożsamość" + :address-explication "Być może tutaj powinien znajdować się tekst wyjaśniający, czym jest adres i gdzie go szukać" + :enter-valid-address "Wprowadź prawidłowy adres lub przeskanuj kod QR" + :contact-already-added "Kontakt został już dodany" + :can-not-add-yourself "Nie możesz dodać samego siebie" + :unknown-address "Nieznany adres" + + + ;login + :connect "Połącz" + :address "Adres" + :password "Hasło" + :login "Login" + :wrong-password "Nieprawidłowe hasło" + + ;recover + :recover-from-passphrase "Odzyskaj na podstawie hasła specjalnego" + :recover-explain "Wprowadź hasło specjalne dla swojego hasła, aby odzyskać dostęp" + :passphrase "Hasło specjalne" + :recover "Odzyskaj" + :enter-valid-passphrase "Wprowadź hasło specjalne" + :enter-valid-password "Wprowadź hasło" + + ;accounts + :recover-access "Odzyskaj dostęp" + :add-account "Dodaj konto " + + ;wallet-qr-code + :done "Zrobione" + :main-wallet "Portfel główny" + + ;validation + :invalid-phone "Nieprawidłowy numer telefonu" + :amount "Kwota" + :not-enough-eth (str "Brak ETH na koncie " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Potwierdź transakcję" + :other "Potwierdź {{count}} transakcje(-i)" + :zero "Brak transakcji"} + :status "Status" + :pending-confirmation "Oczekuje na potwierdzenie" + :recipient "Odbiorca" + :one-more-item "Dodatkowy element" + :fee "Opłata" + :value "Wartość" + + ;:webview + :web-view-error "oj, mamy błąd"}) \ No newline at end of file diff --git a/src/status_im/translations/pt_br.cljs b/src/status_im/translations/pt_br.cljs new file mode 100644 index 0000000000..f85a272ae4 --- /dev/null +++ b/src/status_im/translations/pt_br.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.pt-br) + +(def translations + { + ;common + :members-title "Membros" + :not-implemented "!não implementado" + :chat-name "Nome do bate-papo" + :notifications-title "Notificações e sons" + :offline "Offline" + + ;drawer + :invite-friends "Convidar amigos" + :faq "Dúvidas frequentes" + :switch-users "Trocar usuário" + + ;chat + :is-typing "está digitando" + :and-you "e você" + :search-chat "Pesquisar bate-papo" + :members {:one "1 membro" + :other "{{count}} membros" + :zero "nenhum membro"} + :members-active {:one "1 membro, 1 ativo" + :other "{{count}} membros, {{count}} ativo" + :zero "nenhum membro"} + :active-online "Online" + :active-unknown "Desconhecido" + :available "Disponível" + :no-messages "Nenhuma mensagem" + :suggestions-requests "Solicitações" + :suggestions-commands "Comandos" + + ;sync + :sync-in-progress "Sincronizando..." + :sync-synced "Sincronizado" + + ;messages + :status-sending "Enviando" + :status-pending "Pendente" + :status-sent "Enviado" + :status-seen-by-everyone "Visto por todos" + :status-seen "Visto" + :status-delivered "Entregue" + :status-failed "Malsucedido" + + ;datetime + :datetime-second {:one "segundo" + :other "segundos"} + :datetime-minute {:one "minuto" + :other "minutos"} + :datetime-hour {:one "hora" + :other "horas"} + :datetime-day {:one "dia" + :other "dias"} + :datetime-multiple "s" + :datetime-ago "atrás" + :datetime-yesterday "ontem" + :datetime-today "hoje" + + ;profile + :profile "Perfil" + :report-user "DENUNCIAR USUÁRIO" + :message "Mensagem" + :username "Nome de usuário" + :not-specified "Não especificao" + :public-key "Chave pública" + :phone-number "Número de telefone" + :email "E-mail" + :profile-no-status "Nenhum status" + :add-to-contacts "Adicionar aos contatos" + :error-incorrect-name "Por favor, selecione outro nome" + :error-incorrect-email "E-mail incorreto" + + ;;make_photo + :image-source-title "Imagem do perfil" + :image-source-make-photo "Tirar foto" + :image-source-gallery "Escolher na galeria" + :image-source-cancel "Cancelar" + + ;sign-up + :contacts-syncronized "Seus contatos foram sincronizados" + :confirmation-code (str "Obrigado! Nós lhe enviamos uma mensagem de texto com um código de " + "confirmação. Por favor, informe esse código para confirmar seu número de telefone") + :incorrect-code (str "Desculpe, o código estava incorreto. Por favor, digite novamente") + :generate-passphrase (str "Vou gerar uma frase secreta para você poder restaurar o seu " + "acesso ou entrar a partir de outro dispositivo") + :phew-here-is-your-passphrase "*Ufa* isso foi difícil. Aqui está a sua frase secreta. *Anote-a e guarde-a em segurança!* Você precisará dela para recuperar a sua conta." + :here-is-your-passphrase "Aqui está a sua frase secreta. *Anote-a e guarde-a em segurança!* Você precisará dela para recuperar a sua conta." + :written-down "Certifique-se de tê-la anotado em segurança" + :phone-number-required "Toque aqui para inserir seu número de telefone e eu vou encontrar seus amigos" + :intro-status "Converse comigo para configurar a sua conta e alterar suas definições!" + :intro-message1 "Bem-vindo ao Status\nToque nesta mensagem para definir sua senha e começar!" + :account-generation-message "Mê dê um segundinho. Tenho de fazer uns cálculos malucos para gerar a sua conta!" + + ;chats + :chats "Bate-papos" + :new-chat "Novo bate-papo" + :new-group-chat "Novo bate-papo em grupo" + + ;discover + :discover "Descoberta" + :none "Nenhum(a)" + :search-tags "Digite suas tags de pesquisa aqui" + :popular-tags "Tags populares" + :recent "Recentes" + :no-statuses-discovered "Nenhum status descoberto" + + ;settings + :settings "Configurações" + + ;contacts + :contacts "Contatos" + :new-contact "Novo contato" + :show-all "MOSTRAR TODOS" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Pessoas" + :contacts-group-new-chat "Iniciar novo bate-papo" + :no-contacts "Você ainda não tem contatos" + :show-qr "Mostrar QR" + + ;group-settings + :remove "Remover" + :save "Salvar" + :change-color "Alterar cor" + :clear-history "Apagar histórico" + :delete-and-leave "Excluir e sair" + :chat-settings "Configurações de bate-papo" + :edit "Editar" + :add-members "Adicionar membros" + :blue "Azul" + :purple "Roxo" + :green "Verde" + :red "Vermelho" + + ;commands + :money-command-description "Enviar dinheiro" + :location-command-description "Enviar localização" + :phone-command-description "Enviar número de telefone" + :phone-request-text "Solicitação de número de telefone" + :confirmation-code-command-description "Enviar código de confirmação" + :confirmation-code-request-text "Solicitação de código de confirmação" + :send-command-description "Enviar localização" + :request-command-description "Enviar solicitação" + :keypair-password-command-description "" + :help-command-description "Ajuda" + :request "Solicitar" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH para {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de {{chat-name}}" + :command-text-location "Localização: {{address}}" + :command-text-browse "Página de navegação: {{webpage}}" + :command-text-send "Transação: {{amount}} ETH" + :command-text-help "Ajuda" + + ;new-group + :group-chat-name "Nome do bate-papo" + :empty-group-chat-name "Por favor, informe um nome" + :illegal-group-chat-name "Por favor, selecione outro nome" + + ;participants + :add-participants "Adicionar participantes" + :remove-participants "Remover participantes" + + ;protocol + :received-invitation "recebeu o convite para o bate-papo" + :removed-from-chat "removeu você do bate-papo em grupo" + :left "saiu" + :invited "convidou" + :removed "removeu" + :You "Você" + + ;new-contact + :add-new-contact "Adicionar novo contato" + :import-qr "Importar" + :scan-qr "Escanear QR" + :name "Nome" + :whisper-identity "Identidade Whisper" + :address-explication "Talvez aqui deveria haver algum texto explicando o que é um endereço e onde procurá-lo" + :enter-valid-address "Por favor, digite um endereço válido ou escaneie um código QR" + :contact-already-added "O contato já foi adicionado" + :can-not-add-yourself "Não é possível adicionar a si mesmo" + :unknown-address "E-mail desconhecido" + + + ;login + :connect "Conectar" + :address "Endereço" + :password "Senha" + :login "Entrar" + :wrong-password "Senha incorreta" + + ;recover + :recover-from-passphrase "Recuperar a partir da frase secreta" + :recover-explain "Por favor, digite a frase secreta da sua senha para recuperar o acesso" + :passphrase "Frase secreta" + :recover "Recuperar" + :enter-valid-passphrase "Por favor, digite uma frase secreta" + :enter-valid-password "Por favor, digite uma senha" + + ;accounts + :recover-access "Recuperar o acesso" + :add-account "Adicionar conta" + + ;wallet-qr-code + :done "Concluído" + :main-wallet "Carteira principal" + + ;validation + :invalid-phone "Número de telefone inválido" + :amount "Quantia" + :not-enough-eth (str "ETH insuficiente no saldo " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmar a transação" + :other "Confirmar {{count}} transações" + :zero "Nenhuma transação"} + :status "Status" + :pending-confirmation "Confirmação pendente" + :recipient "Destinatário" + :one-more-item "Mais um item" + :fee "Tarifa" + :value "Valor" + + ;:webview + :web-view-error "Opa, erro"}) diff --git a/src/status_im/translations/pt_pt.cljs b/src/status_im/translations/pt_pt.cljs new file mode 100644 index 0000000000..3adc760a2d --- /dev/null +++ b/src/status_im/translations/pt_pt.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.pt-pt) + +(def translations + { + ;common + :members-title "Membros" + :not-implemented "!não implementado" + :chat-name "Nome no chat" + :notifications-title "Notificações e sons" + :offline "Offline" + + ;drawer + :invite-friends "Convidar amigos" + :faq "FAQ" + :switch-users "Mudar de utilizadores" + + ;chat + :is-typing "está a digitar" + :and-you "e você" + :search-chat "Pesquisar chat" + :members {:one "1 membro" + :other "{{count}} membros" + :zero "sem membros"} + :members-active {:one "1 membro, 1 ativo" + :other "{{count}} membros, {{count}} ativos" + :zero "sem membros"} + :active-online "Online" + :active-unknown "Desconhecido" + :available "Disponível" + :no-messages "Sem mensagens" + :suggestions-requests "Pedidos" + :suggestions-commands "Comandos" + + ;sync + :sync-in-progress "A sincronizar..." + :sync-synced "Em sincronização" + + ;messages + :status-sending "A enviar" + :status-pending "Pendente" + :status-sent "Enviado" + :status-seen-by-everyone "Visto por todos" + :status-seen "Visto" + :status-delivered "Entregue" + :status-failed "Falhou" + + ;datetime + :datetime-second {:one "segundo" + :other "segundos"} + :datetime-minute {:one "minuto" + :other "minutos"} + :datetime-hour {:one "hora" + :other "horas"} + :datetime-day {:one "dia" + :other "dias"} + :datetime-multiple "s" + :datetime-ago "atrás" + :datetime-yesterday "ontem" + :datetime-today "hoje" + + ;profile + :profile "Perfil" + :report-user "DENUNCIAR O UTILIZADOR" + :message "Mensagem" + :username "Nome de utilizador" + :not-specified "Não especificado" + :public-key "Chave Pública" + :phone-number "Número de telefone" + :email "E-mail" + :profile-no-status "Sem estado" + :add-to-contacts "Adicionar aos contactos" + :error-incorrect-name "Por favor, selecione outro nome" + :error-incorrect-email "E-mail incorreto" + + ;;make_photo + :image-source-title "Imagem de perfil" + :image-source-make-photo "Capturar" + :image-source-gallery "Selecionar a partir da galeria" + :image-source-cancel "Cancelar" + + ;sign-up + :contacts-syncronized "Os seus contactos foram sincronizados" + :confirmation-code (str "Obrigado! Enviámos-lhe uma mensagem de texto com um código de " + "confirmação. Por favor, forneça esse código para confirmar o seu número de telefone") + :incorrect-code (str "Desculpe, o código estava incorreto. Por favor, volte a digitar") + :generate-passphrase (str "Vou gerar uma frase-chave para si para poder recuperar o seu " + "acesso ou log in a partir de outro dispositivo") + :phew-here-is-your-passphrase "*Ufa* foi complicado, aqui está a sua frase-chave, *anote-a e mantenha-a em segurança!* Vai precisar dela para recuperar a sua conta." + :here-is-your-passphrase "Aqui está a sua frase-chave, *anote-a e mantenha-a em segurança!* Vai precisar dela para recuperar a sua conta." + :written-down "Certifique-se de que a anotou de forma segura" + :phone-number-required "Toque aqui para digitar o seu número de telefone e vou encontrar os seus amigos" + :intro-status "Fale comigo para configurar a sua conta e alterar as suas definições!" + :intro-message1 "Bem-vindo ao Status\nToque nesta mensagem para definir a sua palavra-passe e começar!" + :account-generation-message "Dê-me um segundo, tenho de fazer alguns cálculos doidos para gerar a sua conta!" + + ;chats + :chats "Chats" + :new-chat "Novo chat" + :new-group-chat "Novo chat em grupo" + + ;discover + :discover "Descoberta" + :none "Nenhum" + :search-tags "Digite aqui os seus tags de pesquisa" + :popular-tags "Tags populares" + :recent "Recente" + :no-statuses-discovered "Não foi descoberto nenhum estado" + + ;settings + :settings "Definições" + + ;contacts + :contacts "Contactos" + :new-contact "Novo Contacto" + :show-all "MOSTRAR TUDO" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Pessoas" + :contacts-group-new-chat "Iniciar um novo chat" + :no-contacts "Ainda sem contactos" + :show-qr "Mostrar QR" + + ;group-settings + :remove "Remover" + :save "Guardar" + :change-color "Mudar de cor" + :clear-history "Limpar o histórico" + :delete-and-leave "Eliminar e sair" + :chat-settings "Definições de chat" + :edit "Editar" + :add-members "Adicionar Membros" + :blue "Azul" + :purple "Roxo" + :green "Verde" + :red "Vermelho" + + ;commands + :money-command-description "Enviar dinheiro" + :location-command-description "Enviar a localização" + :phone-command-description "Enviar o número de telefone" + :phone-request-text "Pedido de número de telefone" + :confirmation-code-command-description "Enviar o código de confirmação" + :confirmation-code-request-text "Pedido de código de confirmação" + :send-command-description "Enviar a localização" + :request-command-description "Enviar o pedido" + :keypair-password-command-description "" + :help-command-description "Ajuda" + :request "Pedido" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH para {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de {{chat-name}}" + :command-text-location "Localização: {{address}}" + :command-text-browse "Página Web de Navegação: {{webpage}}" + :command-text-send "Transação: {{amount}} ETH" + :command-text-help "Ajuda" + + ;new-group + :group-chat-name "Nome no chat" + :empty-group-chat-name "Por favor, digite um nome" + :illegal-group-chat-name "Por favor, selecione outro nome" + + ;participants + :add-participants "Adicionar Participantes" + :remove-participants "Remover os Participantes" + + ;protocol + :received-invitation "recebeu um convite de chat" + :removed-from-chat "removeu-o do chat em grupo" + :left "saiu" + :invited "convidado" + :removed "removido" + :You "Você" + + ;new-contact + :add-new-contact "Adicionar novo contacto" + :import-qr "Importar" + :scan-qr "Digitalizar QR" + :name "Nome" + :whisper-identity "Sussurar a Identidade" + :address-explication "Talvez aqui devesse aparecer algum texto a explicar o que é um endereço e onde procurá-lo" + :enter-valid-address "Por favor, digite um endereço válido ou digitalize um código QR" + :contact-already-added "O contacto já foi adicionado" + :can-not-add-yourself "Não pode adicionar-se a si mesmo" + :unknown-address "Endereço desconhecido" + + + ;login + :connect "Ligar" + :address "Endereço" + :password "Palavra-passe" + :login "Login" + :wrong-password "Palavra-passe errada" + + ;recover + :recover-from-passphrase "Recuperar a partir da frase-chave" + :recover-explain "Por favor, digite a frase-chave para a sua palavra-passe recuperar o acesso" + :passphrase "Frase-chave" + :recover "Recuperar" + :enter-valid-passphrase "Por favor, digite uma frase-chave" + :enter-valid-password "Por favor, digite uma palavra-passe" + + ;accounts + :recover-access "Recuperar o acesso" + :add-account "Adicionar conta" + + ;wallet-qr-code + :done "Concluído" + :main-wallet "Carteira Principal" + + ;validation + :invalid-phone "Número de telefone inválido" + :amount "Montante" + :not-enough-eth (str "Não há ETH suficiente no saldo " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmar a transação" + :other "Confirmar as {{count}} transações" + :zero "Sem transações"} + :status "Estado" + :pending-confirmation "Confirmação pendente" + :recipient "Destinatário" + :one-more-item "Mais um item" + :fee "Taxa" + :value "Valor" + + ;:webview + :web-view-error "ups, erro"}) diff --git a/src/status_im/translations/ro.cljs b/src/status_im/translations/ro.cljs new file mode 100644 index 0000000000..f8b6526a62 --- /dev/null +++ b/src/status_im/translations/ro.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.ro) + +(def translations + { + ;common + :members-title "Membri" + :not-implemented "!nu a fost implementat" + :chat-name "Nume chat" + :notifications-title "Notificări și sunete" + :offline "Offline" + + ;drawer + :invite-friends "Invită prieteni" + :faq "Întrebări frecvente" + :switch-users "Schimbă utilizatori" + + ;chat + :is-typing "tastează" + :and-you "Și cu tine" + :search-chat "Caută în chat" + :members {:one "1 membru" + :other "{{count}} membri" + :zero "nici un membru"} + :members-active {:one "1 membru, 1 activ" + :other "{{count}} membri, {{count}} activi" + :zero "nici un membru"} + :active-online "Online" + :active-unknown "Necunoscut" + :available "Disponibil" + :no-messages "Nici un mesaj" + :suggestions-requests "Solicitări" + :suggestions-commands "Comenzi" + + ;sync + :sync-in-progress "Se sincronizează…" + :sync-synced "Sincronizat" + + ;messages + :status-sending "Se trimite" + :status-pending "În așteptare" + :status-sent "Trimis" + :status-seen-by-everyone "Văzut de toată lumea" + :status-seen "Văzut" + :status-delivered "Livrat" + :status-failed "Eșuat" + + ;datetime + :datetime-second {:one "secundă" + :other "secunde"} + :datetime-minute {:one "minut" + :other "minute"} + :datetime-hour {:one "oră" + :other "ore"} + :datetime-day {:one "zi" + :other "zile"} + :datetime-multiple "s" + :datetime-ago "în urmă" + :datetime-yesterday "ieri" + :datetime-today "azi" + + ;profile + :profile "Profil" + :report-user "RAPORTEAZĂ UTILIZATOR" + :message "Mesaj" + :username "Nume de utilizator" + :not-specified "Nu este specificat" + :public-key "Cheie publică" + :phone-number "Număr de telefon" + :email "E-mail" + :profile-no-status "Nici un status" + :add-to-contacts "Adaugă la contacte" + :error-incorrect-name "Te rugăm să alegi un alt nume" + :error-incorrect-email "E-mail greșit" + + ;;make_photo + :image-source-title "Imagine de profil" + :image-source-make-photo "Captează" + :image-source-gallery "Selectează din galerie" + :image-source-cancel "Anulează" + + ;sign-up + :contacts-syncronized "Contactele tale au fost sincronizate" + :confirmation-code (str "Mulțumim! Ți-am trims un mesaj text cu un cod " + "de confirmare. Te rugăm să ne transmiți acel cod pentru a-ți confirma numărul de telefon") + :incorrect-code (str "Ne pare rău, dar codul este greșit, te rugăm să-l introduci încă o dată") + :generate-passphrase (str "Voi genera o frază de acces pentru tine, ca să poți redobândi " + "accesul sau să te poți conecta de pe alt dispozitiv") + :phew-here-is-your-passphrase "*Pfui* a fost greu, iată fraza ta de acces, *noteaz-o și păstreaz-o în siguranță!* Vei avea nevoie de ea pentru a-ți redobândi accesul la cont." + :here-is-your-passphrase "Iată fraza ta de acces, *noteaz-o și păstreaz-o în siguranță!* Vei avea nevoie de ea pentru a-ți redobândi accesul la cont." + :written-down "Ai grijă să o notezi în condiții de siguranță" + :phone-number-required "Apasă aici ca să-ți introduci numărul de telefon și eu îți voi invita prietenii" + :intro-status "Hai să vorbim pe chat pentru a-ți seta contul și modifica setările!" + :intro-message1 "Bine ai vent în Status\nApasă pe acest mesaj pentru a-ți seta parola și a începe!" + :account-generation-message "O secundă, trebuie să fac niște calcule complicate ca să-ți generez contul!" + + ;chats + :chats "Discuții pe chat" + :new-chat "Discuție nouă" + :new-group-chat "Grup nou de chat" + + ;discover + :discover "Descoperire" + :none "Niciuna" + :search-tags "Tastează aici etichetele de căutat" + :popular-tags "Etichete populare" + :recent "Recente" + :no-statuses-discovered "Nici un status găsit" + + ;settings + :settings "Setări" + + ;contacts + :contacts "Contacte" + :new-contact "Contact nou" + :show-all "AFIȘEAZĂ TOATE" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Oameni" + :contacts-group-new-chat "Începe discuție nouă" + :no-contacts "Nici un contact deocamdată" + :show-qr "Afișează QR" + + ;group-settings + :remove "Elimină" + :save "Salvează" + :change-color "Schimbă culoarea" + :clear-history "Șterge istoricul" + :delete-and-leave "Șterge și ieși" + :chat-settings "Setări chat" + :edit "Editare" + :add-members "Adăugare membri" + :blue "Albastru" + :purple "Violet" + :green "Verde" + :red "Roșu" + + ;commands + :money-command-description "Trimite bani" + :location-command-description "Trimite locație" + :phone-command-description "Trimite număr de telefon" + :phone-request-text "Solicitare număr de telefon" + :confirmation-code-command-description "Trimite cod de confirmare" + :confirmation-code-request-text "Solicitare cod de confirmare" + :send-command-description "Trimite locația" + :request-command-description "Trimite solicitarea" + :keypair-password-command-description "" + :help-command-description "Ajutor" + :request "Solicitare" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH pentru {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH de la {{chat-name}}" + :command-text-location "Locație: {{address}}" + :command-text-browse "Se răsfoiește pagina web: {{webpage}}" + :command-text-send "Tranzacție: {{amount}} ETH" + :command-text-help "Ajutor" + + ;new-group + :group-chat-name "Nume chat" + :empty-group-chat-name "Te rugăm să introduci un nume" + :illegal-group-chat-name "Te rugăm să selectezi un alt nume" + + ;participants + :add-participants "Adaugă participanți" + :remove-participants "Elimină participanți" + + ;protocol + :received-invitation "A primit invitația de chat" + :removed-from-chat "te-a elimination din grupul de chat" + :left "a plecat" + :invited "invitat" + :removed "eliminat" + :You "Tu" + + ;new-contact + :add-new-contact "Adaugă contact nou" + :import-qr "Importă" + :scan-qr "Scanează QR" + :name "Nume" + :whisper-identity "Identitatea" + :address-explication "Poate că aici ar trebui să fie un text care să explice ce este o adresă și unde să o cauți" + :enter-valid-address "Te rugăm să introduci o adresă validă sau să scanezi un cod QR" + :contact-already-added "Contactul a fost adăugat deja" + :can-not-add-yourself "Nu te poți adăuga pe tine" + :unknown-address "Adresă necunoscută" + + + ;login + :connect "Conectare" + :address "Adresă" + :password "Parolă" + :login "Conectează-te" + :wrong-password "Parola greșită" + + ;recover + :recover-from-passphrase "Recuperează folosind fraza de acces" + :recover-explain "Te rugăm să introduci fraza de acces si parola ta pentru a redobandi accesul" + :passphrase "Fraza de acces" + :recover "Recuperează" + :enter-valid-passphrase "Te rugăm să introduci o frază de acces" + :enter-valid-password "Te rugăm să introduci o parolă" + + ;accounts + :recover-access "Recuperează acces" + :add-account "Adaugă cont" + + ;wallet-qr-code + :done "Gata" + :main-wallet "Portofelul principal" + + ;validation + :invalid-phone "Număr de telefon nevalid" + :amount "Sumă" + :not-enough-eth (str "Sold ETH insuficient " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Confirmare tranzacție" + :other "Confirmare {{count}} tranzacții" + :zero "Nicio tranzacție"} + :status "Status" + :pending-confirmation "Se așteaptă confirmarea" + :recipient "Beneficiar" + :one-more-item "Încă un articol" + :fee "Comision" + :value "Valoare" + + ;:webview + :web-view-error "ups, eroare"}) diff --git a/src/status_im/translations/ru.cljs b/src/status_im/translations/ru.cljs new file mode 100644 index 0000000000..7823ead833 --- /dev/null +++ b/src/status_im/translations/ru.cljs @@ -0,0 +1,235 @@ +(ns status-im.translations.ru) + +(def translations + { + ;common + :members-title "Участники" + :not-implemented "!не реализовано" + :chat-name "Имя чата" + :notifications-title "Уведомления и звуки" + :offline "Оффлайн" + + ;drawer + :invite-friends "Пригласить друзей" + :faq "ЧАВО" + :switch-users "Переключить пользователей" + + ;chat + :is-typing "печатает" + :and-you "и вы" + :search-chat "Поиск по чату" + :members {:one "1 член" + :other "{{count}} члена(ов)" + :zero "нет членов"} + :members-active {:one "1 член, 1 активный" + :other "{{count}} члена(ов), {{count}} активных" + :zero "нет членов"} + :active-online "В сети" + :active-unknown "Неизвестно" + :available "Доступно" + :no-messages "Нет сообщений" + :suggestions-requests "Запросы" + :suggestions-commands "Команды" + + ;sync + :sync-in-progress "Синхронизация..." + :sync-synced "Синхронизируется" + + ;messages + :status-sending "Отправляется" + :status-pending "в ожидании" + :status-sent "Отправить" + :status-seen-by-everyone "Просмотрено всеми" + :status-seen "Просмотрено" + :status-delivered "Доставлено" + :status-failed "Ошибка" + + ;datetime + :datetime-second {:one "секунда" + :other "секунды"} + :datetime-minute {:one "минута" + :other "минуты"} + :datetime-hour {:one "час" + :other "часы"} + :datetime-day {:one "день" + :other "дни"} + :datetime-multiple "c" ; TODO probably wrong + :datetime-ago "назад" + :datetime-yesterday "вчера" + :datetime-today "сегодня" + + ;profile + :profile "Профиль" + :report-user "ПОЖАЛОВАТЬСЯ НА ПОЛЬЗОВАТЕЛЯ" + :message "Сообщение" + :username "Имя пользователя" + :not-specified "Не указано" + :public-key "Публичный ключ" + :phone-number "Номер телефона" + :email "Электронная почта" + :profile-no-status "Нет статуса" + :add-to-contacts "Добавить в контакты" + :error-incorrect-name "Выберите другое имя" + :error-incorrect-email "Неправильная электронная почта" + + ;;make_photo + :image-source-title "Изображение профиля" + :image-source-make-photo "Сфотографировать" + :image-source-gallery "Выбрать из галереи" + :image-source-cancel "Отмена" + + ;;sharing + :sharing-copy-to-clipboard "Скопировать" + :sharing-share "Поделиться..." + :sharing-cancel "Отмена" + + ;sign-up + :contacts-syncronized "Ваши контакты синхронизированы" + :confirmation-code (str "Спасибо! Мы отправили вам СМС с кодом подтверждения." + "Введите этот код для подтверждения своего номера телефона") + :incorrect-code (str "Извините, код неправильный, введите еще раз") + :generate-passphrase (str "Я создам для вас парольную фразу, чтобы вы смогли восстановить ваш" + "доступ или войти с другого устройства") + :phew-here-is-your-passphrase (str "*Уф*, это было непросто, вот ваша парольная фраза, *запишите ее и сохраните в надежном месте!* " + "Она будет нужна вам для восстановления аккаунта.") + :here-is-your-passphrase (str "Вот ваша парольная фраза, *запишите ее и сохраните в надежном месте!* " + "Она будет нужна вам для восстановления аккаунта.") + :written-down "Убедитесь, что вы записали ее в надежном месте" + :phone-number-required "Нажмите сюда для ввода своего номера телефона и поиска своих друзей" + :intro-status "Пообщайтесь со мной в чате, чтобы настроить свой аккаунт и изменить свои настройки!" + :intro-message1 "Добро пожаловать в Статус\nНажмите на это сообщение, чтобы установить пароль и начать!" + :account-generation-message "Секундочку, мне нужно выполнить безумно сложные расчеты для создания вашего аккаунта!" + + ;chats + :chats "Чаты" + :new-chat "Новый чат" + :new-group-chat "Новый групповой чат" + + ;discover + :discover "Поиск" + :none "Нет" + :search-tags "Введите теги для поиска сюда" + :popular-tags "Популярные теги" + :recent "Последние" + :no-statuses-discovered "Статусы не обнаружены" + + ;settings + :settings "Настройки" + + ;contacts + :contacts "Контакты" + :new-contact "Новый контакт" + :show-all "ПОКАЗАТЬ ВСЕ" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Люди" + :contacts-group-new-chat "Начать новый час" + :no-contacts "Пока нет контактов" + :show-qr "Показать QR" + :enter-address "Ввести адрес" + + ;group-settings + :remove "Удалить" + :save "Сохранить" + :change-color "Изменить цвет" + :clear-history "Очистить историю" + :delete-and-leave "Удалить и оставить" + :chat-settings "Настройки чата" + :edit "Изменить" + :add-members "Добавить членов" + :blue "Синий" + :purple "Фиолетовый" + :green "Зеленый" + :red "Красный" + + ;commands + :money-command-description "Отправить деньги" + :location-command-description "Отправить местоположение" + :phone-command-description "Отправить номер телефона" + :phone-request-text "Запрос номера телефона" + :confirmation-code-command-description "Отправить код подтверждения" + :confirmation-code-request-text "Запрос кода подтверждения" + :send-command-description "Отправить местоположение" + :request-command-description "Отправить запрос" + :keypair-password-command-description "" + :help-command-description "Помощь" + :request "Запрос" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH в адрес {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH от {{chat-name}}" + :command-text-location "Место {{address}}" + :command-text-browse "Просматривается веб-страница {{webpage}}" + :command-text-send "Транзакция {{amount}} ETH" + :command-text-help "Помощь" + + ;new-group + :group-chat-name "Имя чата" + :empty-group-chat-name "Введите имя" + :illegal-group-chat-name "Выберите другое имя" + + ;participants + :add-participants "Добавить участников" + :remove-participants "Удалить участников" + + ;protocol + :received-invitation "получил(а) приглашение в чат" + :removed-from-chat "удалил(а) вас из группового чата" + :left "осталось" + :invited "приглашен(а)" + :removed "удален(а)" + :You "Вы" + + ;new-contact + :add-new-contact "Добавить новый контакт" + :import-qr "Импорт" + :scan-qr "Сканировать QR" + :name "Имя" + :whisper-identity "Скрытая личность" + :address-explication "Может быть, здесь должен быть какой-то текст, поясняющий адрес и то, где его искать" + :enter-valid-address "Введите действительный адрес или сканируйте QR-код" + :enter-valid-public-key "Введите действительный публичный ключ или сканируйте QR-код" + :contact-already-added "Контакт уже добавлен" + :can-not-add-yourself "Вы не можете добавить себя" + :unknown-address "Неизвестный адрес" + + + ;login + :connect "Подключиться" + :address "Адрес" + :password "Пароль" + :login "Вход" + :wrong-password "Неверный пароль" + + ;recover + :recover-from-passphrase "Восстановление с помощью парольной фразы" + :recover-explain "Введите парольную фразу вместо вашего пароля для восстановления доступа" + :passphrase "Парольная фраза" + :recover "Восстановить" + :enter-valid-passphrase "Введите парольную фразу" + :enter-valid-password "Введите пароль" + + ;accounts + :recover-access "Восстановить доступ" + :add-account "Добавить аккаунт" + + ;wallet-qr-code + :done "Готово" + :main-wallet "Основной кошелек" + + ;validation + :invalid-phone "Неверный номер телефона" + :amount "Сумма" + :not-enough-eth (str "Не хватает ETH на балансе " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Подтвердить транзакцию" + :other "Подтвердите {{count}} транзакции(ий)" + :zero "Нет транзакций"} + :status "Статус" + :pending-confirmation "В ожидании подтверждения" + :recipient "Получатель" + :one-more-item "Еще одна позиция" + :fee "Комиссия" + :value "Сумма" + + ;:webview + :web-view-error "ой, ошибка"}) diff --git a/src/status_im/translations/sl.cljs b/src/status_im/translations/sl.cljs new file mode 100644 index 0000000000..5fdfadb7b1 --- /dev/null +++ b/src/status_im/translations/sl.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.sl) + +(def translations + { + ;common + :members-title "Člani" + :not-implemented "!ni implementirano" + :chat-name "Ime za klepet" + :notifications-title "Obvestila in zvoki" + :offline "Nedosegljiv/-a" + + ;drawer + :invite-friends "Povabi prijatelje" + :faq "Pogosta vprašanja" + :switch-users "Preklopi med uporabniki" + + ;chat + :is-typing "piše" + :and-you "in ti" + :search-chat "Iskanje po klepetu" + :members {:one "1 član" + :other "{{count}} članov" + :zero "ni članov"} + :members-active {:one "1 član, 1 aktiven" + :other "{{count}} članov, {{count}} aktivnih" + :zero "ni članov"} + :active-online "Dosegljiv/-a" + :active-unknown "Neznano" + :available "Na voljo" + :no-messages "Ni sporočil" + :suggestions-requests "Prošnje" + :suggestions-commands "Ukazi" + + ;sync + :sync-in-progress "Sinhronizacija..." + :sync-synced "Sinhronizirano" + + ;messages + :status-sending "Pošiljanje" + :status-pending "V teku" + :status-sent "Poslano" + :status-seen-by-everyone "Vsi so videli" + :status-seen "Videno" + :status-delivered "Dostavljeno" + :status-failed "Ni uspelo" + + ;datetime + :datetime-second {:one "sekunda" + :other "sekund"} + :datetime-minute {:one "minuta" + :other "minut"} + :datetime-hour {:one "ura" + :other "ur"} + :datetime-day {:one "dan" + :other "dni"} + :datetime-multiple "s" + :datetime-ago "nazaj" + :datetime-yesterday "včeraj" + :datetime-today "danes" + + ;profile + :profile "Profil" + :report-user "PRIJAVI UPORABNIKA" + :message "Sporočilo" + :username "Uporabniško ime" + :not-specified "Ni navedeno" + :public-key "Javni ključ" + :phone-number "Telefonska številka" + :email "E-pošta" + :profile-no-status "Brez statusa" + :add-to-contacts "Dodaj med stike" + :error-incorrect-name "Prosimo, izberi drugo ime" + :error-incorrect-email "Nepravilna e-pošta" + + ;;make_photo + :image-source-title "Fotografija profila" + :image-source-make-photo "Zajemi" + :image-source-gallery "Izberi iz galerije" + :image-source-cancel "Prekliči" + + ;sign-up + :contacts-syncronized "Tvoji stiki so bili sinhronizirani" + :confirmation-code (str "Hvala! Poslali smo ti sporočilo s potrditveno " + "kodo. Prosimo, vnesi to kodo in potrdi svojo telefonsko številko") + :incorrect-code (str "Koda na žalost ni bila pravilna, prosimo, da jo ponovno vneseš") + :generate-passphrase (str "Zate bom ustvaril šifrirno geslo, tako da boš lahko obnovil/-a svoj " + "dostop, ali se prijavil/-a iz druge naprave") + :phew-here-is-your-passphrase "*Pfuu* to pa je bilo težko, tukaj je tvoje šifrirno geslo, *zapiši si ga in shrani na varno mesto!* Potreboval/-a ga boš, da obnoviš svoj račun." + :here-is-your-passphrase "Tukaj je tvoje geslo, *zapiši si ga in shrani na varno mesto!* Potreboval/-a ga boš, da obnoviš svoj račun." + :written-down "Poskrbi, da si ga pazljivo zapišeš" + :phone-number-required "Pritisni tukaj in vnesi svojo telefonsko številko ter poiskal bom tvoje prijatelje" + :intro-status "Klepetaj z mano in nastavi svoj račun ter spremeni svoje nastavitve!" + :intro-message1 "Dobrodošel/-la v status\nPritisni to sporočilo in nastavi svoje geslo ter začni!" + :account-generation-message "Počakaj sekundo, opraviti moram noro računico, da ustvarim tvoj račun!" + + ;chats + :chats "Klepeti" + :new-chat "Nov klepet" + :new-group-chat "Nov skupinski klepet" + + ;discover + :discover "Odkrivanje" + :none "Brez" + :search-tags "Sem vnesi svoje priljubljene oznake" + :popular-tags "Priljubljene oznake" + :recent "Nedavno" + :no-statuses-discovered "Ni odkritih statusov" + + ;settings + :settings "Nastavitve" + + ;contacts + :contacts "Stiki" + :new-contact "Nov stik" + :show-all "POKAŽI VSE" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Osebe" + :contacts-group-new-chat "Začni nov klepet" + :no-contacts "Zaenkrat še ni stikov" + :show-qr "Prikaži QR" + + ;group-settings + :remove "Odstrani" + :save "Shrani" + :change-color "Spremeni barvo" + :clear-history "Počisti zgodovino" + :delete-and-leave "Izbriši in zapri" + :chat-settings "Nastavitve klepeta" + :edit "Uredi" + :add-members "Dodaj člane" + :blue "Modra" + :purple "Vijolična" + :green "Zelena" + :red "Rdeča" + + ;commands + :money-command-description "Pošlji denar" + :location-command-description "Pošlji lokacijo" + :phone-command-description "Pošlji telefonsko številko" + :phone-request-text "Prošnja za telefonsko številko" + :confirmation-code-command-description "Pošlji potrditveno kodo" + :confirmation-code-request-text "Prošnja za potrditveno kodo" + :send-command-description "Pošlji lokacijo" + :request-command-description "Pošlji prošnjo" + :keypair-password-command-description "" + :help-command-description "Pomoč" + :request "Prošnja" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH osebi {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH od osebe {{chat-name}}" + :command-text-location "Lokacija: {{address}}" + :command-text-browse "Brskanje po spletni strani: {{webpage}}" + :command-text-send "Transakcija: {{amount}} ETH" + :command-text-help "Pomoč" + + ;new-group + :group-chat-name "Ime za klepet" + :empty-group-chat-name "Prosimo, vnesi ime" + :illegal-group-chat-name "Prosimo, izberi drugo ime" + + ;participants + :add-participants "Dodaj udeležence" + :remove-participants "Odstrani udeležence" + + ;protocol + :received-invitation "je prejel/-a povabilo za klepet" + :removed-from-chat "te je odstranil/-a iz skupinskega klepeta" + :left "preostane" + :invited "povabil/-a" + :removed "odstranil/-a" + :You "Ti" + + ;new-contact + :add-new-contact "Dodaj nov stik" + :import-qr "Uvozi" + :scan-qr "Skeniraj QR" + :name "Ime" + :whisper-identity "Skrivna identiteta" + :address-explication "Sem morda sodi besedilo, ki razlaga, kaj je naslov ter kje ga najti" + :enter-valid-address "Prosimo, vnesi veljaven naslov ali skeniraj QR kodo" + :contact-already-added "Stik je bil že dodan" + :can-not-add-yourself "Sebe ni mogoče dodati" + :unknown-address "Neznan naslov" + + + ;login + :connect "Poveži" + :address "Naslov" + :password "Geslo" + :login "Prijava" + :wrong-password "Napačno geslo" + + ;recover + :recover-from-passphrase "Povrni prek šifrirnega gesla" + :recover-explain "Prosimo, vnesi šifrirno geslo svojega gesla za povrnitev dostopa" + :passphrase "Šifrirno geslo" + :recover "Povrni" + :enter-valid-passphrase "Prosimo, vnesi šifrirno geslo" + :enter-valid-password "Prosimo, vnesi geslo" + + ;accounts + :recover-access "Povrni dostop" + :add-account "Dodaj račun" + + ;wallet-qr-code + :done "Končano" + :main-wallet "Glavna denarnica" + + ;validation + :invalid-phone "Neveljavna telefonska številka" + :amount "Vsota" + :not-enough-eth (str "Stanje ETH na računu je prenizko " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Potrdi transakcijo" + :other "Potrdi {{count}} transakcij" + :zero "Ni transakcij"} + :status "Status" + :pending-confirmation "Potrditev v teku" + :recipient "Prejemnik" + :one-more-item "Še en predmet" + :fee "Plačilo" + :value "Vrednost" + + ;:webview + :web-view-error "ups, napaka"}) diff --git a/src/status_im/translations/sv.cljs b/src/status_im/translations/sv.cljs new file mode 100644 index 0000000000..0c7e161aa4 --- /dev/null +++ b/src/status_im/translations/sv.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.sv) + +(def translations + { + ;common + :members-title "Medlemmar" + :not-implemented "!inte implementerad" + :chat-name "Chattnamn" + :notifications-title "Aviseringar och ljud" + :offline "Offline" + + ;drawer + :invite-friends "Bjud in vänner" + :faq "FAQ" + :switch-users "Byt användare" + + ;chat + :is-typing "skriver" + :and-you "och du" + :search-chat "Sök chatt" + :members {:one "1 medlem" + :other "{{count}} medlemmar" + :zero "inga medlemmar"} + :members-active {:one "1 medlem, 1 aktiv" + :other "{{count}} medlemmar, {{count}} aktiva" + :zero "inga medlemmar"} + :active-online "Online" + :active-unknown "Okänd" + :available "Tillgänglig" + :no-messages "Inga meddelanden" + :suggestions-requests "Begäranden" + :suggestions-commands "Kommandon" + + ;sync + :sync-in-progress "Synkroniserar..." + :sync-synced "Synkroniserad" + + ;messages + :status-sending "Skickar" + :status-pending "Avvaktan" + :status-sent "Skickad" + :status-seen-by-everyone "Sedd av alla" + :status-seen "Sedd" + :status-delivered "Levererad" + :status-failed "Misslyckad" + + ;datetime + :datetime-second {:one "sekund" + :other "sekunder"} + :datetime-minute {:one "minut" + :other "minuter"} + :datetime-hour {:one "timma" + :other "timmar"} + :datetime-day {:one "dag" + :other "dagar"} + :datetime-multiple "s" + :datetime-ago "sedan" + :datetime-yesterday "igår" + :datetime-today "idag" + + ;profile + :profile "Profil" + :report-user "RAPPORTERA ANVÄNDARE" + :message "Meddelande" + :username "Användarnamn" + :not-specified "Inte angivet" + :public-key "Offentlig nyckel" + :phone-number "Telefonnummer" + :email "E-post" + :profile-no-status "Ingen status" + :add-to-contacts "Lägg till i kontakter" + :error-incorrect-name "Var god välj ett annat namn" + :error-incorrect-email "Inkorrekt e-post" + + ;;make_photo + :image-source-title "Profilbild" + :image-source-make-photo "Fånga" + :image-source-gallery "Välj från galleri" + :image-source-cancel "Avbryt" + + ;sign-up + :contacts-syncronized "Dina kontakter har synkroniserats" + :confirmation-code (str "Tack! Vi har skickat dig ett textmeddelande med en bekräftelse " + "kod. Var god ange den koden för att bekräfta ditt telefonnummer") + :incorrect-code (str "Tyvärr var koden felaktig, var god ange den igen") + :generate-passphrase (str "Jag kommer att generera en lösenordsfras för dig så att du kan återställa din " + "åtkomst eller logga in från en annan enhet") + :phew-here-is-your-passphrase "*Pust* det var svårt, här är din lösenordsfras, *skriv ner det här och förvara det säkert!* Du kommer att behöva det för att återställa ditt konto." + :here-is-your-passphrase "Här är din lösenordsfras, *skriv ner det här och förvara det säkert!* Du kommer att behöva det för att återställa ditt konto." + :written-down "Se till att du hade skrivit ner det säkert" + :phone-number-required "Tryck här för att ange ditt telefonnummer och jag kommer att hitta dina vänner" + :intro-status "Chatta med mig för att konfigurera ditt konto och ändra dina inställningar!" + :intro-message1 "Välkommen till Status\nTryck på detta meddelande för att ställa in ditt lösenord och komma igång!" + :account-generation-message "Vänta ett ögonblick, jag måste göra lite galna beräkningar för att generera ditt konto!" + + ;chats + :chats "Chattar" + :new-chat "Ny chatt" + :new-group-chat "Ny gruppchatt" + + ;discover + :discover "Upptäckt" + :none "Inga" + :search-tags "Skriv dina söktaggar här" + :popular-tags "Populära taggar" + :recent "Senaste" + :no-statuses-discovered "Inga statusar upptäckta" + + ;settings + :settings "Inställningar" + + ;contacts + :contacts "Kontakter" + :new-contact "Ny kontakt" + :show-all "VISA ALLA" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Människor" + :contacts-group-new-chat "Starta ny chatt" + :no-contacts "Inga kontakter ännu" + :show-qr "Visa QR" + + ;group-settings + :remove "Ta bort" + :save "Spara" + :change-color "Ändra färg" + :clear-history "Rensa historik" + :delete-and-leave "Radera och lämna" + :chat-settings "Chattinställningar" + :edit "Redigera" + :add-members "Lägg till medlemmar" + :blue "Blå" + :purple "Lila" + :green "Grön" + :red "Röd" + + ;commands + :money-command-description "Skicka pengar" + :location-command-description "Skicka plats" + :phone-command-description "Skicka telefonnummer" + :phone-request-text "Telefonnummerbegäran" + :confirmation-code-command-description "Skicka bekräftelsekod" + :confirmation-code-request-text "Bekräftelsekodbegäran" + :send-command-description "Skicka plats" + :request-command-description "Skicka begäran" + :keypair-password-command-description "" + :help-command-description "Hjälp" + :request "Begäran" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH till {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH från {{chat-name}}" + :command-text-location "Plats: {{address}}" + :command-text-browse "Läser webbsidan: {{webpage}}" + :command-text-send "Transaktion: {{amount}} ETH" + :command-text-help "Hjälp" + + ;new-group + :group-chat-name "Chattnamn" + :empty-group-chat-name "Var god ange ett namn" + :illegal-group-chat-name "Var god välj ett annat namn" + + ;participants + :add-participants "Lägg till Deltagare" + :remove-participants "Ta bort Deltagare" + + ;protocol + :received-invitation "tog emot chattinbjudan" + :removed-from-chat "tog bort dig från gruppchatten" + :left "lämnade" + :invited "inbjudna" + :removed "borttagna" + :You "Du" + + ;new-contact + :add-new-contact "Lägg till ny kontakt" + :import-qr "Importera" + :scan-qr "Skanna QR" + :name "Namn" + :whisper-identity "Viskningsidentitet" + :address-explication "Kanske borde det finnas lite text som förklarar vad en adress är och var man hittar den" + :enter-valid-address "Var god ange en giltig adress eller skanna en QR-kod" + :contact-already-added "Kontakten har redan lagts till" + :can-not-add-yourself "Du kan inte lägga till dig själv" + :unknown-address "Okänd adress" + + + ;login + :connect "Anslut" + :address "Adress" + :password "Lösenord" + :login "Inloggning" + :wrong-password "Fel lösenord" + + ;recover + :recover-from-passphrase "Återställ från lösenordsfras" + :recover-explain "Var god ange lösenordsfrasen för ditt lösenord för att återställa åtkomsten" + :passphrase "Lösenordsfras" + :recover "Återställ" + :enter-valid-passphrase "Var god ange en lösenordsfras" + :enter-valid-password "Var god ange ett lösenord" + + ;accounts + :recover-access "Återställ åtkomst" + :add-account "Lägg till konto" + + ;wallet-qr-code + :done "Klar" + :main-wallet "Huvudplånbok" + + ;validation + :invalid-phone "Ogiltigt telefonnummer" + :amount "Belopp" + :not-enough-eth (str "'Inte tillräcklig ETH på balansen " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Bekräfta transaktion" + :other "Bekräfta {{count}} transaktioner" + :zero "Inga transaktioner"} + :status "Status" + :pending-confirmation "I väntan på bekräftelse" + :recipient "Mottagare" + :one-more-item "En artikel till" + :fee "Avgift" + :value "Värde" + + ;:webview + :web-view-error "hoppsan, fel"}) diff --git a/src/status_im/translations/sw.cljs b/src/status_im/translations/sw.cljs new file mode 100644 index 0000000000..30b0948b41 --- /dev/null +++ b/src/status_im/translations/sw.cljs @@ -0,0 +1,231 @@ +(ns status-im.translations.sw) + +(def translations + { + ;common + :members-title "Wanachama" + :not-implemented "!haijatekelezwa" + :chat-name "Jina la gumzo" + :notifications-title "Notisi na sauti" + :offline "Nje ya mtandao" + + ;drawer + :invite-friends "Karibisha marafiki" + :faq "Maswali Ya Mara kwa mara" + :switch-users "Badili kwa watumiaji" + + ;chat + :is-typing "anaandika" + :and-you "na wewe" + :search-chat "Tafuta gumzo" + :members {:one "Mwanachama 1" + :other "{{count}} wanachama" + :zero "hakuna wanachama"} + :members-active {:one "Mwanachama 1, Aliyewajibika 1" + :other "{{count}} wanachama, {{count}} waliowajibika" + :zero "hakuna wanachama"} + :active-online "Mtandaoni" + :active-unknown "Hawajulikani" + :available "Wanapatikana" + :no-messages "Hakuna ujumbe" + :suggestions-requests "Maombi" + :suggestions-commands "Amri" + + ;sync + :sync-in-progress "Kulandanisha..." + :sync-synced "Katika ulandanishaji" + + ;messages + :status-sending "Kutuma" + :status-pending "Inasubiri" + :status-sent "Tuma" + :status-seen-by-everyone "Imeonekana na kila mtu" + :status-seen "Imeonekana" + :status-delivered "Imefikishwa" + :status-failed "Imeshindwa" + + ;datetime + :datetime-second {:one "sekunde" + :other "sekunde"} + :datetime-minute {:one "dakika" + :other "dakika"} + :datetime-hour {:one "saa" + :other "masaa"} + :datetime-day {:one "siku" + :other "siku"} + :datetime-multiple "s" + :datetime-ago "iliyopita" + :datetime-yesterday "jana" + :datetime-today "leo" + + ;profile + :profile "Profaili" + :report-user "RIPOTI MTUMIAJI" + :message "Ujumbe" + :username "Jina la mtumiaji" + :not-specified "Haijafafanuliwa" + :public-key "Ufunguo wa Umma" + :phone-number "Namba ya Simu" + :email "Barua pepe" + :profile-no-status "Hakuna hadhi" + :add-to-contacts "Ongeza kwa mawasiliano" + :error-incorrect-name "Tafadhali chagua jina lingine" + :error-incorrect-email "Barua pepe sio sahihi" + + ;;make_photo + :image-source-title "Picha ya profaili" + :image-source-make-photo "Chukua picha" + :image-source-gallery "Chagua kutoka nyumba ya sanaa" + :image-source-cancel "Ghairi" + + ;;sharing + :sharing-copy-to-clipboard "Kopiera" + :sharing-share "Dela..." + :sharing-cancel "Ghairi" + + ;sign-up + :contacts-syncronized "Mawasiliano yako yamelandanishwa" + :confirmation-code (str "Asante! Tumekutumia ujumbe mfupi na uthibitisho " + "kificho. Tafadhali peana hicho kificho kuthibitisha namba yako ya simu") + :incorrect-code (str "Samahani kificho hakikuwa sahihi, tafadhali ingiza tena") + :generate-passphrase (str "Nitakutengenezea kaulisiri ili uweze kurejesha " + "upatikanaji au kuingia kwa kutumia kifaa kingine") + :phew-here-is-your-passphrase "*Phew* hiyo ilikuwa ngumu, hapa ni kaulisiri yako, *iandike na uiweke salama!* Utaihitaji kwa ajili ya kufufua akaunti yako." + :here-is-your-passphrase "Hapa ni kaulisiri yako, *iandike na uiweke salama!* Utaihitaji kwa ajili ya kufufua akaunti yako." + :written-down "Hakikisha umeiandika salama" + :phone-number-required "Bofya hapa kuingiza namba yako ya simu na nitapata marafiki zako" + :intro-status "Ongea nami kuanzisha akaunti yako na kubadilisha mipangilio yako!" + :intro-message1 "Karibu kwa Hali na Ubofye ujumbe huu ili kuweka nenosiri lako na uanze!" + :account-generation-message "Nipe sekunde, naenda kufanya baadhi ya hisabati kutengeneza akaunti yako!" + + ;chats + :chats "Gumzo" + :new-chat "Gumzo mpya" + :new-group-chat "Gumzo mpya ya kikundi" + + ;discover + :discover "Ugunduzi" + :none "Hakuna" + :search-tags "Andika vitambulisho vyako vya kutafuta hapa" + :popular-tags "Vitambulisho maarufu" + :recent "Hivi karibuni" + :no-statuses-discovered "Hakuna hali zimegundulika" + + ;settings + :settings "Mipangilio" + + ;contacts + :contacts "Mawasiliano" + :new-contact "Mawasiliano mapya" + :show-all "ONYESHA YOTE" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Watu" + :contacts-group-new-chat "Anza gumzo mpya" + :no-contacts "Bado hakuna mawasiliano" + :show-qr "Onyesha QR" + + ;group-settings + :remove "Ondoa" + :save "Hifadhi" + :change-color "Badilisha rangi" + :clear-history "Futa historia" + :delete-and-leave "Futa na uondoke" + :chat-settings "Mipangilio ya gumzo" + :edit "Hariri" + :add-members "Ongeza wanachama" + :blue "Bluu" + :purple "Zambarau" + :green "Kijani" + :red "Nyekundu" + + ;commands + :money-command-description "Tuma pesa" + :location-command-description "Tuma eneo" + :phone-command-description "Tuma namba ya simu" + :phone-request-text "Ombi la namba ya simu" + :confirmation-code-command-description "Tuma kificho cha uthibitisho" + :confirmation-code-request-text "Ombi la kificho cha uthibitisho" + :send-command-description "Tuma eneo" + :request-command-description "Tuma ombi" + :keypair-password-command-description "" + :help-command-description "Msaada" + :request "Ombi" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH kwa {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH kutoka {{chat-name}}" + :command-text-location "Eneo: {{address}}" + :command-text-browse "Ukurasa wa tovuti wa kutafuta: {{webpage}}" + :command-text-send "Mashirikiano: {{amount}} ETH" + :command-text-help "Msaada" + + ;new-group + :group-chat-name "Jina la gumzo" + :empty-group-chat-name "Tafadhali ingiza jina" + :illegal-group-chat-name "Tafadhali chagua jina lingine" + + ;participants + :add-participants "Ongeza Washirika" + :remove-participants "Ondoa Washirika" + + ;protocol + :received-invitation "mwaliko wa gumzo ulipokelewa" + :removed-from-chat "uliondolewa kwenye kikundi cha gumzo" + :left "uliondoka" + :invited "ulialikwa" + :removed "uliondolewa" + :You "Wewe" + + ;new-contact + :add-new-contact "Ongeza mawasiliano mapya" + :import-qr "Agiza" + :scan-qr "Piga picha QR" + :name "Jina" + :whisper-identity "Utambulisho wa Tetesi" + :address-explication "Labda hapa kunapaswa kuwa na baadhi ya maandishi kueleza anwani ni nini na ni wapi pa kuitafuta" + :enter-valid-address "Tafadhali ingiza anwani sahihi au upige picha ya kificho cha QR" + :contact-already-added "Tayari mawasiliano yameongezwa" + :can-not-add-yourself "Huwezi kujiongeza mwenyewe" + :unknown-address "Anwani Haijulikani" + + + ;login + :connect "Unganisha" + :address "Anwani" + :password "Nenosiri" + :login "Ingia" + :wrong-password "Nenosiri sio halali" + + ;recover + :recover-from-passphrase "Okoa/fufua kutoka kaulisiri" + :recover-explain "Tafadhali ingiza kaulisiri ili nenosiri lako liokoe upatikanaji" + :passphrase "Kaulisiri" + :recover "Okoa" + :enter-valid-passphrase "Tafadhali ingiza kaulisiri" + :enter-valid-password "Tafadhali ingiza nenosiri" + + ;accounts + :recover-access "Okoa ufikiaji/Upatikanaji" + :add-account "Ongeza akaunti" + + ;wallet-qr-code + :done "Imefanyika" + :main-wallet "Mkoba Mkuu" + + ;validation + :invalid-phone "Namba ya simu ni batili" + :amount "Kiasi" + :not-enough-eth (str "ETH haitoshi kwenye salio " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Thibitisha mashirikiano" + :other "Thibitisha {{count}} mashirikiano" + :zero "Hakuna mashirikiano"} + :status "Hali" + :pending-confirmation "Uthibitisho unasubiriwa" + :recipient "Mpokeaji" + :one-more-item "Bidhaa moja zaidi" + :fee "Ada" + :value "Thamani" + + ;:webview + :web-view-error "nadhani, hitilafu"}) diff --git a/src/status_im/translations/th.cljs b/src/status_im/translations/th.cljs new file mode 100644 index 0000000000..2213d7c906 --- /dev/null +++ b/src/status_im/translations/th.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.th) + +(def translations + { + ;common + :members-title "สมาชิก" + :not-implemented "!ยังไม่ได้ดำเนินการ" + :chat-name "ชื่อแชท" + :notifications-title "การแจ้งเตือนและเสียง" + :offline "ออฟไลน์" + + ;drawer + :invite-friends "เชิญเพื่อน" + :faq "คำถามที่พบบ่อย" + :switch-users "สลับผู้ใช้" + + ;chat + :is-typing "กำลังพิมพ์" + :and-you "และคุณ" + :search-chat "ค้นหาแชท" + :members {:one "1 สมาชิก" + :other "{{count}} สมาชิก" + :zero "ไม่มีสมาชิก"} + :members-active {:one "1 สมาชิก, 1 ใช้งานอยู่" + :other "{{count}} สมาชิก, {{count}} ใช้งานอยู่" + :zero "ไม่มีสมาชิก"} + :active-online "ออนไลน์" + :active-unknown "ไม่ทราบ" + :available "มีให้ใช้ได้" + :no-messages "ไม่มีข้อความ" + :suggestions-requests "คำร้องขอ" + :suggestions-commands "คำสั่ง" + + ;sync + :sync-in-progress "กำลังซิงค์..." + :sync-synced "ระหว่างการซิงค์" + + ;messages + :status-sending "กำลังส่ง" + :status-pending "อยู่ระหว่างดำเนินการ" + :status-sent "ส่งแล้ว" + :status-seen-by-everyone "อ่านแล้วโดยทุกคน" + :status-seen "อ่านแล้ว" + :status-delivered "ส่งแล้ว" + :status-failed "ล้มเหลว" + + ;datetime + :datetime-second {:one "วินาที" + :other "วินาที"} + :datetime-minute {:one "นาที" + :other "นาที"} + :datetime-hour {:one "ชั่วโมง" + :other "ชั่วโมง"} + :datetime-day {:one "วัน" + :other "วัน"} + :datetime-multiple "" + :datetime-ago "ที่ผ่านมา" + :datetime-yesterday "เมื่อวาน" + :datetime-today "วันนี้" + + ;profile + :profile "โปรไฟล์" + :report-user "รายงานผู้ใช้" + :message "ข้อความ" + :username "ชื่อผู้ใช้" + :not-specified "ไม่ระบุ" + :public-key "คีย์สาธารณะ" + :phone-number "หมายเลขโทรศัพท์" + :email "อีเมล" + :profile-no-status "ไม่มีสถานะ" + :add-to-contacts "เพิ่มไปยังผู้ติดต่อ" + :error-incorrect-name "โปรดเลือกชื่ออื่น" + :error-incorrect-email "อีเมลไม่ถูกต้อง" + + ;;make_photo + :image-source-title "รูปโปรไฟล์" + :image-source-make-photo "ถ่ายภาพ" + :image-source-gallery "เลือกจากแกลเลอรี" + :image-source-cancel "ยกเลิก" + + ;sign-up + :contacts-syncronized "ไซิงค์รายชื่อผู้ติดต่อของคุณแล้ว" + :confirmation-code (str "ขอขอบคุณ! เราได้ส่งข้อความตัวอักษรพร้อมรหัสยืนยันแล้ว" + "โปรดมอบรหัสนั้นเพื่อยืนยันหมายเลขโทรศัพท์ของคุณ") + :incorrect-code (str "ขออภัย รหัสไม่ถูกต้อง โปรดกรอกอีกครั้ง") + :generate-passphrase (str "ฉันจะสร้างวลีรหัสผ่านให้คุณเพื่อให้คุณสามารถกู้คืน" + "การเข้าถึงของคุณหรือล็อกอินจากอุปกรณ์อื่นได้") + :phew-here-is-your-passphrase "*โล่งอกไปที* มันไม่ง่ายเลย นี่คือวลีรหัสผ่านของคุณ *จดมันไว้และรักษามันให้ปลอดภัย!* คุณจะจำเป็นต้องใช้มันเพื่อกู้คืนบัญชีของคุณ" + :here-is-your-passphrase "นี่คือวลีรหัสผ่านของคุณ *จดมันไว้และรักษามันให้ปลอดภัย!* คุณจะจำเป็นต้องใช้มันเพื่อกู้คืนบัญชีของคุณ" + :written-down "ตรวจสอบให้มั่นใจว่าคุณได้จดบันทึกมันไว้อย่างปลอดภัยแล้ว" + :phone-number-required "แตะที่นี่เพื่อกรอกหมายเลขโทรศัพท์ของคุณ & ฉันจะค้นหาเพื่อนของคุณ" + :intro-status "แชทกับฉันเพื่อตั้งค่าบัญชีของคุณและเปลี่ยนการตั้งค่าของคุณ!" + :intro-message1 "ยินดีต้อนรับสู่สถานะ \n แตะข้อความนี้เพื่อตั้งรหัสผ่านของคุณ & เริ่มต้น!" + :account-generation-message "ให้เวลาฉันหนึ่งวินาที ฉันจะต้องคำนวณอย่างหนักเพื่อสร้างบัญชีของคุณ!" + + ;chats + :chats "แชท" + :new-chat "แชทใหม่" + :new-group-chat "แชทกลุ่มใหม่" + + ;discover + :discover "การค้นพบ" + :none "ไม่มี" + :search-tags "พิมพ์แท็กการค้นหาของคุณที่นี่" + :popular-tags "แท็กยอดนิยม" + :recent "เมื่อเร็ว ๆ นี้" + :no-statuses-discovered "ไม่พบสถานะใด ๆ" + + ;settings + :settings "การตั้งค่า" + + ;contacts + :contacts "ผู้ติดต่อ" + :new-contact "ผู้ติดต่อใหม่" + :show-all "แสดงทั้งหมด" + :contacts-group-dapps "ÐApps" + :contacts-group-people "ผู้คน" + :contacts-group-new-chat "เริ่มแชทใหม่" + :no-contacts "ยังไม่มีผู้ติดต่อ" + :show-qr "แสดง QR" + + ;group-settings + :remove "ลบ" + :save "บันทึก" + :change-color "เปลี่ยนสี" + :clear-history "ลบประวัติ" + :delete-and-leave "ลบและออก" + :chat-settings "การตั้งค่าแชท" + :edit "แก้ไข" + :add-members "เพิ่มสมาชิก" + :blue "น้ำเงิน" + :purple "ม่วง" + :green "เขียว" + :red "แดง" + + ;commands + :money-command-description "ส่งเงิน" + :location-command-description "ส่งตำแหน่ง" + :phone-command-description "ส่งหมายเลขโทรศัพท์" + :phone-request-text "คำร้องขอหมายเลขโทรศัพท์" + :confirmation-code-command-description "ส่งรหัสยืนยัน" + :confirmation-code-request-text "คำร้องขอรหัสยืนยัน" + :send-command-description "ส่งตำแหน่ง" + :request-command-description "ส่งคำร้องขอ" + :keypair-password-command-description "" + :help-command-description "ช่วยเหลือ" + :request "คำร้องขอ" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH ไปยัง {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH จาก {{chat-name}}" + :command-text-location "ตำแหน่ง: {{address}}" + :command-text-browse "กำลังท่องชมหน้าเว็บ: {{webpage}}" + :command-text-send "ธุรกรรม: {{amount}} ETH" + :command-text-help "ช่วยเหลือ" + + ;new-group + :group-chat-name "ชื่อแชท" + :empty-group-chat-name "โปรดกรอกชื่อ" + :illegal-group-chat-name "โปรดเลือกชื่ออื่น" + + ;participants + :add-participants "เพิ่มผู้เข้าร่วม" + :remove-participants "ลบผู้เข้าร่วม" + + ;protocol + :received-invitation "คำเชิญแชทที่ได้รับ" + :removed-from-chat "ลบคุณออกจากแชทกลุ่ม" + :left "ออกไปแล้ว" + :invited "เชิญแล้ว" + :removed "ลบแล้ว" + :You "คุณ" + + ;new-contact + :add-new-contact "เพิ่มผู้ติดต่อใหม่" + :import-qr "นำเข้า" + :scan-qr "สแกน QR" + :name "ชื่อ" + :whisper-identity "กระซิบตัวตน" + :address-explication "บางที ในที่นี้คุณควรกรอกข้อความสักเล็กน้อยเพื่อแสดงที่อยู่หรือสถานที่ที่จะมองหามันได้" + :enter-valid-address "โปรดกรอกที่อยู่ที่ถูกต้องหรือสแกนรหัส QR" + :contact-already-added "ได้มีการเพิ่มผู้ติดต่อนี้แล้ว" + :can-not-add-yourself "คุณไม่สามารถเพิ่มตัวคุณเอง" + :unknown-address "ที่อยู่ที่ไม่ทราบ" + + + ;login + :connect "เชื่อมต่อ" + :address "ที่อยู่" + :password "รหัสผ่าน" + :login "ล็อกอิน" + :wrong-password "รหัสผ่านไม่ถูกต้อง" + + ;recover + :recover-from-passphrase "กู้คืนจากวลีรหัสผ่าน" + :recover-explain "โปรดกรอกวลีรหัสผ่านสำหรับรหัสผ่านของคุณเพื่อกู้คืนการเข้าถึง" + :passphrase "วลีรหัสผ่าน" + :recover "กู้คืน" + :enter-valid-passphrase "โปรดกรอกวลีรหัสผ่าน" + :enter-valid-password "โปรดกรอกรหัสผ่าน" + + ;accounts + :recover-access "กู้คืนการเข้าถึง" + :add-account "เพิ่มบัญชี" + + ;wallet-qr-code + :done "เสร็จสิ้น" + :main-wallet "กระเป๋าเงินหลัก" + + ;validation + :invalid-phone "หมายเลขโทรศัพท์ไม่ถูกต้อง" + :amount "จำนวน" + :not-enough-eth (str "ETH ไม่เพียงพอในยอดคงเหลือ " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "ยืนยันธุรกรรม" + :other "ยืนยัน {{count}} ธุรกรรม" + :zero "ไม่มีธุรกรรม"} + :status "สถานะ" + :pending-confirmation "การยืนยันที่ค้างอยู่" + :recipient "ผู้รับ" + :one-more-item "อีกหนึ่งรายการ" + :fee "ค่าธรรมเนียม" + :value "มูลค่า" + + ;:webview + :web-view-error "อุ๊ย มีข้อผิดพลาด"}) diff --git a/src/status_im/translations/tr.cljs b/src/status_im/translations/tr.cljs new file mode 100644 index 0000000000..eb4f1df693 --- /dev/null +++ b/src/status_im/translations/tr.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.tr) + +(def translations + { + ;common + :members-title "Üyeler" + :not-implemented "!uygulanmadı" + :chat-name "Sohbet adı" + :notifications-title "Bildirimler ve sesler" + :offline "Çevrimdışı" + + ;drawer + :invite-friends "Arkadaşlarınızı davet edin" + :faq "SSS" + :switch-users "Kullanıcıları değiştir" + + ;chat + :is-typing "yazıyor" + :and-you "ve siz" + :search-chat "Sohbette ara" + :members {:one "1 üye" + :other "{{count}} üye" + :zero "üye yok"} + :members-active {:one "1 üye, 1 aktif" + :other "{{count}} üye, {{count}} aktif" + :zero "üye yok"} + :active-online "Çevrimiçi" + :active-unknown "Bilinmiyor" + :available "Uygun" + :no-messages "Mesaj yok" + :suggestions-requests "İstekler" + :suggestions-commands "Komutlar" + + ;sync + :sync-in-progress "Eşitleniyor..." + :sync-synced "Eşitleme" + + ;messages + :status-sending "Gönderiliyor" + :status-pending "Beklemede" + :status-sent "Gönderildi" + :status-seen-by-everyone "Herkes tarafından görüldü" + :status-seen "Görüldü" + :status-delivered "Teslim edildi" + :status-failed "Başarısız" + + ;datetime + :datetime-second {:one "saniye" + :other "saniye"} + :datetime-minute {:one "dakika" + :other "dakika"} + :datetime-hour {:one "saat" + :other "saat"} + :datetime-day {:one "gün" + :other "gün"} + :datetime-multiple "sn" + :datetime-ago "önce" + :datetime-yesterday "dün" + :datetime-today "bugün" + + ;profile + :profile "Profil" + :report-user "KULLANICIYI ŞİKAYET ET" + :message "Mesaj" + :username "Kullanıcı adı" + :not-specified "Belirtilmemiş" + :public-key "Ortak Anahtar" + :phone-number "Telefon numarası'" + :email "E-posta" + :profile-no-status "Durum yok" + :add-to-contacts "Kişi listesine ekle" + :error-incorrect-name "Lütfen başka bir isim seçin" + :error-incorrect-email "Hatalı e-posta adresi" + + ;;make_photo + :image-source-title "Profil resmi" + :image-source-make-photo "Çek" + :image-source-gallery "Galeriden seç" + :image-source-cancel "İptal" + + ;sign-up + :contacts-syncronized "Kişi listeniz eşitlendi" + :confirmation-code (str "Teşekkürler! Size onay kodunu içeren bir kısa mesaj " + "gönderdik. Telefon numaranızı onaylamak için lütfen bu kodu girin") + :incorrect-code (str "Üzgünüz kod hatalıydı, lütfen yeniden girin") + :generate-passphrase (str "sizin için bir parola oluşturacağım, böylece başka bir cihazdan " + "erişim ya da girişinizi kurtarabileceksiniz") + :phew-here-is-your-passphrase "*Oh* bu oldukça zor oldu işte parolanız, *bu parolayı bir yere not ederek saklayın!* Hesabınızı kurtarmak için bu parolaya ihtiyacınız olacaktır." + :here-is-your-passphrase "İşte parolanız, *bu parolayı bir yere not ederek saklayın!* Hesabınızı kurtarmak için bu parolaya ihtiyacınız olacaktır." + :written-down "Güvenli bir şekilde not ettiğinizden emin olun" + :phone-number-required "Telefon numaranızı girmek için buraya dokunun, arkadaşlarınızı ben bulacağım" + :intro-status "Hesabınızı kurmak ve ayarlarınızı değiştirmek için benimle sohbet edin!" + :intro-message1 "Status'e hoş geldiniz\nŞifrenizi oluşturmak ve hemen başlamak için bu mesaja dokunun!" + :account-generation-message "Bana birkaç saniye ayırın, hesabınızı oluşturmak için biraz matematik yapmam gerekecek!" + + ;chats + :chats "Sohbetler" + :new-chat "Yeni sohbet" + :new-group-chat "Yeni grup sohbeti" + + ;discover + :discover "Keşfet" + :none "Hiçbiri" + :search-tags "Arama etiketlerinizi buraya girin" + :popular-tags "Popüler etiketler" + :recent "Güncel" + :no-statuses-discovered "Herhangi bir durum keşfedilmedi" + + ;settings + :settings "Ayarlar" + + ;contacts + :contacts "Kişiler" + :new-contact "Yeni Kişi" + :show-all "TÜMÜNÜ GÖSTER" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Kişiler" + :contacts-group-new-chat "Yeni sohbet başlat" + :no-contacts "Henüz herhangi bir kişi yok" + :show-qr "Kare Kodu göster" + + ;group-settings + :remove "Kaldır" + :save "Kaydet" + :change-color "Rengi değiştir" + :clear-history "Geçmişi temizle" + :delete-and-leave "Sil ve ayrıl" + :chat-settings "Sohbet ayarları" + :edit "Düzenle" + :add-members "Üye Ekle" + :blue "Mavi" + :purple "Mor" + :green "Yeşil" + :red "Kırmızı" + + ;commands + :money-command-description "Para gönder" + :location-command-description "Konum gönder" + :phone-command-description "Telefon numarasını gönder" + :phone-request-text "Telefon numarası iste" + :confirmation-code-command-description "Onay kodunu gönder" + :confirmation-code-request-text "Onay kodu iste" + :send-command-description "Konum gönder" + :request-command-description "İstek gönder" + :keypair-password-command-description "" + :help-command-description "Yardım" + :request "İstek" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH alıcı: {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH gönderen: {{chat-name}}" + :command-text-location "Konum: {{address}}" + :command-text-browse "Web sayfası: {{webpage}}" + :command-text-send "İşlem: {{amount}} ETH" + :command-text-help "Yardım" + + ;new-group + :group-chat-name "Sohbet adı" + :empty-group-chat-name "Lütfen bir isim girin" + :illegal-group-chat-name "Lütfen başka bir isim seçin" + + ;participants + :add-participants "Katılımcı Ekle" + :remove-participants "Katılımcıları Sil" + + ;protocol + :received-invitation "grup sohbeti daveti alındı" + :removed-from-chat "sizi grup sohbetinden sildi" + :left "ayrıldı" + :invited "davet edildi" + :removed "silindi" + :You "Siz" + + ;new-contact + :add-new-contact "Yeni kişi ekle" + :import-qr "İçe¨aktar" + :scan-qr "Kare Kod tara" + :name "İsim" + :whisper-identity "Whisper Kimliği" + :address-explication "Burada belki de bir adresin ne olduğu ve bunu bulmak için nereye bakılmasıyla ilgili bazı metinler yer alabilir" + :enter-valid-address "Lütfen geçerli bir adres girin ya da bir Kare Kod tarayın" + :contact-already-added "Kişi zaten eklendi" + :can-not-add-yourself "Kendinizi ekleyemezsiniz" + :unknown-address "Bilinmeyen adres" + + + ;login + :connect "Bağlan" + :address "Adres" + :password "Şifre" + :login "Oturum Aç" + :wrong-password "Hatalı şifre" + + ;recover + :recover-from-passphrase "Parolayı kullanarak kurtar" + :recover-explain "Erişimi kurtarmak için lütfen şifreniz için parolanızı girin" + :passphrase "Parola" + :recover "Kurtar" + :enter-valid-passphrase "Lütfen bir parola girin" + :enter-valid-password "Lütfen bir şifre girin" + + ;accounts + :recover-access "Erişimi kurtar" + :add-account "Hesap ekle" + + ;wallet-qr-code + :done "Yapıldı" + :main-wallet "Ana Cüzdan" + + ;validation + :invalid-phone "Geçersiz telefon numarası" + :amount "Miktar" + :not-enough-eth (str "Yeterli ETH bakiyesi yok" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "İşlemi onayla" + :other "{{count}} işlemi onayla" + :zero "İşlem yok"} + :status "Durum" + :pending-confirmation "Bekleyen onay" + :recipient "Alıcı" + :one-more-item "Bir öğe daha" + :fee "Ücret" + :value "Değer" + + ;:webview + :web-view-error "hoppala, hata"}) diff --git a/src/status_im/translations/uk.cljs b/src/status_im/translations/uk.cljs new file mode 100644 index 0000000000..37ccf9031b --- /dev/null +++ b/src/status_im/translations/uk.cljs @@ -0,0 +1,232 @@ +(ns status-im.translations.uk) + +(def translations + { + ;common + :members-title "Учасники" + :not-implemented "!не реалізовано" + :chat-name "Назва групи" + :notifications-title "Сповіщення та звуки" + :offline "Оффлайн" + + ;drawer + :invite-friends "Запросити друзів" + :faq "Часті питання" + :switch-users "Зміна користувача" + + ;chat + :is-typing "друкує" + :and-you "і ви" + :search-chat "Пошук групи" + :members {:one "1 учасник" + :other "{{count}} учасників" + :zero "немає учасників"} + :members-active {:one "1 учасник, 1 активний" + :other "{{count}} учасників, {{count}} активних" + :zero "немає учасників"} + :active-online "Онлайн" + :active-unknown "Невідомо" + :available "Доступно" + :no-messages "Немає повідомлень" + :suggestions-requests "Запити" + :suggestions-commands "Команди" + + ;sync + :sync-in-progress "Синхронізація..." + :sync-synced "Синхронізовано" + + ;messages + :status-sending "Відправлення" + :status-pending "Очікує відправлення" + :status-sent "Надіслано" + :status-seen-by-everyone "Переглянуто всіма" + :status-seen "Переглянуто" + :status-delivered "Доставлено" + :status-failed "Помилка" + + ;datetime + :datetime-second {:one "секунда" + :other "секунд"} + :datetime-minute {:one "хвилина" + :other "хвилин"} + :datetime-hour {:one "година" + :other "годин"} + :datetime-day {:one "день" + :other "днів"} + :datetime-multiple "с" + :datetime-ago "тому" + :datetime-yesterday "вчора" + :datetime-today "сьогодні" + + ;profile + :profile "Профіль" + :report-user "ДОПОВІСТИ ПРО КОРИСТУВАЧА" + :message "Повідомлення" + :username "Ім'я користувача" + :not-specified "Не вказано" + :public-key "Відкритий ключ" + :phone-number "Номер телефону" + :email "Ел. пошта" + :profile-no-status "Немає статусу" + :add-to-contacts "Додати до контактів" + :error-incorrect-name "Будь ласка, виберіть інше ім'я" + :error-incorrect-email "Невірна ел. пошта" + + ;;make_photo + :image-source-title "Фото профілю" + :image-source-make-photo "Зробити знімок" + :image-source-gallery "Вибрати з галереї" + :image-source-cancel "Відміна" + + ;;sharing + :sharing-copy-to-clipboard "Скопіювати" + :sharing-share "Поділитися..." + :sharing-cancel "Відміна" + + ;sign-up + :contacts-syncronized "Ваші контактні дані було синхронізовано" + :confirmation-code (str "Дякуємо! Ми відправили вам текстове повідомлення з кодом " + "підтвердження. Будь ласка, надайте цей код, щоб підтвердити свій номер телефону") + :incorrect-code (str "На жаль, код невірний, будь ласка, введіть ще раз") + :generate-passphrase (str "Я створю ключову фразу для вас, щоб ви могли відновити ваш " + "доступ або увійти з іншого пристрою") + :phew-here-is-your-passphrase "*Оце так*, було складно, ось ваша ключова фраза, *запишіть її та надійно зберігайте!* Вона вам знадобиться для відновлення облікового запису." + :here-is-your-passphrase "Ось ваша ключова фраза, *запишіть її та надійно зберігайте!* Вона вам знадобиться для відновлення облікового запису." + :written-down "Переконайтеся, що ви надійно її записали" + :phone-number-required "Торкніться тут, щоб ввести ваш номер телефону і я знайду ваших друзів" + :intro-status "Спілкуйтеся зі мною, щоб налаштувати свій обліковий запис і змінити налаштування!" + :intro-message1 "Вітаємо в Статус\nТоркніться цього повідомлення, щоб встановити пароль і почати!" + :account-generation-message "Почекайте секунду, маю виконати страшенно складні розрахунки, щоб створити ваш обліковий запис!" + + ;chats + :chats "Групи" + :new-chat "Новий чат" + :new-group-chat "Новий груповий чат" + + ;discover + :discover "Відкриття" + :none "Жоден" + :search-tags "Введіть теги для пошуку тут" + :popular-tags "Популярні теги" + :recent "Нещодавні" + :no-statuses-discovered "Статусів не знайдено" + + ;settings + :settings "Налаштування" + + ;contacts + :contacts "Контакти" + :new-contact "Новий контакт" + :show-all "ПОКАЗАТИ ВСІ" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Люди" + :contacts-group-new-chat "Почати нову розмову" + :no-contacts "Поки що контактів немає" + :show-qr "Показати QR" + + ;group-settings + :remove "Видалити" + :save "Зберегти" + :change-color "Змінити колір" + :clear-history "Очистити історію" + :delete-and-leave "Видалити та залишити" + :chat-settings "Налаштування розмови" + :edit "Редагувати" + :add-members "Додати учасників" + :blue "Голубий" + :purple "Фіолетовий" + :green "Зелений" + :red "Червоний" + + ;commands + :money-command-description "Надіслати гроші" + :location-command-description "Надіслати місцезнаходження" + :phone-command-description "Надіслати номер телефону" + :phone-request-text "Запит номеру телефону" + :confirmation-code-command-description "Надіслати код підтвердження" + :confirmation-code-request-text "Запит коду підтвердження" + :send-command-description "Надіслати місцезнаходження" + :request-command-description "Надіслати запит" + :keypair-password-command-description "" + :help-command-description "Допомога" + :request "Запит" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH для {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH від {{chat-name}}" + :command-text-location "Місцезнаходження: {{address}}" + :command-text-browse "Перегляд веб-сторінки: {{webpage}}" + :command-text-send "Транзакція: {{amount}} ETH" + :command-text-help "Допомога" + + ;new-group + :group-chat-name "Назва розмови" + :empty-group-chat-name "Будь ласка, введіть назву" + :illegal-group-chat-name "Будь ласка, виберіть іншу назву" + + ;participants + :add-participants "Додайте учасників" + :remove-participants "Видаліть учасників" + + ;protocol + :received-invitation "отримано запрошення до розмови" + :removed-from-chat "видалив вас з розмови" + :left "вийшов" + :invited "запрошений" + :removed "видалив" + :You "Ви" + + ;new-contact + :add-new-contact "Додати новий контакт" + :import-qr "Імпорт" + :scan-qr "Сканувати QR" + :name "Назва" + :whisper-identity "Прошепотіти справжність" + :address-explication "Можливо тут повинен бути текст, який пояснює, що таке адреса і де її шукати" + :enter-valid-address "Будь ласка, введіть дійсну адресу або відскануйте QR-код" + :enter-valid-public-key "Будь ласка, введіть дійсний відкритий ключ або відскануйте QR-код" + :contact-already-added "Контакт вже додано" + :can-not-add-yourself "Ви не можете додати себе" + :unknown-address "Невідома адреса" + + + ;login + :connect "Підключитися" + :address "Адреса" + :password "Пароль" + :login "Логін" + :wrong-password "Невірний пароль" + + ;recover + :recover-from-passphrase "Відновити з ключовою фразою" + :recover-explain "Будь ласка, введіть ключову фразу для вашого пароля, щоб відновити доступ" + :passphrase "Ключова фраза" + :recover "Відновити" + :enter-valid-passphrase "Будь ласка, введіть ключову фразу" + :enter-valid-password "Будь ласка, введіть пароль" + + ;accounts + :recover-access "Відновити доступ" + :add-account "Додати обліковий запис" + + ;wallet-qr-code + :done "Готово" + :main-wallet "Основний гаманець" + + ;validation + :invalid-phone "Невірний номер телефону" + :amount "Сума" + :not-enough-eth (str "Недостатньо ETH на балансі " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Підтвердити транзакцію" + :other "Підтвердити {{count}} транзакції" + :zero "Немає транзакцій"} + :status "Статус" + :pending-confirmation "Очікує підтвердження" + :recipient "Отримувач" + :one-more-item "Ще один пункт" + :fee "Комісія" + :value "Значення" + + ;:webview + :web-view-error "ой, помилка"}) diff --git a/src/status_im/translations/ur.cljs b/src/status_im/translations/ur.cljs new file mode 100644 index 0000000000..0fd5aa8bd8 --- /dev/null +++ b/src/status_im/translations/ur.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.ur) + +(def translations + { + ;common + :members-title "ممبران" + :not-implemented "نافذ نہیں ہوا!" + :chat-name "چیٹ کا نام" + :notifications-title "نوٹیفیکیشن اور آوازیں" + :offline "آف لائن" + + ;drawer + :invite-friends "دوستوں کو مدعو کریں" + :faq "FAQ" + :switch-users "صارف تبدیل کریں" + + ;chat + :is-typing "اندراج کر رہا ہے" + :and-you "اور آپ" + :search-chat "چیٹ تلاش کریں" + :members {:one "1 ممبر" + :other "{{count}} ممبران" + :zero "کوئی ممبر نہیں"} + :members-active {:one "1 ممبر, 1 فعال" + :other "{{count}} ممبران, {count}} فعال" + :zero "کوئی ممبر نہیں"} + :active-online "آن لائن" + :active-unknown "نامعلوم" + :available "دستیاب" + :no-messages "کوئی پیغام نہیں" + :suggestions-requests "درخواستیں" + :suggestions-commands "کمانڈ" + + ;sync + :sync-in-progress "سنکرونائز ہو رہا ہے" + :sync-synced "دوران سنکرونائز" + + ;messages + :status-sending "بھیجا جا رہا ہے" + :status-pending "زیر غور" + :status-sent "بھیج دیا گیا" + :status-seen-by-everyone "ہر ایک نے دیکھ لیا ہے" + :status-seen "دیکھ لیا گیا" + :status-delivered "پہنچا دیا گیا" + :status-failed "ناکام ہو گیا" + + ;datetime + :datetime-second {:one "سیکنڈ" + :other "سیکنڈ"} + :datetime-minute {:one "منٹ" + :other "منٹ"} + :datetime-hour {:one "گھنٹہ" + :other "گھنٹے"} + :datetime-day {:one "دن" + :other "دن"} + :datetime-multiple "s" + :datetime-ago "قبل" + :datetime-yesterday "کل" + :datetime-today "آج" + + ;profile + :profile "پروفائل" + :report-user "صارف کی رپورٹ کریں" + :message "پیغام" + :username "صارف کا نام" + :not-specified "واضح نہیں کیا گیا" + :public-key "Public Key" + :phone-number "فون نمبر" + :email "ای میل" + :profile-no-status "کوئی سٹیٹس نہیں" + :add-to-contacts "اپنے رابطوں میں درج کریں" + :error-incorrect-name "برائے مہربانی کوئی اور نام چنیں" + :error-incorrect-email "غلط ای میل" + + ;;make_photo + :image-source-title "پروفائل تصویر" + :image-source-make-photo "کھینچیں" + :image-source-gallery "گیلری سے چنیں" + :image-source-cancel "کینسل" + + ;sign-up + :contacts-syncronized "آپ کے کانٹیکٹس سنکرونائز ہو گئے ہیں" + :confirmation-code (str "شکریہ! ہم نے آپ کو تصدیق کا پیغام بھیج دیا ہے " + "کوڈ کے ساتھ۔ برائے مہربانی اپنا نمبر کنفرم کرنے کے لیے وہ کوڈ فراہم کریں۔") + :incorrect-code (str "معذرت، کوڈ غلط تھا، برائے مہربانی دوبارہ درج کریں") + :generate-passphrase (str "میں آپ کے لیے passphrase تشکیل دوں گا تا کہ آپ " + "کسی دوسری ڈیوائس سے لاگ ان یا رسائی حاصل کر سکیں") + :phew-here-is-your-passphrase "*اف* یہ مشکل تھا, یہ آپ کی passphrase ہے۔, *اسے لکھ لیں اور اپنے پاس محفوظ کر لیں۔* آپ کو اپنا اکاؤنٹ بحال کرنے کے لیے اس کی ضرورت ہو گی۔" + :here-is-your-passphrase "یہ آپ کی passphrase ہے۔, *ا اکاؤنٹ بحال کرنے کے لیے اس کی ضرورت * ہے۔, *اسے لکھ لیں اور اپنے پاس محفوظ کر لیں۔* آپ کو ا" + :written-down "یہ یقینی بنا لیں کہ آپ نے اسے بحفاظت لکھ لیا ہے۔" + :phone-number-required "اپنا فون نمبر درج کرنے کے لیے یہاں ٹیپ کریں اور میں آپ کے دوستوں کو تلاش کروں گا" + :intro-status "اپنا اکاؤنٹ سیٹ اپ کرنے اور سیٹنگز تبدیل کرنے کے لیے مجھ سے چیٹ کریں!" + :intro-message1 "سٹیٹس میں خوش آمدید اور اپنا پاسورڈ سیٹ کرنے کے لیے اس کو ٹیپ کریں اور شروعات کریں۔" + :account-generation-message "مجھے ایک سیکنڈ دیں، اپ کا اکاؤنٹ تشکیل دینے کے لیے مجھے کچھ ریاضی سے کام کرنا ہے۔!" + + ;chats + :chats "چیٹ" + :new-chat "نئی چیٹ" + :new-group-chat "نئی گروپ چیٹ" + + ;discover + :discover "دریافت" + :none "کوئی نہیں" + :search-tags "اپنا تلاش کا ٹیگ یہاں درج کریں" + :popular-tags "مشہور ٹیگ" + :recent "حالیہ" + :no-statuses-discovered "کوئی سٹیٹس نہیں پایا گیا" + + ;settings + :settings "سیٹنگز" + + ;contacts + :contacts "رابطے" + :new-contact "نئے رابطے" + :show-all "سب دکھائیں" + :contacts-group-dapps "ÐApps" + :contacts-group-people "لوگ" + :contacts-group-new-chat "نئی چیٹ شروع کریں" + :no-contacts "ابھی یہاں کوئی کنٹیکٹ نہیں" + :show-qr "دکھائیں QR" + + ;group-settings + :remove "نکالیں" + :save "محفوظ کریں" + :change-color "رنگ تبدیل کریں" + :clear-history "تاریخ مٹا دیں" + :delete-and-leave "حذف کریں اور چھوڑ دیں" + :chat-settings "چیٹ کی سیٹنگز" + :edit "تصحیح" + :add-members "ممبران شامل کریں" + :blue "نیلا" + :purple "جامنی" + :green "سبز" + :red "سرخ" + + ;commands + :money-command-description "رقم بھیجیں" + :location-command-description "مقام بھیجیں" + :phone-command-description "فون نمبر بھیجیں" + :phone-request-text "فون نمبر کی درخواست کریں" + :confirmation-code-command-description "برائے مہربانی یقین دہانی کا کوڈ بھیجیں" + :confirmation-code-request-text "تصدیقی کوڈ کی درخواست" + :send-command-description "مقام بھیجیں" + :request-command-description "درخواست بھیجیں" + :keypair-password-command-description "" + :help-command-description "مدد" + :request "درخواست" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH کرنے {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH سے {{chat-name}}" + :command-text-location "مقام: {{address}}" + :command-text-browse "ویب پیج براؤز کریں: {{webpage}}" + :command-text-send "بیوپار: {{amount}} ETH" + :command-text-help "مدد" + + ;new-group + :group-chat-name "چیٹ کا نام" + :empty-group-chat-name "برائے مہربانی کو ایک نام درج کریں" + :illegal-group-chat-name "برائے مہربانی کوئی اور نام درج کریں" + + ;participants + :add-participants "ساتھیوں کو شامل کریں" + :remove-participants "ساتھیوں کو نکالیں" + + ;protocol + :received-invitation "چیٹ کا دعوت نامہ وصول ہوا ہے" + :removed-from-chat "گروپ سے نکال دیا ہے" + :left "چھوڑ دیا" + :invited "مدعو" + :removed "نکال دیا گیا" + :You "آپ" + + ;new-contact + :add-new-contact "نیا کانٹیکٹ شامل کریں" + :import-qr "امپورٹ" + :scan-qr "سکین کریں QR" + :name "نام" + :whisper-identity "شناخت بتائیں" + :address-explication "شائد یہاں آپ کو کچھ لکھنا چاہیے دیکھنے کے لیے کہ پتہ کیا ہے اور اسے کہاں ہونا چاہیے" + :enter-valid-address "برائے مہربانی درست پتہ یا QR سکین کریں" + :contact-already-added "کانٹیکٹ پہلے سے شامل ہے" + :can-not-add-yourself "آپ خود کو شامل نہیں کر سکتے" + :unknown-address "نامعلوم پتہ" + + + ;login + :connect "ملیں" + :address "پتہ" + :password "پاسورڈ" + :login "لاگ ان" + :wrong-password "غلط پاسورڈ" + + ;recover + :recover-from-passphrase "passphrase کے ذریعے بحال کریں" + :recover-explain "رسائی حاصل کرنے کے لیے اپنے پاسورڈ کا passphrase درج کریں" + :passphrase "Passphrase" + :recover "بحال کریں" + :enter-valid-passphrase "برائے مہربانی passphrase درج کریں" + :enter-valid-password "برائے مہربانی پاسورڈ لکھیں" + + ;accounts + :recover-access "رسائی بحال کریں" + :add-account "اکاؤنٹ شامل کریں" + + ;wallet-qr-code + :done "ہو گیا" + :main-wallet "مرکزی والٹ" + + ;validation + :invalid-phone "غلط فون نمبر" + :amount "رقم" + :not-enough-eth (str "بیلنس میں کافی ETH موجود نہیں۔ " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "بیوپار کی تصدیق کریں" + :other "تصدیق {{count}} بیوپار" + :zero "کوئی بیوپار نہیں ہوا"} + :status "سٹیٹس" + :pending-confirmation "تصدیق کی ضرورت ہے" + :recipient "وصول کنندہ" + :one-more-item "ایک اور چیز" + :fee "فیس" + :value "رقم" + + ;:webview + :web-view-error "اوہ، غلطی ہو گئی"}) diff --git a/src/status_im/translations/vi.cljs b/src/status_im/translations/vi.cljs new file mode 100644 index 0000000000..4131fcbde0 --- /dev/null +++ b/src/status_im/translations/vi.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.vi) + +(def translations + { + ;common + :members-title "Các thành viên" + :not-implemented "!không được thực hiện" + :chat-name "Tên trò chuyện" + :notifications-title "Thông báo và âm thanh" + :offline "Không trực tuyến" + + ;drawer + :invite-friends "Mời bạn bè " + :faq "FAQ" + :switch-users "Đổi người dùng" + + ;chat + :is-typing "đang gõ" + :and-you "và bạn" + :search-chat "Tìm kiếm trò chuyện" + :members {:one "1 thành viên" + :other "{{count}} thành viên" + :zero "không thành viên"} + :members-active {:one "1 thành viên, 1 đang hoạt động" + :other "{{count}} thành viên, {{count}} đang hoạt động" + :zero "không thành viên"} + :active-online "Trực tuyến" + :active-unknown "Không rõ" + :available "Sẵn sàng" + :no-messages "Không có tin nhắn" + :suggestions-requests "Các yêu cầu" + :suggestions-commands "Các lệnh" + + ;sync + :sync-in-progress "Đang đồng bộ..." + :sync-synced "Đồng bộ" + + ;messages + :status-sending "Đang gửi" + :status-pending "Chưa giải quyết" + :status-sent "Đã gửi" + :status-seen-by-everyone "Được nhìn thấy bởi mọi người" + :status-seen "Đã nhìn thấy " + :status-delivered "Đã gửi" + :status-failed "Đã thất bại" + + ;datetime + :datetime-second {:one "giây" + :other "giây"} + :datetime-minute {:one "phút" + :other "phút"} + :datetime-hour {:one "giờ" + :other "giờ"} + :datetime-day {:one "ngày" + :other "ngày"} + :datetime-multiple "s" + :datetime-ago "trước đây" + :datetime-yesterday "hôm qua" + :datetime-today "hôm nay" + + ;profile + :profile "Hồ sơ" + :report-user "BÁO CÁO NGƯỜI DÙNG" + :message "Tin nhắn" + :username "Tên người dùng" + :not-specified "Không được xác định" + :public-key "Khóa công khai" + :phone-number "Số điện thoại" + :email "Email" + :profile-no-status "Không có trạng thái" + :add-to-contacts "Thêm vào liên hệ" + :error-incorrect-name "Vui lòng chọn một tên khác" + :error-incorrect-email "E-mail không chính xách" + + ;;make_photo + :image-source-title "Ảnh đại diện" + :image-source-make-photo "Chụp" + :image-source-gallery "Chọn từ gallery" + :image-source-cancel "Hủy" + + ;sign-up + :contacts-syncronized "Các liên hệ của bạn đã được đồng bộ hóa" + :confirmation-code (str "Cảm ơn! Chúng tôi đã gửi cho bạn một tin nhắn văn bản với một xác nhận " + "Vui lòng cung cấp mã đó để xác nhận số điện thoại của bạn") + :incorrect-code (str "Xin lỗi mã không chính xác, vui lòng nhập lại") + :generate-passphrase (str "Tôi sẽ tạo ra một cụm mật khẩu để cho bạn có thể khôi phục " + "quyền truy cập của bạn hoặc đăng nhập từ một thiết bị khác") + :phew-here-is-your-passphrase "*Phù* điều đó thật khó khăn, đây là cụm mật khẩu của bạn, *hãy viết cụm này ra và giữ nó an toàn!* Bạn sẽ cần nó để khôi phục tài khoản của bạn." + :here-is-your-passphrase "Đây là cụm mật khẩu của bạn, *hãy viết cụm này ra và giữ nó an toàn!* Bạn sẽ cần nó để khôi phục tài khoản của bạn." + :written-down "Hãy đảm bảo rằng bạn đã viết nó ra một cách bảo mật" + :phone-number-required "Nhấp vào đây để nhập số điện thoại của bạn & tôi sẽ tìm kiếm bạn bè của bạn" + :intro-status "Trò chuyện với tôi để thiết lập tài khoản của bạn và thay đổi các thiết lập của bạn!" + :intro-message1 "Chào mừng đến với trang Trạng thái\nNhấp vào tin nhắn này để thiết lập mật khẩu của bạn & bắt đầu!" + :account-generation-message "Cho tôi một giây, tôi phải làm vài thuật toán điên khùng để khởi tạo tài khoản của bạn!" + + ;chats + :chats "Trò chuyện" + :new-chat "Cuộc trò chuyện mới" + :new-group-chat "Cuộc trò chuyện theo nhóm mới" + + ;discover + :discover "Khám phá" + :none "Không" + :search-tags "Gõ các thẻ tìm kiếm của bạn tại đây" + :popular-tags "Các thẻ phổ biến" + :recent "Gần đây" + :no-statuses-discovered "Không có trạng thái được tìm thấy" + + ;settings + :settings "Các thiết lập" + + ;contacts + :contacts "Các liên hệ" + :new-contact "Liên hệ mới" + :show-all "HIỂN THỊ TOÀN BỘ" + :contacts-group-dapps "ÐApps" + :contacts-group-people "Cá nhân" + :contacts-group-new-chat "Bắt đầu cuộc trò chuyện mới" + :no-contacts "Chưa có liên hệ" + :show-qr "Hiển thị QR" + + ;group-settings + :remove "Xóa" + :save "Lưu" + :change-color "Thay đổi màu sắc" + :clear-history "Xóa lịch sử" + :delete-and-leave "Xóa và rời đi" + :chat-settings "Thiết lập trò chuyện" + :edit "Điều chỉnh" + :add-members "Thêm Thành viên" + :blue "Màu xanh da trời" + :purple "Màu tím" + :green "Màu xanh lá cây" + :red "Màu đỏ" + + ;commands + :money-command-description "Gửi tiền" + :location-command-description "Gửi vị trí" + :phone-command-description "Gửi số điện thoại" + :phone-request-text "Yêu cầu số điện thoại" + :confirmation-code-command-description "Gửi mã xác nhận" + :confirmation-code-request-text "Yêu cầu mã xác nhận" + :send-command-description "Gửi vị trí" + :request-command-description "Gửi yêu cầu" + :keypair-password-command-description "" + :help-command-description "Giúp đỡ" + :request "Yêu cầu" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH đến {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH từ {{chat-name}}" + :command-text-location "Vị trí: {{address}}" + :command-text-browse "Trình duyệt trang web: {{webpage}}" + :command-text-send "Giao dịch: {{amount}} ETH" + :command-text-help "Giúp đỡ" + + ;new-group + :group-chat-name "Tên trò chuyện" + :empty-group-chat-name "Vui lòng nhập một tên" + :illegal-group-chat-name "Vui lòng chọn một tên khác" + + ;participants + :add-participants "Thêm Người tham gia" + :remove-participants "Xóa Người tham gia" + + ;protocol + :received-invitation "đã nhận lời mời trò chuyện" + :removed-from-chat "đã xóa bạn khỏi cuộc trò chuyện theo nhóm" + :left "đã rời đi" + :invited "đã mời" + :removed "đã xóa" + :You "Bạn" + + ;new-contact + :add-new-contact "Thêm liên hệ mới" + :import-qr "Nhập" + :scan-qr "Quét QR" + :name "Tên" + :whisper-identity "Danh tính Whisper" + :address-explication "Có lẽ ở đây nên có một vài nội dung để giải thích địa chỉ này là gì và phải tìm nó ở đâu" + :enter-valid-address "Vui lòng nhập một địa chỉ hợp lệ hoặc quét một mã QR" + :contact-already-added "Liên hệ đã được thêm vào" + :can-not-add-yourself "Bạn không thể tự thêm mình" + :unknown-address "Địa chỉ không xác định" + + + ;login + :connect "Kết nối" + :address "Địa chỉ" + :password "Mật khẩu" + :login "Đăng nhập" + :wrong-password "Mật khẩu sai" + + ;recover + :recover-from-passphrase "Khôi phục từ cụm mật khẩu" + :recover-explain "Vui lòng nhập cụm mật khẩu cho mật khẩu của bạn để khôi phục quyền truy cập" + :passphrase "Cụm mật khẩu" + :recover "Khôi phục" + :enter-valid-passphrase "Vui lòng nhập một cụm mật khẩu" + :enter-valid-password "Vui lòng nhập một mật khẩu" + + ;accounts + :recover-access "Khôi phục quyền truy cập" + :add-account "Thêm tài khoản" + + ;wallet-qr-code + :done "Đã hoàn thành" + :main-wallet "Ví chính" + + ;validation + :invalid-phone "Số điện thoại không hợp lệ" + :amount "Số tiền" + :not-enough-eth (str "Không đủ ETH trong số dư " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "Xác nhận giao dịch" + :other "Xác nhận {{count}} giao dịch" + :zero "Không có giao dịch"} + :status "Trạng thái" + :pending-confirmation "Chờ xác nhận" + :recipient "Người nhận" + :one-more-item "Thêm một mục nữa" + :fee "Phí" + :value "Giá trị" + + ;:webview + :web-view-error "Ối, lỗi"}) diff --git a/src/status_im/translations/zh_hans.cljs b/src/status_im/translations/zh_hans.cljs new file mode 100644 index 0000000000..4eaa142e89 --- /dev/null +++ b/src/status_im/translations/zh_hans.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.zh-hans) + +(def translations + { + ;common + :members-title "成员" + :not-implemented "!未实现" + :chat-name "聊天名称" + :notifications-title "通知和声音" + :offline "离线" + + ;drawer + :invite-friends "邀请朋友" + :faq "常问问题解答" + :switch-users "S切换用户" + + ;chat + :is-typing "正在打字" + :and-you "你" + :search-chat "搜索聊天" + :members {:one "1个成员" + :other "{{count}}个成员" + :zero "没有成员"} + :members-active {:one "1个成员,1个活跃" + :other "{{count}}个成员,{{count}}个活跃" + :zero "没有成员"} + :active-online "在线" + :active-unknown "未知" + :available "可用" + :no-messages "没有消息" + :suggestions-requests "请求" + :suggestions-commands "命令" + + ;sync + :sync-in-progress "正在同步…" + :sync-synced "同步中" + + ;messages + :status-sending "正在发送" + :status-pending "发送中" + :status-sent "已发送" + :status-seen-by-everyone "所有人可见" + :status-seen "看到" + :status-delivered "已交付" + :status-failed "失败" + + ;datetime + :datetime-second {:one "秒" + :other "秒"} + :datetime-minute {:one "分钟" + :other "分钟"} + :datetime-hour {:one "小时" + :other "小时"} + :datetime-day {:one "天" + :other "天"} + :datetime-multiple "s" + :datetime-ago "前" + :datetime-yesterday "昨天" + :datetime-today "今天" + + ;profile + :profile "个人资料" + :report-user "举报用户" + :message "信息" + :username "用户名" + :not-specified "未指定" + :public-key "公钥" + :phone-number "电话号码" + :email "电子邮件" + :profile-no-status "无状态" + :add-to-contacts "添加到联系人" + :error-incorrect-name "请选择其它名称" + :error-incorrect-email "电子邮件不正确" + + ;;make_photo + :image-source-title "个人资料图片" + :image-source-make-photo "拍摄" + :image-source-gallery "从图库中选择" + :image-source-cancel "取消" + + ;sign-up + :contacts-syncronized "已同步你的联系人" + :confirmation-code (str "谢谢!我们向你发送了一条包含确认信息的短信 " + "代码。请提供该代码以确认你的电话号码") + :incorrect-code (str "抱歉,代码不正确,请重新输入") + :generate-passphrase (str "我会为你生成一个密码短语,以便你恢复自己的密码 " + "从另一台设备访问或登录") + :phew-here-is-your-passphrase "*唷* 真不容易,这是你的密码短语,*写下来,好好保管它!*你需要用它来恢复你的帐户。" + :here-is-your-passphrase "这是你的密码短语,*写下来,好好保管它!*你需要用它来恢复你的帐户。" + :written-down "确保你把它安全地写下来" + :phone-number-required "点击此处输入你的电话号码,我会找到你的朋友" + :intro-status "跟我聊天,以设置你的帐户并更改你的设置!" + :intro-message1 "欢迎来到状态\n点击该消息,以设置你的密码并开始!" + :account-generation-message "给我一点时间,我得疯狂地计算一下,以生成你的帐户!" + + ;chats + :chats "聊天" + :new-chat "新的聊天" + :new-group-chat "新的群聊" + + ;discover + :discover "发现" + :none "无" + :search-tags "在此处输入搜索标签" + :popular-tags "热门标签" + :recent "最近" + :no-statuses-discovered "未发现状态" + + ;settings + :settings "设置" + + ;contacts + :contacts "联系人" + :new-contact "新的联系人" + :show-all "显示所有" + :contacts-group-dapps "ÐApps" + :contacts-group-people "联系人" + :contacts-group-new-chat "开始新的聊天" + :no-contacts "还没有联系人" + :show-qr "显示QR" + + ;group-settings + :remove "删除" + :save "保存" + :change-color "更改颜色" + :clear-history "清除历史记录" + :delete-and-leave "删除并退出" + :chat-settings "聊天设置" + :edit "编辑" + :add-members "添加成员" + :blue "蓝色" + :purple "紫色" + :green "绿色" + :red "红色" + + ;commands + :money-command-description "汇款" + :location-command-description "发送位置" + :phone-command-description "发送电话号码" + :phone-request-text "电话号码请求" + :confirmation-code-command-description "发送确认码" + :confirmation-code-request-text "确认码请求" + :send-command-description "发送位置" + :request-command-description "发送请求" + :keypair-password-command-description "" + :help-command-description "帮助" + :request "请求" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "给{{chat-name}}的{{amount}} ETH" + :chat-send-eth-from "来自{{chat-name}}的{{amount}} ETH" + :command-text-location "位置: {{address}}" + :command-text-browse "浏览网页: {{webpage}}" + :command-text-send "交易: {{amount}} ETH" + :command-text-help "帮助" + + ;new-group + :group-chat-name "聊天名称" + :empty-group-chat-name "请输入名称" + :illegal-group-chat-name "请选择其它名称" + + ;participants + :add-participants "添加参与者" + :remove-participants "删除参与者" + + ;protocol + :received-invitation "收到了聊天邀请" + :removed-from-chat "已将你从群聊中删除" + :left "剩余" + :invited "已邀请" + :removed "已删除" + :You "你" + + ;new-contact + :add-new-contact "添加新的联系人" + :import-qr "导入" + :scan-qr "扫描QR" + :name "名称" + :whisper-identity "Whisper身份" + :address-explication "也许这里应该有一些文本来解释什么是地址,以及在哪里查找它" + :enter-valid-address "请输入有效地址或扫描QR码" + :contact-already-added "已添加该联系人" + :can-not-add-yourself "不能添加自己" + :unknown-address "未知地址" + + + ;login + :connect "连接" + :address "地址" + :password "密码" + :login "登录" + :wrong-password "密码错误" + + ;recover + :recover-from-passphrase "从密码短语恢复" + :recover-explain "请输入密码短语,以便使密码恢复访问" + :passphrase "密码短语" + :recover "恢复" + :enter-valid-passphrase "请输入密码短语" + :enter-valid-password "请输入密码" + + ;accounts + :recover-access "恢复访问" + :add-account "添加帐户" + + ;wallet-qr-code + :done "完成" + :main-wallet "主钱包" + + ;validation + :invalid-phone "电话号码无效" + :amount "金额" + :not-enough-eth (str "ETH余额不足" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "确认交易" + :other "确认{{count}}笔交易" + :zero "无交易"} + :status "状态" + :pending-confirmation "待确认" + :recipient "接受方" + :one-more-item "另一项" + :fee "费用" + :value "值" + + ;:webview + :web-view-error "糟糕,出错了"}) diff --git a/src/status_im/translations/zh_hant.cljs b/src/status_im/translations/zh_hant.cljs new file mode 100644 index 0000000000..1505cd1c4d --- /dev/null +++ b/src/status_im/translations/zh_hant.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.zh-hant) + +(def translations + { + ;common + :members-title "成員" + :not-implemented "還未實施" + :chat-name "聊天名稱" + :notifications-title "通知與聲音" + :offline "離線" + + ;drawer + :invite-friends "邀請好友" + :faq "常見問題" + :switch-users "切換使用者" + + ;chat + :is-typing "正在鍵入中" + :and-you "和您" + :search-chat "搜尋聊天" + :members {:one "1個成員" + :other "{{count}} 個成員" + :zero "沒有成員"} + :members-active {:one "1 個成員, 1 個活動" + :other "{{count}} 個成員, {{count}} 個活動" + :zero "沒有成員"} + :active-online "離線" + :active-unknown "未知" + :available "可用" + :no-messages "沒有訊息" + :suggestions-requests "請求" + :suggestions-commands "命令" + + ;sync + :sync-in-progress "同步中..." + :sync-synced "同步" + + ;messages + :status-sending "正在發送" + :status-pending "待定" + :status-sent "已發送" + :status-seen-by-everyone "全部可見" + :status-seen "可見" + :status-delivered "已送達" + :status-failed "送達失敗" + + ;datetime + :datetime-second {:one "秒" + :other "秒"} + :datetime-minute {:one "分" + :other "分"} + :datetime-hour {:one "時" + :other "時"} + :datetime-day {:one "天" + :other "天"} + :datetime-multiple "秒" + :datetime-ago "以前" + :datetime-yesterday "昨天" + :datetime-today "今天" + + ;profile + :profile "簡況" + :report-user "報告使用者" + :message "訊息" + :username "使用者名稱" + :not-specified "未指定" + :public-key "公開金鑰" + :phone-number "手機號碼" + :email "電子郵件" + :profile-no-status "無狀態" + :add-to-contacts "添加到聯絡資訊" + :error-incorrect-name "請選擇另一個姓名" + :error-incorrect-email "錯誤的電子郵寄地址" + + ;;make_photo + :image-source-title "個人照片" + :image-source-make-photo "捕捉" + :image-source-gallery "從相簿中選擇" + :image-source-cancel "取消" + + ;sign-up + :contacts-syncronized "您的聯絡資訊已同步" + :confirmation-code (str "謝謝!我們已向您發送了包含 " + "確認碼的訊息。請鍵入此代碼以驗證您的手機號碼") + :incorrect-code (str "很抱歉,代碼不正確,請重新鍵入") + :generate-passphrase (str "我會給您生成一條口令句,您可以使用它 " + "在其他裝置上以訪問或登入") + :phew-here-is-your-passphrase "啊哈,這是您的口令句,*請記下來並 好好保管! *您需要使用它以回復您帳號。" + :here-is-your-passphrase "這是您的口令句,*請記下來並 好好保管! *您需要使用它以回復您帳號。" + :written-down "請一定要把它記下來" + :phone-number-required "點選這裡以鍵入您的手機號碼,我會發現您的好友" + :intro-status "與我聊天以配置您的帳號,變更您的設定!" + :intro-message1 "歡迎來到Status\n點選此訊息以設定您的密碼並開始使用!" + :account-generation-message "給我幾秒鐘,我正在努力生成您的帳號!" + + ;chats + :chats "聊天" + :new-chat "新聊天" + :new-group-chat "新的群聊" + + ;discover + :discover "發現" + :none "無" + :search-tags "在這裡鍵入您的搜尋標籤" + :popular-tags "熱門標籤" + :recent "最近" + :no-statuses-discovered "未發現狀態" + + ;settings + :settings "設定" + + ;contacts + :contacts "聯絡資訊" + :new-contact "新聯絡資訊" + :show-all "顯示全部" + :contacts-group-dapps "ÐApps" + :contacts-group-people "人" + :contacts-group-new-chat "開始新聊天" + :no-contacts "還沒有聯絡資訊" + :show-qr "顯示QR碼" + + ;group-settings + :remove "移除" + :save "保存" + :change-color "變更顏色" + :clear-history "清理歷史記錄" + :delete-and-leave "刪除並離開" + :chat-settings "聊天設定" + :edit "編輯" + :add-members "添加成員" + :blue "藍" + :purple "紫" + :green "綠" + :red "紅" + + ;commands + :money-command-description "轉帳" + :location-command-description "發送位置" + :phone-command-description "發送手機號碼" + :phone-request-text "請求手機號碼" + :confirmation-code-command-description "發送確認碼" + :confirmation-code-request-text "請求確認碼" + :send-command-description "發送位置" + :request-command-description "發送請求" + :keypair-password-command-description "" + :help-command-description "幫助" + :request "請求" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH 給 {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH 來自 {{chat-name}}" + :command-text-location "位置: {{address}}" + :command-text-browse "流覽網頁: {{webpage}}" + :command-text-send "交易: {{amount}} ETH" + :command-text-help "幫助" + + ;new-group + :group-chat-name "聊天名稱" + :empty-group-chat-name "請鍵入一個名稱" + :illegal-group-chat-name "請選擇其他名稱" + + ;participants + :add-participants "添加參與人" + :remove-participants "移除參與人" + + ;protocol + :received-invitation "接受的聊天邀請" + :removed-from-chat "把您移出了群聊" + :left "留下的" + :invited "邀請的" + :removed "移出的" + :You "您" + + ;new-contact + :add-new-contact "添加新的聯絡資訊" + :import-qr "匯入" + :scan-qr "掃描QR碼" + :name "名稱" + :whisper-identity "私聊身份" + :address-explication "也許這裡會有文本介紹是什麼樣的位址,在哪裡能找到它" + :enter-valid-address "請鍵入有效的位址或掃描QR碼" + :contact-already-added "聯絡資訊已添加" + :can-not-add-yourself "您無法添加自己" + :unknown-address "未知得的地址" + + + ;login + :connect "連線" + :address "地址" + :password "密碼" + :login "登入" + :wrong-password "密碼錯誤" + + ;recover + :recover-from-passphrase "從口令句恢復" + :recover-explain "請鍵入密碼的口令句以恢復訪問" + :passphrase "口令句" + :recover "恢復" + :enter-valid-passphrase "請鍵入口令句" + :enter-valid-password "請鍵入密碼" + + ;accounts + :recover-access "恢復訪問" + :add-account "添加帳號" + + ;wallet-qr-code + :done "完成" + :main-wallet "錢包" + + ;validation + :invalid-phone "無效的手機號碼" + :amount "金額" + :not-enough-eth (str "餘額中ETH不足 " + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "確認交易" + :other "確認 {{count}} 交易" + :zero "無交易"} + :status "狀態" + :pending-confirmation "等待確認" + :recipient "接收人" + :one-more-item "再來一個" + :fee "費用" + :value "價值" + + ;:webview + :web-view-error "哎呀,出錯了"}) diff --git a/src/status_im/translations/zh_wuu.cljs b/src/status_im/translations/zh_wuu.cljs new file mode 100644 index 0000000000..65f86d9054 --- /dev/null +++ b/src/status_im/translations/zh_wuu.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.zh-wuu) + +(def translations + { + ;common + :members-title "会员" + :not-implemented "!未实现" + :chat-name "聊天名称" + :notifications-title "通知和声音" + :offline "离线" + + ;drawer + :invite-friends "邀请朋友" + :faq "常见问题" + :switch-users "切换用户" + + ;chat + :is-typing "正在输入" + :and-you "和您" + :search-chat "搜索聊天" + :members {:one "1人" + :other "{{count}}会员" + :zero "无会员"} + :members-active {:one "1个会员,1个活跃成员" + :other "{{count}} 会员, {{count}} 活跃成员" + :zero "无会员"} + :active-online "在线" + :active-unknown "未知" + :available "可用" + :no-messages "无信息" + :suggestions-requests "请求" + :suggestions-commands "命令" + + ;sync + :sync-in-progress "同步中..." + :sync-synced "同步" + + ;messages + :status-sending "发送中" + :status-pending "待定" + :status-sent "已发送" + :status-seen-by-everyone "每个人已阅" + :status-seen "已阅" + :status-delivered "已发送" + :status-failed "发送失败" + + ;datetime + :datetime-second {:one "秒" + :other "秒"} + :datetime-minute {:one "分钟" + :other "分钟"} + :datetime-hour {:one "小时" + :other "小时"} + :datetime-day {:one "日" + :other "日"} + :datetime-multiple "s" + :datetime-ago "之前" + :datetime-yesterday "昨天" + :datetime-today "今天" + + ;profile + :profile "个人资料" + :report-user "报告用户" + :message "消息" + :username "用户名" + :not-specified "未指定" + :public-key "公共密钥" + :phone-number "电话号码" + :email "电子邮箱" + :profile-no-status "无状态" + :add-to-contacts "添加至通讯录" + :error-incorrect-name "请选择另一个名字" + :error-incorrect-email "不正确的电子邮箱" + + ;;make_photo + :image-source-title "个人资料照片" + :image-source-make-photo "截图" + :image-source-gallery "从相册中选择" + :image-source-cancel "取消" + + ;sign-up + :contacts-syncronized "您的联系人已同步" + :confirmation-code (str "谢谢!我们已经给您发了一个确认短信" + "代码。请提供该代码以确认您的电话号码") + :incorrect-code (str "对不起,代码不正确,请再输入一次") + :generate-passphrase (str "我会为您生成一个口令短语,您可以恢复您的 " + "从另一个设备访问或登录") + :phew-here-is-your-passphrase "*唷* 是很难的,这是您的口令短语,*写下来并保证安全!* 您将需要它来恢复您的帐户。" + :here-is-your-passphrase "这是您的口令短语,*写下来并保证安全!* 您将需要它来恢复您的帐户。" + :written-down "确保您已经安全地写下来" + :phone-number-required "点击这里进入您的电话号码,我会找到您的朋友" + :intro-status "与我聊天设置您的帐户,并更改您的设置!" + :intro-message1 "欢迎来到\n点击这一消息设置您的密码并开始!" + :account-generation-message "给我一秒,我要做一些疯狂的计算生成您的帐号!" + + ;chats + :chats "聊天" + :new-chat "新的聊天" + :new-group-chat "新的群聊" + + ;discover + :discover "发现" + :none "无" + :search-tags "在这里键入您的搜索标签" + :popular-tags "热门标签" + :recent "最近" + :no-statuses-discovered "未发现状态" + + ;settings + :settings "设置" + + ;contacts + :contacts "联系人" + :new-contact "新的联系人" + :show-all "显示全部" + :contacts-group-dapps "ÐApps" + :contacts-group-people "人" + :contacts-group-new-chat "开始新的聊天" + :no-contacts "暂无联系人" + :show-qr "显示QR" + + ;group-settings + :remove "移动" + :save "保存" + :change-color "改变颜色" + :clear-history "清除历史" + :delete-and-leave "删除并离开" + :chat-settings "聊天设置" + :edit "编辑" + :add-members "添加会员" + :blue "蓝色" + :purple "紫色" + :green "绿色" + :red "红色" + + ;commands + :money-command-description "发送钱" + :location-command-description "发送地址" + :phone-command-description "发送电话号码" + :phone-request-text "请求电话号码" + :confirmation-code-command-description "发送确认码" + :confirmation-code-request-text "请求确认码" + :send-command-description "发送地址" + :request-command-description "发送请求" + :keypair-password-command-description "" + :help-command-description "帮助" + :request "请求" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH至 {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH来自 {{chat-name}}" + :command-text-location "地址: {{address}}" + :command-text-browse "浏览网页: {{webpage}}" + :command-text-send "交易: {{amount}} ETH" + :command-text-help "帮助" + + ;new-group + :group-chat-name "聊天名称" + :empty-group-chat-name "请输入一个名称" + :illegal-group-chat-name "选择另一个名称" + + ;participants + :add-participants "添加参与者" + :remove-participants "删除参与者" + + ;protocol + :received-invitation "接受聊天邀请" + :removed-from-chat "将您从群聊中移除" + :left "离开" + :invited "已邀请" + :removed "已移除" + :You "您" + + ;new-contact + :add-new-contact "增加新的联系人" + :import-qr "导入" + :scan-qr "扫描QR" + :name "名称" + :whisper-identity "耳语身份" + :address-explication "也许这应该有一个文本,解释地址是什么以及在哪里寻找它" + :enter-valid-address "请输入一个有效的地址或扫描QR码" + :contact-already-added "联系人已被添加" + :can-not-add-yourself "您不能添加您自己" + :unknown-address "未知地址" + + + ;login + :connect "连接" + :address "地址" + :password "密码" + :login "登录" + :wrong-password "错误的密码" + + ;recover + :recover-from-passphrase "恢复口令短语" + :recover-explain "请输入密码的口令短语来恢复访问" + :passphrase "口令短语" + :recover "恢复" + :enter-valid-passphrase "请输入口令短语" + :enter-valid-password "请输入密码" + + ;accounts + :recover-access "恢复访问" + :add-account "添加账户" + + ;wallet-qr-code + :done "完成" + :main-wallet "主要钱包" + + ;validation + :invalid-phone "无效的电话号码" + :amount "金额" + :not-enough-eth (str "ETH余额不足" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "确认交易" + :other "确认{{count}} 交易" + :zero "无交易"} + :status "状态" + :pending-confirmation "待确认" + :recipient "收件人" + :one-more-item "一个以上项目" + :fee "金额" + :value "价值" + + ;:webview + :web-view-error "啊哦,错误"}) diff --git a/src/status_im/translations/zh_yue.cljs b/src/status_im/translations/zh_yue.cljs new file mode 100644 index 0000000000..8c959d2508 --- /dev/null +++ b/src/status_im/translations/zh_yue.cljs @@ -0,0 +1,226 @@ +(ns status-im.translations.zh-yue) + +(def translations + { + ;common + :members-title "會員" + :not-implemented "!未能實現" + :chat-name "用戶名稱" + :notifications-title "通知及聲音設定" + :offline "離線" + + ;drawer + :invite-friends "邀請好友" + :faq "常見問題" + :switch-users "切換用戶" + + ;chat + :is-typing "正在輸入" + :and-you "與你" + :search-chat "搜索聊天記錄" + :members {:one "1位成員" + :other "{{count}} 位成員" + :zero "暫無成員"} + :members-active {:one "1位成員, 1位活躍" + :other "{{count}}位成員, {{count}}位活躍" + :zero "暫無成員"} + :active-online "在線" + :active-unknown "未知" + :available "有空" + :no-messages "沒有新訊息" + :suggestions-requests "徵求" + :suggestions-commands "指令" + + ;sync + :sync-in-progress "正在同步..." + :sync-synced "已同步" + + ;messages + :status-sending "發送中" + :status-pending "待定" + :status-sent "發送成功" + :status-seen-by-everyone "所有人已讀" + :status-seen "已讀" + :status-delivered "已發送" + :status-failed "失敗" + + ;datetime + :datetime-second {:one "秒" + :other "秒"} + :datetime-minute {:one "分鐘" + :other "分鐘"} + :datetime-hour {:one "小時" + :other "小時"} + :datetime-day {:one "天" + :other "天"} + :datetime-multiple "s" + :datetime-ago "之前" + :datetime-yesterday "昨天" + :datetime-today "今天" + + ;profile + :profile "用戶簡介" + :report-user "投訴用戶" + :message "短訊" + :username "用戶名稱" + :not-specified "未標明" + :public-key "公共鑰匙" + :phone-number "電話號碼" + :email "電郵" + :profile-no-status "無狀態" + :add-to-contacts "添加到通訊錄" + :error-incorrect-name "請選擇其他名稱" + :error-incorrect-email "電郵錯誤" + + ;;make_photo + :image-source-title "封面照片" + :image-source-make-photo "捕獲照片" + :image-source-gallery "從圖庫中選取" + :image-source-cancel "取消" + + ;sign-up + :contacts-syncronized "你的聯繫人已同步" + :confirmation-code (str "謝謝!我們已以短訊形式將確認信息發送給你" + "代碼。請提供該代碼,以確認你的電話號碼") + :incorrect-code (str "對不起,代碼不正確,請重新輸入") + :generate-passphrase (str "正在為你產生臨時登入碼,以便恢復你的" + "從另一設備訪問或登錄") + :phew-here-is-your-passphrase "*啊* 那可真不簡單。 這是你的臨時登入碼。 *請記錄並保存於安全地方!* 你將會需要它,以恢復你的帳戶。" + :here-is-your-passphrase "這是你的臨時登入碼。 *請記錄並保存於安全地方!* 你將會需要它,以恢復你的帳戶。" + :written-down "請確保你已經將其安全寫下來" + :phone-number-required "請點擊這裡輸入您的電話號碼,我們將為你尋覓你的好友" + :intro-status "如需設置新帳戶或更改現有設置,請與我聊天!" + :intro-message1 "歡迎來到Status\n請點擊此短訊,以設置你的密碼及開始!" + :account-generation-message "請給我一秒鐘。我正在瘋狂地運算,以啟動你的帳戶!" + + ;chats + :chats "聊天史" + :new-chat "新增聊天" + :new-group-chat "新增群聊" + + ;discover + :discover "新發現" + :none "不存在" + :search-tags "請輸入你的搜索標籤" + :popular-tags "熱門標籤" + :recent "最近發現" + :no-statuses-discovered "沒有發現任何狀態" + + ;settings + :settings "設置" + + ;contacts + :contacts "聯絡人" + :new-contact "新增聯絡" + :show-all "顯示所有" + :contacts-group-dapps "ÐApps" + :contacts-group-people "用戶" + :contacts-group-new-chat "開啟新聊天" + :no-contacts "No contacts yet" + :show-qr "暫無聯絡人" + + ;group-settings + :remove "刪除" + :save "儲存" + :change-color "更改顏色" + :clear-history "清除歷史記錄" + :delete-and-leave "刪除並離開" + :chat-settings "聊天設置" + :edit "編輯" + :add-members "增添成員" + :blue "藍" + :purple "紫" + :green "綠" + :red "紅" + + ;commands + :money-command-description "發送金錢" + :location-command-description "發送位置" + :phone-command-description "發送電話號碼" + :phone-request-text "請求電話號碼" + :confirmation-code-command-description "發送確認碼" + :confirmation-code-request-text "徵求確認碼" + :send-command-description "發送位置" + :request-command-description "發送請求" + :keypair-password-command-description "" + :help-command-description "幫助" + :request "徵求" + :chat-send-eth "{{amount}} ETH" + :chat-send-eth-to "{{amount}} ETH 予 {{chat-name}}" + :chat-send-eth-from "{{amount}} ETH 來自 {{chat-name}}" + :command-text-location "地點: {{address}}" + :command-text-browse "瀏覽網頁: {{webpage}}" + :command-text-send "交易: {{amount}} ETH" + :command-text-help "幫助" + + ;new-group + :group-chat-name "聊天用戶名稱" + :empty-group-chat-name "請輸入名稱" + :illegal-group-chat-name "請選擇其他名稱" + + ;participants + :add-participants "增添參與者" + :remove-participants "刪除參與者" + + ;protocol + :received-invitation "收到聊天邀請" + :removed-from-chat "已將你從群組聊天中刪除" + :left "離開" + :invited "邀請" + :removed "刪除" + :You "你" + + ;new-contact + :add-new-contact "添加新聯繫人" + :import-qr "輸入" + :scan-qr "掃描 QR" + :name "名稱" + :whisper-identity "秘密身份" + :address-explication "請注意: 此文本解釋地址是什麼,以及在何處可找到它。" + :enter-valid-address "請輸入有效地址或掃描QR碼" + :contact-already-added "此聯繫人已添加" + :can-not-add-yourself "你不能添加自己" + :unknown-address "未知地址" + + + ;login + :connect "連接" + :address "地址" + :password "密碼" + :login "登入" + :wrong-password "密碼錯誤" + + ;recover + :recover-from-passphrase "以臨時登入碼恢復" + :recover-explain "請輸入密碼的臨時登入碼,以恢復訪問" + :passphrase "臨時登入碼" + :recover "還原" + :enter-valid-passphrase "請輸入臨時登入碼" + :enter-valid-password "請輸入密碼" + + ;accounts + :recover-access "恢復訪問" + :add-account "新增帳戶" + + ;wallet-qr-code + :done "完成" + :main-wallet "主錢包" + + ;validation + :invalid-phone "電話號碼無效" + :amount "金額" + :not-enough-eth (str "沒有足夠ETH餘額" + "({{balance}} ETH)") + ;transactions + :confirm-transactions {:one "確認交易" + :other "確認{{{count}}個交易" + :zero "無交易"} + :status "狀態" + :pending-confirmation "待確認" + :recipient "收件人" + :one-more-item "多一件" + :fee "費用" + :value "價值" + + ;:webview + :web-view-error "抱歉,錯誤"}) diff --git a/src/status_im/utils/crypt.cljs b/src/status_im/utils/crypt.cljs new file mode 100644 index 0000000000..1e40adc8bc --- /dev/null +++ b/src/status_im/utils/crypt.cljs @@ -0,0 +1,26 @@ +(ns status-im.utils.crypt + (:require [goog.crypt :refer [byteArrayToHex]] + [clojure.string :as s] + [status-im.utils.utils :as u]) + (:import goog.crypt.Sha256)) + +(def random-bytes (js/require "react-native-randombytes")) + +(def sha-256 (Sha256.)) + +(defn bytes-to-str [arr] + (s/join (map char arr))) + +(defn str-to-bytes [s] + (map (comp int) s)) + +(defn encrypt [s] + (.reset sha-256) + (.update sha-256 s) + (byteArrayToHex (.digest sha-256))) + +(defn gen-random-bytes [length cb] + (.randomBytes random-bytes length (fn [& [err buf]] + (if err + (cb {:error err}) + (cb {:buffer buf}))))) diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs new file mode 100644 index 0000000000..24d82741c9 --- /dev/null +++ b/src/status_im/utils/datetime.cljs @@ -0,0 +1,77 @@ +(ns status-im.utils.datetime + (:require [cljs-time.core :as t :refer [date-time now plus days hours before?]] + [cljs-time.coerce :refer [from-long to-long from-date]] + [cljs-time.format :refer [formatters + formatter + unparse]] + [status-im.i18n :refer [label label-pluralize]] + [goog.string :as gstring] + goog.string.format)) + +(def hour (* 1000 60 60)) +(def day (* hour 24)) +(def week (* 7 day)) +(def units [{:name :t/datetime-second :limit 60 :in-second 1} + {:name :t/datetime-minute :limit 3600 :in-second 60} + {:name :t/datetime-hour :limit 86400 :in-second 3600} + {:name :t/datetime-day :limit nil :in-second 86400}]) + +(def time-zone-offset (hours (- (/ (.getTimezoneOffset (js/Date.)) 60)))) + +(defn to-short-str + ([ms] + (to-short-str ms #(unparse (formatters :hour-minute) %))) + ([ms today-format-fn] + (let [date (from-long ms) + local (plus date time-zone-offset) + today-date (t/today) + today (date-time (t/year today-date) + (t/month today-date) + (t/day today-date)) + yesterday (plus today (days -1))] + (cond + (before? local yesterday) (unparse (formatter "dd MMM") local) + (before? local today) (label :t/datetime-yesterday) + :else (today-format-fn local))))) + +(defn day-relative [ms] + (when (> ms 0) + (to-short-str ms #(label :t/datetime-today)))) + +(defn format-time-ago [diff unit] + (let [name (label-pluralize diff (:name unit))] + (gstring/format "%s %s %s" diff name (label :t/datetime-ago)))) + +(defn time-ago [time] + (let [diff (t/in-seconds (t/interval time (t/now)))] + (if (< diff 60) + (label :t/active-online) + (let [unit (first (drop-while #(and (>= diff (:limit %)) + (:limit %)) + units))] + (-> (/ diff (:in-second unit)) + Math/floor + int + (format-time-ago unit)))))) + +(defn to-date [ms] + (from-long ms)) + +(defn now-ms [] + (to-long (now))) + +(defn format-date [format date] + (let [local (plus (from-date date) time-zone-offset)] + (unparse (formatter format) local))) + +(defn get-ordinal-date [date] + (let [local (plus (from-date date) time-zone-offset) + day (js/parseInt (unparse (formatter "d") local)) + s {0 "th" + 1 "st" + 2 "nd" + 3 "rd"} + m (mod day 100)] + (str day (or (s (mod (- m 20) 10)) + (s m) + (s 0))))) diff --git a/src/status_im/utils/ethereum_network.cljs b/src/status_im/utils/ethereum_network.cljs new file mode 100644 index 0000000000..2fb3ca707c --- /dev/null +++ b/src/status_im/utils/ethereum_network.cljs @@ -0,0 +1,23 @@ +(ns status-im.utils.ethereum-network + (:require [status-im.constants :as c])) + +(def Web3 (js/require "web3")) + +(defn web3 [] + (->> (Web3.providers.HttpProvider. c/ethereum-rpc-url) + (Web3.))) + +(def networks + {"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" :mainnet + "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" :testnet + ;; Ropsten + "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" :testnet}) + +(defn- on-block [callback] + (fn [error block] + (when-not error + (let [hash (.-hash block)] + (callback (networks hash :unknown)))))) + +(defn get-network [callback] + (.eth.getBlock (web3) 0 false (on-block callback))) diff --git a/src/status_im/utils/event.cljs b/src/status_im/utils/event.cljs new file mode 100644 index 0000000000..25e1775b99 --- /dev/null +++ b/src/status_im/utils/event.cljs @@ -0,0 +1,9 @@ +(ns status-im.utils.event + (:require [cljs.core.async :refer [ (.readFile fs path encoding) + (.then on-read) + (.catch on-error))) diff --git a/src/status_im/utils/gfycat/adjectives.cljs b/src/status_im/utils/gfycat/adjectives.cljs new file mode 100644 index 0000000000..138b38d42a --- /dev/null +++ b/src/status_im/utils/gfycat/adjectives.cljs @@ -0,0 +1,1505 @@ +(ns status-im.utils.gfycat.adjectives) + + +(def data + ["abandoned" + "able" + "absolute" + "academic" + "acceptable" + "acclaimed" + "accomplished" + "accurate" + "aching" + "acidic" + "acrobatic" + "adorable" + "adventurous" + "babyish" + "back" + "bad" + "baggy" + "bare" + "barren" + "basic" + "beautiful" + "belated" + "beloved" + "calculating" + "calm" + "candid" + "canine" + "capital" + "carefree" + "careful" + "careless" + "caring" + "cautious" + "cavernous" + "celebrated" + "charming" + "damaged" + "damp" + "dangerous" + "dapper" + "daring" + "dark" + "darling" + "dazzling" + "dead" + "deadly" + "deafening" + "dear" + "dearest" + "each" + "eager" + "early" + "earnest" + "easy" + "easygoing" + "ecstatic" + "edible" + "educated" + "fabulous" + "failing" + "faint" + "fair" + "faithful" + "fake" + "familiar" + "famous" + "fancy" + "fantastic" + "far" + "faraway" + "farflung" + "faroff" + "gargantuan" + "gaseous" + "general" + "generous" + "gentle" + "genuine" + "giant" + "giddy" + "gigantic" + "hairy" + "half" + "handmade" + "handsome" + "handy" + "happy" + "happygolucky" + "hard" + "icky" + "icy" + "ideal" + "idealistic" + "identical" + "idiotic" + "idle" + "idolized" + "ignorant" + "ill" + "illegal" + "jaded" + "jagged" + "jampacked" + "kaleidoscopic" + "keen" + "lame" + "lanky" + "large" + "last" + "lasting" + "late" + "lavish" + "lawful" + "mad" + "madeup" + "magnificent" + "majestic" + "major" + "male" + "mammoth" + "married" + "marvelous" + "naive" + "narrow" + "nasty" + "natural" + "naughty" + "obedient" + "obese" + "oblong" + "oblong" + "obvious" + "occasional" + "oily" + "palatable" + "pale" + "paltry" + "parallel" + "parched" + "partial" + "passionate" + "past" + "pastel" + "peaceful" + "peppery" + "perfect" + "perfumed" + "quaint" + "qualified" + "radiant" + "ragged" + "rapid" + "rare" + "rash" + "raw" + "recent" + "reckless" + "rectangular" + "sad" + "safe" + "salty" + "same" + "sandy" + "sane" + "sarcastic" + "sardonic" + "satisfied" + "scaly" + "scarce" + "scared" + "scary" + "scented" + "scholarly" + "scientific" + "scornful" + "scratchy" + "scrawny" + "second" + "secondary" + "secondhand" + "secret" + "selfassured" + "selfish" + "selfreliant" + "sentimental" + "talkative" + "tall" + "tame" + "tan" + "tangible" + "tart" + "tasty" + "tattered" + "taut" + "tedious" + "teeming" + "ugly" + "ultimate" + "unacceptable" + "unaware" + "uncomfortable" + "uncommon" + "unconscious" + "understated" + "unequaled" + "vacant" + "vague" + "vain" + "valid" + "wan" + "warlike" + "warm" + "warmhearted" + "warped" + "wary" + "wasteful" + "watchful" + "waterlogged" + "watery" + "wavy" + "yawning" + "yearly" + "zany" + "false" + "active" + "actual" + "adept" + "admirable" + "admired" + "adolescent" + "adorable" + "adored" + "advanced" + "affectionate" + "afraid" + "aged" + "aggravating" + "beneficial" + "best" + "better" + "bewitched" + "big" + "bighearted" + "biodegradable" + "bitesized" + "bitter" + "black" + "cheap" + "cheerful" + "cheery" + "chief" + "chilly" + "chubby" + "circular" + "classic" + "clean" + "clear" + "clearcut" + "clever" + "close" + "closed" + "decent" + "decimal" + "decisive" + "deep" + "defenseless" + "defensive" + "defiant" + "deficient" + "definite" + "definitive" + "delayed" + "delectable" + "delicious" + "elaborate" + "elastic" + "elated" + "elderly" + "electric" + "elegant" + "elementary" + "elliptical" + "embarrassed" + "fast" + "fat" + "fatal" + "fatherly" + "favorable" + "favorite" + "fearful" + "fearless" + "feisty" + "feline" + "female" + "feminine" + "few" + "fickle" + "gifted" + "giving" + "glamorous" + "glaring" + "glass" + "gleaming" + "gleeful" + "glistening" + "glittering" + "hardtofind" + "harmful" + "harmless" + "harmonious" + "harsh" + "hasty" + "hateful" + "haunting" + "illfated" + "illinformed" + "illiterate" + "illustrious" + "imaginary" + "imaginative" + "immaculate" + "immaterial" + "immediate" + "immense" + "impassioned" + "jaunty" + "jealous" + "jittery" + "key" + "kind" + "lazy" + "leading" + "leafy" + "lean" + "left" + "legal" + "legitimate" + "light" + "masculine" + "massive" + "mature" + "meager" + "mealy" + "mean" + "measly" + "meaty" + "medical" + "mediocre" + "nautical" + "near" + "neat" + "necessary" + "needy" + "odd" + "oddball" + "offbeat" + "offensive" + "official" + "old" + "periodic" + "perky" + "personal" + "pertinent" + "pesky" + "pessimistic" + "petty" + "phony" + "physical" + "piercing" + "pink" + "pitiful" + "plain" + "quarrelsome" + "quarterly" + "ready" + "real" + "realistic" + "reasonable" + "red" + "reflecting" + "regal" + "regular" + "separate" + "serene" + "serious" + "serpentine" + "several" + "severe" + "shabby" + "shadowy" + "shady" + "shallow" + "shameful" + "shameless" + "sharp" + "shimmering" + "shiny" + "shocked" + "shocking" + "shoddy" + "short" + "shortterm" + "showy" + "shrill" + "shy" + "sick" + "silent" + "silky" + "tempting" + "tender" + "tense" + "tepid" + "terrible" + "terrific" + "testy" + "thankful" + "that" + "these" + "uneven" + "unfinished" + "unfit" + "unfolded" + "unfortunate" + "unhappy" + "unhealthy" + "uniform" + "unimportant" + "unique" + "valuable" + "vapid" + "variable" + "vast" + "velvety" + "weak" + "wealthy" + "weary" + "webbed" + "wee" + "weekly" + "weepy" + "weighty" + "weird" + "welcome" + "welldocumented" + "yellow" + "zealous" + "aggressive" + "agile" + "agitated" + "agonizing" + "agreeable" + "ajar" + "alarmed" + "alarming" + "alert" + "alienated" + "alive" + "all" + "altruistic" + "blackandwhite" + "bland" + "blank" + "blaring" + "bleak" + "blind" + "blissful" + "blond" + "blue" + "blushing" + "cloudy" + "clueless" + "clumsy" + "cluttered" + "coarse" + "cold" + "colorful" + "colorless" + "colossal" + "comfortable" + "common" + "compassionate" + "competent" + "complete" + "delightful" + "delirious" + "demanding" + "dense" + "dental" + "dependable" + "dependent" + "descriptive" + "deserted" + "detailed" + "determined" + "devoted" + "different" + "embellished" + "eminent" + "emotional" + "empty" + "enchanted" + "enchanting" + "energetic" + "enlightened" + "enormous" + "filthy" + "fine" + "finished" + "firm" + "first" + "firsthand" + "fitting" + "fixed" + "flaky" + "flamboyant" + "flashy" + "flat" + "flawed" + "flawless" + "flickering" + "gloomy" + "glorious" + "glossy" + "glum" + "golden" + "good" + "goodnatured" + "gorgeous" + "graceful" + "healthy" + "heartfelt" + "hearty" + "heavenly" + "heavy" + "hefty" + "helpful" + "helpless" + "impartial" + "impeccable" + "imperfect" + "imperturbable" + "impish" + "impolite" + "important" + "impossible" + "impractical" + "impressionable" + "impressive" + "improbable" + "joint" + "jolly" + "jovial" + "kindhearted" + "kindly" + "lighthearted" + "likable" + "likely" + "limited" + "limp" + "limping" + "linear" + "lined" + "liquid" + "medium" + "meek" + "mellow" + "melodic" + "memorable" + "menacing" + "merry" + "messy" + "metallic" + "mild" + "negative" + "neglected" + "negligible" + "neighboring" + "nervous" + "new" + "oldfashioned" + "only" + "open" + "optimal" + "optimistic" + "opulent" + "plaintive" + "plastic" + "playful" + "pleasant" + "pleased" + "pleasing" + "plump" + "plush" + "pointed" + "pointless" + "poised" + "polished" + "polite" + "political" + "queasy" + "querulous" + "reliable" + "relieved" + "remarkable" + "remorseful" + "remote" + "repentant" + "required" + "respectful" + "responsible" + "silly" + "silver" + "similar" + "simple" + "simplistic" + "sinful" + "single" + "sizzling" + "skeletal" + "skinny" + "sleepy" + "slight" + "slim" + "slimy" + "slippery" + "slow" + "slushy" + "small" + "smart" + "smoggy" + "smooth" + "smug" + "snappy" + "snarling" + "sneaky" + "sniveling" + "snoopy" + "thick" + "thin" + "third" + "thirsty" + "this" + "thorny" + "thorough" + "those" + "thoughtful" + "threadbare" + "united" + "unkempt" + "unknown" + "unlawful" + "unlined" + "unlucky" + "unnatural" + "unpleasant" + "unrealistic" + "venerated" + "vengeful" + "verifiable" + "vibrant" + "vicious" + "wellgroomed" + "wellinformed" + "welllit" + "wellmade" + "welloff" + "welltodo" + "wellworn" + "wet" + "which" + "whimsical" + "whirlwind" + "whispered" + "yellowish" + "zesty" + "amazing" + "ambitious" + "ample" + "amused" + "amusing" + "anchored" + "ancient" + "angelic" + "angry" + "anguished" + "animated" + "annual" + "another" + "antique" + "bogus" + "boiling" + "bold" + "bony" + "boring" + "bossy" + "both" + "bouncy" + "bountiful" + "bowed" + "complex" + "complicated" + "composed" + "concerned" + "concrete" + "confused" + "conscious" + "considerate" + "constant" + "content" + "conventional" + "cooked" + "cool" + "cooperative" + "difficult" + "digital" + "diligent" + "dim" + "dimpled" + "dimwitted" + "direct" + "disastrous" + "discrete" + "disfigured" + "disgusting" + "disloyal" + "dismal" + "enraged" + "entire" + "envious" + "equal" + "equatorial" + "essential" + "esteemed" + "ethical" + "euphoric" + "flimsy" + "flippant" + "flowery" + "fluffy" + "fluid" + "flustered" + "focused" + "fond" + "foolhardy" + "foolish" + "forceful" + "forked" + "formal" + "forsaken" + "gracious" + "grand" + "grandiose" + "granular" + "grateful" + "grave" + "gray" + "great" + "greedy" + "green" + "hidden" + "hideous" + "high" + "highlevel" + "hilarious" + "hoarse" + "hollow" + "homely" + "impure" + "inborn" + "incomparable" + "incompatible" + "incomplete" + "inconsequential" + "incredible" + "indelible" + "indolent" + "inexperienced" + "infamous" + "infantile" + "joyful" + "joyous" + "jubilant" + "klutzy" + "knobby" + "little" + "live" + "lively" + "livid" + "loathsome" + "lone" + "lonely" + "long" + "milky" + "mindless" + "miniature" + "minor" + "minty" + "miserable" + "miserly" + "misguided" + "misty" + "mixed" + "next" + "nice" + "nifty" + "nimble" + "nippy" + "orange" + "orderly" + "ordinary" + "organic" + "ornate" + "ornery" + "poor" + "popular" + "portly" + "posh" + "positive" + "possible" + "potable" + "powerful" + "powerless" + "practical" + "precious" + "present" + "prestigious" + "questionable" + "quick" + "repulsive" + "revolving" + "rewarding" + "rich" + "right" + "rigid" + "ringed" + "ripe" + "sociable" + "soft" + "soggy" + "solid" + "somber" + "some" + "sophisticated" + "sore" + "sorrowful" + "soulful" + "soupy" + "sour" + "spanish" + "sparkling" + "sparse" + "specific" + "spectacular" + "speedy" + "spherical" + "spicy" + "spiffy" + "spirited" + "spiteful" + "splendid" + "spotless" + "spotted" + "spry" + "thrifty" + "thunderous" + "tidy" + "tight" + "timely" + "tinted" + "tiny" + "tired" + "torn" + "total" + "unripe" + "unruly" + "unselfish" + "unsightly" + "unsteady" + "unsung" + "untidy" + "untimely" + "untried" + "victorious" + "vigilant" + "vigorous" + "villainous" + "violet" + "white" + "whole" + "whopping" + "wicked" + "wide" + "wideeyed" + "wiggly" + "wild" + "willing" + "wilted" + "winding" + "windy" + "young" + "zigzag" + "anxious" + "any" + "apprehensive" + "appropriate" + "apt" + "arctic" + "arid" + "aromatic" + "artistic" + "ashamed" + "assured" + "astonishing" + "athletic" + "brave" + "breakable" + "brief" + "bright" + "brilliant" + "brisk" + "broken" + "bronze" + "brown" + "bruised" + "coordinated" + "corny" + "corrupt" + "costly" + "courageous" + "courteous" + "crafty" + "crazy" + "creamy" + "creative" + "creepy" + "criminal" + "crisp" + "dirty" + "disguised" + "dishonest" + "dismal" + "distant" + "distant" + "distinct" + "distorted" + "dizzy" + "dopey" + "downright" + "dreary" + "even" + "evergreen" + "everlasting" + "every" + "evil" + "exalted" + "excellent" + "excitable" + "exemplary" + "exhausted" + "forthright" + "fortunate" + "fragrant" + "frail" + "frank" + "frayed" + "free" + "french" + "frequent" + "fresh" + "friendly" + "frightened" + "frightening" + "frigid" + "gregarious" + "grim" + "grimy" + "gripping" + "grizzled" + "gross" + "grotesque" + "grouchy" + "grounded" + "honest" + "honorable" + "honored" + "hopeful" + "horrible" + "hospitable" + "hot" + "huge" + "infatuated" + "inferior" + "infinite" + "informal" + "innocent" + "insecure" + "insidious" + "insignificant" + "insistent" + "instructive" + "insubstantial" + "judicious" + "juicy" + "jumbo" + "knotty" + "knowing" + "knowledgeable" + "longterm" + "loose" + "lopsided" + "lost" + "loud" + "lovable" + "lovely" + "loving" + "modern" + "modest" + "moist" + "monstrous" + "monthly" + "monumental" + "moral" + "mortified" + "motherly" + "motionless" + "nocturnal" + "noisy" + "nonstop" + "normal" + "notable" + "noted" + "original" + "other" + "our" + "outgoing" + "outlandish" + "outlying" + "precious" + "pretty" + "previous" + "pricey" + "prickly" + "primary" + "prime" + "pristine" + "private" + "prize" + "probable" + "productive" + "profitable" + "quickwitted" + "quiet" + "quintessential" + "roasted" + "robust" + "rosy" + "rotating" + "rotten" + "rough" + "round" + "rowdy" + "square" + "squeaky" + "squiggly" + "stable" + "staid" + "stained" + "stale" + "standard" + "starchy" + "stark" + "starry" + "steel" + "steep" + "sticky" + "stiff" + "stimulating" + "stingy" + "stormy" + "straight" + "strange" + "strict" + "strident" + "striking" + "striped" + "strong" + "studious" + "stunning" + "tough" + "tragic" + "trained" + "traumatic" + "treasured" + "tremendous" + "tremendous" + "triangular" + "tricky" + "trifling" + "trim" + "untrue" + "unused" + "unusual" + "unwelcome" + "unwieldy" + "unwilling" + "unwitting" + "unwritten" + "upbeat" + "violent" + "virtual" + "virtuous" + "visible" + "winged" + "wiry" + "wise" + "witty" + "wobbly" + "woeful" + "wonderful" + "wooden" + "woozy" + "wordy" + "worldly" + "worn" + "youthful" + "attached" + "attentive" + "attractive" + "austere" + "authentic" + "authorized" + "automatic" + "avaricious" + "average" + "aware" + "awesome" + "awful" + "awkward" + "bubbly" + "bulky" + "bumpy" + "buoyant" + "burdensome" + "burly" + "bustling" + "busy" + "buttery" + "buzzing" + "critical" + "crooked" + "crowded" + "cruel" + "crushing" + "cuddly" + "cultivated" + "cultured" + "cumbersome" + "curly" + "curvy" + "cute" + "cylindrical" + "doting" + "double" + "downright" + "drab" + "drafty" + "dramatic" + "dreary" + "droopy" + "dry" + "dual" + "dull" + "dutiful" + "excited" + "exciting" + "exotic" + "expensive" + "experienced" + "expert" + "extralarge" + "extraneous" + "extrasmall" + "extroverted" + "frilly" + "frivolous" + "frizzy" + "front" + "frosty" + "frozen" + "frugal" + "fruitful" + "full" + "fumbling" + "functional" + "funny" + "fussy" + "fuzzy" + "growing" + "growling" + "grown" + "grubby" + "gruesome" + "grumpy" + "guilty" + "gullible" + "gummy" + "humble" + "humiliating" + "humming" + "humongous" + "hungry" + "hurtful" + "husky" + "intelligent" + "intent" + "intentional" + "interesting" + "internal" + "international" + "intrepid" + "ironclad" + "irresponsible" + "irritating" + "itchy" + "jumpy" + "junior" + "juvenile" + "known" + "kooky" + "kosher" + "low" + "loyal" + "lucky" + "lumbering" + "luminous" + "lumpy" + "lustrous" + "luxurious" + "mountainous" + "muddy" + "muffled" + "multicolored" + "mundane" + "murky" + "mushy" + "musty" + "muted" + "mysterious" + "noteworthy" + "novel" + "noxious" + "numb" + "nutritious" + "nutty" + "onerlooked" + "outrageous" + "outstanding" + "oval" + "overcooked" + "overdue" + "overjoyed" + "profuse" + "proper" + "proud" + "prudent" + "punctual" + "pungent" + "puny" + "pure" + "purple" + "pushy" + "putrid" + "puzzled" + "puzzling" + "quirky" + "quixotic" + "quizzical" + "royal" + "rubbery" + "ruddy" + "rude" + "rundown" + "runny" + "rural" + "rusty" + "stupendous" + "stupid" + "sturdy" + "stylish" + "subdued" + "submissive" + "substantial" + "subtle" + "suburban" + "sudden" + "sugary" + "sunny" + "super" + "superb" + "superficial" + "superior" + "supportive" + "surefooted" + "surprised" + "suspicious" + "svelte" + "sweaty" + "sweet" + "sweltering" + "swift" + "sympathetic" + "trivial" + "troubled" + "trusting" + "trustworthy" + "trusty" + "truthful" + "tubby" + "turbulent" + "twin" + "upright" + "upset" + "urban" + "usable" + "used" + "useful" + "useless" + "utilized" + "utter" + "vital" + "vivacious" + "vivid" + "voluminous" + "worried" + "worrisome" + "worse" + "worst" + "worthless" + "worthwhile" + "worthy" + "wrathful" + "wretched" + "writhing" + "wrong" + "wry" + "yummy" + "true" + "aliceblue" + "antiquewhite" + "aqua" + "aquamarine" + "azure" + "beige" + "bisque" + "black" + "blanchedalmond" + "blue" + "blueviolet" + "brown" + "burlywood" + "cadetblue" + "chartreuse" + "chocolate" + "coral" + "cornflowerblue" + "cornsilk" + "crimson" + "cyan" + "darkblue" + "darkcyan" + "darkgoldenrod" + "darkgray" + "darkgreen" + "darkgrey" + "darkkhaki" + "darkmagenta" + "darkolivegreen" + "darkorange" + "darkorchid" + "darkred" + "darksalmon" + "darkseagreen" + "darkslateblue" + "darkslategray" + "darkslategrey" + "darkturquoise" + "darkviolet" + "deeppink" + "deepskyblue" + "dimgray" + "dimgrey" + "dodgerblue" + "firebrick" + "floralwhite" + "forestgreen" + "fractal" + "fuchsia" + "gainsboro" + "ghostwhite" + "gold" + "goldenrod" + "gray" + "green" + "greenyellow" + "honeydew" + "hotpink" + "indianred" + "indigo" + "ivory" + "khaki" + "lavender" + "lavenderblush" + "lawngreen" + "lemonchiffon" + "lightblue" + "lightcoral" + "lightcyan" + "lightgoldenrod" + "lightgoldenrodyellow" + "lightgray" + "lightgreen" + "lightgrey" + "lightpink" + "lightsalmon" + "lightseagreen" + "lightskyblue" + "lightslateblue" + "lightslategray" + "lightsteelblue" + "lightyellow" + "lime" + "limegreen" + "linen" + "magenta" + "maroon" + "mediumaquamarine" + "mediumblue" + "mediumforestgreen" + "mediumgoldenrod" + "mediumorchid" + "mediumpurple" + "mediumseagreen" + "mediumslateblue" + "mediumspringgreen" + "mediumturquoise" + "mediumvioletred" + "midnightblue" + "mintcream" + "mistyrose" + "moccasin" + "navajowhite" + "navy" + "navyblue" + "oldlace" + "olive" + "olivedrab" + "opaque" + "orange" + "orangered" + "orchid" + "palegoldenrod" + "palegreen" + "paleturquoise" + "palevioletred" + "papayawhip" + "peachpuff" + "peru" + "pink" + "plum" + "powderblue" + "purple" + "red" + "rosybrown" + "royalblue" + "saddlebrown" + "salmon" + "sandybrown" + "seagreen" + "seashell" + "sienna" + "silver" + "skyblue" + "slateblue" + "slategray" + "slategrey" + "snow" + "springgreen" + "steelblue" + "tan" + "teal" + "thistle" + "tomato" + "transparent" + "turquoise" + "violet" + "violetred" + "wheat" + "white" + "whitesmoke" + "yellow" + "yellowgreen"]) \ No newline at end of file diff --git a/src/status_im/utils/gfycat/animals.cljs b/src/status_im/utils/gfycat/animals.cljs new file mode 100644 index 0000000000..64a0e660b9 --- /dev/null +++ b/src/status_im/utils/gfycat/animals.cljs @@ -0,0 +1,1753 @@ +(ns status-im.utils.gfycat.animals) + +(def data + ["aardvark" + "aardwolf" + "abalone" + "abyssiniancat" + "abyssiniangroundhornbill" + "acaciarat" + "achillestang" + "acornbarnacle" + "acornweevil" + "acornwoodpecker" + "acouchi" + "adamsstaghornedbeetle" + "addax" + "adder" + "adeliepenguin" + "admiralbutterfly" + "adouri" + "aegeancat" + "affenpinscher" + "afghanhound" + "africanaugurbuzzard" + "africanbushviper" + "africancivet" + "africanclawedfrog" + "africanelephant" + "africanfisheagle" + "africangoldencat" + "africangroundhornbill" + "africanharrierhawk" + "africanhornbill" + "africanjacana" + "africanmolesnake" + "africanparadiseflycatcher" + "africanpiedkingfisher" + "africanporcupine" + "africanrockpython" + "africanwildcat" + "africanwilddog" + "agama" + "agouti" + "aidi" + "airedale" + "airedaleterrier" + "akitainu" + "alabamamapturtle" + "alaskajingle" + "alaskanhusky" + "alaskankleekai" + "alaskanmalamute" + "albacoretuna" + "albatross" + "albertosaurus" + "albino" + "aldabratortoise" + "allensbigearedbat" + "alleycat" + "alligator" + "alligatorgar" + "alligatorsnappingturtle" + "allosaurus" + "alpaca" + "alpinegoat" + "alpineroadguidetigerbeetle" + "altiplanochinchillamouse" + "amazondolphin" + "amazonparrot" + "amazontreeboa" + "amberpenshell" + "ambushbug" + "americanalligator" + "americanavocet" + "americanbadger" + "americanbittern" + "americanblackvulture" + "americanbobtail" + "americanbulldog" + "americancicada" + "americancrayfish" + "americancreamdraft" + "americancrocodile" + "americancrow" + "americancurl" + "americangoldfinch" + "americanindianhorse" + "americankestrel" + "americanlobster" + "americanmarten" + "americanpainthorse" + "americanquarterhorse" + "americanratsnake" + "americanredsquirrel" + "americanriverotter" + "americanrobin" + "americansaddlebred" + "americanshorthair" + "americantoad" + "americanwarmblood" + "americanwigeon" + "americanwirehair" + "amethystgemclam" + "amethystinepython" + "amethystsunbird" + "ammonite" + "amoeba" + "amphibian" + "amphiuma" + "amurminnow" + "amurratsnake" + "amurstarfish" + "anaconda" + "anchovy" + "andalusianhorse" + "andeancat" + "andeancockoftherock" + "andeancondor" + "anemone" + "anemonecrab" + "anemoneshrimp" + "angelfish" + "angelwingmussel" + "anglerfish" + "angora" + "angwantibo" + "anhinga" + "ankole" + "ankolewatusi" + "annashummingbird" + "annelid" + "annelida" + "anole" + "anophelesmosquito" + "ant" + "antarcticfurseal" + "antarcticgiantpetrel" + "antbear" + "anteater" + "antelope" + "antelopegroundsquirrel" + "antipodesgreenparakeet" + "antlion" + "anura" + "aoudad" + "apatosaur" + "ape" + "aphid" + "apisdorsatalaboriosa" + "aplomadofalcon" + "appaloosa" + "aquaticleech" + "arabianhorse" + "arabianoryx" + "arabianwildcat" + "aracari" + "arachnid" + "arawana" + "archaeocete" + "archaeopteryx" + "archerfish" + "arcticduck" + "arcticfox" + "arctichare" + "arcticseal" + "arcticwolf" + "argali" + "argentinehornedfrog" + "argentineruddyduck" + "argusfish" + "arieltoucan" + "arizonaalligatorlizard" + "arkshell" + "armadillo" + "armedcrab" + "armednylonshrimp" + "armyant" + "armyworm" + "arrowana" + "arrowcrab" + "arrowworm" + "arthropods" + "aruanas" + "asianconstablebutterfly" + "asiandamselfly" + "asianelephant" + "asianlion" + "asianpiedstarling" + "asianporcupine" + "asiansmallclawedotter" + "asiantrumpetfish" + "asianwaterbuffalo" + "asiaticgreaterfreshwaterclam" + "asiaticlesserfreshwaterclam" + "asiaticmouflon" + "asiaticwildass" + "asp" + "ass" + "assassinbug" + "astarte" + "astrangiacoral" + "atlanticblackgoby" + "atlanticbluetang" + "atlanticridleyturtle" + "atlanticsharpnosepuffer" + "atlanticspadefish" + "atlasmoth" + "attwatersprairiechicken" + "auk" + "auklet" + "aurochs" + "australiancattledog" + "australiancurlew" + "australianfreshwatercrocodile" + "australianfurseal" + "australiankelpie" + "australiankestrel" + "australianshelduck" + "australiansilkyterrier" + "austrianpinscher" + "avians" + "avocet" + "axisdeer" + "axolotl" + "ayeaye" + "aztecant" + "azurevase" + "azurevasesponge" + "azurewingedmagpie" + "babirusa" + "baboon" + "backswimmer" + "bactrian" + "badger" + "bagworm" + "baiji" + "baldeagle" + "baleenwhale" + "balloonfish" + "ballpython" + "bandicoot" + "bangeltiger" + "bantamrooster" + "banteng" + "barasinga" + "barasingha" + "barb" + "barbet" + "barebirdbat" + "barnacle" + "barnowl" + "barnswallow" + "barracuda" + "basenji" + "basil" + "basilisk" + "bass" + "bassethound" + "bat" + "bats" + "beagle" + "bear" + "beardedcollie" + "beardeddragon" + "beauceron" + "beaver" + "bedbug" + "bedlingtonterrier" + "bee" + "beetle" + "bellfrog" + "bellsnake" + "belugawhale" + "bengaltiger" + "bergerpicard" + "bernesemountaindog" + "betafish" + "bettong" + "bichonfrise" + "bighorn" + "bighornedsheep" + "bighornsheep" + "bigmouthbass" + "bilby" + "billygoat" + "binturong" + "bird" + "birdofparadise" + "bison" + "bittern" + "blackandtancoonhound" + "blackbear" + "blackbird" + "blackbuck" + "blackcrappie" + "blackfish" + "blackfly" + "blackfootedferret" + "blacklab" + "blacklemur" + "blackmamba" + "blacknorwegianelkhound" + "blackpanther" + "blackrhino" + "blackrussianterrier" + "blackwidowspider" + "blesbok" + "blobfish" + "blowfish" + "blueandgoldmackaw" + "bluebird" + "bluebottle" + "bluebottlejellyfish" + "bluebreastedkookaburra" + "bluefintuna" + "bluefish" + "bluegill" + "bluejay" + "bluemorphobutterfly" + "blueshark" + "bluet" + "bluetickcoonhound" + "bluetonguelizard" + "bluewhale" + "boa" + "boaconstrictor" + "boar" + "bobcat" + "bobolink" + "bobwhite" + "boilweevil" + "bongo" + "bonobo" + "booby" + "bordercollie" + "borderterrier" + "borer" + "borzoi" + "boto" + "boubou" + "boutu" + "bovine" + "brahmanbull" + "brahmancow" + "brant" + "bream" + "brocketdeer" + "bronco" + "brontosaurus" + "brownbear" + "brownbutterfly" + "bubblefish" + "buck" + "buckeyebutterfly" + "budgie" + "bufeo" + "buffalo" + "bufflehead" + "bug" + "bull" + "bullfrog" + "bullmastiff" + "bumblebee" + "bunny" + "bunting" + "burro" + "bushbaby" + "bushsqueaker" + "bustard" + "butterfly" + "buzzard" + "caecilian" + "caiman" + "caimanlizard" + "calf" + "camel" + "canadagoose" + "canary" + "canine" + "canvasback" + "capeghostfrog" + "capybara" + "caracal" + "cardinal" + "caribou" + "carp" + "carpenterant" + "cassowary" + "cat" + "catbird" + "caterpillar" + "catfish" + "cats" + "cattle" + "caudata" + "cavy" + "centipede" + "cero" + "chafer" + "chameleon" + "chamois" + "chanticleer" + "cheetah" + "chevrotain" + "chick" + "chickadee" + "chicken" + "chihuahua" + "chimneyswift" + "chimpanzee" + "chinchilla" + "chinesecrocodilelizard" + "chipmunk" + "chital" + "chrysalis" + "chrysomelid" + "chuckwalla" + "chupacabra" + "cicada" + "cirriped" + "civet" + "clam" + "cleanerwrasse" + "clingfish" + "clownanemonefish" + "clumber" + "coati" + "cob" + "cobra" + "cock" + "cockatiel" + "cockatoo" + "cockerspaniel" + "cockroach" + "cod" + "coelacanth" + "collardlizard" + "collie" + "colt" + "comet" + "commabutterfly" + "commongonolek" + "conch" + "condor" + "coney" + "conure" + "cony" + "coot" + "cooter" + "copepod" + "copperbutterfly" + "copperhead" + "coqui" + "coral" + "cormorant" + "cornsnake" + "corydorascatfish" + "cottonmouth" + "cottontail" + "cougar" + "cow" + "cowbird" + "cowrie" + "coyote" + "coypu" + "crab" + "crane" + "cranefly" + "crayfish" + "creature" + "cricket" + "crocodile" + "crocodileskink" + "crossbill" + "crow" + "crownofthornsstarfish" + "crustacean" + "cub" + "cuckoo" + "cur" + "curassow" + "curlew" + "cuscus" + "cusimanse" + "cuttlefish" + "cutworm" + "cygnet" + "dachshund" + "daddylonglegs" + "dairycow" + "dalmatian" + "damselfly" + "danishswedishfarmdog" + "darklingbeetle" + "dartfrog" + "darwinsfox" + "dassie" + "dassierat" + "davidstiger" + "deer" + "deermouse" + "degu" + "degus" + "deinonychus" + "desertpupfish" + "devilfish" + "deviltasmanian" + "diamondbackrattlesnake" + "dikdik" + "dikkops" + "dingo" + "dinosaur" + "diplodocus" + "dipper" + "discus" + "dobermanpinscher" + "doctorfish" + "dodo" + "dodobird" + "doe" + "dog" + "dogfish" + "dogwoodclubgall" + "dogwoodtwigborer" + "dolphin" + "donkey" + "dorado" + "dore" + "dorking" + "dormouse" + "dotterel" + "douglasfirbarkbeetle" + "dove" + "dowitcher" + "drafthorse" + "dragon" + "dragonfly" + "drake" + "drever" + "dromaeosaur" + "dromedary" + "drongo" + "duck" + "duckbillcat" + "duckbillplatypus" + "duckling" + "dugong" + "duiker" + "dungbeetle" + "dungenesscrab" + "dunlin" + "dunnart" + "dutchshepherddog" + "dutchsmoushond" + "dwarfmongoose" + "dwarfrabbit" + "eagle" + "earthworm" + "earwig" + "easternglasslizard" + "easternnewt" + "easteuropeanshepherd" + "eastrussiancoursinghounds" + "eastsiberianlaika" + "echidna" + "eel" + "eelelephant" + "eeve" + "eft" + "egg" + "egret" + "eider" + "eidolonhelvum" + "ekaltadeta" + "eland" + "electriceel" + "elephant" + "elephantbeetle" + "elephantseal" + "elk" + "elkhound" + "elver" + "emeraldtreeskink" + "emperorpenguin" + "emperorshrimp" + "emu" + "englishpointer" + "englishsetter" + "equestrian" + "equine" + "erin" + "ermine" + "erne" + "eskimodog" + "esok" + "estuarinecrocodile" + "ethiopianwolf" + "europeanfiresalamander" + "europeanpolecat" + "ewe" + "eyas" + "eyelashpitviper" + "eyra" + "fairybluebird" + "fairyfly" + "falcon" + "fallowdeer" + "fantail" + "fanworms" + "fattaileddunnart" + "fawn" + "feline" + "fennecfox" + "ferret" + "fiddlercrab" + "fieldmouse" + "fieldspaniel" + "finch" + "finnishspitz" + "finwhale" + "fireant" + "firebelliedtoad" + "firecrest" + "firefly" + "fish" + "fishingcat" + "flamingo" + "flatcoatretriever" + "flatfish" + "flea" + "flee" + "flicker" + "flickertailsquirrel" + "flies" + "flounder" + "fluke" + "fly" + "flycatcher" + "flyingfish" + "flyingfox" + "flyinglemur" + "flyingsquirrel" + "foal" + "fossa" + "fowl" + "fox" + "foxhound" + "foxterrier" + "frenchbulldog" + "freshwatereel" + "frigatebird" + "frilledlizard" + "frillneckedlizard" + "fritillarybutterfly" + "frog" + "frogmouth" + "fruitbat" + "fruitfly" + "fugu" + "fulmar" + "funnelweaverspider" + "furseal" + "gadwall" + "galago" + "galah" + "galapagosalbatross" + "galapagosdove" + "galapagoshawk" + "galapagosmockingbird" + "galapagospenguin" + "galapagossealion" + "galapagostortoise" + "gallinule" + "gallowaycow" + "gander" + "gangesdolphin" + "gannet" + "gar" + "gardensnake" + "garpike" + "gartersnake" + "gaur" + "gavial" + "gazelle" + "gecko" + "geese" + "gelada" + "gelding" + "gemsbok" + "gemsbuck" + "genet" + "gentoopenguin" + "gerbil" + "gerenuk" + "germanpinscher" + "germanshepherd" + "germanshorthairedpointer" + "germanspaniel" + "germanspitz" + "germanwirehairedpointer" + "gharial" + "ghostshrimp" + "giantschnauzer" + "gibbon" + "gilamonster" + "giraffe" + "glassfrog" + "globefish" + "glowworm" + "gnat" + "gnatcatcher" + "gnu" + "goa" + "goat" + "godwit" + "goitered" + "goldeneye" + "goldenmantledgroundsquirrel" + "goldenretriever" + "goldfinch" + "goldfish" + "gonolek" + "goose" + "goosefish" + "gopher" + "goral" + "gordonsetter" + "gorilla" + "goshawk" + "gosling" + "gossamerwingedbutterfly" + "gourami" + "grackle" + "grasshopper" + "grassspider" + "grayfox" + "grayling" + "grayreefshark" + "graysquirrel" + "graywolf" + "greatargus" + "greatdane" + "greathornedowl" + "greatwhiteshark" + "grebe" + "greendarnerdragonfly" + "greyhounddog" + "grison" + "grizzlybear" + "grosbeak" + "groundbeetle" + "groundhog" + "grouper" + "grouse" + "grub" + "grunion" + "guanaco" + "guernseycow" + "guillemot" + "guineafowl" + "guineapig" + "gull" + "guppy" + "gypsymoth" + "gyrfalcon" + "hackee" + "haddock" + "hadrosaurus" + "hagfish" + "hairstreak" + "hairstreakbutterfly" + "hake" + "halcyon" + "halibut" + "halicore" + "hamadryad" + "hamadryas" + "hammerheadbird" + "hammerheadshark" + "hammerkop" + "hamster" + "hanumanmonkey" + "hapuka" + "hapuku" + "harborporpoise" + "harborseal" + "hare" + "harlequinbug" + "harpseal" + "harpyeagle" + "harrier" + "harrierhawk" + "hart" + "hartebeest" + "harvestmen" + "harvestmouse" + "hatchetfish" + "hawaiianmonkseal" + "hawk" + "hectorsdolphin" + "hedgehog" + "heifer" + "hellbender" + "hen" + "herald" + "herculesbeetle" + "hermitcrab" + "heron" + "herring" + "heterodontosaurus" + "hind" + "hippopotamus" + "hoatzin" + "hochstettersfrog" + "hog" + "hogget" + "hoiho" + "hoki" + "homalocephale" + "honeybadger" + "honeybee" + "honeycreeper" + "honeyeater" + "hookersealion" + "hoopoe" + "hornbill" + "hornedtoad" + "hornedviper" + "hornet" + "hornshark" + "horse" + "horsechestnutleafminer" + "horsefly" + "horsemouse" + "horseshoebat" + "horseshoecrab" + "hound" + "housefly" + "hoverfly" + "howlermonkey" + "huemul" + "huia" + "human" + "hummingbird" + "humpbackwhale" + "husky" + "hydatidtapeworm" + "hydra" + "hyena" + "hylaeosaurus" + "hypacrosaurus" + "hypsilophodon" + "hyracotherium" + "hyrax" + "iaerismetalmark" + "ibadanmalimbe" + "iberianbarbel" + "iberianchiffchaff" + "iberianemeraldlizard" + "iberianlynx" + "iberianmidwifetoad" + "iberianmole" + "iberiannase" + "ibex" + "ibis" + "ibisbill" + "ibizanhound" + "iceblueredtopzebra" + "icefish" + "icelandgull" + "icelandichorse" + "icelandicsheepdog" + "ichidna" + "ichneumonfly" + "ichthyosaurs" + "ichthyostega" + "icterinewarbler" + "iggypops" + "iguana" + "iguanodon" + "illadopsis" + "ilsamochadegu" + "imago" + "impala" + "imperatorangel" + "imperialeagle" + "incatern" + "inchworm" + "indianabat" + "indiancow" + "indianelephant" + "indianglassfish" + "indianhare" + "indianjackal" + "indianpalmsquirrel" + "indianpangolin" + "indianrhinoceros" + "indianringneckparakeet" + "indianrockpython" + "indianskimmer" + "indianspinyloach" + "indigobunting" + "indigowingedparrot" + "indochinahogdeer" + "indochinesetiger" + "indri" + "indusriverdolphin" + "inexpectatumpleco" + "inganue" + "insect" + "intermediateegret" + "invisiblerail" + "iraniangroundjay" + "iridescentshark" + "iriomotecat" + "irishdraughthorse" + "irishredandwhitesetter" + "irishsetter" + "irishterrier" + "irishwaterspaniel" + "irishwolfhound" + "irrawaddydolphin" + "irukandjijellyfish" + "isabellineshrike" + "isabellinewheatear" + "islandcanary" + "islandwhistler" + "isopod" + "italianbrownbear" + "italiangreyhound" + "ivorybackedwoodswallow" + "ivorybilledwoodpecker" + "ivorygull" + "izuthrush" + "jabiru" + "jackal" + "jackrabbit" + "jaeger" + "jaguar" + "jaguarundi" + "janenschia" + "japanesebeetle" + "javalina" + "jay" + "jellyfish" + "jenny" + "jerboa" + "joey" + "johndory" + "juliabutterfly" + "jumpingbean" + "junco" + "junebug" + "kagu" + "kakapo" + "kakarikis" + "kangaroo" + "karakul" + "katydid" + "kawala" + "kentrosaurus" + "kestrel" + "kid" + "killdeer" + "killerwhale" + "killifish" + "kingbird" + "kingfisher" + "kinglet" + "kingsnake" + "kinkajou" + "kiskadee" + "kissingbug" + "kite" + "kitfox" + "kitten" + "kittiwake" + "kitty" + "kiwi" + "koala" + "koalabear" + "kob" + "kodiakbear" + "koi" + "komododragon" + "koodoo" + "kookaburra" + "kouprey" + "krill" + "kronosaurus" + "kudu" + "kusimanse" + "labradorretriever" + "lacewing" + "ladybird" + "ladybug" + "lamb" + "lamprey" + "langur" + "lark" + "larva" + "laughingthrush" + "lcont" + "leafbird" + "leafcutterant" + "leafhopper" + "leafwing" + "leech" + "lemming" + "lemur" + "leonberger" + "leopard" + "leopardseal" + "leveret" + "lhasaapso" + "lice" + "liger" + "lightningbug" + "limpet" + "limpkin" + "ling" + "lion" + "lionfish" + "littlenightmonkeys" + "lizard" + "llama" + "lobo" + "lobster" + "locust" + "loggerheadturtle" + "longhorn" + "longhornbeetle" + "longspur" + "loon" + "lorikeet" + "loris" + "louse" + "lovebird" + "lowchen" + "lunamoth" + "lungfish" + "lynx" + "lynxÂ" + "macaque" + "macaw" + "macropod" + "madagascarhissingroach" + "maggot" + "magpie" + "maiasaura" + "majungatholus" + "malamute" + "mallard" + "maltesedog" + "mamba" + "mamenchisaurus" + "mammal" + "mammoth" + "manatee" + "mandrill" + "mangabey" + "manta" + "mantaray" + "mantid" + "mantis" + "mantisray" + "manxcat" + "mara" + "marabou" + "marbledmurrelet" + "mare" + "marlin" + "marmoset" + "marmot" + "marten" + "martin" + "massasauga" + "massospondylus" + "mastiff" + "mastodon" + "mayfly" + "meadowhawk" + "meadowlark" + "mealworm" + "meerkat" + "megalosaurus" + "megalotomusquinquespinosus" + "megaraptor" + "merganser" + "merlin" + "metalmarkbutterfly" + "metamorphosis" + "mice" + "microvenator" + "midge" + "milksnake" + "milkweedbug" + "millipede" + "minibeast" + "mink" + "minnow" + "mite" + "moa" + "mockingbird" + "mole" + "mollies" + "mollusk" + "molly" + "monarch" + "mongoose" + "mongrel" + "monkey" + "monkfishÂ" + "monoclonius" + "montanoceratops" + "moorhen" + "moose" + "moray" + "morayeel" + "morpho" + "mosasaur" + "mosquito" + "moth" + "motmot" + "mouflon" + "mountaincat" + "mountainlion" + "mouse" + "mouse/mice" + "mousebird" + "mudpuppy" + "mule" + "mullet" + "muntjac" + "murrelet" + "muskox" + "muskrat" + "mussaurus" + "mussel" + "mustang" + "mutt" + "myna" + "mynah" + "myotisÂ" + "nabarlek" + "nag" + "naga" + "nagapies" + "nakedmolerat" + "nandine" + "nandoo" + "nandu" + "narwhal" + "narwhale" + "natterjacktoad" + "nauplius" + "nautilus" + "needlefish" + "needletail" + "nematode" + "nene" + "neonblueguppy" + "neonbluehermitcrab" + "neondwarfgourami" + "neonrainbowfish" + "neonredguppy" + "neontetra" + "nerka" + "nettlefish" + "newfoundlanddog" + "newt" + "newtnutria" + "nightcrawler" + "nighthawk" + "nightheron" + "nightingale" + "nightjar" + "nijssenissdwarfchihlid" + "nilgai" + "ninebandedarmadillo" + "noctilio" + "noctule" + "noddy" + "noolbenger" + "northerncardinals" + "northernelephantseal" + "northernflyingsquirrel" + "northernfurseal" + "northernhairynosedwombat" + "northernpike" + "northernseahorse" + "northernspottedowl" + "norwaylobster" + "norwayrat" + "nubiangoat" + "nudibranch" + "numbat" + "nurseshark" + "nutcracker" + "nuthatch" + "nutria" + "nyala" + "nymph" + "ocelot" + "octopus" + "okapi" + "olingo" + "olm" + "opossum" + "orangutan" + "orca" + "oregonsilverspotbutterfly" + "oriole" + "oropendola" + "oropendula" + "oryx" + "osprey" + "ostracod" + "ostrich" + "otter" + "ovenbird" + "owl" + "owlbutterfly" + "ox" + "oxen" + "oxpecker" + "oyster" + "ozarkbigearedbat" + "pacaÂ" + "pachyderm" + "pacificparrotlet" + "paddlefish" + "paintedladybutterfly" + "panda" + "pangolin" + "panther" + "paperwasp" + "papillon" + "parakeet" + "parrot" + "partridge" + "peacock" + "peafowl" + "peccary" + "pekingese" + "pelican" + "pelicinuspetrel" + "penguin" + "perch" + "peregrinefalcon" + "pewee" + "phalarope" + "pharaohhound" + "pheasant" + "phoebe" + "phoenix" + "pig" + "pigeon" + "piglet" + "pika" + "pike" + "pikeperchÂ" + "pilchard" + "pinemarten" + "pinkriverdolphin" + "pinniped" + "pintail" + "pipistrelle" + "pipit" + "piranha" + "pitbull" + "pittabird" + "plainsqueaker" + "plankton" + "planthopper" + "platypus" + "plover" + "polarbear" + "polecat" + "polliwog" + "polyp" + "polyturator" + "pomeranian" + "pondskater" + "pony" + "pooch" + "poodle" + "porcupine" + "porpoise" + "portuguesemanofwar" + "possum" + "prairiedog" + "prawn" + "prayingmantid" + "prayingmantis" + "primate" + "pronghorn" + "pseudodynerusquadrisectus" + "ptarmigan" + "pterodactyls" + "pterosaurs" + "puffer" + "pufferfish" + "puffin" + "pug" + "pullet" + "puma" + "pupa" + "pupfish" + "puppy" + "purplemarten" + "pussycat" + "pygmy" + "python" + "quadrisectus" + "quagga" + "quahog" + "quail" + "queenalexandrasbirdwing" + "queenalexandrasbirdwingbutterfly" + "queenant" + "queenbee" + "queenconch" + "queenslandgrouper" + "queenslandheeler" + "queensnake" + "quelea" + "quetzal" + "quetzalcoatlus" + "quillback" + "quinquespinosus" + "quokka" + "quoll" + "rabbit" + "rabidsquirrel" + "raccoon" + "racer" + "racerunner" + "ragfish" + "rail" + "rainbowfish" + "rainbowlorikeet" + "rainbowtrout" + "ram" + "raptors" + "rasbora" + "rat" + "ratfish" + "rattail" + "rattlesnake" + "raven" + "ray" + "redhead" + "redheadedwoodpecker" + "redpoll" + "redstart" + "redtailedhawk" + "reindeer" + "reptile" + "reynard" + "rhea" + "rhesusmonkey" + "rhino" + "rhinoceros" + "rhinocerosbeetle" + "rhodesianridgeback" + "ringtailedlemur" + "ringworm" + "riograndeescuerzo" + "roach" + "roadrunner" + "roan" + "robberfly" + "robin" + "rockrat" + "rodent" + "roebuck" + "roller" + "rook" + "rooster" + "rottweiler" + "sable" + "sableantelope" + "sablefishÂ" + "saiga" + "sakimonkey" + "salamander" + "salmon" + "saltwatercrocodile" + "sambar" + "samoyeddog" + "sandbarshark" + "sanddollar" + "sanderling" + "sandpiper" + "sapsucker" + "sardine" + "sawfish" + "scallop" + "scarab" + "scarletibis" + "scaup" + "schapendoes" + "schipperke" + "schnauzer" + "scorpion" + "scoter" + "screamer" + "seabird" + "seagull" + "seahog" + "seahorse" + "seal" + "sealion" + "seamonkey" + "seaslug" + "seaurchin" + "senegalpython" + "seriema" + "serpent" + "serval" + "shark" + "shearwater" + "sheep" + "sheldrake" + "shelduck" + "shibainu" + "shihtzu" + "shorebird" + "shoveler" + "shrew" + "shrike" + "shrimp" + "siamang" + "siamesecat" + "siberiantiger" + "sidewinder" + "sifaka" + "silkworm" + "silverfish" + "silverfox" + "silversidefish" + "siskin" + "skimmer" + "skink" + "skipper" + "skua" + "skunk" + "skylark" + "sloth" + "slothbear" + "slug" + "smelts" + "smew" + "snail" + "snake" + "snipe" + "snoutbutterfly" + "snowdog" + "snowgeese" + "snowleopard" + "snowmonkey" + "snowyowl" + "sockeyesalmon" + "solenodon" + "solitaire" + "songbird" + "sora" + "southernhairnosedwombat" + "sow" + "spadefoot" + "sparrow" + "sphinx" + "spider" + "spidermonkey" + "spiketail" + "spittlebug" + "sponge" + "spoonbill" + "spotteddolphin" + "spreadwing" + "springbok" + "springpeeper" + "springtail" + "squab" + "squamata" + "squeaker" + "squid" + "squirrel" + "stag" + "stagbeetle" + "stallion" + "starfish" + "starling" + "steed" + "steer" + "stegosaurus" + "stickinsect" + "stickleback" + "stilt" + "stingray" + "stinkbug" + "stinkpot" + "stoat" + "stonefly" + "stork" + "stud" + "sturgeon" + "sugarglider" + "sulphurbutterfly" + "sunbear" + "sunbittern" + "sunfish" + "swallow" + "swallowtail" + "swallowtailbutterfly" + "swan" + "swellfish" + "swift" + "swordfish" + "tadpole" + "tahr" + "takin" + "tamarin" + "tanager" + "tapaculo" + "tapeworm" + "tapir" + "tarantula" + "tarpan" + "tarsier" + "taruca" + "tasmaniandevil" + "tasmaniantiger" + "tattler" + "tayra" + "teal" + "tegus" + "teledu" + "tench" + "tenrec" + "termite" + "tern" + "terrapin" + "terrier" + "thoroughbred" + "thrasher" + "thrip" + "thrush" + "thunderbird" + "thylacine" + "tick" + "tiger" + "tigerbeetle" + "tigermoth" + "tigershark" + "tilefish" + "tinamou" + "titi" + "titmouse" + "toad" + "toadfish" + "tomtitÂ" + "topi" + "tortoise" + "toucan" + "towhee" + "tragopan" + "treecreeper" + "trex" + "triceratops" + "trogon" + "trout" + "trumpeterbird" + "trumpeterswan" + "tsetsefly" + "tuatara" + "tuna" + "turaco" + "turkey" + "turnstone" + "turtle" + "turtledove" + "uakari" + "ugandakob" + "uintagroundsquirrel" + "ulyssesbutterfly" + "umbrellabird" + "umbrette" + "unau" + "ungulate" + "unicorn" + "upupa" + "urchin" + "urial" + "uromastyxmaliensis" + "uromastyxspinipes" + "urson" + "urubu" + "urus" + "urutu" + "urva" + "utahprairiedog" + "vampirebat" + "vaquita" + "veery" + "velociraptor" + "velvetcrab" + "velvetworm" + "venomoussnake" + "verdin" + "vervet" + "viceroybutterfly" + "vicuna" + "viper" + "viperfish" + "vipersquid" + "vireo" + "virginiaopossum" + "vixen" + "vole" + "volvox" + "vulpesvelox" + "vulpesvulpes" + "vulture" + "walkingstick" + "wallaby" + "wallaroo" + "walleye" + "walrus" + "warbler" + "warthog" + "wasp" + "waterboatman" + "waterbuck" + "waterbuffalo" + "waterbug" + "waterdogs" + "waterdragons" + "watermoccasin" + "waterstrider" + "waterthrush" + "wattlebird" + "watussi" + "waxwing" + "weasel" + "weaverbird" + "weevil" + "westafricanantelope" + "whale" + "whapuku" + "whelp" + "whimbrel" + "whippet" + "whippoorwill" + "whitebeakeddolphin" + "whiteeye" + "whitepelican" + "whiterhino" + "whitetaileddeer" + "whitetippedreefshark" + "whooper" + "whoopingcrane" + "widgeon" + "widowspider" + "wildcat" + "wildebeast" + "wildebeest" + "willet" + "wireworm" + "wisent" + "wobbegongshark" + "wolf" + "wolfspider" + "wolverine" + "wombat" + "woodborer" + "woodchuck" + "woodcock" + "woodnymphbutterfly" + "woodpecker" + "woodstorks" + "woollybearcaterpillar" + "worm" + "wrasse" + "wreckfish" + "wren" + "wrenchbird" + "wryneck" + "wuerhosaurus" + "wyvern" + "xanclomys" + "xanthareel" + "xantus" + "xantusmurrelet" + "xeme" + "xenarthra" + "xenoposeidon" + "xenops" + "xenopterygii" + "xenopus" + "xenotarsosaurus" + "xenurine" + "xenurusunicinctus" + "xerus" + "xiaosaurus" + "xinjiangovenator" + "xiphias" + "xiphiasgladius" + "xiphosuran" + "xoloitzcuintli" + "xoni" + "xrayfish" + "xraytetra" + "xuanhanosaurus" + "xuanhuaceratops" + "xuanhuasaurus" + "yaffle" + "yak" + "yapok" + "yardant" + "yearling" + "yellowbelliedmarmot" + "yellowbellylizard" + "yellowhammer" + "yellowjacket" + "yellowlegs" + "yellowthroat" + "yellowwhitebutterfly" + "yeti" + "ynambu" + "yorkshireterrier" + "yosemitetoad" + "yucker" + "zander" + "zanzibardaygecko" + "zebra" + "zebradove" + "zebrafinch" + "zebrafish" + "zebralongwingbutterfly" + "zebraswallowtailbutterfly" + "zebratailedlizard" + "zebu" + "zenaida" + "zeren" + "zethusspinipes" + "zethuswasp" + "zigzagsalamander" + "zonetailedpigeon" + "zooplankton" + "zopilote" + "zorilla"]) \ No newline at end of file diff --git a/src/status_im/utils/gfycat/core.cljs b/src/status_im/utils/gfycat/core.cljs new file mode 100644 index 0000000000..ce261d0600 --- /dev/null +++ b/src/status_im/utils/gfycat/core.cljs @@ -0,0 +1,16 @@ +(ns status-im.utils.gfycat.core + (:require [status-im.utils.gfycat.animals :as animals] + [status-im.utils.gfycat.adjectives :as adjectives] + [clojure.string :as str])) + +(defn- pick-random + [vector] + (-> (rand-nth vector) + str/capitalize)) + +(defn generate-gfy + [] + (let [first-adjective (pick-random adjectives/data) + second-adjective (pick-random adjectives/data) + animal (pick-random animals/data)] + (str first-adjective " " second-adjective " " animal))) \ No newline at end of file diff --git a/src/status_im/utils/handlers.cljs b/src/status_im/utils/handlers.cljs new file mode 100644 index 0000000000..db49a6609c --- /dev/null +++ b/src/status_im/utils/handlers.cljs @@ -0,0 +1,34 @@ +(ns status-im.utils.handlers + (:require [re-frame.core :refer [after dispatch debug] :as re-core] + [re-frame.utils :refer [log]] + [clojure.string :as str])) + +(defn side-effect! + "Middleware for handlers that will not affect db." + [handler] + (fn [db params] + (handler db params) + db)) + +(defn debug-handlers-names + "Middleware which logs debug information to js/console for each event. + Includes a clojure.data/diff of the db, before vs after, showing the changes + caused by the event." + [handler] + (fn debug-handler + [db v] + (log "Handling re-frame event: " (first v)) + (let [new-db (handler db v)] + new-db))) + +(defn register-handler + ([name handler] (register-handler name nil handler)) + ([name middleware handler] + (re-core/register-handler name [debug-handlers-names middleware] handler))) + +(defn get-hashtags [status] + (if status + (let [hashtags (map #(str/lower-case (subs % 1)) + (re-seq #"#[^ !?,;:.]+" status))] + (set (or hashtags []))) + #{})) diff --git a/src/status_im/utils/hex.cljs b/src/status_im/utils/hex.cljs new file mode 100644 index 0000000000..9df30967fa --- /dev/null +++ b/src/status_im/utils/hex.cljs @@ -0,0 +1,7 @@ +(ns status-im.utils.hex + (:require [clojure.string :as s])) + +(defn normalize-hex [hex] + (if (and hex (s/starts-with? hex "0x")) + (subs hex 2) + hex)) diff --git a/src/status_im/utils/homoglyph.cljs b/src/status_im/utils/homoglyph.cljs new file mode 100644 index 0000000000..6b96d1eee0 --- /dev/null +++ b/src/status_im/utils/homoglyph.cljs @@ -0,0 +1,7 @@ +(ns status-im.utils.homoglyph + (:require [status-im.utils.utils :as u])) + +(def homoglyph-finder (js/require "homoglyph-finder")) + +(defn matches [s1 s2] + (.isMatches homoglyph-finder s1 s2)) diff --git a/src/status_im/utils/identicon.cljs b/src/status_im/utils/identicon.cljs new file mode 100644 index 0000000000..0cbf00db2e --- /dev/null +++ b/src/status_im/utils/identicon.cljs @@ -0,0 +1,13 @@ +(ns status-im.utils.identicon + (:require [clojure.string :as s] + [status-im.utils.utils :as u])) + +(def default-size 40) + +(def identicon-js (js/require "identicon.js")) + +(defn identicon + ([hash] (identicon hash default-size)) + ([hash options] + (str "data:image/png;base64," (.toString (new identicon-js hash options))))) + diff --git a/src/status_im/utils/image_processing.cljs b/src/status_im/utils/image_processing.cljs new file mode 100644 index 0000000000..6f6e664ebf --- /dev/null +++ b/src/status_im/utils/image_processing.cljs @@ -0,0 +1,30 @@ +(ns status-im.utils.image-processing + (:require [reagent.core :as r] + [status-im.utils.fs :refer [read-file]] + [taoensso.timbre :as log] + [clojure.string :as str])) + +(def resizer-class (js/require "react-native-image-resizer")) + +(defn- resize [path max-width max-height on-resize on-error] + (let [resize-fn (aget resizer-class "default" "createResizedImage")] + (-> (resize-fn path max-width max-height "JPEG" 75 0 nil) + (.then on-resize) + (.catch on-error)))) + +(defn- image-base64-encode [path on-success on-error] + (let [on-encoded (fn [data] + (on-success data)) + on-error (fn [error] + (on-error :base64 error))] + (read-file path "base64" on-encoded on-error))) + +(defn img->base64 [path on-success on-error] + (let [on-resized (fn [path] + (let [path (str/replace path "file:" "")] + (log/debug "Resized: " path) + (image-base64-encode path on-success on-error))) + on-error (fn [error] + (log/debug "Resized error: " error) + (on-error :resize error))] + (resize path 150 150 on-resized on-error))) \ No newline at end of file diff --git a/src/status_im/utils/js_resources.cljs b/src/status_im/utils/js_resources.cljs new file mode 100644 index 0000000000..9a9b1621e0 --- /dev/null +++ b/src/status_im/utils/js_resources.cljs @@ -0,0 +1,18 @@ +(ns ^:figwheel-always status-im.utils.js-resources + (:require-macros [status-im.utils.slurp :refer [slurp]]) + (:require [status-im.utils.types :refer [json->clj]])) + +(def default-contacts (-> (slurp "resources/default_contacts.json") + (json->clj))) + +(def commands-js (slurp "resources/commands.js")) +(def console-js (slurp "resources/console.js")) +(def status-js (slurp "resources/status.js")) +(def wallet-js (str commands-js (slurp "resources/wallet.js"))) +(def dapp-js (str (slurp "resources/dapp.js"))) + +(def webview-js (slurp "resources/webview.js")) +(def web3 (str "if (typeof Web3 == 'undefined') {" + (slurp "resources/web3.0_16_0.min.js") + "}")) +(def web3-init (slurp "resources/web3_init.js")) diff --git a/src/status_im/utils/listview.cljs b/src/status_im/utils/listview.cljs new file mode 100644 index 0000000000..d9ab791710 --- /dev/null +++ b/src/status_im/utils/listview.cljs @@ -0,0 +1,18 @@ +(ns status-im.utils.listview + (:require-macros [natal-shell.data-source :refer [data-source]])) + +(defn clone-with-rows [ds rows] + (.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac) + (clj->js []) rows))) + +(defn to-datasource [items] + (clone-with-rows (data-source {:rowHasChanged not=}) items)) + +(defn clone-with-rows-inverted [ds rows] + (let [rows (reduce (fn [ac el] (.push ac el) ac) + (clj->js []) (reverse rows)) + row-ids (.reverse (.map rows (fn [_ index] index)))] + (.cloneWithRows ds rows row-ids))) + +(defn to-datasource-inverted [items] + (clone-with-rows-inverted (data-source {:rowHasChanged not=}) items)) diff --git a/src/status_im/utils/name.cljs b/src/status_im/utils/name.cljs new file mode 100644 index 0000000000..cc55d74d09 --- /dev/null +++ b/src/status_im/utils/name.cljs @@ -0,0 +1,30 @@ +(ns status-im.utils.name + (:require [clojure.string :as str])) + +(defn too-long? [name max-len] + (> (count name) max-len)) + +(defn max-name + [name max-len] + (let [names (str/split name " ")] + (first + (reduce (fn [[name done] next-name] + (if done + name + (let [new-name (str/join " " [name next-name])] + (if (too-long? new-name max-len) + (let [new-name' (str name " " (first next-name) ".")] + (if (too-long? new-name' max-len) + [name true] + [new-name' true])) + [new-name])))) + [(first names)] + (rest names))))) + +(defn shortened-name [name max-len] + (if (> (count name) max-len) + (let [name' (max-name name max-len)] + (if (too-long? name' max-len) + (str (str/trim (subs name 0 max-len)) "...") + name')) + name)) diff --git a/src/status_im/utils/phone_number.cljs b/src/status_im/utils/phone_number.cljs new file mode 100644 index 0000000000..5ae5cb475f --- /dev/null +++ b/src/status_im/utils/phone_number.cljs @@ -0,0 +1,19 @@ +(ns status-im.utils.phone-number + (:require [status-im.utils.utils :as u])) + +(def i18n (js/require "react-native-i18n")) +(def locale (or (.-locale i18n) "___en")) +(def country-code (subs locale 3 5)) +(def awesome-phonenumber (js/require "awesome-phonenumber")) + +;; todo check wrong numbers, .getNumber returns empty string +(defn format-phone-number [number] + (str (.getNumber (awesome-phonenumber. number country-code "international")))) + +(defn valid-mobile-number? [number] + (when (string? number) + (let [pattern #"^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{2})[-. ]*(\d{2})\s*$" + number-obj (awesome-phonenumber. number country-code "international")] + (and (re-matches pattern number) + (.isValid number-obj) + (.isMobile number-obj))))) diff --git a/src/status_im/utils/platform.cljs b/src/status_im/utils/platform.cljs new file mode 100644 index 0000000000..f7e6095a4a --- /dev/null +++ b/src/status_im/utils/platform.cljs @@ -0,0 +1,19 @@ +(ns status-im.utils.platform + (:require [status-im.utils.utils :as u] + [status-im.android.platform :as android] + [status-im.ios.platform :as ios])) + +(def react-native (js/require "react-native")) + +(def platform + (when-let [pl (.-Platform react-native)] + (.-OS pl))) + +(def android? (= platform "android")) +(def ios? (= platform "ios")) + +(def platform-specific + (cond + android? android/platform-specific + ios? ios/platform-specific + :else {})) diff --git a/src/status_im/utils/random.cljs b/src/status_im/utils/random.cljs new file mode 100644 index 0000000000..7553790cc0 --- /dev/null +++ b/src/status_im/utils/random.cljs @@ -0,0 +1,11 @@ +(ns status-im.utils.random) + +(defn timestamp [] + (.getTime (js/Date.))) + +(def Chance (js/require "chance")) + +(def chance (Chance.)) + +(defn id [] + (str (timestamp) "-" (.guid chance))) diff --git a/src/status_im/utils/scheduler.cljs b/src/status_im/utils/scheduler.cljs new file mode 100644 index 0000000000..c67841c227 --- /dev/null +++ b/src/status_im/utils/scheduler.cljs @@ -0,0 +1,10 @@ +(ns status-im.utils.scheduler + (:require-macros [cljs.core.async.macros :refer [go]]) + (:require [cljs.core.async :refer [ms [s] (* 1000 s)) + +(defn execute-later + [function timeout-ms] + (go (clj message :keywordize-keys true)))))) + +(defn remove-sms-listener [subscription] + (when android? + (.remove subscription))) diff --git a/src/status_im/utils/subs.cljs b/src/status_im/utils/subs.cljs new file mode 100644 index 0000000000..60bb24c53b --- /dev/null +++ b/src/status_im/utils/subs.cljs @@ -0,0 +1,10 @@ +(ns status-im.utils.subs + (:require-macros [reagent.ratom :refer [reaction]])) + +(defn contains-sub + "Creates subscrition that cheks if collection (map or set) contains element" + [collection] + (fn [db [_ element]] + (-> (collection @db) + (contains? element) + (reaction)))) diff --git a/src/status_im/utils/types.cljs b/src/status_im/utils/types.cljs new file mode 100644 index 0000000000..d30d09804b --- /dev/null +++ b/src/status_im/utils/types.cljs @@ -0,0 +1,16 @@ +(ns status-im.utils.types) + +(defn to-string [s] + (if (keyword? s) + (name s) + s)) + +(defn to-edn-string [value] + (with-out-str (pr value))) + +(defn clj->json [data] + (.stringify js/JSON (clj->js data))) + +(defn json->clj [json] + (when-not (= json "undefined") + (js->clj (.parse js/JSON json) :keywordize-keys true))) diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs new file mode 100644 index 0000000000..5212c22f7e --- /dev/null +++ b/src/status_im/utils/utils.cljs @@ -0,0 +1,82 @@ +(ns status-im.utils.utils + (:require [status-im.constants :as const] + [reagent.core :as r] + [clojure.string :as str])) + +(defn require [module] + (if (exists? js/window) + (js/require module) + #js {})) + +(defn log [obj] + (.log js/console obj)) + +(def react-native (js/require "react-native")) + +(defn show-popup [title content] + (.alert (.-Alert react-native) + title + content)) + +(defn http-post + ([action data on-success] + (http-post action data on-success nil)) + ([action data on-success on-error] + (-> (.fetch js/window + (str const/server-address action) + (clj->js {:method "POST" + :headers {:accept "application/json" + :content-type "application/json"} + :body (.stringify js/JSON (clj->js data))})) + (.then (fn [response] + (log response) + (.text response))) + (.then (fn [text] + (let [json (.parse js/JSON text) + obj (js->clj json :keywordize-keys true)] + (on-success obj)))) + (.catch (or on-error + (fn [error] + (show-popup "Error" (str error)))))))) + +(defn http-get + ([url on-success on-error] + (-> (.fetch js/window url (clj->js {:method "GET"})) + (.then (fn [response] + (log response) + (.text response))) + (.then on-success) + (.catch (or on-error + (fn [error] + (show-popup "Error" (str error)))))))) + +(defn truncate-str [s max] + (if (and (< max (count s)) s) + (str (subs s 0 (- max 3)) "...") + s)) + +(defn clean-text [s] + (-> s + (str/replace #"\n" "") + (str/replace #"\r" "") + (str/trim))) + +(defn first-index + [cond coll] + (loop [index 0 + cond cond + coll coll] + (when (seq coll) + (if (cond (first coll)) + index + (recur (inc index) cond (next coll)))))) + +(defn get-react-property [name] + (aget react-native name)) + +(defn adapt-class [class] + (when class + (r/adapt-react-class class))) + +(defn get-class [name] + (adapt-class (get-react-property name))) diff --git a/src/status_im/utils/views.clj b/src/status_im/utils/views.clj new file mode 100644 index 0000000000..5894081f5c --- /dev/null +++ b/src/status_im/utils/views.clj @@ -0,0 +1,58 @@ +(ns status-im.utils.views + (:require [clojure.walk :as w])) + +(defn atom? [sub] + (or (vector? sub) + (and (seq sub) + (#{'atom `reagent.core/atom} (first sub))))) + +(defn walk-sub [sub form->sym] + (if (coll? sub) + (w/postwalk (fn [f] + (or (form->sym f) f)) sub) + (or (form->sym sub) sub))) + +(defn prepare-subs [subs] + (let [pairs (map (fn [[form sub]] + {:form form + :sub sub + :sym (if (atom? sub) + (gensym (str (if (map? form) "keys" form))) + form)}) + (partition 2 subs)) + form->sym (->> pairs + (map (fn [{:keys [form sym]}] + [form sym])) + (into {}))] + [(mapcat (fn [{:keys [form sym sub]}] + (if (vector? sub) + [sym `(re-frame.core/subscribe ~(walk-sub sub form->sym))] + [form (walk-sub sub form->sym)])) + pairs) + (apply concat (keep (fn [{:keys [sym form sub]}] + (when (atom? sub) + [form `(deref ~sym)])) + pairs))])) + +(defmacro defview + [n params & rest] + (let [[subs component-map body] (case (count rest) + 1 [nil {} (first rest)] + 2 [(first rest) {} (second rest)] + 3 rest) + [subs-bindings vars-bindings] (prepare-subs subs)] + `(defn ~n ~params + (let [~@subs-bindings] + (reagent.core/create-class + (merge ~(->> component-map + (map (fn [[k f]] + (let [args (gensym "args")] + [k `(fn [& ~args] + (let [~@vars-bindings] + (apply ~f ~args)))]))) + (into {})) + {:reagent-render + (fn ~params + (let [~@vars-bindings] + ~body))})))))) + diff --git a/src/syng_im/android/core.cljs b/src/syng_im/android/core.cljs deleted file mode 100644 index 3ffb47945e..0000000000 --- a/src/syng_im/android/core.cljs +++ /dev/null @@ -1,64 +0,0 @@ -(ns syng-im.android.core - (:require-macros - [natal-shell.back-android :refer [add-event-listener remove-event-listener]]) - (:require [reagent.core :as r :refer [atom]] - [cljs.core :as cljs] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.handlers] - [syng-im.subs] - [syng-im.components.react :refer [navigator app-registry]] - [syng-im.components.contact-list.contact-list :refer [contact-list]] - [syng-im.components.chat :refer [chat]] - [syng-im.components.sign-up :refer [sign-up-view]] - [syng-im.components.sign-up-confirm :refer [sign-up-confirm-view]] - [syng-im.components.chats.chats-list :refer [chats-list]] - [syng-im.components.chats.new-group :refer [new-group]] - [syng-im.components.chat.new-participants :refer [new-participants]] - [syng-im.components.chat.remove-participants :refer [remove-participants]] - [syng-im.utils.logging :as log] - [syng-im.navigation :as nav] - [syng-im.utils.encryption])) - -(def back-button-handler (cljs/atom {:nav nil - :handler nil})) - -(defn init-back-button-handler! [nav] - (let [handler @back-button-handler] - (when-not (= nav (:nav handler)) - (remove-event-listener "hardwareBackPress" (:handler handler)) - (let [new-listener (fn [] - (binding [nav/*nav-render* false] - (when (< 1 (.-length (.getCurrentRoutes nav))) - (nav/nav-pop nav) - true)))] - (reset! back-button-handler {:nav nav - :handler new-listener}) - (add-event-listener "hardwareBackPress" new-listener))))) - -(defn app-root [] - [navigator {:initial-route (clj->js {:view-id :chat-list}) - :render-scene (fn [route nav] - (log/debug "route" route) - (when true ;; nav/*nav-render* - (let [{:keys [view-id]} (js->clj route :keywordize-keys true) - view-id (keyword view-id)] - (init-back-button-handler! nav) - (case view-id - :add-participants (r/as-element [new-participants {:navigator nav}]) - :remove-participants (r/as-element [remove-participants {:navigator nav}]) - :chat-list (r/as-element [chats-list {:navigator nav}]) - :new-group (r/as-element [new-group {:navigator nav}]) - :contact-list (r/as-element [contact-list {:navigator nav}]) - :chat (r/as-element [chat {:navigator nav}]) - :sign-up (r/as-element [sign-up-view {:navigator nav}]) - :sign-up-confirm (r/as-element [sign-up-confirm-view {:navigator nav}])))))}]) - -(defn init [] - (dispatch-sync [:initialize-db]) - (dispatch [:initialize-crypt]) - (dispatch [:initialize-protocol]) - (dispatch [:load-user-phone-number]) - (dispatch [:load-syng-contacts]) - ;; TODO execute on first run only - ;; (dispatch [:set-sign-up-chat]) - (.registerComponent app-registry "SyngIm" #(r/reactify-component app-root))) diff --git a/src/syng_im/components/action_button.cljs b/src/syng_im/components/action_button.cljs deleted file mode 100644 index bfabb5ad0e..0000000000 --- a/src/syng_im/components/action_button.cljs +++ /dev/null @@ -1,7 +0,0 @@ -(ns syng-im.components.action-button - (:require [reagent.core :as r])) - -(set! js/window.ActionButton (js/require "react-native-action-button")) - -(def action-button (r/adapt-react-class (.-default js/ActionButton))) -(def action-button-item (r/adapt-react-class (.. js/ActionButton -default -Item))) \ No newline at end of file diff --git a/src/syng_im/components/chat.cljs b/src/syng_im/components/chat.cljs deleted file mode 100644 index 18efbb4b3b..0000000000 --- a/src/syng_im/components/chat.cljs +++ /dev/null @@ -1,85 +0,0 @@ -(ns syng-im.components.chat - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - text - image - touchable-highlight - navigator - toolbar-android]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.logging :as log] - [syng-im.navigation :refer [nav-pop]] - [syng-im.resources :as res] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] - [reagent.core :as r] - [syng-im.components.chat.chat-message :refer [chat-message]] - [syng-im.components.chat.chat-message-new :refer [chat-message-new]])) - - -(defn contacts-by-identity [contacts] - (->> contacts - (map (fn [{:keys [identity] :as contact}] - [identity contact])) - (into {}))) - -(defn add-msg-color [{:keys [from] :as msg} contact-by-identity] - (if (= "system" from) - (assoc msg :text-color "#4A5258" - :background-color "#D3EEEF") - (let [{:keys [text-color background-color]} (get contact-by-identity from)] - (assoc msg :text-color text-color - :background-color background-color)))) - -(defn chat [{:keys [navigator]}] - (let [messages (subscribe [:get-chat-messages]) - chat (subscribe [:get-current-chat])] - (fn [] - (let [msgs @messages - ;_ (log/debug "messages=" msgs) - datasource (to-realm-datasource msgs) - contacts (:contacts @chat) - contact-by-identity (contacts-by-identity contacts)] - [view {:style {:flex 1 - :backgroundColor "#eef2f5"}} - (when android? - ;; TODO add IOS version - [toolbar-android {:logo res/logo-icon - :title (or (@chat :name) - "Chat name") - :titleColor "#4A5258" - :subtitle "Last seen just now" - :subtitleColor "#AAB2B2" - :navIcon res/nav-back-icon - :style {:backgroundColor "white" - :height 56 - :elevation 2} - :actions (when (and (:group-chat @chat) - (:is-active @chat)) - [{:title "Add Contact to chat" - :icon res/add-icon - :showWithText true} - {:title "Remove Contact from chat" - :icon res/trash-icon - :showWithText true} - {:title "Leave Chat" - :icon res/leave-icon - :showWithText true}]) - :onActionSelected (fn [position] - (case position - 0 (dispatch [:show-add-participants navigator]) - 1 (dispatch [:show-remove-participants navigator]) - 2 (dispatch [:leave-group-chat navigator]))) - :onIconClicked (fn [] - (nav-pop navigator))}]) - [list-view {:dataSource datasource - :renderScrollComponent (fn [props] - (invertible-scroll-view nil)) - :renderRow (fn [row section-id row-id] - (let [msg (-> (js->clj row :keywordize-keys true) - (add-msg-color contact-by-identity))] - (r/as-element [chat-message msg]))) - :style {:backgroundColor "white"}}] - (when (:is-active @chat) - [chat-message-new])])))) diff --git a/src/syng_im/components/chat/chat_message.cljs b/src/syng_im/components/chat/chat_message.cljs deleted file mode 100644 index 20ce00e755..0000000000 --- a/src/syng_im/components/chat/chat_message.cljs +++ /dev/null @@ -1,157 +0,0 @@ -(ns syng-im.components.chat.chat-message - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - text - image - touchable-highlight - navigator - toolbar-android]] - [syng-im.models.commands :as commands] - [syng-im.utils.logging :as log] - [syng-im.navigation :refer [nav-pop]] - [syng-im.resources :as res] - [syng-im.constants :refer [text-content-type - content-type-command]])) - - -(defn message-date [{:keys [date]}] - [text {:style {:marginVertical 10 - :fontFamily "Avenir-Roman" - :fontSize 11 - :color "#AAB2B2" - :letterSpacing 1 - :lineHeight 15 - :textAlign "center" - :opacity 0.8}} - date]) - -(defn message-content-audio [{:keys [content-type content-type]}] - [view {:style {:flexDirection "row" - :alignItems "center"}} - [view {:style {:width 33 - :height 33 - :borderRadius 50 - :elevation 1}} - [image {:source res/play - :style {:width 33 - :height 33}}]] - [view {:style {:marginTop 10 - :marginLeft 10 - :width 120 - :height 26 - :elevation 1}} - [view {:style {:position "absolute" - :top 4 - :width 120 - :height 2 - :backgroundColor "#EC7262"}}] - [view {:style {:position "absolute" - :left 0 - :top 0 - :width 2 - :height 10 - :backgroundColor "#4A5258"}}] - [text {:style {:position "absolute" - :left 1 - :top 11 - :fontFamily "Avenir-Roman" - :fontSize 11 - :color "#4A5258" - :letterSpacing 1 - :lineHeight 15}} - "03:39"]]]) - - -(defn message-content-command [content] - (let [{:keys [command content]} (commands/parse-command-msg-content content)] - [view {:style {:flexDirection "column"}} - [view {:style {:margin 10 - :backgroundColor (:color command) - :borderRadius 10}} - [text {:style {:marginTop -2 - :marginHorizontal 10 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "white"}} - (:text command)]] - [text {:style {:marginTop -2 - :marginHorizontal 10 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "black"}} - ;; TODO isn't smart - (if (= (:command command) :keypair-password) - "******" - content)]])) - -(defn message-content [{:keys [content-type content outgoing text-color background-color]}] - [view {:style (merge {:borderRadius 6} - (if (= content-type text-content-type) - {:paddingVertical 12 - :paddingHorizontal 16} - {:paddingVertical 14 - :paddingHorizontal 10}) - (if outgoing - {:backgroundColor "#D3EEEF"} - {:backgroundColor background-color}))} - (cond - (= content-type text-content-type) - [text {:style (merge {:fontSize 14 - :fontFamily "Avenir-Roman"} - (if outgoing - {:color "#4A5258"} - {:color text-color}))} - content] - (= content-type content-type-command) - [message-content-command content] - :else [message-content-audio {:content content - :content-type content-type}])]) - -(defn message-delivery-status [{:keys [delivery-status]}] - [view {:style {:flexDirection "row" - :marginTop 2}} - [image {:source (case delivery-status - :delivered res/delivered-icon - :seen res/seen-icon - :failed res/delivery-failed-icon) - :style {:marginTop 6 - :opacity 0.6}}] - [text {:style {:fontFamily "Avenir-Roman" - :fontSize 11 - :color "#AAB2B2" - :opacity 0.8 - :marginLeft 5}} - (case delivery-status - :delivered "Delivered" - :seen "Seen" - :failed "Failed")]]) - -(defn message-body [{:keys [msg-id content content-type outgoing delivery-status text-color background-color]}] - [view {:style (merge {:flexDirection "column" - :width 260 - :marginVertical 5} - (if outgoing - {:alignSelf "flex-end" - :alignItems "flex-end"} - {:alignSelf "flex-start" - :alignItems "flex-start"}))} - [message-content {:content-type content-type - :content content - :outgoing outgoing - :text-color text-color - :background-color background-color}] - (when (and outgoing delivery-status) - [message-delivery-status {:delivery-status delivery-status}])]) - -(defn chat-message [{:keys [msg-id content content-type outgoing delivery-status date new-day text-color background-color] :as msg}] - [view {:paddingHorizontal 15} - (when new-day - [message-date {:date date}]) - [message-body {:msg-id msg-id - :content content - :content-type content-type - :outgoing outgoing - :text-color text-color - :background-color background-color - :delivery-status (keyword delivery-status)}]]) diff --git a/src/syng_im/components/chat/chat_message_new.cljs b/src/syng_im/components/chat/chat_message_new.cljs deleted file mode 100644 index 4afa65e0de..0000000000 --- a/src/syng_im/components/chat/chat_message_new.cljs +++ /dev/null @@ -1,38 +0,0 @@ -(ns syng-im.components.chat.chat-message-new - (:require - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - image - text - text-input - touchable-highlight]] - [syng-im.components.chat.plain-message-input :refer [plain-message-input-view]] - [syng-im.components.chat.input.simple-command :refer [simple-command-input-view]] - [syng-im.components.chat.input.phone :refer [phone-input-view]] - [syng-im.components.chat.input.password :refer [password-input-view]] - [syng-im.components.chat.input.money :refer [money-input-view]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log] - [syng-im.resources :as res] - [reagent.core :as r])) - -(defn default-command-input-view [command] - [simple-command-input-view command {}]) - -(defn special-input-view [command] - (case (:command command) - :phone [phone-input-view command] - :keypair-password [password-input-view command] - :money [money-input-view command] - :request [money-input-view command] - [default-command-input-view command])) - -(defn chat-message-new [] - (let [command-atom (subscribe [:get-chat-command])] - (fn [] - (let [command @command-atom] - [view - (if command - [special-input-view command] - [plain-message-input-view])])))) diff --git a/src/syng_im/components/chat/input/money.cljs b/src/syng_im/components/chat/input/money.cljs deleted file mode 100644 index 8401ff31a2..0000000000 --- a/src/syng_im/components/chat/input/money.cljs +++ /dev/null @@ -1,15 +0,0 @@ -(ns syng-im.components.chat.input.money - (:require - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.chat.input.simple-command :refer [simple-command-input-view]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log])) - -(defn money-input-view [command] - [simple-command-input-view command {:keyboardType "numeric" - :style {:flex 1 - :marginLeft 8 - :lineHeight 42 - :fontSize 32 - :fontFamily "Avenir-Roman" - :color "black"}}]) diff --git a/src/syng_im/components/chat/input/password.cljs b/src/syng_im/components/chat/input/password.cljs deleted file mode 100644 index e27c5f8112..0000000000 --- a/src/syng_im/components/chat/input/password.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.components.chat.input.password - (:require - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.chat.input.simple-command :refer [simple-command-input-view]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log])) - -(defn password-input-view [command] - [simple-command-input-view command {:secureTextEntry true}]) diff --git a/src/syng_im/components/chat/input/phone.cljs b/src/syng_im/components/chat/input/phone.cljs deleted file mode 100644 index ed8c71db1e..0000000000 --- a/src/syng_im/components/chat/input/phone.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.components.chat.input.phone - (:require - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.chat.input.simple-command :refer [simple-command-input-view]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log])) - -(defn phone-input-view [command] - [simple-command-input-view command {:keyboardType "phone-pad"}]) diff --git a/src/syng_im/components/chat/input/simple_command.cljs b/src/syng_im/components/chat/input/simple_command.cljs deleted file mode 100644 index 3e84760747..0000000000 --- a/src/syng_im/components/chat/input/simple_command.cljs +++ /dev/null @@ -1,85 +0,0 @@ -(ns syng-im.components.chat.input.simple-command - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - image - text - text-input - touchable-highlight]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log] - [syng-im.resources :as res] - [reagent.core :as r])) - -(defn cancel-command-input [] - (dispatch [:set-chat-command nil])) - -(defn set-input-message [message] - (dispatch [:set-chat-command-content message])) - -(defn send-command [chat-id command text] - (dispatch [:send-chat-command chat-id (:command command) text]) - (cancel-command-input)) - -(defn simple-command-input-view [command input-options] - (let [chat-id-atom (subscribe [:get-current-chat-id]) - message-atom (subscribe [:get-chat-command-content])] - (fn [command input-options] - (let [chat-id @chat-id-atom - message @message-atom] - [view {:style {:flexDirection "row"}} - [view {:style {:flex 1 - :flexDirection "column" - :backgroundColor "white"}} - [view {:style {:flexDirection "column" - :margin 10 - :width 200 - :backgroundColor "#ebf0f4" - :borderRadius 10}} - [view {:style {:flexDirection "row"}} - [view {:style {:flexDirection "column" - :margin 10 - :backgroundColor (:color command) - :borderRadius 10}} - [text {:style {:marginTop -2 - :marginHorizontal 10 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "white"}} - (:text command)]] - [touchable-highlight {:style {:marginTop 14 - :marginRight 16 - :position "absolute" - :top 3 - :right 20} - :onPress (fn [] - (cancel-command-input))} - [image {:source res/att - :style {:width 17 - :height 14}}]]] - [text-input (merge {:style {:flex 1 - :marginLeft 8 - :lineHeight 42 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "black"} - :underlineColorAndroid "transparent" - :autoFocus true - :keyboardType "default" - :onChangeText (fn [new-text] - (set-input-message new-text)) - :onSubmitEditing (fn [e] - (send-command chat-id command message) - (set-input-message nil))} - input-options) - message]]] - [touchable-highlight {:style {:marginTop 14 - :marginRight 16 - :position "absolute" - :right 20 - :bottom 20} - :onPress (fn [] - (cancel-command-input))} - [image {:source res/att - :style {:width 34 - :height 28}}]]])))) diff --git a/src/syng_im/components/chat/new_participants.cljs b/src/syng_im/components/chat/new_participants.cljs deleted file mode 100644 index dedff2578a..0000000000 --- a/src/syng_im/components/chat/new_participants.cljs +++ /dev/null @@ -1,36 +0,0 @@ -(ns syng-im.components.chat.new-participants - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view toolbar-android android? text-input]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-participant-contact :refer [new-participant-contact]] - [reagent.core :as r] - [syng-im.navigation :refer [nav-pop]])) - -(defn new-participants [{:keys [navigator]}] - (let [contacts (subscribe [:all-new-contacts])] - (fn [] - (let [contacts-ds (to-realm-datasource @contacts)] - [view {:style {:flex 1 - :backgroundColor "white"}} - (when android? - ;; TODO add IOS version - [toolbar-android {:logo res/logo-icon - :title "Add Participants" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2} - :actions [{:title "Add" - :icon res/v - :show "always"}] - :onActionSelected (fn [position] - (dispatch [:add-new-participants navigator])) - :navIcon res/nav-back-icon - :onIconClicked (fn [] - (nav-pop navigator))}]) - [list-view {:dataSource contacts-ds - :renderRow (fn [row section-id row-id] - (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) - :style {:backgroundColor "white"}}]])))) diff --git a/src/syng_im/components/chat/plain_message_input.cljs b/src/syng_im/components/chat/plain_message_input.cljs deleted file mode 100644 index 476aa1b13e..0000000000 --- a/src/syng_im/components/chat/plain_message_input.cljs +++ /dev/null @@ -1,63 +0,0 @@ -(ns syng-im.components.chat.plain-message-input - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - image - text-input]] - [syng-im.components.chat.suggestions :refer [suggestions-view]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log] - [syng-im.resources :as res] - [reagent.core :as r])) - -(defn set-input-message [message] - (dispatch [:set-chat-input-text message])) - -(defn plain-message-input-view [] - (let [chat (subscribe [:get-current-chat]) - input-message-atom (subscribe [:get-chat-input-text])] - (fn [] - (let [input-message @input-message-atom] - [view {:style {:flexDirection "column"}} - [suggestions-view] - [view {:style {:flexDirection "row" - :margin 1 - :height 40 - :backgroundColor "white" - :borderRadius 5}} - [image {:source res/mic - :style {:marginTop 11 - :marginLeft 14 - :width 13 - :height 20}}] - [text-input {:underlineColorAndroid "transparent" - :style {:flex 1 - :marginLeft 18 - :lineHeight 42 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "#9CBFC0"} - :autoFocus true - :placeholder "Type your message here" - :onChangeText (fn [new-text] - (set-input-message new-text)) - :onSubmitEditing (fn [e] - (let [{:keys [group-chat chat-id]} @chat] - ;; TODO get text from state? - (if group-chat - (dispatch [:send-group-chat-msg chat-id - input-message]) - (dispatch [:send-chat-msg chat-id - input-message]))) - (set-input-message nil))} - input-message] - [image {:source res/smile - :style {:marginTop 11 - :marginRight 12 - :width 18 - :height 18}}] - [image {:source res/att - :style {:marginTop 14 - :marginRight 16 - :width 17 - :height 14}}]]])))) diff --git a/src/syng_im/components/chat/remove_participants.cljs b/src/syng_im/components/chat/remove_participants.cljs deleted file mode 100644 index 1620ac3757..0000000000 --- a/src/syng_im/components/chat/remove_participants.cljs +++ /dev/null @@ -1,36 +0,0 @@ -(ns syng-im.components.chat.remove-participants - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view toolbar-android android? text-input]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-participant-contact :refer [new-participant-contact]] - [reagent.core :as r] - [syng-im.navigation :refer [nav-pop]])) - -(defn remove-participants [{:keys [navigator]}] - (let [contacts (subscribe [:current-chat-contacts])] - (fn [] - (let [contacts-ds (to-realm-datasource @contacts)] - [view {:style {:flex 1 - :backgroundColor "white"}} - (when android? - ;; TODO add IOS version - [toolbar-android {:logo res/logo-icon - :title "Remove Participants" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2} - :actions [{:title "Remove" - :icon res/trash-icon - :show "always"}] - :onActionSelected (fn [position] - (dispatch [:remove-selected-participants navigator])) - :navIcon res/nav-back-icon - :onIconClicked (fn [] - (nav-pop navigator))}]) - [list-view {:dataSource contacts-ds - :renderRow (fn [row section-id row-id] - (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) - :style {:backgroundColor "white"}}]])))) diff --git a/src/syng_im/components/chat/suggestions.cljs b/src/syng_im/components/chat/suggestions.cljs deleted file mode 100644 index 21b22b6711..0000000000 --- a/src/syng_im/components/chat/suggestions.cljs +++ /dev/null @@ -1,66 +0,0 @@ -(ns syng-im.components.chat.suggestions - (:require-macros - [natal-shell.core :refer [with-error-view]]) - (:require [clojure.string :as cstr] - [reagent.core :as r] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view - image - text - touchable-highlight - list-view - list-item]] - [syng-im.utils.listview :refer [to-datasource]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log])) - -(defn set-command-input [command] - (dispatch [:set-chat-command command])) - -(defn suggestion-list-item [suggestion] - [touchable-highlight {:onPress (fn [] - (set-command-input (keyword (:command suggestion))))} - [view {:style {:flexDirection "row" - :marginVertical 1 - :marginHorizontal 0 - :height 40 - :backgroundColor "white"}} - [view {:style {:flexDirection "column" - :position "absolute" - :top 10 - :left 60 - :backgroundColor (:color suggestion) - :borderRadius 10}} - [text {:style {:marginTop -2 - :marginHorizontal 10 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "white"}} - (:text suggestion)]] - [text {:style {:flex 1 - :position "absolute" - :top 7 - :left 190 - :lineHeight 18 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "black"}} - (:description suggestion)]]]) - -(defn render-row [row section-id row-id] - (list-item [suggestion-list-item (js->clj row :keywordize-keys true)])) - -(defn suggestions-view [] - (let [suggestions-atom (subscribe [:get-suggestions])] - (fn [] - (let [suggestions @suggestions-atom] - (when (not (empty? suggestions)) - [view {:style {:flexDirection "row" - :marginVertical 1 - :marginHorizontal 0 - :height (min 105 (* 42 (count suggestions))) - :backgroundColor "#eef2f5" - :borderRadius 5}} - [list-view {:dataSource (to-datasource suggestions) - :renderRow render-row - :style {}}]]))))) diff --git a/src/syng_im/components/chats/chat_list_item.cljs b/src/syng_im/components/chats/chat_list_item.cljs deleted file mode 100644 index 3f1d76fd3d..0000000000 --- a/src/syng_im/components/chats/chat_list_item.cljs +++ /dev/null @@ -1,26 +0,0 @@ -(ns syng-im.components.chats.chat-list-item - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view - text - image - touchable-highlight]] - [syng-im.utils.logging :as log] - [syng-im.resources :as res])) - -(defn chat-list-item [chat-obj navigator] - [touchable-highlight {:on-press (fn [] - (dispatch [:show-chat (aget chat-obj "chat-id") navigator :push]))} - [view {:style {:flexDirection "row" - :width 260 - :marginVertical 5}} - [image {:source res/chat-icon - :style {:borderWidth 2 - :borderColor "#FFFFFF" - :width 32 - :height 30 - :marginRight 5 - :marginLeft 5}}] - [text {:style {:fontSize 14 - :fontFamily "Avenir-Roman" - :color "#4A5258"}} - (subs (aget chat-obj "name") 0 30)]]]) \ No newline at end of file diff --git a/src/syng_im/components/chats/chats_list.cljs b/src/syng_im/components/chats/chats_list.cljs deleted file mode 100644 index 6947c87742..0000000000 --- a/src/syng_im/components/chats/chats_list.cljs +++ /dev/null @@ -1,63 +0,0 @@ -(ns syng-im.components.chats.chats-list - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - text - image - touchable-highlight - navigator - toolbar-android]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.logging :as log] - [syng-im.navigation :refer [nav-pop]] - [syng-im.resources :as res] - [syng-im.utils.listview :refer [to-realm-datasource]] - [reagent.core :as r] - [syng-im.components.chats.chat-list-item :refer [chat-list-item]] - [syng-im.components.action-button :refer [action-button - action-button-item]] - [syng-im.components.icons.ionicons :refer [icon]])) - - -(defn chats-list [{:keys [navigator]}] - (let [chats (subscribe [:get-chats])] - (fn [] - (let [chats @chats - _ (log/debug "chats=" chats) - datasource (to-realm-datasource chats)] - [view {:style {:flex 1 - :backgroundColor "white"}} - (when android? - ;; TODO add IOS version - [toolbar-android {:logo res/logo-icon - :title "Your Chats" - :titleColor "#4A5258" - :subtitle "List of your recent chats" - :subtitleColor "#AAB2B2" - :navIcon res/nav-back-icon - :style {:backgroundColor "white" - :height 56 - :elevation 2} - :onIconClicked (fn [] - (nav-pop navigator))}]) - [list-view {:dataSource datasource - :renderRow (fn [row section-id row-id] - (r/as-element [chat-list-item row navigator])) - :style {:backgroundColor "white"}}] - [action-button {:buttonColor "rgba(231,76,60,1)"} - [action-button-item {:title "New Chat" - :buttonColor "#9b59b6" - :onPress (fn [] - (dispatch [:show-contacts navigator]))} - [icon {:name "android-create" - :style {:fontSize 20 - :height 22 - :color "white"}}]] - [action-button-item {:title "New Group Chat" - :buttonColor "#1abc9c" - :onPress (fn [] - (dispatch [:show-group-new navigator]))} - [icon {:name "person-stalker" - :style {:fontSize 20 - :height 22 - :color "white"}}]]]])))) diff --git a/src/syng_im/components/chats/new_group.cljs b/src/syng_im/components/chats/new_group.cljs deleted file mode 100644 index c402ebd799..0000000000 --- a/src/syng_im/components/chats/new_group.cljs +++ /dev/null @@ -1,52 +0,0 @@ -(ns syng-im.components.chats.new-group - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view toolbar-android android? text-input]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-group-contact :refer [new-group-contact]] - [reagent.core :as r] - [syng-im.navigation :refer [nav-pop]])) - -(defn new-group [{:keys [navigator]}] - (let [contacts (subscribe [:all-contacts]) - group-name (atom nil)] - (fn [] - (let [contacts-ds (to-realm-datasource @contacts)] - [view {:style {:flex 1 - :backgroundColor "white"}} - (when android? - ;; TODO add IOS version - [toolbar-android {:logo res/logo-icon - :title "New Group Chat" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2} - :actions [{:title "Create" - :icon res/v - :show "always"}] - :onActionSelected (fn [position] - (dispatch [:create-new-group @group-name navigator])) - :navIcon res/nav-back-icon - :onIconClicked (fn [] - (nav-pop navigator))}]) - [text-input {:underlineColorAndroid "#9CBFC0" - :style {:marginLeft 5 - :marginRight 5 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "#9CBFC0"} - :autoFocus true - :placeholder "Group Name" - :value @group-name - :onChangeText (fn [new-text] - (reset! group-name new-text) - (r/flush)) - :onSubmitEditing (fn [e] - ;(dispatch [:send-chat-msg @chat-id @text]) - (reset! group-name nil))}] - [list-view {:dataSource contacts-ds - :renderRow (fn [row section-id row-id] - (r/as-element [new-group-contact (js->clj row :keywordize-keys true) navigator])) - :style {:backgroundColor "white"}}]])))) diff --git a/src/syng_im/components/chats/new_group_contact.cljs b/src/syng_im/components/chats/new_group_contact.cljs deleted file mode 100644 index f0e7303763..0000000000 --- a/src/syng_im/components/chats/new_group_contact.cljs +++ /dev/null @@ -1,24 +0,0 @@ -(ns syng-im.components.chats.new-group-contact - (:require [syng-im.resources :as res] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view]] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]] - [syng-im.components.item-checkbox :refer [item-checkbox]] - [syng-im.utils.logging :as log] - [reagent.core :as r])) - -(defn new-group-contact [{:keys [whisper-identity] :as contact} navigator] - (let [checked (r/atom false)] - (fn [] - [view {:style {:flexDirection "row" - :marginTop 5 - :marginBottom 5 - :paddingLeft 15 - :paddingRight 15 - :height 75}} - [item-checkbox {:onToggle (fn [checked?] - (reset! checked checked?) - (dispatch [:select-for-new-group whisper-identity checked?])) - :checked @checked - :size 30}] - [contact-inner-view contact]]))) diff --git a/src/syng_im/components/chats/new_participant_contact.cljs b/src/syng_im/components/chats/new_participant_contact.cljs deleted file mode 100644 index 97985c6468..0000000000 --- a/src/syng_im/components/chats/new_participant_contact.cljs +++ /dev/null @@ -1,24 +0,0 @@ -(ns syng-im.components.chats.new-participant-contact - (:require [syng-im.resources :as res] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view]] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]] - [syng-im.components.item-checkbox :refer [item-checkbox]] - [syng-im.utils.logging :as log] - [reagent.core :as r])) - -(defn new-participant-contact [{:keys [whisper-identity] :as contact} navigator] - (let [checked (r/atom false)] - (fn [] - [view {:style {:flexDirection "row" - :marginTop 5 - :marginBottom 5 - :paddingLeft 15 - :paddingRight 15 - :height 75}} - [item-checkbox {:onToggle (fn [checked?] - (reset! checked checked?) - (dispatch [:select-new-participant whisper-identity checked?])) - :checked @checked - :size 30}] - [contact-inner-view contact]]))) diff --git a/src/syng_im/components/contact_list/contact.cljs b/src/syng_im/components/contact_list/contact.cljs deleted file mode 100644 index c13f9fd9e6..0000000000 --- a/src/syng_im/components/contact_list/contact.cljs +++ /dev/null @@ -1,14 +0,0 @@ -(ns syng-im.components.contact-list.contact - (:require [syng-im.components.react :refer [view text image touchable-highlight]] - [syng-im.resources :as res] - [syng-im.navigation :as nav] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]])) - -(defn show-chat [navigator whisper-identity] - (nav/nav-push navigator {:view-id :chat})) - -(defn contact-view [{:keys [navigator contact]}] - (let [{:keys [whisper-identity]} contact] - [touchable-highlight {:onPress (fn [] - (show-chat navigator whisper-identity))} - [contact-inner-view contact]])) diff --git a/src/syng_im/components/contact_list/contact_inner.cljs b/src/syng_im/components/contact_list/contact_inner.cljs deleted file mode 100644 index c047551c57..0000000000 --- a/src/syng_im/components/contact_list/contact_inner.cljs +++ /dev/null @@ -1,104 +0,0 @@ -(ns syng-im.components.contact-list.contact-inner - (:require [clojure.string :as s] - [syng-im.components.react :refer [view image text]] - [syng-im.resources :as res])) - -(defn contact-photo [{:keys [photo-path]}] - [view {:width 54 - :height 54 - :borderRadius 50 - :backgroundColor "#FFFFFF" - :elevation 6} - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style {:borderWidth 2 - :borderColor "#FFFFFF" - :borderRadius 50 - :width 54 - :height 54 - :position "absolute"}}]]) - -(defn contact-online [{:keys [online]}] - (when online - [view {:position "absolute" - :top 41 - :left 36 - :width 12 - :height 12 - :borderRadius 50 - :backgroundColor "#FFFFFF" - :elevation 6} - [image {:source res/online-icon - :style {:width 12 - :height 12}}]])) - -(defn contact-inner-view [{:keys [name photo-path delivery-status datetime new-messages-count - online whisper-identity]}] - [view {:style {:flexDirection "row" - :marginTop 5 - :marginBottom 5 - :paddingLeft 15 - :paddingRight 15 - :height 75}} - [view {:width 54 - :height 54} - ;;; photo - [contact-photo {:photo-path photo-path}] - ;;; online - [contact-online {:online online}]] - [view {:style {:flexDirection "column" - :marginLeft 7 - :marginRight 10 - :flex 1 - :position "relative"}} - ;;; name - [text {:style {:fontSize 15 - :fontFamily "Avenir-Roman"}} name] - ;;; last message - [text {:style {:color "#AAB2B2" - :fontFamily "Avenir-Roman" - :fontSize 14 - :marginTop 2 - :paddingRight 10}} - (str "Hi, I'm " name)]] - [view {:style {:flexDirection "column"}} - ;;; delivery status - [view {:style {:flexDirection "row" - :position "absolute" - :top 0 - :right 0}} - (when delivery-status - [image {:source (if (= (keyword delivery-status) :seen) - res/seen-icon - res/delivered-icon) - :style {:marginTop 5}}]) - ;;; datetime - [text {:style {:fontFamily "Avenir-Roman" - :fontSize 11 - :color "#AAB2B2" - :letterSpacing 1 - :lineHeight 15 - :marginLeft 5}} - datetime]] - ;;; new messages count - (when (< 0 new-messages-count) - [view {:style {:position "absolute" - :right 0 - :bottom 24 - :width 18 - :height 18 - :backgroundColor "#6BC6C8" - :borderColor "#FFFFFF" - :borderRadius 50 - :alignSelf "flex-end"}} - [text {:style {:width 18 - :height 17 - :fontFamily "Avenir-Roman" - :fontSize 10 - :color "#FFFFFF" - :lineHeight 19 - :textAlign "center" - :top 1}} - new-messages-count]])]]) - diff --git a/src/syng_im/components/contact_list/contact_list.cljs b/src/syng_im/components/contact_list/contact_list.cljs deleted file mode 100644 index d2aeb9b321..0000000000 --- a/src/syng_im/components/contact_list/contact_list.cljs +++ /dev/null @@ -1,37 +0,0 @@ -(ns syng-im.components.contact-list.contact-list - (:require-macros - [natal-shell.data-source :refer [data-source clone-with-rows]] - [natal-shell.core :refer [with-error-view]]) - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight - navigator list-view toolbar-android - list-item]] - [syng-im.components.contact-list.contact :refer [contact-view]] - [syng-im.resources :as res] - [syng-im.utils.logging :as log])) - -(defn render-row [navigator row section-id row-id] - (list-item [contact-view {:navigator navigator - :contact (js->clj row :keywordize-keys true)}])) - -(defn get-data-source [contacts] - (clone-with-rows (data-source {:rowHasChanged (fn [row1 row2] - (not= row1 row2))}) - contacts)) - -(defn contact-list [{:keys [navigator]}] - (let [contacts (subscribe [:get-contacts])] - (fn [] - (let [contacts-ds (get-data-source @contacts)] - [view {:style {:flex 1 - :backgroundColor "white"}} - [toolbar-android {:logo res/logo-icon - :title "Contacts" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2}}] - (when contacts-ds - [list-view {:dataSource contacts-ds - :renderRow (partial render-row navigator) - :style {:backgroundColor "white"}}])])))) diff --git a/src/syng_im/components/icons/ionicons.cljs b/src/syng_im/components/icons/ionicons.cljs deleted file mode 100644 index 2b76fe5233..0000000000 --- a/src/syng_im/components/icons/ionicons.cljs +++ /dev/null @@ -1,6 +0,0 @@ -(ns syng-im.components.icons.ionicons - (:require [reagent.core :as r])) - -(set! js/window.Ionicons (js/require "react-native-vector-icons/Ionicons")) - -(def icon (r/adapt-react-class js/Ionicons)) diff --git a/src/syng_im/components/invertible_scroll_view.cljs b/src/syng_im/components/invertible_scroll_view.cljs deleted file mode 100644 index fea247863c..0000000000 --- a/src/syng_im/components/invertible_scroll_view.cljs +++ /dev/null @@ -1,8 +0,0 @@ -(ns syng-im.components.invertible-scroll-view) - -(set! js/window.InvertibleScrollView (js/require "react-native-invertible-scroll-view")) - -(defn invertible-scroll-view [props] - (js/React.createElement js/InvertibleScrollView - (clj->js (merge {:inverted true} props)))) - diff --git a/src/syng_im/components/item_checkbox.cljs b/src/syng_im/components/item_checkbox.cljs deleted file mode 100644 index 3ddea93b7f..0000000000 --- a/src/syng_im/components/item_checkbox.cljs +++ /dev/null @@ -1,7 +0,0 @@ -(ns syng-im.components.item-checkbox - (:require [reagent.core :as r])) - -(set! js/window.ItemCheckbox (js/require "react-native-circle-checkbox")) - -(def item-checkbox (r/adapt-react-class js/ItemCheckbox)) - diff --git a/src/syng_im/components/react.cljs b/src/syng_im/components/react.cljs deleted file mode 100644 index a9f31957b7..0000000000 --- a/src/syng_im/components/react.cljs +++ /dev/null @@ -1,60 +0,0 @@ -(ns syng-im.components.react - (:require [reagent.core :as r])) - -(set! js/window.React (js/require "react-native")) - -(def app-registry (.-AppRegistry js/React)) -(def navigator (r/adapt-react-class (.-Navigator js/React))) -(def text (r/adapt-react-class (.-Text js/React))) -(def view (r/adapt-react-class (.-View js/React))) -(def image (r/adapt-react-class (.-Image js/React))) -(def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React))) -(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) -(def list-view (r/adapt-react-class (.-ListView js/React))) -(def text-input (r/adapt-react-class (.-TextInput js/React))) - -(def platform (.. js/React -Platform -OS)) - -(def android? (= platform "android")) - -(defn list-item [component] - (r/as-element component)) - -(comment - (.-width (.get (.. js/React -Dimensions) "window")) - ) - - -;; (do -;; (def activity-indicator-ios (r/adapt-react-class (.-ActivityIndicatorIOS js/React))) -;; (def animated-image (r/adapt-react-class (.-Animated.Image js/React))) -;; (def animated-text (r/adapt-react-class (.-Animated.Text js/React))) -;; (def animated-view (r/adapt-react-class (.-Animated.View js/React))) -;; (def date-picker-ios (r/adapt-react-class (.-DatePickerIOS js/React))) -;; (def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React))) -;; (def image (r/adapt-react-class (.-Image js/React))) -;; (def list-view (r/adapt-react-class (.-ListView js/React))) -;; (def map-view (r/adapt-react-class (.-MapView js/React))) -;; (def modal (r/adapt-react-class (.-Modal js/React))) -;; (def navigator (r/adapt-react-class (.-Navigator js/React))) -;; (def navigator-ios (r/adapt-react-class (.-NavigatorIOS js/React))) -;; (def picker-ios (r/adapt-react-class (.-PickerIOS js/React))) -;; (def progress-bar-android (r/adapt-react-class (.-ProgressBarAndroid js/React))) -;; (def progress-view-ios (r/adapt-react-class (.-ProgressViewIOS js/React))) -;; (def pull-to-refresh-view-android (r/adapt-react-class (.-PullToRefreshViewAndroid js/React))) -;; (def scroll-view (r/adapt-react-class (.-ScrollView js/React))) -;; (def segmented-control-ios (r/adapt-react-class (.-SegmentedControlIOS js/React))) -;; (def slider-ios (r/adapt-react-class (.-SliderIOS js/React))) -;; (def switch (r/adapt-react-class (.-Switch js/React))) -;; (def tab-bar-ios (r/adapt-react-class (.-TabBarIOS js/React))) -;; (def tab-bar-ios-item (r/adapt-react-class (.-TabBarIOS.Item js/React))) -;; (def text (r/adapt-react-class (.-Text js/React))) -;; (def text-input (r/adapt-react-class (.-TextInput js/React))) -;; (def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) -;; (def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React))) -;; (def touchable-native-feedback (r/adapt-react-class (.-TouchableNativeFeedback js/React))) -;; (def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React))) -;; (def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React))) -;; (def view (r/adapt-react-class (.-View js/React))) -;; (def view-pager-android (r/adapt-react-class (.-ViewPagerAndroid js/React))) -;; (def web-view (r/adapt-react-class (.-WebView js/React)))) diff --git a/src/syng_im/components/realm.cljs b/src/syng_im/components/realm.cljs deleted file mode 100644 index 524c2f6909..0000000000 --- a/src/syng_im/components/realm.cljs +++ /dev/null @@ -1,18 +0,0 @@ -(ns syng-im.components.realm - (:require [reagent.core :as r])) - -(set! js/window.RealmReactNative (js/require "realm/react-native")) - -(def list-view (r/adapt-react-class (.-ListView js/RealmReactNative))) - -(comment - - - ;(set! js/wat (js/require "realm.react-native.ListView")) - ;(.-Results js/Realm) - ; - ;(r/realm) - ; - ;(require '[syng-im.persistence.realm :as r]) - - ) diff --git a/src/syng_im/components/sign_up.cljs b/src/syng_im/components/sign_up.cljs deleted file mode 100644 index 917365b5d3..0000000000 --- a/src/syng_im/components/sign_up.cljs +++ /dev/null @@ -1,59 +0,0 @@ -(ns syng-im.components.sign-up - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight - toolbar-android text-input]] - [syng-im.components.spinner :refer [spinner]] - [syng-im.resources :as res] - [syng-im.navigation :as nav] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.phone-number :refer [format-phone-number]])) - -(defn show-confirm-view [navigator] - (dispatch [:set-loading false]) - (nav/nav-replace navigator {:view-id :sign-up-confirm})) - -(defn sign-up [user-phone-number user-identity navigator] - (dispatch [:set-loading true]) - (dispatch [:sign-up user-phone-number user-identity #(show-confirm-view navigator)])) - -(defn update-phone-number [value] - (let [formatted (format-phone-number value)] - (dispatch [:set-user-phone-number formatted]))) - -(defn sign-up-view [{:keys [navigator]}] - (let [loading (subscribe [:get-loading]) - user-phone-number (subscribe [:get-user-phone-number]) - user-identity (subscribe [:get-user-identity])] - (fn [] - [view {:style {:flex 1}} - [view {:style {:flex 1 - :backgroundColor "white"}} - [toolbar-android {:logo res/logo-icon - :title "Sign up" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2}}] - [view {} - [text-input {:underlineColorAndroid "#9CBFC0" - :placeholder "Enter your phone number" - :keyboardType "phone-pad" - :onChangeText (fn [value] - (update-phone-number value)) - :style {:flex 1 - :marginHorizontal 18 - :lineHeight 42 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "#9CBFC0"}} - @user-phone-number] - [touchable-highlight {:onPress #(sign-up @user-phone-number @user-identity navigator) - :style {:alignSelf "center" - :borderRadius 7 - :backgroundColor "#E5F5F6" - :width 100}} - [text {:style {:marginVertical 10 - :textAlign "center"}} - "Sign up"]]]] - (when (or @loading (not @user-identity)) - [spinner {:visible true}])]))) diff --git a/src/syng_im/components/sign_up_confirm.cljs b/src/syng_im/components/sign_up_confirm.cljs deleted file mode 100644 index 4bfba81cd2..0000000000 --- a/src/syng_im/components/sign_up_confirm.cljs +++ /dev/null @@ -1,86 +0,0 @@ -(ns syng-im.components.sign-up-confirm - (:require-macros - [natal-shell.core :refer [with-error-view]]) - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight list-view - toolbar-android text-input]] - [syng-im.components.spinner :refer [spinner]] - [syng-im.resources :as res] - [syng-im.navigation :as nav] - [syng-im.utils.utils :refer [log toast http-post]])) - -(defn show-home-view [navigator] - (dispatch [:set-loading false]) - (nav/nav-replace navigator {:view-id :contact-list})) - -(defn sync-contacts [navigator] - (dispatch [:sync-contacts #(show-home-view navigator)])) - -(defn on-send-code-response [navigator body] - (log body) - (toast (if (:confirmed body) - "Confirmed" - "Wrong code")) - (if (:confirmed body) - ;; TODO user action required - (sync-contacts navigator) - (dispatch [:set-loading false]))) - -(defn code-valid? [code] - (= 4 (count code))) - -(defn send-code [code navigator] - (when (code-valid? code) - (dispatch [:set-loading true]) - (dispatch [:sign-up-confirm code (partial on-send-code-response navigator)]))) - -(defn update-code [value] - (let [formatted value] - (dispatch [:set-confirmation-code formatted]))) - -(defn sign-up-confirm-view [{:keys [navigator]}] - (let [loading (subscribe [:get-loading]) - confirmation-code (subscribe [:get-confirmation-code])] - (fn [] - [view {:style {:flex 1}} - [view {:style {:flex 1 - :backgroundColor "white"}} - [toolbar-android {:logo res/logo-icon - :title "Confirm" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2}}] - [view {} - [text-input {:underlineColorAndroid "#9CBFC0" - :placeholder "Enter confirmation code" - :keyboardType "number-pad" - :maxLength 4 - :onChangeText (fn [value] - (update-code value)) - :style {:flex 1 - :marginHorizontal 18 - :lineHeight 42 - :fontSize 14 - :fontFamily "Avenir-Roman" - :color "#9CBFC0"}} - @confirmation-code] - (if (code-valid? @confirmation-code) - [touchable-highlight {:onPress #(send-code @confirmation-code navigator) - :style {:alignSelf "center" - :borderRadius 7 - :backgroundColor "#E5F5F6" - - :width 100}} - [text {:style {:marginVertical 10 - :textAlign "center"}} - "Confirm"]] - [view {:style {:alignSelf "center" - :borderRadius 7 - :backgroundColor "#AAB2B2" - :width 100}} - [text {:style {:marginVertical 10 - :textAlign "center"}} - "Confirm"]])]] - (when @loading - [spinner {:visible true}])]))) diff --git a/src/syng_im/components/spinner.cljs b/src/syng_im/components/spinner.cljs deleted file mode 100644 index 1526967e10..0000000000 --- a/src/syng_im/components/spinner.cljs +++ /dev/null @@ -1,6 +0,0 @@ -(ns syng-im.components.spinner - (:require [reagent.core :as r])) - -(def react-spinner (.-default (js/require "react-native-loading-spinner-overlay"))) - -(def spinner (r/adapt-react-class react-spinner)) diff --git a/src/syng_im/constants.cljs b/src/syng_im/constants.cljs deleted file mode 100644 index a41bc7251a..0000000000 --- a/src/syng_im/constants.cljs +++ /dev/null @@ -1,35 +0,0 @@ -(ns syng-im.constants) - -(def ethereum-rpc-url "http://localhost:8545") - -(def server-address "http://rpc0.syng.im:20000/") -;; (def server-address "http://10.0.3.2:3000/") - -(def text-content-type "text/plain") -(def content-type-command "command") - -(def group-chat-colors [{:background "#AB7967", :text "#FFFFFF"} - {:background "#B48EAD", :text "#FFFFFF"} - {:background "#8FA1B3", :text "#FFFFFF"} - {:background "#96B5B4", :text "#FFFFFF"} - {:background "#A3BE8C", :text "#FFFFFF"} - {:background "#EBCB8B", :text "#FFFFFF"} - {:background "#D08770", :text "#FFFFFF"} - {:background "#BF616A", :text "#FFFFFF"} - {:background "#EFF1F5", :text "#000000"} - {:background "#DFE1E8", :text "#000000"} - {:background "#C0C5CE", :text "#000000"} - {:background "#A7ADBA", :text "#000000"} - {:background "#65737E", :text "#FFFFFF"} - {:background "#4F5B66", :text "#FFFFFF"} - {:background "#343D46", :text "#FFFFFF"} - {:background "#2B303B", :text "#FFFFFF"}]) - -(comment - - (map (fn [c] - {:background c - :foreground c}) group-chat-colors) - (reverse group-chat-colors) - - ) diff --git a/src/syng_im/db.cljs b/src/syng_im/db.cljs deleted file mode 100644 index b34cfcd49e..0000000000 --- a/src/syng_im/db.cljs +++ /dev/null @@ -1,32 +0,0 @@ -(ns syng-im.db - (:require [schema.core :as s :include-macros true])) - -;; schema of app-db -(def schema {:greeting s/Str}) - -;; initial state of app-db -(def app-db {:greeting "Hello Clojure in iOS and Android!" - :identity-password "replace-me-with-user-entered-password" - :contacts [] - :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" - :command nil} - :chats {} - :chats-updated-signal 0 - :new-group #{} - :new-participants #{}}) - - -(def protocol-initialized-path [:protocol-initialized]) -(def identity-password-path [:identity-password]) -(def current-chat-id-path [:chat :current-chat-id]) -(def updated-chats-signal-path [:chats-updated-signal]) -(defn updated-chat-signal-path [chat-id] - [:chats chat-id :chat-updated-signal]) -(defn chat-input-text-path [chat-id] - [:chats chat-id :input-text]) -(defn chat-command-path [chat-id] - [:chats chat-id :command-input :command]) -(defn chat-command-content-path [chat-id] - [:chats chat-id :command-input :content]) -(def new-group-path [:new-group]) -(def new-participants-path [:new-participants]) diff --git a/src/syng_im/handlers.cljs b/src/syng_im/handlers.cljs deleted file mode 100644 index 76077ab3b6..0000000000 --- a/src/syng_im/handlers.cljs +++ /dev/null @@ -1,426 +0,0 @@ -(ns syng-im.handlers - (:require - [re-frame.core :refer [register-handler after dispatch]] - [schema.core :as s :include-macros true] - [syng-im.db :refer [app-db schema]] - [syng-im.protocol.api :refer [init-protocol]] - [syng-im.protocol.protocol-handler :refer [make-handler]] - [syng-im.models.protocol :refer [update-identity - set-initialized]] - [syng-im.models.user-data :as user-data] - [syng-im.models.contacts :as contacts] - [syng-im.models.messages :refer [save-message - update-message! - message-by-id]] - [syng-im.handlers.server :as server] - [syng-im.handlers.contacts :as contacts-service] - [syng-im.handlers.commands :refer [set-chat-command - set-chat-command-content]] - [syng-im.handlers.sign-up :as sign-up-service] - - [syng-im.models.chats :refer [create-chat - chat-add-participants - chat-remove-participants - set-chat-active]] - [syng-im.models.chat :refer [signal-chat-updated - set-current-chat-id - current-chat-id - update-new-group-selection - update-new-participants-selection - clear-new-group - clear-new-participants - new-group-selection - set-chat-input-text - new-participants-selection]] - [syng-im.utils.logging :as log] - [syng-im.protocol.api :as api] - [syng-im.constants :refer [text-content-type]] - [syng-im.navigation :refer [nav-push - nav-replace - nav-pop]] - [syng-im.utils.crypt :refer [gen-random-bytes]] - [syng-im.utils.random :as random])) - -;; -- Middleware ------------------------------------------------------------ -;; -;; See https://github.com/Day8/re-frame/wiki/Using-Handler-Middleware -;; -(defn check-and-throw - "throw an exception if db doesn't match the schema." - [a-schema db] - (if-let [problems (s/check a-schema db)] - (throw (js/Error. (str "schema check failed: " problems))))) - -(def validate-schema-mw - (after (partial check-and-throw schema))) - - -;; -- Common -------------------------------------------------------------- - -(register-handler :initialize-db - (fn [_ _] - app-db)) - -(register-handler :set-loading - (fn [db [_ value]] - (assoc db :loading value))) - -(register-handler :initialize-crypt - (fn [db _] - (log/debug "initializing crypt") - (gen-random-bytes 1024 (fn [{:keys [error buffer]}] - (if error - (do - (log/error "Failed to generate random bytes to initialize sjcl crypto") - (dispatch [:notify-user {:type :error - :error error}])) - (do - (->> (.toString buffer "hex") - (.toBits (.. js/ecc -sjcl -codec -hex)) - (.addEntropy (.. js/ecc -sjcl -random))) - (dispatch [:crypt-initialized]))))) - db)) - -(register-handler :crypt-initialized - (fn [db _] - (log/debug "crypt initialized") - db)) - -(register-handler :navigate-to - (fn [db [action navigator route nav-type]] - (log/debug action route) - (case nav-type - :push (nav-push navigator route) - :replace (nav-replace navigator route)) - db)) - -;; -- Protocol -------------------------------------------------------------- - -(register-handler :initialize-protocol - (fn [db [_]] - (init-protocol (make-handler db)) - db)) - -(register-handler :protocol-initialized - (fn [db [_ identity]] - (-> db - (update-identity identity) - (set-initialized true)))) - -(register-handler :received-msg - (fn [db [action {chat-id :from - msg-id :msg-id :as msg}]] - (log/debug action "msg" msg) - (save-message chat-id msg) - (-> db - (create-chat chat-id [chat-id] false) - (signal-chat-updated chat-id)))) - -(register-handler :group-received-msg - (fn [db [action {chat-id :group-id :as msg}]] - (log/debug action "msg" msg) - (save-message chat-id msg) - (signal-chat-updated db chat-id))) - -(defn joined-chat-msg [chat-id from msg-id] - (let [contact-name (:name (contacts/contact-by-identity from))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or contact-name from) " received chat invitation") - :content-type text-content-type}))) - -(defn participant-invited-to-group-msg [chat-id identity from msg-id] - (let [inviter-name (:name (contacts/contact-by-identity from)) - invitee-name (:name (contacts/contact-by-identity identity))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or inviter-name from) " invited " (or invitee-name identity)) - :content-type text-content-type}))) - -(defn participant-removed-from-group-msg [chat-id identity from msg-id] - (let [remover-name (:name (contacts/contact-by-identity from)) - removed-name (:name (contacts/contact-by-identity identity))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or remover-name from) " removed " (or removed-name identity)) - :content-type text-content-type}))) - -(defn you-removed-from-group-msg [chat-id from msg-id] - (let [remover-name (:name (contacts/contact-by-identity from))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or remover-name from) " removed you from group chat") - :content-type text-content-type}))) - -(defn participant-left-group-msg [chat-id from msg-id] - (let [left-name (:name (contacts/contact-by-identity from))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or left-name from) " left") - :content-type text-content-type}))) - -(defn removed-participant-msg [chat-id identity] - (let [contact-name (:name (contacts/contact-by-identity identity))] - (save-message chat-id {:from "system" - :msg-id (random/id) - :content (str "You've removed " (or contact-name identity)) - :content-type text-content-type}))) - -(defn left-chat-msg [chat-id] - (save-message chat-id {:from "system" - :msg-id (random/id) - :content "You left this chat" - :content-type text-content-type})) - -(register-handler :group-chat-invite-acked - (fn [db [action from group-id ack-msg-id]] - (log/debug action from group-id ack-msg-id) - (joined-chat-msg group-id from ack-msg-id) - (signal-chat-updated db group-id))) - -(register-handler :participant-removed-from-group - (fn [db [action from group-id identity msg-id]] - (log/debug action msg-id from group-id identity) - (chat-remove-participants group-id [identity]) - (participant-removed-from-group-msg group-id identity from msg-id) - (signal-chat-updated db group-id))) - -(register-handler :you-removed-from-group - (fn [db [action from group-id msg-id]] - (log/debug action msg-id from group-id) - (you-removed-from-group-msg group-id from msg-id) - (set-chat-active group-id false) - (signal-chat-updated db group-id))) - -(register-handler :participant-left-group - (fn [db [action from group-id msg-id]] - (log/debug action msg-id from group-id) - (participant-left-group-msg group-id from msg-id) - (signal-chat-updated db group-id))) - -(register-handler :participant-invited-to-group - (fn [db [action from group-id identity msg-id]] - (log/debug action msg-id from group-id identity) - (participant-invited-to-group-msg group-id identity from msg-id) - (signal-chat-updated db group-id))) - -(register-handler :acked-msg - (fn [db [action from msg-id]] - (log/debug action from msg-id) - (update-message! {:msg-id msg-id - :delivery-status :delivered}) - (signal-chat-updated db from))) - -(register-handler :msg-delivery-failed - (fn [db [action msg-id]] - (log/debug action msg-id) - (update-message! {:msg-id msg-id - :delivery-status :failed}) - (let [{:keys [chat-id]} (message-by-id msg-id)] - (signal-chat-updated db chat-id)))) - -(register-handler :send-chat-msg - (fn [db [action chat-id text]] - (log/debug action "chat-id" chat-id "text" text) - (let [msg (if (= chat-id "console") - (sign-up-service/send-console-msg text) - (let [{msg-id :msg-id - {from :from - to :to} :msg} (api/send-user-msg {:to chat-id - :content text})] - {:msg-id msg-id - :from from - :to to - :content text - :content-type text-content-type - :outgoing true}))] - (save-message chat-id msg) - (signal-chat-updated db chat-id)))) - -(register-handler :leave-group-chat - (fn [db [action navigator]] - (log/debug action) - (let [chat-id (current-chat-id db)] - (api/leave-group-chat chat-id) - (set-chat-active chat-id false) - (left-chat-msg chat-id) - (signal-chat-updated db chat-id)))) - -(register-handler :send-chat-command - (fn [db [action chat-id command content]] - (log/debug action "chat-id" chat-id "command" command "content" content) - (let [msg (if (= chat-id "console") - (sign-up-service/send-console-command command content) - ;; TODO handle command, now sends as plain message - (let [{msg-id :msg-id - {from :from - to :to} :msg} (api/send-user-msg {:to chat-id - :content content})] - {:msg-id msg-id - :from from - :to to - :content content - :content-type text-content-type - :outgoing true}))] - (save-message chat-id msg) - (signal-chat-updated db chat-id)))) - -(register-handler :send-group-chat-msg - (fn [db [action chat-id text]] - (log/debug action "chat-id" chat-id "text" text) - (let [{msg-id :msg-id - {from :from} :msg} (api/send-group-user-msg {:group-id chat-id - :content text}) - msg {:msg-id msg-id - :from from - :to nil - :content text - :content-type text-content-type - :outgoing true}] - (save-message chat-id msg) - (signal-chat-updated db chat-id)))) - -;; -- User data -------------------------------------------------------------- - -(register-handler :set-user-phone-number - (fn [db [_ value]] - (assoc db :user-phone-number value))) - -(register-handler :load-user-phone-number - (fn [db [_]] - (user-data/load-phone-number) - db)) - -;; -- Sign up -------------------------------------------------------------- - -(register-handler :sign-up - (fn [db [_ phone-number whisper-identity handler]] - (server/sign-up phone-number whisper-identity handler) - db)) - -(register-handler :set-confirmation-code - (fn [db [_ value]] - (assoc db :confirmation-code value))) - -(register-handler :sign-up-confirm - (fn [db [_ confirmation-code handler]] - (server/sign-up-confirm confirmation-code handler) - db)) - -(register-handler :sync-contacts - (fn [db [_ handler]] - (contacts-service/sync-contacts handler) - db)) - -;; -- Contacts -------------------------------------------------------------- - -(register-handler :load-syng-contacts - (fn [db [_ value]] - (contacts/load-syng-contacts db))) - -;; -- Chats -------------------------------------------------------------- - -(register-handler :show-chat - (fn [db [action chat-id navigator nav-type]] - (log/debug action "chat-id" chat-id) - (let [db (set-current-chat-id db chat-id)] - (dispatch [:navigate-to navigator {:view-id :chat} nav-type]) - db))) - -(register-handler :set-sign-up-chat - (fn [db [_]] - (-> db - (set-current-chat-id "console") - sign-up-service/intro))) - -;; -- Chat -------------------------------------------------------------- - -(register-handler :set-chat-input-text - (fn [db [_ text]] - (set-chat-input-text db text))) - -(register-handler :set-chat-command - (fn [db [_ command-key]] - (set-chat-command db command-key))) - -(register-handler :set-chat-command-content - (fn [db [_ content]] - (set-chat-command-content db content))) - -(register-handler :show-contacts - (fn [db [action navigator]] - (log/debug action) - (nav-push navigator {:view-id :contact-list}) - db)) - -(register-handler :select-new-participant - (fn [db [action identity add?]] - (log/debug action identity add?) - (update-new-participants-selection db identity add?))) - -(register-handler :show-remove-participants - (fn [db [action navigator]] - (log/debug action) - (nav-push navigator {:view-id :remove-participants}) - (clear-new-participants db))) - -(register-handler :remove-selected-participants - (fn [db [action navigator]] - (log/debug action) - (let [identities (-> (new-participants-selection db) - (vec)) - chat-id (current-chat-id db)] - (chat-remove-participants chat-id identities) - (nav-pop navigator) - (doseq [ident identities] - (api/group-remove-participant chat-id ident) - (removed-participant-msg chat-id ident)) - (signal-chat-updated db chat-id)))) - -(register-handler :show-add-participants - (fn [db [action navigator]] - (log/debug action) - (nav-push navigator {:view-id :add-participants}) - (clear-new-participants db))) - -(register-handler :add-new-participants - (fn [db [action navigator]] - (log/debug action) - (let [identities (-> (new-participants-selection db) - (vec)) - chat-id (current-chat-id db)] - (chat-add-participants chat-id identities) - (nav-pop navigator) - (doseq [ident identities] - (api/group-add-participant chat-id ident)) - db))) - -(register-handler :show-group-new - (fn [db [action navigator]] - (log/debug action) - (nav-push navigator {:view-id :new-group}) - (clear-new-group db))) - -(register-handler :select-for-new-group - (fn [db [action identity add?]] - (log/debug action identity add?) - (update-new-group-selection db identity add?))) - -(register-handler :create-new-group - (fn [db [action group-name navigator]] - (log/debug action) - (let [identities (-> (new-group-selection db) - (vec)) - group-id (api/start-group-chat identities group-name) - db (create-chat db group-id identities true group-name)] - (dispatch [:show-chat group-id navigator :replace]) - db))) - -(register-handler :group-chat-invite-received - (fn [db [action from group-id identities group-name]] - (log/debug action from group-id identities) - (create-chat db group-id identities true group-name))) - -(comment - - ) diff --git a/src/syng_im/handlers/commands.cljs b/src/syng_im/handlers/commands.cljs deleted file mode 100644 index 1b6f2f2e67..0000000000 --- a/src/syng_im/handlers/commands.cljs +++ /dev/null @@ -1,16 +0,0 @@ -(ns syng-im.handlers.commands - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.db :as db] - [syng-im.models.commands :refer [get-command]] - [syng-im.utils.utils :refer [log on-error http-post]] - [syng-im.utils.logging :as log])) - -(defn set-chat-command-content [db content] - (assoc-in db (db/chat-command-content-path (get-in db db/current-chat-id-path)) - content)) - -(defn set-chat-command [db command-key] - (-> db - (set-chat-command-content nil) - (assoc-in (db/chat-command-path (get-in db db/current-chat-id-path)) - (get-command command-key)))) diff --git a/src/syng_im/handlers/contacts.cljs b/src/syng_im/handlers/contacts.cljs deleted file mode 100644 index bd21ffce70..0000000000 --- a/src/syng_im/handlers/contacts.cljs +++ /dev/null @@ -1,64 +0,0 @@ -(ns syng-im.handlers.contacts - (:require-macros [cljs.core.async.macros :refer [go]]) - (:require [clojure.string :as cstr] - [cljs.core.async :as async :refer [chan put! clj (:contacts data)))) - -(defn- get-contacts-by-hash [contacts] - (let [numbers-info (reduce (fn [numbers contact] - (into numbers - (map (fn [c] - {:number (format-phone-number (:number c)) - :contact contact}) - (:phone-numbers contact)))) - '() - contacts)] - (reduce (fn [m number-info] - (let [number (:number number-info) - hash (encrypt number)] - (assoc m hash number-info))) - {} - numbers-info))) - -(defn- request-syng-contacts [contacts] - (let [contacts-by-hash (get-contacts-by-hash contacts) - data (keys contacts-by-hash) - ch (chan)] - (http-post "get-contacts" {:phone-number-hashes data} - (fn [data] - (put! ch - (to-syng-contacts contacts-by-hash data)))) - ch)) - -(defn sync-contacts [handler] - (go - (let [result (> (db/updated-chat-signal-path chat-id) - (get-in db))) - -(defn update-new-group-selection [db identity add?] - (update-in db db/new-group-path (fn [new-group] - (if add? - (conj new-group identity) - (disj new-group identity))))) - -(defn update-new-participants-selection [db identity add?] - (update-in db db/new-participants-path (fn [new-participants] - (if add? - (conj new-participants identity) - (disj new-participants identity))))) - -(defn new-group-selection [db] - (get-in db db/new-group-path)) - -(defn clear-new-group [db] - (assoc-in db db/new-group-path #{})) - -(defn new-participants-selection [db] - (get-in db db/new-participants-path)) - -(defn clear-new-participants [db] - (assoc-in db db/new-participants-path #{})) - -(defn set-chat-input-text [db text] - (assoc-in db (db/chat-input-text-path (current-chat-id db)) text)) - -(comment - - (swap! re-frame.db/app-db (fn [db] - (signal-chat-updated db "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"))) - - (current-chat-id @re-frame.db/app-db) - ) diff --git a/src/syng_im/models/chats.cljs b/src/syng_im/models/chats.cljs deleted file mode 100644 index 12caa0ecce..0000000000 --- a/src/syng_im/models/chats.cljs +++ /dev/null @@ -1,144 +0,0 @@ -(ns syng-im.models.chats - (:require [syng-im.persistence.realm :as r] - [syng-im.utils.random :refer [timestamp]] - [clojure.string :refer [join blank?]] - [syng-im.db :as db] - [syng-im.utils.logging :as log] - [syng-im.constants :refer [group-chat-colors]] - [syng-im.persistence.realm-queries :refer [include-query]])) - -(defn signal-chats-updated [db] - (update-in db db/updated-chats-signal-path (fn [current] - (if current - (inc current) - 0)))) - -(defn chats-updated? [db] - (get-in db db/updated-chats-signal-path)) - -(defn chat-name-from-contacts [identities] - (let [chat-name (->> identities - (map (fn [identity] - (-> (r/get-by-field :contacts :whisper-identity identity) - (r/single-cljs) - :name))) - (filter identity) - (join ","))] - (when-not (blank? chat-name) - chat-name))) - -(defn get-chat-name [chat-id identities] - (or (chat-name-from-contacts identities) - chat-id)) - -(defn create-chat - ([db chat-id identities group-chat?] - (create-chat db chat-id identities group-chat? nil)) - ([db chat-id identities group-chat? chat-name] - (if (r/exists? :chats :chat-id chat-id) - db - (let [chat-name (or chat-name - (get-chat-name chat-id identities)) - _ (log/debug "creating chat" chat-name)] - (r/write - (fn [] - (let [contacts (mapv (fn [ident {:keys [background text]}] - {:identity ident - :background-color background - :text-color text}) identities group-chat-colors)] - (r/create :chats {:chat-id chat-id - :is-active true - :name chat-name - :group-chat group-chat? - :timestamp (timestamp) - :contacts contacts})))) - (signal-chats-updated db))))) - -(defn chats-list [] - (-> (r/get-all :chats) - (r/sorted :timestamp :desc))) - -(defn chat-by-id [chat-id] - (-> (r/get-by-field :chats :chat-id chat-id) - (r/single-cljs) - (r/list-to-array :contacts))) - -(defn chat-add-participants [chat-id identities] - (r/write - (fn [] - (let [contacts (-> (r/get-by-field :chats :chat-id chat-id) - (r/single) - (aget "contacts")) - colors-in-use (->> (.map contacts (fn [object index collection] - {:text-color (aget object "text-color") - :background-color (aget object "background-color")})) - (set)) - colors (->> group-chat-colors - (filter (fn [color] - (not (contains? colors-in-use color))))) - new-contacts (mapv (fn [ident {:keys [background text]}] - {:identity ident - :background-color background - :text-color text}) identities colors)] - (doseq [contact new-contacts] - (.push contacts (clj->js contact))))))) - -(defn chat-remove-participants [chat-id identities] - (r/write - (fn [] - (let [query (include-query :identity identities) - chat (-> (r/get-by-field :chats :chat-id chat-id) - (r/single))] - (-> (aget chat "contacts") - (r/filtered query) - (r/delete)))))) - -(defn active-group-chats [] - (let [results (-> (r/get-all :chats) - (r/filtered "group-chat = true && is-active = true"))] - (->> (.map results (fn [object index collection] - (aget object "chat-id"))) - (js->clj)))) - - -(defn set-chat-active [chat-id active?] - (r/write (fn [] - (-> (r/get-by-field :chats :chat-id chat-id) - (r/single) - (aset "is-active" active?))))) - -(comment - (active-group-chats) - - - (-> (r/get-by-field :chats :chat-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c") - (r/single) - (aget "contacts") - (.map (fn [object index collection] - object))) - - (-> (chat-by-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c") - :contacts - vals - vec) - - - (-> (aget (aget (chats-list) 0) "contacts") - (js->clj :keywordize-keys true) - ) - - (r/delete (chats-list)) - - (swap! re-frame.db/app-db signal-chats-updated) - - (create-chat "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" - ["0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"]) - - (+ 1 1) - - - - (swap! re-frame.db/app-db (fn [db] - (create-chat db "A group chat"))) - - ) \ No newline at end of file diff --git a/src/syng_im/models/commands.cljs b/src/syng_im/models/commands.cljs deleted file mode 100644 index 780f651a2c..0000000000 --- a/src/syng_im/models/commands.cljs +++ /dev/null @@ -1,64 +0,0 @@ -(ns syng-im.models.commands - (:require [clojure.string :refer [join split]] - [clojure.walk :refer [stringify-keys keywordize-keys]] - [cljs.core.async :as async :refer [chan put! !]] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.utils :refer [log toast]] - [syng-im.persistence.realm :as realm])) - -(def commands [{:command :money - :text "!money" - :description "Send money" - :color "#48ba30" - :suggestion true} - {:command :location - :text "!location" - :description "Send location" - :color "#9a5dcf" - :suggestion true} - {:command :phone - :text "!phone" - :description "Send phone number" - :color "#48ba30" - :suggestion true} - {:command :send - :text "!send" - :description "Send location" - :color "#9a5dcf" - :suggestion true} - {:command :request - :text "!request" - :description "Send request" - :color "#48ba30" - :suggestion true} - {:command :keypair-password - :text "!keypairPassword" - :description "" - :color "#019af0" - :suggestion false} - {:command :help - :text "!help" - :description "Help" - :color "#9a5dcf" - :suggestion true}]) - -(def suggestions (filterv :suggestion commands)) - -(defn get-command [command-key] - (first (filter #(= command-key (:command %)) commands))) - -(defn- map-to-str - [m] - (join ";" (map #(join "=" %) (stringify-keys m)))) - -(defn- str-to-map - [s] - (keywordize-keys (apply hash-map (split s #"[;=]")))) - -;; TODO store command key in separate field -(defn format-command-msg-content [command content] - (map-to-str {:command (name command) :content content})) - -;; TODO temp -(defn parse-command-msg-content [content] - (update (str-to-map content) :command #(get-command (keyword %)))) diff --git a/src/syng_im/models/contacts.cljs b/src/syng_im/models/contacts.cljs deleted file mode 100644 index 478f1a5e0e..0000000000 --- a/src/syng_im/models/contacts.cljs +++ /dev/null @@ -1,139 +0,0 @@ -(ns syng-im.models.contacts - (:require [cljs.core.async :as async :refer [chan put! !]] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.utils :refer [log toast]] - [syng-im.persistence.realm :as realm] - [syng-im.persistence.realm :as r] - [syng-im.persistence.realm-queries :refer [include-query - exclude-query]] - [clojure.string :as s])) - -;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 -(def fake-phone-contacts? true) -(def fake-contacts? false) - -(def react-native-contacts (js/require "react-native-contacts")) - -(defn- generate-contact [n] - {:name (str "Contact " n) - :photo-path "" - :phone-numbers [{:label "mobile" :number (apply str (repeat 7 n))}] - :delivery-status (if (< (rand) 0.5) :delivered :seen) - :datetime "15:30" - :new-messages-count (rand-int 3) - :online (< (rand) 0.5)}) - -(defn- generate-contacts [n] - (map generate-contact (range 1 (inc n)))) - -(defn load-phone-contacts [] - (let [ch (chan)] - (if fake-phone-contacts? - (put! ch {:error nil, :contacts (generate-contacts 10)}) - (.getAll react-native-contacts - (fn [error raw-contacts] - (put! ch - {:error error - :contacts - (when (not error) - (log raw-contacts) - (map (fn [contact] - (merge contact - (generate-contact 1) - {:name (:givenName contact) - :photo-path (:thumbnailPath contact) - :phone-numbers (:phoneNumbers contact)})) - (js->clj raw-contacts :keywordize-keys true)))})))) - ch)) - -(defn- get-contacts [] - (if fake-contacts? - [{:phone-number "123" - :whisper-identity "abc" - :name "fake" - :photo-path ""}] - (realm/get-list :contacts))) - -(defn load-syng-contacts [db] - (let [contacts (map (fn [contact] - (merge contact - {:delivery-status (if (< (rand) 0.5) :delivered :seen) - :datetime "15:30" - :new-messages-count (rand-int 3) - :online (< (rand) 0.5)})) - (get-contacts))] - (assoc db :contacts contacts))) - -(defn- create-contact [{:keys [phone-number whisper-identity name photo-path]}] - (realm/create :contacts - {:phone-number phone-number - :whisper-identity whisper-identity - :name (or name "") - :photo-path (or photo-path "")})) - -(defn- contact-exist? [contacts contact] - (some #(= (:phone-number contact) (:phone-number %)) contacts)) - -(defn- add-contacts [contacts] - (realm/write (fn [] - (let [db-contacts (get-contacts)] - (dorun (map (fn [contact] - (if (not (contact-exist? db-contacts contact)) - (create-contact contact) - ;; TODO else override? - )) - contacts)))))) - -(defn save-syng-contacts [syng-contacts] - (add-contacts syng-contacts)) - - -;;;;;;;;;;;;;;;;;;;;---------------------------------------------- - -(defn contacts-list [] - (-> (r/get-all :contacts) - (r/sorted :name :asc))) - -(defn contacts-list-exclude [exclude-idents] - (let [query (exclude-query :whisper-identity exclude-idents)] - (-> (r/get-all :contacts) - (r/filtered query) - (r/sorted :name :asc)))) - -(defn contacts-list-include [include-indents] - (let [query (include-query :whisper-identity include-indents)] - (-> (r/get-all :contacts) - (r/filtered query) - (r/sorted :name :asc)))) - -(defn contact-by-identity [identity] - (-> (r/get-by-field :contacts :whisper-identity identity) - (r/single-cljs))) - -(comment - - (r/write #(create-contact {:phone-number "0543072333" - :whisper-identity "0x043e3a8344049fb48fef030084212a9d41577a5dea18aeb4c8f285c16f783aa84e43f84c32eb8601e22827b12d5f93f14e545f9023034a0521dc18484bbbc44704" - :name "Mr. Bean" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0544828649" - :whisper-identity "0x04e9b01298dd12c4d8f0393d7890302b25762966d825158d1fdffe124703c0efcd7f23a6cf71c466ca50b2af3d54264ea5f224a19ba7775779c1ddbcb237258c5c" - :name "Mr. Batman" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0522222222" - :whisper-identity "0x0487954e7fa746d8cf787403c2c491aadad540b9bb1f0f7b8184792e91c33b6a394079295f5777ec6d4af9ad5ba24794b3ff1ec8be9ff6a708c85a163733192665" - :name "Mr. Eagle" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0533333333" - :whisper-identity "0x04e43e861a6dd99ad9eee7bd58af89dcaa430188ebec8698de7b7bad54573324fff4ac5cb9bb277af317efd7abfc917b91bf48cc41e40bf70062fd79400016a1f9" - :name "Mr. PiggyBear" - :photo-path ""})) - - (contacts-list) - - (:new-group @re-frame.db/app-db) - - ) \ No newline at end of file diff --git a/src/syng_im/models/messages.cljs b/src/syng_im/models/messages.cljs deleted file mode 100644 index 9517d14783..0000000000 --- a/src/syng_im/models/messages.cljs +++ /dev/null @@ -1,61 +0,0 @@ -(ns syng-im.models.messages - (:require [syng-im.persistence.realm :as r] - [cljs.reader :refer [read-string]] - [syng-im.utils.random :refer [timestamp]] - [syng-im.db :as db] - [syng-im.utils.logging :as log])) - -(defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false - to nil} :as msg}] - (log/debug "save-message" chat-id msg) - (when-not (r/exists? :msgs :msg-id msg-id) - (r/write - (fn [] - (r/create :msgs {:chat-id chat-id - :msg-id msg-id - :from from - :to to - :content content - :content-type content-type - :outgoing outgoing - :timestamp (timestamp) - :delivery-status nil} true))))) - -(defn get-messages [chat-id] - (-> (r/get-by-field :msgs :chat-id chat-id) - (r/sorted :timestamp :desc))) - -(defn message-by-id [msg-id] - (-> (r/get-by-field :msgs :msg-id msg-id) - (r/single-cljs))) - -(defn update-message! [{:keys [msg-id] :as msg}] - (log/debug "update-message!" msg) - (r/write - (fn [] - (when (r/exists? :msgs :msg-id msg-id) - (r/create :msgs msg true))))) - -(comment - - (update-message! {:msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f" - :delivery-status "seen2"}) - - (r/get-by-field :msgs :msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f") - - - (save-message "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154" - {:msg-id "153" - :content "hello!" - :content-type "text/plain"}) - - (get-messages* "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154") - - (get-messages "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd") - - (doseq [msg (get-messages* "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43")] - (r/delete msg)) - - @re-frame.db/app-db - - ) \ No newline at end of file diff --git a/src/syng_im/models/protocol.cljs b/src/syng_im/models/protocol.cljs deleted file mode 100644 index bb3bb3ca9f..0000000000 --- a/src/syng_im/models/protocol.cljs +++ /dev/null @@ -1,32 +0,0 @@ -(ns syng-im.models.protocol - (:require [cljs.reader :refer [read-string]] - [syng-im.protocol.state.storage :as s] - [syng-im.utils.encryption :refer [password-encrypt - password-decrypt]] - [syng-im.utils.types :refer [to-edn-string]] - [re-frame.db :refer [app-db]] - [syng-im.db :as db] - [syng-im.persistence.simple-kv-store :as kv] - [syng-im.utils.logging :as log])) - -(defn set-initialized [db initialized?] - (assoc-in db db/protocol-initialized-path initialized?)) - -(defn update-identity [db identity] - (let [password (get-in db db/identity-password-path) - encrypted (->> (to-edn-string identity) - (password-encrypt password))] - (s/put kv/kv-store :identity encrypted) - (assoc db :user-identity identity))) - -(defn stored-identity [db] - (let [encrypted (s/get kv/kv-store :identity) - password (get-in db db/identity-password-path)] - (when encrypted - (-> (password-decrypt password encrypted) - (read-string))))) - -(comment - - (stored-identity @re-frame.db/app-db) - ) \ No newline at end of file diff --git a/src/syng_im/models/user_data.cljs b/src/syng_im/models/user_data.cljs deleted file mode 100644 index 463a731b5a..0000000000 --- a/src/syng_im/models/user_data.cljs +++ /dev/null @@ -1,17 +0,0 @@ -(ns syng-im.models.user-data - (:require-macros - [natal-shell.async-storage :refer [get-item set-item]]) - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.utils :refer [log on-error toast]])) - -(defn save-phone-number [phone-number] - (set-item "user-phone-number" phone-number) - (dispatch [:set-user-phone-number phone-number])) - -(defn load-phone-number [] - (get-item "user-phone-number" - (fn [error value] - (if error - (on-error error) - (dispatch [:set-user-phone-number (when value - (str value))]))))) diff --git a/src/syng_im/navigation.cljs b/src/syng_im/navigation.cljs deleted file mode 100644 index 9c95aa81b2..0000000000 --- a/src/syng_im/navigation.cljs +++ /dev/null @@ -1,17 +0,0 @@ -(ns syng-im.navigation) - -(def ^{:dynamic true :private true} *nav-render* - "Flag to suppress navigator re-renders from outside om when pushing/popping." - true) - -(defn nav-pop [nav] - (binding [*nav-render* true] - (.pop nav))) - -(defn nav-push [nav route] - (binding [*nav-render* true] - (.push nav (clj->js route)))) - -(defn nav-replace [nav route] - (binding [*nav-render* true] - (.replace nav (clj->js route)))) diff --git a/src/syng_im/persistence/realm.cljs b/src/syng_im/persistence/realm.cljs deleted file mode 100644 index e6fd6990e5..0000000000 --- a/src/syng_im/persistence/realm.cljs +++ /dev/null @@ -1,134 +0,0 @@ -(ns syng-im.persistence.realm - (:require [cljs.reader :refer [read-string]] - [syng-im.utils.logging :as log] - [syng-im.utils.types :refer [to-string]]) - (:refer-clojure :exclude [exists?])) - -(set! js/window.Realm (js/require "realm")) - -(def opts {:schema [{:name :contacts - :primaryKey :whisper-identity - :properties {:phone-number {:type "string" - :optional true} - :whisper-identity "string" - :name {:type "string" - :optional true} - :photo-path {:type "string" - :optinal true}}} - {:name :kv-store - :primaryKey :key - :properties {:key "string" - :value "string"}} - {:name :msgs - :primaryKey :msg-id - :properties {:msg-id "string" - :from "string" - :to {:type "string" - :optional true} - :content "string" ;; TODO make it ArrayBuffer - :content-type "string" - :timestamp "int" - :chat-id {:type "string" - :indexed true} - :outgoing "bool" - :delivery-status {:type "string" - :optional true}}} - {:name :chat-contact - :properties {:identity "string" - :text-color "string" - :background-color "string"}} - {:name :chats - :primaryKey :chat-id - :properties {:chat-id "string" - :name "string" - :group-chat {:type "bool" - :indexed true} - :is-active "bool" - :timestamp "int" - :contacts {:type "list" - :objectType "chat-contact"}}}]}) - - -(def realm (js/Realm. (clj->js opts))) - -(def schema-by-name (->> (:schema opts) - (mapv (fn [{:keys [name] :as schema}] - [name schema])) - (into {}))) - -(defn field-type [schema-name field] - (let [field-def (get-in schema-by-name [schema-name :properties field])] - (if (map? field-def) - (:type field-def) - field-def))) - -(defn write [f] - (.write realm f)) - -(defn create - ([schema-name obj] - (create schema-name obj false)) - ([schema-name obj update?] - (.create realm (to-string schema-name) (clj->js obj) update?))) - -(defmulti to-query (fn [schema-name operator field value] - operator)) - -(defmethod to-query :eq [schema-name operator field value] - (let [value (to-string value) - query (str (name field) "=" (if (= "string" (field-type schema-name field)) - (str "\"" value "\"") - value))] - query)) - -(defn get-by-field [schema-name field value] - (let [q (to-query schema-name :eq field value)] - (-> (.objects realm (name schema-name)) - (.filtered q)))) - -(defn get-all [schema-name] - (.objects realm (to-string schema-name))) - -(defn sorted [results field-name order] - (.sorted results (to-string field-name) (if (= order :asc) - false - true))) - -(defn filtered [results filter-query] - (.filtered results filter-query)) - -(defn page [results from to] - (js/Array.prototype.slice.call results from to)) - -(defn single [result] - (-> (aget result 0))) - -(defn single-cljs [result] - (some-> (aget result 0) - (js->clj :keywordize-keys true))) - -(defn list-to-array [record list-field] - (assoc record list-field (-> (get record list-field) - vals - vec))) - -(defn decode-value [{:keys [key value]}] - (read-string value)) - -(defn delete [obj] - (.delete realm obj)) - -(defn exists? [schema-name field value] - (> (.-length (get-by-field schema-name field value)) - 0)) - -(defn get-count [objs] - (.-length objs)) - -(defn get-list [schema-name] - (vals (js->clj (.objects realm (to-string schema-name)) :keywordize-keys true))) - - -(comment - - ) \ No newline at end of file diff --git a/src/syng_im/persistence/realm_queries.cljs b/src/syng_im/persistence/realm_queries.cljs deleted file mode 100644 index 3b8309b329..0000000000 --- a/src/syng_im/persistence/realm_queries.cljs +++ /dev/null @@ -1,19 +0,0 @@ -(ns syng-im.persistence.realm-queries - (:require [clojure.string :as s] - [syng-im.utils.types :refer [to-string]])) - -(defn include-query [field-name values] - (->> values - (map (fn [val] - (str (to-string field-name) " == " (if (string? val) - (str "'" val "'") - val)))) - (s/join " || "))) - -(defn exclude-query [field-name values] - (->> values - (map (fn [val] - (str (to-string field-name) " != " (if (string? val) - (str "'" val "'") - val)))) - (s/join " && "))) \ No newline at end of file diff --git a/src/syng_im/persistence/simple_kv_store.cljs b/src/syng_im/persistence/simple_kv_store.cljs deleted file mode 100644 index 3781154d60..0000000000 --- a/src/syng_im/persistence/simple_kv_store.cljs +++ /dev/null @@ -1,25 +0,0 @@ -(ns syng-im.persistence.simple-kv-store - (:require [syng-im.protocol.state.storage :as st] - [syng-im.persistence.realm :as r] - [syng-im.utils.types :refer [to-edn-string]])) - -(defrecord SimpleKvStore [] - st/Storage - (put [_ key value] - (r/write - (fn [] - (r/create :kv-store {:key key - :value (to-edn-string value)} true)))) - (get [_ key] - (some-> (r/get-by-field :kv-store :key key) - (r/single-cljs) - (r/decode-value))) - (contains-key? [_ key] - (r/exists? :kv-store :key key)) - (delete [_ key] - (r/write (fn [] - (-> (r/get-by-field :kv-store :key key) - (r/single) - (r/delete)))))) - -(def kv-store (->SimpleKvStore)) diff --git a/src/syng_im/protocol/protocol_handler.cljs b/src/syng_im/protocol/protocol_handler.cljs deleted file mode 100644 index 8bb19c8f09..0000000000 --- a/src/syng_im/protocol/protocol_handler.cljs +++ /dev/null @@ -1,43 +0,0 @@ -(ns syng-im.protocol.protocol-handler - (:require [syng-im.utils.logging :as log] - [syng-im.constants :refer [ethereum-rpc-url]] - [re-frame.core :refer [dispatch]] - [syng-im.models.protocol :refer [stored-identity]] - [syng-im.persistence.simple-kv-store :as kv] - [syng-im.models.chats :refer [active-group-chats]])) - - -(defn make-handler [db] - {:ethereum-rpc-url ethereum-rpc-url - :identity (stored-identity db) - :active-group-ids (active-group-chats) - :storage kv/kv-store - :handler (fn [{:keys [event-type] :as event}] - (log/info "Event:" (clj->js event)) - (case event-type - :initialized (let [{:keys [identity]} event] - (dispatch [:protocol-initialized identity])) - :new-msg (let [{:keys [from to payload]} event] - (dispatch [:received-msg (assoc payload :from from :to to)])) - :msg-acked (let [{:keys [msg-id from]} event] - (dispatch [:acked-msg from msg-id])) - :delivery-failed (let [{:keys [msg-id]} event] - (dispatch [:msg-delivery-failed msg-id])) - :new-group-chat (let [{:keys [from group-id identities group-name]} event] - (dispatch [:group-chat-invite-received from group-id identities group-name])) - :new-group-msg (let [{from :from - group-id :group-id - payload :payload} event] - (dispatch [:group-received-msg (assoc payload :from from - :group-id group-id)])) - :group-chat-invite-acked (let [{:keys [from group-id ack-msg-id]} event] - (dispatch [:group-chat-invite-acked from group-id ack-msg-id])) - :group-new-participant (let [{:keys [group-id identity from msg-id]} event] - (dispatch [:participant-invited-to-group from group-id identity msg-id])) - :group-removed-participant (let [{:keys [group-id identity from msg-id]} event] - (dispatch [:participant-removed-from-group from group-id identity msg-id])) - :removed-from-group (let [{:keys [group-id from msg-id]} event] - (dispatch [:you-removed-from-group from group-id msg-id])) - :participant-left-group (let [{:keys [group-id from msg-id]} event] - (dispatch [:participant-left-group from group-id msg-id])) - (log/info "Don't know how to handle" event-type)))}) diff --git a/src/syng_im/subs.cljs b/src/syng_im/subs.cljs deleted file mode 100644 index 7239a549b3..0000000000 --- a/src/syng_im/subs.cljs +++ /dev/null @@ -1,134 +0,0 @@ -(ns syng-im.subs - (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub]] - [syng-im.db :as db] - [syng-im.models.chat :refer [current-chat-id - chat-updated?]] - [syng-im.models.chats :refer [chats-list - chats-updated? - chat-by-id]] - [syng-im.models.messages :refer [get-messages]] - [syng-im.models.contacts :refer [contacts-list - contacts-list-exclude - contacts-list-include]] - [syng-im.handlers.suggestions :refer [get-suggestions]])) - -;; -- Chat -------------------------------------------------------------- - -(register-sub :get-chat-messages - (fn [db _] - (let [chat-id (-> (current-chat-id @db) - (reaction)) - chat-updated (-> (chat-updated? @db @chat-id) - (reaction))] - (reaction - (let [_ @chat-updated] - (get-messages @chat-id)))))) - -(register-sub :get-current-chat-id - (fn [db _] - (-> (current-chat-id @db) - (reaction)))) - -(register-sub :get-suggestions - (fn [db _] - (let [input-text (reaction (get-in @db (db/chat-input-text-path (current-chat-id @db))))] - (reaction (get-suggestions @input-text))))) - -(register-sub :get-chat-input-text - (fn [db _] - (reaction (get-in @db (db/chat-input-text-path (current-chat-id @db)))))) - -(register-sub :get-chat-command - (fn [db _] - (reaction (get-in @db (db/chat-command-path (current-chat-id @db)))))) - -(register-sub :get-chat-command-content - (fn [db _] - (reaction (get-in @db (db/chat-command-content-path (current-chat-id @db)))))) - -;; -- Chats list -------------------------------------------------------------- - -(register-sub :get-chats - (fn [db _] - (let [chats-updated (-> (chats-updated? @db) - (reaction))] - (reaction - (let [_ @chats-updated] - (chats-list)))))) - -(register-sub :get-current-chat - (fn [db _] - (let [current-chat-id (-> (current-chat-id @db) - (reaction)) - chat-updated (-> (chat-updated? @db @current-chat-id) - (reaction))] - (reaction - (let [_ @chat-updated] - (when-let [chat-id @current-chat-id] - (chat-by-id chat-id))))))) - -;; -- User data -------------------------------------------------------------- - -(register-sub - :get-user-phone-number - (fn [db _] - (reaction - (get @db :user-phone-number)))) - -(register-sub - :get-user-identity - (fn [db _] - (reaction - (get @db :user-identity)))) - -(register-sub - :get-loading - (fn [db _] - (reaction - (get @db :loading)))) - -(register-sub - :get-confirmation-code - (fn [db _] - (reaction - (get @db :confirmation-code)))) - -(register-sub - :get-contacts - (fn [db _] - (reaction - (get @db :contacts)))) - -(register-sub :all-contacts - (fn [db _] - (reaction - (contacts-list)))) - -(register-sub :all-new-contacts - (fn [db _] - (let [current-chat-id (-> (current-chat-id @db) - (reaction)) - chat (-> (when-let [chat-id @current-chat-id] - (chat-by-id chat-id)) - (reaction))] - (reaction - (when @chat - (let [current-participants (->> @chat - :contacts - (map :identity))] - (contacts-list-exclude current-participants))))))) - -(register-sub :current-chat-contacts - (fn [db _] - (let [current-chat-id (-> (current-chat-id @db) - (reaction)) - chat (-> (when-let [chat-id @current-chat-id] - (chat-by-id chat-id)) - (reaction))] - (reaction - (when @chat - (let [current-participants (->> @chat - :contacts - (map :identity))] - (contacts-list-include current-participants))))))) diff --git a/src/syng_im/utils/crypt.cljs b/src/syng_im/utils/crypt.cljs deleted file mode 100644 index ecf3ef7ff7..0000000000 --- a/src/syng_im/utils/crypt.cljs +++ /dev/null @@ -1,25 +0,0 @@ -(ns syng-im.utils.crypt - (:require [goog.crypt :refer [byteArrayToHex]]) - (:import goog.crypt.Sha256)) - -(set! js/window.RnRandomBytes (js/require "react-native-randombytes")) - -(def sha-256 (Sha256.)) - -(defn bytes-to-str [arr] - (apply str (map char arr))) - -(defn str-to-bytes [s] - (map (comp int) s)) - -(defn encrypt [s] - (.reset sha-256) - (.update sha-256 s) - (-> (.digest sha-256) - byteArrayToHex)) - -(defn gen-random-bytes [length cb] - (.randomBytes js/window.RnRandomBytes length (fn [& [err buf]] - (if err - (cb {:error err}) - (cb {:buffer buf}))))) diff --git a/src/syng_im/utils/event.cljs b/src/syng_im/utils/event.cljs deleted file mode 100644 index bb47182867..0000000000 --- a/src/syng_im/utils/event.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.utils.event - (:require [cljs.core.async :refer [ (data-source {:rowHasChanged (fn [row1 row2] - (not= row1 row2))}) - (clone-with-rows items))) - -(defn to-realm-datasource [items] - (-> (js/RealmReactNative.ListView.DataSource. (cljs.core/clj->js {:rowHasChanged (fn [row1 row2] - (not= row1 row2))})) - (clone-with-rows items))) \ No newline at end of file diff --git a/src/syng_im/utils/phone_number.cljs b/src/syng_im/utils/phone_number.cljs deleted file mode 100644 index 6a7481faf4..0000000000 --- a/src/syng_im/utils/phone_number.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.utils.phone-number) - -(def i18n (js/require "react-native-i18n")) -(def locale (.-locale i18n)) -(def country-code (subs locale 3 5)) -(set! js/PhoneNumber (js/require "awesome-phonenumber")) - -(defn format-phone-number [number] - (str (.getNumber (js/PhoneNumber. number country-code "international")))) diff --git a/src/syng_im/utils/types.cljs b/src/syng_im/utils/types.cljs deleted file mode 100644 index 71d65e6dc6..0000000000 --- a/src/syng_im/utils/types.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.utils.types) - -(defn to-string [s] - (if (keyword? s) - (name s) - s)) - -(defn to-edn-string [value] - (with-out-str (pr value))) \ No newline at end of file diff --git a/src/syng_im/utils/utils.cljs b/src/syng_im/utils/utils.cljs deleted file mode 100644 index 03c88afd00..0000000000 --- a/src/syng_im/utils/utils.cljs +++ /dev/null @@ -1,36 +0,0 @@ -(ns syng-im.utils.utils - (:require-macros - [natal-shell.async-storage :refer [get-item set-item]] - [natal-shell.alert :refer [alert]] - [natal-shell.toast-android :as toast]) - (:require [syng-im.constants :as const])) - -(defn log [obj] - (.log js/console obj)) - -(defn toast [s] - (toast/show s (toast/long))) - -(defn on-error [error] - (toast (str "error: " error))) - -(defn http-post - ([action data on-success] - (http-post action data on-success nil)) - ([action data on-success on-error] - (-> (.fetch js/window - (str const/server-address action) - (clj->js {:method "POST" - :headers {:accept "application/json" - :content-type "application/json"} - :body (.stringify js/JSON (clj->js data))})) - (.then (fn [response] - (log response) - (.text response))) - (.then (fn [text] - (let [json (.parse js/JSON text) - obj (js->clj json :keywordize-keys true)] - (on-success obj)))) - (.catch (or on-error - (fn [error] - (toast (str error)))))))) diff --git a/test/clj/status_im/appium.clj b/test/clj/status_im/appium.clj new file mode 100644 index 0000000000..bf769a192a --- /dev/null +++ b/test/clj/status_im/appium.clj @@ -0,0 +1,102 @@ +(ns status-im.appium + (:require [clojure.java.io :as io] + [clojure.test :refer :all]) + (:import (org.openqa.selenium.remote DesiredCapabilities) + (org.openqa.selenium By) + (io.appium.java_client.android AndroidDriver) + (java.net URL) + (java.util.concurrent TimeUnit))) + + +(defn init [] + (let [dir (io/file (str (System/getProperty "user.dir") + "/android/app/build/outputs/apk")) + app (io/file dir "app-debug.apk") + capabilities (doto (DesiredCapabilities.) + (.setCapability "deviceName" "device") + (.setCapability "platformVersion" "6.0.0") + (.setCapability "app" (.getAbsolutePath app)) + (.setCapability "appPackage" "im.status.ethereum") + (.setCapability "appActivity" ".MainActivity")) + driver (AndroidDriver. (URL. "http://127.0.0.1:4723/wd/hub") capabilities)] + (-> driver + .manage + .timeouts + (.implicitlyWait 25 TimeUnit/SECONDS)) + driver)) + +(defn by-xpath [driver xpath] + (.findElement driver (By/xpath xpath))) + +(defn elements-by-xpath [driver xpath] + (.findElements driver (By/xpath xpath))) + +(defn by-id [driver id] + (.findElementByAccessibilityId driver (name id))) + +(defn get-element [driver id] + (if (keyword? id) + (by-id driver id) + (by-xpath driver id))) + +(defn click [driver id] + (.click (get-element driver id))) + +(defn write [driver input-xpath text] + (.sendKeys (get-element driver input-xpath) (into-array [text]))) + +(defn get-text [driver xpath] + (.getText (by-xpath driver xpath))) + +(defn xpath-by-text [text] + (str ".//*[@text='" text "']")) + +(defn click-by-text [driver text] + (let [elements (->> (xpath-by-text text) + (elements-by-xpath driver))] + (when (pos? (.size elements)) + (let [element (.get elements 0)] + (.click element))))) + +(defn contains-text [driver text] + (is (pos? (->> (xpath-by-text text) + (elements-by-xpath driver) + (.size))) + (format "Text \"%s\" was not found on screen." text))) + +(defn quit [driver] + (.quit driver)) + +(defmacro appium-test + "Defines test which will create new appium session and will pass that + session as first argument to each command inside it's body. After execution + of all commands session will be closed. + + For instance, + + (appium-test my-test + (click :button) + (write :input \"oops\")) + + will be expanded to + + (deftest my-test + (let [session (init)] + (click session :button) + (write session :input \"oops\") + (quit session)))" + [name & body] + (let [sym (gensym)] + `(deftest ~name + (let [~sym (init)] + (click-by-text ~sym "Continue") + ~@(for [[f & rest] body] + `(~f ~sym ~@rest)) + (quit ~sym))))) + +(defmacro defaction + [name parameters & body] + (let [session (gensym)] + `(defn ~name [~@(concat [session] parameters)] + ~@(for [[f & rest] body] + `(~f ~session ~@rest))))) diff --git a/test/clj/status_im/console.clj b/test/clj/status_im/console.clj new file mode 100644 index 0000000000..c375c3700d --- /dev/null +++ b/test/clj/status_im/console.clj @@ -0,0 +1,51 @@ +(ns status-im.console + (:require [clojure.test :refer :all] + [status-im.appium :refer :all])) + +(def message-text + (str "Your phone number is also required to use the app. Type" + " the exclamation mark or hit the icon to open the command " + "list and choose the !phone command")) + +(defaction send-sommand [] + (click :send-message) + (click :send-message)) + +(defaction respond-to-request + [request value] + (click (keyword (str "request-" (name request)))) + (write :input value) + (send-sommand)) + +(appium-test happy-case + (click :create-account) + (respond-to-request :keypair "123") + (contains-text message-text) + (respond-to-request :phone "+380671111111") + (respond-to-request :confirmation-code "1234") + (click :navigate-back) + (contains-text "Switch users")) + +(appium-test wrong-confirmation-code + (click :create-account) + (respond-to-request :keypair "123") + (respond-to-request :phone "+380671111111") + (respond-to-request :confirmation-code "432") + (contains-text "Wrong format") + (respond-to-request :confirmation-code "4321") + (contains-text "Wrong code!") + (write :input "1234") + (send-sommand) + (click :navigate-back) + (contains-text "Switch users")) + +(appium-test wrong-phone-number + (click :create-account) + (respond-to-request :keypair "123") + (respond-to-request :phone "+380671111111") + (write :input "+380671111112") + (send-sommand) + (write :input "1234") + (send-sommand) + (click :navigate-back) + (contains-text "Switch users")) diff --git a/test/cljs/status_im/test/commands/handlers.cljs b/test/cljs/status_im/test/commands/handlers.cljs new file mode 100644 index 0000000000..cf356be4e2 --- /dev/null +++ b/test/cljs/status_im/test/commands/handlers.cljs @@ -0,0 +1,20 @@ +(ns status-im.test.commands.handlers + (:require [cljs.test :refer-macros [deftest is]] + [status-im.commands.handlers.loading :as h])) + +(deftest test-validate-hash + (let [file "some-js" + db (-> {} + (assoc-in [:chats :user :dapp-hash] -731917028) + (assoc-in [:chats :user2 :dapp-hash] 123))] + (is (::h/valid-hash (h/validate-hash db [:user file]))) + (is (not (::h/valid-hash (h/validate-hash db [:user2 file])))) + (is (not (::h/valid-hash (h/validate-hash db [:user3 file])))))) + +(deftest test-add-commands + (let [obj {:commands {:test {:name "name" + :description "desc"}} + :responses {:test-r {:name "r" + :description "desc-r"}}} + db (h/add-commands {} [:user nil obj])] + (is (= obj (select-keys (get-in db [:chats :user]) [:commands :responses]))))) diff --git a/test/cljs/status_im/test/handlers.cljs b/test/cljs/status_im/test/handlers.cljs new file mode 100644 index 0000000000..70f4ae44c6 --- /dev/null +++ b/test/cljs/status_im/test/handlers.cljs @@ -0,0 +1,6 @@ +(ns status-im.test.handlers + (:require [cljs.test :refer-macros [deftest is]] + [status-im.handlers :as h])) + +(deftest test-set-val + (is (= {:key :val} (h/set-el {} [nil :key :val])))) diff --git a/test/cljs/status_im/test/handlers_stubs.cljs b/test/cljs/status_im/test/handlers_stubs.cljs new file mode 100644 index 0000000000..5336ca9cf7 --- /dev/null +++ b/test/cljs/status_im/test/handlers_stubs.cljs @@ -0,0 +1,24 @@ +(ns status-im.test.handlers-stubs + (:require [re-frame.core :refer [subscribe dispatch after]] + [status-im.utils.handlers :refer [register-handler]] + [status-im.utils.handlers :as u] + [status-im.chat.sign-up :as sign-up-service] + status-im.handlers)) + +(defn init-stubs [] + (register-handler :sign-up + (fn [] + ;; todo save phone number to db + (sign-up-service/on-sign-up-response))) + + (register-handler :sign-up-confirm + (u/side-effect! + (fn [_ [_ confirmation-code]] + (sign-up-service/on-send-code-response + (if (= "1234" confirmation-code) + {:message "Done!" + :status :confirmed + :confirmed true} + {:message "Wrong code!" + :status :failed + :confirmed false})))))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs new file mode 100644 index 0000000000..fef2b15ecf --- /dev/null +++ b/test/cljs/status_im/test/runner.cljs @@ -0,0 +1,7 @@ +(ns status-im.test.runner + (:require [doo.runner :refer-macros [doo-tests]] + [status-im.test.handlers] + [status-im.test.commands.handlers])) + +(doo-tests 'status-im.test.handlers + 'status-im.test.commands.handlers) diff --git a/test/syng_im/core_test.clj b/test/syng_im/core_test.clj deleted file mode 100644 index e839df4792..0000000000 --- a/test/syng_im/core_test.clj +++ /dev/null @@ -1,7 +0,0 @@ -(ns syng-im.core-test - (:require [clojure.test :refer :all] - [syng-im.core :refer :all])) - -(deftest a-test - (testing "FIXME, I fail." - (is (= 0 1))))