Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: application skeleton #100

Merged
merged 14 commits into from
Jan 15, 2020
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ markets.json
dist/
node_modules/
package-lock.json
client/cmd/dexc/dexc
27 changes: 27 additions & 0 deletions client/cmd/dexc/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module decred.org/dcrdex/client/cmd/dexc

go 1.13

replace (
decred.org/dcrdex => ../../../
github.com/ltcsuite/ltcutil => github.com/ltcsuite/ltcutil v0.0.0-20190507133322-23cdfa9fcc3d
)

require (
decred.org/dcrdex v0.0.0-00010101000000-000000000000
github.com/decred/dcrd/chaincfg/chainhash v1.0.2
github.com/decred/dcrd/dcrutil/v2 v2.0.1
github.com/decred/dcrd/gcs v1.1.0
github.com/decred/dcrd/txscript/v2 v2.1.0
github.com/decred/dcrd/wire v1.3.0
github.com/decred/dcrwallet/errors/v2 v2.0.0
github.com/decred/dcrwallet/p2p/v2 v2.0.0
github.com/decred/dcrwallet/validate v1.1.1
github.com/decred/dcrwallet/wallet/v3 v3.1.1-0.20191230143837-6a86dc4676f0
github.com/decred/slog v1.0.0
github.com/gdamore/tcell v1.3.0
github.com/jessevdk/go-flags v1.4.0
github.com/jrick/logrotate v1.0.0
github.com/rivo/tcell v1.0.0
github.com/rivo/tview v0.0.0-20191129065140-82b05c9fb329
)
178 changes: 178 additions & 0 deletions client/cmd/dexc/go.sum

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions client/cmd/dexc/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// This code is available on the terms of the project LICENSE.md file,
// also available online at https://blueoakcouncil.org/license/1.0.0.

package main

import (
"context"
"fmt"
"os"
"os/signal"
"sync"

"decred.org/dcrdex/client/cmd/dexc/ui"
"decred.org/dcrdex/client/core"
"decred.org/dcrdex/client/rpcserver"
"decred.org/dcrdex/client/webserver"
"decred.org/dcrdex/dex"
)

var log dex.Logger

func main() {
appCtx, cancel := context.WithCancel(context.Background())
// Catch ctrl+c. This will need to be smarter eventually, probably displaying
// a modal dialog to confirm closing, especially if servers are running or if
// swaps are in negotiation.
killChan := make(chan os.Signal, 1)
signal.Notify(killChan, os.Interrupt)
go func() {
<-killChan
cancel()
}()

// Parse configuration and set up initial logging.
//
// DRAFT NOTE: It's a little odd that the Configure function is from the ui
// package. The ui.Config struct is used both here and in ui. Could create a
// types package used by both, but doing it this way works for now.
Comment on lines +36 to +38
Copy link
Member

@chappjc chappjc Jan 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only see createApp() in cmd/dexc/ui/widgets.go accessing the package-level cfg variable. All of config.go could be moved up to cmd/dexc if we make a separate ui.Config struct that has only what ui.createApp needs. This would be nice anyway since the Config struct used by go-flags has fields (and all struct field tags) that are only relevant to package main. But you're right, this does work for now.

cfg, err := ui.Configure()
if err != nil {
fmt.Fprint(os.Stderr, "configration error: ", err)
return
}

// If --notui is specified, don't create the tview application. Initialize
// logging with the standard stdout logger.
if cfg.NoTUI {
logStdout := func(msg []byte) {
os.Stdout.Write(msg)
}
clientCore := core.New(&core.Config{
DBPath: cfg.DBPath, // global set in config.go
Logger: ui.NewLogger("CORE", nil),
Certs: cfg.Certs,
})
go clientCore.Run(appCtx)

ui.InitLogging(logStdout)
// At least one of --rpc or --web must be specified.
if !cfg.RPCOn && !cfg.WebOn {
fmt.Fprintf(os.Stderr, "Cannot run without TUI unless --rpc and/or --web is specified")
return
}
var wg sync.WaitGroup
if cfg.RPCOn {
wg.Add(1)
go func() {
defer wg.Done()
rpcserver.Run(appCtx, clientCore, cfg.RPCAddr, ui.NewLogger("RPC", logStdout))
}()
}
if cfg.WebOn {
wg.Add(1)
go func() {
defer wg.Done()
webSrv, err := webserver.New(clientCore, cfg.WebAddr, ui.NewLogger("WEB", logStdout), cfg.ReloadHTML)
if err != nil {
log.Errorf("Error starting web server: %v", err)
return
}
webSrv.Run(appCtx)
}()
}
wg.Wait()
ui.Close()
return
}
// Run in TUI mode.
ui.Run(appCtx)
}
135 changes: 135 additions & 0 deletions client/cmd/dexc/ui/accountsview.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// This code is available on the terms of the project LICENSE.md file,
// also available online at https://blueoakcouncil.org/license/1.0.0.

