diff --git a/ui/toolkit/msg.go b/ui/toolkit/msg.go index 6514aea..bc2da8c 100644 --- a/ui/toolkit/msg.go +++ b/ui/toolkit/msg.go @@ -19,6 +19,7 @@ type MsgHandling struct { OnAnyUncaughtKey func(m interface{}, k tea.KeyMsg) (bool, []tea.Cmd) OnViewResize func(m interface{}) (bool, []tea.Cmd) OnWinOpenCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) + OnWinCloseCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) OnWinRefreshDataCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) OnWinFreshDataCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) } @@ -81,6 +82,10 @@ func (tk *ToolKit) HandleMsg(m interface{}, msg tea.Msg) (bool, []tea.Cmd) { if tk.mh.OnWinOpenCmd != nil { return tk.mh.OnWinOpenCmd(m, msg) } + case cmd.WinClose: + if tk.mh.OnWinCloseCmd != nil { + return tk.mh.OnWinCloseCmd(m, msg) + } case cmd.WinRefreshData: if tk.mh.OnWinRefreshDataCmd != nil { return tk.mh.OnWinRefreshDataCmd(m, msg) diff --git a/ui/windows/postcreate/handlers.go b/ui/windows/postcreate/handlers.go new file mode 100644 index 0000000..95e8e3b --- /dev/null +++ b/ui/windows/postcreate/handlers.go @@ -0,0 +1,80 @@ +package postcreate + +import ( + "strconv" + + tea "github.com/charmbracelet/bubbletea" + "github.com/mrusme/gobbs/models/post" + "github.com/mrusme/gobbs/models/reply" + "github.com/mrusme/gobbs/ui/cmd" +) + +func handleSubmit(mi interface{}) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + var irtID string = "" + var irtIRT string = "" + var irtSysIDX int = 0 + + if m.replyToIdx == 0 { + pst := m.replyToIface.(post.Post) + irtID = pst.ID + irtSysIDX = pst.SysIDX + } else { + rply := m.replyToIface.(reply.Reply) + irtID = strconv.Itoa(m.replyToIdx + 1) + irtIRT = rply.InReplyTo // TODO: THis is empty? Why? + irtSysIDX = rply.SysIDX + } + + r := reply.Reply{ + ID: irtID, + InReplyTo: irtIRT, + Body: m.textarea.Value(), + SysIDX: irtSysIDX, + } + + err := m.a.CreateReply(&r) + if err != nil { + m.ctx.Logger.Error(err) + cmds = append(cmds, cmd.New( + cmd.MsgError, + WIN_ID, + cmd.Arg{Name: "error", Value: err}, + ).Tea()) + return true, cmds + } + + m.textarea.Reset() + m.replyToIdx = 0 + cmds = append(cmds, cmd.New(cmd.WMCloseWin, WIN_ID).Tea()) + return true, cmds +} + +func handleWinOpenCmd(mi interface{}, c cmd.Command) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + if c.Target == WIN_ID { + m.xywh = c.GetArg("xywh").([4]int) + m.replyToIdx = c.GetArg("replyToIdx").(int) + m.replyTo = c.GetArg("replyTo").(string) + m.replyToIface = c.GetArg(m.replyTo) + cmds = append(cmds, m.textarea.Focus()) + return true, cmds + } + + return false, cmds +} + +func handleWinCloseCmd(mi interface{}, c cmd.Command) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + if c.Target == WIN_ID { + m.textarea.Reset() + return true, cmds + } + + return false, cmds +} diff --git a/ui/windows/postcreate/postcreate.go b/ui/windows/postcreate/postcreate.go index 42ac248..c816403 100644 --- a/ui/windows/postcreate/postcreate.go +++ b/ui/windows/postcreate/postcreate.go @@ -1,48 +1,22 @@ package postcreate import ( - "fmt" - "strconv" - "strings" - - "github.com/charmbracelet/bubbles/cursor" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textarea" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" "github.com/mrusme/gobbs/aggregator" - "github.com/mrusme/gobbs/models/post" - "github.com/mrusme/gobbs/models/reply" - "github.com/mrusme/gobbs/ui/cmd" "github.com/mrusme/gobbs/ui/ctx" - "github.com/mrusme/gobbs/ui/helpers" + "github.com/mrusme/gobbs/ui/toolkit" ) var ( WIN_ID = "postcreate" ) -type KeyMap struct { - Refresh key.Binding - Select key.Binding - Esc key.Binding - Quit key.Binding - Reply key.Binding -} - -var DefaultKeyMap = KeyMap{ - Reply: key.NewBinding( - key.WithKeys("ctrl+s"), - key.WithHelp("ctrl+s", "send"), - ), -} - type Model struct { - ctx *ctx.Ctx - keymap KeyMap - wh [2]int - focused bool - xywh [4]int + ctx *ctx.Ctx + tk *toolkit.ToolKit + + xywh [4]int textarea textarea.Model @@ -62,10 +36,13 @@ func (m Model) Init() tea.Cmd { func NewModel(c *ctx.Ctx) Model { m := Model{ - ctx: c, - keymap: DefaultKeyMap, - focused: false, - xywh: [4]int{0, 0, 0, 0}, + ctx: c, + tk: toolkit.New( + WIN_ID, + c.Theme, + c.Logger, + ), + xywh: [4]int{0, 0, 0, 0}, replyToIdx: 0, replyTo: "", @@ -79,100 +56,31 @@ func NewModel(c *ctx.Ctx) Model { m.textarea.Placeholder = "Type in your reply ..." m.textarea.Prompt = "" + m.tk.KeymapAdd("submit", "submit", "ctrl+s") + m.a, _ = aggregator.New(m.ctx) + m.tk.SetViewFunc(buildView) + m.tk.SetMsgHandling(toolkit.MsgHandling{ + OnKeymapKey: []toolkit.MsgHandlingKeymapKey{ + { + ID: "submit", + Handler: handleSubmit, + }, + }, + OnWinOpenCmd: handleWinOpenCmd, + OnWinCloseCmd: handleWinCloseCmd, + }) + return m } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd - switch msg := msg.(type) { - case tea.KeyMsg: - switch { - - case key.Matches(msg, m.keymap.Reply): - - var irtID string = "" - var irtIRT string = "" - var irtSysIDX int = 0 - - if m.replyToIdx == 0 { - pst := m.replyToIface.(post.Post) - irtID = pst.ID - irtSysIDX = pst.SysIDX - } else { - rply := m.replyToIface.(reply.Reply) - irtID = strconv.Itoa(m.replyToIdx + 1) - irtIRT = rply.InReplyTo // TODO: THis is empty? Why? - irtSysIDX = rply.SysIDX - } - - r := reply.Reply{ - ID: irtID, - InReplyTo: irtIRT, - Body: m.textarea.Value(), - SysIDX: irtSysIDX, - } - - err := m.a.CreateReply(&r) - if err != nil { - m.ctx.Logger.Error(err) - return m, cmd.New( - cmd.MsgError, - WIN_ID, - cmd.Arg{Name: "error", Value: err}, - ).Tea() - } - - m.textarea.Reset() - m.replyToIdx = 0 - return m, cmd.New(cmd.WMCloseWin, WIN_ID).Tea() - - } - - case tea.WindowSizeMsg: - m.wh[0] = msg.Width - m.wh[1] = msg.Height - m.ctx.Logger.Debugf("received WindowSizeMsg: %v\n", m.wh) - - case cmd.Command: - m.ctx.Logger.Debugf("got command: %v\n", msg) - switch msg.Call { - case cmd.WinOpen: - if msg.Target == WIN_ID { - m.xywh = msg.GetArg("xywh").([4]int) - m.replyToIdx = msg.GetArg("replyToIdx").(int) - m.replyTo = msg.GetArg("replyTo").(string) - m.replyToIface = msg.GetArg(m.replyTo) - return m, m.textarea.Focus() - } - case cmd.WinClose: - if msg.Target == WIN_ID { - m.textarea.Reset() - return m, nil - } - case cmd.WinFocus: - if msg.Target == WIN_ID || - msg.Target == "*" { - m.Focus() - } - return m, nil - case cmd.WinBlur: - if msg.Target == WIN_ID || - msg.Target == "*" { - m.Blur() - } - return m, nil - default: - m.ctx.Logger.Debugf("received unhandled command: %v\n", msg) - } - - case cursor.BlinkMsg: - m.ctx.Logger.Debugf("textarea is focused: %v\n", m.textarea.Focused()) - - // default: - // m.ctx.Logger.Debugf("received unhandled msg: %v\n", msg) + ret, cmds := m.tk.HandleMsg(&m, msg) + if ret { + return m, tea.Batch(cmds...) } var tcmd tea.Cmd @@ -185,81 +93,3 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } - -func (m *Model) Focus() { - m.focused = true - m.viewcache = m.buildView(false) -} - -func (m *Model) Blur() { - m.focused = false - m.viewcache = m.buildView(false) -} - -func (m Model) View() string { - return m.buildView(true) -} - -func (m Model) buildView(cached bool) string { - var view strings.Builder = strings.Builder{} - - if cached && m.viewcache != "" { - m.ctx.Logger.Debugln("Cached View()") - - m.textarea.SetWidth(m.viewcacheTextareaXY[2]) - m.textarea.SetHeight(m.viewcacheTextareaXY[3]) - - return helpers.PlaceOverlay( - m.viewcacheTextareaXY[0], m.viewcacheTextareaXY[1], - m.textarea.View(), m.viewcache, - false) - } - - title := "Reply" - if m.replyToIdx != 0 { - title += fmt.Sprintf(" to reply #%d", m.replyToIdx) - } - - var style lipgloss.Style - if m.focused { - style = m.ctx.Theme.DialogBox.Titlebar.Focused - } else { - style = m.ctx.Theme.DialogBox.Titlebar.Blurred - } - titlebar := style.Align(lipgloss.Center). - Width(m.wh[0]). - Render(title) - - textareaWidth := m.wh[0] - 2 - textareaHeight := 6 - m.textarea.SetWidth(textareaWidth) - m.textarea.SetHeight(textareaHeight) - - bottombar := m.ctx.Theme.DialogBox.Bottombar. - Width(m.wh[0]). - Render("ctrl+s send ยท esc close") - - replyWindow := lipgloss.JoinVertical( - lipgloss.Center, - titlebar, - m.textarea.View(), - bottombar, - ) - - var tmp string - if m.focused { - tmp = m.ctx.Theme.DialogBox.Window.Focused.Render(replyWindow) - } else { - tmp = m.ctx.Theme.DialogBox.Window.Blurred.Render(replyWindow) - } - - m.viewcacheTextareaXY[0] = 1 - m.viewcacheTextareaXY[1] = 2 - m.viewcacheTextareaXY[2] = textareaWidth - m.viewcacheTextareaXY[3] = textareaHeight - - view = strings.Builder{} - view.WriteString(tmp) - - return view.String() -} diff --git a/ui/windows/postcreate/view.go b/ui/windows/postcreate/view.go new file mode 100644 index 0000000..1425cf9 --- /dev/null +++ b/ui/windows/postcreate/view.go @@ -0,0 +1,51 @@ +package postcreate + +import ( + "fmt" + + "github.com/mrusme/gobbs/ui/helpers" +) + +func (m Model) View() string { + return m.tk.View(&m, true) +} + +func buildView(mi interface{}, cached bool) string { + var m *Model = mi.(*Model) + + if cached && m.viewcache != "" { + m.ctx.Logger.Debugln("Cached View()") + + m.textarea.SetWidth(m.viewcacheTextareaXY[2]) + m.textarea.SetHeight(m.viewcacheTextareaXY[3]) + + return helpers.PlaceOverlay( + m.viewcacheTextareaXY[0], m.viewcacheTextareaXY[1], + m.textarea.View(), m.viewcache, + false) + } + + title := "Reply" + if m.replyToIdx != 0 { + title += fmt.Sprintf(" to reply #%d", m.replyToIdx) + } + + textareaWidth := m.tk.ViewWidth() - 2 + textareaHeight := 6 + m.textarea.SetWidth(textareaWidth) + m.textarea.SetHeight(textareaHeight) + + m.viewcacheTextareaXY[0] = 1 + m.viewcacheTextareaXY[1] = 2 + m.viewcacheTextareaXY[2] = textareaWidth + m.viewcacheTextareaXY[3] = textareaHeight + + m.ctx.Logger.Debugln("View()") + m.ctx.Logger.Debugf("IsFocused: %v\n", m.tk.IsFocused()) + + return m.tk.Dialog( + title, + m.textarea.View(), + ) + +} diff --git a/ui/windows/postshow/postshow.go b/ui/windows/postshow/postshow.go index be85639..52ae675 100644 --- a/ui/windows/postshow/postshow.go +++ b/ui/windows/postshow/postshow.go @@ -25,8 +25,9 @@ var ( ) type Model struct { - ctx *ctx.Ctx - tk *toolkit.ToolKit + ctx *ctx.Ctx + tk *toolkit.ToolKit + viewport viewport.Model a *aggregator.Aggregator @@ -57,11 +58,11 @@ func NewModel(c *ctx.Ctx) Model { replyIDs: []string{}, } - m.tk.SetViewFunc(buildView) - m.a, _ = aggregator.New(m.ctx) - m.tk.KeymapAdd("reply", "reply", "r") + m.a, _ = aggregator.New(m.ctx) + + m.tk.SetViewFunc(buildView) m.tk.SetMsgHandling(toolkit.MsgHandling{ OnKeymapKey: []toolkit.MsgHandlingKeymapKey{ {