feat(@desktop): use emoji hash and identicon ring

Closes: #4782
This commit is contained in:
Patryk Osmaczko 2022-03-09 11:27:32 +01:00 committed by osmaczko
parent ade6a22fda
commit e130953634
45 changed files with 736 additions and 490 deletions

View File

@ -30,6 +30,7 @@ import ../../app_service/service/devices/service as devices_service
import ../../app_service/service/mailservers/service as mailservers_service import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service import ../../app_service/service/gif/service as gif_service
import ../../app_service/service/ens/service as ens_service import ../../app_service/service/ens/service as ens_service
import ../../app_service/service/visual_identity/service as visual_identity_service
import ../modules/startup/module as startup_module import ../modules/startup/module as startup_module
import ../modules/main/module as main_module import ../modules/main/module as main_module
@ -83,6 +84,7 @@ type
nodeService: node_service.Service nodeService: node_service.Service
gifService: gif_service.Service gifService: gif_service.Service
ensService: ens_service.Service ensService: ens_service.Service
visualIdentityService: visual_identity_service.Service
# Modules # Modules
startupModule: startup_module.AccessInterface startupModule: startup_module.AccessInterface
@ -179,6 +181,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.settingsService, result.walletAccountService, result.transactionService, result.ethService, result.settingsService, result.walletAccountService, result.transactionService, result.ethService,
result.networkService, result.tokenService) result.networkService, result.tokenService)
result.providerService = provider_service.newService(result.ensService) result.providerService = provider_service.newService(result.ensService)
result.visualIdentityService = visual_identity_service.newService()
# Modules # Modules
result.startupModule = startup_module.newModule[AppController]( result.startupModule = startup_module.newModule[AppController](
@ -219,6 +222,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.gifService, result.gifService,
result.ensService, result.ensService,
result.networkService, result.networkService,
result.visualIdentityService
) )
# Do connections # Do connections
@ -267,6 +271,7 @@ proc delete*(self: AppController) =
self.generalService.delete self.generalService.delete
self.ensService.delete self.ensService.delete
self.gifService.delete self.gifService.delete
self.visualIdentityService.delete
proc startupDidLoad*(self: AppController) = proc startupDidLoad*(self: AppController) =
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant) singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
@ -336,7 +341,8 @@ proc load(self: AppController) =
self.communityService, self.communityService,
self.messageService, self.messageService,
self.gifService, self.gifService,
self.mailserversService self.mailserversService,
self.visualIdentityService,
) )
proc userLoggedIn*(self: AppController) = proc userLoggedIn*(self: AppController) =

View File

@ -20,6 +20,7 @@ import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/gif/service as gif_service import ../../../../../app_service/service/gif/service as gif_service
import ../../../../../app_service/service/message/service as message_service import ../../../../../app_service/service/message/service as message_service
import ../../../../../app_service/service/mailservers/service as mailservers_service import ../../../../../app_service/service/mailservers/service as mailservers_service
import ../../../../../app_service/service/visual_identity/service as visual_identity_service
export io_interface export io_interface
@ -41,7 +42,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
belongsToCommunity: bool, isUsersListAvailable: bool, settingsService: settings_service.ServiceInterface, belongsToCommunity: bool, isUsersListAvailable: bool, settingsService: settings_service.ServiceInterface,
contactService: contact_service.Service, chatService: chat_service.Service, contactService: contact_service.Service, chatService: chat_service.Service,
communityService: community_service.Service, messageService: message_service.Service, gifService: gif_service.Service, communityService: community_service.Service, messageService: message_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service): mailserversService: mailservers_service.Service, visualIdentityService: visual_identity_service.Service):
Module = Module =
result = Module() result = Module()
result.delegate = delegate result.delegate = delegate
@ -56,7 +57,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
contactService, communityService, chatService, messageService, mailserversService) contactService, communityService, chatService, messageService, mailserversService)
result.usersModule = users_module.newModule( result.usersModule = users_module.newModule(
result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable, result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,
contactService, chat_service, communityService, messageService, contactService, chat_service, communityService, messageService, visualIdentityService
) )
method delete*(self: Module) = method delete*(self: Module) =

View File

@ -6,7 +6,7 @@ import ../../../../../../app_service/service/contacts/service as contact_service
import ../../../../../../app_service/service/community/service as community_service import ../../../../../../app_service/service/community/service as community_service
import ../../../../../../app_service/service/message/service as message_service import ../../../../../../app_service/service/message/service as message_service
import ../../../../../../app_service/service/chat/service as chat_service import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/visual_identity/service as visual_identity_service
import ../../../../../core/eventemitter import ../../../../../core/eventemitter
@ -24,12 +24,13 @@ type
chatService: chat_service.Service chatService: chat_service.Service
communityService: community_service.Service communityService: community_service.Service
messageService: message_service.Service messageService: message_service.Service
visualIdentityService: visual_identity_service.Service
proc newController*( proc newController*(
delegate: io_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string, delegate: io_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service, belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
chatService: chat_service.Service, communityService: community_service.Service, chatService: chat_service.Service, communityService: community_service.Service,
messageService: message_service.Service messageService: message_service.Service, visualIdentityService: visual_identity_service.Service
): Controller = ): Controller =
result = Controller() result = Controller()
result.delegate = delegate result.delegate = delegate
@ -43,6 +44,7 @@ proc newController*(
result.communityService = communityService result.communityService = communityService
result.messageService = messageService result.messageService = messageService
result.chatService = chatService result.chatService = chatService
result.visualIdentityService = visualIdentityService
method delete*(self: Controller) = method delete*(self: Controller) =
discard discard
@ -153,3 +155,9 @@ method getContactDetails*(self: Controller, contactId: string): ContactDetails =
method getStatusForContact*(self: Controller, contactId: string): StatusUpdateDto = method getStatusForContact*(self: Controller, contactId: string): StatusUpdateDto =
return self.contactService.getStatusForContactWithId(contactId) return self.contactService.getStatusForContactWithId(contactId)
method getEmojiHash*(self: Controller, pubkey: string): EmojiHashDto =
return self.visual_identity_service.emojiHashOf(pubkey)
method getColorHash*(self: Controller, pubkey: string): ColorHashDto =
return self.visual_identity_service.colorHashOf(pubkey)

View File

@ -1,5 +1,6 @@
import ../../../../../../app_service/service/contacts/service as contacts_service import ../../../../../../app_service/service/contacts/service as contacts_service
import ../../../../../../app_service/service/chat/service as chat_service import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/visual_identity/service
type type
AccessInterface* {.pure inheritable.} = ref object of RootObj AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -32,3 +33,9 @@ method getChat*(self: AccessInterface): ChatDto {.base.} =
method getChatMemberInfo*(self: AccessInterface, id: string): (bool, bool) = method getChatMemberInfo*(self: AccessInterface, id: string): (bool, bool) =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getEmojiHash*(self: AccessInterface, pubkey: string): EmojiHashDto {.base.} =
raise newException(ValueError, "No implementation available")
method getColorHash*(self: AccessInterface, pubkey: string): ColorHashDto {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -9,6 +9,7 @@ import ../../../../../../app_service/service/contacts/service as contact_service
import ../../../../../../app_service/service/chat/service as chat_service import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/community/service as community_service import ../../../../../../app_service/service/community/service as community_service
import ../../../../../../app_service/service/message/service as message_service import ../../../../../../app_service/service/message/service as message_service
import ../../../../../../app_service/service/visual_identity/service as visual_identity_service
export io_interface export io_interface
@ -24,7 +25,7 @@ proc newModule*(
delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string, delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service, belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
chatService: chat_service.Service, communityService: community_service.Service, chatService: chat_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service, visualIdentityService: visual_identity_service.Service
): Module = ): Module =
result = Module() result = Module()
result.delegate = delegate result.delegate = delegate
@ -32,7 +33,7 @@ proc newModule*(
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.controller = controller.newController( result.controller = controller.newController(
result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable, result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,
contactService, chatService, communityService, messageService, contactService, chatService, communityService, messageService, visualIdentityService
) )
result.moduleLoaded = false result.moduleLoaded = false
@ -62,6 +63,8 @@ method viewDidLoad*(self: Module) =
singletonInstance.userProfile.getIcon(), singletonInstance.userProfile.getIcon(),
singletonInstance.userProfile.getIdenticon(), singletonInstance.userProfile.getIdenticon(),
singletonInstance.userProfile.getIsIdenticon(), singletonInstance.userProfile.getIsIdenticon(),
self.controller.getEmojiHash(singletonInstance.userProfile.getPubKey()),
self.controller.getColorHash(singletonInstance.userProfile.getPubKey()),
isAdded = true, isAdded = true,
admin, admin,
joined, joined,
@ -87,6 +90,8 @@ method viewDidLoad*(self: Module) =
contactDetails.icon, contactDetails.icon,
contactDetails.details.identicon, contactDetails.details.identicon,
contactDetails.isidenticon, contactDetails.isidenticon,
self.controller.getEmojiHash(publicKey),
self.controller.getColorHash(publicKey),
contactDetails.details.added, contactDetails.details.added,
admin, admin,
joined joined
@ -120,6 +125,8 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto]) =
contactDetails.icon, contactDetails.icon,
contactDetails.details.identicon, contactDetails.details.identicon,
contactDetails.isidenticon, contactDetails.isidenticon,
self.controller.getEmojiHash(m.`from`),
self.controller.getColorHash(m.`from`),
contactDetails.details.added, contactDetails.details.added,
)) ))
@ -173,6 +180,8 @@ method onChatMembersAdded*(self: Module, ids: seq[string]) =
contactDetails.icon, contactDetails.icon,
contactDetails.details.identicon, contactDetails.details.identicon,
contactDetails.isidenticon, contactDetails.isidenticon,
self.controller.getEmojiHash(id),
self.controller.getColorHash(id),
contactDetails.details.added, contactDetails.details.added,
admin, admin,
joined joined

