1
0
mirror of https://github.com/mrusme/neonmodem.git synced 2024-06-16 06:25:23 +00:00

Implemented postcreate window

This commit is contained in:
マリウス 2023-01-02 18:46:26 -05:00
parent f878acfe05
commit d9c07422d6
No known key found for this signature in database
GPG Key ID: 272ED814BF63261F
4 changed files with 177 additions and 256 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/mrusme/gobbs/ui/header"
"github.com/mrusme/gobbs/ui/views/posts"
"github.com/mrusme/gobbs/ui/windowmanager"
"github.com/mrusme/gobbs/ui/windows/postcreate"
"github.com/mrusme/gobbs/ui/windows/postshow"
"github.com/mrusme/gobbs/ui/views"
@ -109,13 +110,24 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.Call {
case cmd.WinOpen:
m.ctx.Logger.Debugln("received WinOpen")
ccmds = m.wm.Open(
msg.Target,
postshow.NewModel(m.ctx),
[4]int{3, 1, 4, 4},
&msg,
)
switch msg.Target {
case postshow.WIN_ID:
m.ctx.Logger.Debugln("received WinOpen")
ccmds = m.wm.Open(
msg.Target,
postshow.NewModel(m.ctx),
[4]int{3, 1, 4, 4},
&msg,
)
case postcreate.WIN_ID:
m.ctx.Logger.Debugln("received WinOpen")
ccmds = m.wm.Open(
msg.Target,
postcreate.NewModel(m.ctx),
[4]int{6, int(m.ctx.Content[1] / 3), 8, 4},
&msg,
)
}
m.ctx.Logger.Debugf("got back ccmds: %v\n", ccmds)
default:
if msg.Call < cmd.ViewFocus {
@ -130,8 +142,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Do nothing
default:
m.ctx.Logger.Debugf("updating all with default: %v\n", msg)
cmds = append(cmds, m.wm.UpdateAll(msg)...)
m.ctx.Logger.Debugf("updating focused with default: %v\n", msg)
cmds = append(cmds, m.wm.UpdateFocused(msg)...)
}
v, vcmd := m.views[m.currentView].Update(msg)

View File

@ -69,8 +69,11 @@ func (wm *WM) Close(id string) (bool, []tea.Cmd) {
tcmds = append(tcmds, cmd.New(cmd.WinClose, id).Tea())
wm.ctx.Loading = false
if wm.GetNumberOpen() == 0 {
nrOpen := wm.GetNumberOpen()
if nrOpen == 0 {
tcmds = append(tcmds, cmd.New(cmd.ViewFocus, "*").Tea())
} else {
tcmds = append(tcmds, wm.Focus(wm.stack[(nrOpen-1)].ID)...)
}
return true, tcmds
}
@ -146,6 +149,21 @@ func (wm *WM) UpdateAll(msg tea.Msg) []tea.Cmd {
return tcmds
}
func (wm *WM) UpdateFocused(msg tea.Msg) []tea.Cmd {
var tcmd tea.Cmd
var tcmds []tea.Cmd
i := len(wm.stack) - 1
if i < 0 {
return tcmds
}
wm.stack[i].Win, tcmd = wm.stack[i].Win.Update(msg)
tcmds = append(tcmds, tcmd)
return tcmds
}
func (wm *WM) Resize(id string, w int, h int) []tea.Cmd {
var tcmd tea.Cmd
var tcmds []tea.Cmd

View File

@ -2,12 +2,11 @@ package postcreate
import (
"fmt"
"strconv"
"strings"
"github.com/charmbracelet/bubbles/cursor"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
@ -16,7 +15,6 @@ import (
"github.com/mrusme/gobbs/models/reply"
"github.com/mrusme/gobbs/ui/cmd"
"github.com/mrusme/gobbs/ui/ctx"
"github.com/mrusme/gobbs/ui/helpers"
)
var (
@ -32,21 +30,6 @@ type KeyMap struct {
}
var DefaultKeyMap = KeyMap{
Refresh: key.NewBinding(
key.WithKeys("ctrl+r"),
key.WithHelp("ctrl+r", "refresh"),
),
Select: key.NewBinding(
key.WithKeys("r", "enter"),
key.WithHelp("r/enter", "read"),
),
Esc: key.NewBinding(
key.WithKeys("esc"),
key.WithHelp("esc", "close"),
),
Quit: key.NewBinding(
key.WithKeys("q"),
),
Reply: key.NewBinding(
key.WithKeys("ctrl+s"),
key.WithHelp("ctrl+s", "reply"),
@ -56,13 +39,18 @@ var DefaultKeyMap = KeyMap{
type Model struct {
ctx *ctx.Ctx
keymap KeyMap
textarea textarea.Model
wh [2]int
focused bool
textarea textarea.Model
a *aggregator.Aggregator
glam *glamour.TermRenderer
activeReply *reply.Reply
replyToIdx int
viewcache string
viewcacheTextareaXY []int
}
func (m Model) Init() tea.Cmd {
@ -79,10 +67,20 @@ func (m Model) Blur() {
func NewModel(c *ctx.Ctx) Model {
m := Model{
ctx: c,
keymap: DefaultKeyMap,
ctx: c,
keymap: DefaultKeyMap,
focused: false,
replyToIdx: 0,
viewcache: "",
viewcacheTextareaXY: []int{0, 0, 0, 0},
}
m.textarea = textarea.New()
m.textarea.Placeholder = "Type in your reply ..."
m.textarea.Prompt = ""
m.a, _ = aggregator.New(m.ctx)
return m
@ -96,79 +94,68 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch {
case key.Matches(msg, m.keymap.Reply):
replyToIdx, _ := strconv.Atoi(m.buffer)
m.ctx.Logger.Debugf("replyToIdx: %d", replyToIdx)
var irtID string = ""
var irtIRT string = ""
var irtSysIDX int = 0
if replyToIdx == 0 {
irtID = m.activePost.ID
irtSysIDX = m.activePost.SysIDX
} else {
irt := m.allReplies[(replyToIdx - 1)]
irtID = strconv.Itoa(replyToIdx + 1)
irtIRT = irt.InReplyTo
irtSysIDX = irt.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)
}
m.textarea.Reset()
m.buffer = ""
m.WMClose("reply")
return m, nil
// replyToIdx, _ := strconv.Atoi(m.buffer)
//
// m.ctx.Logger.Debugf("replyToIdx: %d", replyToIdx)
//
// var irtID string = ""
// var irtIRT string = ""
// var irtSysIDX int = 0
//
// if replyToIdx == 0 {
// irtID = m.activePost.ID
// irtSysIDX = m.activePost.SysIDX
// } else {
// irt := m.allReplies[(replyToIdx - 1)]
// irtID = strconv.Itoa(replyToIdx + 1)
// irtIRT = irt.InReplyTo
// irtSysIDX = irt.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)
// }
//
// m.textarea.Reset()
// m.buffer = ""
// m.WMClose("reply")
// return m, nil
}
case tea.WindowSizeMsg:
m.ctx.Logger.Debug("received WindowSizeMsg")
viewportWidth := m.ctx.Content[0] - 9
viewportHeight := m.ctx.Content[1] - 10
viewportStyle.Width(viewportWidth)
viewportStyle.Height(viewportHeight)
m.viewport = viewport.New(viewportWidth-4, viewportHeight-4)
m.viewport.Width = viewportWidth - 4
m.viewport.Height = viewportHeight + 1
// cmds = append(cmds, viewport.Sync(m.viewport))
case *post.Post:
m.ctx.Logger.Debug("got *post.Post")
m.activePost = msg
m.viewport.SetContent(m.renderViewport(m.activePost))
m.ctx.Loading = false
return m, nil
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.WinRefreshData:
if msg.Target == "post" {
m.activePost = msg.GetArg("post").(*post.Post)
m.ctx.Logger.Debugf("loading post: %v", m.activePost.ID)
m.ctx.Loading = true
return m, m.loadPost(m.activePost)
case cmd.WinOpen:
if msg.Target == WIN_ID {
return m, m.textarea.Focus()
}
case cmd.WinClose:
if msg.Target == WIN_ID {
m.textarea.Reset()
return m, nil
}
return m, nil
case cmd.WinFocus:
if msg.Target == "post" {
if msg.Target == WIN_ID ||
msg.Target == "*" {
m.focused = true
}
return m, nil
case cmd.WinBlur:
if msg.Target == "post" {
if msg.Target == WIN_ID ||
msg.Target == "*" {
m.focused = false
}
return m, nil
@ -176,14 +163,20 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
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)
}
var cmd tea.Cmd
var tcmd tea.Cmd
m.viewport, cmd = m.viewport.Update(msg)
cmds = append(cmds, cmd)
if !m.textarea.Focused() {
cmds = append(cmds, m.textarea.Focus())
}
m.textarea, tcmd = m.textarea.Update(msg)
cmds = append(cmds, tcmd)
return m, tea.Batch(cmds...)
}
@ -205,140 +198,57 @@ func (m Model) View() string {
func (m Model) buildView(cached bool) string {
var view strings.Builder = strings.Builder{}
var l string = ""
view.WriteString(lipgloss.JoinHorizontal(
lipgloss.Top,
l,
))
// 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)
// }
var style lipgloss.Style
if m.focused {
style = m.ctx.Theme.DialogBox.Titlebar.Focused
} else {
style = m.ctx.Theme.DialogBox.Titlebar.Blurred
title := "Reply"
if m.replyToIdx != 0 {
title += fmt.Sprintf(" to reply #%d", m.replyToIdx)
}
titlebar := style.Align(lipgloss.Center).
Width(m.viewport.Width + 4).
Render("Post")
titlebar := m.ctx.Theme.DialogBox.Titlebar.Focused.
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.viewport.Width + 4).
Render("[#]r reply · esc close")
Width(m.wh[0]).
Render("ctrl+enter reply · esc close")
ui := lipgloss.JoinVertical(
replyWindow := lipgloss.JoinVertical(
lipgloss.Center,
titlebar,
viewportStyle.Render(m.viewport.View()),
m.textarea.View(),
bottombar,
)
var tmp string
if m.focused {
tmp = helpers.PlaceOverlay(3, 2,
m.ctx.Theme.DialogBox.Window.Focused.Render(ui),
view.String(), true)
} else {
tmp = helpers.PlaceOverlay(3, 2,
m.ctx.Theme.DialogBox.Window.Blurred.Render(ui),
view.String(), true)
}
replyWindowX := 5
replyWindowY := m.ctx.Screen[1] - 21
tmp := m.ctx.Theme.DialogBox.Window.Focused.Render(replyWindow)
m.viewcacheTextareaXY[0] = replyWindowX + 1
m.viewcacheTextareaXY[1] = replyWindowY + 2
m.viewcacheTextareaXY[2] = textareaWidth
m.viewcacheTextareaXY[3] = textareaHeight
view = strings.Builder{}
view.WriteString(tmp)
// m.viewcache = view.String()
// return m.viewcache
return view.String()
}
func (m *Model) renderViewport(p *post.Post) string {
var out string = ""
var err error
m.glam, err = glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(m.viewport.Width),
)
if err != nil {
m.ctx.Logger.Error(err)
m.glam = nil
}
adj := "writes"
if p.Subject[len(p.Subject)-1:] == "?" {
adj = "asks"
}
body, err := m.glam.Render(p.Body)
if err != nil {
m.ctx.Logger.Error(err)
body = p.Body
}
out += fmt.Sprintf(
" %s\n\n %s\n%s",
m.ctx.Theme.Post.Author.Render(
fmt.Sprintf("%s %s:", p.Author.Name, adj),
),
m.ctx.Theme.Post.Subject.Render(p.Subject),
body,
)
m.replyIDs = []string{p.ID}
m.activePost = p
out += m.renderReplies(0, p.Author.Name, &p.Replies)
return out
}
func (m *Model) renderReplies(
level int,
inReplyTo string,
replies *[]reply.Reply,
) string {
var out string = ""
if replies == nil {
return ""
}
for ri, re := range *replies {
var err error = nil
var body string = ""
var author string = ""
if re.Deleted {
body = "\n DELETED\n\n"
author = "DELETED"
} else {
body, err = m.glam.Render(re.Body)
if err != nil {
m.ctx.Logger.Error(err)
body = re.Body
}
author = re.Author.Name
}
m.replyIDs = append(m.replyIDs, re.ID)
m.allReplies = append(m.allReplies, &(*replies)[ri])
idx := len(m.replyIDs) - 1
out += fmt.Sprintf(
"\n\n %s %s%s%s\n%s",
m.ctx.Theme.Reply.Author.Render(
author,
),
lipgloss.NewStyle().
Foreground(m.ctx.Theme.Reply.Author.GetBackground()).
Render(fmt.Sprintf("writes in reply to %s:", inReplyTo)),
strings.Repeat(" ", (m.viewport.Width-len(author)-len(inReplyTo)-28)),
lipgloss.NewStyle().
Foreground(lipgloss.Color("#777777")).
Render(fmt.Sprintf("#%d", idx)),
body,
)
idx++
out += m.renderReplies(level+1, re.Author.Name, &re.Replies)
}
return out
}

View File

@ -2,7 +2,6 @@ package postshow
import (
"fmt"
"strconv"
"strings"
"github.com/charmbracelet/bubbles/key"
@ -15,6 +14,7 @@ import (
"github.com/mrusme/gobbs/models/reply"
"github.com/mrusme/gobbs/ui/cmd"
"github.com/mrusme/gobbs/ui/ctx"
"github.com/mrusme/gobbs/ui/windows/postcreate"
)
var (
@ -30,32 +30,13 @@ var (
)
type KeyMap struct {
Refresh key.Binding
Select key.Binding
Esc key.Binding
Quit key.Binding
Reply key.Binding
Reply key.Binding
}
var DefaultKeyMap = KeyMap{
Refresh: key.NewBinding(
key.WithKeys("ctrl+r"),
key.WithHelp("ctrl+r", "refresh"),
),
Select: key.NewBinding(
key.WithKeys("r", "enter"),
key.WithHelp("r/enter", "read"),
),
Esc: key.NewBinding(
key.WithKeys("esc"),
key.WithHelp("esc", "close"),
),
Quit: key.NewBinding(
key.WithKeys("q"),
),
Reply: key.NewBinding(
key.WithKeys("ctrl+s"),
key.WithHelp("ctrl+s", "reply"),
key.WithKeys("r"),
key.WithHelp("r", "reply"),
),
}
@ -111,28 +92,28 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg:
switch {
case key.Matches(msg, m.keymap.Select):
if m.buffer != "" {
replyToID, err := strconv.Atoi(m.buffer)
if err != nil {
// TODO: Handle error
}
if replyToID >= len(m.replyIDs) {
// TODO: Handle error
}
}
// m.WMOpen("reply")
case key.Matches(msg, m.keymap.Reply):
// if m.buffer != "" {
// replyToID, err := strconv.Atoi(m.buffer)
// if err != nil {
// // TODO: Handle error
// }
//
// if replyToID >= len(m.replyIDs) {
// // TODO: Handle error
// }
// }
cmd := cmd.New(cmd.WinOpen, postcreate.WIN_ID, cmd.Arg{
Name: "post",
Value: &m.activePost,
})
cmds = append(cmds, cmd.Tea())
m.ctx.Logger.Debugln("caching view")
m.ctx.Logger.Debugf("buffer: %s", m.buffer)
// m.viewcache = m.buildView(false)
return m, nil
case key.Matches(msg, m.keymap.Esc), key.Matches(msg, m.keymap.Quit):
// m.WMClose("post")
return m, nil
return m, tea.Batch(cmds...)
default:
switch msg.String() {
@ -244,11 +225,11 @@ func (m Model) buildView(cached bool) string {
style = m.ctx.Theme.DialogBox.Titlebar.Blurred
}
titlebar := style.Align(lipgloss.Center).
Width(m.viewport.Width + 4).
Width(m.wh[0]).
Render("Post")
bottombar := m.ctx.Theme.DialogBox.Bottombar.
Width(m.viewport.Width + 4).
Width(m.wh[0]).
Render("[#]r reply · esc close")
ui := lipgloss.JoinVertical(