Remove vi interface (#376)
This commit is contained in:
parent
8692701e30
commit
c87f3fffc6
7 changed files with 362 additions and 413 deletions
37
README.md
37
README.md
|
|
@ -51,9 +51,9 @@ go build .
|
|||
|
||||
The configuration file allows you to configure and customize the behavior, keybindings, and theme of the application.
|
||||
|
||||
- Unix: `$XDG_CONFIG_HOME/discordo/config.yml` or `$HOME/.config/discordo/config.yml`
|
||||
- Darwin: `$HOME/Library/Application Support/discordo/config.yml`
|
||||
- Windows: `%AppData%/discordo/config.yml`
|
||||
- Unix: `$XDG_CONFIG_HOME/discordo/config.toml` or `$HOME/.config/discordo/config.toml`
|
||||
- Darwin: `$HOME/Library/Application Support/discordo/config.toml`
|
||||
- Windows: `%AppData%/discordo/config.toml`
|
||||
|
||||
```toml
|
||||
mouse = true
|
||||
|
|
@ -62,24 +62,19 @@ timestamps_before_author = false
|
|||
messages_limit = 50
|
||||
editor = "default"
|
||||
|
||||
[keys.normal]
|
||||
insert_mode = "Rune[i]"
|
||||
[keys]
|
||||
focus_guilds_tree = "Ctrl+G"
|
||||
focus_messages_text = "Ctrl+T"
|
||||
toggle_guild_tree = "Ctrl+B"
|
||||
select_previous = "Rune[k]"
|
||||
select_next = "Rune[j]"
|
||||
select_first = "Rune[g]"
|
||||
select_last = "Rune[G]"
|
||||
|
||||
[keys.normal.guilds_tree]
|
||||
[keys.guilds_tree]
|
||||
select_current = "Enter"
|
||||
select_previous = "Rune[k]"
|
||||
select_next = "Rune[j]"
|
||||
select_first = "Rune[g]"
|
||||
select_last = "Rune[G]"
|
||||
|
||||
[keys.normal.messages_text]
|
||||
select_previous = "Rune[k]"
|
||||
select_next = "Rune[j]"
|
||||
select_first = "Rune[g]"
|
||||
select_last = "Rune[G]"
|
||||
[keys.messages_text]
|
||||
select_reply = "Rune[s]"
|
||||
reply = "Rune[r]"
|
||||
reply_mention = "Rune[R]"
|
||||
|
|
@ -87,12 +82,10 @@ delete = "Rune[d]"
|
|||
yank = "Rune[y]"
|
||||
open = "Rune[o]"
|
||||
|
||||
[keys.insert]
|
||||
normal_mode = "Esc"
|
||||
|
||||
[keys.insert.message_input]
|
||||
[keys.message_input]
|
||||
send = "Enter"
|
||||
editor = "Ctrl+E"
|
||||
cancel = "Esc"
|
||||
|
||||
[theme]
|
||||
border = true
|
||||
|
|
@ -104,16 +97,12 @@ background_color = "default"
|
|||
[theme.guilds_tree]
|
||||
auto_expand_folders = true
|
||||
graphics = true
|
||||
|
||||
|
||||
[theme.messages_text]
|
||||
author_color = "aqua"
|
||||
reply_indicator = "╭ "
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
[Here.](./docs)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Automated user accounts or "self-bots" are against Discord's Terms of Service. I am not responsible for any loss caused by using "self-bots" or Discordo.
|
||||
|
|
|
|||
|
|
@ -108,22 +108,23 @@ func (gt *GuildsTree) channelToString(c discord.Channel) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func (gt *GuildsTree) createChannelNode(n *tview.TreeNode, c discord.Channel) {
|
||||
func (gt *GuildsTree) createChannelNode(n *tview.TreeNode, c discord.Channel) *tview.TreeNode {
|
||||
if c.Type != discord.DirectMessage && c.Type != discord.GroupDM {
|
||||
ps, err := discordState.Permissions(c.ID, discordState.Ready().User.ID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ps.Has(discord.PermissionViewChannel) {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
cn := tview.NewTreeNode(gt.channelToString(c))
|
||||
cn.SetReference(c.ID)
|
||||
n.AddChild(cn)
|
||||
return cn
|
||||
}
|
||||
|
||||
func (gt *GuildsTree) createChannelNodes(n *tview.TreeNode, cs []discord.Channel) {
|
||||
|
|
@ -219,23 +220,18 @@ func (gt *GuildsTree) onSelected(n *tview.TreeNode) {
|
|||
}
|
||||
|
||||
func (gt *GuildsTree) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch mainFlex.mode {
|
||||
case ModeNormal:
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Normal.GuildsTree.SelectCurrent:
|
||||
return tcell.NewEventKey(tcell.KeyEnter, 0, tcell.ModNone)
|
||||
case cfg.Keys.Normal.GuildsTree.SelectPrevious:
|
||||
return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
|
||||
case cfg.Keys.Normal.GuildsTree.SelectNext:
|
||||
return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
|
||||
case cfg.Keys.Normal.GuildsTree.SelectFirst:
|
||||
return tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone)
|
||||
case cfg.Keys.Normal.GuildsTree.SelectLast:
|
||||
return tcell.NewEventKey(tcell.KeyEnd, 0, tcell.ModNone)
|
||||
}
|
||||
switch event.Name() {
|
||||
case cfg.Keys.SelectPrevious:
|
||||
return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
|
||||
case cfg.Keys.SelectNext:
|
||||
return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
|
||||
case cfg.Keys.SelectFirst:
|
||||
return tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone)
|
||||
case cfg.Keys.SelectLast:
|
||||
return tcell.NewEventKey(tcell.KeyEnd, 0, tcell.ModNone)
|
||||
|
||||
// do not propagate event to the children in normal mode.
|
||||
return nil
|
||||
case cfg.Keys.GuildsTree.SelectCurrent:
|
||||
return tcell.NewEventKey(tcell.KeyEnter, 0, tcell.ModNone)
|
||||
}
|
||||
|
||||
return event
|
||||
|
|
|
|||
|
|
@ -5,17 +5,9 @@ import (
|
|||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Mode uint
|
||||
|
||||
const (
|
||||
ModeNormal Mode = iota
|
||||
ModeInsert
|
||||
)
|
||||
|
||||
type MainFlex struct {
|
||||
*tview.Flex
|
||||
|
||||
mode Mode
|
||||
guildsTree *GuildsTree
|
||||
messagesText *MessagesText
|
||||
messageInput *MessageInput
|
||||
|
|
@ -25,23 +17,11 @@ func newMainFlex() *MainFlex {
|
|||
mf := &MainFlex{
|
||||
Flex: tview.NewFlex(),
|
||||
|
||||
mode: ModeNormal,
|
||||
guildsTree: newGuildsTree(),
|
||||
messagesText: newMessagesText(),
|
||||
messageInput: newMessageInput(),
|
||||
}
|
||||
|
||||
app.SetBeforeDrawFunc(func(screen tcell.Screen) bool {
|
||||
switch mf.mode {
|
||||
case ModeNormal:
|
||||
mf.messageInput.SetBorderAttributes(tcell.AttrNone)
|
||||
case ModeInsert:
|
||||
mf.messageInput.SetBorderAttributes(tcell.AttrDim)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
mf.init()
|
||||
mf.SetInputCapture(mf.onInputCapture)
|
||||
return mf
|
||||
|
|
@ -60,49 +40,29 @@ func (mf *MainFlex) init() {
|
|||
}
|
||||
|
||||
func (mf *MainFlex) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch mf.mode {
|
||||
case ModeNormal:
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Normal.InsertMode:
|
||||
mf.mode = ModeInsert
|
||||
app.SetFocus(mf.messageInput)
|
||||
return nil
|
||||
|
||||
case cfg.Keys.Normal.FocusGuildsTree:
|
||||
app.SetFocus(mf.guildsTree)
|
||||
return nil
|
||||
case cfg.Keys.Normal.FocusMessagesText:
|
||||
app.SetFocus(mf.messagesText)
|
||||
return nil
|
||||
case cfg.Keys.Normal.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)
|
||||
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.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)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
mf.init()
|
||||
app.SetFocus(mf.guildsTree)
|
||||
}
|
||||
|
||||
// do not propagate event to the children if the message input is focused in normal mode.
|
||||
if mf.messageInput.HasFocus() {
|
||||
return nil
|
||||
}
|
||||
case ModeInsert:
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Insert.NormalMode:
|
||||
mf.mode = ModeNormal
|
||||
return nil
|
||||
}
|
||||
|
||||
if !mf.messageInput.HasFocus() {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
|
|
|
|||
|
|
@ -49,97 +49,106 @@ func newMessageInput() *MessageInput {
|
|||
}
|
||||
|
||||
func (mi *MessageInput) reset() {
|
||||
mi.replyMessageIdx = -1
|
||||
mi.SetTitle("")
|
||||
mi.SetText("", true)
|
||||
}
|
||||
|
||||
func (mi *MessageInput) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch mainFlex.mode {
|
||||
case ModeInsert:
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Insert.MessageInput.Send:
|
||||
if !mainFlex.guildsTree.selectedChannelID.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(mi.GetText())
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if mi.replyMessageIdx != -1 {
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
data := api.SendMessageData{
|
||||
Content: text,
|
||||
Reference: &discord.MessageReference{MessageID: ms[mi.replyMessageIdx].ID},
|
||||
AllowedMentions: &api.AllowedMentions{RepliedUser: option.False},
|
||||
}
|
||||
|
||||
if strings.HasPrefix(mi.GetTitle(), "[@]") {
|
||||
data.AllowedMentions.RepliedUser = option.True
|
||||
}
|
||||
|
||||
go func() {
|
||||
if _, err := discordState.SendMessageComplex(mainFlex.guildsTree.selectedChannelID, data); err != nil {
|
||||
log.Println("failed to send message:", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
if _, err := discordState.SendMessage(mainFlex.guildsTree.selectedChannelID, text); err != nil {
|
||||
log.Println("failed to send message:", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
mi.replyMessageIdx = -1
|
||||
mainFlex.messagesText.Highlight()
|
||||
mi.reset()
|
||||
return nil
|
||||
case cfg.Keys.Insert.MessageInput.Editor:
|
||||
e := cfg.Editor
|
||||
if e == "default" {
|
||||
e = os.Getenv("EDITOR")
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("", constants.TmpFilePattern)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
_, _ = f.WriteString(mi.GetText())
|
||||
f.Close()
|
||||
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
cmd := exec.Command(e, f.Name())
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
app.Suspend(func() {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
msg, err := os.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
mi.SetText(strings.TrimSpace(string(msg)), true)
|
||||
return nil
|
||||
}
|
||||
switch event.Name() {
|
||||
case cfg.Keys.MessageInput.Send:
|
||||
mi.send()
|
||||
return nil
|
||||
case cfg.Keys.MessageInput.Editor:
|
||||
mi.editor()
|
||||
return nil
|
||||
case cfg.Keys.MessageInput.Cancel:
|
||||
mi.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (mi *MessageInput) send() {
|
||||
if !mainFlex.guildsTree.selectedChannelID.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(mi.GetText())
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if mi.replyMessageIdx != -1 {
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
data := api.SendMessageData{
|
||||
Content: text,
|
||||
Reference: &discord.MessageReference{MessageID: ms[mi.replyMessageIdx].ID},
|
||||
AllowedMentions: &api.AllowedMentions{RepliedUser: option.False},
|
||||
}
|
||||
|
||||
if strings.HasPrefix(mi.GetTitle(), "[@]") {
|
||||
data.AllowedMentions.RepliedUser = option.True
|
||||
}
|
||||
|
||||
go func() {
|
||||
if _, err := discordState.SendMessageComplex(mainFlex.guildsTree.selectedChannelID, data); err != nil {
|
||||
log.Println("failed to send message:", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
if _, err := discordState.SendMessage(mainFlex.guildsTree.selectedChannelID, text); err != nil {
|
||||
log.Println("failed to send message:", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
mi.replyMessageIdx = -1
|
||||
mainFlex.messagesText.Highlight()
|
||||
mi.reset()
|
||||
}
|
||||
|
||||
func (mi *MessageInput) editor() {
|
||||
e := cfg.Editor
|
||||
if e == "default" {
|
||||
e = os.Getenv("EDITOR")
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("", constants.TmpFilePattern)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
_, _ = f.WriteString(mi.GetText())
|
||||
f.Close()
|
||||
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
cmd := exec.Command(e, f.Name())
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
app.Suspend(func() {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
msg, err := os.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
mi.SetText(strings.TrimSpace(string(msg)), true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
|
@ -149,179 +150,192 @@ func (mt *MessagesText) createFooter(w io.Writer, m discord.Message) {
|
|||
}
|
||||
}
|
||||
|
||||
func (mt *MessagesText) getSelectedMessage() (*discord.Message, error) {
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil, errors.New("no message is currently selected")
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ms[mt.selectedMessage], nil
|
||||
}
|
||||
|
||||
func (mt *MessagesText) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch mainFlex.mode {
|
||||
case ModeNormal:
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Normal.MessagesText.Yank:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = clipboard.WriteAll(ms[mt.selectedMessage].Content)
|
||||
if err != nil {
|
||||
log.Println("failed to write to clipboard:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case cfg.Keys.Normal.MessagesText.SelectFirst, cfg.Keys.Normal.MessagesText.SelectLast, cfg.Keys.Normal.MessagesText.SelectPrevious, cfg.Keys.Normal.MessagesText.SelectNext, cfg.Keys.Normal.MessagesText.SelectReply:
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Normal.MessagesText.SelectPrevious:
|
||||
// If no message is currently selected, select the latest message.
|
||||
if len(mt.GetHighlights()) == 0 {
|
||||
mt.selectedMessage = 0
|
||||
} else {
|
||||
if mt.selectedMessage < len(ms)-1 {
|
||||
mt.selectedMessage++
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case cfg.Keys.Normal.MessagesText.SelectNext:
|
||||
// If no message is currently selected, select the latest message.
|
||||
if len(mt.GetHighlights()) == 0 {
|
||||
mt.selectedMessage = 0
|
||||
} else {
|
||||
if mt.selectedMessage > 0 {
|
||||
mt.selectedMessage--
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case cfg.Keys.Normal.MessagesText.SelectFirst:
|
||||
mt.selectedMessage = len(ms) - 1
|
||||
case cfg.Keys.Normal.MessagesText.SelectLast:
|
||||
mt.selectedMessage = 0
|
||||
case cfg.Keys.Normal.MessagesText.SelectReply:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ref := ms[mt.selectedMessage].ReferencedMessage; ref != nil {
|
||||
for i, m := range ms {
|
||||
if ref.ID == m.ID {
|
||||
mt.selectedMessage = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mt.Highlight(ms[mt.selectedMessage].ID.String())
|
||||
mt.ScrollToHighlight()
|
||||
return nil
|
||||
case cfg.Keys.Normal.MessagesText.Open:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
attachments := ms[mt.selectedMessage].Attachments
|
||||
if len(attachments) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
go func() {
|
||||
if err := open.Start(a.URL); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
case cfg.Keys.Normal.MessagesText.Reply, cfg.Keys.Normal.MessagesText.ReplyMention:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var title string
|
||||
if event.Name() == cfg.Keys.Normal.MessagesText.ReplyMention {
|
||||
title += "[@] Replying to "
|
||||
} else {
|
||||
title += "Replying to "
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
title += ms[mt.selectedMessage].Author.Tag()
|
||||
mainFlex.messageInput.SetTitle(title)
|
||||
mainFlex.messageInput.replyMessageIdx = mt.selectedMessage
|
||||
|
||||
app.SetFocus(mainFlex.messageInput)
|
||||
return nil
|
||||
case cfg.Keys.Normal.MessagesText.Delete:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
m := ms[mt.selectedMessage]
|
||||
clientID := discordState.Ready().User.ID
|
||||
|
||||
ps, err := discordState.Permissions(mainFlex.guildsTree.selectedChannelID, discordState.Ready().User.ID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Author.ID != clientID && !ps.Has(discord.PermissionManageMessages) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := discordState.DeleteMessage(mainFlex.guildsTree.selectedChannelID, m.ID, ""); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if err := discordState.MessageRemove(mainFlex.guildsTree.selectedChannelID, m.ID); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
ms, err = discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
mt.Clear()
|
||||
|
||||
for i := len(ms) - 1; i >= 0; i-- {
|
||||
mainFlex.messagesText.createMessage(ms[i])
|
||||
}
|
||||
|
||||
switch event.Name() {
|
||||
case cfg.Keys.SelectPrevious, cfg.Keys.SelectNext, cfg.Keys.SelectFirst, cfg.Keys.SelectLast, cfg.Keys.MessagesText.SelectReply:
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// do not propagate event to the children in normal mode.
|
||||
switch event.Name() {
|
||||
case cfg.Keys.SelectPrevious:
|
||||
// If no message is currently selected, select the latest message.
|
||||
if len(mt.GetHighlights()) == 0 {
|
||||
mt.selectedMessage = 0
|
||||
} else {
|
||||
if mt.selectedMessage < len(ms)-1 {
|
||||
mt.selectedMessage++
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case cfg.Keys.SelectNext:
|
||||
// If no message is currently selected, select the latest message.
|
||||
if len(mt.GetHighlights()) == 0 {
|
||||
mt.selectedMessage = 0
|
||||
} else {
|
||||
if mt.selectedMessage > 0 {
|
||||
mt.selectedMessage--
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case cfg.Keys.SelectFirst:
|
||||
mt.selectedMessage = len(ms) - 1
|
||||
case cfg.Keys.SelectLast:
|
||||
mt.selectedMessage = 0
|
||||
case cfg.Keys.MessagesText.SelectReply:
|
||||
if mt.selectedMessage == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ref := ms[mt.selectedMessage].ReferencedMessage; ref != nil {
|
||||
for i, m := range ms {
|
||||
if ref.ID == m.ID {
|
||||
mt.selectedMessage = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mt.Highlight(ms[mt.selectedMessage].ID.String())
|
||||
mt.ScrollToHighlight()
|
||||
return nil
|
||||
|
||||
case cfg.Keys.MessagesText.Yank:
|
||||
mt.yank()
|
||||
return nil
|
||||
case cfg.Keys.MessagesText.Open:
|
||||
mt.open()
|
||||
return nil
|
||||
case cfg.Keys.MessagesText.Reply:
|
||||
mt.reply(false)
|
||||
return nil
|
||||
case cfg.Keys.MessagesText.ReplyMention:
|
||||
mt.reply(true)
|
||||
return nil
|
||||
case cfg.Keys.MessagesText.Delete:
|
||||
mt.delete()
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (mt *MessagesText) yank() {
|
||||
msg, err := mt.getSelectedMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = clipboard.WriteAll(msg.Content)
|
||||
if err != nil {
|
||||
log.Println("failed to write to clipboard:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (mt *MessagesText) open() {
|
||||
msg, err := mt.getSelectedMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
attachments := msg.Attachments
|
||||
if len(attachments) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
go func() {
|
||||
if err := open.Start(a.URL); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (mt *MessagesText) reply(mention bool) {
|
||||
var title string
|
||||
if mention {
|
||||
title += "[@] Replying to "
|
||||
} else {
|
||||
title += "Replying to "
|
||||
}
|
||||
|
||||
msg, err := mt.getSelectedMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
title += msg.Author.Tag()
|
||||
mainFlex.messageInput.SetTitle(title)
|
||||
mainFlex.messageInput.replyMessageIdx = mt.selectedMessage
|
||||
app.SetFocus(mainFlex.messageInput)
|
||||
}
|
||||
|
||||
func (mt *MessagesText) delete() {
|
||||
|
||||
msg, err := mt.getSelectedMessage()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
clientID := discordState.Ready().User.ID
|
||||
if msg.GuildID.IsValid() {
|
||||
ps, err := discordState.Permissions(mainFlex.guildsTree.selectedChannelID, discordState.Ready().User.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if msg.Author.ID != clientID && !ps.Has(discord.PermissionManageMessages) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if msg.Author.ID != clientID {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := discordState.DeleteMessage(mainFlex.guildsTree.selectedChannelID, msg.ID, ""); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := discordState.MessageRemove(mainFlex.guildsTree.selectedChannelID, msg.ID); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
ms, err := discordState.Cabinet.Messages(mainFlex.guildsTree.selectedChannelID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
mt.Clear()
|
||||
|
||||
for i := len(ms) - 1; i >= 0; i-- {
|
||||
mainFlex.messagesText.createMessage(ms[i])
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type Config struct {
|
|||
Theme Theme `toml:"theme"`
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Mouse: true,
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ func Load() (*Config, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cfg := defaultConfig()
|
||||
cfg := DefaultConfig()
|
||||
path = filepath.Join(path, constants.Name, "config.toml")
|
||||
f, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
|
|
|
|||
|
|
@ -2,35 +2,27 @@ package config
|
|||
|
||||
type (
|
||||
Keys struct {
|
||||
Normal NormalModeKeys `toml:"normal"`
|
||||
Insert InsertModeKeys `toml:"insert"`
|
||||
}
|
||||
|
||||
NormalModeKeys struct {
|
||||
InsertMode string `toml:"insert_mode"`
|
||||
FocusGuildsTree string `toml:"focus_guilds_tree"`
|
||||
FocusMessagesText string `toml:"focus_messages_text"`
|
||||
FocusMessageInput string `toml:"focus_message_input"`
|
||||
ToggleGuildsTree string `toml:"toggle_guild_tree"`
|
||||
|
||||
GuildsTree GuildsTreeNormalModeKeys `toml:"guilds_tree"`
|
||||
MessagesText MessagesTextNormalModeKeys `toml:"messages_text"`
|
||||
}
|
||||
|
||||
GuildsTreeNormalModeKeys struct {
|
||||
SelectCurrent string `toml:"select_current"`
|
||||
SelectPrevious string `toml:"select_previous"`
|
||||
SelectNext string `toml:"select_next"`
|
||||
SelectFirst string `toml:"select_first"`
|
||||
SelectLast string `toml:"select_last"`
|
||||
|
||||
GuildsTree GuildsTreeKeys `toml:"guilds_tree"`
|
||||
MessagesText MessagesTextKeys `toml:"messages_text"`
|
||||
MessageInput MessageInputKeys `toml:"message_input"`
|
||||
}
|
||||
|
||||
MessagesTextNormalModeKeys struct {
|
||||
SelectPrevious string `toml:"select_previous"`
|
||||
SelectNext string `toml:"select_next"`
|
||||
SelectFirst string `toml:"select_first"`
|
||||
SelectLast string `toml:"select_last"`
|
||||
SelectReply string `toml:"select_reply"`
|
||||
GuildsTreeKeys struct {
|
||||
SelectCurrent string `toml:"select_current"`
|
||||
}
|
||||
|
||||
MessagesTextKeys struct {
|
||||
SelectReply string `toml:"select_reply"`
|
||||
Reply string `toml:"reply"`
|
||||
ReplyMention string `toml:"reply_mention"`
|
||||
|
||||
|
|
@ -39,55 +31,44 @@ type (
|
|||
Open string `toml:"open"`
|
||||
}
|
||||
|
||||
InsertModeKeys struct {
|
||||
NormalMode string `toml:"normal_mode"`
|
||||
|
||||
MessageInput MessageInputInsertModeKeys `toml:"message_input"`
|
||||
}
|
||||
|
||||
MessageInputInsertModeKeys struct {
|
||||
MessageInputKeys struct {
|
||||
Send string `toml:"send"`
|
||||
Editor string `toml:"editor"`
|
||||
Cancel string `toml:"cancel"`
|
||||
}
|
||||
)
|
||||
|
||||
func defaultKeys() Keys {
|
||||
return Keys{
|
||||
Normal: NormalModeKeys{
|
||||
InsertMode: "Rune[i]",
|
||||
FocusGuildsTree: "Ctrl+G",
|
||||
FocusMessagesText: "Ctrl+T",
|
||||
FocusMessageInput: "Ctrl+P",
|
||||
ToggleGuildsTree: "Ctrl+B",
|
||||
|
||||
FocusGuildsTree: "Ctrl+G",
|
||||
FocusMessagesText: "Ctrl+T",
|
||||
ToggleGuildsTree: "Ctrl+B",
|
||||
SelectPrevious: "Rune[k]",
|
||||
SelectNext: "Rune[j]",
|
||||
SelectFirst: "Rune[g]",
|
||||
SelectLast: "Rune[G]",
|
||||
|
||||
GuildsTree: GuildsTreeNormalModeKeys{
|
||||
SelectCurrent: "Enter",
|
||||
SelectPrevious: "Rune[k]",
|
||||
SelectNext: "Rune[j]",
|
||||
SelectFirst: "Rune[g]",
|
||||
SelectLast: "Rune[G]",
|
||||
},
|
||||
MessagesText: MessagesTextNormalModeKeys{
|
||||
SelectPrevious: "Rune[k]",
|
||||
SelectNext: "Rune[j]",
|
||||
SelectFirst: "Rune[g]",
|
||||
SelectLast: "Rune[G]",
|
||||
SelectReply: "Rune[s]",
|
||||
|
||||
Reply: "Rune[r]",
|
||||
ReplyMention: "Rune[R]",
|
||||
|
||||
Delete: "Rune[d]",
|
||||
Yank: "Rune[y]",
|
||||
Open: "Rune[o]",
|
||||
},
|
||||
GuildsTree: GuildsTreeKeys{
|
||||
SelectCurrent: "Enter",
|
||||
},
|
||||
Insert: InsertModeKeys{
|
||||
NormalMode: "Esc",
|
||||
MessageInput: MessageInputInsertModeKeys{
|
||||
Send: "Enter",
|
||||
Editor: "Ctrl+E",
|
||||
},
|
||||
|
||||
MessagesText: MessagesTextKeys{
|
||||
SelectReply: "Rune[s]",
|
||||
|
||||
Reply: "Rune[r]",
|
||||
ReplyMention: "Rune[R]",
|
||||
|
||||
Delete: "Rune[d]",
|
||||
Yank: "Rune[y]",
|
||||
Open: "Rune[o]",
|
||||
},
|
||||
|
||||
MessageInput: MessageInputKeys{
|
||||
Send: "Enter",
|
||||
Editor: "Ctrl+E",
|
||||
Cancel: "Esc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue