Skip to content

Commit

Permalink
Implement vi interface (#372)
Browse files Browse the repository at this point in the history
  • Loading branch information
ayn2op committed Mar 26, 2024
1 parent ff7dbd1 commit d465ede
Show file tree
Hide file tree
Showing 18 changed files with 572 additions and 605 deletions.
80 changes: 66 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@ Discordo is a lightweight, secure, and feature-rich Discord terminal client. Hea

![Preview](.github/preview.png)

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Disclaimer](#disclaimer)

## Features

- Lightweight
- Secure
- Configurable
Expand Down Expand Up @@ -41,9 +32,6 @@ You can download and install a [prebuilt binary here](https://nightly.link/ayn2o
git clone https://github.com/ayn2op/discordo
cd discordo
go build .

# optional
sudo mv ./discordo /usr/local/bin
```

### Linux clipboard support
Expand All @@ -55,11 +43,75 @@ sudo mv ./discordo /usr/local/bin

1. Run the `discordo` executable with no arguments.

- If you are logging in using an authentication token, provide the `token` command-line flag to the executable (eg: `--token "OTI2MDU5NTQxNDE2Nzc5ODA2.Yc2KKA.2iZ-5JxgxG-9Ub8GHzBSn-NJjNg"`). The token is stored securely in the default OS-specific keyring.
> If you are logging in using an authentication token, provide the `token` command-line flag to the executable (eg: `--token "OTI2MDU5NTQxNDE2Nzc5ODA2.Yc2KKA.2iZ-5JxgxG-9Ub8GHzBSn-NJjNg"`). The token is stored securely in the default OS-specific keyring.
2. Enter your email and password and click on the "Login" button to continue.

- Most of the Discord third-party clients store the token in a configuration file unencrypted. Discordo securely stores the token in the default OS-specific keyring.
## Configuration

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`

```toml
mouse = true
timestamps = false
timestamps_before_author = false
messages_limit = 50
editor = "default"

[keys.normal]
insert_mode = "Rune[i]"
focus_guilds_tree = "Ctrl+G"
focus_messages_text = "Ctrl+T"
toggle_guild_tree = "Ctrl+B"

guilds_tree = {
select_current = "Enter"
select_previous = "Rune[k]"
select_next = "Rune[j]"
select_first = "Rune[g]"
select_last = "Rune[G]"
}

messages_text = {
select_previous = "Rune[k]"
select_next = "Rune[j]"
select_first = "Rune[g]"
select_last = "Rune[G]"
select_reply = "Rune[s]"
reply = "Rune[r]"
reply_mention = "Rune[R]"
delete = "Rune[d]"
yank = "Rune[y]"
open = "Rune[o]"
}

[keys.insert]
normal_mode = "Esc"

message_input = {
send = "Enter"
editor = "Ctrl+E"
}

[theme]
border = true
border_color = "default"
border_padding = [0, 0, 1, 1]
title_color = "default"
background_color = "default"

[theme.guilds_tree]
auto_expand_folders = true
graphics = true

[theme.messages_text]
author_color = "aqua"
reply_indicator = ""
```

## Documentation

Expand Down
57 changes: 0 additions & 57 deletions cmd/attachment_image.go

This file was deleted.

26 changes: 25 additions & 1 deletion cmd/guilds_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func newGuildsTree() *GuildsTree {
gt.SetBorderColor(tcell.GetColor(cfg.Theme.BorderColor))
gt.SetBorderPadding(p[0], p[1], p[2], p[3])

gt.SetInputCapture(gt.onInputCapture)
return gt
}

Expand All @@ -59,7 +60,7 @@ func (gt *GuildsTree) createGuildFolderNode(parent *tview.TreeNode, gf gateway.G
for _, gid := range gf.GuildIDs {
g, err := discordState.Cabinet.Guild(gid)
if err != nil {
log.Println(err)
log.Printf("guild %v not found in state: %v", gid, err)
continue
}

Expand Down Expand Up @@ -216,3 +217,26 @@ 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)
}

// do not propagate event to the children in normal mode.
return nil
}

return event
}
82 changes: 59 additions & 23 deletions cmd/main_flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ 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
Expand All @@ -17,14 +25,25 @@ 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
}

Expand All @@ -41,32 +60,49 @@ func (mf *MainFlex) init() {
}

func (mf *MainFlex) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
switch event.Name() {
case cfg.Keys.GuildsTree.Toggle:
// 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()
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)
}

return nil
}

return nil
case cfg.Keys.GuildsTree.Focus:
if mf.GetItemCount() == 2 {
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
case cfg.Keys.MessagesText.Focus:
app.SetFocus(mf.messagesText)
return nil
case cfg.Keys.MessageInput.Focus:
app.SetFocus(mf.messageInput)
return nil
}

return event
Expand Down
Loading

0 comments on commit d465ede

Please sign in to comment.