View File

@ -19,6 +19,7 @@ import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service import ../../../../app_service/service/message/service as message_service
import ../../../../app_service/service/mailservers/service as mailservers_service import ../../../../app_service/service/mailservers/service as mailservers_service
import ../../../../app_service/service/gif/service as gif_service import ../../../../app_service/service/gif/service as gif_service
import ../../../../app_service/service/visual_identity/service as visual_identity_service
export io_interface export io_interface
@ -85,10 +86,11 @@ proc addSubmodule(self: Module, chatId: string, belongToCommunity: bool, isUsers
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) =
self.chatContentModules[chatId] = chat_content_module.newModule(self, events, self.controller.getMySectionId(), chatId, self.chatContentModules[chatId] = chat_content_module.newModule(self, events, self.controller.getMySectionId(), chatId,
belongToCommunity, isUsersListAvailable, settingsService, contactService, chatService, communityService, belongToCommunity, isUsersListAvailable, settingsService, contactService, chatService, communityService,
messageService, gifService, mailserversService) messageService, gifService, mailserversService, visualIdentityService)
proc removeSubmodule(self: Module, chatId: string) = proc removeSubmodule(self: Module, chatId: string) =
if(not self.chatContentModules.contains(chatId)): if(not self.chatContentModules.contains(chatId)):
@ -102,7 +104,8 @@ proc buildChatUI(self: Module, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) =
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat] let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
let chats = self.controller.getChatDetailsForChatTypes(types) let chats = self.controller.getChatDetailsForChatTypes(types)
@ -130,7 +133,7 @@ proc buildChatUI(self: Module, events: EventEmitter,
active=false, c.position, c.categoryId) active=false, c.position, c.categoryId)
self.view.chatsModel().appendItem(item) self.view.chatsModel().appendItem(item)
self.addSubmodule(c.id, false, isUsersListAvailable, events, settingsService, contactService, chatService, self.addSubmodule(c.id, false, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService, gifService, mailserversService) communityService, messageService, gifService, mailserversService, visualIdentityService)
# make the first Public chat active when load the app # make the first Public chat active when load the app
if(selectedItemId.len == 0 and c.chatType == ChatType.Public): if(selectedItemId.len == 0 and c.chatType == ChatType.Public):
@ -145,7 +148,8 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) =
var selectedItemId = "" var selectedItemId = ""
var selectedSubItemId = "" var selectedSubItemId = ""
let communities = self.controller.getJoinedCommunities() let communities = self.controller.getJoinedCommunities()
@ -166,7 +170,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
notificationsCount, chatDto.muted, blocked=false, active = false, c.position, c.categoryId) notificationsCount, chatDto.muted, blocked=false, active = false, c.position, c.categoryId)
self.view.chatsModel().appendItem(channelItem) self.view.chatsModel().appendItem(channelItem)
self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService, self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService,
messageService, gifService, mailserversService) messageService, gifService, mailserversService, visualIdentityService)
# make the first channel which doesn't belong to any category active when load the app # make the first channel which doesn't belong to any category active when load the app
if(selectedItemId.len == 0): if(selectedItemId.len == 0):
@ -197,7 +201,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
active=false, c.position) active=false, c.position)
categoryChannels.add(channelItem) categoryChannels.add(channelItem)
self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService, self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService,
messageService, gifService, mailserversService) messageService, gifService, mailserversService, visualIdentityService)
# in case there is no channels beyond categories, # in case there is no channels beyond categories,
# make the first channel of the first category active when load the app # make the first channel of the first category active when load the app
@ -262,14 +266,15 @@ method load*(self: Module, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) =
self.controller.init() self.controller.init()
self.view.load() self.view.load()
if(self.controller.isCommunity()): if(self.controller.isCommunity()):
self.buildCommunityUI(events, settingsService, contactService, chatService, communityService, messageService, gifService, mailserversService) self.buildCommunityUI(events, settingsService, contactService, chatService, communityService, messageService, gifService, mailserversService, visualIdentityService)
else: else:
self.buildChatUI(events, settingsService, contactService, chatService, communityService, messageService, gifService, mailserversService) self.buildChatUI(events, settingsService, contactService, chatService, communityService, messageService, gifService, mailserversService, visualIdentityService)
self.initContactRequestsModel() # we do this only in case of chat section (not in case of communities) self.initContactRequestsModel() # we do this only in case of chat section (not in case of communities)
for cModule in self.chatContentModules.values: for cModule in self.chatContentModules.values:
@ -400,6 +405,7 @@ method addNewChat*(
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service, mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service,
setChatAsActive: bool = true) = setChatAsActive: bool = true) =
let hasNotification = chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0 let hasNotification = chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0
let notificationsCount = chatDto.unviewedMentionsCount let notificationsCount = chatDto.unviewedMentionsCount
@ -421,7 +427,7 @@ method addNewChat*(
chatDto.description, chatDto.chatType.int, amIChatAdmin, hasNotification, notificationsCount, chatDto.description, chatDto.chatType.int, amIChatAdmin, hasNotification, notificationsCount,
chatDto.muted, blocked=false, active=false, position = 0, chatDto.categoryId, chatDto.highlight) chatDto.muted, blocked=false, active=false, position = 0, chatDto.categoryId, chatDto.highlight)
self.addSubmodule(chatDto.id, belongsToCommunity, isUsersListAvailable, events, settingsService, contactService, chatService, self.addSubmodule(chatDto.id, belongsToCommunity, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService, gifService, mailserversService) communityService, messageService, gifService, mailserversService, visualIdentityService)
self.chatContentModules[chatDto.id].load() self.chatContentModules[chatDto.id].load()
self.view.chatsModel().appendItem(item) self.view.chatsModel().appendItem(item)
if setChatAsActive: if setChatAsActive:
@ -437,7 +443,7 @@ method addNewChat*(
amIChatAdmin, hasNotification, notificationsCount, chatDto.muted, blocked=false, active=false, amIChatAdmin, hasNotification, notificationsCount, chatDto.muted, blocked=false, active=false,
chatDto.position) chatDto.position)
self.addSubmodule(chatDto.id, belongsToCommunity, isUsersListAvailable, events, settingsService, contactService, chatService, self.addSubmodule(chatDto.id, belongsToCommunity, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService, gifService, mailserversService) communityService, messageService, gifService, mailserversService, visualIdentityService)
self.chatContentModules[chatDto.id].load() self.chatContentModules[chatDto.id].load()
categoryItem.appendSubItem(channelItem) categoryItem.appendSubItem(channelItem)
if setChatAsActive: if setChatAsActive:

View File

@ -7,6 +7,7 @@ import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/message/service as message_service import ../../../../../app_service/service/message/service as message_service
import ../../../../../app_service/service/gif/service as gif_service import ../../../../../app_service/service/gif/service as gif_service
import ../../../../../app_service/service/mailservers/service as mailservers_service import ../../../../../app_service/service/mailservers/service as mailservers_service
import ../../../../../app_service/service/visual_identity/service as visual_identity_service
import ../model as chats_model import ../model as chats_model
@ -22,7 +23,8 @@ method load*(self: AccessInterface, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) {.base.} = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} = method isLoaded*(self: AccessInterface): bool {.base.} =

View File

@ -6,6 +6,7 @@ import ../../../core/signals/types
import ../../../core/eventemitter import ../../../core/eventemitter
import ../../../../app_service/service/community/service as community_service import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/contacts/service as contacts_service import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/visual_identity/service as visual_identity_service
export controller_interface export controller_interface
@ -15,18 +16,21 @@ type
events: EventEmitter events: EventEmitter
communityService: community_service.Service communityService: community_service.Service
contactsService: contacts_service.Service contactsService: contacts_service.Service
visualIdentityService: visual_identity_service.Service
proc newController*( proc newController*(
delegate: io_interface.AccessInterface, delegate: io_interface.AccessInterface,
events: EventEmitter, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
contactsService: contacts_service.Service contactsService: contacts_service.Service,
visualIdentityService: visual_identity_service.Service
): Controller = ): Controller =
result = Controller() result = Controller()
result.delegate = delegate result.delegate = delegate
result.events = events result.events = events
result.communityService = communityService result.communityService = communityService
result.contactsService = contactsService result.contactsService = contactsService
result.visualIdentityService = visualIdentityService
method delete*(self: Controller) = method delete*(self: Controller) =
discard discard
@ -134,3 +138,9 @@ method userCanJoin*(self: Controller, communityId: string): bool =
method isCommunityRequestPending*(self: Controller, communityId: string): bool = method isCommunityRequestPending*(self: Controller, communityId: string): bool =
return self.communityService.isCommunityRequestPending(communityId) return self.communityService.isCommunityRequestPending(communityId)
method getEmojiHash*(self: Controller, pubkey: string): EmojiHashDto =
return self.visualIdentityService.emojiHashOf(pubkey)
method getColorHash*(self: Controller, pubkey: string): ColorHashDto =
return self.visualIdentityService.colorHashOf(pubkey)

