Moved most functionality to react. Update profile still needs to be moved. And the UI needs a bit of a makeover.

This commit is contained in:
emizzle 2018-07-01 19:17:41 +10:00
parent 7701264a5c
commit 6c554ee56d
27 changed files with 1190 additions and 9671 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ node_modules/
dist/
config/production/password
config/livenet/password
chains.json

View File

View File

@ -1,587 +0,0 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */

6760
app/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

49
app/css/dapp.css Normal file
View File

@ -0,0 +1,49 @@
.logs {
background-color: black;
font-size: 14px;
color: white;
font-weight: bold;
padding: 10px;
border-radius: 8px;
}
.tab-content {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
margin: 0px;
}
.nav-tabs {
margin-bottom: 0;
}
.status-offline {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: red;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.status-online {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: mediumseagreen;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
input.form-control {
margin-right: 5px;
}

View File

@ -1,44 +0,0 @@
#profile img {
max-width: 150px;
}
#edit-profile, #edit-profile-photo {
padding-top: 10px;
margin-top: 10px;
}
#edit-profile input, #edit-profile-photo input {
margin-top: 5px;
margin-bottom: 5px;
}
.createProfileView {
display: none;
}
.editLink {
margin-top: 5px;
display: block;
}
#edit-profile-info {
display: none;
}
#tweets {
padding-top: 20px;
}
.main {
padding-top: 80px;
}
.navbar {
margin: 5px;
}
.editLink {
color: blue;
cursor: hand;
}

View File

@ -1,69 +1,21 @@
<html>
<head>
<title>Demo</title>
<link rel="stylesheet" href="css/app.css">
<script src="js/app.js"></script>
</head>
<body class="container">
<!DOCTYPE html>
<html lang="en">
<div class="navbar navbar-default navbar-fixed-top">
<div class="viewUser row">
<div class="left col-md-4">
<input name="username" placeholder="profile username"/><button class="viewUsername btn btn-primary">view profile</button>
</div>
<div class="right col-md-8" align="right">
<span class="createLink btn btn-primary">create account</span>
</div>
</div>
</div>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href="/favicon.ico">
<title>Decentralised Twitter dApp for DappCon 2018!</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="css/app.css" rel="stylesheet">
</head>
<div class="main">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="text/javascript" src="js/app.js"></script>
</body>
<div class="row createProfileView">
<div id="create-profile">
<strong>username: </strong><input type="text" class="form-control name" name="username">
<strong>description: </strong><input type="text" class="form-control description" name="description">
<button class="createProfile set btn btn-primary">create</button>
</div>
</div>
<div class="row profileView">
<div class="left col-md-4">
<div id="profile"><img alt="Username" />
<div class="profile-name">username</div>
<div class="profile-description">..Description..</div>
</div>
<span class="editLink">edit</span>
<div id="edit-profile-info">
<div id="edit-profile">
<strong>Edit Profile Description</strong>
<input type="text" class="form-control description" name="description">
<button class="editProfile set btn btn-primary">update description</button>
</div>
<div id="edit-profile-photo">
<strong>Upload Photo</strong>
<input type="file" class="form-control">
<button class="uploadFile set btn btn-primary">upload</button>
</div>
</div>
</div>
<div class="right col-md-8">
<div id="doTweet">
<input type="text" class="form-control name" name="text" placeholder="type text to be tweeted">
<button class="set btn btn-primary">tweet</button>
</div>
<div id="tweets">
<div class="tweet"></div>
</div>
</div>
</div>
</div>
</body>
</html>
</html>

View File

View File

@ -1,42 +0,0 @@
import $ from 'jquery';
import imgAvatar from '../img/avatar-default.png';
$(document).ready(function() {
$('#profile img').attr('src', imgAvatar);
$(".editLink").click(function() {
$("#edit-profile-info").slideToggle();
});
$(".createLink").click(function() {
$(".createProfileView").show();
$(".profileView").hide();
});
$(".viewUsername").click(function() {
$(".createProfileView").hide();
$(".profileView").show();
});
});
window.updateProfile = function(opts) {
let username = opts.username;
let description = opts.description;
let photo = opts.picture;
if (photo == null || photo == EmbarkJS.Storage.getUrl('')) {
//photo = "http://i.imgur.com/xAmv5AO.jpg";
photo = imgAvatar;
}
$(".profileView .profile-name").html(username);
$(".profileView .profile-description").html(description);
$(".profileView img").attr('src', photo).attr('alt', username);
};
window.addTweet = function(tweet) {
$("#tweets").prepend('<blockquote><div class="tweet well">' + tweet.text + '</div></blockquote>');
};

