Skip to content
Snippets Groups Projects
Commit da1e1a36 authored by Jono Wenger's avatar Jono Wenger
Browse files

Fix ui and add test mode

parent 6b4391d2
No related branches found
No related tags found
No related merge requests found
package main package main
import ( import (
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/api" "gitlab.com/elixxir/client/api"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/contact" "gitlab.com/elixxir/client/interfaces/contact"
"gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/single"
"gitlab.com/elixxir/client/stoppable"
"gitlab.com/xx_network/primitives/utils" "gitlab.com/xx_network/primitives/utils"
"io/ioutil" "io/ioutil"
"gitlab.com/elixxir/client/single"
"os" "os"
jww "github.com/spf13/jwalterweatherman"
"time" "time"
) )
func initClient() (*api.Client, *single.Manager) { type ClientAPI interface {
StartNetworkFollower() (<-chan interfaces.ClientError, error)
GetHealth() interfaces.HealthTracker
AddService(sp api.ServiceProcess)
GetNodeRegistrationStatus() (int, int, error)
}
type SingleManager interface {
StartProcesses() stoppable.Stoppable
TransmitSingleUse(contact.Contact, []byte, string, uint8, single.ReplyComm,
time.Duration) error
}
type TestClient struct {
}
func (tc TestClient) StartNetworkFollower() (<-chan interfaces.ClientError, error) {
return nil, nil
}
func (tc TestClient) GetHealth() interfaces.HealthTracker {
return nil
}
func (tc TestClient) AddService(api.ServiceProcess) {
}
var NodeRegistrationStatusTrack = 0
func (tc TestClient) GetNodeRegistrationStatus() (int, int, error) {
NodeRegistrationStatusTrack++
return NodeRegistrationStatusTrack, 30, nil
}
type TestSingle struct {
}
func (ts TestSingle) StartProcesses() stoppable.Stoppable {
return nil
}
func (ts TestSingle) TransmitSingleUse(_ contact.Contact, payload []byte,
_ string, _ uint8, callback single.ReplyComm, _ time.Duration) error {
go func() {
time.Sleep(5 * time.Second)
// callback(payload, errors.New("ERROR"))
callback(payload, nil)
}()
// return errors.New("ERROR")
return nil
}
func initClient(test bool) (ClientAPI, SingleManager) {
if test {
time.Sleep(1 * time.Second)
return TestClient{}, TestSingle{}
}
createClient() createClient()
pass := password pass := password
...@@ -41,12 +104,11 @@ func initClient() (*api.Client, *single.Manager) { ...@@ -41,12 +104,11 @@ func initClient() (*api.Client, *single.Manager) {
return client, singleMng return client, singleMng
} }
func createClient() *api.Client { func createClient() *api.Client {
pass := password pass := password
storeDir := session storeDir := session
//create a new client if none exist // Create a new client if none exist
if _, err := os.Stat(storeDir); os.IsNotExist(err) { if _, err := os.Stat(storeDir); os.IsNotExist(err) {
// Load NDF // Load NDF
ndfJSON, err := ioutil.ReadFile(ndfPath) ndfJSON, err := ioutil.ReadFile(ndfPath)
...@@ -69,7 +131,6 @@ func createClient() *api.Client { ...@@ -69,7 +131,6 @@ func createClient() *api.Client {
return client return client
} }
func waitUntilConnected(connected chan bool) { func waitUntilConnected(connected chan bool) {
timeoutTimer := time.NewTimer(90 * time.Second) timeoutTimer := time.NewTimer(90 * time.Second)
isConnected := false isConnected := false
......
This diff is collapsed.
...@@ -4,7 +4,14 @@ go 1.13 ...@@ -4,7 +4,14 @@ go 1.13
require ( require (
github.com/dtylman/gowd v0.0.0-20190619113956-15e38debca22 github.com/dtylman/gowd v0.0.0-20190619113956-15e38debca22
github.com/nyaruka/phonenumbers v1.0.68 // indirect
github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/jwalterweatherman v1.1.0
gitlab.com/elixxir/client v1.5.1-0.20210322233828-da6ce1646ade github.com/stretchr/testify v1.7.0 // indirect
github.com/zeebo/assert v1.3.0 // indirect
gitlab.com/elixxir/client v1.5.1-0.20210323170252-7aa7a34f2682
gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20210323141857-08027d57d8cf // indirect
google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )
This diff is collapsed.
img/favicon-16x16.png

1.05 KiB

img/favicon-32x32.png

1.55 KiB

img/favicon.ico

14.7 KiB

img/xx_logo.png

4.02 KiB

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="26" height="29" viewBox="0 0 26 29">
<defs>
<path id="o9gfh6ivaa" d="M0.192 0.041L18.206 0.041 18.206 20.206 0.192 20.206z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<g>
<g>
<path fill="#037281" d="M18.957 6.552c.635 1.395.987 2.925.987 4.53 0 .051.008.1.01.151l5.252-4.666V1l-6.249 5.552z" transform="translate(-35 -42) translate(35 42)"/>
<g transform="translate(-35 -42) translate(35 42) translate(7 .959)">
<mask id="1p1nayj4gb" fill="#fff">
<use xlink:href="#o9gfh6ivaa"/>
</mask>
<path fill="#037281" d="M11.28 10.124C11.28 4.564 6.306.04.192.04v4.162c3.732 0 6.776 2.538 6.914 5.7L4.28 12.415c.752 1.312 1.245 2.768 1.411 4.313l2.522-2.241c1.792 3.38 5.6 5.719 9.994 5.719v-4.161c-3.819 0-6.926-2.656-6.926-5.921" mask="url(#1p1nayj4gb)"/>
</g>
<path fill="#037281" d="M18.015 28.93v-4.162c-3.819 0-6.927-2.656-6.927-5.92 0-5.56-4.974-10.083-11.088-10.083v4.161c3.245 0 5.969 1.92 6.717 4.5l-1.462 1.298-3.028 2.69L0 23.392v5.566l7.572-6.725c1.533 3.898 5.634 6.696 10.443 6.696" transform="translate(-35 -42) translate(35 42)"/>
</g>
</g>
</g>
</svg>
...@@ -7,7 +7,12 @@ ...@@ -7,7 +7,12 @@
<script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript" src="js/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/bootstrap.min.js"></script> <script type="text/javascript" src="js/bootstrap.min.js"></script>
<title>App</title> <script type="text/javascript" src='js/solitaireVictory.js'></script>
<script type="text/javascript" src="js/code.js"></script>
<link rel="icon" type="image/png" sizes="32x32" href="img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="img/favicon-16x16.png">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>xx Coin Game</title>
</head> </head>
<body id="app"></body> <body id="app"></body>
......
var pattern = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
var current = 0;
var keyHandler = function (event) {
// If the key isn't in the pattern, or isn't the current key in the pattern, reset
if (pattern.indexOf(event.key) < 0 || event.key !== pattern[current]) {
current = 0;
return;
}
// Update how much of the pattern is complete
current++;
// If complete, alert and reset
if (pattern.length === current) {
current = 0;
//window.alert('You found it!');
$('img').solitaireVictory({fallToLeft: true});
}
};
// Listen for keydown events
document.addEventListener('keydown', keyHandler, false);
\ No newline at end of file
(function( $ ) {
$.fn.solitaireVictory = function(settings) {
settings = settings || {};
var g = settings.g || -3;
var dt = settings.dt || 20;
var bounce = settings.bounce || 0.7;
var endVelocity = settings.endVelocity || 20;
var stagger = settings.stagger || 200;
var relativeToDocument = settings.relativeToDocument || false;
var clear = settings.clear || false;
var fallToLeft = settings.fallToLeft || false;
var body = $('body');
var windowHeight = (relativeToDocument ? $(document).height() : $(window).height());
var fallIteration = function(elem, elemHeight, oldPos, dx, dy) {
var copy = elem.clone();
body.append(copy);
var newTop = Math.min(windowHeight - elemHeight, oldPos.top + dy);
var newPos = {
left: oldPos.left + dx,
top: newTop
};
copy.offset(newPos);
if (Math.abs(newTop - (windowHeight - elemHeight)) < 5) {
if (dy < 0 || dy > endVelocity) {
dy *= -1*bounce;
setTimeout(function() {
fallIteration(copy, elemHeight, newPos, dx, dy);
}, dt);
}
} else {
dy = dy - g;
setTimeout(function() {
fallIteration(copy, elemHeight, newPos, dx, dy);
}, dt);
}
};
var startFall = function(elem, height, stagger) {
var dx = settings.dx || Math.floor((Math.random()*10)) + 5;
if (fallToLeft) {
dx = -dx;
}
var copy = elem.clone();
copy.addClass('solitaire-victory-clone');
if (relativeToDocument) {
copy.css('position', 'absolute');
} else {
copy.css('position', 'fixed');
}
var originalOffset = elem.offset();
copy.offset({top: originalOffset.top, left: originalOffset.left});
body.append(copy);
setTimeout(function() {fallIteration(copy, height, copy.offset(), dx, 0);}, stagger);
};
if (clear) $('.solitaire-victory-clone').remove();
this.each(function(index) {
var obj = $(this);
if (relativeToDocument || obj.offset().top < $(window).height()) {
if (!obj.hasClass('solitaire-victory-clone')) {
startFall(obj, obj.height(), index*stagger);
}
}
});
};
}( jQuery ));
\ No newline at end of file
...@@ -3,8 +3,11 @@ package main ...@@ -3,8 +3,11 @@ package main
import ( import (
"github.com/dtylman/gowd" "github.com/dtylman/gowd"
"github.com/dtylman/gowd/bootstrap" "github.com/dtylman/gowd/bootstrap"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces/contact" "gitlab.com/elixxir/client/interfaces/contact"
"gitlab.com/elixxir/client/single" "regexp"
"strconv"
"strings"
"time" "time"
) )
...@@ -15,32 +18,105 @@ var logPath = "client.log" ...@@ -15,32 +18,105 @@ var logPath = "client.log"
var botContactPath = "botContact.bin" var botContactPath = "botContact.bin"
var botContact contact.Contact var botContact contact.Contact
var singleMngr *single.Manager var singleMngr SingleManager
var client ClientAPI
var body *gowd.Element var body *gowd.Element
var testMode = false
func main() { func main() {
initLog() initLog()
botContact = readBotContact()
_, singleMngr = initClient()
// creates a new bootstrap fluid container // creates a new bootstrap fluid container
body = bootstrap.NewContainer(false) body = bootstrap.NewContainer(false)
// add some elements using the object model // add some elements using the object model
div := bootstrap.NewElement("div", "well") div := bootstrap.NewElement("div", "well")
row := bootstrap.NewRow(bootstrap.NewColumn(bootstrap.ColumnSmall, 3, div)) div.SetAttribute("style", "font-size:1.5em;margin-top:25px;")
body.AddElement(row) body.AddElement(div)
logo := bootstrap.NewElement("img", "")
logo.SetAttribute("src", "img/xx_logo.svg")
logo.SetAttribute("style", "float:right;margin: -10px -10px 0 0;")
logo.SetAttribute("id", "logo")
div.AddElement(logo)
progressBarTitle := bootstrap.NewElement(gowd.Heading4, "")
progressBarTitle.SetAttribute("style", "text-align:center;")
progressBarTitle.SetText("Connecting to network")
spinner := bootstrap.NewElement("span", "spinner-grow")
spinner.SetAttribute("role", "status")
spinner.SetAttribute("style", "width: 3rem; height: 3rem;text-align:center;margin:0 auto;")
spinnerContainer := bootstrap.NewElement("div", "")
spinnerContainer.SetAttribute("style", "text-align: center")
spinnerContainer.AddElement(spinner)
div.AddElement(progressBarTitle)
div.AddElement(spinnerContainer)
err := body.Render()
if err != nil {
jww.ERROR.Printf("Failed to render body: %+v", err)
}
botContact = readBotContact()
client, singleMngr = initClient(testMode)
div.RemoveElement(spinnerContainer)
div.RemoveElement(progressBarTitle)
progressBarTitle = bootstrap.NewElement(gowd.Heading4, "")
progressBarTitle.SetAttribute("style", "text-align:center;")
progressBarTitle.SetText("Generating Keys and Registering with Network")
div.AddElement(progressBarTitle)
progressBar := bootstrap.NewProgressBar()
progressBar.Kids[0].SetAttribute("style", "background:#037281;")
div.AddElement(progressBar.Element)
go func() {
for {
time.Sleep(100 * time.Millisecond)
registeredNodes, totalNodes, err := client.GetNodeRegistrationStatus()
if err != nil {
jww.FATAL.Panicf("Failed to get node registration status: %+v", err)
}
max := (totalNodes * 8) / 10
progressBar.SetText(strconv.Itoa(registeredNodes) + "/" + strconv.Itoa(totalNodes))
err = progressBar.SetValue(registeredNodes, totalNodes)
if err != nil {
jww.ERROR.Printf("Failed to set progress bar value: %+v", err)
}
err = body.Render()
if err != nil {
jww.ERROR.Printf("Failed to render body: %+v", err)
}
if registeredNodes >= max {
break
}
}
div.RemoveElement(progressBarTitle)
div.RemoveElement(progressBar.Element)
printForm(div)
}()
row.SetAttribute("style", "font-size:1.5em") // Start the ui loop
err = gowd.Run(body)
if err != nil {
jww.ERROR.Printf("Failed to start ui loop: %+v", err)
}
}
func printForm(div *gowd.Element) {
ethAddr := bootstrap.NewFormInput(bootstrap.InputTypeText, "Ethereum Address:") ethAddr := bootstrap.NewFormInput(bootstrap.InputTypeText, "Ethereum Address:")
ethAddr.Element.Kids[1].SetAttribute("style", "font-family:'Roboto Mono', 'Courier New', Courier, monospace;") ethAddr.Element.Kids[1].SetAttribute("style", "font-family:'Roboto Mono', Consolas, 'Courier New', Courier, monospace;")
sendText := bootstrap.NewFormInput(bootstrap.InputTypeText, "Message:") sendText := bootstrap.NewFormInput(bootstrap.InputTypeText, "Message:")
// FIXME: remove this
// ethAddr.SetValue("0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7")
div.AddElement(ethAddr.Element) div.AddElement(ethAddr.Element)
div.AddElement(sendText.Element) div.AddElement(sendText.Element)
...@@ -48,92 +124,142 @@ func main() { ...@@ -48,92 +124,142 @@ func main() {
btn := bootstrap.NewButton(bootstrap.ButtonPrimary, "Send over xx") btn := bootstrap.NewButton(bootstrap.ButtonPrimary, "Send over xx")
btn.SetAttribute("style", "background:#037281;background-color:#037281") btn.SetAttribute("style", "background:#037281;background-color:#037281")
rtnDiv := bootstrap.NewElement("div", "well") rtnDiv := bootstrap.NewElement("div", "well")
rtnRow := bootstrap.NewRow(bootstrap.NewColumn(bootstrap.ColumnSmall, 3, rtnDiv)) body.AddElement(rtnDiv)
body.AddElement(rtnRow)
btnEvent := func(sender *gowd.Element, event *gowd.EventElement) { btnEvent := func(sender *gowd.Element, event *gowd.EventElement) {
btnClicked(sender, event, ethAddr.Element.Kids[1], sendText.Element.Kids[1], rtnDiv) btnClicked(sender, event, ethAddr.Element.Kids[1], sendText.Element.Kids[1], rtnDiv)
} }
btn.OnEvent(gowd.OnClick, btnEvent) btn.OnEvent(gowd.OnClick, btnEvent)
div.AddElement(btn) div.AddElement(btn)
err := body.Render()
if err != nil {
// div.AddHTML(` jww.ERROR.Printf("Failed to render body: %+v", err)
// <label for="fname">Ethereum address:</label><br> }
// <input type="text" id="ethaddr" name="ethaddr"><br>
// <label for="lname">Message:</label><br>
// <input type="text" id="message" name="message"><br><br>`, nil)
//
// // add a button
// btn := bootstrap.NewButton(bootstrap.ButtonPrimary, "Send")
// btn.OnEvent(gowd.OnClick, btnClicked)
// row.AddElement(bootstrap.NewColumn(bootstrap.ColumnSmall, 3, bootstrap.NewElement("div", "well", btn)))
// start the ui loop
gowd.Run(body)
} }
var lastElement *gowd.Element var lastElement, btnMessage, ethAddrFailure *gowd.Element
// happens when the 'start' button is clicked // happens when the 'start' button is clicked
func btnClicked(sender *gowd.Element, event *gowd.EventElement, ethAddr, func btnClicked(sender *gowd.Element, _ *gowd.EventElement, ethAddr,
sendText *gowd.Element, div *gowd.Element) { sendText *gowd.Element, div *gowd.Element) {
sender.SetAttribute("disabled", "true") // makes the body stop responding to user events
sender.Parent.AddHTML(`<div class="spinner-border" role="status"> body.Disable()
<span class="sr-only">Loading...</span>
</div>`, nil) if ethAddrFailure != nil {
body.Render() ethAddr.Parent.RemoveElement(ethAddrFailure)
}
if !validEthereumAddress(ethAddr.GetValue()) {
ethAddrFailure = bootstrap.NewElement("span", bootstrap.AlertWarning+" very-small")
ethAddrFailure.SetText("Must be valid Ethereum address.")
ethAddr.Parent.AddElement(ethAddrFailure)
return
}
sender.SetAttribute("disabled", "")
spinner := bootstrap.NewElement("span", "spinner-grow")
spinner.SetAttribute("role", "status")
spinner.SetAttribute("style", "width: 3rem; height: 3rem;text-align:center;margin:0 auto;")
div.SetAttribute("style", "text-align: center")
div.AddElement(spinner)
if lastElement != nil { if lastElement != nil {
div.RemoveElement(lastElement) div.RemoveElement(lastElement)
} }
if btnMessage != nil {
sender.Parent.RemoveElement(btnMessage)
}
// adds test to the body err := body.Render()
if err != nil {
// makes the body stop responding to user events jww.ERROR.Printf("Failed to render body: %+v", err)
body.Disable() }
// Send the message // Send the message
message := ethAddr.GetValue() + ";" + sendText.GetValue() message := ethAddr.GetValue() + ";" + sendText.GetValue()
defer func() { defer func() {
time.Sleep(1 * time.Second)
sender.RemoveAttribute("disabled") sender.RemoveAttribute("disabled")
body.Render() div.RemoveElement(spinner)
div.RemoveAttribute("style")
err := body.Render()
if err != nil {
jww.ERROR.Printf("Failed to render body: %+v", err)
}
body.Enable() body.Enable()
}() }()
//text.SetText(message) replyString := make(chan string, 1)
replyString := make(chan string)
// Inline function to print message from client to page, callback for upcoming function // Inline function to print message from client to page, callback for upcoming function
replyFunc := func(payload []byte, err error) { replyFunc := func(payload []byte, err error) {
sender.Parent.RemoveElement(btnMessage)
btnMessage = bootstrap.NewElement("span", "")
var result string var result string
if err != nil { if err != nil {
result = err.Error() btnMessage.SetText("Failed to receive response.")
btnMessage.SetAttribute("style", "font-size:0.75em;padding:0.5em;color:#d62424;")
result = "ERROR: " + err.Error()
} else { } else {
btnMessage.SetText("Received response.")
btnMessage.SetAttribute("style", "font-size:0.75em;padding:0.5em;color:#24d627;")
result = string(payload) result = string(payload)
} }
replyString <- result replyString <- result
//sender.SetText("Start") sender.Parent.AddElement(btnMessage)
//body.RemoveElement(text)
//body.Enable()
} }
err := singleMngr.TransmitSingleUse(botContact, []byte(message), err = singleMngr.TransmitSingleUse(botContact, []byte(message),
"xxCoinGame", 10, replyFunc, 30*time.Second) "xxCoinGame", 10, replyFunc, 30*time.Second)
if err != nil { if err != nil {
//body.Enable()
lastElement = div.AddElement(gowd.NewStyledText(err.Error(), gowd.BoldText)) lastElement = div.AddElement(gowd.NewStyledText(err.Error(), gowd.BoldText))
//sender.SetText("Start")
//body.RemoveElement(text) if btnMessage != nil {
//body.Enable() sender.Parent.RemoveElement(btnMessage)
return
} }
result := <- replyString btnMessage = bootstrap.NewElement("span", "")
btnMessage.SetText("Message failed to send.")
btnMessage.SetAttribute("style", "font-size:0.75em;padding:0.5em;color:#d62424;")
sender.Parent.AddElement(btnMessage)
err := body.Render()
if err != nil {
jww.ERROR.Printf("Failed to render body: %+v", err)
}
return
} else {
if btnMessage != nil {
sender.Parent.RemoveElement(btnMessage)
}
time.Sleep(1 * time.Second)
btnMessage = bootstrap.NewElement("span", "")
btnMessage.SetAttribute("style", "font-size:0.75em;padding:0.5em;color:#24d627;")
sender.Parent.AddElement(btnMessage)
btnMessage.SetText("Message sent successfully. Waiting for response.")
err = body.Render()
if err != nil {
jww.ERROR.Printf("Failed to render body: %+v", err)
}
}
result := <-replyString
lastElement = div.AddElement(gowd.NewStyledText(result, gowd.BoldText)) lastElement = div.AddElement(gowd.NewStyledText(result, gowd.BoldText))
lastElementStyle := "font-size:1.25em;line-height:1.25em;"
lastElement.SetAttribute("style", lastElementStyle)
if strings.Contains(result, "ERROR: ") {
lastElement.SetAttribute("style", lastElementStyle+"color:#d62424;")
}
}
func validEthereumAddress(address string) bool {
r, err := regexp.Compile("^0x[0-9a-fA-F]{40}$")
if err != nil {
jww.ERROR.Print(err)
}
return r.MatchString(address)
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment