diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/client/ui/core/core.go | 40 | ||||
| -rw-r--r-- | internal/client/ui/core/networkjoin/networkjoin.go | 163 |
2 files changed, 195 insertions, 8 deletions
diff --git a/internal/client/ui/core/core.go b/internal/client/ui/core/core.go index 46eaca5..3048375 100644 --- a/internal/client/ui/core/core.go +++ b/internal/client/ui/core/core.go @@ -18,6 +18,7 @@ import ( "github.com/kyren223/eko/internal/client/ui/core/frequencycreation" "github.com/kyren223/eko/internal/client/ui/core/frequencylist" "github.com/kyren223/eko/internal/client/ui/core/networkcreation" + "github.com/kyren223/eko/internal/client/ui/core/networkjoin" "github.com/kyren223/eko/internal/client/ui/core/networklist" "github.com/kyren223/eko/internal/client/ui/core/state" "github.com/kyren223/eko/internal/client/ui/loadscreen" @@ -53,6 +54,7 @@ type Model struct { connected bool networkCreationPopup *networkcreation.Model + networkJoinPopup *networkjoin.Model frequencyCreationPopup *frequencycreation.Model networkList networklist.Model frequencyList frequencylist.Model @@ -70,6 +72,7 @@ func New(privKey ed25519.PrivateKey, name string) Model { timeout: initialTimeout, connected: false, networkCreationPopup: nil, + networkJoinPopup: nil, frequencyCreationPopup: nil, networkList: networklist.New(), frequencyList: frequencylist.New(), @@ -106,6 +109,8 @@ func (m Model) View() string { popup = m.networkCreationPopup.View() } else if m.frequencyCreationPopup != nil { popup = m.frequencyCreationPopup.View() + } else if m.networkJoinPopup != nil { + popup = m.networkJoinPopup.View() } if popup != "" { x := (ui.Width - lipgloss.Width(popup)) / 2 @@ -238,12 +243,11 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { } case tea.KeyMsg: - key := msg.Type - switch key { - case tea.KeyCtrlN: + switch msg.String() { + case "n": switch m.focus { case FocusNetworkList: - if m.networkCreationPopup == nil { + if m.networkCreationPopup == nil && m.networkJoinPopup == nil { popup := networkcreation.New() m.networkCreationPopup = &popup } @@ -256,14 +260,22 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { } } - case tea.KeyEscape: + case "i": + if m.focus == FocusNetworkList && m.networkJoinPopup == nil && m.networkCreationPopup == nil { + popup := networkjoin.New() + m.networkJoinPopup = &popup + } + + case "esc": if m.networkCreationPopup != nil { m.networkCreationPopup = nil } else if m.frequencyCreationPopup != nil { m.frequencyCreationPopup = nil + } else if m.networkJoinPopup != nil { + m.networkJoinPopup = nil } - case tea.KeyEnter: + case "enter": if m.networkCreationPopup != nil { cmd := m.networkCreationPopup.Select() if cmd != nil { @@ -276,6 +288,12 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { m.frequencyCreationPopup = nil } return cmd + } else if m.networkJoinPopup != nil { + cmd := m.networkJoinPopup.Select() + if cmd != nil { + m.networkJoinPopup = nil + } + return cmd } default: @@ -287,6 +305,10 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { popup, cmd := m.frequencyCreationPopup.Update(msg) m.frequencyCreationPopup = &popup return cmd + } else if m.networkJoinPopup != nil { + popup, cmd := m.networkJoinPopup.Update(msg) + m.networkJoinPopup = &popup + return cmd } if m.focus != FocusChat || !m.chat.Locked() { @@ -303,8 +325,10 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd { } } - inPopup := m.networkCreationPopup != nil || m.frequencyCreationPopup != nil - if inPopup { + isPopup := m.networkCreationPopup != nil || + m.frequencyCreationPopup != nil || + m.networkJoinPopup != nil + if isPopup { return nil } diff --git a/internal/client/ui/core/networkjoin/networkjoin.go b/internal/client/ui/core/networkjoin/networkjoin.go new file mode 100644 index 0000000..1df0db3 --- /dev/null +++ b/internal/client/ui/core/networkjoin/networkjoin.go @@ -0,0 +1,163 @@ +package networkjoin + +import ( + "errors" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/kyren223/eko/internal/client/gateway" + "github.com/kyren223/eko/internal/client/ui/colors" + "github.com/kyren223/eko/internal/client/ui/field" + "github.com/kyren223/eko/internal/client/ui/layouts/flex" + "github.com/kyren223/eko/internal/packet" + "github.com/kyren223/eko/pkg/assert" +) + +var ( + width = 48 + + style = lipgloss.NewStyle(). + Border(lipgloss.ThickBorder()). + Padding(1, 4). + Align(lipgloss.Center, lipgloss.Center) + + headerStyle = lipgloss.NewStyle().Foreground(colors.Turquoise) + + fieldBlurredStyle = lipgloss.NewStyle(). + PaddingLeft(1). + Border(lipgloss.RoundedBorder()). + BorderForeground(colors.DarkCyan) + fieldFocusedStyle = fieldBlurredStyle. + BorderForeground(colors.Focus). + Border(lipgloss.ThickBorder()) + + blurredCreate = lipgloss.NewStyle(). + Background(colors.Gray).Padding(0, 1).Render("Join Network") + focusedCreate = lipgloss.NewStyle(). + Background(colors.Blue).Padding(0, 1).Render("Join Network") +) + +const ( + MaxIconLength = 2 + MaxHexDigits = 6 +) + +const ( + NameField = iota + CreateField + FieldCount +) + +type Model struct { + name field.Model + create string + + selected int + nameWidth int +} + +func New() Model { + name := field.New(width) + name.Header = "Network ID" + name.HeaderStyle = headerStyle + name.FocusedStyle = fieldFocusedStyle + name.BlurredStyle = fieldBlurredStyle + name.ErrorStyle = lipgloss.NewStyle().Foreground(colors.Error) + name.Input.CharLimit = width + name.Focus() + name.Input.Validate = func(s string) error { + if strings.TrimSpace(s) == "" { + return errors.New("cannot be empty") + } + + return nil + } + + nameWidth := lipgloss.Width(name.View()) + + return Model{ + name: name, + create: blurredCreate, + nameWidth: nameWidth, + } +} + +func (m Model) Init() tea.Cmd { + return nil +} + +func (m Model) View() string { + name := m.name.View() + create := lipgloss.NewStyle(). + Width(m.nameWidth).Align(lipgloss.Center). + Render(m.create) + + content := flex.NewVertical(name, create).WithGap(1).View() + return style.Render(content) +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + key := msg.Type + switch key { + case tea.KeyTab: + return m, m.cycle(1) + case tea.KeyShiftTab: + return m, m.cycle(-1) + + default: + var cmd tea.Cmd + switch m.selected { + case NameField: + m.name, cmd = m.name.Update(msg) + } + return m, cmd + } + } + + return m, nil +} + +func (m *Model) cycle(step int) tea.Cmd { + m.selected += step + if m.selected < 0 { + m.selected = FieldCount - 1 + } else { + m.selected %= FieldCount + } + return m.updateFocus() +} + +func (m *Model) updateFocus() tea.Cmd { + m.name.Blur() + m.create = blurredCreate + switch m.selected { + case NameField: + return m.name.Focus() + case CreateField: + m.create = focusedCreate + return nil + default: + assert.Never("missing switch statement field in update focus", "selected", m.selected) + return nil + } +} + +func (m *Model) Select() tea.Cmd { + if m.selected != CreateField { + return nil + } + + m.name.Input.Err = m.name.Input.Validate(m.name.Input.Value()) + if m.name.Input.Err != nil { + return nil + } + + // TODO: change this to actual invite packet + request := packet.CreateNetwork{ + Name: m.name.Input.Value(), + } + return gateway.Send(&request) +} |
