2021-09-11 12:10:51 +00:00
|
|
|
|
import json, strutils, sequtils, tables, chronicles, times, sugar, algorithm
|
2021-09-11 21:50:36 +00:00
|
|
|
|
import statusgo_backend/chat as status_chat
|
2021-09-20 13:00:10 +00:00
|
|
|
|
import statusgo_backend/contacts as status_contacts
|
2021-09-11 21:50:36 +00:00
|
|
|
|
import statusgo_backend/chatCommands as status_chat_commands
|
2021-10-01 16:53:38 +00:00
|
|
|
|
import types/[message, status_update, activity_center_notification, sticker, removed_message, profile]
|
2021-09-11 12:10:51 +00:00
|
|
|
|
import types/chat as chat_type
|
2021-10-01 16:53:38 +00:00
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
import utils as status_utils
|
|
|
|
|
import stickers
|
|
|
|
|
import ../eventemitter
|
|
|
|
|
|
|
|
|
|
import contacts
|
|
|
|
|
import chat/[chat, utils]
|
|
|
|
|
import ens, accounts
|
|
|
|
|
|
|
|
|
|
include utils/json_utils
|
|
|
|
|
|
|
|
|
|
logScope:
|
|
|
|
|
topics = "chat-model"
|
|
|
|
|
|
|
|
|
|
const backToFirstChat* = "__goBackToFirstChat"
|
|
|
|
|
const ZERO_ADDRESS* = "0x0000000000000000000000000000000000000000"
|
|
|
|
|
|
2021-09-11 12:10:51 +00:00
|
|
|
|
proc buildFilter*(chat: Chat):JsonNode =
|
|
|
|
|
if chat.chatType == ChatType.PrivateGroupChat:
|
|
|
|
|
return newJNull()
|
|
|
|
|
result = %* { "ChatID": chat.id, "OneToOne": chat.chatType == ChatType.OneToOne }
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
type
|
|
|
|
|
ChatUpdateArgs* = ref object of Args
|
|
|
|
|
chats*: seq[Chat]
|
|
|
|
|
messages*: seq[Message]
|
|
|
|
|
pinnedMessages*: seq[Message]
|
|
|
|
|
emojiReactions*: seq[Reaction]
|
|
|
|
|
communities*: seq[Community]
|
|
|
|
|
communityMembershipRequests*: seq[CommunityMembershipRequest]
|
|
|
|
|
activityCenterNotifications*: seq[ActivityCenterNotification]
|
|
|
|
|
statusUpdates*: seq[StatusUpdate]
|
|
|
|
|
deletedMessages*: seq[RemovedMessage]
|
|
|
|
|
|
|
|
|
|
ChatIdArg* = ref object of Args
|
|
|
|
|
chatId*: string
|
|
|
|
|
|
|
|
|
|
ChannelArgs* = ref object of Args
|
|
|
|
|
chat*: Chat
|
|
|
|
|
|
|
|
|
|
ChatArgs* = ref object of Args
|
|
|
|
|
chats*: seq[Chat]
|
|
|
|
|
|
|
|
|
|
CommunityActiveChangedArgs* = ref object of Args
|
|
|
|
|
active*: bool
|
|
|
|
|
|
|
|
|
|
MsgsLoadedArgs* = ref object of Args
|
|
|
|
|
chatId*: string
|
|
|
|
|
messages*: seq[Message]
|
|
|
|
|
statusUpdates*: seq[StatusUpdate]
|
|
|
|
|
|
|
|
|
|
ActivityCenterNotificationsArgs* = ref object of Args
|
|
|
|
|
activityCenterNotifications*: seq[ActivityCenterNotification]
|
|
|
|
|
|
|
|
|
|
ReactionsLoadedArgs* = ref object of Args
|
|
|
|
|
reactions*: seq[Reaction]
|
|
|
|
|
|
|
|
|
|
MessageArgs* = ref object of Args
|
|
|
|
|
id*: string
|
|
|
|
|
channel*: string
|
|
|
|
|
|
|
|
|
|
MessageSendingSuccess* = ref object of Args
|
|
|
|
|
chat*: Chat
|
|
|
|
|
message*: Message
|
|
|
|
|
|
|
|
|
|
MarkAsReadNotificationProperties* = ref object of Args
|
|
|
|
|
communityId*: string
|
|
|
|
|
channelId*: string
|
|
|
|
|
notificationTypes*: seq[ActivityCenterNotificationType]
|
|
|
|
|
|
2021-11-16 11:37:56 +00:00
|
|
|
|
MarkAsUnreadNotificationProperties* = ref object of Args
|
|
|
|
|
communityId*: string
|
|
|
|
|
channelId*: string
|
|
|
|
|
notificationTypes*: seq[ActivityCenterNotificationType]
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
type ChatModel* = ref object
|
|
|
|
|
publicKey*: string
|
|
|
|
|
events*: EventEmitter
|
|
|
|
|
communitiesToFetch*: seq[string]
|
|
|
|
|
mailserverReady*: bool
|
|
|
|
|
channels*: Table[string, Chat]
|
|
|
|
|
msgCursor: Table[string, string]
|
|
|
|
|
pinnedMsgCursor: Table[string, string]
|
|
|
|
|
activityCenterCursor*: string
|
|
|
|
|
emojiCursor: Table[string, string]
|
|
|
|
|
lastMessageTimestamps*: Table[string, int64]
|
|
|
|
|
|
|
|
|
|
proc newChatModel*(events: EventEmitter): ChatModel =
|
|
|
|
|
result = ChatModel()
|
|
|
|
|
result.events = events
|
|
|
|
|
result.mailserverReady = false
|
|
|
|
|
result.communitiesToFetch = @[]
|
|
|
|
|
result.channels = initTable[string, Chat]()
|
|
|
|
|
result.msgCursor = initTable[string, string]()
|
|
|
|
|
result.pinnedMsgCursor = initTable[string, string]()
|
|
|
|
|
result.emojiCursor = initTable[string, string]()
|
|
|
|
|
result.lastMessageTimestamps = initTable[string, int64]()
|
|
|
|
|
|
2021-09-20 13:00:10 +00:00
|
|
|
|
proc getContacts*(self: ChatModel): Table[string, Profile] =
|
|
|
|
|
let (index, usedCache) = status_contacts.getContactsIndex()
|
|
|
|
|
if not usedCache:
|
|
|
|
|
let (contacts, _) = status_contacts.getContacts()
|
|
|
|
|
self.events.emit("contactUpdate", ContactUpdateArgs(contacts: contacts))
|
|
|
|
|
|
|
|
|
|
return index
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest], pinnedMessages: seq[Message], activityCenterNotifications: seq[ActivityCenterNotification], statusUpdates: seq[StatusUpdate], deletedMessages: seq[RemovedMessage]) =
|
|
|
|
|
for chat in chats:
|
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
|
|
|
|
|
for message in messages:
|
|
|
|
|
let chatId = message.chatId
|
|
|
|
|
let ts = times.convert(Milliseconds, Seconds, message.whisperTimestamp.parseInt())
|
|
|
|
|
if not self.lastMessageTimestamps.hasKey(chatId):
|
|
|
|
|
self.lastMessageTimestamps[chatId] = ts
|
|
|
|
|
else:
|
|
|
|
|
if self.lastMessageTimestamps[chatId] > ts:
|
|
|
|
|
self.lastMessageTimestamps[chatId] = ts
|
|
|
|
|
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chats, emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications, statusUpdates: statusUpdates, deletedMessages: deletedMessages))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc parseChatResponse(self: ChatModel, response: string): (seq[Chat], seq[Message]) =
|
|
|
|
|
var parsedResponse = parseJson(response)
|
|
|
|
|
var chats: seq[Chat] = @[]
|
|
|
|
|
var messages: seq[Message] = @[]
|
|
|
|
|
if parsedResponse{"result"}{"messages"} != nil:
|
|
|
|
|
for jsonMsg in parsedResponse["result"]["messages"]:
|
|
|
|
|
messages.add(jsonMsg.toMessage())
|
|
|
|
|
if parsedResponse{"result"}{"chats"} != nil:
|
|
|
|
|
for jsonChat in parsedResponse["result"]["chats"]:
|
|
|
|
|
let chat = jsonChat.toChat
|
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
chats.add(chat)
|
|
|
|
|
result = (chats, messages)
|
|
|
|
|
|
|
|
|
|
proc emitUpdate(self: ChatModel, response: string) =
|
|
|
|
|
var (chats, messages) = self.parseChatResponse(response)
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode)
|
|
|
|
|
|
|
|
|
|
proc removeChatFilters(self: ChatModel, chatId: string) =
|
|
|
|
|
# TODO: this code should be handled by status-go / stimbus instead of the client
|
|
|
|
|
# Clients should not have to care about filters. For more info about filters:
|
|
|
|
|
# https://github.com/status-im/specs/blob/master/docs/stable/3-whisper-usage.md#keys-management
|
|
|
|
|
let filters = parseJson(status_chat.loadFilters(@[]))["result"]
|
|
|
|
|
|
|
|
|
|
case self.channels[chatId].chatType
|
|
|
|
|
of ChatType.Public:
|
|
|
|
|
for filter in filters:
|
|
|
|
|
if filter["chatId"].getStr == chatId:
|
|
|
|
|
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
|
|
|
|
of ChatType.OneToOne, ChatType.Profile:
|
|
|
|
|
# Check if user does not belong to any active chat group
|
|
|
|
|
var inGroup = false
|
|
|
|
|
for channel in self.channels.values:
|
|
|
|
|
if channel.isActive and channel.id != chatId and channel.chatType == ChatType.PrivateGroupChat:
|
|
|
|
|
inGroup = true
|
|
|
|
|
break
|
|
|
|
|
if not inGroup: self.removeFiltersByChatId(chatId, filters)
|
|
|
|
|
of ChatType.PrivateGroupChat:
|
|
|
|
|
for member in self.channels[chatId].members:
|
|
|
|
|
# Check that any of the members are not in other active group chats, or that you don’t have a one-to-one open.
|
|
|
|
|
var hasConversation = false
|
|
|
|
|
for channel in self.channels.values:
|
|
|
|
|
if (channel.isActive and channel.chatType == ChatType.OneToOne and channel.id == member.id) or
|
|
|
|
|
(channel.isActive and channel.id != chatId and channel.chatType == ChatType.PrivateGroupChat and channel.isMember(member.id)):
|
|
|
|
|
hasConversation = true
|
|
|
|
|
break
|
|
|
|
|
if not hasConversation: self.removeFiltersByChatId(member.id, filters)
|
|
|
|
|
else:
|
|
|
|
|
error "Unknown chat type removed", chatId
|
|
|
|
|
|
|
|
|
|
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode) =
|
|
|
|
|
var partitionedTopic = ""
|
|
|
|
|
for filter in filters:
|
|
|
|
|
# Contact code filter should be removed
|
|
|
|
|
if filter["identity"].getStr == chatId and filter["chatId"].getStr.endsWith("-contact-code"):
|
|
|
|
|
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
|
|
|
|
|
|
|
|
|
# Remove partitioned topic if no other user in an active group chat or one-to-one is from the
|
|
|
|
|
# same partitioned topic
|
|
|
|
|
if filter["identity"].getStr == chatId and filter["chatId"].getStr.startsWith("contact-discovery-"):
|
|
|
|
|
partitionedTopic = filter["topic"].getStr
|
|
|
|
|
var samePartitionedTopic = false
|
|
|
|
|
for f in filters.filterIt(it["topic"].getStr == partitionedTopic and it["filterId"].getStr != filter["filterId"].getStr):
|
|
|
|
|
let fIdentity = f["identity"].getStr;
|
|
|
|
|
if self.channels.hasKey(fIdentity) and self.channels[fIdentity].isActive:
|
|
|
|
|
samePartitionedTopic = true
|
|
|
|
|
break
|
|
|
|
|
if not samePartitionedTopic:
|
|
|
|
|
status_chat.removeFilters(chatId, filter["filterId"].getStr)
|
|
|
|
|
|
|
|
|
|
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
|
|
|
|
self.channels.hasKey(chatId)
|
|
|
|
|
|
|
|
|
|
proc getActiveChannel*(self: ChatModel): string =
|
|
|
|
|
if (self.channels.len == 0): "" else: toSeq(self.channels.values)[self.channels.len - 1].id
|
|
|
|
|
|
|
|
|
|
proc emitTopicAndJoin(self: ChatModel, chat: Chat) =
|
2021-09-11 12:10:51 +00:00
|
|
|
|
let filterResult = status_chat.loadFilters(@[buildFilter(chat)])
|
2021-09-08 18:05:39 +00:00
|
|
|
|
self.events.emit("channelJoined", ChannelArgs(chat: chat))
|
|
|
|
|
|
|
|
|
|
proc join*(self: ChatModel, chatId: string, chatType: ChatType, ensName: string = "", pubKey: string = "") =
|
|
|
|
|
if self.hasChannel(chatId): return
|
|
|
|
|
var chat = newChat(chatId, ChatType(chatType))
|
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
status_chat.saveChat(chatId, chatType, color=chat.color, ensName=ensName, profile=pubKey)
|
|
|
|
|
self.emitTopicAndJoin(chat)
|
|
|
|
|
|
|
|
|
|
proc createOneToOneChat*(self: ChatModel, publicKey: string, ensName: string = "") =
|
2021-11-10 19:47:41 +00:00
|
|
|
|
if self.hasChannel(publicKey):
|
|
|
|
|
# We want to show the chat to the user and for that we activate the chat
|
|
|
|
|
status_chat.saveChat(publicKey, ChatType.OneToOne, color=self.channels[publicKey].color, ensName=ensName)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
self.emitTopicAndJoin(self.channels[publicKey])
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var chat = newChat(publicKey, ChatType.OneToOne)
|
|
|
|
|
if ensName != "":
|
|
|
|
|
chat.name = ensName
|
|
|
|
|
chat.ensName = ensName
|
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
discard status_chat.createOneToOneChat(publicKey)
|
|
|
|
|
self.emitTopicAndJoin(chat)
|
|
|
|
|
|
|
|
|
|
proc createPublicChat*(self: ChatModel, chatId: string) =
|
|
|
|
|
if self.hasChannel(chatId): return
|
|
|
|
|
var chat = newChat(chatId, ChatType.Public)
|
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
discard status_chat.createPublicChat(chatId)
|
|
|
|
|
self.emitTopicAndJoin(chat)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc requestMissingCommunityInfos*(self: ChatModel) =
|
|
|
|
|
if (self.communitiesToFetch.len == 0):
|
|
|
|
|
return
|
|
|
|
|
for communityId in self.communitiesToFetch:
|
|
|
|
|
status_chat.requestCommunityInfo(communityId)
|
|
|
|
|
|
2021-09-11 12:10:51 +00:00
|
|
|
|
proc sortChats(x, y: chat_type.Chat): int =
|
|
|
|
|
var t1 = x.lastMessage.whisperTimestamp
|
|
|
|
|
var t2 = y.lastMessage.whisperTimestamp
|
|
|
|
|
|
|
|
|
|
if t1 <= $x.joined:
|
|
|
|
|
t1 = $x.joined
|
|
|
|
|
if t2 <= $y.joined:
|
|
|
|
|
t2 = $y.joined
|
|
|
|
|
|
|
|
|
|
if t1 > t2: 1
|
|
|
|
|
elif t1 == t2: 0
|
|
|
|
|
else: -1
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
proc init*(self: ChatModel, pubKey: string) =
|
|
|
|
|
self.publicKey = pubKey
|
|
|
|
|
|
2021-09-20 13:00:10 +00:00
|
|
|
|
var (contacts, _) = status_contacts.getContacts()
|
2021-10-05 20:10:45 +00:00
|
|
|
|
contacts = contacts.filter(c => c.added)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
var chatList = status_chat.loadChats()
|
2021-09-11 12:10:51 +00:00
|
|
|
|
chatList.sort(sortChats)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
let profileUpdatesChatIds = chatList.filter(c => c.chatType == ChatType.Profile).map(c => c.id)
|
|
|
|
|
|
|
|
|
|
if chatList.filter(c => c.chatType == ChatType.Timeline).len == 0:
|
|
|
|
|
var timelineChannel = newChat(status_utils.getTimelineChatId(), ChatType.Timeline)
|
|
|
|
|
self.join(timelineChannel.id, timelineChannel.chatType)
|
|
|
|
|
chatList.add(timelineChannel)
|
|
|
|
|
|
|
|
|
|
let timelineChatId = status_utils.getTimelineChatId(pubKey)
|
|
|
|
|
|
|
|
|
|
if not profileUpdatesChatIds.contains(timelineChatId):
|
|
|
|
|
var profileUpdateChannel = newChat(timelineChatId, ChatType.Profile)
|
|
|
|
|
status_chat.saveChat(profileUpdateChannel.id, profileUpdateChannel.chatType, profile=pubKey)
|
|
|
|
|
chatList.add(profileUpdateChannel)
|
|
|
|
|
|
|
|
|
|
# For profile updates and timeline, we have to make sure that for
|
|
|
|
|
# each added contact, a chat has been saved for the currently logged-in
|
|
|
|
|
# user. Users that will use a version of Status with timeline support for the
|
|
|
|
|
# first time, won't have any of those otherwise.
|
|
|
|
|
if profileUpdatesChatIds.filter(id => id != timelineChatId).len != contacts.len:
|
|
|
|
|
for contact in contacts:
|
|
|
|
|
if not profileUpdatesChatIds.contains(status_utils.getTimelineChatId(contact.address)):
|
|
|
|
|
let profileUpdatesChannel = newChat(status_utils.getTimelineChatId(contact.address), ChatType.Profile)
|
|
|
|
|
status_chat.saveChat(profileUpdatesChannel.id, profileUpdatesChannel.chatType, ensName=contact.ensName, profile=contact.address)
|
|
|
|
|
chatList.add(profileUpdatesChannel)
|
|
|
|
|
|
|
|
|
|
var filters:seq[JsonNode] = @[]
|
|
|
|
|
for chat in chatList:
|
|
|
|
|
if self.hasChannel(chat.id):
|
|
|
|
|
continue
|
2021-09-11 12:10:51 +00:00
|
|
|
|
filters.add buildFilter(chat)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
self.channels[chat.id] = chat
|
|
|
|
|
self.events.emit("channelLoaded", ChannelArgs(chat: chat))
|
|
|
|
|
|
|
|
|
|
if filters.len == 0: return
|
|
|
|
|
|
|
|
|
|
let filterResult = status_chat.loadFilters(filters)
|
|
|
|
|
|
|
|
|
|
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
|
|
|
|
|
|
|
|
|
|
self.events.once("mailserverAvailable") do(a: Args):
|
|
|
|
|
self.mailserverReady = true
|
|
|
|
|
self.requestMissingCommunityInfos()
|
|
|
|
|
|
|
|
|
|
proc statusUpdates*(self: ChatModel) =
|
|
|
|
|
let statusUpdates = status_chat.statusUpdates()
|
|
|
|
|
self.events.emit("messagesLoaded", MsgsLoadedArgs(statusUpdates: statusUpdates))
|
|
|
|
|
|
|
|
|
|
proc leave*(self: ChatModel, chatId: string) =
|
|
|
|
|
self.removeChatFilters(chatId)
|
|
|
|
|
|
|
|
|
|
if self.channels[chatId].chatType == ChatType.PrivateGroupChat:
|
|
|
|
|
let leaveGroupResponse = status_chat.leaveGroupChat(chatId)
|
|
|
|
|
self.emitUpdate(leaveGroupResponse)
|
|
|
|
|
|
|
|
|
|
discard status_chat.deactivateChat(self.channels[chatId])
|
|
|
|
|
|
|
|
|
|
self.channels.del(chatId)
|
|
|
|
|
discard status_chat.clearChatHistory(chatId)
|
|
|
|
|
self.events.emit("channelLeft", ChatIdArg(chatId: chatId))
|
|
|
|
|
|
|
|
|
|
proc clearHistory*(self: ChatModel, chatId: string) =
|
|
|
|
|
discard status_chat.clearChatHistory(chatId)
|
|
|
|
|
let chat = self.channels[chatId]
|
|
|
|
|
self.events.emit("chatHistoryCleared", ChannelArgs(chat: chat))
|
|
|
|
|
|
|
|
|
|
proc setActiveChannel*(self: ChatModel, chatId: string) =
|
|
|
|
|
self.events.emit("activeChannelChanged", ChatIdArg(chatId: chatId))
|
|
|
|
|
|
|
|
|
|
proc processMessageUpdateAfterSend(self: ChatModel, response: string): (seq[Chat], seq[Message]) =
|
|
|
|
|
result = self.parseChatResponse(response)
|
|
|
|
|
var (chats, messages) = result
|
|
|
|
|
if chats.len == 0 and messages.len == 0:
|
|
|
|
|
self.events.emit("messageSendingFailed", Args())
|
|
|
|
|
return
|
2021-09-17 14:36:02 +00:00
|
|
|
|
|
|
|
|
|
# This fixes issue#3490
|
|
|
|
|
var msg = messages[0]
|
|
|
|
|
for m in messages:
|
|
|
|
|
if(m.responseTo.len > 0):
|
|
|
|
|
msg = m
|
|
|
|
|
break
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
2021-09-17 14:36:02 +00:00
|
|
|
|
self.events.emit("messageSendingSuccess", MessageSendingSuccess(message: msg, chat: chats[0]))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc sendMessage*(self: ChatModel, chatId: string, msg: string, replyTo: string = "", contentType: int = ContentType.Message.int, communityId: string = "") =
|
|
|
|
|
var response = status_chat.sendChatMessage(chatId, msg, replyTo, contentType, communityId)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc editMessage*(self: ChatModel, messageId: string, msg: string) =
|
|
|
|
|
var response = status_chat.editMessage(messageId, msg)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
2021-09-28 21:10:22 +00:00
|
|
|
|
proc sendAudio*(self: ChatModel, chatId: string, audioBase64: string, durationMs: uint64) =
|
|
|
|
|
var response = status_chat.sendAudioMessage(chatId, audioBase64, durationMs)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
proc sendImage*(self: ChatModel, chatId: string, image: string) =
|
|
|
|
|
var response = status_chat.sendImageMessage(chatId, image)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc sendImages*(self: ChatModel, chatId: string, images: var seq[string]) =
|
|
|
|
|
var response = status_chat.sendImageMessages(chatId, images)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc deleteMessageAndSend*(self: ChatModel, messageId: string) =
|
2021-09-28 21:10:54 +00:00
|
|
|
|
discard status_chat.deleteMessageAndSend(messageId)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc sendSticker*(self: ChatModel, chatId: string, replyTo: string, sticker: Sticker) =
|
|
|
|
|
var response = status_chat.sendStickerMessage(chatId, replyTo, sticker)
|
|
|
|
|
self.events.emit("stickerSent", StickerArgs(sticker: sticker, save: true))
|
|
|
|
|
var (chats, messages) = self.parseChatResponse(response)
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
self.events.emit("sendingMessage", MessageArgs(id: messages[0].id, channel: messages[0].chatId))
|
|
|
|
|
|
|
|
|
|
proc addEmojiReaction*(self: ChatModel, chatId: string, messageId: string, emojiId: int) =
|
|
|
|
|
let reactions = status_chat.addEmojiReaction(chatId, messageId, emojiId)
|
|
|
|
|
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
|
|
|
|
|
|
|
|
|
proc removeEmojiReaction*(self: ChatModel, emojiReactionId: string) =
|
|
|
|
|
let reactions = status_chat.removeEmojiReaction(emojiReactionId)
|
|
|
|
|
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
|
|
|
|
|
|
|
|
|
proc onMarkMessagesRead(self: ChatModel, response: string, chatId: string): JsonNode =
|
|
|
|
|
result = parseJson(response)
|
|
|
|
|
if self.channels.hasKey(chatId):
|
|
|
|
|
self.channels[chatId].unviewedMessagesCount = 0
|
|
|
|
|
self.channels[chatId].mentionsCount = 0
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("channelUpdate", ChatUpdateArgs(messages: @[], chats: @[self.channels[chatId]]))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc onAsyncMarkMessagesRead*(self: ChatModel, response: string) =
|
|
|
|
|
let parsedResponse = parseJson(response)
|
|
|
|
|
discard self.onMarkMessagesRead(parsedResponse{"response"}.getStr, parsedResponse{"chatId"}.getStr)
|
|
|
|
|
|
|
|
|
|
proc markAllChannelMessagesRead*(self: ChatModel, chatId: string): JsonNode =
|
|
|
|
|
var response = status_chat.markAllRead(chatId)
|
|
|
|
|
return self.onMarkMessagesRead(response, chatId)
|
|
|
|
|
|
|
|
|
|
proc markMessagesSeen*(self: ChatModel, chatId: string, messageIds: seq[string]): JsonNode =
|
|
|
|
|
var response = status_chat.markMessagesSeen(chatId, messageIds)
|
|
|
|
|
return self.onMarkMessagesRead(response, chatId)
|
|
|
|
|
|
|
|
|
|
proc confirmJoiningGroup*(self: ChatModel, chatId: string) =
|
|
|
|
|
var response = status_chat.confirmJoiningGroup(chatId)
|
|
|
|
|
self.emitUpdate(response)
|
|
|
|
|
|
|
|
|
|
proc renameGroup*(self: ChatModel, chatId: string, newName: string) =
|
|
|
|
|
var response = status_chat.renameGroup(chatId, newName)
|
|
|
|
|
self.emitUpdate(response)
|
|
|
|
|
|
|
|
|
|
proc getUserName*(self: ChatModel, id: string, defaultUserName: string):string =
|
2021-09-20 13:00:10 +00:00
|
|
|
|
let contacts = self.getContacts()
|
|
|
|
|
if(contacts.hasKey(id)):
|
|
|
|
|
return userNameOrAlias(contacts[id])
|
2021-09-08 18:05:39 +00:00
|
|
|
|
else:
|
|
|
|
|
return defaultUserName
|
|
|
|
|
|
|
|
|
|
proc processGroupChatCreation*(self: ChatModel, result: string) =
|
|
|
|
|
var response = parseJson(result)
|
|
|
|
|
var (chats, messages) = formatChatUpdate(response)
|
|
|
|
|
let chat = chats[0]
|
|
|
|
|
self.channels[chat.id] = chat
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
self.events.emit("activeChannelChanged", ChatIdArg(chatId: chat.id))
|
|
|
|
|
|
|
|
|
|
proc createGroup*(self: ChatModel, groupName: string, pubKeys: seq[string]) =
|
|
|
|
|
var result = status_chat.createGroup(groupName, pubKeys)
|
|
|
|
|
self.processGroupChatCreation(result)
|
|
|
|
|
|
|
|
|
|
proc createGroupChatFromInvitation*(self: ChatModel, groupName: string, chatID: string, adminPK: string) =
|
|
|
|
|
var result = status_chat.createGroupChatFromInvitation(groupName, chatID, adminPK)
|
|
|
|
|
self.processGroupChatCreation(result)
|
|
|
|
|
|
|
|
|
|
proc addGroupMembers*(self: ChatModel, chatId: string, pubKeys: seq[string]) =
|
|
|
|
|
var response = status_chat.addGroupMembers(chatId, pubKeys)
|
|
|
|
|
self.emitUpdate(response)
|
|
|
|
|
|
|
|
|
|
proc kickGroupMember*(self: ChatModel, chatId: string, pubKey: string) =
|
|
|
|
|
var response = status_chat.kickGroupMember(chatId, pubKey)
|
|
|
|
|
self.emitUpdate(response)
|
|
|
|
|
|
|
|
|
|
proc makeAdmin*(self: ChatModel, chatId: string, pubKey: string) =
|
|
|
|
|
var response = status_chat.makeAdmin(chatId, pubKey)
|
|
|
|
|
self.emitUpdate(response)
|
|
|
|
|
|
|
|
|
|
proc resendMessage*(self: ChatModel, messageId: string) =
|
|
|
|
|
discard status_chat.reSendChatMessage(messageId)
|
|
|
|
|
|
|
|
|
|
proc muteChat*(self: ChatModel, chat: Chat) =
|
|
|
|
|
discard status_chat.muteChat(chat.id)
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat]))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc unmuteChat*(self: ChatModel, chat: Chat) =
|
|
|
|
|
discard status_chat.unmuteChat(chat.id)
|
2021-09-20 13:00:10 +00:00
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat]))
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc processUpdateForTransaction*(self: ChatModel, messageId: string, response: string) =
|
|
|
|
|
var (chats, messages) = self.processMessageUpdateAfterSend(response)
|
|
|
|
|
self.events.emit("messageDeleted", MessageArgs(id: messageId, channel: chats[0].id))
|
|
|
|
|
|
|
|
|
|
proc acceptRequestAddressForTransaction*(self: ChatModel, messageId: string, address: string) =
|
|
|
|
|
let response = status_chat_commands.acceptRequestAddressForTransaction(messageId, address)
|
|
|
|
|
self.processUpdateForTransaction(messageId, response)
|
|
|
|
|
|
|
|
|
|
proc declineRequestAddressForTransaction*(self: ChatModel, messageId: string) =
|
|
|
|
|
let response = status_chat_commands.declineRequestAddressForTransaction(messageId)
|
|
|
|
|
self.processUpdateForTransaction(messageId, response)
|
|
|
|
|
|
|
|
|
|
proc declineRequestTransaction*(self: ChatModel, messageId: string) =
|
|
|
|
|
let response = status_chat_commands.declineRequestTransaction(messageId)
|
|
|
|
|
self.processUpdateForTransaction(messageId, response)
|
|
|
|
|
|
|
|
|
|
proc requestAddressForTransaction*(self: ChatModel, chatId: string, fromAddress: string, amount: string, tokenAddress: string) =
|
|
|
|
|
let address = if (tokenAddress == ZERO_ADDRESS): "" else: tokenAddress
|
|
|
|
|
let response = status_chat_commands.requestAddressForTransaction(chatId, fromAddress, amount, address)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc acceptRequestTransaction*(self: ChatModel, transactionHash: string, messageId: string, signature: string) =
|
|
|
|
|
let response = status_chat_commands.acceptRequestTransaction(transactionHash, messageId, signature)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc requestTransaction*(self: ChatModel, chatId: string, fromAddress: string, amount: string, tokenAddress: string) =
|
|
|
|
|
let address = if (tokenAddress == ZERO_ADDRESS): "" else: tokenAddress
|
|
|
|
|
let response = status_chat_commands.requestTransaction(chatId, fromAddress, amount, address)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
|
|
|
|
|
|
|
|
|
proc getAllComunities*(self: ChatModel): seq[Community] =
|
|
|
|
|
result = status_chat.getAllComunities()
|
|
|
|
|
|
|
|
|
|
proc getJoinedComunities*(self: ChatModel): seq[Community] =
|
|
|
|
|
result = status_chat.getJoinedComunities()
|
|
|
|
|
|
|
|
|
|
proc createCommunity*(self: ChatModel, name: string, description: string, access: int, ensOnly: bool, color: string, imageUrl: string, aX: int, aY: int, bX: int, bY: int): Community =
|
|
|
|
|
result = status_chat.createCommunity(name, description, access, ensOnly, color, imageUrl, aX, aY, bX, bY)
|
|
|
|
|
|
|
|
|
|
proc editCommunity*(self: ChatModel, id: string, name: string, description: string, access: int, ensOnly: bool, color: string, imageUrl: string, aX: int, aY: int, bX: int, bY: int): Community =
|
|
|
|
|
result = status_chat.editCommunity(id, name, description, access, ensOnly, color, imageUrl, aX, aY, bX, bY)
|
|
|
|
|
|
|
|
|
|
proc createCommunityChannel*(self: ChatModel, communityId: string, name: string, description: string): Chat =
|
|
|
|
|
result = status_chat.createCommunityChannel(communityId, name, description)
|
|
|
|
|
|
2021-10-05 20:33:35 +00:00
|
|
|
|
proc editCommunityChannel*(self: ChatModel, communityId: string, channelId: string, name: string, description: string, categoryId: string, position: int): Chat =
|
|
|
|
|
result = status_chat.editCommunityChannel(communityId, channelId, name, description, categoryId, position)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc deleteCommunityChat*(self: ChatModel, communityId: string, channelId: string) =
|
|
|
|
|
status_chat.deleteCommunityChat(communityId, channelId)
|
|
|
|
|
|
|
|
|
|
proc reorderCommunityCategories*(self: ChatModel, communityId: string, categoryId: string, position: int) =
|
|
|
|
|
status_chat.reorderCommunityCategories(communityId, categoryId, position)
|
|
|
|
|
|
|
|
|
|
proc createCommunityCategory*(self: ChatModel, communityId: string, name: string, channels: seq[string]): CommunityCategory =
|
|
|
|
|
result = status_chat.createCommunityCategory(communityId, name, channels)
|
|
|
|
|
|
|
|
|
|
proc editCommunityCategory*(self: ChatModel, communityId: string, categoryId: string, name: string, channels: seq[string]) =
|
|
|
|
|
status_chat.editCommunityCategory(communityId, categoryId, name, channels)
|
|
|
|
|
|
|
|
|
|
proc deleteCommunityCategory*(self: ChatModel, communityId: string, categoryId: string) =
|
|
|
|
|
status_chat.deleteCommunityCategory(communityId, categoryId)
|
|
|
|
|
|
|
|
|
|
proc reorderCommunityChannel*(self: ChatModel, communityId: string, categoryId: string, chatId: string, position: int) =
|
|
|
|
|
status_chat.reorderCommunityChat(communityId, categoryId, chatId, position)
|
|
|
|
|
|
|
|
|
|
proc joinCommunity*(self: ChatModel, communityId: string) =
|
|
|
|
|
status_chat.joinCommunity(communityId)
|
|
|
|
|
|
|
|
|
|
proc requestCommunityInfo*(self: ChatModel, communityId: string) =
|
|
|
|
|
if (not self.mailserverReady):
|
|
|
|
|
self.communitiesToFetch.add(communityId)
|
|
|
|
|
self.communitiesToFetch = self.communitiesToFetch.deduplicate()
|
|
|
|
|
return
|
|
|
|
|
status_chat.requestCommunityInfo(communityId)
|
|
|
|
|
|
|
|
|
|
proc leaveCommunity*(self: ChatModel, communityId: string) =
|
|
|
|
|
status_chat.leaveCommunity(communityId)
|
|
|
|
|
|
|
|
|
|
proc inviteUserToCommunity*(self: ChatModel, communityId: string, pubKey: string) =
|
2021-09-09 05:51:07 +00:00
|
|
|
|
let response = status_chat.inviteUsersToCommunity(communityId, @[pubKey])
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc inviteUsersToCommunity*(self: ChatModel, communityId: string, pubKeys: seq[string]) =
|
2021-09-09 05:51:07 +00:00
|
|
|
|
let response = status_chat.inviteUsersToCommunity(communityId, pubKeys)
|
|
|
|
|
discard self.processMessageUpdateAfterSend(response)
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
proc removeUserFromCommunity*(self: ChatModel, communityId: string, pubKey: string) =
|
|
|
|
|
status_chat.removeUserFromCommunity(communityId, pubKey)
|
|
|
|
|
|
|
|
|
|
proc banUserFromCommunity*(self: ChatModel, pubKey: string, communityId: string): string =
|
|
|
|
|
return status_chat.banUserFromCommunity(pubKey, communityId)
|
|
|
|
|
|
|
|
|
|
proc exportCommunity*(self: ChatModel, communityId: string): string =
|
|
|
|
|
result = status_chat.exportCommunity(communityId)
|
|
|
|
|
|
|
|
|
|
proc importCommunity*(self: ChatModel, communityKey: string): string =
|
|
|
|
|
result = status_chat.importCommunity(communityKey)
|
|
|
|
|
|
|
|
|
|
proc requestToJoinCommunity*(self: ChatModel, communityKey: string, ensName: string): seq[CommunityMembershipRequest] =
|
|
|
|
|
status_chat.requestToJoinCommunity(communityKey, ensName)
|
|
|
|
|
|
|
|
|
|
proc acceptRequestToJoinCommunity*(self: ChatModel, requestId: string) =
|
|
|
|
|
status_chat.acceptRequestToJoinCommunity(requestId)
|
|
|
|
|
|
|
|
|
|
proc declineRequestToJoinCommunity*(self: ChatModel, requestId: string) =
|
|
|
|
|
status_chat.declineRequestToJoinCommunity(requestId)
|
|
|
|
|
|
|
|
|
|
proc pendingRequestsToJoinForCommunity*(self: ChatModel, communityKey: string): seq[CommunityMembershipRequest] =
|
|
|
|
|
result = status_chat.pendingRequestsToJoinForCommunity(communityKey)
|
|
|
|
|
|
|
|
|
|
proc setCommunityMuted*(self: ChatModel, communityId: string, muted: bool) =
|
|
|
|
|
status_chat.setCommunityMuted(communityId, muted)
|
|
|
|
|
|
|
|
|
|
proc myPendingRequestsToJoin*(self: ChatModel): seq[CommunityMembershipRequest] =
|
|
|
|
|
result = status_chat.myPendingRequestsToJoin()
|
|
|
|
|
|
|
|
|
|
proc setPinMessage*(self: ChatModel, messageId: string, chatId: string, pinned: bool) =
|
|
|
|
|
status_chat.setPinMessage(messageId, chatId, pinned)
|
|
|
|
|
|
|
|
|
|
proc activityCenterNotifications*(self: ChatModel, initialLoad: bool = true) =
|
|
|
|
|
# Notifications were already loaded, since cursor will
|
|
|
|
|
# be nil/empty if there are no more notifs
|
|
|
|
|
if(not initialLoad and self.activityCenterCursor == ""): return
|
|
|
|
|
|
|
|
|
|
let activityCenterNotificationsTuple = status_chat.activityCenterNotification(self.activityCenterCursor)
|
|
|
|
|
self.activityCenterCursor = activityCenterNotificationsTuple[0];
|
|
|
|
|
|
|
|
|
|
self.events.emit("activityCenterNotificationsLoaded", ActivityCenterNotificationsArgs(activityCenterNotifications: activityCenterNotificationsTuple[1]))
|
|
|
|
|
|
|
|
|
|
proc activityCenterNotifications*(self: ChatModel, cursor: string = "", activityCenterNotifications: seq[ActivityCenterNotification]) =
|
|
|
|
|
self.activityCenterCursor = cursor
|
|
|
|
|
|
|
|
|
|
self.events.emit("activityCenterNotificationsLoaded", ActivityCenterNotificationsArgs(activityCenterNotifications: activityCenterNotifications))
|
|
|
|
|
|
|
|
|
|
proc markAllActivityCenterNotificationsRead*(self: ChatModel): string =
|
|
|
|
|
try:
|
|
|
|
|
status_chat.markAllActivityCenterNotificationsRead()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error "Error marking all as read", msg = e.msg
|
|
|
|
|
result = e.msg
|
|
|
|
|
|
|
|
|
|
# This proc should accept ActivityCenterNotificationType in order to clear all notifications
|
|
|
|
|
# per type, that's why we have this part here. If we add all types to notificationsType that
|
|
|
|
|
# means that we need to clear all notifications for all types.
|
|
|
|
|
var types : seq[ActivityCenterNotificationType]
|
|
|
|
|
for t in ActivityCenterNotificationType:
|
|
|
|
|
types.add(t)
|
|
|
|
|
|
|
|
|
|
self.events.emit("markNotificationsAsRead", MarkAsReadNotificationProperties(notificationTypes: types))
|
|
|
|
|
|
|
|
|
|
proc markActivityCenterNotificationRead*(self: ChatModel, notificationId: string,
|
|
|
|
|
markAsReadProps: MarkAsReadNotificationProperties): string =
|
|
|
|
|
try:
|
|
|
|
|
status_chat.markActivityCenterNotificationsRead(@[notificationId])
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error "Error marking as read", msg = e.msg
|
|
|
|
|
result = e.msg
|
|
|
|
|
|
|
|
|
|
self.events.emit("markNotificationsAsRead", markAsReadProps)
|
|
|
|
|
|
2021-11-16 11:37:56 +00:00
|
|
|
|
proc markActivityCenterNotificationUnread*(self: ChatModel, notificationId: string,
|
|
|
|
|
markAsUnreadProps: MarkAsUnreadNotificationProperties): string =
|
|
|
|
|
try:
|
|
|
|
|
status_chat.markActivityCenterNotificationsUnread(@[notificationId])
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error "Error marking as unread", msg = e.msg
|
|
|
|
|
result = e.msg
|
|
|
|
|
|
|
|
|
|
self.events.emit("markNotificationsAsUnread", markAsUnreadProps)
|
|
|
|
|
|
2021-09-08 18:05:39 +00:00
|
|
|
|
proc acceptActivityCenterNotifications*(self: ChatModel, ids: seq[string]): string =
|
|
|
|
|
try:
|
|
|
|
|
let response = status_chat.acceptActivityCenterNotifications(ids)
|
|
|
|
|
|
|
|
|
|
let (chats, messages) = self.parseChatResponse(response)
|
|
|
|
|
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error "Error marking as accepted", msg = e.msg
|
|
|
|
|
result = e.msg
|
|
|
|
|
|
|
|
|
|
proc dismissActivityCenterNotifications*(self: ChatModel, ids: seq[string]): string =
|
|
|
|
|
try:
|
|
|
|
|
discard status_chat.dismissActivityCenterNotifications(ids)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error "Error marking as dismissed", msg = e.msg
|
|
|
|
|
result = e.msg
|
|
|
|
|
|
|
|
|
|
proc unreadActivityCenterNotificationsCount*(self: ChatModel): int =
|
|
|
|
|
status_chat.unreadActivityCenterNotificationsCount()
|
|
|
|
|
|
|
|
|
|
proc getLinkPreviewData*(link: string, success: var bool): JsonNode =
|
|
|
|
|
result = status_chat.getLinkPreviewData(link, success)
|
|
|
|
|
|
|
|
|
|
proc getCommunityIdForChat*(self: ChatModel, chatId: string): string =
|
|
|
|
|
if (not self.hasChannel(chatId)):
|
|
|
|
|
return ""
|
|
|
|
|
return self.channels[chatId].communityId
|
|
|
|
|
|
|
|
|
|
proc onAsyncSearchMessages*(self: ChatModel, response: string) =
|
|
|
|
|
let responseObj = response.parseJson
|
|
|
|
|
if (responseObj.kind != JObject):
|
|
|
|
|
info "search messages response is not an json object"
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var chatId: string
|
|
|
|
|
discard responseObj.getProp("chatId", chatId)
|
|
|
|
|
|
|
|
|
|
var messagesObj: JsonNode
|
|
|
|
|
if (not responseObj.getProp("messages", messagesObj)):
|
|
|
|
|
info "search messages response doesn't contain messages property"
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var messagesArray: JsonNode
|
|
|
|
|
if (not messagesObj.getProp("messages", messagesArray)):
|
|
|
|
|
info "search messages response doesn't contain messages array"
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var messages: seq[Message] = @[]
|
|
|
|
|
if (messagesArray.kind == JArray):
|
|
|
|
|
for jsonMsg in messagesArray:
|
|
|
|
|
messages.add(jsonMsg.toMessage())
|
|
|
|
|
|
|
|
|
|
self.events.emit("searchMessagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: messages))
|
|
|
|
|
|
|
|
|
|
proc onLoadMoreMessagesForChannel*(self: ChatModel, response: string) =
|
|
|
|
|
let responseObj = response.parseJson
|
|
|
|
|
if (responseObj.kind != JObject):
|
|
|
|
|
info "load more messages response is not an json object"
|
|
|
|
|
|
|
|
|
|
# notify view
|
|
|
|
|
self.events.emit("messagesLoaded", MsgsLoadedArgs())
|
|
|
|
|
self.events.emit("reactionsLoaded", ReactionsLoadedArgs())
|
|
|
|
|
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs())
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var chatId: string
|
|
|
|
|
discard responseObj.getProp("chatId", chatId)
|
|
|
|
|
|
|
|
|
|
# handling chat messages
|
|
|
|
|
var chatMessagesObj: JsonNode
|
|
|
|
|
var chatCursor: string
|
|
|
|
|
discard responseObj.getProp("messages", chatMessagesObj)
|
|
|
|
|
discard responseObj.getProp("messagesCursor", chatCursor)
|
|
|
|
|
|
|
|
|
|
self.msgCursor[chatId] = chatCursor
|
|
|
|
|
|
|
|
|
|
var messages: seq[Message] = @[]
|
|
|
|
|
if (chatMessagesObj.kind == JArray):
|
|
|
|
|
for jsonMsg in chatMessagesObj:
|
|
|
|
|
messages.add(jsonMsg.toMessage())
|
|
|
|
|
|
|
|
|
|
if messages.len > 0:
|
|
|
|
|
let lastMsgIndex = messages.len - 1
|
|
|
|
|
let ts = times.convert(Milliseconds, Seconds, messages[lastMsgIndex].whisperTimestamp.parseInt())
|
|
|
|
|
self.lastMessageTimestamps[chatId] = ts
|
|
|
|
|
|
|
|
|
|
# handling reactions
|
|
|
|
|
var reactionsObj: JsonNode
|
|
|
|
|
var reactionsCursor: string
|
|
|
|
|
discard responseObj.getProp("reactions", reactionsObj)
|
|
|
|
|
discard responseObj.getProp("reactionsCursor", reactionsCursor)
|
|
|
|
|
|
|
|
|
|
self.emojiCursor[chatId] = reactionsCursor;
|
|
|
|
|
|
|
|
|
|
var reactions: seq[Reaction] = @[]
|
|
|
|
|
if (reactionsObj.kind == JArray):
|
|
|
|
|
for jsonMsg in reactionsObj:
|
|
|
|
|
reactions.add(jsonMsg.toReaction)
|
|
|
|
|
|
|
|
|
|
# handling pinned messages
|
|
|
|
|
var pinnedMsgObj: JsonNode
|
|
|
|
|
var pinnedMsgCursor: string
|
|
|
|
|
discard responseObj.getProp("pinnedMessages", pinnedMsgObj)
|
|
|
|
|
discard responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)
|
|
|
|
|
|
|
|
|
|
self.pinnedMsgCursor[chatId] = pinnedMsgCursor
|
|
|
|
|
|
|
|
|
|
var pinnedMessages: seq[Message] = @[]
|
|
|
|
|
if (pinnedMsgObj.kind == JArray):
|
|
|
|
|
for jsonMsg in pinnedMsgObj:
|
|
|
|
|
var msgObj: JsonNode
|
|
|
|
|
if(jsonMsg.getProp("message", msgObj)):
|
|
|
|
|
var msg: Message
|
|
|
|
|
msg = msgObj.toMessage()
|
|
|
|
|
discard jsonMsg.getProp("pinnedBy", msg.pinnedBy)
|
|
|
|
|
pinnedMessages.add(msg)
|
|
|
|
|
|
|
|
|
|
# notify view
|
|
|
|
|
self.events.emit("messagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: messages))
|
|
|
|
|
self.events.emit("reactionsLoaded", ReactionsLoadedArgs(reactions: reactions))
|
|
|
|
|
self.events.emit("pinnedMessagesLoaded", MsgsLoadedArgs(chatId: chatId, messages: pinnedMessages))
|
|
|
|
|
|
|
|
|
|
proc userNameOrAlias*(self: ChatModel, pubKey: string,
|
|
|
|
|
prettyForm: bool = false): string =
|
|
|
|
|
## Returns ens name or alias, in case if prettyForm is true and ens name
|
|
|
|
|
## ends with ".stateofus.eth" that part will be removed.
|
|
|
|
|
var alias: string
|
2021-09-20 13:00:10 +00:00
|
|
|
|
let contacts = self.getContacts()
|
|
|
|
|
if contacts.hasKey(pubKey):
|
|
|
|
|
alias = ens.userNameOrAlias(contacts[pubKey])
|
2021-09-08 18:05:39 +00:00
|
|
|
|
else:
|
|
|
|
|
alias = generateAlias(pubKey)
|
|
|
|
|
|
|
|
|
|
if (prettyForm and alias.endsWith(".stateofus.eth")):
|
|
|
|
|
alias = alias[0 .. ^15]
|
|
|
|
|
|
|
|
|
|
return alias
|
|
|
|
|
|
|
|
|
|
proc chatName*(self: ChatModel, chatItem: Chat): string =
|
|
|
|
|
if (not chatItem.chatType.isOneToOne):
|
|
|
|
|
return chatItem.name
|
|
|
|
|
|
2021-09-20 13:00:10 +00:00
|
|
|
|
let contacts = self.getContacts()
|
|
|
|
|
if (contacts.hasKey(chatItem.id) and contacts[chatItem.id].hasNickname()):
|
|
|
|
|
return contacts[chatItem.id].localNickname
|
2021-09-08 18:05:39 +00:00
|
|
|
|
|
|
|
|
|
if chatItem.ensName != "":
|
|
|
|
|
return "@" & userName(chatItem.ensName).userName(true)
|
|
|
|
|
|
|
|
|
|
return self.userNameOrAlias(chatItem.id)
|
|
|
|
|
|
|
|
|
|
proc isMessageCursorSet*(self: ChatModel, channelId: string): bool =
|
|
|
|
|
self.msgCursor.hasKey(channelId)
|
|
|
|
|
|
|
|
|
|
proc getCurrentMessageCursor*(self: ChatModel, channelId: string): string =
|
|
|
|
|
if(not self.msgCursor.hasKey(channelId)):
|
|
|
|
|
self.msgCursor[channelId] = ""
|
|
|
|
|
|
|
|
|
|
return self.msgCursor[channelId]
|
|
|
|
|
|
|
|
|
|
proc isEmojiCursorSet*(self: ChatModel, channelId: string): bool =
|
|
|
|
|
self.emojiCursor.hasKey(channelId)
|
|
|
|
|
|
|
|
|
|
proc getCurrentEmojiCursor*(self: ChatModel, channelId: string): string =
|
|
|
|
|
if(not self.emojiCursor.hasKey(channelId)):
|
|
|
|
|
self.emojiCursor[channelId] = ""
|
|
|
|
|
|
|
|
|
|
return self.emojiCursor[channelId]
|
|
|
|
|
|
|
|
|
|
proc isPinnedMessageCursorSet*(self: ChatModel, channelId: string): bool =
|
|
|
|
|
self.pinnedMsgCursor.hasKey(channelId)
|
|
|
|
|
|
|
|
|
|
proc getCurrentPinnedMessageCursor*(self: ChatModel, channelId: string): string =
|
|
|
|
|
if(not self.pinnedMsgCursor.hasKey(channelId)):
|
|
|
|
|
self.pinnedMsgCursor[channelId] = ""
|
|
|
|
|
|
|
|
|
|
return self.pinnedMsgCursor[channelId]
|