refactor: layout -> app

This commit is contained in:
ayn2op 2025-02-28 17:29:03 -05:00
parent bad44a4490
commit 34158b2f9e
6 changed files with 96 additions and 101 deletions

View file

@ -11,20 +11,22 @@ import (
"github.com/zalando/go-keyring"
)
type Layout struct {
type App struct {
*tview.Application
cfg *config.Config
app *tview.Application
flex *tview.Flex
guildsTree *GuildsTree
messagesText *MessagesText
messageInput *MessageInput
}
func newLayout(cfg *config.Config) *Layout {
func newApp(cfg *config.Config) *App {
app := tview.NewApplication()
l := &Layout{
a := &App{
Application: app,
cfg: cfg,
app: app,
flex: tview.NewFlex(),
guildsTree: newGuildsTree(app, cfg),
@ -32,66 +34,64 @@ func newLayout(cfg *config.Config) *Layout {
messageInput: newMessageInput(app, cfg),
}
l.init()
l.app.EnableMouse(cfg.Mouse)
l.app.SetInputCapture(l.onAppInputCapture)
l.flex.SetInputCapture(l.onFlexInputCapture)
return l
a.init()
a.EnableMouse(cfg.Mouse)
a.SetInputCapture(a.onInputCapture)
a.flex.SetInputCapture(a.onFlexInputCapture)
return a
}
func (l *Layout) show(token string) error {
func (app *App) show(token string) error {
if token == "" {
loginForm := login.NewForm(l.app, func(token string) {
if err := l.show(token); err != nil {
loginForm := login.NewForm(app.Application, func(token string) {
if err := app.show(token); err != nil {
slog.Error("failed to show app", "err", err)
return
}
})
l.app.SetRoot(loginForm, true)
app.SetRoot(loginForm, true)
} else {
if err := openState(token, l.app, l.cfg); err != nil {
if err := openState(token); err != nil {
return err
}
l.app.SetRoot(l.flex, true)
app.SetRoot(app.flex, true)
}
return nil
}
func (l *Layout) run(token string) error {
if err := l.show(token); err != nil {
func (app *App) run(token string) error {
if err := app.show(token); err != nil {
return err
}
return l.app.Run()
return app.Run()
}
func (l *Layout) init() {
l.flex.Clear()
func (app *App) init() {
app.flex.Clear()
right := tview.NewFlex()
right.SetDirection(tview.FlexRow)
right.AddItem(l.messagesText, 0, 1, false)
right.AddItem(l.messageInput, 3, 1, false)
right.AddItem(app.messagesText, 0, 1, false)
right.AddItem(app.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)
app.flex.AddItem(app.guildsTree, 0, 1, true)
app.flex.AddItem(right, 0, 4, false)
}
func (l *Layout) onAppInputCapture(event *tcell.EventKey) *tcell.EventKey {
func (app *App) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
switch event.Name() {
case l.cfg.Keys.Quit:
case app.cfg.Keys.Quit:
if discordState != nil {
if err := discordState.Close(); err != nil {
slog.Error("failed to close the session", "err", err)
}
}
l.app.Stop()
app.Stop()
case "Ctrl+C":
// https://github.com/rivo/tview/blob/a64fc48d7654432f71922c8b908280cdb525805c/application.go#L153
return tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone)
@ -100,19 +100,19 @@ func (l *Layout) onAppInputCapture(event *tcell.EventKey) *tcell.EventKey {
return event
}
func (l *Layout) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey {
func (app *App) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey {
switch event.Name() {
case l.cfg.Keys.FocusGuildsTree:
l.app.SetFocus(l.guildsTree)
case app.cfg.Keys.FocusGuildsTree:
app.SetFocus(app.guildsTree)
return nil
case l.cfg.Keys.FocusMessagesText:
l.app.SetFocus(l.messagesText)
case app.cfg.Keys.FocusMessagesText:
app.SetFocus(app.messagesText)
return nil
case l.cfg.Keys.FocusMessageInput:
l.app.SetFocus(l.messageInput)
case app.cfg.Keys.FocusMessageInput:
app.SetFocus(app.messageInput)
return nil
case l.cfg.Keys.Logout:
l.app.Stop()
case app.cfg.Keys.Logout:
app.Stop()
if err := keyring.Delete(consts.Name, "token"); err != nil {
slog.Error("failed to delete token from keyring", "err", err)
@ -120,17 +120,17 @@ func (l *Layout) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey {
}
return nil
case l.cfg.Keys.ToggleGuildsTree:
case app.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 app.flex.GetItemCount() == 2 {
app.flex.RemoveItem(app.guildsTree)
if l.guildsTree.HasFocus() {
l.app.SetFocus(l.flex)
if app.guildsTree.HasFocus() {
app.SetFocus(app.flex)
}
} else {
l.init()
l.app.SetFocus(l.guildsTree)
app.init()
app.SetFocus(app.guildsTree)
}
return nil

View file

@ -175,8 +175,8 @@ PARENT_CHANNELS:
func (gt *GuildsTree) onSelected(n *tview.TreeNode) {
gt.selectedChannelID = 0
layout.messagesText.reset()
layout.messageInput.reset()
app.messagesText.reset()
app.messageInput.reset()
if len(n.GetChildren()) != 0 {
n.SetExpanded(!n.IsExpanded())
@ -197,8 +197,8 @@ func (gt *GuildsTree) onSelected(n *tview.TreeNode) {
gt.createChannelNodes(n, cs)
case discord.ChannelID:
layout.messagesText.drawMsgs(ref)
layout.messagesText.ScrollToEnd()
app.messagesText.drawMsgs(ref)
app.messagesText.ScrollToEnd()
c, err := discordState.Cabinet.Channel(ref)
if err != nil {
@ -206,10 +206,10 @@ func (gt *GuildsTree) onSelected(n *tview.TreeNode) {
return
}
layout.messagesText.SetTitle(gt.channelToString(*c))
app.messagesText.SetTitle(gt.channelToString(*c))
gt.selectedChannelID = ref
gt.app.SetFocus(layout.messageInput)
gt.app.SetFocus(app.messageInput)
case nil: // Direct messages
cs, err := discordState.PrivateChannels()
if err != nil {

View file

@ -77,7 +77,7 @@ func (mi *MessageInput) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
}
func (mi *MessageInput) send() {
if !layout.guildsTree.selectedChannelID.IsValid() {
if !app.guildsTree.selectedChannelID.IsValid() {
return
}
@ -99,16 +99,16 @@ func (mi *MessageInput) send() {
}
go func() {
if _, err := discordState.SendMessageComplex(layout.guildsTree.selectedChannelID, data); err != nil {
slog.Error("failed to send message in channel", "channel_id", layout.guildsTree.selectedChannelID, "err", err)
if _, err := discordState.SendMessageComplex(app.guildsTree.selectedChannelID, data); err != nil {
slog.Error("failed to send message in channel", "channel_id", app.guildsTree.selectedChannelID, "err", err)
}
}()
mi.replyMessageID = 0
mi.reset()
layout.messagesText.Highlight()
layout.messagesText.ScrollToEnd()
app.messagesText.Highlight()
app.messagesText.ScrollToEnd()
}
func (mi *MessageInput) editor() {

View file

@ -73,12 +73,12 @@ func (mt *MessagesText) drawMsgs(cID discord.ChannelID) {
}
for _, m := range slices.Backward(ms) {
layout.messagesText.createMessage(m)
app.messagesText.createMessage(m)
}
}
func (mt *MessagesText) reset() {
layout.messagesText.selectedMessageID = 0
app.messagesText.selectedMessageID = 0
mt.SetTitle("")
mt.Clear()
@ -172,7 +172,7 @@ func (mt *MessagesText) getSelectedMessage() (*discord.Message, error) {
return nil, errors.New("no message is currently selected")
}
msg, err := discordState.Cabinet.Message(layout.guildsTree.selectedChannelID, mt.selectedMessageID)
msg, err := discordState.Cabinet.Message(app.guildsTree.selectedChannelID, mt.selectedMessageID)
if err != nil {
return nil, fmt.Errorf("could not retrieve selected message: %w", err)
}
@ -181,7 +181,7 @@ func (mt *MessagesText) getSelectedMessage() (*discord.Message, error) {
}
func (mt *MessagesText) getSelectedMessageIndex() (int, error) {
ms, err := discordState.Cabinet.Messages(layout.guildsTree.selectedChannelID)
ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID)
if err != nil {
return -1, err
}
@ -215,9 +215,9 @@ func (mt *MessagesText) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
}
func (mt *MessagesText) _select(name string) {
ms, err := discordState.Cabinet.Messages(layout.guildsTree.selectedChannelID)
ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID)
if err != nil {
slog.Error("failed to get messages", "err", err, "channel_id", layout.guildsTree.selectedChannelID)
slog.Error("failed to get messages", "err", err, "channel_id", app.guildsTree.selectedChannelID)
return
}
@ -341,9 +341,9 @@ func (mt *MessagesText) reply(mention bool) {
}
title += msg.Author.Tag()
layout.messageInput.SetTitle(title)
layout.messageInput.replyMessageID = mt.selectedMessageID
mt.app.SetFocus(layout.messageInput)
app.messageInput.SetTitle(title)
app.messageInput.replyMessageID = mt.selectedMessageID
mt.app.SetFocus(app.messageInput)
}
func (mt *MessagesText) delete() {
@ -355,7 +355,7 @@ func (mt *MessagesText) delete() {
clientID := discordState.Ready().User.ID
if msg.GuildID.IsValid() {
ps, err := discordState.Permissions(layout.guildsTree.selectedChannelID, discordState.Ready().User.ID)
ps, err := discordState.Permissions(app.guildsTree.selectedChannelID, discordState.Ready().User.ID)
if err != nil {
return
}
@ -369,25 +369,25 @@ func (mt *MessagesText) delete() {
}
}
if err := discordState.DeleteMessage(layout.guildsTree.selectedChannelID, msg.ID, ""); err != nil {
slog.Error("failed to delete message", "err", err, "channel_id", layout.guildsTree.selectedChannelID, "message_id", msg.ID)
if err := discordState.DeleteMessage(app.guildsTree.selectedChannelID, msg.ID, ""); err != nil {
slog.Error("failed to delete message", "err", err, "channel_id", app.guildsTree.selectedChannelID, "message_id", msg.ID)
return
}
if err := discordState.MessageRemove(layout.guildsTree.selectedChannelID, msg.ID); err != nil {
slog.Error("failed to delete message", "err", err, "channel_id", layout.guildsTree.selectedChannelID, "message_id", msg.ID)
if err := discordState.MessageRemove(app.guildsTree.selectedChannelID, msg.ID); err != nil {
slog.Error("failed to delete message", "err", err, "channel_id", app.guildsTree.selectedChannelID, "message_id", msg.ID)
return
}
ms, err := discordState.Cabinet.Messages(layout.guildsTree.selectedChannelID)
ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID)
if err != nil {
slog.Error("failed to delete message", "err", err, "channel_id", layout.guildsTree.selectedChannelID)
slog.Error("failed to delete message", "err", err, "channel_id", app.guildsTree.selectedChannelID)
return
}
mt.Clear()
for _, m := range slices.Backward(ms) {
layout.messagesText.createMessage(m)
app.messagesText.createMessage(m)
}
}

View file

@ -11,7 +11,7 @@ import (
var (
discordState *State
layout *Layout
app *App
)
func Run(token string) error {
@ -34,6 +34,6 @@ func Run(token string) error {
return err
}
layout = newLayout(cfg)
return layout.run(token)
app = newApp(cfg)
return app.run(token)
}

View file

@ -6,7 +6,6 @@ import (
"runtime"
"slices"
"github.com/ayn2op/discordo/internal/config"
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
@ -18,29 +17,25 @@ import (
type State struct {
*ningen.State
cfg *config.Config
app *tview.Application
}
func openState(token string, app *tview.Application, cfg *config.Config) error {
api.UserAgent = cfg.UserAgent
func openState(token string) error {
api.UserAgent = app.cfg.UserAgent
gateway.DefaultIdentity = gateway.IdentifyProperties{
OS: runtime.GOOS,
Device: "",
Browser: cfg.Browser,
BrowserVersion: cfg.BrowserVersion,
BrowserUserAgent: cfg.UserAgent,
Browser: app.cfg.Browser,
BrowserVersion: app.cfg.BrowserVersion,
BrowserUserAgent: app.cfg.UserAgent,
}
gateway.DefaultPresence = &gateway.UpdatePresenceCommand{
Status: cfg.Status,
Status: app.cfg.Status,
}
discordState = &State{
State: ningen.New(token),
cfg: cfg,
app: app,
}
// Handlers
@ -62,11 +57,11 @@ func (s *State) onRequest(r httpdriver.Request) error {
}
func (s *State) onReady(r *gateway.ReadyEvent) {
root := layout.guildsTree.GetRoot()
root := app.guildsTree.GetRoot()
root.ClearChildren()
dmNode := tview.NewTreeNode("Direct Messages")
dmNode.SetColor(tcell.GetColor(s.cfg.Theme.GuildsTree.PrivateChannelColor))
dmNode.SetColor(tcell.GetColor(app.cfg.Theme.GuildsTree.PrivateChannelColor))
root.AddChild(dmNode)
// Track guilds that have a parent (folder) to add orphan channels later
@ -78,32 +73,32 @@ func (s *State) onReady(r *gateway.ReadyEvent) {
}
folderGuildIds = append(folderGuildIds, folder.GuildIDs...)
layout.guildsTree.createFolderNode(folder)
app.guildsTree.createFolderNode(folder)
}
// add orphan (without folder) guilds to guilds tree
for _, guild := range r.Guilds {
if !slices.Contains(folderGuildIds, guild.ID) {
layout.guildsTree.createGuildNode(root, guild.Guild)
app.guildsTree.createGuildNode(root, guild.Guild)
}
}
layout.guildsTree.SetCurrentNode(root)
s.app.SetFocus(layout.guildsTree)
app.guildsTree.SetCurrentNode(root)
app.SetFocus(app.guildsTree)
}
func (s *State) onMessageCreate(m *gateway.MessageCreateEvent) {
if layout.guildsTree.selectedChannelID.IsValid() && layout.guildsTree.selectedChannelID == m.ChannelID {
layout.messagesText.createMessage(m.Message)
if app.guildsTree.selectedChannelID.IsValid() && app.guildsTree.selectedChannelID == m.ChannelID {
app.messagesText.createMessage(m.Message)
}
}
func (s *State) onMessageDelete(m *gateway.MessageDeleteEvent) {
if layout.guildsTree.selectedChannelID == m.ChannelID {
layout.messagesText.selectedMessageID = 0
layout.messagesText.Highlight()
layout.messagesText.Clear()
if app.guildsTree.selectedChannelID == m.ChannelID {
app.messagesText.selectedMessageID = 0
app.messagesText.Highlight()
app.messagesText.Clear()
layout.messagesText.drawMsgs(m.ChannelID)
app.messagesText.drawMsgs(m.ChannelID)
}
}