239 lines
5.4 KiB
Go
Raw Normal View History

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"
"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
notifications *Notifications
2019-01-08 21:02:11 +01:00
contact client.Contact
firstRequest protocol.RequestOptions
lastRequest protocol.RequestOptions
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.
func NewChatViewController(vc *ViewController, id Identity, m *client.Messenger) (*ChatViewController, error) {
2019-01-08 21:02:11 +01:00
return &ChatViewController{
ViewController: vc,
notifications: &Notifications{writer: vc},
2019-01-08 21:02:11 +01:00
identity: id,
messenger: m,
2019-01-08 21:02:11 +01:00
}, nil
}
func (c *ChatViewController) readEventsLoop() {
c.done = make(chan struct{})
defer close(c.done)
2019-01-22 09:39:23 +01:00
2019-03-17 21:48:55 +01:00
var buffer []interface{}
t := time.NewTicker(time.Second)
defer t.Stop()
for {
log.Printf("[ChatViewController::readEventsLoops] waiting for events")
select {
2019-03-17 21:48:55 +01:00
case <-t.C:
chat := c.messenger.Chat(c.contact)
if chat == nil {
c.notifications.Error("getting chat", "chat does not exist") // nolint: errcheck
break
}
2019-03-17 21:48:55 +01:00
redraw := requiresRedraw(buffer)
2019-03-17 21:48:55 +01:00
log.Printf("[ChatViewController::readEventsLoops] redraw = %t", redraw)
2019-03-17 21:48:55 +01:00
if redraw {
messages := chat.Messages()
log.Printf("[ChatViewController::readEventsLoops] retrieved %d messages", len(messages))
2019-03-17 21:48:55 +01:00
c.printMessages(true, messages...)
} else {
for _, event := range buffer {
switch ev := event.(type) {
case client.EventMessage:
c.printMessages(false, ev.Message())
}
}
}
2019-03-17 21:48:55 +01:00
buffer = nil
case event := <-c.messenger.Events():
log.Printf("[ChatViewController::readEventsLoops] received an event: %+v", event)
switch ev := event.(type) {
case client.EventError:
c.notifications.Error("error", ev.Error().Error()) // nolint: errcheck
default:
buffer = append(buffer, event)
}
case <-c.cancel:
return
}
2019-01-08 21:02:11 +01:00
}
}
2019-01-08 21:02:11 +01:00
2019-03-17 21:48:55 +01:00
func requiresRedraw(events []interface{}) bool {
for _, event := range events {
switch ev := event.(type) {
case client.Event:
switch ev.Type() {
case client.EventTypeInit, client.EventTypeRearrange:
return true
}
}
}
return false
}
// 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()
2019-01-08 21:02:11 +01:00
}
c.contact = contact
2019-01-08 21:02:11 +01:00
params := protocol.DefaultRequestOptions()
err := c.messenger.Join(contact, params)
if err == nil {
c.updateRequests(params)
}
return err
}
func (c *ChatViewController) RequestOptions(older bool) protocol.RequestOptions {
params := protocol.DefaultRequestOptions()
if older && c.firstRequest != (protocol.RequestOptions{}) {
params.From = c.firstRequest.From - 60*60*24
params.To = c.firstRequest.From
} else if c.lastRequest != (protocol.RequestOptions{}) {
params.From = c.lastRequest.To
}
return params
2019-02-25 08:29:35 +01:00
}
func (c *ChatViewController) RequestMessages(params protocol.RequestOptions) error {
c.notifications.Debug( // nolint: errcheck
"REQUEST",
fmt.Sprintf("get historic messages: %+v", params),
)
chat := c.messenger.Chat(c.contact)
if chat == nil {
return fmt.Errorf("chat not found")
}
err := chat.Request(params)
if err == nil {
c.updateRequests(params)
}
return err
}
func (c *ChatViewController) updateRequests(params protocol.RequestOptions) {
if c.firstRequest.From == 0 || c.firstRequest.From > params.From {
c.firstRequest = params
}
if c.lastRequest.To < params.To {
c.lastRequest = params
}
2019-01-08 21:02:11 +01:00
}
func (c *ChatViewController) Send(data []byte) error {
chat := c.messenger.Chat(c.contact)
if chat == nil {
return fmt.Errorf("chat not found")
}
return chat.Send(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,
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
// 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
}
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 {
author = "0x" + hex.EncodeToString(crypto.CompressPubkey(id))[:7]
2019-01-08 21:02:11 +01:00
}
return fmt.Sprintf(
"%s | %#+x | %s | %s",
2019-01-08 21:02:11 +01:00
author,
hash[:3],
2019-01-08 21:02:11 +01:00
t.Format(time.RFC822),
strings.TrimSpace(text),
)
}