Skip to content

Commit

Permalink
Merge pull request #3 from treethought/cleaner-msg-flow
Browse files Browse the repository at this point in the history
Cleaner msg flow, better sizing
  • Loading branch information
treethought authored Jun 15, 2024
2 parents b3a5fa6 + 19bde1f commit 76f3148
Show file tree
Hide file tree
Showing 13 changed files with 405 additions and 237 deletions.
221 changes: 89 additions & 132 deletions ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
// TODO provide to models
var renderer *lipgloss.Renderer = lipgloss.DefaultRenderer()

var (
mainStyle = lipgloss.NewStyle().Margin(0).Padding(0).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("#874BFD"))
)

func NewStyle() lipgloss.Style {
return renderer.NewStyle()
}
Expand All @@ -34,16 +38,6 @@ func navNameCmd(name string) tea.Cmd {
}
}

type FocusMsg struct {
Name string
}

func focusCmd(name string) tea.Cmd {
return func() tea.Msg {
return FocusMsg{Name: name}
}
}

type SelectCastMsg struct {
cast *api.Cast
}
Expand All @@ -55,7 +49,6 @@ type AppContext struct {
}

type App struct {
models map[string]tea.Model
ctx *AppContext
client *api.Client
cfg *config.Config
Expand All @@ -73,6 +66,11 @@ type App struct {
// signinPrompt *SigninPrompt
splash *SplashView
help *HelpView

feed *FeedView
channel *FeedView
profile *Profile
cast *CastView
}

func (a *App) PublicKey() string {
Expand Down Expand Up @@ -117,12 +115,20 @@ func NewApp(cfg *config.Config, ctx *AppContext) *App {
ctx = &AppContext{}
}
a := &App{
models: make(map[string]tea.Model),
showSidebar: true,
ctx: ctx,
client: api.NewClient(cfg),
cfg: cfg,
}
a.feed = NewFeedView(a, feedTypeFollowing)
a.focusedModel = a.feed

a.profile = NewProfile(a)

a.channel = NewFeedView(a, feedTypeChannel)

a.cast = NewCastView(a, nil)

a.sidebar = NewSidebar(a)
a.quickSelect = NewQuickSelect(a)
a.publish = NewPublishInput(a)
Expand All @@ -135,81 +141,76 @@ func NewApp(cfg *config.Config, ctx *AppContext) *App {
}
a.SetNavName("feed")

feed := NewFeedView(a)
a.Register("feed", feed)
a.SetFocus("feed")

castDetails := NewCastView(a, nil)
a.Register("cast", castDetails)

profile := NewProfile(a)
a.Register("profile", profile)
return a
}

func (a *App) GetModel(name string) tea.Model {
m, ok := a.models[name]
if !ok {
log.Println("model not found: ", name)
}
return m
}

func (a *App) Register(name string, model tea.Model) {
a.models[name] = model
}

func (a *App) SetNavName(name string) {
a.prevName = a.navname
a.navname = name
}

func (a *App) SetFocus(name string) tea.Cmd {
func (a *App) focusMain() {
if a.showQuickSelect {
a.showQuickSelect = false
}
if a.publish.Active() {
a.publish.SetActive(false)
a.publish.SetFocus(false)
}
if name == "" || name == a.focused {
return nil
}
// clear if we're back at feed
a.prev = ""
if name != "feed" {
a.prev = a.focused
}
m, ok := a.models[name]
if !ok {
log.Println("model not found: ", name)
}
a.focusedModel = m
a.focused = name
a.sidebar.SetActive(false)
if a.help.IsFull() {
a.help.SetFull(false)
}
}

func (a *App) FocusFeed() tea.Cmd {
a.focusMain()
a.SetNavName("feed")
a.focusedModel = a.feed
a.focused = "feed"
return nil
return m.Init()
}

func (a *App) FocusProfile() tea.Cmd {
a.focusMain()
a.SetNavName("profile")
a.focusedModel = a.profile
a.focused = "profile"
return a.profile.Init()
}

func (a *App) FocusChannel() tea.Cmd {
a.focusMain()
a.SetNavName("channel")
a.focusedModel = a.channel
a.focused = "channel"
return a.channel.Init()
}

func (a *App) FocusCast() tea.Cmd {
a.focusMain()
a.SetNavName("cast")
a.focusedModel = a.cast
a.focused = "cast"
return a.cast.Init()
}

func (a *App) GetFocused() tea.Model {
return a.focusedModel
}

func (a *App) FocusPrev() tea.Cmd {
if a.help.IsFull() {
a.help.SetFull(false)
}
prev := a.GetModel(a.prev)
if a.prev == "" || prev == nil {
return nil
}

if m := a.GetModel(a.prev); m != nil {
a.SetNavName(a.prevName)
return a.SetFocus(a.prev)
}
a.SetNavName("feed")
return a.SetFocus("feed")
switch a.prev {
case "feed":
return a.FocusFeed()
case "profile":
return a.FocusProfile()
case "channel":
return a.FocusChannel()
case "cast":
return a.FocusCast()
}
return a.FocusFeed()
}

func (a *App) Init() tea.Cmd {
Expand All @@ -222,16 +223,6 @@ func (a *App) Init() tea.Cmd {
return tea.Batch(cmds...)
}

func (a *App) propagateEvent(msg tea.Msg) tea.Cmd {
for name, m := range a.models {
if name == a.focused {
um, cmd := m.Update(msg)
a.models[name] = um
return cmd
}
}
return nil
}
func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

// log.Println("received msg type: ", reflect.TypeOf(msg))
Expand All @@ -247,11 +238,6 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case navNameMsg:
a.SetNavName(msg.name)
return a, nil
case FocusMsg:
cmd := a.SetFocus(msg.Name)
if cmd != nil {
return a, cmd
}
case *postResponseMsg:
_, cmd := a.publish.Update(msg)
return a, tea.Sequence(cmd, a.FocusPrev())
Expand All @@ -268,80 +254,61 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case *channelInfoMsg:
a.splash.SetInfo(msg.channel.Name)
case *api.FeedResponse:
// allow msg to pass through to profile's embedded feed
fm := a.GetFocused()
if a.focused == "profile" {
if p, ok := fm.(*Profile); ok {
p.feed.Clear()
}
_, cmd := fm.Update(msg)
return a, cmd
}
if a.focused == "feed" && a.prev == "feed" {
return a, nil
}
feed := a.GetModel("feed").(*FeedView)
feed.Clear()
focusCmd := a.SetFocus("feed")
// for first load
a.splash.SetInfo("loading channels...")
return a, tea.Batch(feed.setItems(msg.Casts), focusCmd)
case SelectProfileMsg:
focusCmd := a.SetFocus("profile")
cmd := a.GetModel("profile").(*Profile).SetFID(msg.fid)
return a, tea.Batch(focusCmd, cmd)
// pas through to feed or profile
// case SelectProfileMsg:
case SelectCastMsg:
nav := fmt.Sprintf("cast by @%s", msg.cast.Author.Username)
if msg.cast.ParentHash != "" {
nav = fmt.Sprintf("reply by @%s", msg.cast.Author.Username)
}

a.SetNavName(nav)
focusCmd := a.SetFocus("cast")
cmd := a.GetModel("cast").(*CastView).SetCast(msg.cast)
return a, tea.Sequence(cmd, focusCmd)
return a, tea.Sequence(
a.cast.SetCast(msg.cast),
a.FocusCast(),
)

case tea.WindowSizeMsg:
SetHeight(msg.Height)
SetWidth(msg.Width)

a.statusLine.SetSize(msg.Width, 1)
_, statusHeight := lipgloss.Size(a.statusLine.View())

// set the height of the statusLine
wx, wy := msg.Width, msg.Height-1
wx, wy := msg.Width, msg.Height-statusHeight

sideMax := 30
sidePct := int(float64(wx) * 0.2)
sx := sidePct
if sideMax < sidePct {
sx = sideMax
}
a.sidebar.SetSize(sx, wy)

qw := wx - sx
qh := wy - 10
a.quickSelect.SetSize(qw, qh)
a.sidebar.SetSize(sx, wy-4)
sideWidth, _ := lipgloss.Size(a.sidebar.View())

pw := wx - sx
pw := wx - sideWidth
py := wy - 10
a.publish.SetSize(pw, py)
a.splash.SetSize(pw, py)
a.quickSelect.SetSize(pw, py)
a.help.SetSize(pw, py)

hw := wx - sx
hy := wy - 10
a.help.SetSize(hw, hy)
fx, fy := mainStyle.GetFrameSize()
mx, my := wx-sideWidth-fx-4, wy-fy

// substract the sidebar width from the window width
mx, my := wx-sx, wy
childMsg := tea.WindowSizeMsg{
Width: mx,
Height: my,
}

for n, m := range a.models {
um, cmd := m.Update(childMsg)
a.models[n] = um
cmds = append(cmds, cmd)
}
_, fcmd := a.feed.Update(childMsg)
_, pcmd := a.profile.Update(childMsg)
_, ccmd := a.channel.Update(childMsg)
_, cscmd := a.cast.Update(childMsg)

cmds = append(cmds, fcmd, pcmd, ccmd, cscmd)

case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
Expand Down Expand Up @@ -426,21 +393,11 @@ func (a *App) View() string {
main = a.help.View()
}

main = mainStyle.Render(main)

return lipgloss.JoinVertical(lipgloss.Top,
lipgloss.JoinHorizontal(lipgloss.Center, side, main),
a.statusLine.View(),
)

}

func UpdateChildren(msg tea.Msg, models ...tea.Model) tea.Cmd {
cmds := make([]tea.Cmd, len(models))

// Only text inputs with Focus() set will respond, so it's safe to simply
// update all of them here without any further logic.
for i := range models {
models[i], cmds[i] = models[i].Update(msg)
}

return tea.Batch(cmds...)
}
Loading

0 comments on commit 76f3148

Please sign in to comment.