File diff suppressed because one or more lines are too long

18
app/js/components/App.js Normal file
View File

@ -0,0 +1,18 @@
import Header from './Header'
import Main from './Main'
import {DTwitter} from 'Embark/contracts';
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
window.EmbarkJS = EmbarkJS;
window.DTwitter = DTwitter;
const App = () => (
<div>
<Header />
<Main />
</div>
)
export default App

View File

@ -0,0 +1,136 @@
import { Button, FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap';
import { withRouter } from 'react-router-dom'
import React from 'react';
class CreateUser extends React.Component {
constructor(props, context) {
super(props, context);
// event bindings
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
// initial state
this.state = {
isLoading: false,
username: '',
description: '',
userExists: false,
usernameHasChanged: false
};
}
/**
* Events
*/
handleClick() {
this.setState({ isLoading: true });
EmbarkJS.onReady(() => {
console.log('creating account with username = ' + this.state.username + ', and description = ' + this.state.description);
DTwitter.methods.createAccount(this.state.username, this.state.description).send({ gas: 800000 }).then(() => {
console.log('account created event fired: ' + JSON.stringify(event));
// Completed of async action, set loading state back
this.setState({ isLoading: false });
this.props.history.push('/@' + this.state.username);
}).catch((err) => {
console.error(err);
this.setState({ isLoading: false, error: err.message });
});
});
}
handleChange(e) {
let state = {usernameHasChanged: true};
const input = e.target.name;
const value = e.target.value;
state[input] = value;
console.log('this.state = ' + JSON.stringify(this.state));
if (input === 'username' && value.length >= 5) {
// not loading, check username doesn't exist
if (!this.state.isLoading) {
console.log('checking if username exists: ' + value);
DTwitter.methods.userExists(value).call().then((exists) => {
console.log(`response username '${value}' exists: ${exists}`);
state.isLoading = false;
state.userExists = exists;
this.setState(state);
}).catch((err) => {
console.error(err);
state.isLoading = false;
state.userExists = exists;
state.error = err.message;
this.setState(state);
});
console.log('sent async username check, setting isLoading is true');
// set loading state while checking the contract
return this.setState({ isLoading: true });
}
// we are loading already, do nothing while we wait
return;
}
this.setState(state);
}
/**
* Helper methods
*/
getValidationState() {
if (this.state.isLoading) return null;
const length = this.state.username.length;
if(length === 0 && !this.state.usernameHasChanged) return null;
if (length <= 5) return 'error';
return this.state.userExists ? 'error' : 'success';
}
/**
* React methods
*/
render() {
const { isLoading } = this.state;
let validationState = this.getValidationState();
let isValid = validationState !== 'error';
return (
<form>
<FormGroup
controlId="formBasicText"
validationState={validationState}
>
<ControlLabel>Enter desired username</ControlLabel>
<FormControl
type="text"
value={this.state.username}
disabled={isLoading}
placeholder="@username"
onChange={this.handleChange}
name="username"
/>
<FormControl
type="text"
value={this.state.description}
placeholder="description"
onChange={this.handleChange}
name="description"
/>
<Button
bsStyle="primary"
disabled={isLoading || !isValid}
onClick={(isLoading || !isValid) ? null : this.handleClick}
>
{isLoading ? 'Loading...' : 'Create user'}
</Button>
<FormControl.Feedback />
<HelpBlock>{this.state.error || 'Usernames must be 5 or more characters.'}</HelpBlock>
</FormGroup>
</form>
);
}
}
export default withRouter(CreateUser);

View File

@ -0,0 +1,91 @@
import { Link } from 'react-router-dom'
import { Button, FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap';
import React from 'react';
// The Header creates links that can be used to navigate
// between routes.
class DoTweet extends React.Component{
constructor(props, context) {
super(props, context);
// properties pass-through
const { username, visible } = props;
console.log('Do Tweet: username = '+ username + ', visible = ' + visible);
// event bindings
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
// initial state
this.state = {
visible: visible,
username: username,
tweet: '',
tweetHasChanged: false
};
}
componentDidMount(){
}
/**
* Events
*/
handleClick(e) {
if(this.getValidationState() === 'error'){
return e.preventDefault();
}
// send tweet to the contract
DTwitter.methods.tweet(this.props.username, this.state.tweet).send({gas: 800000});
}
handleChange(e) {
let state = {tweetHasChanged: true};
state[e.target.name] = e.target.value;
this.setState(state);
}
/**
* Helper methods
*/
getValidationState() {
return ((this.state.tweet === '' && !this.state.tweetHasChanged) || (this.state.tweet.length > 0 && this.state.tweet.length <= 140)) ? null : 'error';
}
render(){
// hide when not visible
if(!this.props.visible) return null;
let validationState = this.getValidationState();
let isValid = validationState !== 'error';
return (
<form>
<FormGroup
controlId="formBasicText"
validationState={validationState}
>
<FormControl
type="text"
value={this.state.tweet}
placeholder="140 characters or less..."
onChange={this.handleChange}
name="tweet"
componentClass="textarea"
maxLength="140"
/>
<Button
bsStyle="primary"
disabled={!isValid}
onClick={!isValid ? null : this.handleClick}
>Post tweet</Button>
<FormControl.Feedback />
</FormGroup>
</form>
);
}
}
export default DoTweet

View File

@ -0,0 +1,92 @@
import { Link, withRouter } from 'react-router-dom'
import { Button, FormGroup, ControlLabel, FormControl, HelpBlock } from 'react-bootstrap';
import React from 'react';
// The Header creates links that can be used to navigate
// between routes.
class Header extends React.Component{
constructor(props, context) {
super(props, context);
// event bindings
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
// initial state
this.state = {
username: '',
usernameHasChanged: false
};
}
componentDidMount(){
//this.setState({username: this.props.match.username});
}
/**
* Events
*/
handleClick(e) {
console.log('handling click, validations state = ' + this.getValidationState());
if(this.getValidationState() === 'error'){
return e.preventDefault();
}
this.props.history.push('/@' + this.state.username);
}
handleChange(e) {
let state = {usernameHasChanged: true};
state[e.target.name] = e.target.value;
this.setState(state);
}
/**
* Helper methods
*/
getValidationState() {
return (this.state.username === '' && !this.state.usernameHasChanged) || this.state.username.length > 0 ? null : 'error';
}
render(){
let validationState = this.getValidationState();
let isValid = validationState !== 'error';
return (
<form>
<Link to='/create'>Create user</Link>
<FormGroup
controlId="formBasicText"
validationState={validationState}
>
<FormControl
type="text"
value={this.state.username}
placeholder="@username"
onChange={this.handleChange}
name="username"
/>
<Button
bsStyle="primary"
disabled={!isValid}
onClick={!isValid ? null : this.handleClick}
>Get tweets</Button>
<FormControl.Feedback />
</FormGroup>
</form>
);
}
// const Header = () => (
// <header>
// <nav>
// <ul>
// <li><Link to='/'>Home</Link></li>
// <li><Link to='/create'>Create user</Link></li>
// </ul>
// <input type="text" placeholder="@username"/><button id="btnGetUserTweets">Get Tweets</button>
// </nav>
// </header>
// )
}
export default withRouter(Header)

View File

@ -0,0 +1,9 @@
import React from 'react';
const Home = () => (
<div>
<h1>Decentralised Twitter dApp for DappCon 2018!</h1>
</div>
)
export default Home

24
app/js/components/Main.js Normal file
View File

@ -0,0 +1,24 @@
import { Switch, Route } from 'react-router-dom'
import Home from './Home'
import UserTweets from './UserTweets'
import CreateUser from './CreateUser'
import UpdateUser from './UpdateUser'
import React from 'react';
// The Main component renders one of the three provided
// Routes (provided that one matches). Both the /roster
// and /schedule routes will match any pathname that starts
// with /roster or /schedule. The / route will only match
// when the pathname is exactly the string "/"
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/@:username' component={UserTweets}/>
<Route path='/create' component={CreateUser}/>
<Route path='/update/@:username' component={UpdateUser}/>
</Switch>
</main>
)
export default Main

View File

@ -0,0 +1,7 @@
import React from 'react';
class UpdateUser extends React.Component{
}
export default UpdateUser;

View File

@ -0,0 +1,101 @@
import { Link } from 'react-router-dom';
import {Grid, Row, Col, Thumbnail, ListGroup, ListGroupItem } from 'react-bootstrap';
import React from 'react';
import imgAvatar from '../../img/avatar-default.png';
import DoTweet from './DoTweet';
// The Player looks up the player using the number parsed from
// the URL's pathname. If no player is found with the given
// number, then a "player not found" message is displayed.
class UserTweets extends React.Component {
constructor(props, context){
super(props, context);
this.state = {
user: {},
account: '',
tweets: []
};
}
componentDidMount(){
const self = this;
EmbarkJS.onReady(() => {
// get user details and update state
DTwitter.methods.users(web3.utils.keccak256(this.props.match.params.username)).call().then((user) => {
user.picture = user.picture.length > 0 ? EmbarkJS.Storage.getUrl(user.picture) : imgAvatar;
console.log('user: ' + JSON.stringify(user));
this.setState({user: user});
}).catch(console.error);
// so we can check our current node accounts to see if we are the owner of this account
web3.eth.getAccounts().then((accounts) => {
console.log('got accounts: ' + accounts);
if(accounts.length){
this.setState({account: accounts[0]});
}
}).catch(console.error);
// subscribe to tweet events
DTwitter.events.NewTweet({_from: web3.utils.keccak256(this.props.match.params.username), fromBlock: 0})
.on('data', function (event){
console.log('new tweet event fired: ' + JSON.stringify(event));
let index = parseInt(event.returnValues.index);
console.log('calling getTweet with index ' + index);
DTwitter.methods.getTweet(self.props.match.params.username, index).call().then(function(tweet) {
console.log('get tweet at index ' + index + ': ' + JSON.stringify(tweet));
let tweets = self.state.tweets;
tweets.push(tweet);
self.setState({tweets: tweets});
}).catch(function(error){
console.error('error getting tweet at index ' + index, error);
});
})
.on('changed', function (event){
console.warn('event removed: ' + JSON.stringify(event));
})
.on('error', function(error){
console.error('error occurred with tweet event: ', error);
});
});
}
render(){
const {user} = this.state;
const isEditable = this.state.account != '' && this.state.account === user.owner;
if (user === {}) {
// Render loading state ...
return (<div>Loading...</div>);
} else if (user.username === ''){
return (<div>User doesn't exist!</div>);
}else {
// Render real UI ...
const {username, description, picture} = this.state.user;
const tweetList = this.state.tweets.map(function(tweet, index){
return <ListGroupItem key={index} header={username}>{tweet}</ListGroupItem>
});
return (
<Grid>
<Row>
<Col xs={12} md={4}>
<Thumbnail src={picture} alt={username} href={isEditable ? '/update/@' + username : ''}>
<h3>{username}</h3>
<p>{description}</p>
</Thumbnail>
<DoTweet username={username} visible={isEditable}></DoTweet>
</Col>
<Col xs={12} md={8}>
<ListGroup>
{tweetList}
</ListGroup>
</Col>
</Row>
</Grid>
)
}
}
}
export default UserTweets

View File

@ -0,0 +1,171 @@
import { Alert, Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
import React from 'react';
class Storage extends React.Component {
constructor(props) {
super(props);
this.state = {
textToSave: 'hello world!',
generatedHash: '',
loadText: '',
storedText: '',
fileToUpload: null,
fileHash: '',
imageToDownload: '',
url: '',
logs: [],
storageError: ''
};
}
handleChange(e, name){
this.state[name] = e.target.value;
this.setState(this.state);
}
handleFileUpload(e){
this.setState({ fileToUpload: [e.target] });
}
addToLog(txt){
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
setText(e){
e.preventDefault();
EmbarkJS.Storage.saveText(this.state.textToSave)
.then((hash) => {
this.setState({
generatedHash: hash,
loadText: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.saveText('" + this.state.textToSave + "').then(function(hash) { })");
})
.catch((err) => {
if(err){
this.setState({storageError: err.message});
console.log("Storage saveText Error => " + err.message);
}
});
}
loadHash(e){
e.preventDefault();
EmbarkJS.Storage.get(this.state.loadText)
.then((content) => {
this.setState({storedText: content, storageError: ''});
this.addToLog("EmbarkJS.Storage.get('" + this.state.loadText + "').then(function(content) { })");
})
.catch((err) => {
if(err){
this.setState({storageError: err.message})
console.log("Storage get Error => " + err.message);
}
});
}
uploadFile(e){
e.preventDefault();
EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
.then((hash) => {
this.setState({
fileHash: hash,
imageToDownload: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.uploadFile(this.state.fileToUpload).then(function(hash) { })");
})
.catch((err) => {
if(err){
this.setState({storageError: err.message});
console.log("Storage uploadFile Error => " + err.message);
}
});
}
loadFile(e){
let _url = EmbarkJS.Storage.getUrl(this.state.imageToDownload);
this.setState({url: _url})
this.addToLog("EmbarkJS.Storage.getUrl('" + this.state.imageToDownload + "')");
}
render(){
return <React.Fragment>
{
!this.props.enabled ?
<React.Fragment>
<Alert bsStyle="warning">The node you are using does not support IPFS. Please ensure <a href="https://github.com/ipfs/js-ipfs-api#cors" target="_blank">CORS</a> is setup for the IPFS node.</Alert>
</React.Fragment> : ''
}
{
this.state.storageError !== '' ?
<Alert bsStyle="danger">{this.state.storageError}</Alert>
: ''
}
<h3>Save text to storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.textToSave}
onChange={e => this.handleChange(e, 'textToSave')} />
<Button bsStyle="primary" onClick={(e) => this.setText(e)}>Save Text</Button>
<HelpBlock>generated Hash: <span className="textHash">{this.state.generatedHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Load text from storage given an hash</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.loadText}
onChange={e => this.handleChange(e, 'loadText')} />
<Button bsStyle="primary" onClick={(e) => this.loadHash(e)}>Load</Button>
<HelpBlock>result: <span className="textHash">{this.state.storedText}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Upload file to storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="file"
onChange={(e) => this.handleFileUpload(e)} />
<Button bsStyle="primary" onClick={(e) => this.uploadFile(e)}>Upload</Button>
<HelpBlock>generated hash: <span className="fileHash">{this.state.fileHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Get file or image from storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.imageToDownload}
onChange={e => this.handleChange(e, 'imageToDownload')} />
<Button bsStyle="primary" onClick={(e) => this.loadFile(e)}>Download</Button>
<HelpBlock>file available at: <span><a href={this.state.url} target="_blank">{this.state.url}</a></span></HelpBlock>
<HelpBlock><img src={this.state.url} /></HelpBlock>
</FormGroup>
</Form>
<p>Javascript calls being made: </p>
<div className="logs">
<p>EmbarkJS.Storage.setProvider('ipfs',{'{'}server: 'localhost', port: '5001'{'}'})</p>
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>;
}
}
export default Storage;

10
app/js/dapp.js Normal file
View File

@ -0,0 +1,10 @@
import { render } from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './components/App';
import React from 'react';
render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'));

View File

@ -1,78 +0,0 @@
import $ from 'jquery';
import {DTwitter} from 'Embark/contracts';
import EmbarkJS from 'Embark/EmbarkJS';
window.EmbarkJS = EmbarkJS;
window.DTwitter = DTwitter;
let tweetEvent;
$(document).ready(function() {
// Create Account
$(".createProfile").click(function() {
let username = $(".createProfileView input[name=username]").val();
let description = $(".createProfileView input[name=description]").val();
EmbarkJS.onReady(function(){
DTwitter.methods.createAccount(username, description).send({gas: 800000});
});
});
// View Account
$(".viewUsername").click(function() {
let username = $(".viewUser input[name=username]").val();
console.log('username: ' + username + ', hash: ' + web3.utils.keccak256(username));
DTwitter.methods.users(web3.utils.keccak256(username)).call().then(function(user) {
console.log('user: ' + JSON.stringify(user));
updateProfile({
username: user.username,
description: user.description,
picture: EmbarkJS.Storage.getUrl(user.picture)
});
});
if(!tweetEvent) return;
if (tweetEvent) {
tweetEvent.stop();
}
$("#tweets").empty();
// Listen to tweets
tweetEvent = DTwitter.events.NewTweet({_from: web3.utils.keccak256(username)}, {fromBlock: 0});
tweetEvent.then(function(event) {
let index = event.args.index.toNumber();
DTwitter.methods.getTweet(username, index).call().then(function(tweet) {
addTweet({text: tweet});
});
});
});
// Edit Account Description
$("#edit-profile-info button").click(function() {
let username = $(".viewUser input[name=username]").val();
let description = $("#edit-profile-info .description").val();
DTwitter.methods.editAccount(username, description).send({gas: 800000});
});
// Upload Photo
$("#edit-profile-photo button").click(function() {
let username = $(".viewUser input[name=username]").val();
let uploadInput = $("input[type=file]");
EmbarkJS.Storage.uploadFile(uploadInput).then(function(hash) {
DTwitter.methods.updateProfilePicture(username, hash).send({gas: 800000});
});
});
// Make a Tweet
$("#doTweet button").click(function() {
let username = $(".viewUser input[name=username]").val();
let text = $("input[name=text]").val();
DTwitter.methods.tweet(username, text).send({gas: 800000});
});
});

View File

@ -35,7 +35,7 @@
},
"0x1797453304e4b69b44fb7d649a72ca80394a8b4e1d14c2cc530f5dc3ae1f0ea3": {
"name": "ENSRegistry",
"address": "0x4F11C0308398661b149727c225726b6A2f1Bd533"
"address": "0x54cD1E7d22Be240C0F8728C2Daa51336447e532D"
},
"0xf7082984e1d389aef8e723917135a24b36de42604f88aaaa240e2fb530992c5d": {
"name": "FIFSRegistrar",
@ -115,11 +115,19 @@
},
"0xe5409c181a9197409bb86c1dac705b0cea3c4e30f361b42dbdaea7e7f1729a93": {
"name": "DTwitter",
"address": "0x1aAB77723bD0a27DD85A76Ae4c6f471dC8643e15"
"address": "0xBc14A4C0A71D290ec86A6F2A4B0B26a64a9512B1"
},
"0x6d1e2e0bd96a493aaab99909e0fd4ed874e93edd9a58020916846e08876c3b01": {
"name": "FIFSRegistrar",
"address": "0xc97EB051Ea7544d2DEA2846b791212Ae938173c2"
},
"0x471afc554d47abbf4a63f004f2bb69bc54ab69908c3c21285bf2f32af72c0189": {
"name": "FIFSRegistrar",
"address": "0x89114C48c7A31a936cA79Ed170E3AA7075204D21"
},
"0x19128eed1c8800a3f992243b8dad181ab39f3e207713d027d66229f40f952a48": {
"name": "FIFSRegistrar",
"address": "0xb9C97eD16F3f9CF645d14F37D14491BFb9FaB16F"
}
}
},
@ -146,5 +154,145 @@
"address": "0x1124f45c80A2Fe638B1a9042125299bCB588A3D7"
}
}
},
"0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d": {
"contracts": {
"0x1797453304e4b69b44fb7d649a72ca80394a8b4e1d14c2cc530f5dc3ae1f0ea3": {
"name": "ENSRegistry",
"address": "0xa684Bd6FdfA41332069Dc5dE3A2c6dA2099A4012"
},
"0xe5409c181a9197409bb86c1dac705b0cea3c4e30f361b42dbdaea7e7f1729a93": {
"name": "DTwitter",
"address": "0x64b646dFA40e23fC5E681f9851861acBDa377D6d"
},
"0x6d1e2e0bd96a493aaab99909e0fd4ed874e93edd9a58020916846e08876c3b01": {
"name": "FIFSRegistrar",
"address": "0x20d298a978bE2AE9A2533A1E53c49F1A7Ea17cFC"
}
}
},
"0x2719372560498ffc102f913621fdd11c668620634b0bd19ac99f4c1b67b4a7ff": {
"contracts": {
"0x1797453304e4b69b44fb7d649a72ca80394a8b4e1d14c2cc530f5dc3ae1f0ea3": {
"name": "ENSRegistry",
"address": "0x3a305F52947bDdb1663ba48229BcbC33238790d8"
},
"0x6a770cca5f266bae333e2c802b4f0710a548ccb64661907121279190a5e8cd2b": {
"name": "FIFSRegistrar",
"address": "0x380F1cd84E746B792Aae796c62aC48783800475d"
},
"0x940f63055cb17182e1de6792164769bba2f02a4c663a79a8b1bdacb05dbfdef2": {
"name": "DTwitter",
"address": "0xc16e87a0716cAeF4560E36B2FfdaBfd0f74d4e17"
},
"0x8512591968343fa8314588a1194311ca6ba57650bae5085b1198f3508513fc09": {
"name": "FIFSRegistrar",
"address": "0x0Fa5D598ad59B6Ad9936735C82697A2f12AB9309"
},
"0x7fc0f6d624bcbcaf4649145e39c9b642b05f87c9cd62a83e75b4167bf6280661": {
"name": "FIFSRegistrar",
"address": "0xC1eaF9904B3D0C2dA8fd8feCfFB58F524Caa4235"
},
"0xdbaf82c08d54eeed3a161922e9e9cf16acc9a031ecf00e66c1a6c9a7dfcb7470": {
"name": "DTwitter",
"address": "0x2Eaf83D1421238776568b5c0d6779dCae111d3B0"
},
"0x6d1e2e0bd96a493aaab99909e0fd4ed874e93edd9a58020916846e08876c3b01": {
"name": "FIFSRegistrar",
"address": "0x6CcEa7207DF046dA53691F623EbA319D16b86cF9"
},
"0x0493e11c7d7363b628e1ecd882629c40096d4c05dd62aa0c6a6cff950e41829c": {
"name": "DTwitter",
"address": "0x782bB0785C0Fc0a19CD3f0AD6861e1E01355D497"
},
"0x82270912d9d3b43446636416c256f2fda79674a77417315ed6bb4620b52fe54b": {
"name": "DTwitter",
"address": "0x3838787BA50DbDEF323FF15dfC56F179FABb86c3"
},
"0x0639bcd42730e51cf5ecc22a06ffd7792fe85c3d774b8ba59f0d08829b914b16": {
"name": "FIFSRegistrar",
"address": "0x3A5eaaeFdB48Dc7a5F8DF1a3b036296451233FD2"
},
"0x9069e55a1671e0d65292da254624454e1164d95a59b5054226444641dc5b7241": {
"name": "DTwitter",
"address": "0x9C65d2936E38F13210ba52Db2eA66113f403F0A4"
},
"0x4d7362a677446c5c3833b1f92c495c9a4ef102f5c89baecd8ee5c7dedda43bc0": {
"name": "FIFSRegistrar",
"address": "0xd6bb0D548Cc339e93303e839D2F31A0CB8e0C7Ed"
},
"0xf39de5ffc41b8c0cd27c9428411594ad4e93df94a4d8d200dff4d616ff2c2da6": {
"name": "FIFSRegistrar",
"address": "0x6826F913EB5D5fC1b02832128DfFB51305050295"
},
"0xeef98a0087f598564ce46e90efb76c011dabe1aaaa42f2f058b7cf6aa7076a3d": {
"name": "FIFSRegistrar",
"address": "0x466C87E7d667E142dE074e7B95de2Ac0ee840d55"
},
"0x9e01145e31f3e40a45b0a87e04f331d553433c01872b51cce608ec1e7a618f53": {
"name": "FIFSRegistrar",
"address": "0x444eE0078C0eaDb191226845A8EFeA9920A1B3Ac"
},
"0xd90232b0ff53ce7e8f0f9f952727e5465d4a75883b55374a9285ef2c87487a74": {
"name": "FIFSRegistrar",
"address": "0x63E87033f39A098a4D2778f74059b747c8Fa9b2e"
},
"0x87d94e2612fa834a9fdb8198d579a2b13d5e3bb55b077c744ffe9a60f85a494a": {
"name": "FIFSRegistrar",
"address": "0x1Af8411262Bd7Bcd3653aBb81abAA3C4d3B67262"
},
"0x6111dd2ed21153ccfb4a0222c5afb3a40da7a1331876068d720d9eca1d127cff": {
"name": "FIFSRegistrar",
"address": "0x874F6416a2751A9685942ceBad5724F6552363Ba"
},
"0xc07d9ab43515dc74fc1152d38bd21e18d669d74da0996fb5336084dfc3ced879": {
"name": "FIFSRegistrar",
"address": "0x2551ECA589280510F52455A4c436A5c092750a57"
},
"0x8e9719440b59d6df083533898ef3ea6a77962dcb8ce81677aac7948ef0c20e34": {
"name": "FIFSRegistrar",
"address": "0xefc2913cA38Cc42a2c7e37d5b8772C38d8f3E6EE"
},
"0x24f84ae6b65cd098c258c29426edccff5dd244bf406691fc283b3a4d9a3c37c6": {
"name": "FIFSRegistrar",
"address": "0x303d27c6f23A67e88Ea12eFdc763AaeD1Cc14a1c"
},
"0x3a721b7e56717bee6a4962e63d8288bbbdb3dca04559a9e4f9953e212dc0df48": {
"name": "FIFSRegistrar",
"address": "0x538689FDBCDff43FF7DbD1a1bd32135986b48B7c"
},
"0x3a8831a9b9c58734d580907b3d9901e2dcf2085623a8374cb799fe728aa44457": {
"name": "FIFSRegistrar",
"address": "0xe6B9e1fE969B8Ae25d865bBAF5857D470d395A7c"
},
"0x4d366bd85cc54f5c4a83f6b40b3a3677310eb0dd34f389767d63387c27967a94": {
"name": "FIFSRegistrar",
"address": "0x848DEd3483FC46943696FD5126fC6EBea606755C"
},
"0x306a442b9d884112f40feb96d7f7c6feedfcb5e3a22f7f0fb94e2707f705e9ae": {
"name": "FIFSRegistrar",
"address": "0x8Dae7b46F857FbaB2b6CdB72E19F00c19E935e32"
},
"0x4106d6b2ac55d1b27a711efdc9958888872ea92150182b6acdbba71337ecb4b0": {
"name": "FIFSRegistrar",
"address": "0xaeF05E95a9911DDcd1C6a74bC5Ec5418f59D8721"
},
"0xfa425d3927a89290384086a8ee3094e79705d1efb5e26d9488b2afdef186b85a": {
"name": "FIFSRegistrar",
"address": "0x8Bb521a91a87bA2b45838b945f42bB38Eea75AfD"
},
"0xcc778f563e76137fa39d446d61c7eab7c4a0c7ff90022e952e7102cd393a5d7a": {
"name": "FIFSRegistrar",
"address": "0xD7C4419cF91C9B3D2d960339EC7BD9590b30D2aB"
},
"0x17e26bdacc4661c4eb3e1244d0a3f4989c2a25edd5305b89f2ffe844ce1344b1": {
"name": "FIFSRegistrar",
"address": "0xBB6599908d6a5e9B93da8D25eDA766e516a9dad4"
},
"0x848e86fb58f1f7768f1189a9d854c8dfef9da22ddeff9c61cd2e0fba36be8d7c": {
"name": "FIFSRegistrar",
"address": "0xdB823DCB3EB04b7A5d766F5001Ce007A2c2AE7fe"
}
}
}
}

