package ui import ( "context" "fmt" "log" "runtime" "strings" "github.com/ayn2op/discordo/config" "github.com/diamondburned/arikawa/v3/api" "github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/gateway" "github.com/diamondburned/arikawa/v3/state" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) func init() { tview.Borders.TopLeftFocus = tview.Borders.TopLeft tview.Borders.TopRightFocus = tview.Borders.TopRight tview.Borders.BottomLeftFocus = tview.Borders.BottomLeft tview.Borders.BottomRightFocus = tview.Borders.BottomRight tview.Borders.HorizontalFocus = tview.Borders.Horizontal tview.Borders.VerticalFocus = tview.Borders.Vertical tview.Borders.TopLeft = 0 tview.Borders.TopRight = 0 tview.Borders.BottomLeft = 0 tview.Borders.BottomRight = 0 tview.Borders.Horizontal = 0 tview.Borders.Vertical = 0 api.UserAgent = fmt.Sprintf("%s/%s %s/%s", config.Name, "0.1", "arikawa", "v3") gateway.DefaultIdentity = gateway.IdentifyProperties{ OS: runtime.GOOS, Browser: config.Name, Device: "", } } // Application is responsible for initialization and management of the application, widgets, configuration, and state. type Application struct { *tview.Application view *View config *config.Config state *state.State } func NewApplication(cfg *config.Config) *Application { app := &Application{ Application: tview.NewApplication(), config: cfg, } app.EnableMouse(app.config.Mouse) app.SetBeforeDrawFunc(app.onBeforeDraw) // The styles must be assigned before initializing a new view. tview.Styles.PrimitiveBackgroundColor = tcell.GetColor(cfg.Theme.Background) tview.Styles.BorderColor = tcell.GetColor(cfg.Theme.Border) tview.Styles.TitleColor = tcell.GetColor(cfg.Theme.Title) app.view = newView(app) return app } func (app *Application) Run(token string) { if token != "" { if err := app.Connect(token); err != nil { log.Fatal(err) } app.SetRoot(app.view, true) app.SetFocus(app.view.GuildsTree) } else { loginView := newLoginForm(app) app.SetRoot(loginView, true) } if err := app.Application.Run(); err != nil { log.Fatal(err) } } func (app *Application) Connect(token string) error { app.state = state.New(token) app.state.AddHandler(app.onReady) app.state.AddHandler(app.onGuildCreate) app.state.AddHandler(app.onGuildDelete) app.state.AddHandler(app.onMessageCreate) return app.state.Open(context.Background()) } func (app *Application) onBeforeDraw(screen tcell.Screen) bool { if app.config.Theme.Background == "default" { screen.Clear() } return false } func (c *Application) onReady(r *gateway.ReadyEvent) { root := c.view.GuildsTree.GetRoot() for _, gf := range r.UserSettings.GuildFolders { if gf.ID == 0 { for _, gID := range gf.GuildIDs { g, err := c.state.Cabinet.Guild(gID) if err != nil { log.Println(err) continue } guildNode := tview.NewTreeNode(g.Name) guildNode.SetReference(g.ID) root.AddChild(guildNode) } } else { var b strings.Builder if gf.Color != discord.NullColor { b.WriteByte('[') b.WriteString(gf.Color.String()) b.WriteByte(']') } else { b.WriteString("[#ED4245]") } if gf.Name != "" { b.WriteString(gf.Name) } else { b.WriteString("Folder") } b.WriteString("[-]") folderNode := tview.NewTreeNode(b.String()) root.AddChild(folderNode) for _, gID := range gf.GuildIDs { g, err := c.state.Cabinet.Guild(gID) if err != nil { log.Println(err) continue } guildNode := tview.NewTreeNode(g.Name) guildNode.SetReference(g.ID) folderNode.AddChild(guildNode) } } } c.view.GuildsTree.SetCurrentNode(root) c.SetFocus(c.view.GuildsTree) } func (c *Application) onGuildCreate(g *gateway.GuildCreateEvent) { guildNode := tview.NewTreeNode(g.Name) guildNode.SetReference(g.ID) rootNode := c.view.GuildsTree.GetRoot() rootNode.AddChild(guildNode) c.view.GuildsTree.SetCurrentNode(rootNode) c.SetFocus(c.view.GuildsTree) c.Draw() } func (c *Application) onGuildDelete(g *gateway.GuildDeleteEvent) { rootNode := c.view.GuildsTree.GetRoot() var parentNode *tview.TreeNode rootNode.Walk(func(node, _ *tview.TreeNode) bool { if node.GetReference() == g.ID { parentNode = node return false } return true }) if parentNode != nil { rootNode.RemoveChild(parentNode) } c.Draw() } func (c *Application) onMessageCreate(m *gateway.MessageCreateEvent) { if c.view.ChannelsTree.selected != nil && m.ChannelID == c.view.ChannelsTree.selected.ID { _, err := c.view.MessagesText.Write(buildMessage(c, m.Message)) if err != nil { return } if len(c.view.MessagesText.GetHighlights()) == 0 { c.view.MessagesText.ScrollToEnd() } } }