132 lines
2.6 KiB
Go
132 lines
2.6 KiB
Go
package ui
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/jroimartin/gocui"
|
|
|
|
"github.com/mempool/mempool-cli/client"
|
|
)
|
|
|
|
type FeeDistribution struct {
|
|
gui *gocui.Gui
|
|
|
|
m sync.Mutex
|
|
loading bool
|
|
isProjected bool
|
|
cancelFn context.CancelFunc
|
|
fees client.Fees
|
|
title string
|
|
}
|
|
|
|
func NewFeeDistribution(g *gocui.Gui) *FeeDistribution {
|
|
return &FeeDistribution{gui: g}
|
|
}
|
|
|
|
func (fd *FeeDistribution) newCtx() context.Context {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
fd.cancelFn = cancel
|
|
return ctx
|
|
}
|
|
|
|
func (fd *FeeDistribution) FetchProjection(n int) error {
|
|
fn := func(ctx context.Context) (client.Fees, error) {
|
|
return client.GetMempoolFee(ctx, n)
|
|
}
|
|
return fd.fetch(fn)
|
|
}
|
|
|
|
func (fd *FeeDistribution) FetchBlock(n int) error {
|
|
fn := func(ctx context.Context) (client.Fees, error) {
|
|
return client.GetBlockFee(ctx, n)
|
|
}
|
|
return fd.fetch(fn)
|
|
}
|
|
|
|
func (fd *FeeDistribution) fetch(fn func(ctx context.Context) (client.Fees, error)) error {
|
|
fd.m.Lock()
|
|
defer fd.m.Unlock()
|
|
|
|
if fn := fd.cancelFn; fn != nil {
|
|
fn()
|
|
}
|
|
fd.loading = true
|
|
|
|
ctx := fd.newCtx()
|
|
go func() {
|
|
fees, err := fn(ctx)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
fd.fees = fees
|
|
fd.loading = false
|
|
fd.gui.Update(fd.Layout)
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (fd *FeeDistribution) Layout(g *gocui.Gui) error {
|
|
fd.m.Lock()
|
|
defer fd.m.Unlock()
|
|
|
|
if fd.loading == false && (fd.fees == nil || len(fd.fees) == 0) {
|
|
return nil
|
|
}
|
|
|
|
x, y := g.Size()
|
|
name := "fee_distribution"
|
|
v, err := g.SetView(name, x/2-20, y/2-3, x/2+20, y/2+3)
|
|
if err != nil {
|
|
if err != gocui.ErrUnknownView {
|
|
return err
|
|
}
|
|
|
|
v.Title = "Fee distribution" + fd.title + "('esc' to close)"
|
|
g.SetCurrentView(name)
|
|
g.SetViewOnTop(name)
|
|
g.SetKeybinding(name, gocui.KeyEsc, gocui.ModNone, fd.close)
|
|
}
|
|
|
|
v.Clear()
|
|
|
|
if fd.loading == true {
|
|
fmt.Fprint(v, "Loading...")
|
|
return nil
|
|
}
|
|
|
|
sort.Sort(fd.fees)
|
|
txs := len(fd.fees)
|
|
min, max := fd.fees[0].FPV, fd.fees[txs-1].FPV
|
|
|
|
var (
|
|
white = color.New(color.FgWhite).SprintfFunc()
|
|
yellow = color.New(color.FgYellow).SprintfFunc()
|
|
)
|
|
fmt.Fprintf(v, white("Fee span:")+" %d - %d "+yellow("sat/vByte\n"), ceil(min), ceil(max))
|
|
fmt.Fprintf(v, white("Tx count:")+" %d "+white("transactions\n"), txs)
|
|
fmt.Fprintf(v, white("Median: ")+" ~%d "+yellow("sat/vBytes\n"), ceil(fd.fees[txs/2].FPV))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (fd *FeeDistribution) close(g *gocui.Gui, v *gocui.View) error {
|
|
fd.m.Lock()
|
|
defer fd.m.Unlock()
|
|
|
|
if fn := fd.cancelFn; fn != nil {
|
|
fn()
|
|
}
|
|
|
|
fd.cancelFn = nil
|
|
fd.loading = false
|
|
fd.fees = nil
|
|
g.DeleteView(v.Name())
|
|
return nil
|
|
}
|