Merge branch 'master' into edge-support

feature/default_network_editable
Csaba S 7 years ago committed by GitHub
commit 2f34630486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .eslintignore
  2. 5
      .gitignore
  3. 10
      .stylelintignore
  4. 50
      .stylelintrc
  5. BIN
      app/fonts/DIN Next/DIN Next W01 Bold.otf
  6. BIN
      app/fonts/DIN Next/DIN Next W01 Regular.otf
  7. BIN
      app/fonts/DIN Next/DIN Next W10 Black.otf
  8. BIN
      app/fonts/DIN Next/DIN Next W10 Italic.otf
  9. BIN
      app/fonts/DIN Next/DIN Next W10 Light.otf
  10. BIN
      app/fonts/DIN Next/DIN Next W10 Medium.otf
  11. BIN
      app/fonts/DIN_OT/DINOT-2.otf
  12. BIN
      app/fonts/DIN_OT/DINOT-Bold 2.otf
  13. BIN
      app/fonts/DIN_OT/DINOT-BoldItalic.otf
  14. BIN
      app/fonts/DIN_OT/DINOT-Italic 2.otf
  15. BIN
      app/fonts/DIN_OT/DINOT-Medium 2.otf
  16. BIN
      app/fonts/DIN_OT/DINOT-MediumItalic 2.otf
  17. BIN
      app/fonts/Lato/Lato-Black.ttf
  18. BIN
      app/fonts/Lato/Lato-BlackItalic.ttf
  19. BIN
      app/fonts/Lato/Lato-Bold.ttf
  20. BIN
      app/fonts/Lato/Lato-BoldItalic.ttf
  21. BIN
      app/fonts/Lato/Lato-Hairline.ttf
  22. BIN
      app/fonts/Lato/Lato-HairlineItalic.ttf
  23. BIN
      app/fonts/Lato/Lato-Italic.ttf
  24. BIN
      app/fonts/Lato/Lato-Light.ttf
  25. BIN
      app/fonts/Lato/Lato-LightItalic.ttf
  26. BIN
      app/fonts/Lato/Lato-Regular.ttf
  27. 93
      app/fonts/Lato/OFL.txt
  28. BIN
      app/fonts/Roboto/Roboto-Black.ttf
  29. BIN
      app/fonts/Roboto/Roboto-BlackItalic.ttf
  30. BIN
      app/fonts/Roboto/Roboto-Bold.ttf
  31. BIN
      app/fonts/Roboto/Roboto-BoldItalic.ttf
  32. BIN
      app/fonts/Roboto/Roboto-Italic.ttf
  33. BIN
      app/fonts/Roboto/Roboto-Light.ttf
  34. BIN
      app/fonts/Roboto/Roboto-LightItalic.ttf
  35. BIN
      app/fonts/Roboto/Roboto-Medium.ttf
  36. BIN
      app/fonts/Roboto/Roboto-MediumItalic.ttf
  37. BIN
      app/fonts/Roboto/Roboto-Regular.ttf
  38. BIN
      app/fonts/Roboto/Roboto-Thin.ttf
  39. BIN
      app/fonts/Roboto/Roboto-ThinItalic.ttf
  40. BIN
      app/fonts/Roboto/RobotoCondensed-Bold.ttf
  41. BIN
      app/fonts/Roboto/RobotoCondensed-BoldItalic.ttf
  42. BIN
      app/fonts/Roboto/RobotoCondensed-Italic.ttf
  43. BIN
      app/fonts/Roboto/RobotoCondensed-Light.ttf
  44. BIN
      app/fonts/Roboto/RobotoCondensed-LightItalic.ttf
  45. BIN
      app/fonts/Roboto/RobotoCondensed-Regular.ttf
  46. BIN
      app/images/.DS_Store
  47. 14
      app/images/check-white.svg
  48. BIN
      app/images/coinbase logo.png
  49. 11
      app/images/eth_logo.svg
  50. 18
      app/images/import-account.svg
  51. BIN
      app/images/info-logo.png
  52. 128
      app/images/metamask-fox.svg
  53. 11
      app/images/mm-bolt.svg
  54. 11
      app/images/mm-info-icon.svg
  55. 15
      app/images/open.svg
  56. 17
      app/images/plus-btn-white.svg
  57. 21
      app/images/popout.svg
  58. 44
      app/images/settings.svg
  59. BIN
      app/images/shapeshift logo.png
  60. 2
      app/manifest.json
  61. 4
      app/notification.html
  62. 4
      app/popup.html
  63. 12
      app/scripts/background.js
  64. 22
      app/scripts/config.js
  65. 40
      app/scripts/controllers/network.js
  66. 37
      app/scripts/controllers/preferences.js
  67. 4
      app/scripts/controllers/transactions.js
  68. 11
      app/scripts/lib/config-manager.js
  69. 10
      app/scripts/lib/environment-type.js
  70. 5
      app/scripts/lib/is-popup-or-notification.js
  71. 2
      app/scripts/lib/notification-manager.js
  72. 29
      app/scripts/metamask-controller.js
  73. 5
      app/scripts/platforms/extension.js
  74. 2
      app/scripts/popup-core.js
  75. 29
      app/scripts/popup.js
  76. 26
      development/backGroundConnectionModifiers.js
  77. 5
      development/mockExtension.js
  78. 11
      development/selector.js
  79. 132
      development/states/add-token.json
  80. 154
      development/states/confirm-new-ui.json
  81. 175
      development/states/confirm-sig-requests.json
  82. 11
      development/states/first-time.json
  83. 154
      development/states/send-edit.json
  84. 133
      development/states/send-new-ui.json
  85. 6
      docker-compose.yml
  86. 70
      gulpfile.js
  87. 3
      mascara/server/index.js
  88. 4
      mascara/server/util.js
  89. 198
      mascara/src/app/buy-ether-widget/index.js
  90. 5
      mascara/src/app/first-time/backup-phrase-screen.js
  91. 5
      mascara/src/app/first-time/breadcrumbs.js
  92. 3
      mascara/src/app/first-time/buy-ether-screen.js
  93. 146
      mascara/src/app/first-time/create-password-screen.js
  94. 3
      mascara/src/app/first-time/import-account-screen.js
  95. 59
      mascara/src/app/first-time/import-seed-phrase-screen.js
  96. 62
      mascara/src/app/first-time/index.css
  97. 43
      mascara/src/app/first-time/index.js
  98. 10
      mascara/src/app/first-time/loading-screen.js
  99. 72
      mascara/src/app/first-time/notice-screen.js
  100. 3
      mascara/src/app/first-time/unique-image-screen.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,3 +3,4 @@ test/integration/bundle.js
test/integration/jquery-3.1.0.min.js test/integration/jquery-3.1.0.min.js
test/integration/helpers.js test/integration/helpers.js
test/integration/lib/first-time.js test/integration/lib/first-time.js
ui/lib/blockies.js

5
.gitignore vendored

@ -6,6 +6,8 @@ app/bower_components
test/bower_components test/bower_components
package package
.idea
temp temp
.tmp .tmp
.sass-cache .sass-cache
@ -24,6 +26,9 @@ test/background.js
test/bundle.js test/bundle.js
test/test-bundle.js test/test-bundle.js
#ignore css output and sourcemaps
ui/app/css/output/
notes.txt notes.txt
.coveralls.yml .coveralls.yml

@ -0,0 +1,10 @@
app/
development/
dist/
docs/
fonts/
images/
mascara/
node_modules/
notices/
test/

@ -0,0 +1,50 @@
{
"extends": "stylelint-config-standard",
"rules": {
"color-named": "never",
"font-family-name-quotes": "always-where-recommended",
"font-weight-notation": "numeric",
"function-url-quotes": "always",
"number-leading-zero": "never",
"value-no-vendor-prefix": true,
"value-list-comma-newline-before": "never-multi-line",
"custom-property-empty-line-before": "never",
"property-no-unknown": [
true,
{
"ignoreProperties": [
"composes",
"all",
"-webkit-appearance"
]
}
],
"declaration-block-semicolon-newline-after": "always",
"block-opening-brace-newline-after": "always",
"selector-attribute-quotes": "always",
"selector-max-specificity": "0,5,2",
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["local", "global"]
}
],
"at-rule-empty-line-before": [
"always",
{
"ignore": [
"after-comment",
]
}
],
"indentation": [
2,
{
"indentInsideParens": "once-at-root-twice-in-block"
}
],
"max-nesting-depth": 3,
"no-duplicate-selectors": true,
"no-unknown-animations": true
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,93 @@
Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
app/images/.DS_Store vendored

