737 lines
23 KiB
Go
737 lines
23 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.rocketnine.space/tslocum/cbind"
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/normen/whatscli/config"
|
|
"github.com/normen/whatscli/messages"
|
|
"github.com/rivo/tview"
|
|
"github.com/skratchdot/open-golang/open"
|
|
"github.com/zyedidia/clipboard"
|
|
)
|
|
|
|
var VERSION string = "v1.0.11"
|
|
|
|
var sndTxt string = ""
|
|
var currentReceiver messages.Chat = messages.Chat{}
|
|
var curRegions []messages.Message
|
|
|
|
var textView *tview.TextView
|
|
var treeView *tview.TreeView
|
|
var textInput *tview.InputField
|
|
var topBar *tview.TextView
|
|
var infoBar *tview.TextView
|
|
|
|
var chatRoot *tview.TreeNode
|
|
var app *tview.Application
|
|
|
|
var sessionManager *messages.SessionManager
|
|
|
|
var keyBindings *cbind.Configuration
|
|
|
|
var uiHandler messages.UiMessageHandler
|
|
|
|
func main() {
|
|
config.InitConfig()
|
|
uiHandler = UiHandler{}
|
|
sessionManager = &messages.SessionManager{}
|
|
sessionManager.Init(uiHandler)
|
|
|
|
app = tview.NewApplication()
|
|
|
|
sideBarWidth := config.Config.Ui.ChatSidebarWidth
|
|
gridLayout := tview.NewGrid()
|
|
gridLayout.SetRows(1, 0, 1)
|
|
gridLayout.SetColumns(sideBarWidth, 0, sideBarWidth)
|
|
gridLayout.SetBorders(true)
|
|
gridLayout.SetBackgroundColor(tcell.ColorNames[config.Config.Colors.Background])
|
|
gridLayout.SetBordersColor(tcell.ColorNames[config.Config.Colors.Borders])
|
|
|
|
cmdPrefix := config.Config.General.CmdPrefix
|
|
topBar = tview.NewTextView()
|
|
topBar.SetDynamicColors(true)
|
|
topBar.SetScrollable(false)
|
|
topBar.SetText("[::b] WhatsCLI " + VERSION + " [-::d]Type " + cmdPrefix + "help or press " + config.Config.Keymap.CommandHelp + " for help")
|
|
topBar.SetBackgroundColor(tcell.ColorNames[config.Config.Colors.Background])
|
|
|
|
infoBar = tview.NewTextView()
|
|
infoBar.SetDynamicColors(true)
|
|
UpdateStatusBar(messages.SessionStatus{})
|
|
|
|
textView = tview.NewTextView().
|
|
SetDynamicColors(true).
|
|
SetRegions(true).
|
|
SetWordWrap(true).
|
|
SetChangedFunc(func() {
|
|
app.Draw()
|
|
})
|
|
textView.SetBackgroundColor(tcell.ColorNames[config.Config.Colors.Background])
|
|
textView.SetTextColor(tcell.ColorNames[config.Config.Colors.Text])
|
|
|
|
PrintHelp()
|
|
|
|
textInput = tview.NewInputField()
|
|
textInput.SetBackgroundColor(tcell.ColorNames[config.Config.Colors.Background])
|
|
textInput.SetFieldBackgroundColor(tcell.ColorNames[config.Config.Colors.InputBackground])
|
|
textInput.SetFieldTextColor(tcell.ColorNames[config.Config.Colors.InputText])
|
|
textInput.SetChangedFunc(func(change string) {
|
|
sndTxt = change
|
|
})
|
|
textInput.SetDoneFunc(EnterCommand)
|
|
textInput.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
if event.Key() == tcell.KeyDown {
|
|
offset, _ := textView.GetScrollOffset()
|
|
offset += 1
|
|
textView.ScrollTo(offset, 0)
|
|
return nil
|
|
}
|
|
if event.Key() == tcell.KeyUp {
|
|
offset, _ := textView.GetScrollOffset()
|
|
offset -= 1
|
|
textView.ScrollTo(offset, 0)
|
|
return nil
|
|
}
|
|
if event.Key() == tcell.KeyPgDn {
|
|
offset, _ := textView.GetScrollOffset()
|
|
offset += 10
|
|
textView.ScrollTo(offset, 0)
|
|
return nil
|
|
}
|
|
if event.Key() == tcell.KeyPgUp {
|
|
offset, _ := textView.GetScrollOffset()
|
|
offset -= 10
|
|
textView.ScrollTo(offset, 0)
|
|
return nil
|
|
}
|
|
return event
|
|
})
|
|
|
|
gridLayout.AddItem(topBar, 0, 0, 1, 4, 0, 0, false)
|
|
gridLayout.AddItem(infoBar, 2, 0, 1, 1, 0, 0, false)
|
|
gridLayout.AddItem(MakeTree(), 1, 0, 1, 1, 0, 0, false)
|
|
gridLayout.AddItem(textView, 1, 1, 1, 3, 0, 0, false)
|
|
gridLayout.AddItem(textInput, 2, 1, 1, 3, 0, 0, false)
|
|
|
|
app.SetRoot(gridLayout, true)
|
|
app.EnableMouse(true)
|
|
app.SetFocus(textInput)
|
|
if err := sessionManager.StartManager(); err != nil {
|
|
PrintError(err)
|
|
}
|
|
LoadShortcuts()
|
|
app.Run()
|
|
}
|
|
|
|
// creates the TreeView for chats
|
|
func MakeTree() *tview.TreeView {
|
|
rootDir := "Chats"
|
|
chatRoot = tview.NewTreeNode(rootDir).
|
|
SetColor(tcell.ColorNames[config.Config.Colors.ListHeader])
|
|
treeView = tview.NewTreeView().
|
|
SetRoot(chatRoot).
|
|
SetCurrentNode(chatRoot)
|
|
treeView.SetBackgroundColor(tcell.ColorNames[config.Config.Colors.Background])
|
|
|
|
// If a chat was selected, open it.
|
|
treeView.SetChangedFunc(func(node *tview.TreeNode) {
|
|
reference := node.GetReference()
|
|
if reference == nil {
|
|
SetDisplayedChat(messages.Chat{"", false, "", 0, 0})
|
|
return // Selecting the root node does nothing.
|
|
}
|
|
children := node.GetChildren()
|
|
if len(children) == 0 {
|
|
// Load and show files in this directory.
|
|
recv := reference.(messages.Chat)
|
|
SetDisplayedChat(recv)
|
|
} else {
|
|
// Collapse if visible, expand if collapsed.
|
|
node.SetExpanded(!node.IsExpanded())
|
|
}
|
|
})
|
|
return treeView
|
|
}
|
|
|
|
func handleFocusMessage(ev *tcell.EventKey) *tcell.EventKey {
|
|
if !textView.HasFocus() {
|
|
app.SetFocus(textView)
|
|
if curRegions != nil && len(curRegions) > 0 {
|
|
textView.Highlight(curRegions[len(curRegions)-1].Id)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handleFocusInput(ev *tcell.EventKey) *tcell.EventKey {
|
|
ResetMsgSelection()
|
|
if !textInput.HasFocus() {
|
|
app.SetFocus(textInput)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handleFocusContacts(ev *tcell.EventKey) *tcell.EventKey {
|
|
ResetMsgSelection()
|
|
if !treeView.HasFocus() {
|
|
app.SetFocus(treeView)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handleSwitchPanels(ev *tcell.EventKey) *tcell.EventKey {
|
|
ResetMsgSelection()
|
|
if !textInput.HasFocus() {
|
|
app.SetFocus(textInput)
|
|
} else {
|
|
app.SetFocus(treeView)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handleCommand(command string) func(ev *tcell.EventKey) *tcell.EventKey {
|
|
return func(ev *tcell.EventKey) *tcell.EventKey {
|
|
sessionManager.CommandChannel <- messages.Command{command, nil}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func handleCopyUser(ev *tcell.EventKey) *tcell.EventKey {
|
|
if hls := textView.GetHighlights(); len(hls) > 0 {
|
|
for _, val := range curRegions {
|
|
if val.Id == hls[0] {
|
|
clipboard.WriteAll(val.ContactId, "clipboard")
|
|
PrintText("copied id of " + val.ContactName + " to clipboard")
|
|
}
|
|
}
|
|
ResetMsgSelection()
|
|
} else if currentReceiver.Id != "" {
|
|
clipboard.WriteAll(currentReceiver.Id, "clipboard")
|
|
PrintText("copied id of " + currentReceiver.Name + " to clipboard")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handlePasteUser(ev *tcell.EventKey) *tcell.EventKey {
|
|
if clip, err := clipboard.ReadAll("clipboard"); err == nil {
|
|
textInput.SetText(textInput.GetText() + " " + clip)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func handleQuit(ev *tcell.EventKey) *tcell.EventKey {
|
|
sessionManager.CommandChannel <- messages.Command{"disconnect", nil}
|
|
app.Stop()
|
|
return nil
|
|
}
|
|
|
|
func handleHelp(ev *tcell.EventKey) *tcell.EventKey {
|
|
PrintHelp()
|
|
return nil
|
|
}
|
|
|
|
func handleMessageCommand(command string) func(ev *tcell.EventKey) *tcell.EventKey {
|
|
return func(ev *tcell.EventKey) *tcell.EventKey {
|
|
hls := textView.GetHighlights()
|
|
if len(hls) > 0 {
|
|
sessionManager.CommandChannel <- messages.Command{command, []string{hls[0]}}
|
|
ResetMsgSelection()
|
|
app.SetFocus(textInput)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func handleMessagesMove(amount int) func(ev *tcell.EventKey) *tcell.EventKey {
|
|
return func(ev *tcell.EventKey) *tcell.EventKey {
|
|
if curRegions == nil || len(curRegions) == 0 {
|
|
return nil
|
|
}
|
|
hls := textView.GetHighlights()
|
|
if len(hls) > 0 {
|
|
newId := GetOffsetMsgId(hls[0], amount)
|
|
if newId != "" {
|
|
textView.Highlight(newId)
|
|
}
|
|
} else {
|
|
if amount < 0 {
|
|
textView.Highlight(curRegions[0].Id)
|
|
} else {
|
|
textView.Highlight(curRegions[len(curRegions)-1].Id)
|
|
}
|
|
}
|
|
textView.ScrollToHighlight()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func handleChatPanelUp(ev *tcell.EventKey) *tcell.EventKey {
|
|
//TODO: scroll selection in treeView? or chatRoot? How?
|
|
return ev
|
|
}
|
|
|
|
func handleChatPanelDown(ev *tcell.EventKey) *tcell.EventKey {
|
|
return ev
|
|
}
|
|
|
|
func handleMessagesLast(ev *tcell.EventKey) *tcell.EventKey {
|
|
if curRegions == nil || len(curRegions) == 0 {
|
|
return nil
|
|
}
|
|
textView.Highlight(curRegions[len(curRegions)-1].Id)
|
|
textView.ScrollToHighlight()
|
|
return nil
|
|
}
|
|
|
|
func handleMessagesFirst(ev *tcell.EventKey) *tcell.EventKey {
|
|
if curRegions == nil || len(curRegions) == 0 {
|
|
return nil
|
|
}
|
|
textView.Highlight(curRegions[0].Id)
|
|
textView.ScrollToHighlight()
|
|
return nil
|
|
}
|
|
|
|
func handleExitMessages(ev *tcell.EventKey) *tcell.EventKey {
|
|
if curRegions == nil || len(curRegions) == 0 {
|
|
return nil
|
|
}
|
|
ResetMsgSelection()
|
|
app.SetFocus(textInput)
|
|
return nil
|
|
}
|
|
|
|
// load the key map
|
|
func LoadShortcuts() {
|
|
// global bindings for app
|
|
keyBindings = cbind.NewConfiguration()
|
|
if err := keyBindings.Set(config.Config.Keymap.FocusMessages, handleFocusMessage); err != nil {
|
|
PrintErrorMsg("focus_messages:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.FocusInput, handleFocusInput); err != nil {
|
|
PrintErrorMsg("focus_input:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.FocusChats, handleFocusContacts); err != nil {
|
|
PrintErrorMsg("focus_contacts:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.SwitchPanels, handleSwitchPanels); err != nil {
|
|
PrintErrorMsg("switch_panels:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.CommandRead, handleCommand("read")); err != nil {
|
|
PrintErrorMsg("command_read:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.Copyuser, handleCopyUser); err != nil {
|
|
PrintErrorMsg("copyuser:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.Pasteuser, handlePasteUser); err != nil {
|
|
PrintErrorMsg("pasteuser:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.CommandBacklog, handleCommand("backlog")); err != nil {
|
|
PrintErrorMsg("command_backlog:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.CommandConnect, handleCommand("login")); err != nil {
|
|
PrintErrorMsg("command_connect:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.CommandQuit, handleQuit); err != nil {
|
|
PrintErrorMsg("command_quit:", err)
|
|
}
|
|
if err := keyBindings.Set(config.Config.Keymap.CommandHelp, handleHelp); err != nil {
|
|
PrintErrorMsg("command_help:", err)
|
|
}
|
|
app.SetInputCapture(keyBindings.Capture)
|
|
// bindings for chat message text view
|
|
keysMessages := cbind.NewConfiguration()
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageDownload, handleMessageCommand("download")); err != nil {
|
|
PrintErrorMsg("message_download:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageOpen, handleMessageCommand("open")); err != nil {
|
|
PrintErrorMsg("message_open:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.Copyuser, handleCopyUser); err != nil {
|
|
PrintErrorMsg("copyuser:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.Pasteuser, handlePasteUser); err != nil {
|
|
PrintErrorMsg("pasteuser:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageShow, handleMessageCommand("show")); err != nil {
|
|
PrintErrorMsg("message_show:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageUrl, handleMessageCommand("url")); err != nil {
|
|
PrintErrorMsg("message_url:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageInfo, handleMessageCommand("info")); err != nil {
|
|
PrintErrorMsg("message_info:", err)
|
|
}
|
|
if err := keysMessages.Set(config.Config.Keymap.MessageRevoke, handleMessageCommand("revoke")); err != nil {
|
|
PrintErrorMsg("message_revoke:", err)
|
|
}
|
|
keysMessages.SetKey(tcell.ModNone, tcell.KeyEscape, handleExitMessages)
|
|
keysMessages.SetKey(tcell.ModNone, tcell.KeyUp, handleMessagesMove(-1))
|
|
keysMessages.SetKey(tcell.ModNone, tcell.KeyDown, handleMessagesMove(1))
|
|
keysMessages.SetKey(tcell.ModNone, tcell.KeyPgUp, handleMessagesMove(-10))
|
|
keysMessages.SetKey(tcell.ModNone, tcell.KeyPgDn, handleMessagesMove(10))
|
|
keysMessages.SetRune(tcell.ModNone, 'k', handleMessagesMove(-1))
|
|
keysMessages.SetRune(tcell.ModNone, 'j', handleMessagesMove(1))
|
|
keysMessages.SetRune(tcell.ModNone, 'g', handleMessagesFirst)
|
|
keysMessages.SetRune(tcell.ModNone, 'G', handleMessagesLast)
|
|
keysMessages.SetRune(tcell.ModCtrl, 'u', handleMessagesMove(-10))
|
|
keysMessages.SetRune(tcell.ModCtrl, 'd', handleMessagesMove(10))
|
|
textView.SetInputCapture(keysMessages.Capture)
|
|
keysChatPanel := cbind.NewConfiguration()
|
|
keysChatPanel.SetRune(tcell.ModCtrl, 'u', handleChatPanelUp)
|
|
keysChatPanel.SetRune(tcell.ModCtrl, 'd', handleChatPanelDown)
|
|
treeView.SetInputCapture(keysChatPanel.Capture)
|
|
}
|
|
|
|
// prints help to chat view
|
|
func PrintHelp() {
|
|
cmdPrefix := config.Config.General.CmdPrefix
|
|
fmt.Fprintln(textView, "[-::u]Keys:[-::-]")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "Global")
|
|
fmt.Fprintln(textView, "[::b] Up/Down[::-] = Scroll history/chats")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.SwitchPanels, "[::-] = Switch input/chats")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.FocusMessages, "[::-] = Focus message panel")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.CommandQuit, "[::-] = Exit app")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "[-::-]Message panel[-::-]")
|
|
fmt.Fprintln(textView, "[::b] Up/Down[::-] = select message")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageDownload, "[::-] = Download attachment")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageOpen, "[::-] = Download & open attachment")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageShow, "[::-] = Download & show image using", config.Config.General.ShowCommand)
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageUrl, "[::-] = Find URL in message and open it")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageRevoke, "[::-] = Revoke message")
|
|
fmt.Fprintln(textView, "[::b]", config.Config.Keymap.MessageInfo, "[::-] = Info about message")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "Config file in ->", config.GetConfigFilePath())
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "Type [::b]"+cmdPrefix+"commands[::-] to see all commands")
|
|
fmt.Fprintln(textView, "")
|
|
}
|
|
|
|
func PrintCommands() {
|
|
cmdPrefix := config.Config.General.CmdPrefix
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "[-::u]Commands:[-::-]")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "[-::-]Global[-::-]")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"connect [::-]or[::b]", config.Config.Keymap.CommandConnect, "[::-] = (Re)Connect to server")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"disconnect[::-] = Close the connection")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"logout[::-] = Remove login data from computer")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"quit [::-]or[::b]", config.Config.Keymap.CommandQuit, "[::-] = Exit app")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "[-::-]Chat[-::-]")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"backlog [::-]or[::b]", config.Config.Keymap.CommandBacklog, "[::-] = load next", config.Config.General.BacklogMsgQuantity, "previous messages")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"read [::-]or[::b]", config.Config.Keymap.CommandRead, "[::-] = mark new messages in chat as read")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"upload[::-] /path/to/file = Upload any file as document")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"sendimage[::-] /path/to/file = Send image message")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"sendvideo[::-] /path/to/file = Send video message")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"sendaudio[::-] /path/to/file = Send audio message")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "[-::-]Groups[-::-]")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"leave[::-] = Leave group")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"create[::-] [user-id[] [user-id[] Group Subject = Create group with users")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"subject[::-] New Subject = Change subject of group")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"add[::-] [user-id[] = Add user to group")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"remove[::-] [user-id[] = Remove user from group")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"admin[::-] [user-id[] = Set admin role for user in group")
|
|
fmt.Fprintln(textView, "[::b] "+cmdPrefix+"removeadmin[::-] [user-id[] = Remove admin role for user in group")
|
|
fmt.Fprintln(textView, "")
|
|
fmt.Fprintln(textView, "Use[::b]", config.Config.Keymap.Copyuser, "[::-]to copy a selected user id to clipboard")
|
|
fmt.Fprintln(textView, "Use[::b]", config.Config.Keymap.Pasteuser, "[::-]to paste clipboard to text input")
|
|
fmt.Fprintln(textView, "")
|
|
}
|
|
|
|
// called when text is entered by the user
|
|
func EnterCommand(key tcell.Key) {
|
|
if sndTxt == "" {
|
|
return
|
|
}
|
|
if key == tcell.KeyEsc {
|
|
textInput.SetText("")
|
|
return
|
|
}
|
|
cmdPrefix := config.Config.General.CmdPrefix
|
|
if sndTxt == cmdPrefix+"help" {
|
|
PrintHelp()
|
|
textInput.SetText("")
|
|
return
|
|
}
|
|
if sndTxt == cmdPrefix+"commands" {
|
|
PrintCommands()
|
|
textInput.SetText("")
|
|
return
|
|
}
|
|
if sndTxt == cmdPrefix+"quit" {
|
|
sessionManager.CommandChannel <- messages.Command{"disconnect", nil}
|
|
app.Stop()
|
|
return
|
|
}
|
|
if strings.HasPrefix(sndTxt, cmdPrefix) {
|
|
cmd := strings.TrimPrefix(sndTxt, cmdPrefix)
|
|
var params []string
|
|
if strings.Index(cmd, " ") >= 0 {
|
|
cmdParts := strings.Split(cmd, " ")
|
|
cmd = cmdParts[0]
|
|
params = cmdParts[1:]
|
|
}
|
|
sessionManager.CommandChannel <- messages.Command{cmd, params}
|
|
textInput.SetText("")
|
|
return
|
|
}
|
|
if currentReceiver.Id == "" {
|
|
PrintText("no receiver")
|
|
textInput.SetText("")
|
|
return
|
|
}
|
|
// no command, send as message
|
|
msg := messages.Command{
|
|
Name: "send",
|
|
Params: []string{currentReceiver.Id, sndTxt},
|
|
}
|
|
sessionManager.CommandChannel <- msg
|
|
textInput.SetText("")
|
|
}
|
|
|
|
// get the next message id to select (highlighted + offset)
|
|
func GetOffsetMsgId(curId string, offset int) string {
|
|
if curRegions == nil || len(curRegions) == 0 {
|
|
return ""
|
|
}
|
|
for idx, val := range curRegions {
|
|
if val.Id == curId {
|
|
arrPos := idx + offset
|
|
if len(curRegions) > arrPos && arrPos >= 0 {
|
|
return curRegions[arrPos].Id
|
|
}
|
|
}
|
|
}
|
|
if offset > 0 {
|
|
return curRegions[0].Id
|
|
} else {
|
|
return curRegions[len(curRegions)-1].Id
|
|
}
|
|
}
|
|
|
|
// resets the selection in the textView and scrolls it down
|
|
func ResetMsgSelection() {
|
|
if len(textView.GetHighlights()) > 0 {
|
|
textView.Highlight("")
|
|
}
|
|
textView.ScrollToEnd()
|
|
}
|
|
|
|
// prints text to the TextView
|
|
func PrintText(txt string) {
|
|
fmt.Fprintln(textView, txt)
|
|
}
|
|
|
|
// prints an error to the TextView
|
|
func PrintError(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
fmt.Fprintln(textView, "["+config.Config.Colors.Negative+"]", err.Error(), "[-]")
|
|
}
|
|
|
|
// prints an error to the TextView
|
|
func PrintErrorMsg(text string, err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
fmt.Fprintln(textView, "["+config.Config.Colors.Negative+"]", text, err.Error(), "[-]")
|
|
}
|
|
|
|
// prints an image attachment to the TextView (by message id)
|
|
func PrintImage(path string) {
|
|
var err error
|
|
cmdParts := strings.Split(config.Config.General.ShowCommand, " ")
|
|
cmdParts = append(cmdParts, path)
|
|
var cmd *exec.Cmd
|
|
size := len(cmdParts)
|
|
if size > 1 {
|
|
cmd = exec.Command(cmdParts[0], cmdParts[1:]...)
|
|
} else if size > 0 {
|
|
cmd = exec.Command(cmdParts[0])
|
|
}
|
|
var stdout io.ReadCloser
|
|
if stdout, err = cmd.StdoutPipe(); err == nil {
|
|
if err = cmd.Start(); err == nil {
|
|
reader := bufio.NewReader(stdout)
|
|
io.Copy(tview.ANSIWriter(textView), reader)
|
|
return
|
|
}
|
|
}
|
|
PrintError(err)
|
|
}
|
|
|
|
// updates the status bar
|
|
func UpdateStatusBar(statusInfo messages.SessionStatus) {
|
|
out := " "
|
|
if statusInfo.Connected {
|
|
out += "[" + config.Config.Colors.Positive + "]online[-]"
|
|
} else {
|
|
out += "[" + config.Config.Colors.Negative + "]offline[-]"
|
|
}
|
|
out += " "
|
|
out += "[::d] ("
|
|
out += fmt.Sprint(statusInfo.BatteryCharge)
|
|
out += "%"
|
|
if statusInfo.BatteryLoading {
|
|
out += " [" + config.Config.Colors.Positive + "]L[-]"
|
|
} else {
|
|
out += " [" + config.Config.Colors.Negative + "]l[-]"
|
|
}
|
|
if statusInfo.BatteryPowersave {
|
|
out += " [" + config.Config.Colors.Negative + "]S[-]"
|
|
} else {
|
|
out += " [" + config.Config.Colors.Positive + "]s[-]"
|
|
}
|
|
out += ")[::-] "
|
|
out += statusInfo.LastSeen
|
|
infoBar.SetText(out)
|
|
//infoBar.SetText("🔋: ??%")
|
|
}
|
|
|
|
// sets the current chat, loads text from storage to TextView
|
|
func SetDisplayedChat(wid messages.Chat) {
|
|
//TODO: how to get chat to set
|
|
currentReceiver = wid
|
|
textView.Clear()
|
|
textView.SetTitle(wid.Name)
|
|
sessionManager.CommandChannel <- messages.Command{"select", []string{currentReceiver.Id}}
|
|
}
|
|
|
|
// get a string representation of all messages for chat
|
|
func getMessagesString(msgs []messages.Message) string {
|
|
out := ""
|
|
for _, msg := range msgs {
|
|
out += getTextMessageString(&msg)
|
|
out += "\n"
|
|
}
|
|
return out
|
|
}
|
|
|
|
// create a formatted string with regions based on message ID from a text message
|
|
//TODO: optimize, use Sprintf etc
|
|
func getTextMessageString(msg *messages.Message) string {
|
|
colorMe := config.Config.Colors.ChatMe
|
|
colorContact := config.Config.Colors.ChatContact
|
|
out := ""
|
|
text := tview.Escape(msg.Text)
|
|
if msg.Forwarded {
|
|
text = "[" + config.Config.Colors.ForwardedText + "]" + text + "[-]"
|
|
}
|
|
tim := time.Unix(int64(msg.Timestamp), 0)
|
|
time := tim.Format("02-01-06 15:04:05")
|
|
out += "[\""
|
|
out += msg.Id
|
|
out += "\"]"
|
|
if msg.FromMe { //msg from me
|
|
out += "[-::d](" + time + ") [" + colorMe + "::b]Me: [-::-]" + text
|
|
} else { // message from others
|
|
out += "[-::d](" + time + ") [" + colorContact + "::b]" + msg.ContactShort + ": [-::-]" + text
|
|
}
|
|
out += "[\"\"]"
|
|
return out
|
|
}
|
|
|
|
type UiHandler struct{}
|
|
|
|
func (u UiHandler) NewMessage(msg messages.Message) {
|
|
//TODO: its stupid to "go" this as its supposed to run
|
|
//on the ui thread anyway. But QueueUpdate blocks...?
|
|
go app.QueueUpdateDraw(func() {
|
|
curRegions = append(curRegions, msg)
|
|
PrintText(getTextMessageString(&msg))
|
|
})
|
|
}
|
|
|
|
func (u UiHandler) NewScreen(msgs []messages.Message) {
|
|
go app.QueueUpdateDraw(func() {
|
|
textView.Clear()
|
|
screen := getMessagesString(msgs)
|
|
textView.SetText(screen)
|
|
curRegions = msgs
|
|
if screen == "" {
|
|
if currentReceiver.Id == "" {
|
|
PrintHelp()
|
|
} else {
|
|
PrintText("[::d] ~~~ no messages, press " + config.Config.Keymap.CommandBacklog + " to load backlog if available ~~~[::-]")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// loads the chat data from storage to the TreeView
|
|
func (u UiHandler) SetChats(ids []messages.Chat) {
|
|
go app.QueueUpdateDraw(func() {
|
|
chatRoot.ClearChildren()
|
|
oldId := currentReceiver.Id
|
|
for _, element := range ids {
|
|
name := element.Name
|
|
if name == "" {
|
|
name = strings.TrimSuffix(strings.TrimSuffix(element.Id, messages.GROUPSUFFIX), messages.CONTACTSUFFIX)
|
|
}
|
|
if element.Unread > 0 {
|
|
name += " ([" + config.Config.Colors.UnreadCount + "]" + fmt.Sprint(element.Unread) + "[-])"
|
|
//tim := time.Unix(element.LastMessage, 0)
|
|
//sin := time.Since(tim)
|
|
//since := fmt.Sprintf("%s", sin)
|
|
//time := tim.Format("02-01-06 15:04:05")
|
|
//name += since
|
|
}
|
|
node := tview.NewTreeNode(name).
|
|
SetReference(element).
|
|
SetSelectable(true)
|
|
if element.IsGroup {
|
|
node.SetColor(tcell.ColorNames[config.Config.Colors.ListGroup])
|
|
} else {
|
|
node.SetColor(tcell.ColorNames[config.Config.Colors.ListContact])
|
|
}
|
|
// store new currentReceiver, else the selection on the left goes off
|
|
if element.Id == oldId {
|
|
currentReceiver = element
|
|
}
|
|
chatRoot.AddChild(node)
|
|
if element.Id == currentReceiver.Id {
|
|
treeView.SetCurrentNode(node)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (u UiHandler) PrintError(err error) {
|
|
PrintError(err)
|
|
}
|
|
|
|
func (u UiHandler) PrintText(msg string) {
|
|
PrintText(msg)
|
|
}
|
|
|
|
func (u UiHandler) PrintFile(path string) {
|
|
go app.QueueUpdateDraw(func() {
|
|
PrintImage(path)
|
|
})
|
|
}
|
|
|
|
func (u UiHandler) OpenFile(path string) {
|
|
open.Run(path)
|
|
}
|
|
|
|
func (u UiHandler) SetStatus(status messages.SessionStatus) {
|
|
go app.QueueUpdateDraw(func() {
|
|
UpdateStatusBar(status)
|
|
})
|
|
}
|
|
|
|
func (u UiHandler) GetWriter() io.Writer {
|
|
return textView
|
|
}
|