2019-03-19 08:54:45 +00:00
|
|
|
package views
|
|
|
|
|
2019-03-19 13:19:49 +00:00
|
|
|
import (
|
2019-03-20 12:44:30 +00:00
|
|
|
"bytes"
|
2019-03-19 13:19:49 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/jroimartin/gocui"
|
2019-04-04 14:24:44 +00:00
|
|
|
"golang.org/x/text/language"
|
|
|
|
"golang.org/x/text/message"
|
2019-03-19 13:19:49 +00:00
|
|
|
|
2019-03-29 08:30:05 +00:00
|
|
|
netmodels "github.com/edouardparis/lntop/network/models"
|
2019-03-20 09:04:17 +00:00
|
|
|
"github.com/edouardparis/lntop/ui/color"
|
2019-03-29 08:30:05 +00:00
|
|
|
"github.com/edouardparis/lntop/ui/models"
|
2019-03-19 13:19:49 +00:00
|
|
|
)
|
|
|
|
|
2019-03-20 09:04:17 +00:00
|
|
|
const (
|
2019-03-22 15:52:12 +00:00
|
|
|
CHANNELS = "channels"
|
|
|
|
CHANNELS_COLUMNS = "channels_columns"
|
2019-04-05 07:35:49 +00:00
|
|
|
CHANNELS_FOOTER = "channels_footer"
|
2019-03-20 09:04:17 +00:00
|
|
|
)
|
2019-03-19 13:19:49 +00:00
|
|
|
|
2019-03-19 08:54:45 +00:00
|
|
|
type Channels struct {
|
2019-04-10 11:37:55 +00:00
|
|
|
columns *gocui.View
|
2019-04-10 07:44:52 +00:00
|
|
|
view *gocui.View
|
2019-03-29 08:30:05 +00:00
|
|
|
channels *models.Channels
|
2019-03-19 13:19:49 +00:00
|
|
|
}
|
|
|
|
|
2019-04-02 08:14:39 +00:00
|
|
|
func (c Channels) Name() string {
|
|
|
|
return CHANNELS
|
|
|
|
}
|
|
|
|
|
2019-04-10 07:44:52 +00:00
|
|
|
func (c *Channels) Wrap(v *gocui.View) view {
|
|
|
|
c.view = v
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2019-04-10 11:16:51 +00:00
|
|
|
func (c *Channels) CursorDown() error {
|
2019-04-10 11:32:11 +00:00
|
|
|
return cursorDown(c.view, 1)
|
2019-04-10 11:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Channels) CursorUp() error {
|
2019-04-10 11:32:11 +00:00
|
|
|
return cursorUp(c.view, 1)
|
2019-04-10 11:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Channels) CursorRight() error {
|
2019-04-10 11:37:55 +00:00
|
|
|
err := cursorRight(c.columns, 2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-04-10 11:32:11 +00:00
|
|
|
return cursorRight(c.view, 2)
|
2019-04-10 11:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Channels) CursorLeft() error {
|
2019-04-10 11:37:55 +00:00
|
|
|
err := cursorLeft(c.columns, 2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-04-10 11:32:11 +00:00
|
|
|
return cursorLeft(c.view, 2)
|
2019-04-10 11:16:51 +00:00
|
|
|
}
|
|
|
|
|
2019-03-19 13:19:49 +00:00
|
|
|
func (c *Channels) Set(g *gocui.Gui, x0, y0, x1, y1 int) error {
|
2019-04-10 11:37:55 +00:00
|
|
|
var err error
|
|
|
|
c.columns, err = g.SetView(CHANNELS_COLUMNS, x0-1, y0, x1+2, y0+2)
|
2019-03-20 09:04:17 +00:00
|
|
|
if err != nil {
|
|
|
|
if err != gocui.ErrUnknownView {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-04-10 11:37:55 +00:00
|
|
|
c.columns.Frame = false
|
|
|
|
c.columns.BgColor = gocui.ColorGreen
|
|
|
|
c.columns.FgColor = gocui.ColorBlack
|
|
|
|
displayChannelsColumns(c.columns)
|
2019-03-20 09:04:17 +00:00
|
|
|
|
2019-04-10 07:44:52 +00:00
|
|
|
c.view, err = g.SetView(CHANNELS, x0-1, y0+1, x1+2, y1-1)
|
2019-03-19 13:19:49 +00:00
|
|
|
if err != nil {
|
|
|
|
if err != gocui.ErrUnknownView {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-21 08:34:45 +00:00
|
|
|
_, err = g.SetCurrentView(CHANNELS)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-19 13:19:49 +00:00
|
|
|
}
|
2019-04-10 07:44:52 +00:00
|
|
|
c.view.Frame = false
|
|
|
|
c.view.Autoscroll = false
|
|
|
|
c.view.SelBgColor = gocui.ColorCyan
|
|
|
|
c.view.SelFgColor = gocui.ColorBlack
|
|
|
|
c.view.Highlight = true
|
2019-03-19 13:19:49 +00:00
|
|
|
|
2019-04-10 07:44:52 +00:00
|
|
|
c.display()
|
2019-04-05 07:35:49 +00:00
|
|
|
|
2019-04-10 07:27:10 +00:00
|
|
|
footer, err := g.SetView(CHANNELS_FOOTER, x0-1, y1-2, x1+2, y1)
|
2019-04-05 07:35:49 +00:00
|
|
|
if err != nil {
|
|
|
|
if err != gocui.ErrUnknownView {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
footer.Frame = false
|
|
|
|
footer.BgColor = gocui.ColorCyan
|
|
|
|
footer.FgColor = gocui.ColorBlack
|
|
|
|
footer.Clear()
|
2019-04-05 07:48:29 +00:00
|
|
|
fmt.Fprintln(footer, fmt.Sprintf("%s%s %s%s %s%s",
|
|
|
|
color.BlackBg("F1"), "Help",
|
|
|
|
color.BlackBg("Enter"), "Channel",
|
|
|
|
color.BlackBg("F10"), "Quit",
|
|
|
|
))
|
2019-03-19 13:19:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-22 15:52:12 +00:00
|
|
|
func displayChannelsColumns(v *gocui.View) {
|
2019-04-03 12:11:49 +00:00
|
|
|
v.Clear()
|
2019-04-11 07:57:00 +00:00
|
|
|
fmt.Fprintln(v, fmt.Sprintf("%-13s %-25s %-21s %12s %12s %5s %-10s %-6s %-15s %s %-19s",
|
2019-04-05 11:42:37 +00:00
|
|
|
"STATUS",
|
|
|
|
"ALIAS",
|
|
|
|
"GAUGE",
|
|
|
|
"LOCAL",
|
|
|
|
"CAP",
|
|
|
|
"HTLC",
|
2019-04-11 07:57:00 +00:00
|
|
|
"UNSETTLED",
|
|
|
|
"CFEE",
|
2019-04-04 08:10:33 +00:00
|
|
|
"Last Update",
|
2019-04-11 07:57:00 +00:00
|
|
|
"PRIVATE",
|
2019-04-04 11:24:01 +00:00
|
|
|
"ID",
|
2019-03-20 12:44:30 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2019-04-10 07:44:52 +00:00
|
|
|
func (c *Channels) display() {
|
2019-04-04 14:24:44 +00:00
|
|
|
p := message.NewPrinter(language.English)
|
2019-04-10 07:44:52 +00:00
|
|
|
c.view.Clear()
|
2019-04-03 12:11:49 +00:00
|
|
|
for _, item := range c.channels.List() {
|
2019-04-11 07:57:00 +00:00
|
|
|
line := fmt.Sprintf("%s %-25s %s %s %s %5d %s %s %s %s %19s %500s",
|
2019-04-08 11:38:21 +00:00
|
|
|
status(item),
|
2019-04-04 11:24:01 +00:00
|
|
|
alias(item),
|
2019-03-22 09:14:58 +00:00
|
|
|
gauge(item),
|
2019-04-04 14:24:44 +00:00
|
|
|
color.Cyan(p.Sprintf("%12d", item.LocalBalance)),
|
2019-04-04 15:00:29 +00:00
|
|
|
p.Sprintf("%12d", item.Capacity),
|
2019-03-22 16:20:41 +00:00
|
|
|
len(item.PendingHTLC),
|
2019-04-11 07:57:00 +00:00
|
|
|
color.Yellow(p.Sprintf("%10d", item.UnsettledBalance)),
|
|
|
|
p.Sprintf("%6d", item.CommitFee),
|
2019-04-04 08:10:33 +00:00
|
|
|
lastUpdate(item),
|
2019-04-11 07:57:00 +00:00
|
|
|
channelPrivate(item),
|
|
|
|
channelID(item),
|
2019-03-25 17:29:25 +00:00
|
|
|
"",
|
2019-03-19 16:20:36 +00:00
|
|
|
)
|
2019-04-10 07:44:52 +00:00
|
|
|
fmt.Fprintln(c.view, line)
|
2019-03-19 13:19:49 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-19 08:54:45 +00:00
|
|
|
|
2019-04-11 07:57:00 +00:00
|
|
|
func channelPrivate(c *netmodels.Channel) string {
|
|
|
|
if c.Private {
|
|
|
|
return color.Red("private")
|
|
|
|
}
|
|
|
|
|
|
|
|
return color.Green("public ")
|
|
|
|
}
|
|
|
|
|
|
|
|
func channelID(c *netmodels.Channel) string {
|
|
|
|
if c.ID == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%d", c.ID)
|
|
|
|
}
|
|
|
|
|
2019-04-04 11:24:01 +00:00
|
|
|
func alias(c *netmodels.Channel) string {
|
|
|
|
if c.Node == nil || c.Node.Alias == "" {
|
|
|
|
return c.RemotePubKey[:19]
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.Node.Alias
|
|
|
|
}
|
|
|
|
|
2019-04-04 08:10:33 +00:00
|
|
|
func lastUpdate(c *netmodels.Channel) string {
|
|
|
|
if c.LastUpdate != nil {
|
2019-04-11 07:57:00 +00:00
|
|
|
return color.Cyan(
|
|
|
|
fmt.Sprintf("%15s", c.LastUpdate.Format("15:04:05 Jan _2")),
|
|
|
|
)
|
2019-04-04 08:10:33 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 07:57:00 +00:00
|
|
|
return fmt.Sprintf("%15s", "")
|
2019-04-04 08:10:33 +00:00
|
|
|
}
|
|
|
|
|
2019-04-08 11:38:21 +00:00
|
|
|
func status(c *netmodels.Channel) string {
|
|
|
|
switch c.Status {
|
|
|
|
case netmodels.ChannelActive:
|
2019-04-09 08:08:43 +00:00
|
|
|
return color.Green(fmt.Sprintf("%-13s", "active"))
|
2019-04-08 11:38:21 +00:00
|
|
|
case netmodels.ChannelInactive:
|
2019-04-09 08:08:43 +00:00
|
|
|
return color.Red(fmt.Sprintf("%-13s", "inactive"))
|
2019-04-08 11:38:21 +00:00
|
|
|
case netmodels.ChannelOpening:
|
2019-04-09 08:08:43 +00:00
|
|
|
return color.Yellow(fmt.Sprintf("%-13s", "opening"))
|
2019-04-08 11:38:21 +00:00
|
|
|
case netmodels.ChannelClosing:
|
2019-04-09 08:08:43 +00:00
|
|
|
return color.Yellow(fmt.Sprintf("%-13s", "closing"))
|
2019-04-08 11:38:21 +00:00
|
|
|
case netmodels.ChannelForceClosing:
|
2019-04-09 08:08:43 +00:00
|
|
|
return color.Yellow(fmt.Sprintf("%-13s", "force closing"))
|
|
|
|
case netmodels.ChannelWaitingClose:
|
|
|
|
return color.Yellow(fmt.Sprintf("%-13s", "waiting close"))
|
2019-03-20 09:04:17 +00:00
|
|
|
}
|
2019-04-08 11:38:21 +00:00
|
|
|
return ""
|
2019-03-20 12:44:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 08:30:05 +00:00
|
|
|
func gauge(c *netmodels.Channel) string {
|
2019-04-05 11:42:37 +00:00
|
|
|
index := int(c.LocalBalance * int64(15) / c.Capacity)
|
2019-03-22 09:14:58 +00:00
|
|
|
var buffer bytes.Buffer
|
2019-04-05 11:42:37 +00:00
|
|
|
for i := 0; i < 15; i++ {
|
2019-03-22 09:14:58 +00:00
|
|
|
if i < index {
|
|
|
|
buffer.WriteString(color.Cyan("|"))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
buffer.WriteString(" ")
|
|
|
|
}
|
2019-03-22 14:00:14 +00:00
|
|
|
return fmt.Sprintf("[%s] %2d%%", buffer.String(), c.LocalBalance*100/c.Capacity)
|
2019-03-22 09:14:58 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 08:30:05 +00:00
|
|
|
func NewChannels(channels *models.Channels) *Channels {
|
|
|
|
return &Channels{channels: channels}
|
2019-03-21 09:08:33 +00:00
|
|
|
}
|