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

Merge branch 'jono/ui-fix' into 'master'

Fix ui and add test mode

See merge request elixxir/xx-coin-game-ui!2
parents 6b4391d2 da1e1a36
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