Skip to content

Commit 1edc0da

Browse files
committed
start new user interface
Lays out the structure for the new React-based user interface. Implements wallet initialization sequence. Implements basic header and sidebar interface used for most views. Implements functionality related to forms and context management.
1 parent d34394b commit 1edc0da

130 files changed

Lines changed: 9961 additions & 196 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/core/core.go

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,13 +1753,13 @@ func (c *Core) Run(ctx context.Context) {
17531753

17541754
// Construct enabled fiat rate sources.
17551755
fetchers:
1756-
for token, rateFetcher := range fiatRateFetchers {
1756+
for src, f := range fiatRateFetchers {
17571757
for _, v := range disabledSources {
1758-
if token == v {
1758+
if src == v {
17591759
continue fetchers
17601760
}
17611761
}
1762-
c.fiatRateSources[token] = newCommonRateSource(rateFetcher)
1762+
c.fiatRateSources[src] = newCommonRateSource(src, f)
17631763
}
17641764
c.fetchFiatExchangeRates(ctx)
17651765

@@ -2585,12 +2585,14 @@ func (c *Core) assetMap() map[uint32]*SupportedAsset {
25852585
// User is a thread-safe getter for the User.
25862586
func (c *Core) User() *User {
25872587
m := c.coreMesh()
2588+
idRates, tickerRates := c.fiatConversions()
25882589
return &User{
25892590
Assets: c.assetMap(),
25902591
Exchanges: c.Exchanges(),
25912592
Initialized: c.IsInitialized(),
25922593
SeedGenerationTime: c.seedGenerationTime,
2593-
FiatRates: c.fiatConversions(),
2594+
FiatRates: idRates,
2595+
TickerRates: tickerRates,
25942596
Net: c.net,
25952597
ExtensionConfig: c.extensionModeConfig,
25962598
Actions: c.requestedActionsList(),
@@ -11382,9 +11384,9 @@ func (c *Core) refreshFiatRates(ctx context.Context) {
1138211384
// Remove expired rate source if any.
1138311385
c.removeExpiredRateSources()
1138411386

11385-
fiatRatesMap := c.fiatConversions()
11386-
if len(fiatRatesMap) != 0 {
11387-
c.notify(newFiatRatesUpdate(fiatRatesMap))
11387+
idRates, tickerRates := c.fiatConversions()
11388+
if len(idRates) != 0 {
11389+
c.notify(newFiatRatesUpdate(idRates, tickerRates))
1138811390
}
1138911391
}
1139011392

@@ -11403,12 +11405,13 @@ func (c *Core) FiatRateSources() map[string]bool {
1140311405
// FiatConversionRates are the currently cached fiat conversion rates. Must have
1140411406
// 1 or more fiat rate sources enabled.
1140511407
func (c *Core) FiatConversionRates() map[uint32]float64 {
11406-
return c.fiatConversions()
11408+
idRates, _ := c.fiatConversions()
11409+
return idRates
1140711410
}
1140811411

1140911412
// fiatConversions returns fiat rate for all supported assets that have a
1141011413
// wallet.
11411-
func (c *Core) fiatConversions() map[uint32]float64 {
11414+
func (c *Core) fiatConversions() (map[uint32]float64, map[string]float64) {
1141211415
assetIDs := make(map[uint32]struct{})
1141311416
supportedAssets := asset.Assets()
1141411417
for assetID, asset := range supportedAssets {
@@ -11419,6 +11422,7 @@ func (c *Core) fiatConversions() map[uint32]float64 {
1141911422
}
1142011423

1142111424
fiatRatesMap := make(map[uint32]float64, len(supportedAssets))
11425+
tickerRatesMap := make(map[string]float64)
1142211426
for assetID := range assetIDs {
1142311427
var rateSum float64
1142411428
var sources int
@@ -11437,19 +11441,22 @@ func (c *Core) fiatConversions() map[uint32]float64 {
1143711441
}
1143811442
}
1143911443
if rateSum != 0 {
11440-
fiatRatesMap[assetID] = rateSum / float64(sources) // get average rate.
11444+
r := rateSum / float64(sources) // get average rate.
11445+
fiatRatesMap[assetID] = r
11446+
ui, _ := asset.UnitInfo(assetID)
11447+
tickerRatesMap[ui.Conventional.Unit] = r
1144111448
}
1144211449
}
11443-
return fiatRatesMap
11450+
return fiatRatesMap, tickerRatesMap
1144411451
}
1144511452

1144611453
// ToggleRateSourceStatus toggles a fiat rate source status. If disable is true,
1144711454
// the fiat rate source is disabled, otherwise the rate source is enabled.
11448-
func (c *Core) ToggleRateSourceStatus(source string, disable bool) error {
11449-
if disable {
11450-
return c.disableRateSource(source)
11455+
func (c *Core) ToggleRateSourceStatus(source string, enable bool) error {
11456+
if enable {
11457+
return c.enableRateSource(source)
1145111458
}
11452-
return c.enableRateSource(source)
11459+
return c.disableRateSource(source)
1145311460
}
1145411461

1145511462
// enableRateSource enables a fiat rate source.
@@ -11467,7 +11474,7 @@ func (c *Core) enableRateSource(source string) error {
1146711474
}
1146811475

1146911476
// Build fiat rate source.
11470-
rateSource := newCommonRateSource(rateFetcher)
11477+
rateSource := newCommonRateSource(source, rateFetcher)
1147111478
c.fiatRateSources[source] = rateSource
1147211479

1147311480
select {
@@ -11957,6 +11964,11 @@ func (c *Core) ExtensionModeConfig() *ExtensionModeConfig {
1195711964
return c.extensionModeConfig
1195811965
}
1195911966

11967+
func (c *Core) ValidateSeed(seed string) (bool, error) {
11968+
_, _, err := decodeSeedString(seed)
11969+
return err == nil, nil
11970+
}
11971+
1196011972
// calcParcelLimit computes the users score-scaled user parcel limit.
1196111973
func calcParcelLimit(tier int64, score, maxScore int32) uint32 {
1196211974
// Users limit starts at 2 parcels per tier.

client/core/core_test.go

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10814,7 +10814,7 @@ func TestToggleRateSourceStatus(t *testing.T) {
1081410814

1081510815
// Test disabling fiat rate source.
1081610816
for _, test := range tests {
10817-
err := tCore.ToggleRateSourceStatus(test.source, true)
10817+
err := tCore.ToggleRateSourceStatus(test.source, false)
1081810818
if test.wantErr != (err != nil) {
1081910819
t.Fatalf("%s: wantErr = %t, err = %v", test.name, test.wantErr, err)
1082010820
}
@@ -10823,76 +10823,15 @@ func TestToggleRateSourceStatus(t *testing.T) {
1082310823
// Test enabling fiat rate source.
1082410824
for _, test := range tests {
1082510825
if test.init {
10826-
tCore.fiatRateSources[test.source] = newCommonRateSource(tFetcher)
10826+
tCore.fiatRateSources[test.source] = newCommonRateSource(test.source, tFetcher)
1082710827
}
10828-
err := tCore.ToggleRateSourceStatus(test.source, false)
10828+
err := tCore.ToggleRateSourceStatus(test.source, true)
1082910829
if test.wantErr != (err != nil) {
1083010830
t.Fatalf("%s: wantErr = %t, err = %v", test.name, test.wantErr, err)
1083110831
}
1083210832
}
1083310833
}
1083410834

10835-
func TestFiatRateSources(t *testing.T) {
10836-
rig := newTestRig()
10837-
defer rig.shutdown()
10838-
tCore := rig.core
10839-
supportedFetchers := len(fiatRateFetchers)
10840-
rateSources := tCore.FiatRateSources()
10841-
if len(rateSources) != supportedFetchers {
10842-
t.Fatalf("Expected %d number of fiat rate source/fetchers", supportedFetchers)
10843-
}
10844-
}
10845-
10846-
func TestFiatConversions(t *testing.T) {
10847-
rig := newTestRig()
10848-
defer rig.shutdown()
10849-
tCore := rig.core
10850-
10851-
// No fiat rate source initialized
10852-
fiatRates := tCore.fiatConversions()
10853-
if len(fiatRates) != 0 {
10854-
t.Fatal("Unexpected asset rate values.")
10855-
}
10856-
10857-
// Initialize fiat rate sources.
10858-
for token := range fiatRateFetchers {
10859-
tCore.fiatRateSources[token] = newCommonRateSource(tFetcher)
10860-
}
10861-
10862-
// Fetch fiat rates.
10863-
tCore.wg.Add(1)
10864-
go func() {
10865-
defer tCore.wg.Done()
10866-
tCore.refreshFiatRates(tCtx)
10867-
}()
10868-
tCore.wg.Wait()
10869-
10870-
// Expects assets fiat rate values.
10871-
fiatRates = tCore.fiatConversions()
10872-
if len(fiatRates) != 2 {
10873-
t.Fatal("Expected assets fiat rate for two assets")
10874-
}
10875-
10876-
// fiat rates for assets can expire, and fiat rate fetchers can be
10877-
// removed if expired.
10878-
for token, source := range tCore.fiatRateSources {
10879-
source.fiatRates[tUTXOAssetA.ID].lastUpdate = time.Now().Add(-time.Minute)
10880-
source.fiatRates[tUTXOAssetB.ID].lastUpdate = time.Now().Add(-time.Minute)
10881-
if source.isExpired(55 * time.Second) {
10882-
delete(tCore.fiatRateSources, token)
10883-
}
10884-
}
10885-
10886-
fiatRates = tCore.fiatConversions()
10887-
if len(fiatRates) != 0 {
10888-
t.Fatal("Unexpected assets fiat rate values, expected to ignore expired fiat rates.")
10889-
}
10890-
10891-
if len(tCore.fiatRateSources) != 0 {
10892-
t.Fatal("Expected fiat conversion to be disabled, all rate source data has expired.")
10893-
}
10894-
}
10895-
1089610835
func TestValidateAddress(t *testing.T) {
1089710836
rig := newTestRig()
1089810837
defer rig.shutdown()

client/core/exchangeratefetcher.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const (
2525

2626
// Tokens. Used to identify fiat rate source, source name must not contain a
2727
// comma.
28-
messari = "Messari"
2928
coinpaprika = "Coinpaprika"
3029
dcrdataDotOrg = "dcrdata"
3130
)
@@ -64,6 +63,7 @@ type fiatRateInfo struct {
6463
type rateFetcher func(context context.Context, logger dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64
6564

6665
type commonRateSource struct {
66+
source string
6767
fetchRates rateFetcher
6868

6969
mtx sync.RWMutex
@@ -114,7 +114,7 @@ func (source *commonRateSource) refreshRates(ctx context.Context, logger dex.Log
114114
}
115115

116116
// Used to initialize a fiat rate source.
117-
func newCommonRateSource(fetcher rateFetcher) *commonRateSource {
117+
func newCommonRateSource(name string, fetcher rateFetcher) *commonRateSource {
118118
return &commonRateSource{
119119
fetchRates: fetcher,
120120
fiatRates: make(map[uint32]*fiatRateInfo),

client/core/notification.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,15 +522,17 @@ func newConnEventNote(topic Topic, subject, host string, status comms.Connection
522522
// FiatRatesNote is an update of fiat rate data for assets.
523523
type FiatRatesNote struct {
524524
db.Notification
525-
FiatRates map[uint32]float64 `json:"fiatRates"`
525+
FiatRates map[uint32]float64 `json:"fiatRates"`
526+
TickerRates map[string]float64 `json:"tickerRates"`
526527
}
527528

528529
const TopicFiatRatesUpdate Topic = "fiatrateupdate"
529530

530-
func newFiatRatesUpdate(rates map[uint32]float64) *FiatRatesNote {
531+
func newFiatRatesUpdate(idRates map[uint32]float64, tickerRates map[string]float64) *FiatRatesNote {
531532
return &FiatRatesNote{
532533
Notification: db.NewNotification(NoteTypeFiatRates, TopicFiatRatesUpdate, "", "", db.Data),
533-
FiatRates: rates,
534+
FiatRates: idRates,
535+
TickerRates: tickerRates,
534536
}
535537
}
536538

client/core/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ type User struct {
217217
SeedGenerationTime uint64 `json:"seedgentime"`
218218
Assets map[uint32]*SupportedAsset `json:"assets"`
219219
FiatRates map[uint32]float64 `json:"fiatRates"`
220+
TickerRates map[string]float64 `json:"tickerRates"`
220221
Net dex.Network `json:"net"`
221222
ExtensionConfig *ExtensionModeConfig `json:"extensionModeConfig,omitempty"`
222223
Actions []*asset.ActionRequiredNote `json:"actions,omitempty"`

client/webserver/api.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,25 @@ func (s *WebServer) apiInit(w http.ResponseWriter, r *http.Request) {
965965
})
966966
}
967967

968+
func (s *WebServer) apiValidateSeed(w http.ResponseWriter, r *http.Request) {
969+
var seed string
970+
if !readPost(w, r, &seed) {
971+
return
972+
}
973+
974+
ok, err := s.core.ValidateSeed(seed)
975+
if err != nil {
976+
s.writeAPIError(w, fmt.Errorf("error validating seed: %w", err))
977+
return
978+
}
979+
980+
writeJSON(w, struct {
981+
OK bool `json:"ok"`
982+
}{
983+
OK: ok,
984+
})
985+
}
986+
968987
// apiIsInitialized is the handler for the '/isinitialized' request.
969988
func (s *WebServer) apiIsInitialized(w http.ResponseWriter, r *http.Request) {
970989
writeJSON(w, &struct {
@@ -1608,13 +1627,13 @@ func (s *WebServer) apiUser(w http.ResponseWriter, r *http.Request) {
16081627
// apiToggleRateSource handles the /toggleratesource API request.
16091628
func (s *WebServer) apiToggleRateSource(w http.ResponseWriter, r *http.Request) {
16101629
form := &struct {
1611-
Disable bool `json:"disable"`
1612-
Source string `json:"source"`
1630+
Enable bool `json:"enable"`
1631+
Source string `json:"source"`
16131632
}{}
16141633
if !readPost(w, r, form) {
16151634
return
16161635
}
1617-
err := s.core.ToggleRateSourceStatus(form.Source, form.Disable)
1636+
err := s.core.ToggleRateSourceStatus(form.Source, form.Enable)
16181637
if err != nil {
16191638
s.writeAPIError(w, fmt.Errorf("error disabling/enabling rate source: %w", err))
16201639
return
@@ -2267,6 +2286,7 @@ func (s *WebServer) writeAPIError(w http.ResponseWriter, err error) {
22672286
innerErr := core.UnwrapErr(err)
22682287
resp := &standardResponse{
22692288
OK: false,
2289+
Bad: true,
22702290
Msg: innerErr.Error(),
22712291
Code: code,
22722292
}

client/webserver/live_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,8 +1891,8 @@ func (c *TCore) UpdateDEXHost(string, string, []byte, any) (*core.Exchange, erro
18911891
func (c *TCore) WalletRestorationInfo(pw []byte, assetID uint32) ([]*asset.WalletRestoration, error) {
18921892
return nil, nil
18931893
}
1894-
func (c *TCore) ToggleRateSourceStatus(src string, disable bool) error {
1895-
c.fiatSources[src] = !disable
1894+
func (c *TCore) ToggleRateSourceStatus(src string, enable bool) error {
1895+
c.fiatSources[src] = enable
18961896
return nil
18971897
}
18981898
func (c *TCore) FiatRateSources() map[string]bool {
@@ -2059,6 +2059,10 @@ func (*TCore) PoliteiaDetails() (string, bool, int64) {
20592059
return "", false, 0
20602060
}
20612061

2062+
func (*TCore) ValidateSeed(string) (bool, error) {
2063+
return true, nil
2064+
}
2065+
20622066
func newMarketDay() *libxc.MarketDay {
20632067
avgPrice := tenToThe(7)
20642068
return &libxc.MarketDay{

client/webserver/middleware.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,6 @@ func (s *WebServer) tokenAuthMiddleware(next http.Handler) http.Handler {
6464
})
6565
}
6666

67-
// extractBooleanCookie extracts the cookie value with key k from the Request,
68-
// and interprets the value as true only if it's equal to the string "1".
69-
func extractBooleanCookie(r *http.Request, k string, defaultVal bool) bool {
70-
cookie, err := r.Cookie(k)
71-
switch {
72-
// Dark mode is the default
73-
case err == nil:
74-
return cookie.Value == "1"
75-
case errors.Is(err, http.ErrNoCookie):
76-
default:
77-
log.Errorf("Cookie %q retrieval error: %v", k, err)
78-
}
79-
return defaultVal
80-
}
81-
8267
// requireInit ensures that the core app is initialized before allowing the
8368
// incoming request to proceed. Redirects to the register page if the app is
8469
// not initialized.

client/webserver/newui.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//go:build newui
2+
3+
// This code is available on the terms of the project LICENSE.md file,
4+
// also available online at https://blueoakcouncil.org/license/1.0.0.
5+
6+
package webserver
7+
8+
import (
9+
"embed"
10+
"io/fs"
11+
)
12+
13+
const (
14+
newUI = true
15+
// site is the common prefix for the site resources with respect to this
16+
// webserver package.
17+
site = "newui"
18+
)
19+
20+
var (
21+
//go:embed newui/dist newui/src/img newui/src/font
22+
staticSiteRes embed.FS
23+
24+
// Unused for New UI
25+
htmlTmplRes embed.FS
26+
htmlTmplSub fs.FS
27+
)

0 commit comments

Comments
 (0)