From 9d05f2999cf9cde88059a50a6644127b9df7c4b4 Mon Sep 17 00:00:00 2001 From: ayn2op Date: Sat, 12 Oct 2024 19:28:35 -0400 Subject: [PATCH] refactor: merge Application with Layout --- cmd/application.go | 68 ---------------------- cmd/guilds_tree.go | 7 ++- cmd/layout.go | 134 +++++++++++++++++++++++++++++++++++++++++++ cmd/main_flex.go | 83 --------------------------- cmd/message_input.go | 6 +- cmd/messages_text.go | 7 ++- cmd/run.go | 8 +-- cmd/state.go | 6 +- 8 files changed, 153 insertions(+), 166 deletions(-) delete mode 100644 cmd/application.go create mode 100644 cmd/layout.go delete mode 100644 cmd/main_flex.go diff --git a/cmd/application.go b/cmd/application.go deleted file mode 100644 index b62c463..0000000 --- a/cmd/application.go +++ /dev/null @@ -1,68 +0,0 @@ -package cmd - -import ( - "log/slog" - - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" -) - -type Application struct { - *tview.Application -} - -func newApplication() *Application { - app := &Application{ - Application: tview.NewApplication(), - } - - app.EnableMouse(cfg.Mouse) - app.SetInputCapture(app.onInputCapture) - return app -} - -func (app *Application) onInputCapture(event *tcell.EventKey) *tcell.EventKey { - switch event.Name() { - case cfg.Keys.Quit: - app.Stop() - case "Ctrl+C": - // https://github.com/rivo/tview/blob/a64fc48d7654432f71922c8b908280cdb525805c/application.go#L153 - return tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone) - } - - return event -} - -func (app *Application) show(token string) error { - if token == "" { - loginForm := newLoginForm(func(token string, err error) { - if err != nil { - slog.Error("failed to login", "err", err) - return - } - - if err := app.show(token); err != nil { - slog.Error("failed to show app", "err", err) - } - }) - app.SetRoot(loginForm, true) - } else { - // mainFlex must be initialized before opening a new state. - mainFlex = newMainFlex() - if err := openState(token); err != nil { - return err - } - - app.SetRoot(mainFlex, true) - } - - return nil -} - -func (app *Application) Run(token string) error { - if err := app.show(token); err != nil { - return err - } - - return app.Application.Run() -} diff --git a/cmd/guilds_tree.go b/cmd/guilds_tree.go index 0acde2b..0a05fc7 100644 --- a/cmd/guilds_tree.go +++ b/cmd/guilds_tree.go @@ -14,13 +14,14 @@ import ( type GuildsTree struct { *tview.TreeView - + app *tview.Application selectedChannelID discord.ChannelID } -func newGuildsTree() *GuildsTree { +func newGuildsTree(app *tview.Application) *GuildsTree { gt := &GuildsTree{ TreeView: tview.NewTreeView(), + app: app, } root := tview.NewTreeNode("") @@ -209,7 +210,7 @@ func (gt *GuildsTree) onSelected(n *tview.TreeNode) { mainFlex.messagesText.SetTitle(gt.channelToString(*c)) gt.selectedChannelID = ref - app.SetFocus(mainFlex.messageInput) + gt.app.SetFocus(mainFlex.messageInput) case nil: // Direct messages cs, err := discordState.PrivateChannels() if err != nil { diff --git a/cmd/layout.go b/cmd/layout.go new file mode 100644 index 0000000..f07be9b --- /dev/null +++ b/cmd/layout.go @@ -0,0 +1,134 @@ +package cmd + +import ( + "log/slog" + + "github.com/ayn2op/discordo/internal/constants" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + "github.com/zalando/go-keyring" +) + +type Layout struct { + app *tview.Application + flex *tview.Flex + + guildsTree *GuildsTree + messagesText *MessagesText + messageInput *MessageInput +} + +func newLayout() *Layout { + app := tview.NewApplication() + l := &Layout{ + app: app, + flex: tview.NewFlex(), + + guildsTree: newGuildsTree(app), + messagesText: newMessagesText(app), + messageInput: newMessageInput(app), + } + + l.init() + + l.app.EnableMouse(cfg.Mouse) + l.app.SetInputCapture(l.onAppInputCapture) + + l.flex.SetInputCapture(l.onFlexInputCapture) + return l +} + +func (l *Layout) show(token string) error { + if token == "" { + loginForm := newLoginForm(func(token string, err error) { + if err != nil { + slog.Error("failed to login", "err", err) + return + } + + if err := l.show(token); err != nil { + slog.Error("failed to show app", "err", err) + } + }) + l.app.SetRoot(loginForm, true) + } else { + if err := openState(token, l.app); err != nil { + return err + } + + l.app.SetRoot(l.flex, true) + } + + return nil +} + +func (l *Layout) run(token string) error { + if err := l.show(token); err != nil { + return err + } + + return l.app.Run() +} + +func (l *Layout) init() { + l.flex.Clear() + + right := tview.NewFlex() + right.SetDirection(tview.FlexRow) + right.AddItem(l.messagesText, 0, 1, false) + right.AddItem(l.messageInput, 3, 1, false) + // The guilds tree is always focused first at start-up. + l.flex.AddItem(l.guildsTree, 0, 1, true) + l.flex.AddItem(right, 0, 4, false) +} + +func (l *Layout) onAppInputCapture(event *tcell.EventKey) *tcell.EventKey { + switch event.Name() { + case cfg.Keys.Quit: + l.app.Stop() + case "Ctrl+C": + // https://github.com/rivo/tview/blob/a64fc48d7654432f71922c8b908280cdb525805c/application.go#L153 + return tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone) + } + + return event +} + +func (l *Layout) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey { + switch event.Name() { + case cfg.Keys.FocusGuildsTree: + l.app.SetFocus(l.guildsTree) + return nil + case cfg.Keys.FocusMessagesText: + l.app.SetFocus(l.messagesText) + return nil + case cfg.Keys.FocusMessageInput: + l.app.SetFocus(l.messageInput) + return nil + case cfg.Keys.Logout: + l.app.Stop() + + if err := keyring.Delete(constants.Name, "token"); err != nil { + slog.Error("failed to delete token from keyring", "err", err) + return nil + } + + return nil + case cfg.Keys.ToggleGuildsTree: + // The guilds tree is visible if the numbers of items is two. + if l.flex.GetItemCount() == 2 { + l.flex.RemoveItem(l.guildsTree) + + if l.guildsTree.HasFocus() { + l.app.SetFocus(l.flex) + } + } else { + l.init() + l.app.SetFocus(l.guildsTree) + } + + return nil + } + + return event +} diff --git a/cmd/main_flex.go b/cmd/main_flex.go deleted file mode 100644 index d49ddad..0000000 --- a/cmd/main_flex.go +++ /dev/null @@ -1,83 +0,0 @@ -package cmd - -import ( - "log/slog" - - "github.com/ayn2op/discordo/internal/constants" - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" - "github.com/zalando/go-keyring" -) - -type MainFlex struct { - *tview.Flex - - guildsTree *GuildsTree - messagesText *MessagesText - messageInput *MessageInput -} - -func newMainFlex() *MainFlex { - mf := &MainFlex{ - Flex: tview.NewFlex(), - - guildsTree: newGuildsTree(), - messagesText: newMessagesText(), - messageInput: newMessageInput(), - } - - mf.init() - mf.SetInputCapture(mf.onInputCapture) - return mf -} - -func (mf *MainFlex) init() { - mf.Clear() - - right := tview.NewFlex() - right.SetDirection(tview.FlexRow) - right.AddItem(mf.messagesText, 0, 1, false) - right.AddItem(mf.messageInput, 3, 1, false) - // The guilds tree is always focused first at start-up. - mf.AddItem(mf.guildsTree, 0, 1, true) - mf.AddItem(right, 0, 4, false) -} - -func (mf *MainFlex) onInputCapture(event *tcell.EventKey) *tcell.EventKey { - switch event.Name() { - case cfg.Keys.FocusGuildsTree: - app.SetFocus(mf.guildsTree) - return nil - case cfg.Keys.FocusMessagesText: - app.SetFocus(mf.messagesText) - return nil - case cfg.Keys.FocusMessageInput: - app.SetFocus(mf.messageInput) - return nil - case cfg.Keys.Logout: - app.Stop() - - if err := keyring.Delete(constants.Name, "token"); err != nil { - slog.Error("failed to delete token from keyring", "err", err) - return nil - } - - return nil - case cfg.Keys.ToggleGuildsTree: - // The guilds tree is visible if the numbers of items is two. - if mf.GetItemCount() == 2 { - mf.RemoveItem(mf.guildsTree) - - if mf.guildsTree.HasFocus() { - app.SetFocus(mf) - } - } else { - mf.init() - app.SetFocus(mf.guildsTree) - } - - return nil - } - - return event -} diff --git a/cmd/message_input.go b/cmd/message_input.go index b5994a1..3a43260 100644 --- a/cmd/message_input.go +++ b/cmd/message_input.go @@ -17,12 +17,14 @@ import ( type MessageInput struct { *tview.TextArea + app *tview.Application replyMessageID discord.MessageID } -func newMessageInput() *MessageInput { +func newMessageInput(app *tview.Application) *MessageInput { mi := &MessageInput{ TextArea: tview.NewTextArea(), + app: app, } mi.SetTextStyle(tcell.StyleDefault.Background(tcell.GetColor(cfg.Theme.BackgroundColor))) @@ -131,7 +133,7 @@ func (mi *MessageInput) editor() { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - app.Suspend(func() { + mi.app.Suspend(func() { err := cmd.Run() if err != nil { slog.Error("failed to run command", "err", err, "command", cmd) diff --git a/cmd/messages_text.go b/cmd/messages_text.go index 5aef5f0..8134174 100644 --- a/cmd/messages_text.go +++ b/cmd/messages_text.go @@ -21,13 +21,14 @@ import ( type MessagesText struct { *tview.TextView - + app *tview.Application selectedMessageID discord.MessageID } -func newMessagesText() *MessagesText { +func newMessagesText(app *tview.Application) *MessagesText { mt := &MessagesText{ TextView: tview.NewTextView(), + app: app, } mt.SetDynamicColors(true) @@ -345,7 +346,7 @@ func (mt *MessagesText) reply(mention bool) { title += msg.Author.Tag() mainFlex.messageInput.SetTitle(title) mainFlex.messageInput.replyMessageID = mt.selectedMessageID - app.SetFocus(mainFlex.messageInput) + mt.app.SetFocus(mainFlex.messageInput) } func (mt *MessagesText) delete() { diff --git a/cmd/run.go b/cmd/run.go index a27e0d0..0164f45 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -9,8 +9,7 @@ var ( discordState *State cfg *config.Config - app *Application - mainFlex *MainFlex + mainFlex *Layout ) func Run(token string) error { @@ -24,7 +23,6 @@ func Run(token string) error { return err } - // app must be initialized after configuration is loaded - app = newApplication() - return app.Run(token) + mainFlex = newLayout() + return mainFlex.run(token) } diff --git a/cmd/state.go b/cmd/state.go index a83c878..af7742f 100644 --- a/cmd/state.go +++ b/cmd/state.go @@ -27,11 +27,13 @@ func init() { type State struct { *ningen.State + app *tview.Application } -func openState(token string) error { +func openState(token string, app *tview.Application) error { discordState = &State{ State: ningen.New(token), + app: app, } // Handlers @@ -78,7 +80,7 @@ func (s *State) onReady(r *gateway.ReadyEvent) { } mainFlex.guildsTree.SetCurrentNode(root) - app.SetFocus(mainFlex.guildsTree) + s.app.SetFocus(mainFlex.guildsTree) } func (s *State) onMessageCreate(m *gateway.MessageCreateEvent) {