448 lines
9.0 KiB
Go
448 lines
9.0 KiB
Go
package protocol
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/status-im/status-go/protocol/common"
|
|
"github.com/status-im/status-go/protocol/requests"
|
|
"github.com/status-im/status-go/protocol/transport"
|
|
)
|
|
|
|
func (m *Messenger) Chats() []*Chat {
|
|
var chats []*Chat
|
|
|
|
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
|
|
chats = append(chats, chat)
|
|
return true
|
|
})
|
|
|
|
return chats
|
|
}
|
|
|
|
func (m *Messenger) ActiveChats() []*Chat {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
var chats []*Chat
|
|
|
|
m.allChats.Range(func(chatID string, c *Chat) bool {
|
|
if c.Active {
|
|
chats = append(chats, c)
|
|
}
|
|
return true
|
|
})
|
|
|
|
return chats
|
|
}
|
|
|
|
func (m *Messenger) initChatSyncFields(chat *Chat) error {
|
|
defaultSyncPeriod, err := m.settings.GetDefaultSyncPeriod()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncPeriod
|
|
chat.SyncedTo = timestamp
|
|
chat.SyncedFrom = timestamp
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) CreatePublicChat(request *requests.CreatePublicChat) (*MessengerResponse, error) {
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chatID := request.ID
|
|
|
|
chat, ok := m.allChats.Load(chatID)
|
|
if !ok {
|
|
chat = CreatePublicChat(chatID, m.getTimesource())
|
|
|
|
}
|
|
chat.Active = true
|
|
chat.DeletedAtClockValue = 0
|
|
|
|
// Save topics
|
|
_, err := m.Join(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Store chat
|
|
m.allChats.Store(chat.ID, chat)
|
|
|
|
willSync, err := m.scheduleSyncChat(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We set the synced to, synced from to the default time
|
|
if !willSync {
|
|
if err := m.initChatSyncFields(chat); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err = m.saveChat(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Sync if it was created
|
|
if !ok {
|
|
if err := m.syncPublicChat(context.Background(), chat); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
response := &MessengerResponse{}
|
|
response.AddChat(chat)
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (m *Messenger) CreateProfileChat(request *requests.CreateProfileChat) (*MessengerResponse, error) {
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
publicKey, err := common.HexToPubkey(request.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chat := m.buildProfileChat(request.ID)
|
|
|
|
chat.Active = true
|
|
|
|
// Save topics
|
|
_, err = m.Join(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check contact code
|
|
filter, err := m.transport.JoinPrivate(publicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Store chat
|
|
m.allChats.Store(chat.ID, chat)
|
|
|
|
response := &MessengerResponse{}
|
|
response.AddChat(chat)
|
|
|
|
willSync, err := m.scheduleSyncChat(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We set the synced to, synced from to the default time
|
|
if !willSync {
|
|
if err := m.initChatSyncFields(chat); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
_, err = m.scheduleSyncFilters([]*transport.Filter{filter})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = m.saveChat(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*MessengerResponse, error) {
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chatID := request.ID.String()
|
|
pk, err := common.HexToPubkey(chatID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chat, ok := m.allChats.Load(chatID)
|
|
if !ok {
|
|
chat = CreateOneToOneChat(chatID, pk, m.getTimesource())
|
|
}
|
|
chat.Active = true
|
|
|
|
filters, err := m.Join(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO(Samyoul) remove storing of an updated reference pointer?
|
|
m.allChats.Store(chatID, chat)
|
|
|
|
response := &MessengerResponse{}
|
|
response.AddChat(chat)
|
|
|
|
willSync, err := m.scheduleSyncFilters(filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We set the synced to, synced from to the default time
|
|
if !willSync {
|
|
if err := m.initChatSyncFields(chat); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err = m.saveChat(chat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
|
|
}
|
|
|
|
func (m *Messenger) DeleteChat(chatID string) error {
|
|
return m.deleteChat(chatID)
|
|
}
|
|
|
|
func (m *Messenger) deleteChat(chatID string) error {
|
|
err := m.persistence.DeleteChat(chatID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
chat, ok := m.allChats.Load(chatID)
|
|
|
|
if ok && chat.Active && chat.Public() {
|
|
m.allChats.Delete(chatID)
|
|
return m.reregisterForPushNotifications()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) SaveChat(chat *Chat) error {
|
|
return m.saveChat(chat)
|
|
}
|
|
|
|
func (m *Messenger) DeactivateChat(request *requests.DeactivateChat) (*MessengerResponse, error) {
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m.deactivateChat(request.ID)
|
|
}
|
|
|
|
func (m *Messenger) deactivateChat(chatID string) (*MessengerResponse, error) {
|
|
var response MessengerResponse
|
|
chat, ok := m.allChats.Load(chatID)
|
|
if !ok {
|
|
return nil, ErrChatNotFound
|
|
}
|
|
|
|
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
|
|
|
err := m.persistence.DeactivateChat(chat, clock)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We re-register as our options have changed and we don't want to
|
|
// receive PN from mentions in this chat anymore
|
|
if chat.Public() {
|
|
err := m.reregisterForPushNotifications()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = m.transport.ClearProcessedMessageIDsCache()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
m.allChats.Store(chatID, chat)
|
|
|
|
response.AddChat(chat)
|
|
// TODO: Remove filters
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
func (m *Messenger) saveChats(chats []*Chat) error {
|
|
err := m.persistence.SaveChats(chats)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, chat := range chats {
|
|
m.allChats.Store(chat.ID, chat)
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (m *Messenger) saveChat(chat *Chat) error {
|
|
previousChat, ok := m.allChats.Load(chat.ID)
|
|
if chat.OneToOne() {
|
|
name, identicon, err := generateAliasAndIdenticon(chat.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
chat.Alias = name
|
|
chat.Identicon = identicon
|
|
}
|
|
// Sync chat if it's a new active public chat, but not a timeline chat
|
|
if !ok && chat.Active && chat.Public() && !chat.ProfileUpdates() && !chat.Timeline() {
|
|
|
|
if err := m.syncPublicChat(context.Background(), chat); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// We check if it's a new chat, or chat.Active has changed
|
|
// we check here, but we only re-register once the chat has been
|
|
// saved an added
|
|
shouldRegisterForPushNotifications := chat.Public() && (!ok && chat.Active) || (ok && chat.Active != previousChat.Active)
|
|
|
|
err := m.persistence.SaveChat(*chat)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
m.allChats.Store(chat.ID, chat)
|
|
|
|
if shouldRegisterForPushNotifications {
|
|
// Re-register for push notifications, as we want to receive mentions
|
|
if err := m.reregisterForPushNotifications(); err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) Join(chat *Chat) ([]*transport.Filter, error) {
|
|
switch chat.ChatType {
|
|
case ChatTypeOneToOne:
|
|
pk, err := chat.PublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f, err := m.transport.JoinPrivate(pk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []*transport.Filter{f}, nil
|
|
case ChatTypePrivateGroupChat:
|
|
members, err := chat.MembersAsPublicKeys()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.transport.JoinGroup(members)
|
|
case ChatTypePublic, ChatTypeProfile, ChatTypeTimeline:
|
|
f, err := m.transport.JoinPublic(chat.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []*transport.Filter{f}, nil
|
|
default:
|
|
return nil, errors.New("chat is neither public nor private")
|
|
}
|
|
}
|
|
|
|
func (m *Messenger) buildProfileChat(id string) *Chat {
|
|
// Create the corresponding profile chat
|
|
profileChatID := buildProfileChatID(id)
|
|
profileChat, ok := m.allChats.Load(profileChatID)
|
|
|
|
if !ok {
|
|
profileChat = CreateProfileChat(id, m.getTimesource())
|
|
}
|
|
|
|
return profileChat
|
|
|
|
}
|
|
|
|
func (m *Messenger) ensureTimelineChat() error {
|
|
chat, err := m.persistence.Chat(timelineChatID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if chat != nil {
|
|
return nil
|
|
}
|
|
|
|
chat = CreateTimelineChat(m.getTimesource())
|
|
m.allChats.Store(timelineChatID, chat)
|
|
return m.saveChat(chat)
|
|
}
|
|
|
|
func (m *Messenger) ensureMyOwnProfileChat() error {
|
|
chatID := common.PubkeyToHex(&m.identity.PublicKey)
|
|
_, ok := m.allChats.Load(chatID)
|
|
if ok {
|
|
return nil
|
|
}
|
|
|
|
chat := m.buildProfileChat(chatID)
|
|
|
|
chat.Active = true
|
|
|
|
// Save topics
|
|
_, err := m.Join(chat)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return m.saveChat(chat)
|
|
}
|
|
|
|
func (m *Messenger) ClearHistory(request *requests.ClearHistory) (*MessengerResponse, error) {
|
|
if err := request.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m.clearHistory(request.ID)
|
|
}
|
|
|
|
func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
|
|
chat, ok := m.allChats.Load(id)
|
|
if !ok {
|
|
return nil, ErrChatNotFound
|
|
}
|
|
|
|
clock, _ := chat.NextClockAndTimestamp(m.transport)
|
|
|
|
err := m.persistence.ClearHistory(chat, clock)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if chat.Public() {
|
|
|
|
err = m.transport.ClearProcessedMessageIDsCache()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
m.allChats.Store(id, chat)
|
|
|
|
response := &MessengerResponse{}
|
|
response.AddChat(chat)
|
|
return response, nil
|
|
}
|