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 json, strutils, sequtils, tables, chronicles, times, sugar, algorithm
import statusgo_backend/chat as status_chat import statusgo_backend/chat as status_chat
import statusgo_backend/contacts as status_contacts
import statusgo_backend/chatCommands as status_chat_commands import statusgo_backend/chatCommands as status_chat_commands
import types/[message, status_update, activity_center_notification, import types/[message, status_update, activity_center_notification,
sticker, removed_message] sticker, removed_message]
@ -31,7 +32,6 @@ type
chats*: seq[Chat] chats*: seq[Chat]
messages*: seq[Message] messages*: seq[Message]
pinnedMessages*: seq[Message] pinnedMessages*: seq[Message]
contacts*: seq[Profile]
emojiReactions*: seq[Reaction] emojiReactions*: seq[Reaction]
communities*: seq[Community] communities*: seq[Community]
communityMembershipRequests*: seq[CommunityMembershipRequest] communityMembershipRequests*: seq[CommunityMembershipRequest]
@ -80,7 +80,6 @@ type ChatModel* = ref object
events*: EventEmitter events*: EventEmitter
communitiesToFetch*: seq[string] communitiesToFetch*: seq[string]
mailserverReady*: bool mailserverReady*: bool
contacts*: Table[string, Profile]
channels*: Table[string, Chat] channels*: Table[string, Chat]
msgCursor: Table[string, string] msgCursor: Table[string, string]
pinnedMsgCursor: Table[string, string] pinnedMsgCursor: Table[string, string]
@ -93,13 +92,20 @@ proc newChatModel*(events: EventEmitter): ChatModel =
result.events = events result.events = events
result.mailserverReady = false result.mailserverReady = false
result.communitiesToFetch = @[] result.communitiesToFetch = @[]
result.contacts = initTable[string, Profile]()
result.channels = initTable[string, Chat]() result.channels = initTable[string, Chat]()
result.msgCursor = initTable[string, string]() result.msgCursor = initTable[string, string]()
result.pinnedMsgCursor = initTable[string, string]() result.pinnedMsgCursor = initTable[string, string]()
result.emojiCursor = initTable[string, string]() result.emojiCursor = initTable[string, string]()
result.lastMessageTimestamps = initTable[string, int64]() 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]) = 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: for chat in chats:
self.channels[chat.id] = chat 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: if self.lastMessageTimestamps[chatId] > ts:
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]) = proc parseChatResponse(self: ChatModel, response: string): (seq[Chat], seq[Message]) =
var parsedResponse = parseJson(response) var parsedResponse = parseJson(response)
@ -131,7 +137,7 @@ proc parseChatResponse(self: ChatModel, response: string): (seq[Chat], seq[Messa
proc emitUpdate(self: ChatModel, response: string) = proc emitUpdate(self: ChatModel, response: string) =
var (chats, messages) = self.parseChatResponse(response) 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) proc removeFiltersByChatId(self: ChatModel, chatId: string, filters: JsonNode)
@ -225,11 +231,6 @@ proc createPublicChat*(self: ChatModel, chatId: string) =
self.emitTopicAndJoin(chat) 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) = proc requestMissingCommunityInfos*(self: ChatModel) =
if (self.communitiesToFetch.len == 0): if (self.communitiesToFetch.len == 0):
return return
@ -252,7 +253,8 @@ proc sortChats(x, y: chat_type.Chat): int =
proc init*(self: ChatModel, pubKey: string) = proc init*(self: ChatModel, pubKey: string) =
self.publicKey = pubKey self.publicKey = pubKey
var contacts = getAddedContacts() var (contacts, _) = status_contacts.getContacts()
contacts = contacts.filter(c => c.systemTags.contains(contactAdded))
var chatList = status_chat.loadChats() var chatList = status_chat.loadChats()
chatList.sort(sortChats) chatList.sort(sortChats)
@ -295,15 +297,10 @@ proc init*(self: ChatModel, pubKey: string) =
self.events.emit("chatsLoaded", ChatArgs(chats: chatList)) self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
self.events.once("mailserverAvailable") do(a: Args): self.events.once("mailserverAvailable") do(a: Args):
self.mailserverReady = true self.mailserverReady = true
self.requestMissingCommunityInfos() self.requestMissingCommunityInfos()
self.events.on("contactUpdate") do(a: Args):
var evArgs = ContactUpdateArgs(a)
self.updateContacts(evArgs.contacts)
proc statusUpdates*(self: ChatModel) = proc statusUpdates*(self: ChatModel) =
let statusUpdates = status_chat.statusUpdates() let statusUpdates = status_chat.statusUpdates()
self.events.emit("messagesLoaded", MsgsLoadedArgs(statusUpdates: 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) var response = status_chat.sendStickerMessage(chatId, replyTo, sticker)
self.events.emit("stickerSent", StickerArgs(sticker: sticker, save: true)) self.events.emit("stickerSent", StickerArgs(sticker: sticker, save: true))
var (chats, messages) = self.parseChatResponse(response) 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)) self.events.emit("sendingMessage", MessageArgs(id: messages[0].id, channel: messages[0].chatId))
proc addEmojiReaction*(self: ChatModel, chatId: string, messageId: string, emojiId: int) = 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): if self.channels.hasKey(chatId):
self.channels[chatId].unviewedMessagesCount = 0 self.channels[chatId].unviewedMessagesCount = 0
self.channels[chatId].mentionsCount = 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) = proc onAsyncMarkMessagesRead*(self: ChatModel, response: string) =
let parsedResponse = parseJson(response) let parsedResponse = parseJson(response)
@ -408,8 +405,9 @@ proc renameGroup*(self: ChatModel, chatId: string, newName: string) =
self.emitUpdate(response) self.emitUpdate(response)
proc getUserName*(self: ChatModel, id: string, defaultUserName: string):string = proc getUserName*(self: ChatModel, id: string, defaultUserName: string):string =
if(self.contacts.hasKey(id)): let contacts = self.getContacts()
return userNameOrAlias(self.contacts[id]) if(contacts.hasKey(id)):
return userNameOrAlias(contacts[id])
else: else:
return defaultUserName return defaultUserName
@ -418,7 +416,7 @@ proc processGroupChatCreation*(self: ChatModel, result: string) =
var (chats, messages) = formatChatUpdate(response) var (chats, messages) = formatChatUpdate(response)
let chat = chats[0] let chat = chats[0]
self.channels[chat.id] = chat 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)) self.events.emit("activeChannelChanged", ChatIdArg(chatId: chat.id))
proc createGroup*(self: ChatModel, groupName: string, pubKeys: seq[string]) = 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) = proc muteChat*(self: ChatModel, chat: Chat) =
discard status_chat.muteChat(chat.id) 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) = proc unmuteChat*(self: ChatModel, chat: Chat) =
discard status_chat.unmuteChat(chat.id) 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) = proc processUpdateForTransaction*(self: ChatModel, messageId: string, response: string) =
var (chats, messages) = self.processMessageUpdateAfterSend(response) 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 ## Returns ens name or alias, in case if prettyForm is true and ens name
## ends with ".stateofus.eth" that part will be removed. ## ends with ".stateofus.eth" that part will be removed.
var alias: string var alias: string
if self.contacts.hasKey(pubKey): let contacts = self.getContacts()
alias = ens.userNameOrAlias(self.contacts[pubKey]) if contacts.hasKey(pubKey):
alias = ens.userNameOrAlias(contacts[pubKey])
else: else:
alias = generateAlias(pubKey) alias = generateAlias(pubKey)
@ -755,9 +754,9 @@ proc chatName*(self: ChatModel, chatItem: Chat): string =
if (not chatItem.chatType.isOneToOne): if (not chatItem.chatType.isOneToOne):
return chatItem.name return chatItem.name
if (self.contacts.hasKey(chatItem.id) and let contacts = self.getContacts()
self.contacts[chatItem.id].hasNickname()): if (contacts.hasKey(chatItem.id) and contacts[chatItem.id].hasNickname()):
return self.contacts[chatItem.id].localNickname return contacts[chatItem.id].localNickname
if chatItem.ensName != "": if chatItem.ensName != "":
return "@" & userName(chatItem.ensName).userName(true) 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/contacts as status_contacts
import statusgo_backend/accounts as status_accounts import statusgo_backend/accounts as status_accounts
import statusgo_backend/chat as status_chat import statusgo_backend/chat as status_chat
@ -19,6 +19,13 @@ proc newContactModel*(events: EventEmitter): ContactModel =
result = ContactModel() result = ContactModel()
result.events = events 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 = proc getContactByID*(self: ContactModel, id: string): Profile =
let response = status_contacts.getContactByID(id) let response = status_contacts.getContactByID(id)
# TODO: change to options # TODO: change to options
@ -31,24 +38,21 @@ proc getContactByID*(self: ContactModel, id: string): Profile =
proc blockContact*(self: ContactModel, id: string): string = proc blockContact*(self: ContactModel, id: string): string =
var contact = self.getContactByID(id) var contact = self.getContactByID(id)
contact.systemTags.add(contactBlocked) 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()) self.events.emit("contactBlocked", Args())
proc unblockContact*(self: ContactModel, id: string): string = proc unblockContact*(self: ContactModel, id: string): string =
var contact = self.getContactByID(id) var contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactBlocked)) 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()) self.events.emit("contactUnblocked", Args())
proc getAllContacts*(): seq[Profile] = proc getContacts*(self: ContactModel, useCache: bool = true): seq[Profile] =
result = map(status_contacts.getContacts().getElems(), proc(x: JsonNode): Profile = x.toProfileModel()) let (contacts, usedCache) = status_contacts.getContacts(useCache)
if not usedCache:
self.events.emit("contactUpdate", ContactUpdateArgs(contacts: contacts))
proc getAddedContacts*(): seq[Profile] = return contacts
result = getAllContacts().filter(c => c.systemTags.contains(contactAdded))
proc getContacts*(self: ContactModel): seq[Profile] =
result = getAllContacts()
self.events.emit("contactUpdate", ContactUpdateArgs(contacts: result))
proc getOrCreateContact*(self: ContactModel, id: string): Profile = proc getOrCreateContact*(self: ContactModel, id: string): Profile =
result = self.getContactByID(id) result = self.getContactByID(id)
@ -66,7 +70,7 @@ proc getOrCreateContact*(self: ContactModel, id: string): Profile =
systemTags: @[] 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) var contact = self.getOrCreateContact(id)
let nickname = let nickname =
if (localNickname == ""): if (localNickname == ""):
@ -76,18 +80,16 @@ proc setNickName*(self: ContactModel, id: string, localNickname: string): string
else: else:
localNickname localNickname
var thumbnail = "" contact.localNickname = nickname
if contact.identityImage != nil: result = self.saveContact(contact)
thumbnail = contact.identityImage.thumbnail
result = status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, nickname)
self.events.emit("contactAdded", Args()) self.events.emit("contactAdded", Args())
discard requestContactUpdate(contact.id) discard sendContactUpdate(contact.id, accountKeyUID)
proc addContact*(self: ContactModel, id: string, accountKeyUID: string): string =
proc addContact*(self: ContactModel, id: string): string =
var contact = self.getOrCreateContact(id) var contact = self.getOrCreateContact(id)
let updating = contact.systemTags.contains(contactAdded) let updating = contact.systemTags.contains(contactAdded)
if not updating: if not updating:
contact.systemTags.add(contactAdded) contact.systemTags.add(contactAdded)
discard status_chat.createProfileChat(contact.id) discard status_chat.createProfileChat(contact.id)
@ -96,13 +98,9 @@ proc addContact*(self: ContactModel, id: string): string =
if (index > -1): if (index > -1):
contact.systemTags.delete(index) contact.systemTags.delete(index)
var thumbnail = "" result = self.saveContact(contact)
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)
self.events.emit("contactAdded", Args()) self.events.emit("contactAdded", Args())
discard requestContactUpdate(contact.id) discard sendContactUpdate(contact.id, accountKeyUID)
if updating: if updating:
let profile = Profile( let profile = Profile(
@ -122,11 +120,7 @@ proc removeContact*(self: ContactModel, id: string) =
let contact = self.getContactByID(id) let contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactAdded)) contact.systemTags.delete(contact.systemTags.find(contactAdded))
var thumbnail = "" discard self.saveContact(contact)
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)
self.events.emit("contactRemoved", Args()) self.events.emit("contactRemoved", Args())
proc isAdded*(self: ContactModel, id: string): bool = proc isAdded*(self: ContactModel, id: string): bool =
@ -143,9 +137,5 @@ proc rejectContactRequest*(self: ContactModel, id: string) =
let contact = self.getContactByID(id) let contact = self.getContactByID(id)
contact.systemTags.delete(contact.systemTags.find(contactRequest)) contact.systemTags.delete(contact.systemTags.find(contactRequest))
var thumbnail = "" discard self.saveContact(contact)
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)
self.events.emit("contactRemoved", Args()) self.events.emit("contactRemoved", Args())

