fix: Keep only one place to store contacts and index (#37)

This commit is contained in:
Anthony Laibe 2021-09-20 15:00:10 +02:00 committed by GitHub
parent 3655396b57
commit 23db2d1216
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 81 deletions

View File

@ -1,5 +1,6 @@
import json, strutils, sequtils, tables, chronicles, times, sugar, algorithm
import statusgo_backend/chat as status_chat
import statusgo_backend/contacts as status_contacts
import statusgo_backend/chatCommands as status_chat_commands
import types/[message, status_update, activity_center_notification,
sticker, removed_message]
@ -31,7 +32,6 @@ type
chats*: seq[Chat]
messages*: seq[Message]
pinnedMessages*: seq[Message]
contacts*: seq[Profile]
emojiReactions*: seq[Reaction]
communities*: seq[Community]
communityMembershipRequests*: seq[CommunityMembershipRequest]
@ -80,7 +80,6 @@ type ChatModel* = ref object
events*: EventEmitter
communitiesToFetch*: seq[string]
mailserverReady*: bool
contacts*: Table[string, Profile]
channels*: Table[string, Chat]
msgCursor: Table[string, string]
pinnedMsgCursor: Table[string, string]
@ -93,13 +92,20 @@ proc newChatModel*(events: EventEmitter): ChatModel =
result.events = events
result.mailserverReady = false
result.communitiesToFetch = @[]
result.contacts = initTable[string, Profile]()
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]()
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
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
@ -113,7 +119,7 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
if self.lastMessageTimestamps[chatId] > ts:
self.lastMessageTimestamps[chatId] = ts
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chats, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications, statusUpdates: statusUpdates, deletedMessages: deletedMessages))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chats, emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications, statusUpdates: statusUpdates, deletedMessages: deletedMessages))
proc parseChatResponse(self: ChatModel, response: string): (seq[Chat], seq[Message]) =
var parsedResponse = parseJson(response)
@ -131,7 +137,7 @@ proc parseChatResponse(self: ChatModel, response: string): (seq[Chat], seq[Messa
proc emitUpdate(self: ChatModel, response: string) =
var (chats, messages) = self.parseChatResponse(response)
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode)
@ -225,11 +231,6 @@ proc createPublicChat*(self: ChatModel, chatId: string) =
self.emitTopicAndJoin(chat)
proc updateContacts*(self: ChatModel, contacts: seq[Profile]) =
for c in contacts:
self.contacts[c.id] = c
self.events.emit("chatUpdate", ChatUpdateArgs(contacts: contacts))
proc requestMissingCommunityInfos*(self: ChatModel) =
if (self.communitiesToFetch.len == 0):
return
@ -252,7 +253,8 @@ proc sortChats(x, y: chat_type.Chat): int =
proc init*(self: ChatModel, pubKey: string) =
self.publicKey = pubKey
var contacts = getAddedContacts()
var (contacts, _) = status_contacts.getContacts()
contacts = contacts.filter(c => c.systemTags.contains(contactAdded))
var chatList = status_chat.loadChats()
chatList.sort(sortChats)
@ -295,15 +297,10 @@ proc init*(self: ChatModel, pubKey: string) =
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
self.events.once("mailserverAvailable") do(a: Args):
self.mailserverReady = true
self.requestMissingCommunityInfos()
self.events.on("contactUpdate") do(a: Args):
var evArgs = ContactUpdateArgs(a)
self.updateContacts(evArgs.contacts)
proc statusUpdates*(self: ChatModel) =
let statusUpdates = status_chat.statusUpdates()
self.events.emit("messagesLoaded", MsgsLoadedArgs(statusUpdates: statusUpdates))
@ -369,7 +366,7 @@ proc sendSticker*(self: ChatModel, chatId: string, replyTo: string, sticker: Sti
var response = status_chat.sendStickerMessage(chatId, replyTo, sticker)
self.events.emit("stickerSent", StickerArgs(sticker: sticker, save: true))
var (chats, messages) = self.parseChatResponse(response)
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
self.events.emit("sendingMessage", MessageArgs(id: messages[0].id, channel: messages[0].chatId))
proc addEmojiReaction*(self: ChatModel, chatId: string, messageId: string, emojiId: int) =
@ -385,7 +382,7 @@ proc onMarkMessagesRead(self: ChatModel, response: string, chatId: string): Json
if self.channels.hasKey(chatId):
self.channels[chatId].unviewedMessagesCount = 0
self.channels[chatId].mentionsCount = 0
self.events.emit("channelUpdate", ChatUpdateArgs(messages: @[], chats: @[self.channels[chatId]], contacts: @[]))
self.events.emit("channelUpdate", ChatUpdateArgs(messages: @[], chats: @[self.channels[chatId]]))
proc onAsyncMarkMessagesRead*(self: ChatModel, response: string) =
let parsedResponse = parseJson(response)
@ -408,8 +405,9 @@ proc renameGroup*(self: ChatModel, chatId: string, newName: string) =
self.emitUpdate(response)
proc getUserName*(self: ChatModel, id: string, defaultUserName: string):string =
if(self.contacts.hasKey(id)):
return userNameOrAlias(self.contacts[id])
let contacts = self.getContacts()
if(contacts.hasKey(id)):
return userNameOrAlias(contacts[id])
else:
return defaultUserName
@ -418,7 +416,7 @@ proc processGroupChatCreation*(self: ChatModel, result: string) =
var (chats, messages) = formatChatUpdate(response)
let chat = chats[0]
self.channels[chat.id] = chat
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
self.events.emit("activeChannelChanged", ChatIdArg(chatId: chat.id))
proc createGroup*(self: ChatModel, groupName: string, pubKeys: seq[string]) =
@ -446,11 +444,11 @@ proc resendMessage*(self: ChatModel, messageId: string) =
proc muteChat*(self: ChatModel, chat: Chat) =
discard status_chat.muteChat(chat.id)
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat], contacts: @[]))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat]))
proc unmuteChat*(self: ChatModel, chat: Chat) =
discard status_chat.unmuteChat(chat.id)
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat], contacts: @[]))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: @[], chats: @[chat]))
proc processUpdateForTransaction*(self: ChatModel, messageId: string, response: string) =
var (chats, messages) = self.processMessageUpdateAfterSend(response)
@ -741,8 +739,9 @@ proc userNameOrAlias*(self: ChatModel, pubKey: 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
if self.contacts.hasKey(pubKey):
alias = ens.userNameOrAlias(self.contacts[pubKey])
let contacts = self.getContacts()
if contacts.hasKey(pubKey):
alias = ens.userNameOrAlias(contacts[pubKey])
else:
alias = generateAlias(pubKey)
@ -755,9 +754,9 @@ proc chatName*(self: ChatModel, chatItem: Chat): string =
if (not chatItem.chatType.isOneToOne):
return chatItem.name
if (self.contacts.hasKey(chatItem.id) and
self.contacts[chatItem.id].hasNickname()):
return self.contacts[chatItem.id].localNickname
let contacts = self.getContacts()
if (contacts.hasKey(chatItem.id) and contacts[chatItem.id].hasNickname()):
return contacts[chatItem.id].localNickname
if chatItem.ensName != "":
return "@" & userName(chatItem.ensName).userName(true)

View File

@ -1,4 +1,4 @@
import json, sequtils, sugar, chronicles
import json, chronicles
import statusgo_backend/contacts as status_contacts
import statusgo_backend/accounts as status_accounts
import statusgo_backend/chat as status_chat
@ -19,6 +19,13 @@ proc newContactModel*(events: EventEmitter): ContactModel =
result = ContactModel()
result.events = events
proc saveContact(self: ContactModel, contact: Profile): string =
var thumbnail = ""
if contact.identityImage != nil:
thumbnail = contact.identityImage.thumbnail
return status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, contact.localNickname)
proc getContactByID*(self: ContactModel, id: string): Profile =
let response = status_contacts.getContactByID(id)
# TODO: change to options
@ -31,24 +38,21 @@ proc getContactByID*(self: ContactModel, id: string): Profile =
proc blockContact*(self: ContactModel, id: string): string =
var contact = self.getContactByID(id)
contact.systemTags.add(contactBlocked)
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, contact.identityImage.thumbnail, contact.systemTags, contact.localNickname)
discard self.saveContact(contact)
self.events.emit("contactBlocked", Args())
proc unblockContact*(self: ContactModel, id: string): string =
var contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactBlocked))
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, contact.identityImage.thumbnail, contact.systemTags, contact.localNickname)
discard self.saveContact(contact)
self.events.emit("contactUnblocked", Args())
proc getAllContacts*(): seq[Profile] =
result = map(status_contacts.getContacts().getElems(), proc(x: JsonNode): Profile = x.toProfileModel())
proc getContacts*(self: ContactModel, useCache: bool = true): seq[Profile] =
let (contacts, usedCache) = status_contacts.getContacts(useCache)
if not usedCache:
self.events.emit("contactUpdate", ContactUpdateArgs(contacts: contacts))
proc getAddedContacts*(): seq[Profile] =
result = getAllContacts().filter(c => c.systemTags.contains(contactAdded))
proc getContacts*(self: ContactModel): seq[Profile] =
result = getAllContacts()
self.events.emit("contactUpdate", ContactUpdateArgs(contacts: result))
return contacts
proc getOrCreateContact*(self: ContactModel, id: string): Profile =
result = self.getContactByID(id)
@ -66,7 +70,7 @@ proc getOrCreateContact*(self: ContactModel, id: string): Profile =
systemTags: @[]
)
proc setNickName*(self: ContactModel, id: string, localNickname: string): string =
proc setNickName*(self: ContactModel, id: string, localNickname: string, accountKeyUID: string): string =
var contact = self.getOrCreateContact(id)
let nickname =
if (localNickname == ""):
@ -76,18 +80,16 @@ proc setNickName*(self: ContactModel, id: string, localNickname: string): string
else:
localNickname
var thumbnail = ""
if contact.identityImage != nil:
thumbnail = contact.identityImage.thumbnail
result = status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, nickname)
contact.localNickname = nickname
result = self.saveContact(contact)
self.events.emit("contactAdded", Args())
discard requestContactUpdate(contact.id)
discard sendContactUpdate(contact.id, accountKeyUID)
proc addContact*(self: ContactModel, id: string): string =
proc addContact*(self: ContactModel, id: string, accountKeyUID: string): string =
var contact = self.getOrCreateContact(id)
let updating = contact.systemTags.contains(contactAdded)
if not updating:
contact.systemTags.add(contactAdded)
discard status_chat.createProfileChat(contact.id)
@ -96,13 +98,9 @@ proc addContact*(self: ContactModel, id: string): string =
if (index > -1):
contact.systemTags.delete(index)
var thumbnail = ""
if contact.identityImage != nil:
thumbnail = contact.identityImage.thumbnail
result = status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, contact.localNickname)
result = self.saveContact(contact)
self.events.emit("contactAdded", Args())
discard requestContactUpdate(contact.id)
discard sendContactUpdate(contact.id, accountKeyUID)
if updating:
let profile = Profile(
@ -122,11 +120,7 @@ proc removeContact*(self: ContactModel, id: string) =
let contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactAdded))
var thumbnail = ""
if contact.identityImage != nil:
thumbnail = contact.identityImage.thumbnail
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, contact.localNickname)
discard self.saveContact(contact)
self.events.emit("contactRemoved", Args())
proc isAdded*(self: ContactModel, id: string): bool =
@ -143,9 +137,5 @@ proc rejectContactRequest*(self: ContactModel, id: string) =
let contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactRequest))
var thumbnail = ""
if contact.identityImage != nil:
thumbnail = contact.identityImage.thumbnail
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, contact.localNickname)
discard self.saveContact(contact)
self.events.emit("contactRemoved", Args())