Binary file not shown.

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="13px" viewBox="0 0 16 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
<title>check-white</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="MetaMascara-v2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="account-dropdown-top-bar-IXD" transform="translate(-17.000000, -80.000000)" fill-rule="nonzero" fill="#FFFFFF">
<g id="Group-11" transform="translate(18.000000, 74.000000)">
<polygon id="check-white" points="4.2 15.5712828 0.714212839 12.0143571 -0.714212839 13.4142143 4.2 18.4287172 14.7142128 7.69992858 13.2857872 6.30007142"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="417px" viewBox="0 0 256 417" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<polygon fill="#343434" points="127.9611 0 125.1661 9.5 125.1661 285.168 127.9611 287.958 255.9231 212.32"/>
<polygon fill="#8C8C8C" points="127.962 0 0 212.32 127.962 287.959 127.962 154.158"/>
<polygon fill="#3C3C3B" points="127.9611 312.1866 126.3861 314.1066 126.3861 412.3056 127.9611 416.9066 255.9991 236.5866"/>
<polygon fill="#8C8C8C" points="127.962 416.9052 127.962 312.1852 0 236.5852"/>
<polygon fill="#141414" points="127.9611 287.9577 255.9211 212.3207 127.9611 154.1587"/>
<polygon fill="#393939" points="0.0009 212.3208 127.9609 287.9578 127.9609 154.1588"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 854 B

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
<title>import-account</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="MetaMascara-v2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="account-dropdown-top-bar-IXD" transform="translate(-25.000000, -718.000000)">
<g id="Group-6" transform="translate(4.000000, 646.000000)">
<g id="import-account" transform="translate(21.000000, 72.000000)">
<rect id="Rectangle-49" fill="#FFFFFF" x="0" y="13.1721326" width="14.4893459" height="1.08397642"></rect>
<rect id="Rectangle" fill="#FFFFFF" x="6.5860663" y="0" width="1.08397642" height="10.5377061"></rect>
<polyline id="Path-12" stroke="#FFFFFF" points="2.63442652 6.5860663 7.24467293 10.5377061 11.8549193 6.5860663"></polyline>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 318.6 318.6"
style="enable-background:new 0 0 318.6 318.6;" xml:space="preserve">
<style type="text/css">
.st0{fill:#161616;stroke:#161616;}
.st1{fill:#E4761B;stroke:#E4761B;stroke-linecap:round;stroke-linejoin:round;}
.st2{fill:#763D16;stroke:#763D16;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:#F6851B;stroke:#F6851B;stroke-linecap:round;stroke-linejoin:round;}
.st4{fill:#E2761B;stroke:#E2761B;stroke-linecap:round;stroke-linejoin:round;}
.st5{fill:#CD6116;stroke:#CD6116;stroke-linecap:round;stroke-linejoin:round;}
.st6{fill:#C0AD9E;stroke:#C0AD9E;stroke-linecap:round;stroke-linejoin:round;}
.st7{fill:#D7C1B3;stroke:#D7C1B3;stroke-linecap:round;stroke-linejoin:round;}
.st8{fill:#E4751F;stroke:#E4751F;stroke-linecap:round;stroke-linejoin:round;}
.st9{fill:#233447;stroke:#233447;stroke-linecap:round;stroke-linejoin:round;}
.st10{fill:#161616;stroke:#161616;stroke-linecap:round;stroke-linejoin:round;}
</style>
<polygon class="st0" points="277.3,145.6 272.3,142 280.3,134.7 274.2,129.9 282.2,123.8 276.9,119.8 285.3,79 272.7,41.1
191.6,71.4 124.1,71.4 43,41.1 30.4,79 38.9,119.8 33.5,123.8 41.5,129.9 35.4,134.7 43.4,142 38.4,145.6 49.9,159.1 32.5,213.3
48.6,268.6 105.3,253 116.3,262 138.7,277.5 177,277.5 199.4,262 210.4,253 267.1,268.6 283.3,213.3 265.8,159.1 "/>
<g>
<polygon class="st1" points="105.3,253 48.6,268.6 32.5,213.3 "/>
<polygon class="st1" points="283.3,213.3 267.1,268.6 210.4,253 "/>
<polygon class="st2" points="265.8,159.1 213.5,143.8 231.8,139 "/>
<polygon class="st2" points="49.9,159.1 84,139 102.2,143.8 "/>
<polygon class="st2" points="43.4,142 41.5,129.9 84,139 "/>
<polygon class="st2" points="272.3,142 231.8,139 274.2,129.9 "/>
<polygon class="st2" points="272.3,142 265.8,159.1 231.8,139 "/>
<polygon class="st2" points="43.4,142 84,139 49.9,159.1 "/>
<polygon class="st2" points="231.8,139 276.9,119.8 274.2,129.9 "/>
<polygon class="st2" points="84,139 41.5,129.9 38.9,119.8 "/>
<polygon class="st3" points="124.1,71.4 191.6,71.4 176.5,112.5 "/>
<polygon class="st3" points="176.5,112.5 139.2,112.5 124.1,71.4 "/>
<polygon class="st2" points="276.9,119.8 231.8,139 231,87.4 "/>
<polygon class="st2" points="102.2,143.8 84,139 84.7,87.4 "/>
<polygon class="st2" points="84.7,87.4 84,139 38.9,119.8 "/>
<polygon class="st2" points="231,87.4 231.8,139 213.5,143.8 "/>
<polygon class="st1" points="139.2,112.5 43,41.1 124.1,71.4 "/>
<polygon class="st4" points="272.7,41.1 176.5,112.5 191.6,71.4 "/>
<polygon class="st1" points="210.4,253 236.9,213.3 283.3,213.3 "/>
<polygon class="st1" points="32.5,213.3 78.9,213.3 105.3,253 "/>
<polygon class="st3" points="229.3,167.7 283.3,213.3 236.9,213.3 "/>
<polygon class="st3" points="86.4,167.7 32.5,213.3 49.9,159.1 "/>
<polygon class="st3" points="78.9,213.3 32.5,213.3 86.4,167.7 "/>
<polygon class="st3" points="229.3,167.7 265.8,159.1 283.3,213.3 "/>
<polygon class="st2" points="84.7,87.4 139.2,112.5 102.2,143.8 "/>
<polygon class="st2" points="213.5,143.8 176.5,112.5 231,87.4 "/>
<polygon class="st2" points="265.8,159.1 272.3,142 277.3,145.6 "/>
<polygon class="st2" points="49.9,159.1 38.4,145.6 43.4,142 "/>
<polygon class="st2" points="272.3,142 274.2,129.9 280.3,134.7 "/>
<polygon class="st2" points="43.4,142 35.4,134.7 41.5,129.9 "/>
<polygon class="st2" points="33.5,123.8 38.9,119.8 41.5,129.9 "/>
<polygon class="st2" points="282.2,123.8 274.2,129.9 276.9,119.8 "/>
<polygon class="st3" points="49.9,159.1 102.2,143.8 86.4,167.7 "/>
<polygon class="st3" points="265.8,159.1 229.3,167.7 213.5,143.8 "/>
<polygon class="st2" points="38.9,119.8 30.4,79 84.7,87.4 "/>
<polygon class="st2" points="231,87.4 285.3,79 276.9,119.8 "/>
<polygon class="st1" points="102.2,143.8 139.2,112.5 142.6,170.2 "/>
<polygon class="st1" points="213.5,143.8 229.3,167.7 173.1,170.2 "/>
<polygon class="st1" points="173.1,170.2 176.5,112.5 213.5,143.8 "/>
<polygon class="st1" points="142.6,170.2 86.4,167.7 102.2,143.8 "/>
<polygon class="st2" points="272.7,41.1 285.3,79 231,87.4 "/>
<polygon class="st2" points="43,41.1 139.2,112.5 84.7,87.4 "/>
<polygon class="st2" points="231,87.4 176.5,112.5 272.7,41.1 "/>
<polygon class="st2" points="84.7,87.4 30.4,79 43,41.1 "/>
<polygon class="st5" points="105.3,253 78.9,213.3 110,213.7 "/>
<polygon class="st5" points="210.4,253 205.7,213.7 236.9,213.3 "/>
<polygon class="st3" points="173.1,170.2 142.6,170.2 139.2,112.5 "/>
<polygon class="st3" points="139.2,112.5 176.5,112.5 173.1,170.2 "/>
<polygon class="st6" points="116.3,262 105.3,253 136.8,267.9 "/>
<polygon class="st6" points="178.9,267.9 210.4,253 199.4,262 "/>
<polygon class="st7" points="136.6,258.6 136.8,267.9 105.3,253 "/>
<polygon class="st7" points="179.2,258.6 210.4,253 178.9,267.9 "/>
<polygon class="st3" points="86.4,167.7 110,213.7 78.9,213.3 "/>
<polygon class="st3" points="236.9,213.3 205.7,213.7 229.3,167.7 "/>
<polygon class="st8" points="86.4,167.7 109.2,190.8 110,213.7 "/>
<polygon class="st8" points="229.3,167.7 205.7,213.7 206.6,190.8 "/>
<polygon class="st7" points="105.3,253 139.2,236.5 136.6,258.6 "/>
<polygon class="st7" points="210.4,253 179.2,258.6 176.5,236.5 "/>
<polygon class="st1" points="139.2,236.5 105.3,253 110,213.7 "/>
<polygon class="st1" points="176.5,236.5 205.7,213.7 210.4,253 "/>
<polygon class="st5" points="173.1,170.2 229.3,167.7 206.6,190.8 "/>
<polygon class="st5" points="109.2,190.8 86.4,167.7 142.6,170.2 "/>
<polygon class="st5" points="142.6,170.2 129.1,181.7 109.2,190.8 "/>
<polygon class="st5" points="206.6,190.8 186.6,181.7 173.1,170.2 "/>
<polygon class="st3" points="205.7,213.7 178.3,199.1 206.6,190.8 "/>
<polygon class="st3" points="110,213.7 109.2,190.8 137.4,199.1 "/>
<polygon class="st9" points="137.4,199.1 109.2,190.8 129.1,181.7 "/>
<polygon class="st9" points="178.3,199.1 186.6,181.7 206.6,190.8 "/>
<polygon class="st5" points="186.6,181.7 178.3,199.1 173.1,170.2 "/>
<polygon class="st5" points="129.1,181.7 142.6,170.2 137.4,199.1 "/>
<polygon class="st6" points="199.4,262 177,277.5 178.9,267.9 "/>
<polygon class="st6" points="136.8,267.9 138.7,277.5 116.3,262 "/>
<polygon class="st4" points="178.3,199.1 171.8,188.4 173.1,170.2 "/>
<polygon class="st8" points="137.4,199.1 142.6,170.2 143.9,188.4 "/>
<polygon class="st3" points="173.1,170.2 171.8,188.4 143.9,188.4 "/>
<polygon class="st3" points="143.9,188.4 142.6,170.2 173.1,170.2 "/>
<polygon class="st3" points="178.3,199.1 205.7,213.7 176.5,236.5 "/>
<polygon class="st3" points="139.2,236.5 110,213.7 137.4,199.1 "/>
<polygon class="st3" points="137.4,199.1 144,233.2 139.2,236.5 "/>
<polygon class="st3" points="176.5,236.5 171.7,233.2 178.3,199.1 "/>
<polygon class="st8" points="171.8,188.4 178.3,199.1 171.7,233.2 "/>
<polygon class="st8" points="143.9,188.4 144,233.2 137.4,199.1 "/>
<polygon class="st3" points="143.9,188.4 171.8,188.4 171.7,233.2 "/>
<polygon class="st3" points="171.7,233.2 144,233.2 143.9,188.4 "/>
<polygon class="st6" points="179.2,258.6 178.9,267.9 177,277.5 "/>
<polygon class="st6" points="138.7,277.5 136.8,267.9 136.6,258.6 "/>
<polygon class="st6" points="136.6,258.6 139,256.4 138.7,277.5 "/>
<polygon class="st6" points="177,277.5 176.7,256.4 179.2,258.6 "/>
<polygon class="st6" points="138.7,277.5 139,256.4 176.7,256.4 "/>
<polygon class="st6" points="176.7,256.4 177,277.5 138.7,277.5 "/>
<polygon class="st10" points="176.5,236.5 179.2,258.6 176.7,256.4 "/>
<polygon class="st10" points="139,256.4 136.6,258.6 139.2,236.5 "/>
<polygon class="st10" points="139.2,236.5 140.7,241.2 139,256.4 "/>
<polygon class="st10" points="176.7,256.4 175,241.2 176.5,236.5 "/>
<polygon class="st10" points="143.7,237.7 140.7,241.2 139.2,236.5 "/>
<polygon class="st10" points="176.5,236.5 175,241.2 172,237.7 "/>
<polygon class="st10" points="172,237.7 171.7,233.2 176.5,236.5 "/>
<polygon class="st10" points="139.2,236.5 144,233.2 143.7,237.7 "/>
<polygon class="st10" points="171.7,233.2 172,237.7 143.7,237.7 "/>
<polygon class="st10" points="143.7,237.7 144,233.2 171.7,233.2 "/>
<polygon class="st10" points="140.7,241.2 175,241.2 176.7,256.4 "/>
<polygon class="st10" points="176.7,256.4 139,256.4 140.7,241.2 "/>
<polygon class="st10" points="140.7,241.2 143.7,237.7 172,237.7 "/>
<polygon class="st10" points="172,237.7 175,241.2 140.7,241.2 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 252 251.7" style="enable-background:new 0 0 252 251.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#757575;}
</style>
<path class="st0" d="M211.3,103.9h-60.7c-2,0-3.6-1.6-3.6-3.6V3.6c0-3.5-4.5-5-6.6-2.2l-102.7,140c-1.8,2.4,0,5.8,2.9,5.8h60.7
c2,0,3.6,1.6,3.6,3.6v96.6c0,3.5,4.5,5,6.6,2.2l102.7-140C216,107.3,214.3,103.9,211.3,103.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 732 B

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 10 10" style="enable-background:new 0 0 10 10;" xml:space="preserve">
<style type="text/css">
.st0{fill:#B8B8B8;}
</style>
<path class="st0" d="M5,0C2.2,0,0,2.2,0,5s2.2,5,5,5s5-2.2,5-5S7.8,0,5,0z M5,2c0.4,0,0.7,0.3,0.7,0.7c0,0.4-0.3,0.7-0.7,0.7
S4.3,3.2,4.3,2.8C4.3,2.4,4.6,2,5,2z M5.7,8H4.3V4.3h1.5V8z"/>
</svg>

After

Width:  |  Height:  |  Size: 689 B

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
<title>open</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Mobile-screens" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="MetaMascara-Mobile---structured" transform="translate(-329.000000, -93.000000)">
<g id="open" transform="translate(330.000000, 94.000000)">
<path d="M26,13 C26,20.1799 20.1799,26 13,26 C5.8201,26 0,20.1799 0,13 C0,5.8201 5.8201,0 13,0 C20.1799,0 26,5.8201 26,13 Z" id="Stroke-3" stroke="#4A4A4A"></path>
<path d="M6,17 C6,17 7.78735344,10.8360387 13.7616996,10.8360387 L13.7616996,8 L19,12.3733433 L13.7616996,17 L13.7616996,14.1639613 C13.7616996,14.1639613 9.54083576,13.4629933 6,17" id="Fill-5" fill="#4A4A4A"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
<title>plus-btn-white</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="MetaMascara-v2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="account-dropdown-top-bar-IXD" transform="translate(-24.000000, -669.000000)" fill="#FFFFFF">
<g id="Group-6" transform="translate(4.000000, 646.000000)">
<g id="plus-btn-white" transform="translate(20.000000, 23.000000)">
<rect id="Rectangle-48" x="7.38461538" y="0" width="1.23076923" height="16"></rect>
<rect id="Rectangle-48" transform="translate(8.000000, 8.000000) rotate(-90.000000) translate(-8.000000, -8.000000) " x="7.38461538" y="0" width="1.23076923" height="16"></rect>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>popout</title>
<desc>Created with Sketch.</desc>
<defs>
<polygon id="path-1" points="-0.00035 0 10.9999 0 10.9999 10.9997 -0.00035 10.9997"></polygon>
</defs>
<g id="MetaMascara-Mobile---structured-TOKEN" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-327.000000, -96.000000)">
<g id="popout" transform="translate(327.000000, 96.000000)">
<g id="Group-3" transform="translate(11.000000, 0.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Clip-2"></g>
<path d="M10.9229,0.6177 C10.8209,0.3737 10.6269,0.1787 10.3819,0.0767 C10.2599,0.0267 10.1309,-0.0003 9.9999,-0.0003 L3.9999,-0.0003 C3.4479,-0.0003 2.9999,0.4477 2.9999,0.9997 C2.9999,1.5527 3.4479,1.9997 3.9999,1.9997 L7.5859,1.9997 L0.2929,9.2927 C-0.0981,9.6837 -0.0981,10.3167 0.2929,10.7067 C0.4879,10.9027 0.7439,10.9997 0.9999,10.9997 C1.2559,10.9997 1.5119,10.9027 1.7069,10.7067 L8.9999,3.4137 L8.9999,6.9997 C8.9999,7.5527 9.4479,7.9997 9.9999,7.9997 C10.5519,7.9997 10.9999,7.5527 10.9999,6.9997 L10.9999,0.9997 C10.9999,0.8697 10.9739,0.7407 10.9229,0.6177" id="Fill-1" fill="#4A4A4A" mask="url(#mask-2)"></path>
</g>
<path d="M19,10 C18.448,10 18,10.448 18,11 L18,19 C18,19.551 17.551,20 17,20 L3,20 C2.449,20 2,19.551 2,19 L2,5 C2,4.449 2.449,4 3,4 L11,4 C11.552,4 12,3.552 12,3 C12,2.448 11.552,2 11,2 L3,2 C1.346,2 0,3.346 0,5 L0,19 C0,20.654 1.346,22 3,22 L17,22 C18.654,22 20,20.654 20,19 L20,11 C20,10.448 19.552,10 19,10" id="Fill-4" fill="#4A4A4A"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -1,24 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <title>settings</title>
width="24.088px" height="24px" viewBox="0 0 24.088 24" enable-background="new 0 0 24.088 24" xml:space="preserve"> <desc>Created with Sketch.</desc>
<path d="M21.525,10.147c-0.41-0.059-0.847-0.428-0.974-0.82l-0.608-1.481c-0.191-0.365-0.146-0.935,0.1-1.264l0.99-1.318 <defs>
c0.246-0.33,0.227-0.854-0.047-1.162l-1.084-1.086c-0.31-0.272-0.832-0.293-1.164-0.045l-1.316,0.988 <polygon id="path-1" points="20 10 20 19.9998 0 19.9998 0 10 0 0.0002 20 0.0002"></polygon>
c-0.33,0.248-0.898,0.293-1.264,0.101l-1.48-0.609c-0.395-0.126-0.764-0.562-0.82-0.971l-0.233-1.629 </defs>
c-0.058-0.409-0.44-0.778-0.851-0.822c0,0-0.254-0.026-0.77-0.026c-0.514,0-0.77,0.026-0.77,0.026 <g id="MetaMascara-v2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
c-0.41,0.044-0.793,0.413-0.852,0.822L10.15,2.48c-0.059,0.409-0.428,0.845-0.82,0.971L7.85,4.06 <g id="account-dropdown-top-bar-IXD" transform="translate(-25.000000, -826.000000)">
C7.484,4.251,6.916,4.207,6.586,3.959L5.268,2.97c-0.33-0.248-0.854-0.228-1.162,0.045L3.021,4.101 <g id="Group-6" transform="translate(4.000000, 646.000000)">
C2.749,4.41,2.727,4.933,2.975,5.263l0.988,1.318c0.249,0.33,0.293,0.899,0.102,1.264l-0.61,1.482 <g id="settings" transform="translate(21.000000, 180.000000)">
c-0.125,0.393-0.562,0.762-0.972,0.82l-1.629,0.231c-0.408,0.059-0.776,0.442-0.82,0.853c0,0-0.026,0.255-0.026,0.77 <mask id="mask-2" fill="white">
c0,0.516,0.026,0.77,0.026,0.77c0.044,0.412,0.412,0.793,0.82,0.853l1.629,0.231c0.408,0.06,0.847,0.429,0.972,0.82l0.61,1.48 <use xlink:href="#path-1"></use>
c0.191,0.365,0.146,0.936-0.102,1.264l-0.988,1.318c-0.248,0.33-0.308,0.779-0.132,0.994c0.175,0.217,0.677,0.752,0.679,0.754 </mask>
c0,0.002,0.17,0.156,0.375,0.344c0.203,0.188,1.041,0.449,1.371,0.203l1.317-0.99c0.33-0.246,0.897-0.293,1.265-0.1l1.479,0.608 <g id="Clip-2"></g>
c0.394,0.125,0.763,0.562,0.819,0.972l0.233,1.629c0.058,0.408,0.44,0.779,0.853,0.822c0,0,0.254,0.026,0.769,0.026 <path d="M10,13.6602 C7.979,13.6602 6.34,12.0212 6.34,10.0002 C6.34,7.9782 7.979,6.3402 10,6.3402 C12.021,6.3402 13.66,7.9782 13.66,10.0002 C13.66,12.0212 12.021,13.6602 10,13.6602 L10,13.6602 Z M19.157,11.8112 C19.53,11.8112 19.878,11.5092 19.929,11.1392 C19.929,11.1392 20,10.6182 20,10.0002 C20,9.3822 19.929,8.8622 19.929,8.8622 C19.878,8.4922 19.53,8.1892 19.157,8.1892 L17.228,8.1892 C16.854,8.1892 16.466,7.9512 16.365,7.6602 C16.265,7.3682 16.127,6.4352 16.391,6.1712 L17.755,4.8072 C18.019,4.5432 18.039,4.0922 17.8,3.8052 L16.195,2.2002 C15.908,1.9602 15.458,1.9812 15.193,2.2452 L13.829,3.6092 C13.565,3.8732 13.125,3.9802 12.852,3.8462 C12.578,3.7122 11.812,3.1462 11.812,2.7732 L11.812,0.8432 C11.812,0.4702 11.509,0.1222 11.139,0.0722 C11.139,0.0722 10.619,0.0002 10,0.0002 C9.382,0.0002 8.862,0.0722 8.862,0.0722 C8.492,0.1222 8.189,0.4702 8.189,0.8432 L8.189,2.7732 C8.189,3.1462 7.951,3.5352 7.66,3.6352 C7.369,3.7352 6.435,3.8732 6.171,3.6092 L4.807,2.2452 C4.542,1.9812 4.092,1.9612 3.805,2.2002 L2.2,3.8052 C1.96,4.0922 1.981,4.5432 2.245,4.8072 L3.609,6.1712 C3.873,6.4352 3.98,6.8752 3.846,7.1482 C3.711,7.4222 3.146,8.1892 2.773,8.1892 L0.843,8.1892 C0.47,8.1892 0.123,8.4922 0.072,8.8622 C0.072,8.8622 0,9.3822 0,10.0002 C0,10.6182 0.072,11.1392 0.072,11.1392 C0.123,11.5092 0.47,11.8112 0.843,11.8112 L2.773,11.8112 C3.146,11.8112 3.535,12.0502 3.635,12.3412 C3.735,12.6322 3.874,13.5642 3.609,13.8292 L2.246,15.1932 C1.981,15.4572 1.961,15.9082 2.2,16.1952 L3.805,17.8002 C4.092,18.0392 4.542,18.0192 4.807,17.7552 L6.171,16.3902 C6.435,16.1272 6.875,16.0202 7.148,16.1542 C7.422,16.2882 8.189,16.8532 8.189,17.2272 L8.189,19.1572 C8.189,19.5302 8.492,19.8782 8.862,19.9292 C8.862,19.9292 9.382,20.0002 10,20.0002 C10.619,20.0002 11.139,19.9292 11.139,19.9292 C11.509,19.8772 11.812,19.5302 11.812,19.1572 L11.812,17.2272 C11.812,16.8532 12.05,16.4662 12.341,16.3652 C12.632,16.2642 13.565,16.1272 13.829,16.3902 L15.193,17.7552 C15.458,18.0182 15.908,18.0392 16.195,17.8002 L17.8,16.1952 C18.039,15.9082 18.02,15.4582 17.755,15.1932 L16.391,13.8292 C16.127,13.5652 16.021,13.1252 16.154,12.8512 C16.288,12.5782 16.854,11.8112 17.228,11.8112 L19.157,11.8112 Z" id="Fill-1" fill="#B3B3B3" mask="url(#mask-2)"></path>
s0.771-0.026,0.771-0.026c0.408-0.043,0.793-0.414,0.85-0.822l0.234-1.629c0.057-0.408,0.426-0.847,0.819-0.972l1.479-0.61 </g>
c0.365-0.191,0.935-0.146,1.265,0.102l1.317,0.99c0.332,0.246,0.854,0.227,1.164-0.047l1.082-1.084 </g>
c0.273-0.312,0.293-0.834,0.047-1.164l-0.989-1.318c-0.246-0.328-0.291-0.898-0.101-1.264l0.609-1.48 </g>
c0.127-0.393,0.562-0.762,0.973-0.82l1.627-0.231c0.41-0.06,0.779-0.44,0.822-0.853c0,0,0.027-0.254,0.027-0.77 </g>
c0-0.515-0.027-0.77-0.027-0.77c-0.043-0.41-0.412-0.794-0.822-0.853L21.525,10.147z M12.004,15.001c-1.657,0-3-1.344-3-3
c0-1.657,1.343-3,3-3s3,1.344,3,3S13.66,15.001,12.004,15.001z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -1,7 +1,7 @@
{ {
"name": "MetaMask", "name": "MetaMask",
"short_name": "Metamask", "short_name": "Metamask",
"version": "3.14.2", "version": "4.0.12",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "Ethereum Browser Extension", "description": "Ethereum Browser Extension",

@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html> <html style="height:600px;">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>MetaMask Notification</title> <title>MetaMask Notification</title>
@ -9,7 +9,7 @@
} }
</style> </style>
</head> </head>
<body> <body class="notification" style="height:600px;">
<div id="app-content"></div> <div id="app-content"></div>
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script> <script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
</body> </body>

@ -1,11 +1,11 @@
<!doctype html> <!doctype html>
<html> <html style="width:357px; height:600px;">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
<title>MetaMask Plugin</title> <title>MetaMask Plugin</title>
</head> </head>
<body style="width:357px; height:500px;"> <body style="width:357px; height:600px;">
<div id="app-content"></div> <div id="app-content"></div>
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script> <script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
</body> </body>

@ -38,6 +38,7 @@ const isIE = !!document.documentMode
const isEdge = !isIE && !!window.StyleMedia const isEdge = !isIE && !!window.StyleMedia
let popupIsOpen = false let popupIsOpen = false
let openMetamaskTabsIDs = {}
// state persistence // state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
@ -125,9 +126,15 @@ function setupController (initState) {
popupIsOpen = popupIsOpen || (remotePort.name === 'popup') popupIsOpen = popupIsOpen || (remotePort.name === 'popup')
controller.setupTrustedCommunication(portStream, 'MetaMask') controller.setupTrustedCommunication(portStream, 'MetaMask')
// record popup as closed // record popup as closed
if (remotePort.sender.url.match(/home.html$/)) {
openMetamaskTabsIDs[remotePort.sender.tab.id] = true
}
if (remotePort.name === 'popup') { if (remotePort.name === 'popup') {
endOfStream(portStream, () => { endOfStream(portStream, () => {
popupIsOpen = false popupIsOpen = false
if (remotePort.sender.url.match(/home.html$/)) {
openMetamaskTabsIDs[remotePort.sender.tab.id] = false
}
}) })
} }
} else { } else {
@ -170,7 +177,10 @@ function setupController (initState) {
// popup trigger // popup trigger
function triggerUi () { function triggerUi () {
if (!popupIsOpen) notificationManager.showPopup() extension.tabs.query({ active: true }, (tabs) => {
const currentlyActiveMetamaskTab = tabs.find(tab => openMetamaskTabsIDs[tab.id])
if (!popupIsOpen && !currentlyActiveMetamaskTab) notificationManager.showPopup()
})
} }
// On first install, open a window to MetaMask website to how-it-works. // On first install, open a window to MetaMask website to how-it-works.

@ -4,6 +4,15 @@ const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
const LOCALHOST_RPC_URL = 'http://localhost:8545' const LOCALHOST_RPC_URL = 'http://localhost:8545'
const MAINET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2'
const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2'
const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2'
const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2'
const DEFAULT_RPC = 'rinkeby'
const OLD_UI_NETWORK_TYPE = 'network'
const BETA_UI_NETWORK_TYPE = 'networkBeta'
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
module.exports = { module.exports = {
@ -14,9 +23,22 @@ module.exports = {
kovan: KOVAN_RPC_URL, kovan: KOVAN_RPC_URL,
rinkeby: RINKEBY_RPC_URL, rinkeby: RINKEBY_RPC_URL,
}, },
// Used for beta UI
networkBeta: {
localhost: LOCALHOST_RPC_URL,
mainnet: MAINET_RPC_URL_BETA,
ropsten: ROPSTEN_RPC_URL_BETA,
kovan: KOVAN_RPC_URL_BETA,
rinkeby: RINKEBY_RPC_URL_BETA,
},
networkNames: { networkNames: {
3: 'Ropsten', 3: 'Ropsten',
4: 'Rinkeby', 4: 'Rinkeby',
42: 'Kovan', 42: 'Kovan',
}, },
enums: {
DEFAULT_RPC,
OLD_UI_NETWORK_TYPE,
BETA_UI_NETWORK_TYPE,
},
} }

@ -8,14 +8,19 @@ const ComposedStore = require('obs-store/lib/composed')
const extend = require('xtend') const extend = require('xtend')
const EthQuery = require('eth-query') const EthQuery = require('eth-query')
const createEventEmitterProxy = require('../lib/events-proxy.js') const createEventEmitterProxy = require('../lib/events-proxy.js')
const RPC_ADDRESS_LIST = require('../config.js').network const networkConfig = require('../config.js')
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
module.exports = class NetworkController extends EventEmitter { module.exports = class NetworkController extends EventEmitter {
constructor (config) { constructor (config) {
super() super()
this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider) config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
this.networkStore = new ObservableStore('loading') this.networkStore = new ObservableStore('loading')
this.providerStore = new ObservableStore(config.provider) this.providerStore = new ObservableStore(config.provider)
@ -25,6 +30,23 @@ module.exports = class NetworkController extends EventEmitter {
this.on('networkDidChange', this.lookupNetwork) this.on('networkDidChange', this.lookupNetwork)
} }
async setNetworkEndpoints (version) {
if (version === this._networkEndpointVersion) {
return
}
this._networkEndpointVersion = version
this._networkEndpoints = this.getNetworkEndpoints(version)
this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
const { type } = this.getProviderConfig()
return this.setProviderType(type, true)
}
getNetworkEndpoints (version = OLD_UI_NETWORK_TYPE) {
return networkConfig[version]
}
initializeProvider (_providerParams) { initializeProvider (_providerParams) {
this._baseProviderParams = _providerParams this._baseProviderParams = _providerParams
const { type, rpcTarget } = this.providerStore.getState() const { type, rpcTarget } = this.providerStore.getState()
@ -84,10 +106,13 @@ module.exports = class NetworkController extends EventEmitter {
return this.getRpcAddressForType(provider.type) return this.getRpcAddressForType(provider.type)
} }
async setProviderType (type) { async setProviderType (type, forceUpdate = false) {
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
// skip if type already matches // skip if type already matches
if (type === this.getProviderConfig().type) return if (type === this.getProviderConfig().type && !forceUpdate) {
return
}
const rpcTarget = this.getRpcAddressForType(type) const rpcTarget = this.getRpcAddressForType(type)
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
this.providerStore.updateState({ type, rpcTarget }) this.providerStore.updateState({ type, rpcTarget })
@ -99,8 +124,11 @@ module.exports = class NetworkController extends EventEmitter {
} }
getRpcAddressForType (type, provider = this.getProviderConfig()) { getRpcAddressForType (type, provider = this.getProviderConfig()) {
if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type] if (this._networkEndpoints[type]) {
return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC return this._networkEndpoints[type]
}
return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc
} }
// //

@ -9,11 +9,21 @@ class PreferencesController {
frequentRpcList: [], frequentRpcList: [],
currentAccountTab: 'history', currentAccountTab: 'history',
tokens: [], tokens: [],
useBlockie: false,
featureFlags: {},
}, opts.initState) }, opts.initState)
this.store = new ObservableStore(initState) this.store = new ObservableStore(initState)
} }
// PUBLIC METHODS // PUBLIC METHODS
setUseBlockie (val) {
this.store.updateState({ useBlockie: val })
}
getUseBlockie () {
return this.store.getState().useBlockie
}
setSelectedAddress (_address) { setSelectedAddress (_address) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const address = normalizeAddress(_address) const address = normalizeAddress(_address)
@ -43,6 +53,17 @@ class PreferencesController {
} }
this.store.updateState({ tokens }) this.store.updateState({ tokens })
return Promise.resolve(tokens)
}
removeToken (rawAddress) {
const tokens = this.store.getState().tokens
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
this.store.updateState({ tokens: updatedTokens })
return Promise.resolve(updatedTokens)
} }
getTokens () { getTokens () {
@ -82,6 +103,22 @@ class PreferencesController {
getFrequentRpcList () { getFrequentRpcList () {
return this.store.getState().frequentRpcList return this.store.getState().frequentRpcList
} }
setFeatureFlag (feature, activated) {
const currentFeatureFlags = this.store.getState().featureFlags
const updatedFeatureFlags = {
...currentFeatureFlags,
[feature]: activated,
}
this.store.updateState({ featureFlags: updatedFeatureFlags })
return Promise.resolve(updatedFeatureFlags)
}
getFeatureFlags () {
return this.store.getState().featureFlags
}
// //
// PRIVATE METHODS // PRIVATE METHODS
// //

@ -233,6 +233,10 @@ module.exports = class TransactionController extends EventEmitter {
this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry') this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
} }
async updateTransaction (txMeta) {
this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
}
async updateAndApproveTransaction (txMeta) { async updateAndApproveTransaction (txMeta) {
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
await this.approveTransaction(txMeta.id) await this.approveTransaction(txMeta.id)

@ -42,6 +42,17 @@ ConfigManager.prototype.getData = function () {
return this.store.getState() return this.store.getState()
} }
ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
data.forgottenPassword = passwordForgottenState
this.setData(data)
}
ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
return data.forgottenPassword
}
ConfigManager.prototype.setWallet = function (wallet) { ConfigManager.prototype.setWallet = function (wallet) {
var data = this.getData() var data = this.getData()
data.wallet = wallet data.wallet = wallet

@ -0,0 +1,10 @@
module.exports = function environmentType () {
const url = window.location.href
if (url.match(/popup.html$/)) {
return 'popup'
} else if (url.match(/home.html$/)) {
return 'responsive'
} else {
return 'notification'
}
}

@ -1,6 +1,9 @@
module.exports = function isPopupOrNotification () { module.exports = function isPopupOrNotification () {
const url = window.location.href const url = window.location.href
if (url.match(/popup.html$/)) { // if (url.match(/popup.html$/) || url.match(/home.html$/)) {
// Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js)
// Revert below regexes to above commented out regexes before merge to master
if (url.match(/popup.html(?:\?.+)*$/) || url.match(/home.html(?:\?.+)*$/)) {
return 'popup' return 'popup'
} else { } else {
return 'notification' return 'notification'

@ -1,5 +1,5 @@
const extension = require('extensionizer') const extension = require('extensionizer')
const height = 520 const height = 620
const width = 360 const width = 360

@ -310,6 +310,7 @@ module.exports = class MetamaskController extends EventEmitter {
{ {
lostAccounts: this.configManager.getLostAccounts(), lostAccounts: this.configManager.getLostAccounts(),
seedWords: this.configManager.getSeedWords(), seedWords: this.configManager.getSeedWords(),
forgottenPassword: this.configManager.getPasswordForgotten(),
} }
) )
} }
@ -330,7 +331,10 @@ module.exports = class MetamaskController extends EventEmitter {
// etc // etc
getState: (cb) => cb(null, this.getState()), getState: (cb) => cb(null, this.getState()),
setCurrentCurrency: this.setCurrentCurrency.bind(this), setCurrentCurrency: this.setCurrentCurrency.bind(this),
setUseBlockie: this.setUseBlockie.bind(this),
markAccountsFound: this.markAccountsFound.bind(this), markAccountsFound: this.markAccountsFound.bind(this),
markPasswordForgotten: this.markPasswordForgotten.bind(this),
unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this),
// coinbase // coinbase
buyEth: this.buyEth.bind(this), buyEth: this.buyEth.bind(this),
@ -348,13 +352,16 @@ module.exports = class MetamaskController extends EventEmitter {
submitPassword: nodeify(keyringController.submitPassword, keyringController), submitPassword: nodeify(keyringController.submitPassword, keyringController),
// network management // network management
setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
setProviderType: nodeify(networkController.setProviderType, networkController), setProviderType: nodeify(networkController.setProviderType, networkController),
setCustomRpc: nodeify(this.setCustomRpc, this), setCustomRpc: nodeify(this.setCustomRpc, this),
// PreferencesController // PreferencesController
setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController), setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController),
addToken: nodeify(preferencesController.addToken, preferencesController), addToken: nodeify(preferencesController.addToken, preferencesController),
removeToken: nodeify(preferencesController.removeToken, preferencesController),
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
// AddressController // AddressController
setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController), setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController),
@ -369,6 +376,7 @@ module.exports = class MetamaskController extends EventEmitter {
// txController // txController
cancelTransaction: nodeify(txController.cancelTransaction, txController), cancelTransaction: nodeify(txController.cancelTransaction, txController),
updateTransaction: nodeify(txController.updateTransaction, txController),
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
retryTransaction: nodeify(this.retryTransaction, this), retryTransaction: nodeify(this.retryTransaction, this),
@ -789,6 +797,18 @@ module.exports = class MetamaskController extends EventEmitter {
cb(null, this.getState()) cb(null, this.getState())
} }
markPasswordForgotten(cb) {
this.configManager.setPasswordForgotten(true)
this.sendUpdate()
cb()
}
unMarkPasswordForgotten(cb) {
this.configManager.setPasswordForgotten(false)
this.sendUpdate()
cb()
}
restoreOldVaultAccounts (migratorOutput) { restoreOldVaultAccounts (migratorOutput) {
const { serialized } = migratorOutput const { serialized } = migratorOutput
return this.keyringController.restoreKeyring(serialized) return this.keyringController.restoreKeyring(serialized)
@ -856,6 +876,15 @@ module.exports = class MetamaskController extends EventEmitter {
return rpcTarget return rpcTarget
} }
setUseBlockie (val, cb) {
try {
this.preferencesController.setUseBlockie(val)
cb(null)
} catch (err) {
cb(err)
}
}
recordFirstTimeInfo (initState) { recordFirstTimeInfo (initState) {
if (!('firstTimeInfo' in initState)) { if (!('firstTimeInfo' in initState)) {
initState.firstTimeInfo = { initState.firstTimeInfo = {

@ -17,6 +17,11 @@ class ExtensionPlatform {
return extension.runtime.getManifest().version return extension.runtime.getManifest().version
} }
openExtensionInBrowser () {
const extensionURL = extension.runtime.getURL('home.html')
this.openWindow({ url: extensionURL })
}
getPlatformInfo (cb) { getPlatformInfo (cb) {
try { try {
extension.runtime.getPlatformInfo((platform) => { extension.runtime.getPlatformInfo((platform) => {

@ -1,6 +1,7 @@
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter
const async = require('async') const async = require('async')
const Dnode = require('dnode') const Dnode = require('dnode')
const Eth = require('ethjs')
const EthQuery = require('eth-query') const EthQuery = require('eth-query')
const launchMetamaskUi = require('../../ui') const launchMetamaskUi = require('../../ui')
const StreamProvider = require('web3-stream-provider') const StreamProvider = require('web3-stream-provider')
@ -34,6 +35,7 @@ function setupWeb3Connection (connectionStream) {
providerStream.on('error', console.error.bind(console)) providerStream.on('error', console.error.bind(console))
global.ethereumProvider = providerStream global.ethereumProvider = providerStream
global.ethQuery = new EthQuery(providerStream) global.ethQuery = new EthQuery(providerStream)
global.eth = new Eth(providerStream)
} }
function setupControllerConnection (connectionStream, cb) { function setupControllerConnection (connectionStream, cb) {

@ -1,5 +1,6 @@
const injectCss = require('inject-css') const injectCss = require('inject-css')
const MetaMaskUiCss = require('../../ui/css') const OldMetaMaskUiCss = require('../../old-ui/css')
const NewMetaMaskUiCss = require('../../ui/css')
const startPopup = require('./popup-core') const startPopup = require('./popup-core')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const isPopupOrNotification = require('./lib/is-popup-or-notification') const isPopupOrNotification = require('./lib/is-popup-or-notification')
@ -17,8 +18,8 @@ const release = global.platform.getVersion()
setupRaven({ release }) setupRaven({ release })
// inject css // inject css
const css = MetaMaskUiCss() // const css = MetaMaskUiCss()
injectCss(css) // injectCss(css)
// identify window type (popup, notification) // identify window type (popup, notification)
const windowType = isPopupOrNotification() const windowType = isPopupOrNotification()
@ -33,8 +34,30 @@ const connectionStream = new PortStream(extensionPort)
const container = document.getElementById('app-content') const container = document.getElementById('app-content')
startPopup({ container, connectionStream }, (err, store) => { startPopup({ container, connectionStream }, (err, store) => {
if (err) return displayCriticalError(err) if (err) return displayCriticalError(err)
// Code commented out until we begin auto adding users to NewUI
// const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
// const firstTime = Object.keys(identities).length === 0
const { isMascara, featureFlags = {} } = store.getState().metamask
let betaUIState = featureFlags.betaUI
// Code commented out until we begin auto adding users to NewUI
// const useBetaCss = isMascara || firstTime || betaUIState
const useBetaCss = isMascara || betaUIState
let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
let deleteInjectedCss = injectCss(css)
let newBetaUIState
store.subscribe(() => { store.subscribe(() => {
const state = store.getState() const state = store.getState()
newBetaUIState = state.metamask.featureFlags.betaUI
if (newBetaUIState !== betaUIState) {
deleteInjectedCss()
betaUIState = newBetaUIState
css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
deleteInjectedCss = injectCss(css)
}
if (state.appState.shouldClose) notificationManager.closePopup() if (state.appState.shouldClose) notificationManager.closePopup()
}) })
}) })

@ -0,0 +1,26 @@
module.exports = {
"confirm sig requests": {
signMessage: (msgData, cb) => {
const stateUpdate = {
unapprovedMsgs: {},
unapprovedMsgCount: 0,
}
return cb(null, stateUpdate)
},
signPersonalMessage: (msgData, cb) => {
const stateUpdate = {
unapprovedPersonalMsgs: {},
unapprovedPersonalMsgsCount: 0,
}
return cb(null, stateUpdate)
},
signTypedMessage: (msgData, cb) => {
const stateUpdate = {
unapprovedTypedMessages: {},
unapprovedTypedMessagesCount: 0,
}
return cb(null, stateUpdate)
},
},
}

@ -37,3 +37,8 @@ apis.forEach(function (api) {
extension.runtime.reload = noop extension.runtime.reload = noop
extension.tabs.create = noop extension.tabs.create = noop
extension.runtime.getManifest = function () {
return {
version: 'development'
}
}

@ -11,7 +11,14 @@ function NewComponent () {
NewComponent.prototype.render = function () { NewComponent.prototype.render = function () {
const props = this.props const props = this.props
let { states, selectedKey, actions, store } = props let {
states,
selectedKey,
actions,
store,
modifyBackgroundConnection,
backGroundConnectionModifiers,
} = props
const state = this.state || {} const state = this.state || {}
const selected = state.selected || selectedKey const selected = state.selected || selectedKey
@ -23,6 +30,8 @@ NewComponent.prototype.render = function () {
value: selected, value: selected,
onChange:(event) => { onChange:(event) => {
const selectedKey = event.target.value const selectedKey = event.target.value
const backgroundConnectionModifier = backGroundConnectionModifiers[selectedKey]
modifyBackgroundConnection(backgroundConnectionModifier || {})
store.dispatch(actions.update(selectedKey)) store.dispatch(actions.update(selectedKey))
this.setState({ selected: selectedKey }) this.setState({ selected: selectedKey })
}, },

@ -0,0 +1,132 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": null,
"gasPrice": null,
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": "",
"to": "",
"amount": "0x0",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accountDetail",
"detailView": null,
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -0,0 +1,154 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedTxs": {
"4768706228115573": {
"id": 4768706228115573,
"time": 1487363153561,
"status": "unapproved",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"txParams": {
"from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"value": "0x1bc16d674ec80000",
"metamaskId": 4768706228115573,
"metamaskNetworkId": "3",
"gas": "0xea60",
"gasPrice": "0xba43b7400"
}
}
},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"balance": "0x37452b1315889f80"
},
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"amount": "0x1bc16d674ec80000",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "confTx",
"detailView": null,
"context": 0
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@
"frequentRpcList": [], "frequentRpcList": [],
"unapprovedTxs": {}, "unapprovedTxs": {},
"currentCurrency": "USD", "currentCurrency": "USD",
"featureFlags": {"betaUI": false},
"conversionRate": 12.7527416, "conversionRate": 12.7527416,
"conversionDate": 1487624341, "conversionDate": 1487624341,
"noActiveNotices": false, "noActiveNotices": false,
@ -34,7 +35,8 @@
"type": "testnet" "type": "testnet"
}, },
"shapeShiftTxList": [], "shapeShiftTxList": [],
"lostAccounts": [] "lostAccounts": [],
"tokens": []
}, },
"appState": { "appState": {
"menuOpen": false, "menuOpen": false,
@ -47,7 +49,12 @@
}, },
"transForward": true, "transForward": true,
"isLoading": false, "isLoading": false,
"warning": null "warning": null,
"modal": {
"modalState": {"name": null},
"open": false,
"previousModalState": {"name": null}
}
}, },
"identities": {}, "identities": {},
"computedBalances": {} "computedBalances": {}

@ -0,0 +1,154 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedTxs": {
"4768706228115573": {
"id": 4768706228115573,
"time": 1487363153561,
"status": "unapproved",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"txParams": {
"from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"value": "0x1bc16d674ec80000",
"metamaskId": 4768706228115573,
"metamaskNetworkId": "3",
"gas": "0xea60",
"gasPrice": "0xba43b7400"
}
}
},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"balance": "0x37452b1315889f80"
},
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"amount": "0x1bc16d674ec80000",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "confTx",
"detailView": null,
"context": 0
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -0,0 +1,133 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": null,
"gasPrice": null,
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": "",
"to": "",
"amount": "0x0",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accountDetail",
"detailView": null,
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -4,8 +4,8 @@ metamascara:
ports: ports:
- "9001" - "9001"
environment: environment:
MASCARA_ORIGIN: "https://zero.metamask.io" MASCARA_ORIGIN: "https://wallet.metamask.io"
VIRTUAL_PORT: "9001" VIRTUAL_PORT: "9001"
VIRTUAL_HOST: "zero.metamask.io" VIRTUAL_HOST: "wallet.metamask.io"
LETSENCRYPT_HOST: "zero.metamask.io" LETSENCRYPT_HOST: "wallet.metamask.io"
LETSENCRYPT_EMAIL: "admin@metamask.io" LETSENCRYPT_EMAIL: "admin@metamask.io"

@ -21,10 +21,18 @@ var replace = require('gulp-replace')
var mkdirp = require('mkdirp') var mkdirp = require('mkdirp')
var asyncEach = require('async/each') var asyncEach = require('async/each')
var exec = require('child_process').exec var exec = require('child_process').exec
var sass = require('gulp-sass')
var autoprefixer = require('gulp-autoprefixer')
var gulpStylelint = require('gulp-stylelint')
var stylefmt = require('gulp-stylefmt')
var uglify = require('gulp-uglify-es').default
var babel = require('gulp-babel')
var disableDebugTools = gutil.env.disableDebugTools var disableDebugTools = gutil.env.disableDebugTools
var debug = gutil.env.debug var debug = gutil.env.debug
// browser reload // browser reload
gulp.task('dev:reload', function() { gulp.task('dev:reload', function() {
@ -203,14 +211,55 @@ const jsFiles = [
'popup', 'popup',
] ]
// scss compilation and autoprefixing tasks
gulp.task('build:scss', function () {
return gulp.src('ui/app/css/index.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(autoprefixer())
.pipe(gulp.dest('ui/app/css/output'))
})
gulp.task('watch:scss', function() {
gulp.watch(['ui/app/css/**/*.scss'], gulp.series(['build:scss']))
})
gulp.task('lint-scss', function() {
return gulp
.src('ui/app/css/itcss/**/*.scss')
.pipe(gulpStylelint({
reporters: [
{formatter: 'string', console: true}
],
fix: true,
}));
});
gulp.task('fmt-scss', function () {
return gulp.src('ui/app/css/itcss/**/*.scss')
.pipe(stylefmt())
.pipe(gulp.dest('ui/app/css/itcss'));
});
// bundle tasks // bundle tasks
var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`) var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`)
var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`) var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`)
jsFiles.forEach((jsFile) => { jsFiles.forEach((jsFile) => {
gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, label: jsFile, filename: `${jsFile}.js` })) gulp.task(`dev:js:${jsFile}`, bundleTask({
gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` })) watch: true,
label: jsFile,
filename: `${jsFile}.js`,
isBuild: false
}))
gulp.task(`build:js:${jsFile}`, bundleTask({
watch: false,
label: jsFile,
filename: `${jsFile}.js`,
isBuild: true
}))
}) })
// inpage must be built before all other scripts: // inpage must be built before all other scripts:
@ -244,12 +293,18 @@ gulp.task('zip:edge', zipTask('edge'))
gulp.task('zip:opera', zipTask('opera')) gulp.task('zip:opera', zipTask('opera'))
gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera')) gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera'))
// set env var for production
gulp.task('apply-prod-environment', function(done) {
process.env.NODE_ENV = 'production'
done()
});
// high level tasks // high level tasks
gulp.task('dev', gulp.series('dev:js', 'copy', gulp.parallel('copy:watch', 'dev:reload'))) gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload')))
gulp.task('build', gulp.series('clean', gulp.parallel('build:js', 'copy'))) gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy')))
gulp.task('dist', gulp.series('build', 'zip')) gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip'))
// task generators // task generators
@ -276,7 +331,7 @@ function zipTask(target) {
return () => { return () => {
return gulp.src(`dist/${target}/**`) return gulp.src(`dist/${target}/**`)
.pipe(zip(`metamask-${target}-${manifest.version}.zip`)) .pipe(zip(`metamask-${target}-${manifest.version}.zip`))
.pipe(gulp.dest('builds')); .pipe(gulp.dest('builds'))
} }
} }
@ -342,7 +397,6 @@ function bundleTask(opts) {
throw err throw err
} }
}) })
// convert bundle stream to gulp vinyl stream // convert bundle stream to gulp vinyl stream
.pipe(source(opts.filename)) .pipe(source(opts.filename))
// inject variables into bundle // inject variables into bundle
@ -352,6 +406,8 @@ function bundleTask(opts) {
// sourcemaps // sourcemaps
// loads map from browserify file // loads map from browserify file
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true }))) .pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
// Minification
.pipe(gulpif(opts.isBuild, uglify()))
// writes .map file // writes .map file
.pipe(gulpif(debug, sourcemaps.write('./'))) .pipe(gulpif(debug, sourcemaps.write('./')))
// write completed bundles // write completed bundles

@ -2,6 +2,7 @@ const path = require('path')
const express = require('express') const express = require('express')
const createBundle = require('./util').createBundle const createBundle = require('./util').createBundle
const serveBundle = require('./util').serveBundle const serveBundle = require('./util').serveBundle
const compression = require('compression')
module.exports = createMetamascaraServer module.exports = createMetamascaraServer
@ -16,6 +17,8 @@ function createMetamascaraServer () {
// serve bundles // serve bundles
const server = express() const server = express()
server.use(compression())
// ui window // ui window
serveBundle(server, '/ui.js', uiBundle) serveBundle(server, '/ui.js', uiBundle)
server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') })) server.use(express.static(path.join(__dirname, '/../ui/'), { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') }))

@ -23,7 +23,9 @@ function createBundle (entryPoint) {
cache: {}, cache: {},
packageCache: {}, packageCache: {},
plugin: [watchify], plugin: [watchify],
}).transform('babelify') })
.transform('babelify')
.transform('uglifyify', { global: true })
bundler.on('update', bundle) bundler.on('update', bundle)
bundle() bundle()

@ -0,0 +1,198 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import {connect} from 'react-redux'
import {qrcode} from 'qrcode-npm'
import copyToClipboard from 'copy-to-clipboard'
import ShapeShiftForm from '../shapeshift-form'
import {buyEth, showAccountDetail} from '../../../../ui/app/actions'
const OPTION_VALUES = {
COINBASE: 'coinbase',
SHAPESHIFT: 'shapeshift',
QR_CODE: 'qr_code',
}
const OPTIONS = [
{
name: 'Direct Deposit',
value: OPTION_VALUES.QR_CODE,
},
{
name: 'Buy with Dollars',
value: OPTION_VALUES.COINBASE,
},
{
name: 'Buy with Cryptos',
value: OPTION_VALUES.SHAPESHIFT,
},
]
class BuyEtherWidget extends Component {
static propTypes = {
address: PropTypes.string,
skipText: PropTypes.string,
className: PropTypes.string,
onSkip: PropTypes.func,
goToCoinbase: PropTypes.func,
showAccountDetail: PropTypes.func,
};
state = {
selectedOption: OPTION_VALUES.QR_CODE,
};
copyToClipboard = () => {
const { address } = this.props
this.setState({ justCopied: true }, () => copyToClipboard(address))
setTimeout(() => this.setState({ justCopied: false }), 1000)
}
renderSkip () {
const {showAccountDetail, address, skipText, onSkip} = this.props
return (
<div
className="buy-ether__do-it-later"
onClick={() => {
if (onSkip) return onSkip()
showAccountDetail(address)
}}
>
{skipText || 'Do it later'}
</div>
)
}
renderCoinbaseLogo () {
return (
<svg width="140px" height="49px" viewBox="0 0 579 126" version="1.1">
<g id="Page-1" stroke="none" strokeWidth={1} fill="none" fillRule="evenodd">
<g id="Imported-Layers" fill="#0081C9">
<path d="M37.752,125.873 C18.824,125.873 0.369,112.307 0.369,81.549 C0.369,50.79 18.824,37.382 37.752,37.382 C47.059,37.382 54.315,39.749 59.52,43.219 L53.841,55.68 C50.371,53.156 45.166,51.579 39.961,51.579 C28.604,51.579 18.193,60.57 18.193,81.391 C18.193,102.212 28.919,111.361 39.961,111.361 C45.166,111.361 50.371,109.783 53.841,107.26 L59.52,120.036 C54.157,123.664 47.059,125.873 37.752,125.873" id="Fill-1" />
<path d="M102.898,125.873 C78.765,125.873 65.515,106.786 65.515,81.549 C65.515,56.311 78.765,37.382 102.898,37.382 C127.032,37.382 140.282,56.311 140.282,81.549 C140.282,106.786 127.032,125.873 102.898,125.873 L102.898,125.873 Z M102.898,51.105 C89.491,51.105 82.866,63.093 82.866,81.391 C82.866,99.688 89.491,111.834 102.898,111.834 C116.306,111.834 122.931,99.688 122.931,81.391 C122.931,63.093 116.306,51.105 102.898,51.105 L102.898,51.105 Z" id="Fill-2" />
<path d="M163.468,23.659 C157.79,23.659 153.215,19.243 153.215,13.88 C153.215,8.517 157.79,4.1 163.468,4.1 C169.146,4.1 173.721,8.517 173.721,13.88 C173.721,19.243 169.146,23.659 163.468,23.659 L163.468,23.659 Z M154.793,39.118 L172.144,39.118 L172.144,124.138 L154.793,124.138 L154.793,39.118 Z" id="Fill-3" />
<path d="M240.443,124.137 L240.443,67.352 C240.443,57.415 234.449,51.263 222.619,51.263 C216.31,51.263 210.473,52.367 207.003,53.787 L207.003,124.137 L189.81,124.137 L189.81,43.376 C198.328,39.906 209.212,37.382 222.461,37.382 C246.28,37.382 257.794,47.793 257.794,65.775 L257.794,124.137 L240.443,124.137" id="Fill-4" />
<path d="M303.536,125.873 C292.494,125.873 281.611,123.191 274.986,119.879 L274.986,0.314 L292.179,0.314 L292.179,41.326 C296.28,39.433 302.905,37.856 308.741,37.856 C330.667,37.856 345.494,53.629 345.494,79.656 C345.494,111.676 328.931,125.873 303.536,125.873 L303.536,125.873 Z M305.744,51.263 C301.012,51.263 295.491,52.367 292.179,54.103 L292.179,109.941 C294.703,111.045 299.593,112.149 304.482,112.149 C318.205,112.149 328.301,102.685 328.301,80.918 C328.301,62.305 319.467,51.263 305.744,51.263 L305.744,51.263 Z" id="Fill-5" />
<path d="M392.341,125.873 C367.892,125.873 355.589,115.935 355.589,99.215 C355.589,75.555 380.826,71.296 406.537,69.876 L406.537,64.513 C406.537,53.787 399.439,50.001 388.555,50.001 C380.511,50.001 370.731,52.525 365.053,55.207 L360.636,43.376 C367.419,40.379 378.933,37.382 390.29,37.382 C410.638,37.382 422.942,45.269 422.942,66.248 L422.942,119.879 C416.79,123.191 404.329,125.873 392.341,125.873 L392.341,125.873 Z M406.537,81.391 C389.186,82.337 371.835,83.757 371.835,98.9 C371.835,107.89 378.776,113.411 391.868,113.411 C397.389,113.411 403.856,112.465 406.537,111.203 L406.537,81.391 L406.537,81.391 Z" id="Fill-6" />
<path d="M461.743,125.873 C451.806,125.873 441.395,123.191 435.244,119.879 L441.08,106.629 C445.496,109.31 454.803,112.149 461.27,112.149 C470.576,112.149 476.728,107.575 476.728,100.477 C476.728,92.748 470.261,89.751 461.586,86.596 C450.228,82.337 437.452,77.132 437.452,61.201 C437.452,47.162 448.336,37.382 467.264,37.382 C477.517,37.382 486.035,39.906 492.029,43.376 L486.665,55.364 C482.88,52.998 475.309,50.317 469.157,50.317 C460.166,50.317 455.118,55.049 455.118,61.201 C455.118,68.93 461.428,71.611 469.788,74.766 C481.618,79.183 494.71,84.072 494.71,100.635 C494.71,115.935 483.038,125.873 461.743,125.873" id="Fill-7" />
<path d="M578.625,81.233 L522.155,89.12 C523.89,104.42 533.828,112.149 548.182,112.149 C556.699,112.149 565.848,110.099 571.684,106.944 L576.732,119.879 C570.107,123.349 558.75,125.873 547.078,125.873 C520.262,125.873 505.277,108.679 505.277,81.549 C505.277,55.522 519.789,37.382 543.607,37.382 C565.69,37.382 578.782,51.894 578.782,74.766 C578.782,76.816 578.782,79.025 578.625,81.233 L578.625,81.233 Z M543.292,50.001 C530.042,50.001 521.367,60.097 521.051,77.763 L562.22,72.084 C562.062,57.257 554.649,50.001 543.292,50.001 L543.292,50.001 Z" id="Fill-8" />
</g>
</g>
</svg>
)
}
renderCoinbaseForm () {
const {goToCoinbase, address} = this.props
return (
<div className="buy-ether__action-content-wrapper">
<div>{this.renderCoinbaseLogo()}</div>
<div className="buy-ether__body-text">Coinbase is the worlds most popular way to buy and sell bitcoin, ethereum, and litecoin.</div>
<a className="first-time-flow__link buy-ether__faq-link">What is Ethereum?</a>
<div className="buy-ether__buttons">
<button
className="first-time-flow__button"
onClick={() => goToCoinbase(address)}
>
Buy
</button>
</div>
</div>
)
}
renderContent () {
const { address } = this.props
const { justCopied } = this.state
const qrImage = qrcode(4, 'M')
qrImage.addData(address)
qrImage.make()
switch (this.state.selectedOption) {
case OPTION_VALUES.COINBASE:
return this.renderCoinbaseForm()
case OPTION_VALUES.SHAPESHIFT:
return (
<div className="buy-ether__action-content-wrapper">
<div className="shapeshift-logo" />
<div className="buy-ether__body-text">
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
</div>
<ShapeShiftForm btnClass="first-time-flow__button" />
</div>
)
case OPTION_VALUES.QR_CODE:
return (
<div className="buy-ether__action-content-wrapper">
<div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
<div className="buy-ether__body-text">Deposit Ether directly into your account.</div>
<div className="buy-ether__small-body-text">(This is the account address that MetaMask created for you to recieve funds.)</div>
<div className="buy-ether__buttons">
<button
className="first-time-flow__button"
onClick={this.copyToClipboard}
disabled={justCopied}
>
{ justCopied ? 'Copied' : 'Copy' }
</button>
</div>
</div>
)
default:
return null
}
}
render () {
const { className = '' } = this.props
const { selectedOption } = this.state
return (
<div className={`${className} buy-ether__content-wrapper`}>
<div className="buy-ether__content-headline-wrapper">
<div className="buy-ether__content-headline">Deposit Options</div>
{this.renderSkip()}
</div>
<div className="buy-ether__content">
<div className="buy-ether__side-panel">
{OPTIONS.map(({ name, value }) => (
<div
key={value}
className={classnames('buy-ether__side-panel-item', {
'buy-ether__side-panel-item--selected': value === selectedOption,
})}
onClick={() => this.setState({ selectedOption: value })}
>
<div className="buy-ether__side-panel-item-name">{name}</div>
{value === selectedOption && (
<svg viewBox="0 0 574 1024" id="si-ant-right" width="15px" height="15px">
<path d="M10 9Q0 19 0 32t10 23l482 457L10 969Q0 979 0 992t10 23q10 9 24 9t24-9l506-480q10-10 10-23t-10-23L58 9Q48 0 34 0T10 9z" />
</svg>
)}
</div>
))}
</div>
<div className="buy-ether__action-content">
{this.renderContent()}
</div>
</div>
</div>
)
}
}
export default connect(
({ metamask: { selectedAddress } }) => ({
address: selectedAddress,
}),
dispatch => ({
goToCoinbase: address => dispatch(buyEth({ network: '1', address, amount: 0 })),
showAccountDetail: address => dispatch(showAccountDetail(address)),
})
)(BuyEtherWidget)

@ -1,5 +1,6 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import {connect} from 'react-redux'; import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import classnames from 'classnames' import classnames from 'classnames'
import shuffle from 'lodash.shuffle' import shuffle from 'lodash.shuffle'
import {compose, onlyUpdateForPropTypes} from 'recompose' import {compose, onlyUpdateForPropTypes} from 'recompose'

@ -1,10 +1,11 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Breadcrumbs extends Component { export default class Breadcrumbs extends Component {
static propTypes = { static propTypes = {
total: PropTypes.number, total: PropTypes.number,
currentIndex: PropTypes.number currentIndex: PropTypes.number,
}; };
render() { render() {

@ -1,4 +1,5 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {qrcode} from 'qrcode-npm' import {qrcode} from 'qrcode-npm'

@ -1,8 +1,11 @@
import React, {Component, PropTypes} from 'react' import EventEmitter from 'events'
import {connect} from 'react-redux'; import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {createNewVaultAndKeychain} from '../../../../ui/app/actions' import {createNewVaultAndKeychain} from '../../../../ui/app/actions'
import LoadingScreen from './loading-screen' import LoadingScreen from './loading-screen'
import Breadcrumbs from './breadcrumbs' import Breadcrumbs from './breadcrumbs'
import Mascot from '../../../../ui/app/components/mascot'
class CreatePasswordScreen extends Component { class CreatePasswordScreen extends Component {
static propTypes = { static propTypes = {
@ -10,92 +13,115 @@ class CreatePasswordScreen extends Component {
createAccount: PropTypes.func.isRequired, createAccount: PropTypes.func.isRequired,
goToImportWithSeedPhrase: PropTypes.func.isRequired, goToImportWithSeedPhrase: PropTypes.func.isRequired,
goToImportAccount: PropTypes.func.isRequired, goToImportAccount: PropTypes.func.isRequired,
next: PropTypes.func.isRequired next: PropTypes.func.isRequired,
} }
state = { state = {
password: '', password: '',
confirmPassword: '' confirmPassword: '',
} }
isValid() { constructor () {
const {password, confirmPassword} = this.state; super()
this.animationEventEmitter = new EventEmitter()
}
isValid () {
const {password, confirmPassword} = this.state
if (!password || !confirmPassword) { if (!password || !confirmPassword) {
return false; return false
} }
if (password.length < 8) { if (password.length < 8) {
return false; return false
} }
return password === confirmPassword; return password === confirmPassword
} }
createAccount = () => { createAccount = () => {
if (!this.isValid()) { if (!this.isValid()) {
return; return
} }
const {password} = this.state; const {password} = this.state
const {createAccount, next} = this.props; const {createAccount, next} = this.props
createAccount(password) createAccount(password)
.then(next); .then(next)
} }
render() { render () {
const { isLoading, goToImportAccount, goToImportWithSeedPhrase } = this.props const { isLoading, goToImportWithSeedPhrase } = this.props
return isLoading return isLoading
? <LoadingScreen loadingMessage="Creating your new account" /> ? <LoadingScreen loadingMessage="Creating your new account" />
: ( : (
<div className="create-password"> <div>
<div className="create-password__title"> <h2 className="alpha-warning">Warning: This is Experimental software and is a Developer BETA</h2>
Create Password <div className="first-view-main">
<div className="mascara-info">
<Mascot
animationEventEmitter={this.animationEventEmitter}
width="225"
height="225"
/>
<div className="info">
MetaMask is a secure identity vault for Ethereum.
</div>
<div className="info">
It allows you to hold ether & tokens, and interact with decentralized applications.
</div>
</div>
<div className="create-password">
<div className="create-password__title">
Create Password
</div>
<input
className="first-time-flow__input"
type="password"
placeholder="New Password (min 8 characters)"
onChange={e => this.setState({password: e.target.value})}
/>
<input
className="first-time-flow__input create-password__confirm-input"
type="password"
placeholder="Confirm Password"
onChange={e => this.setState({confirmPassword: e.target.value})}
/>
<button
className="first-time-flow__button"
disabled={!this.isValid()}
onClick={this.createAccount}
>
Create
</button>
<a
href=""
className="first-time-flow__link create-password__import-link"
onClick={e => {
e.preventDefault()
goToImportWithSeedPhrase()
}}
>
Import with seed phrase
</a>
{ /* }
<a
href=""
className="first-time-flow__link create-password__import-link"
onClick={e => {
e.preventDefault()
goToImportAccount()
}}
>
Import an account
</a>
{ */ }
<Breadcrumbs total={3} currentIndex={0} />
</div>
</div> </div>
<input
className="first-time-flow__input"
type="password"
placeholder="New Password (min 8 characters)"
onChange={e => this.setState({password: e.target.value})}
/>
<input
className="first-time-flow__input create-password__confirm-input"
type="password"
placeholder="Confirm Password"
onChange={e => this.setState({confirmPassword: e.target.value})}
/>
<button
className="first-time-flow__button"
disabled={!this.isValid()}
onClick={this.createAccount}
>
Create
</button>
<a
href=""
className="first-time-flow__link create-password__import-link"
onClick={e => {
e.preventDefault()
goToImportWithSeedPhrase()
}}
>
Import with seed phrase
</a>
{ /* }
<a
href=""
className="first-time-flow__link create-password__import-link"
onClick={e => {
e.preventDefault()
goToImportAccount()
}}
>
Import an account
</a>
{ */ }
<Breadcrumbs total={3} currentIndex={0} />
</div> </div>
) )
} }

@ -1,4 +1,5 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import classnames from 'classnames' import classnames from 'classnames'
import LoadingScreen from './loading-screen' import LoadingScreen from './loading-screen'

@ -1,7 +1,14 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import LoadingScreen from './loading-screen' import LoadingScreen from './loading-screen'
import {createNewVaultAndRestore, hideWarning, displayWarning} from '../../../../ui/app/actions' import {
createNewVaultAndRestore,
hideWarning,
displayWarning,
unMarkPasswordForgotten,
clearNotices,
} from '../../../../ui/app/actions'
class ImportSeedPhraseScreen extends Component { class ImportSeedPhraseScreen extends Component {
static propTypes = { static propTypes = {
@ -22,7 +29,7 @@ class ImportSeedPhraseScreen extends Component {
onClick = () => { onClick = () => {
const { password, seedPhrase, confirmPassword } = this.state const { password, seedPhrase, confirmPassword } = this.state
const { createNewVaultAndRestore, next, displayWarning } = this.props const { createNewVaultAndRestore, next, displayWarning, leaveImportSeedScreenState } = this.props
if (seedPhrase.split(' ').length !== 12) { if (seedPhrase.split(' ').length !== 12) {
this.warning = 'Seed Phrases are 12 words long' this.warning = 'Seed Phrases are 12 words long'
@ -42,6 +49,7 @@ class ImportSeedPhraseScreen extends Component {
return return
} }
this.warning = null this.warning = null
leaveImportSeedScreenState()
createNewVaultAndRestore(password, seedPhrase) createNewVaultAndRestore(password, seedPhrase)
.then(next) .then(next)
} }
@ -67,27 +75,37 @@ class ImportSeedPhraseScreen extends Component {
<div className="import-account__selector-label"> <div className="import-account__selector-label">
Enter your secret twelve word phrase here to restore your vault. Enter your secret twelve word phrase here to restore your vault.
</div> </div>
<textarea <div className="import-account__input-wrapper">
className="import-account__secret-phrase" <label className="import-account__input-label">Wallet Seed</label>
onChange={e => this.setState({seedPhrase: e.target.value})} <textarea
/> className="import-account__secret-phrase"
onChange={e => this.setState({seedPhrase: e.target.value})}
placeholder="Separate each word with a single space"
/>
</div>
<span <span
className="error" className="error"
> >
{this.props.warning} {this.props.warning}
</span> </span>
<input <div className="import-account__input-wrapper">
className="first-time-flow__input" <label className="import-account__input-label">New Password</label>
type="password" <input
placeholder="New Password (min 8 characters)" className="first-time-flow__input"
onChange={e => this.setState({password: e.target.value})} type="password"
/> placeholder="New Password (min 8 characters)"
<input onChange={e => this.setState({password: e.target.value})}
className="first-time-flow__input create-password__confirm-input" />
type="password" </div>
placeholder="Confirm Password" <div className="import-account__input-wrapper">
onChange={e => this.setState({confirmPassword: e.target.value})} <label className="import-account__input-label">Confirm Password</label>
/> <input
className="first-time-flow__input"
type="password"
placeholder="Confirm Password"
onChange={e => this.setState({confirmPassword: e.target.value})}
/>
</div>
<button <button
className="first-time-flow__button" className="first-time-flow__button"
onClick={this.onClick} onClick={this.onClick}
@ -102,6 +120,9 @@ class ImportSeedPhraseScreen extends Component {
export default connect( export default connect(
({ appState: { isLoading, warning } }) => ({ isLoading, warning }), ({ appState: { isLoading, warning } }) => ({ isLoading, warning }),
dispatch => ({ dispatch => ({
leaveImportSeedScreenState: () => {
dispatch(unMarkPasswordForgotten())
},
createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)), createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)),
displayWarning: (warning) => dispatch(displayWarning(warning)), displayWarning: (warning) => dispatch(displayWarning(warning)),
hideWarning: () => dispatch(hideWarning()), hideWarning: () => dispatch(hideWarning()),

@ -1,3 +1,4 @@
.first-time-flow { .first-time-flow {
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
@ -5,6 +6,36 @@
overflow: auto; overflow: auto;
} }
.alpha-warning {
background: #f7861c;
color: #fff;
line-height: 2em;
padding-left: 2em;
}
.first-view-main {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
}
.mascara-info {
display: flex;
flex-flow: column;
margin-top: 70px;
margin-right: 10vw;
width: 35vw;
max-width: 550px;
}
.mascara-info :first-child {
align-self: flex-end;
}
.info {
font-size: 19px;
}
.create-password, .create-password,
.unique-image, .unique-image,
.tou, .tou,
@ -13,10 +44,14 @@
.buy-ether { .buy-ether {
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
margin: 67px 0 0 146px; margin: 67px 0 50px 146px;
max-width: 35rem; max-width: 35rem;
} }
.import-account {
max-width: initial;
}
@media only screen and (max-width: 575px) { @media only screen and (max-width: 575px) {
.create-password, .create-password,
.unique-image, .unique-image,
@ -104,14 +139,16 @@
.backup-phrase__title, .backup-phrase__title,
.import-account__title, .import-account__title,
.buy-ether__title { .buy-ether__title {
width: 280px;
color: #1B344D; color: #1B344D;
font-size: 40px; font-size: 40px;
font-weight: 500;
line-height: 51px; line-height: 51px;
margin-bottom: 24px; margin-bottom: 24px;
} }
.import-account__title {
margin-bottom: 10px;
}
.tou__title, .tou__title,
.backup-phrase__title { .backup-phrase__title {
width: 480px; width: 480px;
@ -257,9 +294,7 @@
.import-account__back-button:hover { .import-account__back-button:hover {
margin-bottom: 18px; margin-bottom: 18px;
color: #22232C; color: #22232C;
font-family: Montserrat Regular;
font-size: 16px; font-size: 16px;
font-weight: 500;
line-height: 21px; line-height: 21px;
} }
@ -280,6 +315,12 @@ button.backup-phrase__reveal-button:hover {
.import-account__secret-phrase { .import-account__secret-phrase {
font-size: 16px; font-size: 16px;
margin: initial;
}
.import-account__secret-phrase::placeholder {
color: #9B9B9B;
font-weight: 200;
} }
.backup-phrase__confirm-seed-options { .backup-phrase__confirm-seed-options {
@ -319,9 +360,7 @@ button.backup-phrase__confirm-seed-option:hover {
.import-account__selector-label { .import-account__selector-label {
color: #1B344D; color: #1B344D;
font-family: Montserrat Light; font-size: 16px;
font-size: 18px;
line-height: 23px;
} }
.import-account__dropdown { .import-account__dropdown {
@ -363,7 +402,6 @@ button.backup-phrase__confirm-seed-option:hover {
margin-top: 10px; margin-top: 10px;
width: 422px; width: 422px;
color: #FF001F; color: #FF001F;
font-family: Montserrat Light;
font-size: 16px; font-size: 16px;
line-height: 21px; line-height: 21px;
} }
@ -371,10 +409,8 @@ button.backup-phrase__confirm-seed-option:hover {
.import-account__input-label { .import-account__input-label {
margin-bottom: 9px; margin-bottom: 9px;
color: #1B344D; color: #1B344D;
font-family: Montserrat Light;
font-size: 18px; font-size: 18px;
line-height: 23px; line-height: 23px;
text-transform: uppercase;
} }
.import-account__input { .import-account__input {
@ -518,7 +554,7 @@ button.backup-phrase__confirm-seed-option:hover {
width: 350px; width: 350px;
font-size: 18px; font-size: 18px;
line-height: 24px; line-height: 24px;
padding: 15px 28px; padding: 15px;
border: 1px solid #CDCDCD; border: 1px solid #CDCDCD;
background-color: #FFFFFF; background-color: #FFFFFF;
} }
@ -540,10 +576,10 @@ button.backup-phrase__confirm-seed-option:hover {
text-transform: uppercase; text-transform: uppercase;
margin: 35px 0 14px; margin: 35px 0 14px;
transition: 200ms ease-in-out; transition: 200ms ease-in-out;
background-color: rgba(247, 134, 28, 0.9);
} }
button.first-time-flow__button[disabled] { button.first-time-flow__button[disabled] {
background-color: rgba(247, 134, 28, 0.9);
opacity: .6; opacity: .6;
} }

@ -1,4 +1,5 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import CreatePasswordScreen from './create-password-screen' import CreatePasswordScreen from './create-password-screen'
import UniqueImageScreen from './unique-image-screen' import UniqueImageScreen from './unique-image-screen'
@ -6,7 +7,10 @@ import NoticeScreen from './notice-screen'
import BackupPhraseScreen from './backup-phrase-screen' import BackupPhraseScreen from './backup-phrase-screen'
import ImportAccountScreen from './import-account-screen' import ImportAccountScreen from './import-account-screen'
import ImportSeedPhraseScreen from './import-seed-phrase-screen' import ImportSeedPhraseScreen from './import-seed-phrase-screen'
import {onboardingBuyEthView} from '../../../../ui/app/actions' import {
onboardingBuyEthView,
unMarkPasswordForgotten,
} from '../../../../ui/app/actions'
class FirstTimeFlow extends Component { class FirstTimeFlow extends Component {
@ -32,6 +36,7 @@ class FirstTimeFlow extends Component {
NOTICE: 'notice', NOTICE: 'notice',
BACK_UP_PHRASE: 'back_up_phrase', BACK_UP_PHRASE: 'back_up_phrase',
CONFIRM_BACK_UP_PHRASE: 'confirm_back_up_phrase', CONFIRM_BACK_UP_PHRASE: 'confirm_back_up_phrase',
LOADING: 'loading',
}; };
constructor (props) { constructor (props) {
@ -50,11 +55,15 @@ class FirstTimeFlow extends Component {
isInitialized, isInitialized,
seedWords, seedWords,
noActiveNotices, noActiveNotices,
forgottenPassword,
} = this.props } = this.props
const {SCREEN_TYPE} = FirstTimeFlow const {SCREEN_TYPE} = FirstTimeFlow
// return SCREEN_TYPE.NOTICE // return SCREEN_TYPE.NOTICE
if (forgottenPassword) {
return SCREEN_TYPE.IMPORT_SEED_PHRASE
}
if (!isInitialized) { if (!isInitialized) {
return SCREEN_TYPE.CREATE_PASSWORD return SCREEN_TYPE.CREATE_PASSWORD
} }
@ -70,7 +79,13 @@ class FirstTimeFlow extends Component {
renderScreen () { renderScreen () {
const {SCREEN_TYPE} = FirstTimeFlow const {SCREEN_TYPE} = FirstTimeFlow
const {goToBuyEtherView, address} = this.props const {
goToBuyEtherView,
address,
restoreCreatePasswordScreen,
forgottenPassword,
leaveImportSeedScreenState,
} = this.props
switch (this.state.screenType) { switch (this.state.screenType) {
case SCREEN_TYPE.CREATE_PASSWORD: case SCREEN_TYPE.CREATE_PASSWORD:
@ -91,8 +106,14 @@ class FirstTimeFlow extends Component {
case SCREEN_TYPE.IMPORT_SEED_PHRASE: case SCREEN_TYPE.IMPORT_SEED_PHRASE:
return ( return (
<ImportSeedPhraseScreen <ImportSeedPhraseScreen
back={() => this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)} back={() => {
next={() => this.setScreenType(SCREEN_TYPE.NOTICE)} leaveImportSeedScreenState()
this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)
}}
next={() => {
const newScreenType = forgottenPassword ? null : SCREEN_TYPE.NOTICE
this.setScreenType(newScreenType)
}}
/> />
) )
case SCREEN_TYPE.UNIQUE_IMAGE: case SCREEN_TYPE.UNIQUE_IMAGE:
@ -129,13 +150,23 @@ class FirstTimeFlow extends Component {
} }
export default connect( export default connect(
({ metamask: { isInitialized, seedWords, noActiveNotices, selectedAddress } }) => ({ ({
metamask: {
isInitialized,
seedWords,
noActiveNotices,
selectedAddress,
forgottenPassword,
}
}) => ({
isInitialized, isInitialized,
seedWords, seedWords,
noActiveNotices, noActiveNotices,
address: selectedAddress, address: selectedAddress,
forgottenPassword,
}), }),
dispatch => ({ dispatch => ({
leaveImportSeedScreenState: () => dispatch(unMarkPasswordForgotten()),
goToBuyEtherView: address => dispatch(onboardingBuyEthView(address)), goToBuyEtherView: address => dispatch(onboardingBuyEthView(address)),
}) })
)(FirstTimeFlow) )(FirstTimeFlow)

@ -1,4 +1,5 @@
import React, {Component, PropTypes} from 'react' import React from 'react'
import PropTypes from 'prop-types'
import Spinner from './spinner' import Spinner from './spinner'
export default function LoadingScreen({ className = '', loadingMessage }) { export default function LoadingScreen({ className = '', loadingMessage }) {
@ -7,5 +8,10 @@ export default function LoadingScreen({ className = '', loadingMessage }) {
<Spinner color="#1B344D" /> <Spinner color="#1B344D" />
<div className="loading-screen__message">{loadingMessage}</div> <div className="loading-screen__message">{loadingMessage}</div>
</div> </div>
); )
}
LoadingScreen.propTypes = {
className: PropTypes.string,
loadingMessage: PropTypes.string,
} }

@ -1,10 +1,12 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import {markNoticeRead} from '../../../../ui/app/actions' import {markNoticeRead} from '../../../../ui/app/actions'
import Identicon from '../../../../ui/app/components/identicon' import Identicon from '../../../../ui/app/components/identicon'
import Breadcrumbs from './breadcrumbs' import Breadcrumbs from './breadcrumbs'
import LoadingScreen from './loading-screen'
class NoticeScreen extends Component { class NoticeScreen extends Component {
static propTypes = { static propTypes = {
@ -12,25 +14,26 @@ class NoticeScreen extends Component {
lastUnreadNotice: PropTypes.shape({ lastUnreadNotice: PropTypes.shape({
title: PropTypes.string, title: PropTypes.string,
date: PropTypes.string, date: PropTypes.string,
body: PropTypes.string body: PropTypes.string,
}), }),
next: PropTypes.func.isRequired next: PropTypes.func.isRequired,
markNoticeRead: PropTypes.func,
}; };
static defaultProps = { static defaultProps = {
lastUnreadNotice: {} lastUnreadNotice: {},
}; };
state = { state = {
atBottom: false, atBottom: false,
} }
componentDidMount() { componentDidMount () {
this.onScroll() this.onScroll()
} }
acceptTerms = () => { acceptTerms = () => {
const { markNoticeRead, lastUnreadNotice, next } = this.props; const { markNoticeRead, lastUnreadNotice, next } = this.props
const defer = markNoticeRead(lastUnreadNotice) const defer = markNoticeRead(lastUnreadNotice)
.then(() => this.setState({ atBottom: false })) .then(() => this.setState({ atBottom: false }))
@ -43,50 +46,53 @@ class NoticeScreen extends Component {
if (this.state.atBottom) return if (this.state.atBottom) return
const target = document.querySelector('.tou__body') const target = document.querySelector('.tou__body')
const {scrollTop, offsetHeight, scrollHeight} = target; const {scrollTop, offsetHeight, scrollHeight} = target
const atBottom = scrollTop + offsetHeight >= scrollHeight; const atBottom = scrollTop + offsetHeight >= scrollHeight
this.setState({atBottom: atBottom}) this.setState({atBottom: atBottom})
}, 25) }, 25)
render() { render () {
const { const {
address, address,
lastUnreadNotice: { title, body } lastUnreadNotice: { title, body },
} = this.props; isLoading,
} = this.props
const { atBottom } = this.state const { atBottom } = this.state
return ( return (
<div isLoading
className="tou" ? <LoadingScreen />
onScroll={this.onScroll} : <div
> className="tou"
<Identicon address={address} diameter={70} /> onScroll={this.onScroll}
<div className="tou__title">{title}</div>
<Markdown
className="tou__body markdown"
source={body}
skipHtml
/>
<button
className="first-time-flow__button"
onClick={atBottom && this.acceptTerms}
disabled={!atBottom}
> >
Accept <Identicon address={address} diameter={70} />
</button> <div className="tou__title">{title}</div>
<Breadcrumbs total={3} currentIndex={2} /> <Markdown
</div> className="tou__body markdown"
source={body}
skipHtml
/>
<button
className="first-time-flow__button"
onClick={atBottom && this.acceptTerms}
disabled={!atBottom}
>
Accept
</button>
<Breadcrumbs total={3} currentIndex={2} />
</div>
) )
} }
} }
export default connect( export default connect(
({ metamask: { selectedAddress, lastUnreadNotice } }) => ({ ({ metamask: { selectedAddress, lastUnreadNotice }, appState: { isLoading } }) => ({
lastUnreadNotice, lastUnreadNotice,
address: selectedAddress address: selectedAddress,
}), }),
dispatch => ({ dispatch => ({
markNoticeRead: notice => dispatch(markNoticeRead(notice)) markNoticeRead: notice => dispatch(markNoticeRead(notice)),
}) })
)(NoticeScreen) )(NoticeScreen)

@ -1,4 +1,5 @@
import React, {Component, PropTypes} from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import Identicon from '../../../../ui/app/components/identicon' import Identicon from '../../../../ui/app/components/identicon'
import Breadcrumbs from './breadcrumbs' import Breadcrumbs from './breadcrumbs'

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

Loading…
Cancel
Save