2019-03-11 18:49:18 +01:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
2019-03-13 21:20:22 +01:00
|
|
|
"log"
|
2019-03-11 18:49:18 +01:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/status-im/status-console-client/protocol/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Messenger coordinates chats.
|
|
|
|
type Messenger struct {
|
2019-03-13 21:20:22 +01:00
|
|
|
sync.RWMutex
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
proto protocol.Chat
|
|
|
|
identity *ecdsa.PrivateKey
|
|
|
|
db *Database
|
|
|
|
|
2019-03-13 23:54:07 +01:00
|
|
|
chats map[Contact]*Chat
|
|
|
|
chatsCancels map[Contact]chan struct{} // cancel handling chat events
|
2019-03-11 18:49:18 +01:00
|
|
|
|
|
|
|
events chan interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMessanger returns a new Messanger.
|
|
|
|
func NewMessenger(proto protocol.Chat, identity *ecdsa.PrivateKey, db *Database) *Messenger {
|
|
|
|
return &Messenger{
|
2019-03-13 23:54:07 +01:00
|
|
|
proto: proto,
|
|
|
|
identity: identity,
|
|
|
|
db: db,
|
|
|
|
chats: make(map[Contact]*Chat),
|
|
|
|
chatsCancels: make(map[Contact]chan struct{}),
|
|
|
|
events: make(chan interface{}),
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events returns a channel with chat events.
|
|
|
|
func (m *Messenger) Events() <-chan interface{} {
|
2019-03-13 21:20:22 +01:00
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
2019-03-11 18:49:18 +01:00
|
|
|
return m.events
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:20:22 +01:00
|
|
|
func (m *Messenger) Chat(c Contact) *Chat {
|
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
|
|
|
return m.chats[c]
|
|
|
|
}
|
|
|
|
|
2019-03-11 18:49:18 +01:00
|
|
|
// Join creates a new chat and creates a subscription.
|
|
|
|
func (m *Messenger) Join(contact Contact) error {
|
2019-03-13 21:20:22 +01:00
|
|
|
m.RLock()
|
|
|
|
chat, found := m.chats[contact]
|
|
|
|
m.RUnlock()
|
2019-03-11 18:49:18 +01:00
|
|
|
|
2019-03-13 21:20:22 +01:00
|
|
|
if found {
|
|
|
|
return chat.Load()
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:20:22 +01:00
|
|
|
chat = NewChat(m.proto, m.identity, contact, m.db)
|
2019-03-13 23:54:07 +01:00
|
|
|
cancel := make(chan struct{})
|
2019-03-13 21:20:22 +01:00
|
|
|
|
|
|
|
m.Lock()
|
2019-03-11 18:49:18 +01:00
|
|
|
m.chats[contact] = chat
|
2019-03-13 23:54:07 +01:00
|
|
|
m.chatsCancels[contact] = cancel
|
2019-03-13 21:20:22 +01:00
|
|
|
m.Unlock()
|
2019-03-11 18:49:18 +01:00
|
|
|
|
2019-03-15 15:12:00 +01:00
|
|
|
go m.chatEventsLoop(chat, contact, cancel)
|
|
|
|
|
|
|
|
return chat.Subscribe()
|
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
|
2019-03-15 15:12:00 +01:00
|
|
|
func (m *Messenger) chatEventsLoop(chat *Chat, contact Contact, cancel chan struct{}) {
|
|
|
|
log.Printf("[Messenger::Join] waiting for events")
|
|
|
|
|
|
|
|
LOOP:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case ev := <-chat.Events():
|
|
|
|
log.Printf("[Messenger::Join] received an event: %+v", ev)
|
|
|
|
m.events <- ev
|
|
|
|
case <-cancel:
|
|
|
|
break LOOP
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
2019-03-15 15:12:00 +01:00
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
|
2019-03-15 15:12:00 +01:00
|
|
|
if err := chat.Err(); err != nil {
|
|
|
|
m.events <- errorEvent{
|
|
|
|
baseEvent: baseEvent{contact: contact, typ: EventTypeError},
|
|
|
|
err: err,
|
|
|
|
}
|
|
|
|
}
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Leave unsubscribes from the chat.
|
|
|
|
func (m *Messenger) Leave(contact Contact) error {
|
2019-03-13 21:20:22 +01:00
|
|
|
m.RLock()
|
2019-03-11 18:49:18 +01:00
|
|
|
chat, ok := m.chats[contact]
|
2019-03-13 23:54:07 +01:00
|
|
|
cancel := m.chatsCancels[contact]
|
2019-03-13 21:20:22 +01:00
|
|
|
m.RUnlock()
|
2019-03-11 18:49:18 +01:00
|
|
|
if !ok {
|
|
|
|
return errors.New("chat for the contact not found")
|
|
|
|
}
|
|
|
|
|
2019-03-15 15:12:00 +01:00
|
|
|
chat.leave() // close chat loops; must happen before closing the chat events loop
|
2019-03-13 23:54:07 +01:00
|
|
|
|
2019-03-15 15:12:00 +01:00
|
|
|
close(cancel) // close a loop reading and forwarding the chat events
|
2019-03-13 21:20:22 +01:00
|
|
|
|
|
|
|
m.Lock()
|
2019-03-11 18:49:18 +01:00
|
|
|
delete(m.chats, contact)
|
2019-03-13 21:20:22 +01:00
|
|
|
m.Unlock()
|
2019-03-11 18:49:18 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) Contacts() ([]Contact, error) {
|
|
|
|
return m.db.Contacts()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) AddContact(c Contact) error {
|
|
|
|
contacts, err := m.db.Contacts()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if ContainsContact(contacts, c) {
|
|
|
|
return errors.New("duplicated contact")
|
|
|
|
}
|
|
|
|
|
|
|
|
contacts = append(contacts, c)
|
|
|
|
return m.db.SaveContacts(contacts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) RemoveContact(c Contact) error {
|
|
|
|
contacts, err := m.db.Contacts()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, item := range contacts {
|
2019-03-13 21:20:22 +01:00
|
|
|
if item != c {
|
|
|
|
continue
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
2019-03-13 21:20:22 +01:00
|
|
|
|
|
|
|
copy(contacts[i:], contacts[i+1:])
|
|
|
|
contacts[len(contacts)-1] = Contact{}
|
|
|
|
contacts = contacts[:len(contacts)-1]
|
|
|
|
|
|
|
|
break
|
2019-03-11 18:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
contacts = append(contacts, c)
|
|
|
|
return m.db.SaveContacts(contacts)
|
|
|
|
}
|