2019-01-08 21:02:11 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
|
"github.com/fatih/color"
|
|
|
|
"github.com/jroimartin/gocui"
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
"github.com/status-im/status-console-client/protocol/client"
|
2019-02-11 09:40:43 +01:00
|
|
|
"github.com/status-im/status-console-client/protocol/v1"
|
2019-01-08 21:02:11 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// ChatViewController manages chat view.
|
|
|
|
type ChatViewController struct {
|
|
|
|
*ViewController
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
notifications *Notifications
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
contact client.Contact
|
|
|
|
|
|
|
|
identity *ecdsa.PrivateKey
|
|
|
|
messenger *client.Messenger
|
2019-01-08 21:02:11 +01:00
|
|
|
|
|
|
|
cancel chan struct{} // cancel the current chat loop
|
|
|
|
done chan struct{} // wait for the current chat loop to finish
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewChatViewController returns a new chat view controller.
|
2019-03-11 18:49:18 +01:00
|
|
|
func NewChatViewController(vc *ViewController, id Identity, m *client.Messenger) (*ChatViewController, error) {
|
2019-01-08 21:02:11 +01:00
|
|
|
return &ChatViewController{
|
|
|
|
ViewController: vc,
|
2019-03-11 18:49:18 +01:00
|
|
|
notifications: &Notifications{writer: vc},
|
2019-01-08 21:02:11 +01:00
|
|
|
identity: id,
|
2019-03-11 18:49:18 +01:00
|
|
|
messenger: m,
|
2019-01-08 21:02:11 +01:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
func (c *ChatViewController) readEventsLoop() {
|
|
|
|
c.done = make(chan struct{})
|
|
|
|
defer close(c.done)
|
2019-01-22 09:39:23 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
for {
|
2019-03-13 21:20:22 +01:00
|
|
|
log.Printf("[ChatViewController::readEventsLoops] waiting for events")
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
select {
|
|
|
|
case event := <-c.messenger.Events():
|
2019-03-13 21:20:22 +01:00
|
|
|
log.Printf("[ChatViewController::readEventsLoops] received an event: %+v", event)
|
2019-03-11 18:49:18 +01:00
|
|
|
|
|
|
|
switch ev := event.(type) {
|
|
|
|
case client.EventError:
|
|
|
|
c.notifications.Error("error", ev.Error().Error()) // nolint: errcheck
|
2019-03-13 21:20:22 +01:00
|
|
|
case client.EventMessage:
|
|
|
|
c.printMessages(false, ev.Message())
|
2019-03-11 18:49:18 +01:00
|
|
|
case client.Event:
|
2019-03-13 21:20:22 +01:00
|
|
|
if ev.Type() != client.EventTypeInit {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
chat := c.messenger.Chat(c.contact)
|
|
|
|
if chat == nil {
|
|
|
|
c.notifications.Error("getting chat", "chat does not exist") // nolint: errcheck
|
2019-03-11 18:49:18 +01:00
|
|
|
break
|
|
|
|
}
|
2019-03-13 21:20:22 +01:00
|
|
|
|
|
|
|
messages := chat.Messages()
|
|
|
|
|
|
|
|
log.Printf("[ChatViewController::readEventsLoops] retrieved %d messages", len(messages))
|
|
|
|
|
|
|
|
c.printMessages(true, messages...)
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
|
|
|
case <-c.cancel:
|
|
|
|
return
|
|
|
|
}
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
// Select informs the chat view controller about a selected contact.
|
|
|
|
// The chat view controller setup subscribers and request recent messages.
|
|
|
|
func (c *ChatViewController) Select(contact client.Contact) error {
|
2019-03-13 21:20:22 +01:00
|
|
|
log.Printf("[ChatViewController::Select] contact %s", contact.Name)
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
if c.cancel == nil {
|
|
|
|
c.cancel = make(chan struct{})
|
|
|
|
go c.readEventsLoop()
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
c.contact = contact
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
return c.messenger.Join(contact)
|
2019-02-25 08:29:35 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
func (c *ChatViewController) RequestMessages(params protocol.RequestOptions) error {
|
|
|
|
c.notifications.Debug( // nolint: errcheck
|
|
|
|
"REQUEST",
|
|
|
|
fmt.Sprintf("get historic messages: %+v", params),
|
|
|
|
)
|
2019-03-13 21:20:22 +01:00
|
|
|
|
|
|
|
chat := c.messenger.Chat(c.contact)
|
|
|
|
if chat == nil {
|
|
|
|
return fmt.Errorf("chat not found")
|
|
|
|
}
|
|
|
|
return chat.Request(params)
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
func (c *ChatViewController) Send(data []byte) error {
|
2019-03-13 21:20:22 +01:00
|
|
|
chat := c.messenger.Chat(c.contact)
|
|
|
|
if chat == nil {
|
|
|
|
return fmt.Errorf("chat not found")
|
|
|
|
}
|
|
|
|
return chat.Send(data)
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-03-13 21:20:22 +01:00
|
|
|
func (c *ChatViewController) printMessages(clear bool, messages ...*protocol.Message) {
|
2019-03-16 20:26:59 +01:00
|
|
|
log.Printf("[ChatViewController::printMessages] printing %d messages", len(messages))
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
c.g.Update(func(*gocui.Gui) error {
|
2019-03-13 21:20:22 +01:00
|
|
|
if clear {
|
|
|
|
if err := c.Clear(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
2019-01-22 09:39:23 +01:00
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
for _, message := range messages {
|
2019-03-13 21:20:22 +01:00
|
|
|
if err := c.writeMessage(message); err != nil {
|
2019-01-22 09:39:23 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
return nil
|
|
|
|
})
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:20:22 +01:00
|
|
|
func (c *ChatViewController) writeMessage(message *protocol.Message) error {
|
2019-01-08 21:02:11 +01:00
|
|
|
myPubKey := c.identity.PublicKey
|
2019-01-22 09:39:23 +01:00
|
|
|
pubKey := message.SigPubKey
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-01-22 09:39:23 +01:00
|
|
|
line := formatMessageLine(
|
|
|
|
pubKey,
|
2019-03-11 18:49:18 +01:00
|
|
|
message.Hash,
|
2019-01-22 09:39:23 +01:00
|
|
|
time.Unix(message.Decoded.Timestamp/1000, 0),
|
|
|
|
message.Decoded.Text,
|
|
|
|
)
|
2019-01-08 21:02:11 +01:00
|
|
|
|
2019-01-22 09:39:23 +01:00
|
|
|
println := fmt.Fprintln
|
2019-03-11 18:49:18 +01:00
|
|
|
// TODO: extract
|
2019-01-22 09:39:23 +01:00
|
|
|
if pubKey.X.Cmp(myPubKey.X) == 0 && pubKey.Y.Cmp(myPubKey.Y) == 0 {
|
|
|
|
println = color.New(color.FgGreen).Fprintln
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := println(c.ViewController, line); err != nil {
|
|
|
|
return err
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
func formatMessageLine(id *ecdsa.PublicKey, hash []byte, t time.Time, text string) string {
|
2019-01-08 21:02:11 +01:00
|
|
|
author := "<unknown>"
|
|
|
|
if id != nil {
|
2019-03-11 18:49:18 +01:00
|
|
|
author = "0x" + hex.EncodeToString(crypto.CompressPubkey(id))[:7]
|
2019-01-08 21:02:11 +01:00
|
|
|
}
|
|
|
|
return fmt.Sprintf(
|
2019-03-11 18:49:18 +01:00
|
|
|
"%s | %#+x | %s | %s",
|
2019-01-08 21:02:11 +01:00
|
|
|
author,
|
2019-03-11 18:49:18 +01:00
|
|
|
hash[:3],
|
2019-01-08 21:02:11 +01:00
|
|
|
t.Format(time.RFC822),
|
|
|
|
strings.TrimSpace(text),
|
|
|
|
)
|
|
|
|
}
|