summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2024-12-04 18:35:11 +0200
committerKyren223 <Kyren223@proton.me>2024-12-04 18:35:11 +0200
commit37cd043920aa92189d9ae19e7631968290e4d352 (patch)
tree62282034ca56a52bbe36634d77932beaefa5d0ef /internal
parent567d569dd649cc0b82d6f8890e2bd77b052ebed6 (diff)
Started work on basic vim input box
Diffstat (limited to 'internal')
-rw-r--r--internal/client/api/api.go130
-rw-r--r--internal/client/ui/chat/chat.go130
-rw-r--r--internal/client/ui/core/chat/chat.go48
-rw-r--r--internal/client/ui/core/core.go20
-rw-r--r--internal/client/ui/core/network/network.go12
-rw-r--r--internal/client/ui/messagebox/messagebox.go131
-rw-r--r--internal/client/ui/viminput/linedecorations.go17
-rw-r--r--internal/client/ui/viminput/viminput.go89
8 files changed, 175 insertions, 402 deletions
diff --git a/internal/client/api/api.go b/internal/client/api/api.go
deleted file mode 100644
index cfc4ce9..0000000
--- a/internal/client/api/api.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package api
-
-import (
- "errors"
- "fmt"
- "log"
-
- tea "github.com/charmbracelet/bubbletea"
-
- "github.com/kyren223/eko/internal/client/gateway"
- "github.com/kyren223/eko/internal/data"
- "github.com/kyren223/eko/internal/packet"
- "github.com/kyren223/eko/pkg/assert"
- "github.com/kyren223/eko/pkg/snowflake"
-)
-
-type (
- AppendMessage data.Message
- UserProfileUpdate data.User
-)
-
-func SendMessage(message string) tea.Cmd {
- return func() tea.Msg {
- log.Println("request SendMessage sent")
- frequencyId := snowflake.ID(1852771536100921344)
- request := packet.SendMessage{
- Content: message,
- FrequencyID: &frequencyId,
- }
- response, ok := <-gateway.Send(&request)
- if !ok {
- return errors.New("request SendMessage timeout")
- }
- log.Println("request SendMessage received response")
-
- switch response := response.(type) {
- case *packet.ErrorMessage:
- return errors.New(response.Error)
- case *packet.Messages:
- assert.Assert(len(response.Messages) == 1, "server must return only the one message that was sent")
- return AppendMessage(response.Messages[0])
- }
- return fmt.Errorf("received invalid response from server: %v", response.Type())
- }
-}
-
-func GetMessages() tea.Msg {
- log.Println("request GetMessages sent")
- frequencyId := snowflake.ID(1852771536100921344)
- request := packet.GetMessagesRange{
- FrequencyID: &frequencyId,
- ReceiverID: nil,
- From: nil,
- To: nil,
- }
- response, ok := <-gateway.Send(&request)
- if !ok {
- return errors.New("request GetMessages timeout")
- }
- log.Println("request GetMessages received response")
-
- switch response := response.(type) {
- case *packet.ErrorMessage:
- return errors.New(response.Error)
- case *packet.Messages:
- return response
- }
- return fmt.Errorf("received invalid response from server: %v", response.Type())
-}
-
-func GetUserById(id snowflake.ID) tea.Cmd {
- return func() tea.Msg {
- log.Println("request GetUserById for ID", id, "sent")
- request := packet.GetUserByID{UserID: id}
- response, ok := <-gateway.Send(&request)
- if !ok {
- return errors.New("request GetUserById timeout")
- }
- log.Println("request GetUserById received response")
-
- switch response := response.(type) {
- case *packet.ErrorMessage:
- return errors.New(response.Error)
- case *packet.Users:
- if len(response.Users) == 0 {
- return fmt.Errorf("requested user id %v not found", id)
- }
- assert.Assert(len(response.Users) == 1, "server must return only one user with the matching id")
- return UserProfileUpdate(response.Users[0])
- }
- return fmt.Errorf("received invalid response from server: %v", response.Type())
- }
-}
-
-func CreateServer(
- name string,
- icon string,
- bgHexColor string,
- fgHexColor string,
- isPublic bool,
-) tea.Cmd {
- return func() tea.Msg {
- requestName := "CreateServer"
- log.Println("request", requestName, "sent")
- request := packet.CreateNetwork{
- Name: name,
- Icon: icon,
- BgHexColor: bgHexColor,
- FgHexColor: fgHexColor,
- IsPublic: isPublic,
- }
- response, ok := <-gateway.Send(&request)
- if !ok {
- return fmt.Errorf("request %s timeout", requestName)
- }
- log.Println("request", requestName, "received response")
-
- switch response := response.(type) {
- case *packet.ErrorMessage:
- return errors.New(response.Error)
- case *packet.Users:
- if len(response.Users) == 0 {
- return fmt.Errorf("requested user id %v not found", id)
- }
- assert.Assert(len(response.Users) == 1, "server must return only one user with the matching id")
- return UserProfileUpdate(response.Users[0])
- }
- return fmt.Errorf("received invalid response from server: %v", response.Type())
- }
-}
diff --git a/internal/client/ui/chat/chat.go b/internal/client/ui/chat/chat.go
deleted file mode 100644
index c69d0fd..0000000
--- a/internal/client/ui/chat/chat.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package chat
-
-import (
- "fmt"
- "log"
- "strings"
-
- "github.com/charmbracelet/bubbles/cursor"
- "github.com/charmbracelet/bubbles/textarea"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
-
- "github.com/kyren223/eko/internal/client/api"
- "github.com/kyren223/eko/internal/client/ui/messagebox"
- "github.com/kyren223/eko/internal/data"
- "github.com/kyren223/eko/internal/packet"
-)
-
-type Model struct {
- messagebox messagebox.Model
- textarea textarea.Model
- err error
-}
-
-func New() Model {
- ta := textarea.New()
- ta.Placeholder = "Send a message..."
- ta.Focus()
-
- ta.Prompt = "┃ "
- ta.CharLimit = 280
-
- ta.SetWidth(30)
- ta.SetHeight(3)
-
- // Remove cursor line styling
- ta.FocusedStyle.CursorLine = lipgloss.NewStyle()
-
- ta.ShowLineNumbers = false
-
- ta.KeyMap.InsertNewline.SetEnabled(false)
-
- return Model{
- textarea: ta,
- messagebox: messagebox.New(30, 20),
- err: nil,
- }
-}
-
-func (m Model) Init() tea.Cmd {
- return tea.Batch(textarea.Blink, api.GetMessages)
-}
-
-func (m Model) View() string {
- return fmt.Sprintf(
- "%s\n%s",
- m.messagebox.View(),
- m.textarea.View(),
- ) + ""
-}
-
-func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
- switch msg := msg.(type) {
- case tea.WindowSizeMsg:
- m.messagebox.Viewport.Width = msg.Width
- m.messagebox.Viewport.Height = msg.Height - m.textarea.Height()
- m.messagebox.Viewport.GotoBottom()
-
- m.textarea.SetWidth(msg.Width)
- log.Println("resized to:", msg.Width, "x", msg.Height)
-
- var mbCmd, taCmd tea.Cmd
- m.messagebox, mbCmd = m.messagebox.Update(msg)
- m.textarea, taCmd = m.textarea.Update(msg)
- return m, tea.Batch(mbCmd, taCmd)
-
- case tea.KeyMsg:
- switch msg.Type {
-
- case tea.KeyEnter:
- value := m.textarea.Value()
-
- content := strings.TrimSpace(value)
- if content == "" {
- return m, nil
- }
- m.textarea.Reset()
-
- return m, api.SendMessage(content)
-
- default:
- var cmd tea.Cmd
- m.textarea, cmd = m.textarea.Update(msg)
- return m, cmd
- }
-
- case packet.Payload:
- switch msg := msg.(type) {
- case *packet.Messages:
- m.messagebox.SetMessages(msg.Messages)
-
- var cmd tea.Cmd
- m.messagebox, cmd = m.messagebox.Update(msg)
- return m, cmd
-
- default:
- return m, nil
- }
-
- case api.AppendMessage:
- m.messagebox.AppendMessage(data.Message(msg))
-
- var cmd tea.Cmd
- m.messagebox, cmd = m.messagebox.Update(msg)
- return m, cmd
-
- case api.UserProfileUpdate:
- var cmd tea.Cmd
- m.messagebox, cmd = m.messagebox.Update(msg)
- return m, cmd
-
- case cursor.BlinkMsg:
- var cmd tea.Cmd
- m.textarea, cmd = m.textarea.Update(msg)
- return m, cmd
-
- default:
- return m, nil
- }
-}
diff --git a/internal/client/ui/core/chat/chat.go b/internal/client/ui/core/chat/chat.go
new file mode 100644
index 0000000..b3597a8
--- /dev/null
+++ b/internal/client/ui/core/chat/chat.go
@@ -0,0 +1,48 @@
+package chat
+
+import (
+ "log"
+
+ "github.com/charmbracelet/bubbles/textarea"
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+
+ "github.com/kyren223/eko/internal/client/ui/colors"
+ "github.com/kyren223/eko/internal/client/ui/viminput"
+)
+
+type Model struct {
+ vi viminput.Model
+}
+
+func New() Model {
+ vi := viminput.New(30, 3)
+ vi.Placeholder = "Send a message..."
+ vi.PlaceholderStyle = lipgloss.NewStyle().Foreground(colors.Gray)
+ vi.LineDecoration = func(lnum int, line string, cursorLnum int) string {
+ return "┃ "
+ }
+ // vi.ShowLineNumbers = false
+
+ // vi.CharLimit = 280
+
+ // vi.Focus()
+
+ log.Println("Placeholder:", vi.Placeholder)
+
+ return Model{
+ vi: vi,
+ }
+}
+
+func (m Model) Init() tea.Cmd {
+ return tea.Batch(textarea.Blink, nil)
+}
+
+func (m Model) View() string {
+ return m.vi.View()
+}
+
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+ return m, nil
+}
diff --git a/internal/client/ui/core/core.go b/internal/client/ui/core/core.go
index 5031dc0..b4390e0 100644
--- a/internal/client/ui/core/core.go
+++ b/internal/client/ui/core/core.go
@@ -58,14 +58,18 @@ type Model struct {
func New(privKey ed25519.PrivateKey, name string) Model {
m := Model{
- name: name,
- privKey: privKey,
- networkList: networklist.New(),
- loading: loadscreen.New(connectingToServer),
- timer: newTimer(initialTimeout),
- timeout: initialTimeout,
- connected: false,
- focus: FocusNetworkList,
+ name: name,
+ privKey: privKey,
+ id: 0,
+ loading: loadscreen.New(connectingToServer),
+ timer: newTimer(initialTimeout),
+ timeout: initialTimeout,
+ connected: false,
+ networkCreationPopup: nil,
+ frequencyCreationPopup: nil,
+ networkList: networklist.New(),
+ network: network.New(),
+ focus: FocusNetworkList,
}
m.move(0) // Update focus
diff --git a/internal/client/ui/core/network/network.go b/internal/client/ui/core/network/network.go
index 75ea318..3c24350 100644
--- a/internal/client/ui/core/network/network.go
+++ b/internal/client/ui/core/network/network.go
@@ -8,6 +8,7 @@ import (
"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/chat"
"github.com/kyren223/eko/internal/client/ui/core/state"
"github.com/kyren223/eko/internal/packet"
)
@@ -31,12 +32,16 @@ type Model struct {
networkIndex int
index int
focus bool
+ chat chat.Model
}
func New() Model {
+ chat := chat.New()
+
return Model{
focus: false,
index: 0,
+ chat: chat,
}
}
@@ -69,13 +74,14 @@ func (m Model) View() string {
builder.WriteString("\n")
}
- result := builder.String()
-
+ sidebar := builder.String()
sep := sepStyle.Height(ui.Height)
if m.focus {
sep = sep.BorderForeground(colors.Focus)
}
- result = lipgloss.JoinHorizontal(lipgloss.Top, result, sep.String())
+ sidebar = lipgloss.JoinHorizontal(lipgloss.Top, sidebar, sep.String())
+
+ result := lipgloss.JoinHorizontal(lipgloss.Top, sidebar, m.chat.View())
return result
}
diff --git a/internal/client/ui/messagebox/messagebox.go b/internal/client/ui/messagebox/messagebox.go
deleted file mode 100644
index 0728cb1..0000000
--- a/internal/client/ui/messagebox/messagebox.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package messagebox
-
-import (
- "slices"
- "strings"
- "time"
-
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
-
- "github.com/kyren223/eko/internal/client/api"
- "github.com/kyren223/eko/internal/data"
- "github.com/kyren223/eko/pkg/snowflake"
-)
-
-var (
- senderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Bold(true)
- timestampStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#585c62")).Italic(true)
-)
-
-type user struct {
- name string
- isFetching bool
-}
-
-type Model struct {
- Viewport viewport.Model
- messages []data.Message
- users map[snowflake.ID]user
-}
-
-func New(width, height int) Model {
- return Model{
- Viewport: viewport.New(width, height),
- messages: nil,
- users: make(map[snowflake.ID]user),
- }
-}
-
-func (m Model) Init() tea.Cmd {
- return api.GetMessages
-}
-
-func (m Model) View() string {
- return m.Viewport.View()
-}
-
-func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
- var cmds []tea.Cmd
-
- for id, user := range m.users {
- if !user.isFetching && user.name == "" {
- cmds = append(cmds, api.GetUserById(id))
- user.isFetching = true
- m.users[id] = user
- }
- }
-
- var cmd tea.Cmd
- m.Viewport, cmd = m.Viewport.Update(msg)
- cmds = append(cmds, cmd)
-
- switch msg := msg.(type) {
- case api.UserProfileUpdate:
- m.users[msg.ID] = user{msg.Name, false}
- m.UpdateMessages()
- }
-
- return m, tea.Batch(cmds...)
-}
-
-func (m *Model) AppendMessage(message data.Message) {
- m.messages = append(m.messages, message)
- m.UpdateMessages()
-}
-
-func (m *Model) SetMessages(messages []data.Message) {
- m.messages = messages
- m.UpdateMessages()
-}
-
-func (m *Model) UpdateMessages() {
- for _, message := range m.messages {
- if _, exists := m.users[message.SenderID]; !exists {
- m.users[message.SenderID] = user{name: "", isFetching: false}
- }
- }
-
- m.sortMessages()
- m.updateContent()
- m.Viewport.GotoBottom()
-}
-
-func (m *Model) sortMessages() {
- slices.SortFunc(m.messages, func(a, b data.Message) int {
- if a.ID-b.ID < 0 {
- return -1
- } else {
- return 1
- }
- })
-}
-
-func (m *Model) updateContent() {
- if len(m.messages) == 0 {
- return
- }
-
- var builder strings.Builder
-
- for _, message := range m.messages {
- sender := m.users[message.SenderID].name
- builder.WriteString(senderStyle.Render(sender))
- builder.WriteByte(' ')
-
- timestamp := timestampFromID(message.ID)
- builder.WriteString(timestampStyle.Render(timestamp))
- builder.WriteByte('\n')
-
- builder.WriteString(message.Content)
- builder.WriteByte('\n')
- }
-
- m.Viewport.SetContent(builder.String()[:builder.Len()-1])
-}
-
-func timestampFromID(id snowflake.ID) string {
- localTime := time.UnixMilli(id.Time())
- return localTime.Format("02/01/2006 3:04 PM")
-}
diff --git a/internal/client/ui/viminput/linedecorations.go b/internal/client/ui/viminput/linedecorations.go
new file mode 100644
index 0000000..82e51f1
--- /dev/null
+++ b/internal/client/ui/viminput/linedecorations.go
@@ -0,0 +1,17 @@
+package viminput
+
+import (
+ "strconv"
+
+ "github.com/charmbracelet/lipgloss"
+)
+
+func LineNumberDecoration(style lipgloss.Style) LineDecoration {
+ return func(lnum int, line string, cursorLnum int) string {
+ return style.Render(strconv.FormatInt(int64(lnum), 10))
+ }
+}
+
+func EmptyLineDecoration(lnum int, line string, cursorLnum int) string {
+ return ""
+}
diff --git a/internal/client/ui/viminput/viminput.go b/internal/client/ui/viminput/viminput.go
new file mode 100644
index 0000000..e2b65a8
--- /dev/null
+++ b/internal/client/ui/viminput/viminput.go
@@ -0,0 +1,89 @@
+package viminput
+
+import (
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+)
+
+type (
+ Keymap struct{}
+ LineDecoration = func(lnum int, line string, cursorLnum int) string
+)
+
+type Line []byte
+
+type Model struct {
+ keymap Keymap
+
+ PlaceholderStyle lipgloss.Style
+ PromptStyle lipgloss.Style
+
+ Placeholder string
+ LineDecoration LineDecoration
+
+ lines []Line
+
+ width int
+ height int
+ focus bool
+}
+
+func New(width, height int) Model {
+ return Model{
+ keymap: Keymap{},
+ PlaceholderStyle: lipgloss.NewStyle(),
+ PromptStyle: lipgloss.NewStyle(),
+ Placeholder: "",
+ LineDecoration: EmptyLineDecoration,
+ width: width,
+ height: height,
+ focus: false,
+ }
+}
+
+func (m Model) Init() tea.Cmd {
+ return nil
+}
+
+func (m Model) View() string {
+ placeholder := m.PlaceholderStyle.Render(m.Placeholder)
+ lineDecoration := m.LineDecoration(1, "", 1)
+
+ result := lipgloss.JoinHorizontal(lipgloss.Top, lineDecoration, placeholder)
+
+ result = lipgloss.NewStyle().Width(m.width).Height(m.height).Render(result)
+
+ return result
+}
+
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+ if !m.focus {
+ return m, nil
+ }
+
+ return m, nil
+}
+
+func (m *Model) SetWidth(width int) {
+ m.width = width
+}
+
+func (m Model) Width() int {
+ return m.width
+}
+
+func (m *Model) SetHeight(height int) {
+ m.height = height
+}
+
+func (m Model) Height() int {
+ return m.height
+}
+
+func (m *Model) Focus() {
+ m.focus = true
+}
+
+func (m *Model) Blur() {
+ m.focus = false
+}