View File

@ -1,5 +1,6 @@
import ../../../../app_service/service/community/service as community_service import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/contacts/service as contacts_service import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/visual_identity/service as visual_identity_service
type type
AccessInterface* {.pure inheritable.} = ref object of RootObj AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -63,6 +64,12 @@ method getContactNameAndImage*(self: AccessInterface, contactId: string):
method getContactDetails*(self: AccessInterface, contactId: string): ContactDetails {.base.} = method getContactDetails*(self: AccessInterface, contactId: string): ContactDetails {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getEmojiHash*(self: AccessInterface, pubkey: string): EmojiHashDto {.base.} =
raise newException(ValueError, "No implementation available")
method getColorHash*(self: AccessInterface, pubkey: string): ColorHashDto {.base.} =
raise newException(ValueError, "No implementation available")
type type
## Abstract class (concept) which must be implemented by object/s used in this ## Abstract class (concept) which must be implemented by object/s used in this
## module. ## module.

View File

@ -9,6 +9,7 @@ import ../../../global/global_singleton
import ../../../core/eventemitter import ../../../core/eventemitter
import ../../../../app_service/service/community/service as community_service import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/contacts/service as contacts_service import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/visual_identity/service as visual_identity_service
export io_interface export io_interface
@ -33,7 +34,8 @@ proc newModule*(
delegate: delegate_interface.AccessInterface, delegate: delegate_interface.AccessInterface,
events: EventEmitter, events: EventEmitter,
communityService: community_service.Service, communityService: community_service.Service,
contactsService: contacts_service.Service contactsService: contacts_service.Service,
visualIdentityService: visual_identity_service.Service
): Module = ): Module =
result = Module() result = Module()
result.delegate = delegate result.delegate = delegate
@ -43,7 +45,8 @@ proc newModule*(
result, result,
events, events,
communityService, communityService,
contactsService contactsService,
visualIdentityService
) )
result.moduleLoaded = false result.moduleLoaded = false
@ -100,6 +103,8 @@ method getCommunityItem(self: Module, c: CommunityDto): SectionItem =
contactDetails.icon, contactDetails.icon,
contactDetails.details.identicon, contactDetails.details.identicon,
contactDetails.isidenticon, contactDetails.isidenticon,
self.controller.getEmojiHash(member.id),
self.controller.getColorHash(member.id),
contactDetails.details.added, contactDetails.details.added,
)) ))
) )

View File

@ -16,6 +16,7 @@ import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/mailservers/service as mailservers_service import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/privacy/service as privacy_service import ../../../app_service/service/privacy/service as privacy_service
import ../../../app_service/service/node/service as node_service import ../../../app_service/service/node/service as node_service
import ../../../app_service/service/visual_identity/service as visual_identity_service
export controller_interface export controller_interface
@ -37,6 +38,7 @@ type
privacyService: privacy_service.Service privacyService: privacy_service.Service
mailserversService: mailservers_service.Service mailserversService: mailservers_service.Service
nodeService: node_service.Service nodeService: node_service.Service
visualIdentityService: visual_identity_service.Service
activeSectionId: string activeSectionId: string
proc newController*(delegate: io_interface.AccessInterface, proc newController*(delegate: io_interface.AccessInterface,
@ -51,7 +53,8 @@ proc newController*(delegate: io_interface.AccessInterface,
gifService: gif_service.Service, gifService: gif_service.Service,
privacyService: privacy_service.Service, privacyService: privacy_service.Service,
mailserversService: mailservers_service.Service, mailserversService: mailservers_service.Service,
nodeService: node_service.Service nodeService: node_service.Service,
visualIdentityService: visual_identity_service.Service
): ):
Controller = Controller =
result = Controller() result = Controller()
@ -67,6 +70,7 @@ proc newController*(delegate: io_interface.AccessInterface,
result.gifService = gifService result.gifService = gifService
result.privacyService = privacyService result.privacyService = privacyService
result.nodeService = nodeService result.nodeService = nodeService
result.visualIdentityService = visualIdentityService
method delete*(self: Controller) = method delete*(self: Controller) =
discard discard
@ -103,7 +107,8 @@ method init*(self: Controller) =
self.communityService, self.communityService,
self.messageService, self.messageService,
self.gifService, self.gifService,
self.mailserversService self.mailserversService,
self.visualIdentityService
) )
self.events.on(TOGGLE_SECTION) do(e:Args): self.events.on(TOGGLE_SECTION) do(e:Args):
@ -121,7 +126,8 @@ method init*(self: Controller) =
self.communityService, self.communityService,
self.messageService, self.messageService,
self.gifService, self.gifService,
self.mailserversService self.mailserversService,
self.visualIdentityService
) )
self.events.on(SIGNAL_COMMUNITY_IMPORTED) do(e:Args): self.events.on(SIGNAL_COMMUNITY_IMPORTED) do(e:Args):
@ -137,7 +143,8 @@ method init*(self: Controller) =
self.communityService, self.communityService,
self.messageService, self.messageService,
self.gifService, self.gifService,
self.mailserversService self.mailserversService,
self.visualIdentityService
) )
self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args): self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args):
@ -286,3 +293,9 @@ method switchTo*(self: Controller, sectionId, chatId, messageId: string) =
method getCommunityById*(self: Controller, communityId: string): CommunityDto = method getCommunityById*(self: Controller, communityId: string): CommunityDto =
return self.communityService.getCommunityById(communityId) return self.communityService.getCommunityById(communityId)
method getEmojiHash*(self: Controller, pubkey: string): EmojiHashDto =
return self.visualIdentityService.emojiHashOf(pubkey)
method getColorHash*(self: Controller, pubkey: string): ColorHashDto =
return self.visualIdentityService.colorHashOf(pubkey)

View File

