233 lines
5.9 KiB
Go
Raw Normal View History

2019-01-08 21:02:11 +01:00
package main
import (
2019-04-30 10:24:33 +02:00
"context"
2019-01-08 21:02:11 +01:00
"crypto/ecdsa"
"encoding/hex"
"fmt"
"log"
"strings"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/fatih/color"
"github.com/jroimartin/gocui"
"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
contact client.Contact
identity *ecdsa.PrivateKey
2019-06-06 10:59:28 +02:00
messenger *client.Messenger
2019-01-08 21:02:11 +01:00
2019-03-25 11:01:42 +01:00
onError func(error)
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
changeContact chan client.Contact
2019-01-08 21:02:11 +01:00
}
// NewChatViewController returns a new chat view controller.
2019-06-06 10:59:28 +02:00
func NewChatViewController(vc *ViewController, id Identity, m *client.Messenger, onError func(error)) *ChatViewController {
2019-03-25 11:01:42 +01:00
if onError == nil {
onError = func(error) {}
}
2019-01-08 21:02:11 +01:00
return &ChatViewController{
ViewController: vc,
identity: id,
messenger: m,
2019-03-25 11:01:42 +01:00
onError: onError,
changeContact: make(chan client.Contact, 1),
2019-03-25 11:01:42 +01:00
}
2019-01-08 21:02:11 +01:00
}
func (c *ChatViewController) readEventsLoop(contact client.Contact) {
c.done = make(chan struct{})
defer close(c.done)
2019-01-22 09:39:23 +01:00
var (
messages = []*protocol.Message{}
clock int64
inorder bool
events = make(chan client.Event)
sub = c.messenger.Subscribe(events)
// We use a ticker in order to buffer storm of received events.
t = time.NewTicker(time.Second)
)
defer sub.Unsubscribe()
2019-03-17 21:48:55 +01:00
defer t.Stop()
for {
select {
2019-03-17 21:48:55 +01:00
case <-t.C:
if !inorder {
// messages are sorted by clock value
// TODO draw messages only after offset (if possible)
all, err := c.messenger.Messages(c.contact, 0)
if err != nil {
c.onError(err)
continue
}
if len(all) != 0 {
clock = all[len(all)-1].Clock
}
log.Printf("[ChatViewController::readEventsLoop] retrieved %d messages", len(messages))
c.printMessages(true, all...)
inorder = true
2019-03-17 21:48:55 +01:00
} else {
if len(messages) != 0 {
c.printMessages(false, messages...)
}
}
messages = []*protocol.Message{}
case err := <-sub.Err():
if err == nil {
return
}
c.onError(err)
return
case event := <-events:
log.Printf("[ChatViewController::readEventsLoop] received an event: %+v", event)
2019-03-17 21:48:55 +01:00
switch ev := event.Interface.(type) {
2019-04-30 10:24:33 +02:00
case client.EventWithError:
c.onError(ev.GetError())
case client.EventWithContact:
if !ev.GetContact().Equal(contact) {
log.Printf("[ChatViewController::readEventsLoop] selected and received message contact are not equal: %s, %s", contact, ev.GetContact())
continue
}
msgev, ok := ev.(client.EventWithMessage)
if !ok {
log.Printf("[ChatViewController::readEventsLoop] can not convert to EventWithMessage")
continue
}
if !inorder {
log.Printf("[ChatViewController::readEventsLoop] not in order; skipping")
continue
}
msg := msgev.GetMessage()
log.Printf("[ChatViewController::readEventsLoop] received message %v", msg)
if msg.Clock < clock {
inorder = false
log.Printf("[ChatViewController::readEventsLoop] received message is out of order")
continue
}
messages = append(messages, msg)
2019-03-17 21:48:55 +01:00
}
case contact = <-c.changeContact:
inorder = false
clock = 0
messages = []*protocol.Message{}
case <-c.cancel:
return
}
2019-01-08 21:02:11 +01:00
}
}
2019-01-08 21:02:11 +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 {
log.Printf("[ChatViewController::Select] contact %s", contact.Name)
2019-01-08 21:02:11 +01:00
if c.cancel == nil {
c.cancel = make(chan struct{})
go c.readEventsLoop(contact)
2019-01-08 21:02:11 +01:00
}
c.changeContact <- contact
c.contact = contact
2019-01-08 21:02:11 +01:00
2019-04-30 10:24:33 +02:00
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
return c.messenger.Join(ctx, contact)
}
2019-04-15 20:35:56 +02:00
// RequestOptions returns the RequestOptions for the next request call.
// Newest param when true means that we are interested in the most recent messages.
2019-04-30 10:24:33 +02:00
func (c *ChatViewController) RequestOptions(newest bool) (protocol.RequestOptions, error) {
return protocol.DefaultRequestOptions(), nil
2019-02-25 08:29:35 +01:00
}
2019-04-15 20:35:56 +02:00
// RequestMessages sends a request fro historical messages.
func (c *ChatViewController) RequestMessages(params protocol.RequestOptions) error {
2019-04-30 10:24:33 +02:00
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
return c.messenger.Request(ctx, c.contact, params)
2019-01-08 21:02:11 +01:00
}
2019-04-15 20:35:56 +02:00
// Send sends a payload as a message.
func (c *ChatViewController) Send(data []byte) error {
log.Printf("[ChatViewController::Send]")
return c.messenger.Send(c.contact, data)
}
2019-01-08 21:02:11 +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))
c.g.Update(func(*gocui.Gui) error {
if clear {
if err := c.Clear(); err != nil {
return err
}
}
2019-01-22 09:39:23 +01:00
for _, message := range messages {
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
}
return nil
})
2019-01-08 21:02:11 +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-04-15 20:35:56 +02:00
message.ID,
int64(message.Clock),
message.Timestamp.Time(),
message.Text,
2019-01-22 09:39:23 +01:00
)
2019-01-08 21:02:11 +01:00
2019-01-22 09:39:23 +01:00
println := fmt.Fprintln
// 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-25 11:01:42 +01:00
func formatMessageLine(id *ecdsa.PublicKey, hash []byte, clock int64, t time.Time, text string) string {
2019-01-08 21:02:11 +01:00
author := "<unknown>"
if id != nil {
author = "0x" + hex.EncodeToString(crypto.CompressPubkey(id))[:7]
2019-01-08 21:02:11 +01:00
}
return fmt.Sprintf(
2019-03-25 11:01:42 +01:00
"%s | %#+x | %d | %s | %s",
2019-01-08 21:02:11 +01:00
author,
hash[:3],
2019-03-25 11:01:42 +01:00
clock,
2019-01-08 21:02:11 +01:00
t.Format(time.RFC822),
strings.TrimSpace(text),
)
}