From 9bbf36550b4871299bad0ff0a7d42617914b543a Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Sat, 30 Nov 2024 16:55:11 +0200 Subject: Added frequencies UI --- internal/client/ui/colors/colors.go | 8 +- internal/client/ui/core/core.go | 23 ++- internal/client/ui/core/network/network.go | 157 +++++++++++++++++++++ internal/client/ui/core/networklist/networklist.go | 38 +++-- 4 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 internal/client/ui/core/network/network.go (limited to 'internal/client/ui') diff --git a/internal/client/ui/colors/colors.go b/internal/client/ui/colors/colors.go index bd4e708..8a023a5 100644 --- a/internal/client/ui/colors/colors.go +++ b/internal/client/ui/colors/colors.go @@ -13,6 +13,7 @@ var ( DarkCyan = lipgloss.Color("#007E8A") DarkerCyan = lipgloss.Color("#005d66") DarkMidnightBlue = lipgloss.Color("#1E1E2E") + MidnightBlue = lipgloss.Color("#3c3c5d") Turquoise = lipgloss.Color("#54D7A9") Red = lipgloss.Color("#F16265") White = lipgloss.Color("#FFFFFF") @@ -21,9 +22,10 @@ var ( Purple = lipgloss.Color("#BB91F0") DarkPurple = lipgloss.Color("#87123d") - Background = DarkMidnightBlue - Error = Red - Focus = LightBlue + Background = DarkMidnightBlue + BackgroundHighlight = MidnightBlue + Error = Red + Focus = LightBlue ) func ToHex(color lipgloss.Color) string { diff --git a/internal/client/ui/core/core.go b/internal/client/ui/core/core.go index 6949aee..6ba25c1 100644 --- a/internal/client/ui/core/core.go +++ b/internal/client/ui/core/core.go @@ -13,6 +13,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/kyren223/eko/internal/client/gateway" "github.com/kyren223/eko/internal/client/ui" + "github.com/kyren223/eko/internal/client/ui/core/network" "github.com/kyren223/eko/internal/client/ui/core/networkcreation" "github.com/kyren223/eko/internal/client/ui/core/networklist" "github.com/kyren223/eko/internal/client/ui/core/state" @@ -32,9 +33,7 @@ var ( const ( FocusNetworkList = iota - FocusFrequencies - FocusChat - FocusUsers + FocusNetwork FocusMax ) @@ -50,6 +49,7 @@ type Model struct { networkCreationPopup *networkcreation.Model networkList networklist.Model + network network.Model focus int } @@ -78,7 +78,9 @@ func (m Model) View() string { return m.loading.View() } - result := m.networkList.View() + networkList := m.networkList.View() + network := m.network.View() + result := lipgloss.JoinHorizontal(lipgloss.Top, networkList, network) result = lipgloss.Place( ui.Width, ui.Height, @@ -201,6 +203,13 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { m.networkCreationPopup = &popup return cmd } + + if msg.String() == "L" && m.focus == FocusNetworkList { + m.move(1) + } else if msg.String() == "H" && m.focus == FocusNetwork { + m.move(-1) + } + } } @@ -208,6 +217,9 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { switch m.focus { case FocusNetworkList: m.networkList, cmd = m.networkList.Update(msg) + m.network.Set(m.networkList.Index()) + case FocusNetwork: + m.network, cmd = m.network.Update(msg) } return cmd @@ -227,9 +239,12 @@ func (m *Model) move(direction int) { m.focus = max(0, min(FocusMax-1, focus)) m.networkList.Blur() + m.network.Blur() switch m.focus { case FocusNetworkList: m.networkList.Focus() + case FocusNetwork: + m.network.Focus() default: assert.Never("missing switch statement field in move", "focus", m.focus) } diff --git a/internal/client/ui/core/network/network.go b/internal/client/ui/core/network/network.go new file mode 100644 index 0000000..5a5d0ec --- /dev/null +++ b/internal/client/ui/core/network/network.go @@ -0,0 +1,157 @@ +package network + +import ( + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/kyren223/eko/internal/client/gateway" + "github.com/kyren223/eko/internal/client/ui" + "github.com/kyren223/eko/internal/client/ui/colors" + "github.com/kyren223/eko/internal/client/ui/core/state" + "github.com/kyren223/eko/internal/packet" +) + +var ( + width = 24 + sepStyle = lipgloss.NewStyle().Width(0). + Border(lipgloss.ThickBorder(), false, true, false, false) + nameStyle = lipgloss.NewStyle(). + Margin(0, 0, 1).Padding(1).Width(width).Align(lipgloss.Center). + Border(lipgloss.ThickBorder(), false, false, true) + margin = 3 + frequencyStyle = lipgloss.NewStyle(). + Margin(0, margin).Padding(0, 1).Width(width - (margin * 2)). + Align(lipgloss.Left) + symbol = "# " +) + +type Model struct { + network *packet.FullNetwork + history []func(m *Model) + index int + focus bool +} + +func New() Model { + return Model{ + focus: false, + index: 0, + network: nil, + } +} + +func (m Model) Init() tea.Cmd { + return nil +} + +func (m Model) View() string { + if m.network == nil { + return "" + } + + var builder strings.Builder + + bg := lipgloss.Color(m.network.BgHexColor) + fg := lipgloss.Color(m.network.FgHexColor) + nameStyle := nameStyle.Background(bg).Foreground(fg) + if m.focus { + nameStyle = nameStyle.BorderForeground(colors.Focus) + } + builder.WriteString(nameStyle.Render(m.network.Name)) + + builder.WriteString("\n") + for i, frequency := range m.network.Frequencies { + color := colors.White + if frequency.HexColor != nil { + color = lipgloss.Color(*frequency.HexColor) + } + frequencyStyle := frequencyStyle.Foreground(color) + if m.index == i { + frequencyStyle = frequencyStyle.Background(colors.BackgroundHighlight) + } + builder.WriteString(frequencyStyle.Render(symbol + frequency.Name)) + builder.WriteString("\n") + } + + result := builder.String() + + sep := sepStyle.Height(ui.Height) + if m.focus { + sep = sep.BorderForeground(colors.Focus) + } + result = lipgloss.JoinHorizontal(lipgloss.Top, result, sep.String()) + + return result +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + switch msg := msg.(type) { + case *packet.SwapFrequencies: + // Pop first by shifting to the left + copy(m.history, m.history[1:]) + m.history = m.history[:len(m.history)-1] + case *packet.Error: + if msg.PktType == packet.PacketSwapFrequencies { + // Server failed, revert! + undo := m.history[len(m.history)-1] + m.history = m.history[:len(m.history)-1] + undo(&m) + } + + case tea.KeyMsg: + key := msg.String() + switch key { + case "K": + if 0 < m.index { + return m.Swap(-1) + } + case "J": + if m.index < m.FrequenciesLength()-1 { + return m.Swap(1) + } + case "k": + m.index = max(0, m.index-1) + case "j": + m.index = min(m.FrequenciesLength()-1, m.index+1) + } + } + return m, nil +} + +func (m *Model) Focus() { + m.focus = true +} + +func (m *Model) Blur() { + m.focus = false +} + +func (m Model) Swap(dir int) (Model, tea.Cmd) { + cmd := gateway.Send(&packet.SwapFrequencies{ + Network: m.network.ID, + Pos1: m.index, + Pos2: m.index + dir, + }) + tmp := m.network.Frequencies[m.index] + m.network.Frequencies[m.index] = m.network.Frequencies[m.index+dir] + m.network.Frequencies[m.index+dir] = tmp + m.index += dir + m.history = append(m.history, func(m *Model) { + m.index -= dir + tmp := m.network.Frequencies[m.index] + m.network.Frequencies[m.index] = m.network.Frequencies[m.index+dir] + m.network.Frequencies[m.index+dir] = tmp + }) + return m, cmd +} + +func (m *Model) Set(index int) { + if index != -1 { + m.network = &state.State.Networks[index] + } +} + +func (m Model) FrequenciesLength() int { + return len(m.network.Frequencies) +} diff --git a/internal/client/ui/core/networklist/networklist.go b/internal/client/ui/core/networklist/networklist.go index 355d3e2..0f7ca7e 100644 --- a/internal/client/ui/core/networklist/networklist.go +++ b/internal/client/ui/core/networklist/networklist.go @@ -13,16 +13,21 @@ import ( ) var ( - style = lipgloss.NewStyle().Border(lipgloss.ThickBorder(), true, false) + sepStyle = lipgloss.NewStyle().Width(0). + Border(lipgloss.ThickBorder(), false, true, false, false) partialIconStyle = lipgloss.NewStyle(). Width(6).Height(3). Align(lipgloss.Center). Border(lipgloss.ThickBorder(), false, false) selectedIndicator = "šŸ­€\nā–Œ\nšŸ­›" - trustedUsersIcon = IconStyle("ī· ", lipgloss.Color(colors.Turquoise), lipgloss.Color(colors.DarkerCyan)) + trustedUsersIcon = IconStyle("ī· ", colors.Turquoise, colors.DarkerCyan) trustedUsersButton = trustedUsersIcon.Margin(0, 1, 1).String() - trustedUsersButtonSelected = lipgloss.JoinHorizontal(ui.Center, selectedIndicator, trustedUsersIcon.Margin(0, 1, 1, 0).String()) + trustedUsersButtonSelected = lipgloss.JoinHorizontal( + ui.Center, + selectedIndicator, + trustedUsersIcon.Margin(0, 1, 1, 0).String(), + ) ) func IconStyle(icon string, fg, bg lipgloss.Color) lipgloss.Style { @@ -38,7 +43,7 @@ type Model struct { func New() Model { return Model{ focus: false, - index: 0, + index: -1, } } @@ -47,11 +52,7 @@ func (m Model) Init() tea.Cmd { } func (m Model) View() string { - // border := lipgloss.ThickBorder() var builder strings.Builder - - // top := strings.Repeat(border.Top, 6) - // builder.WriteString(fmt.Sprintf("%s%s%s\n", border.TopLeft, top, border.TopRight)) builder.WriteString("\n") if m.index == -1 { builder.WriteString(trustedUsersButtonSelected) @@ -60,24 +61,29 @@ func (m Model) View() string { } builder.WriteString("\n") for i, network := range state.State.Networks { - icon := IconStyle(network.Icon, lipgloss.Color(network.FgHexColor), lipgloss.Color(network.BgHexColor)) + icon := IconStyle(network.Icon, + lipgloss.Color(network.FgHexColor), + lipgloss.Color(network.BgHexColor), + ) if m.index == i { - builder.WriteString(lipgloss.JoinHorizontal(ui.Center, selectedIndicator, icon.Margin(0, 1, 1, 0).String())) + builder.WriteString(lipgloss.JoinHorizontal( + ui.Center, + selectedIndicator, + icon.Margin(0, 1, 1, 0).String(), + )) } else { builder.WriteString(icon.Margin(0, 1, 1).String()) } builder.WriteString("\n") } - // bottom := strings.Repeat(border.Bottom, 6) - // builder.WriteString(fmt.Sprintf("\n%s%s%s", border.BottomLeft, bottom, border.BottomRight)) result := builder.String() - sep := lipgloss.NewStyle().Border(lipgloss.ThickBorder(), false, true, false, false).Height(ui.Height).Width(0) + sep := sepStyle.Height(ui.Height) if m.focus { sep = sep.BorderForeground(colors.Focus) } - result = lipgloss.JoinHorizontal(lipgloss.Top, result, sep.Render("")) + result = lipgloss.JoinHorizontal(lipgloss.Top, result, sep.String()) return result } @@ -141,3 +147,7 @@ func (m Model) Swap(dir int) (Model, tea.Cmd) { }) return m, cmd } + +func (m Model) Index() int { + return m.index +} -- cgit v1.3.1