@ -2,6 +2,7 @@ import ../shared_models/section_item
import ../../../app_service/service/contacts/dto/contact_details as contact_details import ../../../app_service/service/contacts/dto/contact_details as contact_details
import ../../../app_service/service/contacts/dto/contacts as contacts_dto import ../../../app_service/service/contacts/dto/contacts as contacts_dto
import ../../../app_service/service/community/service as community_service import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/visual_identity/service as visual_identity_service
type type
AccessInterface* {.pure inheritable.} = ref object of RootObj AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -65,3 +66,9 @@ method getCommunityById*(self: AccessInterface, communityId: string): CommunityD
method isConnected*(self: AccessInterface): bool {.base.} = method isConnected*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getEmojiHash*(self: AccessInterface, pubkey: string): EmojiHashDto {.base.} =
raise newException(ValueError, "No implementation available")
method getColorHash*(self: AccessInterface, pubkey: string): ColorHashDto {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,4 +1,4 @@
import NimQml, tables, json, sugar, sequtils, strformat import NimQml, tables, json, sugar, sequtils, strformat, marshal
import io_interface, view, controller, chat_search_item, chat_search_model import io_interface, view, controller, chat_search_item, chat_search_model
import ./communities/models/[pending_request_item, pending_request_model] import ./communities/models/[pending_request_item, pending_request_model]
@ -49,6 +49,7 @@ import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/gif/service as gif_service import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/ens/service as ens_service import ../../../app_service/service/ens/service as ens_service
import ../../../app_service/service/network/service as network_service import ../../../app_service/service/network/service as network_service
import ../../../app_service/service/visual_identity/service as visual_identity_service
import ../../core/notifications/details import ../../core/notifications/details
import ../../core/eventemitter import ../../core/eventemitter
@ -108,6 +109,7 @@ proc newModule*[T](
gifService: gif_service.Service, gifService: gif_service.Service,
ensService: ens_service.Service, ensService: ens_service.Service,
networkService: network_service.Service, networkService: network_service.Service,
visualIdentityService: visual_identity_service.Service
): Module[T] = ): Module[T] =
result = Module[T]() result = Module[T]()
result.delegate = delegate result.delegate = delegate
@ -126,7 +128,8 @@ proc newModule*[T](
gifService, gifService,
privacyService, privacyService,
mailserversService, mailserversService,
nodeService nodeService,
visualIdentityService
) )
result.moduleLoaded = false result.moduleLoaded = false
@ -149,7 +152,7 @@ proc newModule*[T](
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService) result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService)
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService, result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
messageService, chatService) messageService, chatService)
result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService) result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService, visualIdentityService)
result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService, result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService,
messageService) messageService)
result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService) result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService)
@ -210,6 +213,8 @@ proc createCommunityItem[T](self: Module[T], c: CommunityDto): SectionItem =
contactDetails.icon, contactDetails.icon,
contactDetails.details.identicon, contactDetails.details.identicon,
contactDetails.isidenticon, contactDetails.isidenticon,
self.controller.getEmojiHash(member.id),
self.controller.getColorHash(member.id),
contactDetails.details.added contactDetails.details.added
)), )),
c.pendingRequestsToJoin.map(x => pending_request_item.initItem( c.pendingRequestsToJoin.map(x => pending_request_item.initItem(
@ -231,7 +236,8 @@ method load*[T](
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service
) = ) =
singletonInstance.engine.setRootContextProperty("mainModule", self.viewVariant) singletonInstance.engine.setRootContextProperty("mainModule", self.viewVariant)
self.controller.init() self.controller.init()
@ -347,9 +353,9 @@ method load*[T](
activeSection = profileSettingsSectionItem activeSection = profileSettingsSectionItem
# Load all sections # Load all sections
self.chatSectionModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService) self.chatSectionModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService, visualIdentityService)
for cModule in self.communitySectionsModule.values: for cModule in self.communitySectionsModule.values:
cModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService) cModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService, visualIdentityService)
self.browserSectionModule.load() self.browserSectionModule.load()
# self.nodeManagementSectionModule.load() # self.nodeManagementSectionModule.load()
@ -575,7 +581,8 @@ method communityJoined*[T](
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service
) = ) =
var firstCommunityJoined = false var firstCommunityJoined = false
if (self.communitySectionsModule.len == 0): if (self.communitySectionsModule.len == 0):
@ -593,7 +600,7 @@ method communityJoined*[T](
gifService, gifService,
mailserversService mailserversService
) )
self.communitySectionsModule[community.id].load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService) self.communitySectionsModule[community.id].load(events, settingsService, contactsService, chatService, communityService, messageService, gifService, mailserversService, visualIdentityService)
let communitySectionItem = self.createCommunityItem(community) let communitySectionItem = self.createCommunityItem(community)
if (firstCommunityJoined): if (firstCommunityJoined):
@ -646,6 +653,17 @@ method getContactDetailsAsJson*[T](self: Module[T], publicKey: string): string =
} }
return $jsonObj return $jsonObj
method getEmojiHashAsJson*[T](self: Module[T], publicKey: string): string =
let emojiHash = self.controller.getEmojiHash(publicKey)
return $$emojiHash
method getColorHashAsJson*[T](self: Module[T], publicKey: string): string =
let colorHash = self.controller.getColorHash(publicKey)
let json = newJArray()
for segment in colorHash:
json.add(%* {"segmentLength": segment.len, "colorId": segment.colorIdx})
return $json
method resolveENS*[T](self: Module[T], ensName: string, uuid: string) = method resolveENS*[T](self: Module[T], ensName: string, uuid: string) =
if ensName.len == 0: if ensName.len == 0:
error "error: cannot do a lookup for empty ens name" error "error: cannot do a lookup for empty ens name"

View File

@ -5,6 +5,7 @@ import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service import ../../../../app_service/service/message/service as message_service
import ../../../../app_service/service/gif/service as gif_service import ../../../../app_service/service/gif/service as gif_service
import ../../../../app_service/service/mailservers/service as mailservers_service import ../../../../app_service/service/mailservers/service as mailservers_service
import ../../../../app_service/service/visual_identity/service as visual_identity_service
import ../../../core/eventemitter import ../../../core/eventemitter
@ -20,7 +21,8 @@ method load*(
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service
) )
{.base.} = {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -23,7 +23,8 @@ method communityJoined*(self: AccessInterface, community: CommunityDto, events:
communityService: community_service.Service, communityService: community_service.Service,
messageService: message_service.Service, messageService: message_service.Service,
gifService: gif_service.Service, gifService: gif_service.Service,
mailserversService: mailservers_service.Service) {.base.} = mailserversService: mailservers_service.Service,
visualIdentityService: visual_identity_service.Service) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method communityEdited*(self: AccessInterface, community: CommunityDto) {.base.} = method communityEdited*(self: AccessInterface, community: CommunityDto) {.base.} =

View File