View File

@ -1,8 +1,9 @@
import json, strmisc, atomics
import core, ../utils
import tables, json, strmisc, atomics, sugar, sequtils, json_serialization, chronicles
import ./core, ./settings, ./accounts, ../utils, ../types/[profile, setting]
var
contacts {.threadvar.}: JsonNode
contacts {.threadvar.}: seq[Profile]
contactsIndex {.threadvar.}: Table[string, Profile]
contactsInited {.threadvar.}: bool
dirty: Atomic[bool]
@ -10,20 +11,35 @@ proc getContactByID*(id: string): string =
result = callPrivateRPC("getContactByID".prefix, %* [id])
dirty.store(true)
proc getContacts*(): JsonNode =
let cacheIsDirty = (not contactsInited) or dirty.load
proc getContacts*(useCache: bool = true): (seq[Profile], bool) =
let cacheIsDirty = (not useCache) or (not contactsInited) or dirty.load
if not cacheIsDirty:
result = contacts
return (contacts, true)
let payload = %* []
let response = callPrivateRPC("contacts".prefix, payload).parseJson
dirty.store(false)
contactsIndex = initTable[string, Profile]()
contactsInited = true
if response["result"].kind == JNull:
contacts = @[]
return (contacts, false)
contacts = map(response["result"].getElems(), proc(x: JsonNode): Profile = x.toProfileModel())
for contact in contacts:
contactsIndex[contact.id] = contact
return (contacts, false)
proc getContactsIndex*(): (Table[string, Profile], bool)=
let cacheIsDirty = (not contactsInited) or dirty.load
if not cacheIsDirty:
return (contactsIndex, true)
else:
let payload = %* []
let response = callPrivateRPC("contacts".prefix, payload).parseJson
if response["result"].kind == JNull:
result = %* []
else:
result = response["result"]
dirty.store(false)
contacts = result
contactsInited = true
discard getContacts()
return (contactsIndex, false)
proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string, identicon: string, thumbnail: string, systemTags: seq[string], localNickname: string): string =
let payload = %* [{
@ -40,6 +56,15 @@ proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string,
result = callPrivateRPC("saveContact".prefix, payload)
dirty.store(true)
proc requestContactUpdate*(publicKey: string): string =
result = callPrivateRPC("sendContactUpdate".prefix, %* [publicKey, "", ""])
proc sendContactUpdate*(publicKey: string, accountKeyUID: string) : string =
let preferredUsername = getSetting[string](Setting.PreferredUsername, "")
let usernames = getSetting[seq[string]](Setting.Usernames, @[])
var ensName = ""
if len(preferredUsername) > 0:
ensName = preferredUsername
elif len(usernames) >= 1:
ensName = usernames[0]
let identityImage = getIdentityImage(accountKeyUID)
result = callPrivateRPC("sendContactUpdate".prefix, %* [publicKey, ensName, identityImage.thumbnail])
dirty.store(true)

View File

@ -16,7 +16,7 @@ type Profile* = ref object
systemTags*: seq[string]
proc `$`*(self: Profile): string =
return fmt"Profile(id:{self.id}, username:{self.username})"
return fmt"Profile(id:{self.id}, username:{self.username}, systemTags: {self.systemTags}, ensName: {self.ensName})"
proc toProfileModel*(profile: JsonNode): Profile =
var systemTags: seq[string] = @[]