View File

@ -30,6 +30,11 @@ contract DTwitter {
users[usernameHash].description = description;
}
function userExists(string username) public view returns (bool) {
bytes32 usernameHash = keccak256(abi.encodePacked(username));
return users[usernameHash].creationDate != 0;
}
function updateProfilePicture(string username, string pictureHash) public {
bytes32 usernameHash = keccak256(abi.encodePacked(username));
require(users[usernameHash].owner == msg.sender);
@ -56,7 +61,7 @@ contract DTwitter {
function getTweet(string username, uint index) public view returns(string retTweet) {
bytes32 usernameHash = keccak256(abi.encodePacked(username));
require(users[usernameHash].creationDate != 0);
return users[usernameHash].tweets[index];
}

View File

@ -1,8 +1,8 @@
{
"contracts": ["app/contracts/**"],
"contracts": ["contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"js/app.js": ["app/js/_vendor/**", "app/js/**"],
"js/app.js": ["app/js/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",

2377
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
{
"name": "DTwitter",
"name": "app_name",
"version": "0.0.1",
"description": "",
"main": "Gruntfile.js",
"scripts": {
"test": "embark test"
},
"author": "",
"license": "ISC",
"homepage": "",
"devDependencies": {
"embark": "^2.4.2",
"mocha": "^2.2.5"
},
"dependencies": {
"jquery": "^3.3.1"
"react": "^16.3.2",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.3.2",
"react-router-dom": "^4.2.2"
}
}