package ui

import (
"github.com/rivo/tview"
)

var dummyFormBox = tview.NewBox()

// accountsViewer is a view for manipulating DEX account and wallet settings.
type accountsViewer struct {
*tview.Flex
chain *focusChain
form *tview.Grid
}

// newAccountsView is the constructor for an accountsViewer.
func newAccountsView() *accountsViewer {
// A journal for logging account-related messages.
acctsJournal := newJournal("Accounts Journal", nil)
// acctsLog is global.
acctsLog = NewLogger("ACCTS", acctsJournal.Write)
formBox := tview.NewGrid()
formBox.SetBackgroundColor(colorBlack)
var acctForm *tview.Form

// The list of available DEX accounts, and a button to display a form to add
// a new DEX account.
acctsList := newChooser("Accounts", nil)
newAcctBttn := newSimpleButton("Add New DEX Account", func() {
formBox.Clear()
formBox.AddItem(acctForm, 0, 0, 1, 1, 0, 0, false)
acctForm.SetBorderColor(focusColor)
app.SetFocus(acctForm)
})
acctsColumn := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(acctsList, 0, 1, false).
AddItem(newAcctBttn, 3, 0, false)

// The list of exchange wallets registered, and a button to dipslay a form
// to add a new wallet.
walletsList := newChooser("Wallets", nil)
newWalletBttn := newSimpleButton("Add Wallet", func() {
acctsLog.Errorf("cannot add a wallet without an account")
})
walletsColumn := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(walletsList, 0, 1, false).
AddItem(newWalletBttn, 3, 0, false)

// The third column is the main content area (formBox), and a log display at
// the bottom
thirdColumn := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(fullyCentered(formBox, 60, 17), 0, 3, false).
AddItem(acctsJournal, 0, 1, false)

// The main view.
wgt := tview.NewFlex().
AddItem(acctsColumn, 30, 0, false).
AddItem(walletsColumn, 30, 0, false).
AddItem(thirdColumn, 0, 1, false)

// A form to add a DEX account.
//
// DRAFT NOTE: I think we need more control over forms and should implement
// them using tview.Grid, but this is a start. A major downside is that
// tview.Form hijacks tab and arrow keys, making for some unintuitive
// navigation behavior.
var acctURL, dcrAcctName, dcrwAddr, dcrwPW, dcrwRPCUser, dcrwRPCPass string
acctForm = tview.NewForm().
AddInputField("DEX URL", "", 0, nil, func(url string) {
acctURL = url
}).
AddInputField("Account Name", "", 0, nil, func(name string) {
dcrAcctName = name
}).
AddInputField("dcrwallet RPC Address", "", 0, nil, func(name string) {
dcrwAddr = name
}).
AddInputField("RPC Username", "", 0, nil, func(name string) {
dcrwRPCUser = name
}).
AddPasswordField("RPC Password", "", 0, 0, func(name string) {
dcrwRPCPass = name
}).
AddPasswordField("Wallet Password", "", 0, 0, func(pw string) {
dcrwPW = pw
}).
AddButton("register", func() {
buck54321 marked this conversation as resolved.
Show resolved Hide resolved
// Obviously the password won't be echoed. Just this way for
// demonstration.
acctsLog.Infof("registering acct %s with password %s and wallet node %s for DEX %s, using RPC username %s and RPC password %s",
dcrAcctName, dcrwPW, dcrwAddr, acctURL, dcrwRPCUser, dcrwRPCPass)
}).
SetButtonsAlign(tview.AlignRight).
SetFieldBackgroundColor(metalBlue).
SetButtonBackgroundColor(metalBlue)
acctForm.SetCancelFunc(func() {
acctForm.SetBorderColor(blurColor)
acctsView.setForm(dummyFormBox)
setFocus(newAcctBttn)
}).SetBorder(true).SetBorderColor(blurColor)
acctForm.SetTitle("DEX Registration")

av := &accountsViewer{
Flex: wgt,
form: formBox,
}
av.chain = newFocusChain(av, acctsList, newAcctBttn, walletsList, newWalletBttn, acctsJournal)
return av
}

// AddFocus is part of the focuser interface. Since the accountsViewer supports
// sub-focus, this method simply passes focus to the focus chain and sets the
// view's border color.
func (v *accountsViewer) AddFocus() {
// Pass control to the focusChain, but keep the border color on the view.
v.chain.focus()
v.SetBorderColor(focusColor)
}

// RemoveFocus is part of the focuser interface.
func (v *accountsViewer) RemoveFocus() {
v.SetBorderColor(blurColor)
}

// Set the currently displayed form.
func (v *accountsViewer) setForm(form tview.Primitive) {
v.form.Clear()
v.form.AddItem(form, 0, 0, 1, 1, 0, 0, false)
}
Loading