View File

@ -1,8 +1,9 @@
import json, strmisc, atomics import tables, json, strmisc, atomics, sugar, sequtils, json_serialization, chronicles
import core, ../utils import ./core, ./settings, ./accounts, ../utils, ../types/[profile, setting]
var var
contacts {.threadvar.}: JsonNode contacts {.threadvar.}: seq[Profile]
contactsIndex {.threadvar.}: Table[string, Profile]
contactsInited {.threadvar.}: bool contactsInited {.threadvar.}: bool
dirty: Atomic[bool] dirty: Atomic[bool]
@ -10,20 +11,35 @@ proc getContactByID*(id: string): string =
result = callPrivateRPC("getContactByID".prefix, %* [id]) result = callPrivateRPC("getContactByID".prefix, %* [id])
dirty.store(true) dirty.store(true)
proc getContacts*(): JsonNode = proc getContacts*(useCache: bool = true): (seq[Profile], bool) =
let cacheIsDirty = (not contactsInited) or dirty.load let cacheIsDirty = (not useCache) or (not contactsInited) or dirty.load
if not cacheIsDirty: 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: else:
let payload = %* [] discard getContacts()
let response = callPrivateRPC("contacts".prefix, payload).parseJson return (contactsIndex, false)
if response["result"].kind == JNull:
result = %* []
else:
result = response["result"]
dirty.store(false)
contacts = result
contactsInited = true
proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string, identicon: string, thumbnail: string, systemTags: seq[string], localNickname: string): string = proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string, identicon: string, thumbnail: string, systemTags: seq[string], localNickname: string): string =
let payload = %* [{ let payload = %* [{
@ -40,6 +56,15 @@ proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string,
result = callPrivateRPC("saveContact".prefix, payload) result = callPrivateRPC("saveContact".prefix, payload)
dirty.store(true) dirty.store(true)
proc requestContactUpdate*(publicKey: string): string = proc sendContactUpdate*(publicKey: string, accountKeyUID: string) : string =
result = callPrivateRPC("sendContactUpdate".prefix, %* [publicKey, "", ""]) 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) dirty.store(true)

View File

@ -16,7 +16,7 @@ type Profile* = ref object
systemTags*: seq[string] systemTags*: seq[string]
proc `$`*(self: Profile): 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 = proc toProfileModel*(profile: JsonNode): Profile =
var systemTags: seq[string] = @[] var systemTags: seq[string] = @[]