@ -26,6 +26,12 @@ method getAppSearchModule*(self: AccessInterface): QVariant {.base.} =
method getContactDetailsAsJson*(self: AccessInterface, publicKey: string): string {.base.} = method getContactDetailsAsJson*(self: AccessInterface, publicKey: string): string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getEmojiHashAsJson*(self: AccessInterface, publicKey: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method getColorHashAsJson*(self: AccessInterface, publicKey: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method resolveENS*(self: AccessInterface, ensName: string, uuid: string) {.base.} = method resolveENS*(self: AccessInterface, ensName: string, uuid: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -151,6 +151,14 @@ QtObject:
proc getContactDetailsAsJson(self: View, publicKey: string): string {.slot.} = proc getContactDetailsAsJson(self: View, publicKey: string): string {.slot.} =
return self.delegate.getContactDetailsAsJson(publicKey) return self.delegate.getContactDetailsAsJson(publicKey)
# serialized return - nimqml does not allow QVariant return type in slots
proc getEmojiHashAsJson(self: View, publicKey: string): string {.slot.} =
return self.delegate.getEmojiHashAsJson(publicKey)
# serialized return - nimqml does not allow QVariant return type in slots
proc getColorHashAsJson(self: View, publicKey: string): string {.slot.} =
return self.delegate.getColorHashAsJson(publicKey)
proc resolveENS*(self: View, ensName: string, uuid: string) {.slot.} = proc resolveENS*(self: View, ensName: string, uuid: string) {.slot.} =
self.delegate.resolveENS(ensName, uuid) self.delegate.resolveENS(ensName, uuid)

View File

@ -0,0 +1,23 @@
import strformat
type
Item* = ref object
length: int
colorIdx: int
proc initItem*(length: int, colorIdx: int): Item =
result = Item()
result.length = length
result.colorIdx = colorIdx
proc `$`*(self: Item): string =
result = fmt"""ColorHashItem(
length: {$self.length},
colorIdx: {$self.colorIdx},
]"""
proc length*(self: Item): int {.inline.} =
self.length
proc colorIdx*(self: Item): int {.inline.} =
self.colorIdx

View File

@ -0,0 +1,53 @@
import NimQml, Tables
import color_hash_item
type
ModelRole {.pure.} = enum
Length = UserRole + 1
ColorIdx
QtObject:
type
Model* = ref object of QAbstractListModel
items*: seq[Item]
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
self.endResetModel()
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method data(self: Model, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.items.len:
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Length:
result = newQVariant(item.length)
of ModelRole.ColorIdx:
result = newQVariant(item.colorIdx)
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Length.int:"length",
ModelRole.ColorIdx.int:"colorIdx",
}.toTable

View File

@ -0,0 +1,42 @@
import NimQml, Tables
type
RoleNames {.pure.} = enum
Emoji = UserRole + 1,
QtObject:
type
Model* = ref object of QAbstractListModel
items*: seq[string]
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
proc setItems*(self: Model, items: seq[string]) =
self.beginResetModel()
self.items = items
self.endResetModel()
proc items*(self: Model): seq[string] =
return self.items
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method data(self: Model, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.items.len:
return
return newQVariant(self.items[index.row])
method roleNames(self: Model): Table[int, string] =
{ RoleNames.Emoji.int:"emoji" }.toTable

View File

@ -1,4 +1,6 @@
import strformat import strformat, sequtils, sugar
import ./emojis_model, ./color_hash_model, ./color_hash_item
type type
OnlineStatus* {.pure.} = enum OnlineStatus* {.pure.} = enum
@ -8,6 +10,9 @@ type
Idle Idle
Invisible Invisible
type
ColorHashSegment* = tuple[len, colorIdx: int]
# TODO add role when it is needed # TODO add role when it is needed
type type
Item* = ref object Item* = ref object
@ -20,6 +25,8 @@ type
icon: string icon: string
identicon: string identicon: string
isIdenticon: bool isIdenticon: bool
emojiHashModel: emojis_model.Model
colorHashModel: color_hash_model.Model
isAdded: bool isAdded: bool
isAdmin: bool isAdmin: bool
joined: bool joined: bool
@ -34,6 +41,8 @@ proc initItem*(
icon: string, icon: string,
identicon: string, identicon: string,
isidenticon: bool, isidenticon: bool,
emojiHash: seq[string],
colorHash: seq[ColorHashSegment],
isAdded: bool = false, isAdded: bool = false,
isAdmin: bool = false, isAdmin: bool = false,
joined: bool = false, joined: bool = false,
@ -48,6 +57,10 @@ proc initItem*(
result.icon = icon result.icon = icon
result.identicon = identicon result.identicon = identicon
result.isIdenticon = isidenticon result.isIdenticon = isidenticon
result.emojiHashModel = emojis_model.newModel()
result.emojiHashModel.setItems(emojiHash)
result.colorHashModel = color_hash_model.newModel()
result.colorHashModel.setItems(map(colorHash, x => color_hash_item.initItem(x.len, x.colorIdx)))
result.isAdded = isAdded result.isAdded = isAdded
result.isAdmin = isAdmin result.isAdmin = isAdmin
result.joined = joined result.joined = joined
@ -61,10 +74,10 @@ proc `$`*(self: Item): string =
onlineStatus: {$self.onlineStatus.int}, onlineStatus: {$self.onlineStatus.int},
icon: {self.icon}, icon: {self.icon},
identicon: {self.identicon}, identicon: {self.identicon},
isIdenticon: {$self.isIdenticon} isIdenticon: {$self.isIdenticon},
isAdded: {$self.isAdded} isAdded: {$self.isAdded},
isAdmin: {$self.isAdmin} isAdmin: {$self.isAdmin},
joined: {$self.joined} joined: {$self.joined},
]""" ]"""
proc id*(self: Item): string {.inline.} = proc id*(self: Item): string {.inline.} =
@ -132,3 +145,9 @@ proc joined*(self: Item): bool {.inline.} =
proc `joined=`*(self: Item, value: bool) {.inline.} = proc `joined=`*(self: Item, value: bool) {.inline.} =
self.joined = value self.joined = value
proc emojiHashModel*(self: Item): emojis_model.Model {.inline.} =
self.emojiHashModel
proc colorHashModel*(self: Item): color_hash_model.Model {.inline.} =
self.colorHashModel

View File

@ -13,6 +13,8 @@ type
Icon Icon
Identicon Identicon
IsIdenticon IsIdenticon
EmojiHashModel
ColorHashModel
IsAdded IsAdded
IsAdmin IsAdmin
Joined Joined
@ -66,6 +68,8 @@ QtObject:
ModelRole.Icon.int:"icon", ModelRole.Icon.int:"icon",
ModelRole.Identicon.int:"identicon", ModelRole.Identicon.int:"identicon",
ModelRole.IsIdenticon.int:"isIdenticon", ModelRole.IsIdenticon.int:"isIdenticon",
ModelRole.EmojiHashModel.int:"emojiHashModel",
ModelRole.ColorHashModel.int:"colorHashModel",
ModelRole.IsAdded.int:"isAdded", ModelRole.IsAdded.int:"isAdded",
ModelRole.IsAdmin.int:"isAdmin", ModelRole.IsAdmin.int:"isAdmin",
ModelRole.Joined.int:"joined", ModelRole.Joined.int:"joined",
@ -100,6 +104,10 @@ QtObject:
result = newQVariant(item.identicon) result = newQVariant(item.identicon)
of ModelRole.IsIdenticon: of ModelRole.IsIdenticon:
result = newQVariant(item.isIdenticon) result = newQVariant(item.isIdenticon)
of ModelRole.EmojiHashModel:
result = newQVariant(item.emojiHashModel)
of ModelRole.ColorHashModel:
result = newQVariant(item.colorHashModel)
of ModelRole.IsAdded: of ModelRole.IsAdded:
result = newQVariant(item.isAdded) result = newQVariant(item.isAdded)
of ModelRole.IsAdmin: of ModelRole.IsAdmin:

View File

@ -0,0 +1,17 @@
import json, sequtils, sugar
type
EmojiHashDto* = seq[string]
ColorHashSegmentDto* = tuple[len, colorIdx: int]
ColorHashDto* = seq[ColorHashSegmentDto]
proc toEmojiHashDto*(jsonObj: JsonNode): EmojiHashDto =
result = map(jsonObj.getElems(), node => node.getStr())
return
proc toColorHashDto*(jsonObj: JsonNode): ColorHashDto =
result = map(jsonObj.getElems(),
node => (len: node.getElems()[0].getInt(),
colorIdx: node.getElems()[1].getInt())
)
return

View File

@ -0,0 +1,42 @@
import chronicles
import ./dto as dto
import ./service_interface
import ../../../backend/visual_identity as status_visual_identity
export dto
type
Service* = ref object of service_interface.ServiceInterface
proc newService*(): Service =
result = Service()
method delete*(self: Service) =
discard
proc emojiHashOf*(self: Service, pubkey: string): EmojiHashDto =
try:
let response = status_visual_identity.emojiHashOf(pubkey)
if(not response.error.isNil):
error "error emojiHashOf: ", errDescription = response.error.message
result = toEmojiHashDto(response.result)
except Exception as e:
error "error: ", methodName = "emojiHashOf", errName = e.name,
errDesription = e.msg
proc colorHashOf*(self: Service, pubkey: string): ColorHashDto =
try:
let response = status_visual_identity.colorHashOf(pubkey)
if(not response.error.isNil):
error "error colorHashOf: ", errDescription = response.error.message
result = toColorHashDto(response.result)
except Exception as e:
error "error: ", methodName = "colorHashOf", errName = e.name,
errDesription = e.msg

View File

@ -0,0 +1,15 @@
import ./dto as dto
export dto
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for this service access.
method delete*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method emojiHashOf*(self: ServiceInterface, pubkey: string): EmojiHashDto {.base.} =
raise newException(ValueError, "No implementation available")
method colorHashOf*(self: ServiceInterface, pubkey: string): ColorHashDto {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,15 @@
import json, chronicles, core
import response_type
export response_type
logScope:
topics = "rpc-visual-identity"
proc emojiHashOf*(key: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [key]
result = core.callPrivateRPC("visualIdentity_emojiHashOf", payload)
proc colorHashOf*(key: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [key]
result = core.callPrivateRPC("visualIdentity_colorHashOf", payload)

@ -1 +1 @@
Subproject commit 428b165198e53caa5a55ef605f241cc4b6f7e02d Subproject commit 082bb8ef45b64986be1bf67f4d7c95b1a8ea1440

View File

@ -98,6 +98,7 @@ StatusAppThreePanelLayout {
Component { Component {
id: userListComponent id: userListComponent
UserListPanel { UserListPanel {
rootStore: root.rootStore
label: localAccountSensitiveSettings.communitiesEnabled && label: localAccountSensitiveSettings.communitiesEnabled &&
root.rootStore.chatCommunitySectionModule.isCommunity() ? root.rootStore.chatCommunitySectionModule.isCommunity() ?
//% "Members" //% "Members"

View File

@ -1,10 +1,12 @@
import QtQuick 2.13 import QtQuick 2.14
import QtQuick.Controls 2.13 import QtQuick.Controls 2.14
import shared 1.0
import shared.panels 1.0
import utils 1.0 import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.controls.chat 1.0
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
@ -20,6 +22,7 @@ Item {
property string icon: "" property string icon: ""
property string identicon: "" property string identicon: ""
property bool isIdenticon: true property bool isIdenticon: true
property bool isCurrentUser: false
property bool isAdded: false property bool isAdded: false
property string iconToShow: { property string iconToShow: {
if (isIdenticon || (!isAdded && if (isIdenticon || (!isAdded &&
@ -48,23 +51,19 @@ Item {
radius: 8 radius: 8
color: wrapper.color color: wrapper.color
StatusSmartIdenticon { UserImage {
id: contactImage id: contactImage
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Style.current.padding anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
image: StatusImageSettings {
width: 28 imageWidth: 28
height: 28 imageHeight: 28
source: wrapper.iconToShow
isIdenticon: wrapper.isIdenticon
}
icon: StatusIconSettings {
width: 28
height: 28
letterSize: 15
}
name: wrapper.name name: wrapper.name
pubkey: wrapper.publicKey
icon: wrapper.iconToShow
isIdenticon: wrapper.isIdenticon
showRing: !(wrapper.isCurrentUser || wrapper.isAdded)
} }
StyledText { StyledText {

View File

@ -27,11 +27,9 @@ ScrollView {
clip: true clip: true
delegate: StatusListItem { delegate: StatusListItem {
id: contactDelegate id: contactDelegate
property bool isChecked: selectedPubKeys.indexOf(model.pubKey) !== -1 property bool isChecked: selectedPubKeys.indexOf(model.pubKey) !== -1
title: !model.name.endsWith(".eth") && !!model.localNickname ?
model.localNickname : Utils.removeStatusEns(model.name)
image.source: Global.getProfileImage(model.pubKey) || model.identicon
image.isIdenticon: !!model.identicon
visible: { visible: {
if (selectMode) { if (selectMode) {
return !searchString || model.name.toLowerCase().includes(searchString) return !searchString || model.name.toLowerCase().includes(searchString)
@ -39,6 +37,12 @@ ScrollView {
return checkbox.checked return checkbox.checked
} }
title: !model.name.endsWith(".eth") && !!model.localNickname ?
model.localNickname : Utils.removeStatusEns(model.name)
image.source: Global.getProfileImage(model.pubKey) || model.identicon
image.isIdenticon: !!model.identicon
ringSettings.ringSpecModel: Utils.getColorHashAsJson(model.pubKey)
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
function contactToggled(pubKey) { function contactToggled(pubKey) {

View File

@ -22,6 +22,8 @@ Item {
property var messageContextMenu property var messageContextMenu
property string label property string label
property var rootStore
StatusBaseText { StatusBaseText {
id: titleText id: titleText
anchors.top: parent.top anchors.top: parent.top
@ -61,6 +63,7 @@ Item {
isAdded: model.isAdded isAdded: model.isAdded
userStatus: model.onlineStatus userStatus: model.onlineStatus
messageContextMenu: root.messageContextMenu messageContextMenu: root.messageContextMenu
isCurrentUser: rootStore.isCurrentUser(model.id)
} }
section.property: "onlineStatus" section.property: "onlineStatus"
section.delegate: (root.width > 58) ? sectionDelegateComponent : null section.delegate: (root.width > 58) ? sectionDelegateComponent : null

View File

@ -134,6 +134,14 @@ QtObject {
globalUtilsInst.downloadImage(content, path) globalUtilsInst.downloadImage(content, path)
} }
function isCurrentUser(pubkey) {
return userProfileInst.pubKey === pubkey
}
function displayName(name, pubkey) {
return isCurrentUser(pubkey) ? qsTr("You") : name
}
function getCommunity(communityId) { function getCommunity(communityId) {
// Not Refactored Yet // Not Refactored Yet
// try { // try {

View File

@ -289,10 +289,9 @@ Item {
icon: root.ensUsernamesStore.icon icon: root.ensUsernamesStore.icon
isIdenticon: root.ensUsernamesStore.isIdenticon isIdenticon: root.ensUsernamesStore.isIdenticon
showRing: true
onClickMessage: { onClicked: root.parent.clickMessage(true, false, false, null, false, false, false)
root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
}
} }
UsernameLabel { UsernameLabel {

View File

@ -0,0 +1,115 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import utils 1.0
import shared.panels 1.0
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
Item {
id: root
property string displayName
property string pubkey
property string icon
property bool isIdenticon: false
property bool displayNameVisible: true
property bool pubkeyVisible: true
property alias imageWidth: userImage.imageWidth
property alias imageHeight: userImage.imageHeight
property alias emojiSize: emojihash.size
property alias imageOverlay: imageOverlay.sourceComponent
signal clicked()
height: visible ? contentContainer.height : 0
ColumnLayout {
id: contentContainer
anchors {
left: parent.left
right: parent.right
leftMargin: Style.current.smallPadding
rightMargin: Style.current.smallPadding
}
UserImage {
id: userImage
Layout.alignment: Qt.AlignHCenter
name: root.displayName
pubkey: root.pubkey
icon: root.icon
isIdenticon: root.isIdenticon
showRing: true
interactive: false
Loader {
id: imageOverlay
anchors.fill: parent
}
}
StyledText {
Layout.fillWidth: true
visible: root.displayNameVisible
text: root.displayName
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
maximumLineCount: 3
wrapMode: Text.Wrap
font {
weight: Font.Medium
pixelSize: Style.current.primaryTextFontSize
}
}
StyledText {
Layout.fillWidth: true
visible: root.pubkeyVisible
text: pubkey.substring(0, 10) + "..." + pubkey.substring(
pubkey.length - 4)
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Style.current.asideTextFontSize
color: Style.current.secondaryText
}
Text {
id: emojihash
property string size: "14x14"
Layout.fillWidth: true
text: {
const emojiHash = Utils.getEmojiHashAsJson(root.pubkey)
var emojiHashFirstLine = ""
var emojiHashSecondLine = ""
for (var i = 0; i < 7; i++) {
emojiHashFirstLine += emojiHash[i]
}
for (var i = 7; i < emojiHash.length; i++) {
emojiHashSecondLine += emojiHash[i]
}
return StatusQUtils.Emoji.parse(emojiHashFirstLine, size) + "<br>" +
StatusQUtils.Emoji.parse(emojiHashSecondLine, size)
}
horizontalAlignment: Text.AlignHCenter
font.pointSize: 1 // make sure there is no padding for emojis due to 'style: "vertical-align: top"'
}
}
}

View File

@ -4,46 +4,56 @@ import shared.panels 1.0
import utils 1.0 import utils 1.0
import StatusQ.Components 0.1
Loader { Loader {
id: root id: root
height: active ? item.height : 0
property int imageHeight: 36 property int imageHeight: 36
property int imageWidth: 36 property int imageWidth: 36
property string name
property string pubkey
property string icon: "" property string icon: ""
property bool isIdenticon: false property bool isIdenticon: false
property bool showRing: false
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool emojiOnly, bool hideEmojiPicker, bool isReply) property bool interactive: true
sourceComponent: Component { signal clicked()
Item {
id: chatImage
width: identiconImage.width
height: identiconImage.height
RoundedImage { height: active ? item.height : 0
id: identiconImage
sourceComponent: StatusSmartIdenticon {
name: root.name
image {
width: root.imageWidth width: root.imageWidth
height: root.imageHeight height: root.imageHeight
border.width: root.isIdenticon ? 1 : 0 source: root.isIdenticon ? "" : root.icon
border.color: Style.current.border isIdenticon: false
source: root.icon }
smooth: false icon {
antialiasing: true width: root.imageWidth
height: root.imageHeight
color: Style.current.background
textColor: Style.current.secondaryText
letterSize: Math.max(4, root.imageWidth / 2.4)
charactersLen: 2
}
ringSettings {
ringSpecModel: root.showRing ? Utils.getColorHashAsJson(root.pubkey) : undefined
ringPxSize: Math.max(root.imageWidth / 24.0)
}
MouseArea { Loader {
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent anchors.fill: parent
onClicked: {
if (!!messageContextMenu) { active: root.interactive
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = root sourceComponent: MouseArea {
messageContextMenu.setXPosition = function() { return root.width + 4} cursorShape: Qt.PointingHandCursor
messageContextMenu.setYPosition = function() { return root.height/2 + 4} hoverEnabled: true
} onClicked: root.clicked()
root.clickMessage(true, false, false, null, false, false, isReplyImage)
}
}
} }
} }
} }

View File

@ -12,3 +12,4 @@ StateBubble 1.0 StateBubble.qml
GasSelectorButton 1.0 GasSelectorButton.qml GasSelectorButton 1.0 GasSelectorButton.qml
MessageBorder 1.0 MessageBorder.qml MessageBorder 1.0 MessageBorder.qml
EmojiReaction 1.0 EmojiReaction.qml EmojiReaction 1.0 EmojiReaction.qml
ProfileHeader 1.0 ProfileHeader.qml

View File

@ -13,12 +13,15 @@ import StatusQ.Core.Utils 0.1 as StatusQUtils
Loader { Loader {
id: root id: root
property bool amISenderOfTheRepliedMessage property bool amISenderOfTheRepliedMessage
property int repliedMessageContentType property int repliedMessageContentType
property string repliedMessageSenderIcon property string repliedMessageSenderIcon
property bool repliedMessageSenderIconIsIdenticon property bool repliedMessageSenderIconIsIdenticon
property bool repliedMessageIsEdited property bool repliedMessageIsEdited
property string repliedMessageSender property string repliedMessageSender
property string repliedMessageSenderPubkey
property bool repliedMessageSenderIsAdded
property string repliedMessageContent property string repliedMessageContent
property string repliedMessageImage property string repliedMessageImage
property bool isCurrentUser: false property bool isCurrentUser: false
@ -97,16 +100,20 @@ Loader {
UserImage { UserImage {
id: userImage id: userImage
anchors.left: replyCorner.right
anchors.leftMargin: Style.current.halfPadding
imageHeight: 20 imageHeight: 20
imageWidth: 20 imageWidth: 20
active: true active: true
anchors.left: replyCorner.right
anchors.leftMargin: Style.current.halfPadding name: repliedMessageSender
pubkey: repliedMessageSenderPubkey
icon: repliedMessageSenderIcon icon: repliedMessageSenderIcon
isIdenticon: repliedMessageSenderIconIsIdenticon isIdenticon: repliedMessageSenderIconIsIdenticon
onClickMessage: { showRing: !(amISenderOfTheRepliedMessage || repliedMessageSenderIsAdded)
root.clickMessage(true, false, false, null, false, false, true)
} onClicked: root.clickMessage(true, false, false, null, false, false, true)
} }
StyledTextEdit { StyledTextEdit {

View File

@ -3,12 +3,13 @@ import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import QtGraphicalEffects 1.13 import QtGraphicalEffects 1.13
import utils 1.0 import utils 1.0
import shared 1.0 import shared 1.0
import shared.popups 1.0 import shared.popups 1.0
import shared.stores 1.0 import shared.stores 1.0
import shared.controls.chat 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
@ -29,9 +30,9 @@ StatusModal {
property string userNickname: "" property string userNickname: ""
property string userEnsName: "" property string userEnsName: ""
property string userIcon: "" property string userIcon: ""
property bool isUserIconIdenticon: true
property string text: "" property string text: ""
readonly property int innerMargin: 20 readonly property int innerMargin: 20
property bool userIsEnsVerified: false property bool userIsEnsVerified: false
@ -62,6 +63,7 @@ StatusModal {
} else { } else {
userIcon = contactDetails.displayIcon userIcon = contactDetails.displayIcon
} }
isUserIconIdenticon = contactDetails.isDisplayIconIdenticon
userIsEnsVerified = contactDetails.ensVerified userIsEnsVerified = contactDetails.ensVerified
userIsBlocked = contactDetails.isBlocked userIsBlocked = contactDetails.isBlocked
isAddedContact = contactDetails.isContact isAddedContact = contactDetails.isContact
@ -72,17 +74,9 @@ StatusModal {
popup.open() popup.open()
} }
onHeaderImageClicked: {
Global.openChangeProfilePicPopup()
}
header.title: userDisplayName header.title: userDisplayName
header.subTitle: userIsEnsVerified ? userName : userPublicKey header.subTitle: userIsEnsVerified ? userName : userPublicKey
header.subTitleElide: Text.ElideMiddle header.subTitleElide: Text.ElideMiddle
// In the following line we need to use `icon` property as that's the easiest way
// to update image on change (in case of logged in user)
header.image.source: isCurrentUser? popup.profileStore.icon : userIcon
header.headerImageEditable: isCurrentUser
headerActionButton: StatusFlatRoundButton { headerActionButton: StatusFlatRoundButton {
type: StatusFlatRoundButton.Type.Secondary type: StatusFlatRoundButton.Type.Secondary
@ -109,6 +103,50 @@ StatusModal {
anchors.top: parent.top anchors.top: parent.top
width: parent.width width: parent.width
Item {
height: 16
width: parent.width
}
ProfileHeader {
width: parent.width
displayName: popup.userDisplayName
pubkey: popup.userPublicKey
icon: popup.isCurrentUser ? popup.profileStore.icon : popup.userIcon
isIdenticon: popup.isCurrentUser ? popup.profileStore.isIdenticon : popup.isUserIconIdenticon
displayNameVisible: false
pubkeyVisible: false
emojiSize: "20x20"
imageWidth: 80
imageHeight: 80
imageOverlay: Item {
visible: popup.isCurrentUser
StatusFlatRoundButton {
width: 24
height: 24
anchors {
right: parent.right
bottom: parent.bottom
rightMargin: -8
}
type: StatusFlatRoundButton.Type.Secondary
icon.name: "pencil"
icon.color: Theme.palette.directColor1
icon.width: 12.5
icon.height: 12.5
onClicked: Global.openChangeProfilePicPopup()
}
}
}
StatusBanner { StatusBanner {
width: parent.width width: parent.width
visible: popup.userIsBlocked visible: popup.userIsBlocked

View File

@ -4,85 +4,54 @@ import QtQuick.Layouts 1.3
import QtQml.Models 2.3 import QtQml.Models 2.3
import utils 1.0 import utils 1.0
import "../panels" import shared.controls.chat 1.0
import "." import shared.panels 1.0
import StatusQ.Components 0.1 import StatusQ.Components 0.1
// TODO: replace with StatusPopupMenu // TODO: replace with StatusPopupMenu
PopupMenu { PopupMenu {
id: root id: root
property var store property var store
width: profileHeader.width
width: 200
closePolicy: Popup.CloseOnReleaseOutsideParent | Popup.CloseOnEscape closePolicy: Popup.CloseOnReleaseOutsideParent | Popup.CloseOnEscape
overrideTextColor: Style.current.textColor
ProfileHeader {
width: parent.width
displayName: root.store.userProfileInst.name
pubkey: root.store.userProfileInst.pubKey
icon: root.store.userProfileInst.icon
isIdenticon: root.store.userProfileInst.isIdenticon
}
Item { Item {
id: profileHeader height: root.topPadding
width: 200
height: visible ? profileImage.height + username.height + viewProfileBtn.height + Style.current.padding * 2 : 0
Rectangle {
anchors.fill: parent
visible: mouseArea.containsMouse
color: Style.current.backgroundHover
}
StatusSmartIdenticon {
id: profileImage
anchors.top: parent.top
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
image.source: root.store.userProfileInst.icon
image.isIdenticon: root.store.userProfileInst.isIdenticon
}
StyledText {
id: username
text: root.store.userProfileInst.name
elide: Text.ElideRight
maximumLineCount: 3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
anchors.top: profileImage.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
font.weight: Font.Medium
font.pixelSize: 13
} }
StyledText { Separator {
id: viewProfileBtn
text: qsTr("My profile →")
horizontalAlignment: Text.AlignHCenter
anchors.top: username.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
font.weight: Font.Medium
font.pixelSize: Style.current.tertiaryTextFontSize
color: Style.current.secondaryText
} }
MouseArea { Action {
id: mouseArea text: qsTr("View My Profile")
anchors.fill: parent
hoverEnabled: true icon.source: Style.svg("profile")
cursorShape: Qt.PointingHandCursor icon.width: 16
onClicked: { icon.height: 16
onTriggered: {
Global.openProfilePopup(root.store.userProfileInst.pubKey) Global.openProfilePopup(root.store.userProfileInst.pubKey)
root.close() root.close()
} }
} }
}
Separator { Separator {
anchors.bottom: profileHeader.bottom
} }
overrideTextColor: Style.current.textColor
Action { Action {
text: qsTr("Online") text: qsTr("Online")
onTriggered: { onTriggered: {
@ -113,5 +82,4 @@ PopupMenu {
icon.width: 16 icon.width: 16
icon.height: 16 icon.height: 16
} }
} }

View File

@ -11,6 +11,7 @@ import shared.controls.chat 1.0
import StatusQ.Controls 0.1 as StatusQControls import StatusQ.Controls 0.1 as StatusQControls
import StatusQ.Core.Utils 0.1 as StatusQUtils import StatusQ.Core.Utils 0.1 as StatusQUtils
import StatusQ.Components 0.1
Item { Item {
id: root id: root
@ -306,6 +307,8 @@ Item {
// TODO: not sure about is edited at the moment // TODO: not sure about is edited at the moment
repliedMessageIsEdited = false repliedMessageIsEdited = false
repliedMessageSender = obj.senderDisplayName repliedMessageSender = obj.senderDisplayName
repliedMessageSenderPubkey = obj.senderId
repliedMessageSenderIsAdded = obj.senderIsAdded
repliedMessageContent = obj.messageText repliedMessageContent = obj.messageText
repliedMessageImage = obj.messageImage repliedMessageImage = obj.messageImage
} }
@ -327,17 +330,22 @@ Item {
UserImage { UserImage {
id: chatImage id: chatImage
active: isMessage && headerRepeatCondition active: isMessage && headerRepeatCondition
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Style.current.padding anchors.leftMargin: Style.current.padding
anchors.top: chatReply.active ? chatReply.bottom : anchors.top: chatReply.active ? chatReply.bottom :
pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
anchors.topMargin: chatReply.active || pinnedRectangleLoader.active ? 4 : Style.current.smallPadding anchors.topMargin: chatReply.active || pinnedRectangleLoader.active ? 4 : Style.current.smallPadding
icon: root.senderIcon icon: root.senderIcon
isIdenticon: root.isSenderIconIdenticon isIdenticon: root.isSenderIconIdenticon
onClickMessage: { pubkey: senderId
root.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply, false, "") name: senderDisplayName
} showRing: !(root.amISender || senderIsAdded)
onClicked: root.clickMessage(true, false, false, null, false, false, false, false, "")
} }
UsernameLabel { UsernameLabel {

View File

@ -116,53 +116,19 @@ StatusPopupMenu {
} }
} }
Item { ProfileHeader {
id: profileHeader
visible: root.isProfile
width: parent.width width: parent.width
height: visible ? profileImage.height + username.height + Style.current.padding : 0 visible: root.isProfile
Rectangle {
anchors.fill: parent displayName: root.selectedUserDisplayName
visible: mouseArea.containsMouse pubkey: root.selectedUserPublicKey
color: Style.current.backgroundHover icon: root.selectedUserIcon
isIdenticon: root.isSelectedUserIconIdenticon
} }
StatusSmartIdenticon { Item {
id: profileImage visible: root.isProfile
anchors.top: parent.top height: root.topPadding
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
image.source: root.selectedUserIcon
image.isIdenticon: root.isSelectedUserIconIdenticon
}
StyledText {
id: username
text: selectedUserDisplayName
elide: Text.ElideRight
maximumLineCount: 3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
anchors.top: profileImage.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
font.weight: Font.Medium
font.pixelSize: 15
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.openProfileClicked(root.selectedUserPublicKey)
root.close()
}
}
} }
Separator { Separator {

View File

@ -149,7 +149,6 @@ Column {
|| contentType === Constants.messageContentType.communityInviteType || contentType === Constants.messageContentType.transactionType || contentType === Constants.messageContentType.communityInviteType || contentType === Constants.messageContentType.transactionType
property bool isExpired: (outgoingStatus === "sending" && (Math.floor(timestamp) + 180000) < Date.now()) property bool isExpired: (outgoingStatus === "sending" && (Math.floor(timestamp) + 180000) < Date.now())
property bool isStatusUpdate: false
property int statusAgeEpoch: 0 property int statusAgeEpoch: 0
signal imageClicked(var image) signal imageClicked(var image)
@ -196,8 +195,8 @@ Column {
if(!obj) if(!obj)
return return
messageContextMenu.messageSenderId = obj.id messageContextMenu.messageSenderId = obj.senderId
messageContextMenu.selectedUserPublicKey = obj.id messageContextMenu.selectedUserPublicKey = obj.senderId
messageContextMenu.selectedUserDisplayName = obj.senderDisplayName messageContextMenu.selectedUserDisplayName = obj.senderDisplayName
messageContextMenu.selectedUserIcon = obj.senderIcon messageContextMenu.selectedUserIcon = obj.senderIcon
messageContextMenu.isSelectedUserIconIdenticon = obj.isSenderIconIdenticon messageContextMenu.isSelectedUserIconIdenticon = obj.isSenderIconIdenticon
@ -258,7 +257,7 @@ Column {
case Constants.messageContentType.gapType: case Constants.messageContentType.gapType:
return gapComponent return gapComponent
default: default:
return isStatusUpdate ? statusUpdateComponent : compactMessageComponent return compactMessageComponent
} }
} }
@ -335,38 +334,6 @@ Column {
} }
} }
Component {
id: statusUpdateComponent
StatusUpdateView {
messageStore: root.messageStore
statusAgeEpoch: root.statusAgeEpoch
container: root
// Not Refactored Yet
// store: root.rootStore
messageContextMenu: root.messageContextMenu
onAddEmoji: {
root.clickMessage(isProfileClick, isSticker, isImage , image, emojiOnly, hideEmojiPicker);
}
onChatImageClicked: root.imageClicked(image)
onUserNameClicked: {
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick);
}
onEmojiBtnClicked: {
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly);
}
onClickMessage: {
// Not Refactored Yet - Should do it via messageStore
// root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
}
onSetMessageActive: {
root.setMessageActive(messageId, active);
}
}
}
Component { Component {
id: compactMessageComponent id: compactMessageComponent

View File

@ -1,211 +0,0 @@
import QtQuick 2.3
import QtGraphicalEffects 1.13
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.status 1.0
import shared.panels.chat 1.0
import shared.views.chat 1.0
import shared.controls.chat 1.0
import StatusQ.Controls 0.1
MouseArea {
id: root
// property var store
property var messageStore
property bool hovered: containsMouse
property var container
property int statusAgeEpoch: 0
property var messageContextMenu
signal userNameClicked(bool isProfileClick)
signal setMessageActive(string messageId, bool active)
signal emojiBtnClicked(bool isProfileClick, bool isSticker, bool isImage, var image, bool emojiOnly)
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool emojiOnly, bool hideEmojiPicker, bool isReply)
// TODO bring those back and remove dynamic scoping
// property var emojiReactionsModel
// property string timestamp: ""
// property bool isCurrentUser: false
// property bool isMessageActive: false
// property string userName: ""
// property string localName: ""
// property string displayUserName: ""
// property bool isImage: false
// property bool isMessage: false
// property string profileImageSource: ""
// property string userIdenticon: ""
anchors.top: parent.top
anchors.topMargin: 0
height: (isImage ? chatImageContent.height : chatText.height) + chatName.height + 2* Style.current.padding + (emojiReactionsModel.length ? 20 : 0)
width: parent.width
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: true
signal chatImageClicked(string image)
signal addEmoji(bool isProfileClick, bool isSticker, bool isImage , var image, bool emojiOnly, bool hideEmojiPicker)
onClicked: {
mouse.accepted = false
}
Rectangle {
id: rootRect
anchors.fill: parent
radius: Style.current.radius
color: root.hovered ? Style.current.border : Style.current.background
UserImage {
id: chatImage
active: isMessage || isImage
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
// messageContextMenu: root.messageContextMenu
// profileImage: root.profileImageSource
// isMessage: root.isMessage
// identiconImageSource: root.userIdenticon
onClickMessage: {
root.clickMessage(true, false, false, null, false, false, isReplyImage)
}
}
UsernameLabel {
id: chatName
z: 51
visible: chatImage.visible
anchors.leftMargin: Style.current.halfPadding
anchors.top: chatImage.top
anchors.left: chatImage.right
label.font.pixelSize: Style.current.primaryTextFontSize
// messageContextMenu: root.messageContextMenu
// isCurrentUser: root.isCurrentUser
// userName: root.userName
// localName: root.localName
// displayUserName: root.displayUserName
onClickMessage: {
root.userNameClicked(true);
}
}
ChatTimePanel {
id: chatTime
// statusAgeEpoch is used to trigger Qt property update
// since the returned string will be the same in 99% cases, this should not trigger ChatTime re-rendering
text: Utils.formatAgeFromTime(timestamp, statusAgeEpoch)
visible: chatName.visible
anchors.verticalCenter: chatName.verticalCenter
anchors.left: chatName.right
anchors.leftMargin: Style.current.halfPadding
//timestamp: timestamp
}
ChatTextView {
id: chatText
anchors.top: chatName.visible ? chatName.bottom : chatImage.top
anchors.topMargin: chatName.visible ? 6 : 0
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
// store: root.store
}
Loader {
id: chatImageContent
active: isImage
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.top: chatText.bottom
z: 51
sourceComponent: Component {
StatusChatImage {
playing: root.messageStore.playAnimation
imageSource: image
imageWidth: 200
container: root.container
onClicked: {
root.chatImageClicked(image);
}
}
}
}
StatusFlatRoundButton {
id: emojiBtn
width: 32
height: 32
anchors.top: rootRect.top
anchors.topMargin: -height / 4
anchors.right: rootRect.right
anchors.rightMargin: Style.current.halfPadding
visible: root.hovered
icon.name: "reaction-b"
icon.width: 20
icon.height: 20
type: StatusFlatRoundButton.Type.Tertiary
backgroundHoverColor: Style.current.background
onClicked: {
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = emojiBtn
messageContextMenu.setXPosition = function() { return -messageContextMenu.width + emojiBtn.width}
messageContextMenu.setYPosition = function() { return -messageContextMenu.height - 4}
root.emojiBtnClicked(false, false, false, null, true)
}
}
DropShadow {
anchors.fill: emojiBtn
horizontalOffset: 0
verticalOffset: 2
radius: 10
samples: 12
color: "#22000000"
source: emojiBtn
}
Loader {
id: emojiReactionLoader
active: emojiReactionsModel.length
sourceComponent: emojiReactionsComponent
anchors.left: chatImage.right
anchors.leftMargin: Style.current.halfPadding
anchors.top: isImage ? chatImageContent.bottom : chatText.bottom
anchors.topMargin: Style.current.halfPadding
}
Component {
id: emojiReactionsComponent
EmojiReactionsPanel {
store: messageStore
emojiReactionsModel: reactionsModel
isMessageActive: isMessageActive
isCurrentUser: isCurrentUser
onAddEmojiClicked: {
root.addEmoji(false, false, false, null, true, false);
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = emojiReactionLoader
messageContextMenu.setXPosition = function() { return (messageContextMenu.parent.x + 4)}
messageContextMenu.setYPosition = function() { return (-messageContextMenu.height - 4)}
}
onToggleReaction: messageStore.toggleReaction(messageId, emojiID)
onSetMessageActive: {
root.setMessageActive(messageId, active);
}
}
}
Separator {
anchors.bottom: parent.bottom
visible: !root.hovered
}
}
}

View File

@ -1,7 +1,6 @@
ChannelIdentifierView 1.0 ChannelIdentifierView.qml ChannelIdentifierView 1.0 ChannelIdentifierView.qml
ChatTextView 1.0 ChatTextView.qml ChatTextView 1.0 ChatTextView.qml
MessageView 1.0 MessageView.qml MessageView 1.0 MessageView.qml
StatusUpdateView 1.0 StatusUpdateView.qml
TransactionBubbleView 1.0 TransactionBubbleView.qml TransactionBubbleView 1.0 TransactionBubbleView.qml
LinksMessageView 1.0 LinksMessageView.qml LinksMessageView 1.0 LinksMessageView.qml
InvitationBubbleView 1.0 InvitationBubbleView.qml InvitationBubbleView 1.0 InvitationBubbleView.qml

View File

@ -604,7 +604,21 @@ QtObject {
} }
} }
function getEmojiHashAsJson(publicKey) {
if (publicKey === "") {
return ""
}
let jsonObj = mainModule.getEmojiHashAsJson(publicKey)
return JSON.parse(jsonObj)
}
function getColorHashAsJson(publicKey) {
if (publicKey === "") {
return ""
}
let jsonObj = mainModule.getColorHashAsJson(publicKey)
return JSON.parse(jsonObj)
}
// Leave this function at the bottom of the file as QT Creator messes up the code color after this // Leave this function at the bottom of the file as QT Creator messes up the code color after this
function isPunct(c) { function isPunct(c) {