Răsfoiți Sursa

refactor: merge Application with Layout

ayn2op 1 an în urmă
părinte
comite
9d05f2999c
8 a modificat fișierele cu 153 adăugiri și 166 ștergeri
  1. 0 68
      cmd/application.go
  2. 4 3
      cmd/guilds_tree.go
  3. 134 0
      cmd/layout.go
  4. 0 83
      cmd/main_flex.go
  5. 4 2
      cmd/message_input.go
  6. 4 3
      cmd/messages_text.go
  7. 3 5
      cmd/run.go
  8. 4 2
      cmd/state.go

+ 0 - 68
cmd/application.go

@@ -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()
-}

+ 4 - 3
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 {

+ 134 - 0
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
+}

+ 0 - 83
cmd/main_flex.go

@@ -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
-}

+ 4 - 2
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)

+ 4 - 3
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() {

+ 3 - 5
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)
 }

+ 4 - 2
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) {