mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-09 05:52:41 +00:00
refactor(online-users): adding online users for chat/channel
This commit is contained in:
parent
afe6d34735
commit
f138fecdd2
@ -362,6 +362,7 @@ proc load(self: AppController) =
|
|||||||
# load main module
|
# load main module
|
||||||
self.mainModule.load(
|
self.mainModule.load(
|
||||||
self.statusFoundation.status.events,
|
self.statusFoundation.status.events,
|
||||||
|
self.contactsService,
|
||||||
self.chatService,
|
self.chatService,
|
||||||
self.communityService,
|
self.communityService,
|
||||||
self.messageService
|
self.messageService
|
||||||
|
@ -4,7 +4,7 @@ import
|
|||||||
proc handleSignals(self: ChatController) =
|
proc handleSignals(self: ChatController) =
|
||||||
self.status.events.on(SignalType.Message.event) do(e:Args):
|
self.status.events.on(SignalType.Message.event) do(e:Args):
|
||||||
var data = MessageSignal(e)
|
var data = MessageSignal(e)
|
||||||
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests, data.pinnedMessages, data.activityCenterNotification, data.statusUpdates, data.deletedMessages)
|
#self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests, data.pinnedMessages, data.activityCenterNotification, data.statusUpdates, data.deletedMessages)
|
||||||
|
|
||||||
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
|
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
|
||||||
## Handle mailserver peers being added and removed
|
## Handle mailserver peers being added and removed
|
||||||
@ -39,8 +39,9 @@ proc handleSignals(self: ChatController) =
|
|||||||
self.view.messageView.messageList[chatId].checkTimeout(messageId)
|
self.view.messageView.messageList[chatId].checkTimeout(messageId)
|
||||||
|
|
||||||
self.status.events.on(SignalType.CommunityFound.event) do(e: Args):
|
self.status.events.on(SignalType.CommunityFound.event) do(e: Args):
|
||||||
var data = CommunitySignal(e)
|
discard
|
||||||
self.view.communities.addCommunityToList(data.community)
|
# var data = CommunitySignal(e)
|
||||||
|
# self.view.communities.addCommunityToList(data.community)
|
||||||
|
|
||||||
self.status.events.on(SignalType.MailserverRequestCompleted.event) do(e:Args):
|
self.status.events.on(SignalType.MailserverRequestCompleted.event) do(e:Args):
|
||||||
# TODO: if the signal contains a cursor, request additional messages
|
# TODO: if the signal contains a cursor, request additional messages
|
||||||
|
@ -2,13 +2,13 @@ import json
|
|||||||
|
|
||||||
import base
|
import base
|
||||||
|
|
||||||
import status/types/community
|
import ../../../../app_service/service/community/dto/[community]
|
||||||
import signal_type
|
import signal_type
|
||||||
|
|
||||||
type CommunitySignal* = ref object of Signal
|
type CommunitySignal* = ref object of Signal
|
||||||
community*: Community
|
community*: CommunityDto
|
||||||
|
|
||||||
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
|
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
|
||||||
result = CommunitySignal()
|
result = CommunitySignal()
|
||||||
result.signalType = SignalType.CommunityFound
|
result.signalType = SignalType.CommunityFound
|
||||||
result.community = event["event"].toCommunity()
|
result.community = event["event"].toCommunityDto()
|
||||||
|
@ -2,20 +2,26 @@ import json
|
|||||||
|
|
||||||
import base
|
import base
|
||||||
|
|
||||||
import status/types/[message, chat, community, profile, installation,
|
# Step by step we should remove all these types from `status-lib`
|
||||||
activity_center_notification, status_update, removed_message]
|
import status/types/[installation, activity_center_notification, removed_message]
|
||||||
|
import status/types/community as old_community
|
||||||
|
|
||||||
|
import ../../../../app_service/service/message/dto/[message, pinned_message, reaction]
|
||||||
|
import ../../../../app_service/service/chat/dto/[chat]
|
||||||
|
import ../../../../app_service/service/community/dto/[community]
|
||||||
|
import ../../../../app_service/service/contacts/dto/[contacts, status_update]
|
||||||
|
|
||||||
type MessageSignal* = ref object of Signal
|
type MessageSignal* = ref object of Signal
|
||||||
messages*: seq[Message]
|
messages*: seq[MessageDto]
|
||||||
pinnedMessages*: seq[Message]
|
pinnedMessages*: seq[PinnedMessageDto]
|
||||||
chats*: seq[Chat]
|
chats*: seq[ChatDto]
|
||||||
contacts*: seq[Profile]
|
contacts*: seq[ContactsDto]
|
||||||
installations*: seq[Installation]
|
installations*: seq[Installation]
|
||||||
emojiReactions*: seq[Reaction]
|
emojiReactions*: seq[ReactionDto]
|
||||||
communities*: seq[Community]
|
communities*: seq[CommunityDto]
|
||||||
membershipRequests*: seq[CommunityMembershipRequest]
|
membershipRequests*: seq[old_community.CommunityMembershipRequest]
|
||||||
activityCenterNotification*: seq[ActivityCenterNotification]
|
activityCenterNotification*: seq[ActivityCenterNotification]
|
||||||
statusUpdates*: seq[StatusUpdate]
|
statusUpdates*: seq[StatusUpdateDto]
|
||||||
deletedMessages*: seq[RemovedMessage]
|
deletedMessages*: seq[RemovedMessage]
|
||||||
|
|
||||||
proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
||||||
@ -25,27 +31,21 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
|||||||
|
|
||||||
if event["event"]{"contacts"} != nil:
|
if event["event"]{"contacts"} != nil:
|
||||||
for jsonContact in event["event"]["contacts"]:
|
for jsonContact in event["event"]["contacts"]:
|
||||||
signal.contacts.add(jsonContact.toProfile())
|
signal.contacts.add(jsonContact.toContactsDto())
|
||||||
|
|
||||||
var chatsWithMentions: seq[string] = @[]
|
|
||||||
|
|
||||||
if event["event"]{"messages"} != nil:
|
if event["event"]{"messages"} != nil:
|
||||||
for jsonMsg in event["event"]["messages"]:
|
for jsonMsg in event["event"]["messages"]:
|
||||||
var message = jsonMsg.toMessage()
|
var message = jsonMsg.toMessageDto()
|
||||||
if message.hasMention:
|
|
||||||
chatsWithMentions.add(message.chatId)
|
|
||||||
signal.messages.add(message)
|
signal.messages.add(message)
|
||||||
|
|
||||||
if event["event"]{"chats"} != nil:
|
if event["event"]{"chats"} != nil:
|
||||||
for jsonChat in event["event"]["chats"]:
|
for jsonChat in event["event"]["chats"]:
|
||||||
var chat = jsonChat.toChat
|
var chat = jsonChat.toChatDto()
|
||||||
if chatsWithMentions.contains(chat.id):
|
|
||||||
chat.mentionsCount.inc
|
|
||||||
signal.chats.add(chat)
|
signal.chats.add(chat)
|
||||||
|
|
||||||
if event["event"]{"statusUpdates"} != nil:
|
if event["event"]{"statusUpdates"} != nil:
|
||||||
for jsonStatusUpdate in event["event"]["statusUpdates"]:
|
for jsonStatusUpdate in event["event"]["statusUpdates"]:
|
||||||
var statusUpdate = jsonStatusUpdate.toStatusUpdate
|
var statusUpdate = jsonStatusUpdate.toStatusUpdateDto()
|
||||||
signal.statusUpdates.add(statusUpdate)
|
signal.statusUpdates.add(statusUpdate)
|
||||||
|
|
||||||
if event["event"]{"installations"} != nil:
|
if event["event"]{"installations"} != nil:
|
||||||
@ -54,11 +54,11 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
|||||||
|
|
||||||
if event["event"]{"emojiReactions"} != nil:
|
if event["event"]{"emojiReactions"} != nil:
|
||||||
for jsonReaction in event["event"]["emojiReactions"]:
|
for jsonReaction in event["event"]["emojiReactions"]:
|
||||||
signal.emojiReactions.add(jsonReaction.toReaction)
|
signal.emojiReactions.add(jsonReaction.toReactionDto())
|
||||||
|
|
||||||
if event["event"]{"communities"} != nil:
|
if event["event"]{"communities"} != nil:
|
||||||
for jsonCommunity in event["event"]["communities"]:
|
for jsonCommunity in event["event"]["communities"]:
|
||||||
signal.communities.add(jsonCommunity.toCommunity)
|
signal.communities.add(jsonCommunity.toCommunityDto())
|
||||||
|
|
||||||
if event["event"]{"requestsToJoinCommunity"} != nil:
|
if event["event"]{"requestsToJoinCommunity"} != nil:
|
||||||
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
||||||
@ -73,23 +73,25 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
|||||||
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification())
|
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification())
|
||||||
|
|
||||||
if event["event"]{"pinMessages"} != nil:
|
if event["event"]{"pinMessages"} != nil:
|
||||||
for jsonPinnedMessage in event["event"]["pinMessages"]:
|
discard
|
||||||
var contentType: ContentType
|
# Need to refactor this
|
||||||
try:
|
# for jsonPinnedMessage in event["event"]["pinMessages"]:
|
||||||
contentType = ContentType(jsonPinnedMessage{"contentType"}.getInt)
|
# var contentType: ContentType
|
||||||
except:
|
# try:
|
||||||
contentType = ContentType.Message
|
# contentType = ContentType(jsonPinnedMessage{"contentType"}.getInt)
|
||||||
signal.pinnedMessages.add(Message(
|
# except:
|
||||||
id: jsonPinnedMessage{"message_id"}.getStr,
|
# contentType = ContentType.Message
|
||||||
chatId: jsonPinnedMessage{"chat_id"}.getStr,
|
# signal.pinnedMessages.add(Message(
|
||||||
localChatId: jsonPinnedMessage{"localChatId"}.getStr,
|
# id: jsonPinnedMessage{"message_id"}.getStr,
|
||||||
pinnedBy: jsonPinnedMessage{"from"}.getStr,
|
# chatId: jsonPinnedMessage{"chat_id"}.getStr,
|
||||||
identicon: jsonPinnedMessage{"identicon"}.getStr,
|
# localChatId: jsonPinnedMessage{"localChatId"}.getStr,
|
||||||
alias: jsonPinnedMessage{"alias"}.getStr,
|
# pinnedBy: jsonPinnedMessage{"from"}.getStr,
|
||||||
clock: jsonPinnedMessage{"clock"}.getInt,
|
# identicon: jsonPinnedMessage{"identicon"}.getStr,
|
||||||
isPinned: jsonPinnedMessage{"pinned"}.getBool,
|
# alias: jsonPinnedMessage{"alias"}.getStr,
|
||||||
contentType: contentType
|
# clock: jsonPinnedMessage{"clock"}.getInt,
|
||||||
))
|
# isPinned: jsonPinnedMessage{"pinned"}.getBool,
|
||||||
|
# contentType: contentType
|
||||||
|
# ))
|
||||||
|
|
||||||
result = signal
|
result = signal
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ QtObject:
|
|||||||
self.activeSubItemChanged()
|
self.activeSubItemChanged()
|
||||||
|
|
||||||
proc getId(self: ActiveItem): string {.slot.} =
|
proc getId(self: ActiveItem): string {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return ""
|
||||||
return self.item.id
|
return self.item.id
|
||||||
|
|
||||||
QtProperty[string] id:
|
QtProperty[string] id:
|
||||||
@ -48,54 +50,72 @@ QtObject:
|
|||||||
read = getIsSubItemActive
|
read = getIsSubItemActive
|
||||||
|
|
||||||
proc getName(self: ActiveItem): string {.slot.} =
|
proc getName(self: ActiveItem): string {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return ""
|
||||||
return self.item.name
|
return self.item.name
|
||||||
|
|
||||||
QtProperty[string] name:
|
QtProperty[string] name:
|
||||||
read = getName
|
read = getName
|
||||||
|
|
||||||
proc getIcon(self: ActiveItem): string {.slot.} =
|
proc getIcon(self: ActiveItem): string {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return ""
|
||||||
return self.item.icon
|
return self.item.icon
|
||||||
|
|
||||||
QtProperty[string] icon:
|
QtProperty[string] icon:
|
||||||
read = getIcon
|
read = getIcon
|
||||||
|
|
||||||
proc getColor(self: ActiveItem): string {.slot.} =
|
proc getColor(self: ActiveItem): string {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return ""
|
||||||
return self.item.color
|
return self.item.color
|
||||||
|
|
||||||
QtProperty[string] color:
|
QtProperty[string] color:
|
||||||
read = getColor
|
read = getColor
|
||||||
|
|
||||||
proc getDescription(self: ActiveItem): string {.slot.} =
|
proc getDescription(self: ActiveItem): string {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return ""
|
||||||
return self.item.description
|
return self.item.description
|
||||||
|
|
||||||
QtProperty[string] description:
|
QtProperty[string] description:
|
||||||
read = getDescription
|
read = getDescription
|
||||||
|
|
||||||
proc getType(self: ActiveItem): int {.slot.} =
|
proc getType(self: ActiveItem): int {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return 0
|
||||||
return self.item.`type`
|
return self.item.`type`
|
||||||
|
|
||||||
QtProperty[int] type:
|
QtProperty[int] type:
|
||||||
read = getType
|
read = getType
|
||||||
|
|
||||||
proc getHasUnreadMessages(self: ActiveItem): bool {.slot.} =
|
proc getHasUnreadMessages(self: ActiveItem): bool {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return false
|
||||||
return self.item.hasUnreadMessages
|
return self.item.hasUnreadMessages
|
||||||
|
|
||||||
QtProperty[bool] hasUnreadMessages:
|
QtProperty[bool] hasUnreadMessages:
|
||||||
read = getHasUnreadMessages
|
read = getHasUnreadMessages
|
||||||
|
|
||||||
proc getNotificationCount(self: ActiveItem): int {.slot.} =
|
proc getNotificationCount(self: ActiveItem): int {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return 0
|
||||||
return self.item.notificationsCount
|
return self.item.notificationsCount
|
||||||
|
|
||||||
QtProperty[int] notificationCount:
|
QtProperty[int] notificationCount:
|
||||||
read = getNotificationCount
|
read = getNotificationCount
|
||||||
|
|
||||||
proc getMuted(self: ActiveItem): bool {.slot.} =
|
proc getMuted(self: ActiveItem): bool {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return false
|
||||||
return self.item.muted
|
return self.item.muted
|
||||||
|
|
||||||
QtProperty[bool] muted:
|
QtProperty[bool] muted:
|
||||||
read = getMuted
|
read = getMuted
|
||||||
|
|
||||||
proc getPosition(self: ActiveItem): int {.slot.} =
|
proc getPosition(self: ActiveItem): int {.slot.} =
|
||||||
|
if(self.item.isNil):
|
||||||
|
return 0
|
||||||
return self.item.position
|
return self.item.position
|
||||||
|
|
||||||
QtProperty[int] position:
|
QtProperty[int] position:
|
||||||
|
@ -18,18 +18,20 @@ type
|
|||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
chatId: string
|
chatId: string
|
||||||
belongsToCommunity: bool
|
belongsToCommunity: bool
|
||||||
|
isUsersListAvailable: bool #users list is not available for 1:1 chat
|
||||||
chatService: chat_service.ServiceInterface
|
chatService: chat_service.ServiceInterface
|
||||||
communityService: community_service.ServiceInterface
|
communityService: community_service.ServiceInterface
|
||||||
messageService: message_service.Service
|
messageService: message_service.Service
|
||||||
|
|
||||||
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, chatId: string,
|
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, chatId: string,
|
||||||
belongsToCommunity: bool, chatService: chat_service.ServiceInterface,
|
belongsToCommunity: bool, isUsersListAvailable: bool, chatService: chat_service.ServiceInterface,
|
||||||
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
|
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
|
||||||
result = Controller()
|
result = Controller()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.events = events
|
result.events = events
|
||||||
result.chatId = chatId
|
result.chatId = chatId
|
||||||
result.belongsToCommunity = belongsToCommunity
|
result.belongsToCommunity = belongsToCommunity
|
||||||
|
result.isUsersListAvailable = isUsersListAvailable
|
||||||
result.chatService = chatService
|
result.chatService = chatService
|
||||||
result.communityService = communityService
|
result.communityService = communityService
|
||||||
result.messageService = messageService
|
result.messageService = messageService
|
||||||
@ -67,4 +69,7 @@ method unpinMessage*(self: Controller, messageId: string) =
|
|||||||
|
|
||||||
method getMessageDetails*(self: Controller, messageId: string):
|
method getMessageDetails*(self: Controller, messageId: string):
|
||||||
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] =
|
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] =
|
||||||
return self.messageService.getDetailsForMessage(self.chatId, messageId)
|
return self.messageService.getDetailsForMessage(self.chatId, messageId)
|
||||||
|
|
||||||
|
method isUsersListAvailable*(self: Controller): bool =
|
||||||
|
return self.isUsersListAvailable
|
@ -21,4 +21,7 @@ method unpinMessage*(self: AccessInterface, messageId: string) {.base.} =
|
|||||||
|
|
||||||
method getMessageDetails*(self: AccessInterface, messageId: string):
|
method getMessageDetails*(self: AccessInterface, messageId: string):
|
||||||
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] {.base.} =
|
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method isUsersListAvailable*(self: AccessInterface): bool {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
@ -10,6 +10,7 @@ import input_area/module as input_area_module
|
|||||||
import messages/module as messages_module
|
import messages/module as messages_module
|
||||||
import users/module as users_module
|
import users/module as users_module
|
||||||
|
|
||||||
|
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
|
||||||
@ -32,7 +33,8 @@ type
|
|||||||
usersModule: users_module.AccessInterface
|
usersModule: users_module.AccessInterface
|
||||||
moduleLoaded: bool
|
moduleLoaded: bool
|
||||||
|
|
||||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, chatId: string, belongsToCommunity: bool,
|
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||||
|
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
|
||||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
messageService: message_service.Service):
|
messageService: message_service.Service):
|
||||||
Module =
|
Module =
|
||||||
@ -40,14 +42,15 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
|
|||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.view = view.newView(result)
|
result.view = view.newView(result)
|
||||||
result.viewVariant = newQVariant(result.view)
|
result.viewVariant = newQVariant(result.view)
|
||||||
result.controller = controller.newController(result, events, chatId, belongsToCommunity, chatService, communityService,
|
result.controller = controller.newController(result, events, chatId, belongsToCommunity, isUsersListAvailable,
|
||||||
messageService)
|
chatService, communityService, messageService)
|
||||||
result.moduleLoaded = false
|
result.moduleLoaded = false
|
||||||
|
|
||||||
result.inputAreaModule = input_area_module.newModule(result, chatId, belongsToCommunity, chatService, communityService)
|
result.inputAreaModule = input_area_module.newModule(result, chatId, belongsToCommunity, chatService, communityService)
|
||||||
result.messagesModule = messages_module.newModule(result, events, chatId, belongsToCommunity, chatService,
|
result.messagesModule = messages_module.newModule(result, events, chatId, belongsToCommunity, chatService,
|
||||||
communityService, messageService)
|
communityService, messageService)
|
||||||
result.usersModule = users_module.newModule(result, chatId, belongsToCommunity, chatService, communityService)
|
result.usersModule = users_module.newModule(result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,
|
||||||
|
contactService, communityService, messageService)
|
||||||
|
|
||||||
method delete*(self: Module) =
|
method delete*(self: Module) =
|
||||||
self.inputAreaModule.delete
|
self.inputAreaModule.delete
|
||||||
@ -147,4 +150,6 @@ method onPinMessage*(self: Module, messageId: string) =
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.view.model.appendItem(item)
|
self.view.model.appendItem(item)
|
||||||
|
|
||||||
|
method isUsersListAvailable*(self: Module): bool =
|
||||||
|
self.controller.isUsersListAvailable()
|
@ -13,4 +13,7 @@ method getUsersModule*(self: AccessInterface): QVariant {.base.} =
|
|||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method unpinMessage*(self: AccessInterface, messageId: string) {.base.} =
|
method unpinMessage*(self: AccessInterface, messageId: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method isUsersListAvailable*(self: AccessInterface): bool {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
@ -1,33 +1,80 @@
|
|||||||
|
import sequtils, sugar
|
||||||
import controller_interface
|
import controller_interface
|
||||||
import io_interface
|
import io_interface
|
||||||
|
|
||||||
|
import ../../../../../../app_service/service/contacts/service as contact_service
|
||||||
import ../../../../../../app_service/service/community/service_interface as community_service
|
import ../../../../../../app_service/service/community/service_interface as community_service
|
||||||
|
import ../../../../../../app_service/service/message/service as message_service
|
||||||
|
|
||||||
|
import eventemitter
|
||||||
|
|
||||||
export controller_interface
|
export controller_interface
|
||||||
|
|
||||||
type
|
type
|
||||||
Controller* = ref object of controller_interface.AccessInterface
|
Controller* = ref object of controller_interface.AccessInterface
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
|
events: EventEmitter
|
||||||
|
sectionId: string
|
||||||
chatId: string
|
chatId: string
|
||||||
belongsToCommunity: bool
|
belongsToCommunity: bool
|
||||||
|
isUsersListAvailable: bool #users list is not available for 1:1 chat
|
||||||
|
contactService: contact_service.Service
|
||||||
communityService: community_service.ServiceInterface
|
communityService: community_service.ServiceInterface
|
||||||
|
messageService: message_service.Service
|
||||||
|
|
||||||
proc newController*(delegate: io_interface.AccessInterface, chatId: string, belongsToCommunity: bool,
|
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||||
communityService: community_service.ServiceInterface): Controller =
|
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
|
||||||
|
communityService: community_service.ServiceInterface, messageService: message_service.Service):
|
||||||
|
Controller =
|
||||||
result = Controller()
|
result = Controller()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
|
result.events = events
|
||||||
|
result.sectionId = sectionId
|
||||||
result.chatId = chatId
|
result.chatId = chatId
|
||||||
result.belongsToCommunity = belongsToCommunity
|
result.belongsToCommunity = belongsToCommunity
|
||||||
|
result.isUsersListAvailable = isUsersListAvailable
|
||||||
|
result.contactService = contactService
|
||||||
result.communityService = communityService
|
result.communityService = communityService
|
||||||
|
result.messageService = messageService
|
||||||
|
|
||||||
method delete*(self: Controller) =
|
method delete*(self: Controller) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
method init*(self: Controller) =
|
method init*(self: Controller) =
|
||||||
discard
|
if(self.isUsersListAvailable):
|
||||||
|
self.events.on(SIGNAL_MESSAGES_LOADED) do(e:Args):
|
||||||
|
let args = MessagesLoadedArgs(e)
|
||||||
|
if(self.chatId != args.chatId):
|
||||||
|
return
|
||||||
|
|
||||||
method getChatId*(self: Controller): string =
|
self.delegate.newMessagesLoaded(args.messages)
|
||||||
return self.chatId
|
|
||||||
|
|
||||||
method belongsToCommunity*(self: Controller): bool =
|
self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args):
|
||||||
return self.belongsToCommunity
|
var args = ContactNicknameUpdatedArgs(e)
|
||||||
|
self.delegate.contactNicknameChanged(args.contactId, args.nickname)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_CONTACTS_STATUS_UPDATED) do(e: Args):
|
||||||
|
var args = ContactsStatusUpdatedArgs(e)
|
||||||
|
self.delegate.contactsStatusUpdated(args.statusUpdates)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_CONTACT_UPDATED) do(e: Args):
|
||||||
|
var args = ContactUpdatedArgs(e)
|
||||||
|
self.delegate.contactUpdated(args.contact)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED) do(e: Args):
|
||||||
|
self.delegate.loggedInUserImageChanged()
|
||||||
|
|
||||||
|
method getMembersPublicKeys*(self: Controller): seq[string] =
|
||||||
|
# in case of chat section, there is no a members list
|
||||||
|
if(not self.belongsToCommunity):
|
||||||
|
return
|
||||||
|
|
||||||
|
let communityDto = self.communityService.getCommunityById(self.sectionId)
|
||||||
|
result = communityDto.members.map(x => x.id)
|
||||||
|
|
||||||
|
method getContactNameAndImage*(self: Controller, contactId: string):
|
||||||
|
tuple[name: string, image: string, isIdenticon: bool] =
|
||||||
|
return self.contactService.getContactNameAndImage(contactId)
|
||||||
|
|
||||||
|
method getStatusForContact*(self: Controller, contactId: string): StatusUpdateDto =
|
||||||
|
return self.contactService.getStatusForContactWithId(contactId)
|
@ -1,4 +1,4 @@
|
|||||||
import ../../../../../../app_service/service/community/service_interface as community_service
|
import ../../../../../../app_service/service/contacts/service as contacts_service
|
||||||
|
|
||||||
type
|
type
|
||||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||||
@ -13,7 +13,12 @@ method init*(self: AccessInterface) {.base.} =
|
|||||||
method getChatId*(self: AccessInterface): string {.base.} =
|
method getChatId*(self: AccessInterface): string {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
|
method getMembersPublicKeys*(self: AccessInterface): seq[string] {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getContactNameAndImage*(self: AccessInterface, contactId: string):
|
||||||
|
tuple[name: string, image: string, isIdenticon: bool] {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getStatusForContact*(self: AccessInterface, contactId: string): StatusUpdateDto {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
@ -1,24 +1,26 @@
|
|||||||
type
|
type
|
||||||
OnlineStatus* {.pure.} = enum
|
OnlineStatus* {.pure.} = enum
|
||||||
Online = 0
|
Offline = 0
|
||||||
Idle
|
Online
|
||||||
DoNotDisturb
|
DoNotDisturb
|
||||||
|
Idle
|
||||||
Invisible
|
Invisible
|
||||||
Offline
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Item* = ref object
|
Item* = ref object
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
onlineStatus: OnlineStatus
|
onlineStatus: OnlineStatus
|
||||||
identicon: string
|
icon: string
|
||||||
|
isIdenticon: bool
|
||||||
|
|
||||||
proc initItem*(id: string, name: string, onlineStatus: OnlineStatus, identicon: string): Item =
|
proc initItem*(id: string, name: string, onlineStatus: OnlineStatus, icon: string, isidenticon: bool): Item =
|
||||||
result = Item()
|
result = Item()
|
||||||
result.id = id
|
result.id = id
|
||||||
result.name = name
|
result.name = name
|
||||||
result.onlineStatus = onlineStatus
|
result.onlineStatus = onlineStatus
|
||||||
result.identicon = identicon
|
result.icon = icon
|
||||||
|
result.isIdenticon = isidenticon
|
||||||
|
|
||||||
proc id*(self: Item): string {.inline.} =
|
proc id*(self: Item): string {.inline.} =
|
||||||
self.id
|
self.id
|
||||||
@ -35,5 +37,14 @@ proc onlineStatus*(self: Item): OnlineStatus {.inline.} =
|
|||||||
proc `onlineStatus=`*(self: Item, value: OnlineStatus) {.inline.} =
|
proc `onlineStatus=`*(self: Item, value: OnlineStatus) {.inline.} =
|
||||||
self.onlineStatus = value
|
self.onlineStatus = value
|
||||||
|
|
||||||
proc identicon*(self: Item): string {.inline.} =
|
proc icon*(self: Item): string {.inline.} =
|
||||||
self.identicon
|
self.icon
|
||||||
|
|
||||||
|
proc `icon=`*(self: Item, value: string) {.inline.} =
|
||||||
|
self.icon = value
|
||||||
|
|
||||||
|
proc isIdenticon*(self: Item): bool {.inline.} =
|
||||||
|
self.isIdenticon
|
||||||
|
|
||||||
|
proc `isIdenticon=`*(self: Item, value: bool) {.inline.} =
|
||||||
|
self.isIdenticon = value
|
@ -7,7 +7,8 @@ type
|
|||||||
Id = UserRole + 1
|
Id = UserRole + 1
|
||||||
Name
|
Name
|
||||||
OnlineStatus
|
OnlineStatus
|
||||||
Identicon
|
Icon
|
||||||
|
IsIdenticon
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
@ -33,7 +34,8 @@ QtObject:
|
|||||||
ModelRole.Id.int:"id",
|
ModelRole.Id.int:"id",
|
||||||
ModelRole.Name.int:"name",
|
ModelRole.Name.int:"name",
|
||||||
ModelRole.OnlineStatus.int:"onlineStatus",
|
ModelRole.OnlineStatus.int:"onlineStatus",
|
||||||
ModelRole.Identicon.int:"identicon",
|
ModelRole.Icon.int:"icon",
|
||||||
|
ModelRole.IsIdenticon.int:"isIdenticon",
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||||
@ -53,13 +55,30 @@ QtObject:
|
|||||||
result = newQVariant(item.name)
|
result = newQVariant(item.name)
|
||||||
of ModelRole.OnlineStatus:
|
of ModelRole.OnlineStatus:
|
||||||
result = newQVariant(item.onlineStatus.int)
|
result = newQVariant(item.onlineStatus.int)
|
||||||
of ModelRole.Identicon:
|
of ModelRole.Icon:
|
||||||
result = newQVariant(item.identicon)
|
result = newQVariant(item.icon)
|
||||||
|
of ModelRole.IsIdenticon:
|
||||||
|
result = newQVariant(item.isIdenticon)
|
||||||
|
|
||||||
proc setItems*(self: Model, items: seq[Item]) =
|
proc addItem*(self: Model, item: Item) =
|
||||||
self.beginResetModel()
|
# we need to maintain online contact on top, that means
|
||||||
self.items = items
|
# if we add an item online status we add it as the last online item (before the first offline item)
|
||||||
self.endResetModel()
|
# if we add an item with offline status we add it as the first offline item (after the last online item)
|
||||||
|
var position = -1
|
||||||
|
for i in 0 ..< self.items.len:
|
||||||
|
if(self.items[i].onlineStatus == OnlineStatus.Offline):
|
||||||
|
position = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if(position == -1):
|
||||||
|
position = self.items.len
|
||||||
|
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
self.beginInsertRows(parentModelIndex, position, position)
|
||||||
|
self.items.insert(item, position)
|
||||||
|
self.endInsertRows()
|
||||||
|
|
||||||
proc findIndexForMessageId(self: Model, id: string): int =
|
proc findIndexForMessageId(self: Model, id: string): int =
|
||||||
for i in 0 ..< self.items.len:
|
for i in 0 ..< self.items.len:
|
||||||
@ -68,6 +87,17 @@ QtObject:
|
|||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
proc removeItemWithIndex(self: Model, index: int) =
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
|
||||||
|
self.beginRemoveRows(parentModelIndex, index, index)
|
||||||
|
self.items.delete(index)
|
||||||
|
self.endRemoveRows()
|
||||||
|
|
||||||
|
proc isContactWithIdAdded*(self: Model, id: string): bool =
|
||||||
|
return self.findIndexForMessageId(id) != -1
|
||||||
|
|
||||||
proc setName*(self: Model, id: string, name: string) =
|
proc setName*(self: Model, id: string, name: string) =
|
||||||
let ind = self.findIndexForMessageId(id)
|
let ind = self.findIndexForMessageId(id)
|
||||||
if(ind == -1):
|
if(ind == -1):
|
||||||
@ -78,12 +108,38 @@ QtObject:
|
|||||||
let index = self.createIndex(ind, 0, nil)
|
let index = self.createIndex(ind, 0, nil)
|
||||||
self.dataChanged(index, index, @[ModelRole.Name.int])
|
self.dataChanged(index, index, @[ModelRole.Name.int])
|
||||||
|
|
||||||
|
proc setIcon*(self: Model, id: string, icon: string, isIdenticon: bool) =
|
||||||
|
let ind = self.findIndexForMessageId(id)
|
||||||
|
if(ind == -1):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.items[ind].icon = icon
|
||||||
|
self.items[ind].isIdenticon = isIdenticon
|
||||||
|
|
||||||
|
let index = self.createIndex(ind, 0, nil)
|
||||||
|
self.dataChanged(index, index, @[ModelRole.Icon.int, ModelRole.IsIdenticon.int])
|
||||||
|
|
||||||
|
proc updateItem*(self: Model, id: string, name: string, icon: string, isIdenticon: bool) =
|
||||||
|
let ind = self.findIndexForMessageId(id)
|
||||||
|
if(ind == -1):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.items[ind].name = name
|
||||||
|
self.items[ind].icon = icon
|
||||||
|
self.items[ind].isIdenticon = isIdenticon
|
||||||
|
|
||||||
|
let index = self.createIndex(ind, 0, nil)
|
||||||
|
self.dataChanged(index, index, @[ModelRole.Name.int, ModelRole.Icon.int, ModelRole.IsIdenticon.int])
|
||||||
|
|
||||||
proc setOnlineStatus*(self: Model, id: string, onlineStatus: OnlineStatus) =
|
proc setOnlineStatus*(self: Model, id: string, onlineStatus: OnlineStatus) =
|
||||||
let ind = self.findIndexForMessageId(id)
|
let ind = self.findIndexForMessageId(id)
|
||||||
if(ind == -1):
|
if(ind == -1):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.items[ind].onlineStatus = onlineStatus
|
if(self.items[ind].onlineStatus == onlineStatus):
|
||||||
|
return
|
||||||
let index = self.createIndex(ind, 0, nil)
|
|
||||||
self.dataChanged(index, index, @[ModelRole.OnlineStatus.int])
|
var item = self.items[ind]
|
||||||
|
item.onlineStatus = onlineStatus
|
||||||
|
self.removeItemWithIndex(ind)
|
||||||
|
self.addItem(item)
|
@ -1,11 +1,14 @@
|
|||||||
import NimQml
|
import NimQml
|
||||||
import io_interface
|
import io_interface
|
||||||
import ../io_interface as delegate_interface
|
import ../io_interface as delegate_interface
|
||||||
import view, controller
|
import view, item, model, controller
|
||||||
import ../../../../../global/global_singleton
|
import ../../../../../global/global_singleton
|
||||||
|
|
||||||
import ../../../../../../app_service/service/chat/service_interface as chat_service
|
import ../../../../../../app_service/service/contacts/service as contact_service
|
||||||
import ../../../../../../app_service/service/community/service_interface as community_service
|
import ../../../../../../app_service/service/community/service_interface as community_service
|
||||||
|
import ../../../../../../app_service/service/message/service as message_service
|
||||||
|
|
||||||
|
import eventemitter
|
||||||
|
|
||||||
export io_interface
|
export io_interface
|
||||||
|
|
||||||
@ -17,14 +20,16 @@ type
|
|||||||
controller: controller.AccessInterface
|
controller: controller.AccessInterface
|
||||||
moduleLoaded: bool
|
moduleLoaded: bool
|
||||||
|
|
||||||
proc newModule*(delegate: delegate_interface.AccessInterface, chatId: string, belongsToCommunity: bool,
|
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface):
|
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
|
||||||
|
communityService: community_service.ServiceInterface, messageService: message_service.Service):
|
||||||
Module =
|
Module =
|
||||||
result = Module()
|
result = Module()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.view = view.newView(result)
|
result.view = view.newView(result)
|
||||||
result.viewVariant = newQVariant(result.view)
|
result.viewVariant = newQVariant(result.view)
|
||||||
result.controller = controller.newController(result, chatId, belongsToCommunity, communityService)
|
result.controller = controller.newController(result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,
|
||||||
|
contactService, communityService, messageService)
|
||||||
result.moduleLoaded = false
|
result.moduleLoaded = false
|
||||||
|
|
||||||
method delete*(self: Module) =
|
method delete*(self: Module) =
|
||||||
@ -33,8 +38,6 @@ method delete*(self: Module) =
|
|||||||
self.controller.delete
|
self.controller.delete
|
||||||
|
|
||||||
method load*(self: Module) =
|
method load*(self: Module) =
|
||||||
singletonInstance.engine.setRootContextProperty("usersModule", self.viewVariant)
|
|
||||||
|
|
||||||
self.controller.init()
|
self.controller.init()
|
||||||
self.view.load()
|
self.view.load()
|
||||||
|
|
||||||
@ -42,8 +45,56 @@ method isLoaded*(self: Module): bool =
|
|||||||
return self.moduleLoaded
|
return self.moduleLoaded
|
||||||
|
|
||||||
method viewDidLoad*(self: Module) =
|
method viewDidLoad*(self: Module) =
|
||||||
|
# add me as the first user to the list
|
||||||
|
let loggedInUserDisplayName = singletonInstance.userProfile.getName() & "(You)"
|
||||||
|
self.view.model().addItem(initItem(singletonInstance.userProfile.getPubKey(), loggedInUserDisplayName,
|
||||||
|
OnlineStatus.Online, singletonInstance.userProfile.getIcon(), singletonInstance.userProfile.getIsIdenticon()))
|
||||||
|
|
||||||
|
# add other memebers
|
||||||
|
let usersKeys = self.controller.getMembersPublicKeys()
|
||||||
|
for k in usersKeys:
|
||||||
|
let (name, image, isIdenticon) = self.controller.getContactNameAndImage(k)
|
||||||
|
let statusUpdateDto = self.controller.getStatusForContact(k)
|
||||||
|
let status = statusUpdateDto.statusType.int.OnlineStatus
|
||||||
|
self.view.model().addItem(initItem(k, name, status, image, isidenticon))
|
||||||
|
|
||||||
self.moduleLoaded = true
|
self.moduleLoaded = true
|
||||||
self.delegate.usersDidLoad()
|
self.delegate.usersDidLoad()
|
||||||
|
|
||||||
method getModuleAsVariant*(self: Module): QVariant =
|
method getModuleAsVariant*(self: Module): QVariant =
|
||||||
return self.viewVariant
|
return self.viewVariant
|
||||||
|
|
||||||
|
method newMessagesLoaded*(self: Module, messages: seq[MessageDto]) =
|
||||||
|
for m in messages:
|
||||||
|
if(self.view.model().isContactWithIdAdded(m.`from`)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
let (name, image, isIdenticon) = self.controller.getContactNameAndImage(m.`from`)
|
||||||
|
let statusUpdateDto = self.controller.getStatusForContact(m.`from`)
|
||||||
|
let status = statusUpdateDto.statusType.int.OnlineStatus
|
||||||
|
self.view.model().addItem(initItem(m.`from`, name, status, image, isidenticon))
|
||||||
|
|
||||||
|
method contactNicknameChanged*(self: Module, publicKey: string, nickname: string) =
|
||||||
|
if(nickname.len == 0):
|
||||||
|
let (name, _, _) = self.controller.getContactNameAndImage(publicKey)
|
||||||
|
self.view.model().setName(publicKey, name)
|
||||||
|
else:
|
||||||
|
self.view.model().setName(publicKey, nickname)
|
||||||
|
|
||||||
|
method contactsStatusUpdated*(self: Module, statusUpdates: seq[StatusUpdateDto]) =
|
||||||
|
for s in statusUpdates:
|
||||||
|
let status = s.statusType.int.OnlineStatus
|
||||||
|
self.view.model().setOnlineStatus(s.publicKey, status)
|
||||||
|
|
||||||
|
method contactUpdated*(self: Module, contact: ContactsDto) =
|
||||||
|
var icon = contact.identicon
|
||||||
|
var isIdenticon = contact.identicon.len > 0
|
||||||
|
if(contact.image.thumbnail.len > 0):
|
||||||
|
icon = contact.image.thumbnail
|
||||||
|
isIdenticon = false
|
||||||
|
|
||||||
|
self.view.model().updateItem(contact.id, contact.userNameOrAlias(), icon, isIdenticon)
|
||||||
|
|
||||||
|
method loggedInUserImageChanged*(self: Module) =
|
||||||
|
self.view.model().setIcon(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getThumbnailImage(),
|
||||||
|
singletonInstance.userProfile.getIsIdenticon())
|
@ -0,0 +1,17 @@
|
|||||||
|
import ../../../../../../../app_service/service/message/dto/[message]
|
||||||
|
import ../../../../../../../app_service/service/contacts/dto/[contacts, status_update]
|
||||||
|
|
||||||
|
method newMessagesLoaded*(self: AccessInterface, messages: seq[MessageDto]) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method contactNicknameChanged*(self: AccessInterface, publicKey: string, nickname: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method contactsStatusUpdated*(self: AccessInterface, statusUpdates: seq[StatusUpdateDto]) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method contactUpdated*(self: AccessInterface, contact: ContactsDto) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method loggedInUserImageChanged*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
@ -7,9 +7,11 @@ QtObject:
|
|||||||
View* = ref object of QObject
|
View* = ref object of QObject
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
model: Model
|
model: Model
|
||||||
|
modelVariant: QVariant
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
|
self.modelVariant.delete
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
|
||||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||||
@ -17,6 +19,19 @@ QtObject:
|
|||||||
result.QObject.setup
|
result.QObject.setup
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.model = newModel()
|
result.model = newModel()
|
||||||
|
result.modelVariant = newQVariant(result.model)
|
||||||
|
|
||||||
|
proc model*(self: View): Model =
|
||||||
|
return self.model
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
|
|
||||||
|
proc modelChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc getModel(self: View): QVariant {.slot.} =
|
||||||
|
return self.modelVariant
|
||||||
|
|
||||||
|
QtProperty[QVariant] model:
|
||||||
|
read = getModel
|
||||||
|
notify = modelChanged
|
@ -52,4 +52,7 @@ QtObject:
|
|||||||
read = getModel
|
read = getModel
|
||||||
|
|
||||||
proc unpinMessage*(self: View, messageId: string) {.slot.} =
|
proc unpinMessage*(self: View, messageId: string) {.slot.} =
|
||||||
self.delegate.unpinMessage(messageId)
|
self.delegate.unpinMessage(messageId)
|
||||||
|
|
||||||
|
proc isUsersListAvailable*(self: View): bool {.slot.} =
|
||||||
|
return self.delegate.isUsersListAvailable()
|
@ -3,6 +3,7 @@ import Tables
|
|||||||
import controller_interface
|
import controller_interface
|
||||||
import io_interface
|
import io_interface
|
||||||
|
|
||||||
|
import ../../../../app_service/service/contacts/service as contact_service
|
||||||
import ../../../../app_service/service/chat/service_interface as chat_service
|
import ../../../../app_service/service/chat/service_interface as chat_service
|
||||||
import ../../../../app_service/service/community/service_interface as community_service
|
import ../../../../app_service/service/community/service_interface as community_service
|
||||||
import ../../../../app_service/service/message/service as message_service
|
import ../../../../app_service/service/message/service as message_service
|
||||||
@ -19,19 +20,20 @@ type
|
|||||||
activeItemId: string
|
activeItemId: string
|
||||||
activeSubItemId: string
|
activeSubItemId: string
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
|
contactService: contact_service.Service
|
||||||
chatService: chat_service.ServiceInterface
|
chatService: chat_service.ServiceInterface
|
||||||
communityService: community_service.ServiceInterface
|
communityService: community_service.ServiceInterface
|
||||||
messageService: message_service.Service
|
messageService: message_service.Service
|
||||||
|
|
||||||
proc newController*(delegate: io_interface.AccessInterface, sectionId: string, isCommunity: bool, events: EventEmitter,
|
proc newController*(delegate: io_interface.AccessInterface, sectionId: string, isCommunity: bool, events: EventEmitter,
|
||||||
chatService: chat_service.ServiceInterface,
|
contactService: contact_service.Service, chatService: chat_service.ServiceInterface,
|
||||||
communityService: community_service.ServiceInterface,
|
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
|
||||||
messageService: message_service.Service): Controller =
|
|
||||||
result = Controller()
|
result = Controller()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.sectionId = sectionId
|
result.sectionId = sectionId
|
||||||
result.isCommunitySection = isCommunity
|
result.isCommunitySection = isCommunity
|
||||||
result.events = events
|
result.events = events
|
||||||
|
result.contactService = contactService
|
||||||
result.chatService = chatService
|
result.chatService = chatService
|
||||||
result.communityService = communityService
|
result.communityService = communityService
|
||||||
result.messageService = messageService
|
result.messageService = messageService
|
||||||
@ -87,5 +89,5 @@ method getOneToOneChatNameAndImage*(self: Controller, chatId: string):
|
|||||||
method createPublicChat*(self: Controller, chatId: string) =
|
method createPublicChat*(self: Controller, chatId: string) =
|
||||||
let response = self.chatService.createPublicChat(chatId)
|
let response = self.chatService.createPublicChat(chatId)
|
||||||
if(response.success):
|
if(response.success):
|
||||||
self.delegate.addNewPublicChat(response.chatDto, self.events, self.chatService, self.communityService,
|
self.delegate.addNewPublicChat(response.chatDto, self.events, self.contactService, self.chatService,
|
||||||
self.messageService)
|
self.communityService, self.messageService)
|
@ -5,6 +5,7 @@ import view, controller, item, sub_item, model, sub_model
|
|||||||
|
|
||||||
import chat_content/module as chat_content_module
|
import chat_content/module as chat_content_module
|
||||||
|
|
||||||
|
import ../../../../app_service/service/contacts/service as contact_service
|
||||||
import ../../../../app_service/service/chat/service_interface as chat_service
|
import ../../../../app_service/service/chat/service_interface as chat_service
|
||||||
import ../../../../app_service/service/community/service_interface as community_service
|
import ../../../../app_service/service/community/service_interface as community_service
|
||||||
import ../../../../app_service/service/message/service as message_service
|
import ../../../../app_service/service/message/service as message_service
|
||||||
@ -31,6 +32,7 @@ proc newModule*(
|
|||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
sectionId: string,
|
sectionId: string,
|
||||||
isCommunity: bool,
|
isCommunity: bool,
|
||||||
|
contactService: contact_service.Service,
|
||||||
chatService: chat_service.ServiceInterface,
|
chatService: chat_service.ServiceInterface,
|
||||||
communityService: community_service.ServiceInterface,
|
communityService: community_service.ServiceInterface,
|
||||||
messageService: message_service.Service
|
messageService: message_service.Service
|
||||||
@ -39,8 +41,8 @@ proc newModule*(
|
|||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.view = view.newView(result)
|
result.view = view.newView(result)
|
||||||
result.viewVariant = newQVariant(result.view)
|
result.viewVariant = newQVariant(result.view)
|
||||||
result.controller = controller.newController(result, sectionId, isCommunity, events, chatService, communityService,
|
result.controller = controller.newController(result, sectionId, isCommunity, events, contactService, chatService,
|
||||||
messageService)
|
communityService, messageService)
|
||||||
result.moduleLoaded = false
|
result.moduleLoaded = false
|
||||||
|
|
||||||
result.chatContentModule = initOrderedTable[string, chat_content_module.AccessInterface]()
|
result.chatContentModule = initOrderedTable[string, chat_content_module.AccessInterface]()
|
||||||
@ -56,14 +58,15 @@ method delete*(self: Module) =
|
|||||||
method isCommunity*(self: Module): bool =
|
method isCommunity*(self: Module): bool =
|
||||||
return self.controller.isCommunity()
|
return self.controller.isCommunity()
|
||||||
|
|
||||||
proc addSubmodule(self: Module, chatId: string, belongToCommunity: bool, events: EventEmitter,
|
proc addSubmodule(self: Module, chatId: string, belongToCommunity: bool, isUsersListAvailable: bool, events: EventEmitter,
|
||||||
|
contactService: contact_service.Service, chatService: chat_service.ServiceInterface,
|
||||||
|
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
||||||
|
self.chatContentModule[chatId] = chat_content_module.newModule(self, events, self.controller.getMySectionId(), chatId,
|
||||||
|
belongToCommunity, isUsersListAvailable, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
|
proc buildChatUI(self: Module, events: EventEmitter, contactService: contact_service.Service,
|
||||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
messageService: message_service.Service) =
|
messageService: message_service.Service) =
|
||||||
self.chatContentModule[chatId] = chat_content_module.newModule(self, events, chatId, belongToCommunity, chatService,
|
|
||||||
communityService, messageService)
|
|
||||||
|
|
||||||
proc buildChatUI(self: Module, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
|
||||||
communityService: community_service.ServiceInterface, messageService: message_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)
|
||||||
|
|
||||||
@ -74,13 +77,15 @@ proc buildChatUI(self: Module, events: EventEmitter, chatService: chat_service.S
|
|||||||
var chatName = c.name
|
var chatName = c.name
|
||||||
var chatImage = c.identicon
|
var chatImage = c.identicon
|
||||||
var isIdenticon = false
|
var isIdenticon = false
|
||||||
|
var isUsersListAvailable = true
|
||||||
if(c.chatType == ChatType.OneToOne):
|
if(c.chatType == ChatType.OneToOne):
|
||||||
|
isUsersListAvailable = false
|
||||||
(chatName, chatImage, isIdenticon) = self.controller.getOneToOneChatNameAndImage(c.id)
|
(chatName, chatImage, isIdenticon) = self.controller.getOneToOneChatNameAndImage(c.id)
|
||||||
|
|
||||||
let item = initItem(c.id, chatName, chatImage, isIdenticon, c.color, c.description, c.chatType.int, hasNotification,
|
let item = initItem(c.id, chatName, chatImage, isIdenticon, c.color, c.description, c.chatType.int, hasNotification,
|
||||||
notificationsCount, c.muted, false, 0)
|
notificationsCount, c.muted, false, 0)
|
||||||
self.view.appendItem(item)
|
self.view.appendItem(item)
|
||||||
self.addSubmodule(c.id, false, events, chatService, communityService, messageService)
|
self.addSubmodule(c.id, false, isUsersListAvailable, events, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
# 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):
|
||||||
@ -88,8 +93,9 @@ proc buildChatUI(self: Module, events: EventEmitter, chatService: chat_service.S
|
|||||||
|
|
||||||
self.setActiveItemSubItem(selectedItemId, "")
|
self.setActiveItemSubItem(selectedItemId, "")
|
||||||
|
|
||||||
proc buildCommunityUI(self: Module, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
proc buildCommunityUI(self: Module, events: EventEmitter, contactService: contact_service.Service,
|
||||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
|
messageService: message_service.Service) =
|
||||||
var selectedItemId = ""
|
var selectedItemId = ""
|
||||||
var selectedSubItemId = ""
|
var selectedSubItemId = ""
|
||||||
let communityIds = self.controller.getCommunityIds()
|
let communityIds = self.controller.getCommunityIds()
|
||||||
@ -107,7 +113,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter, chatService: chat_serv
|
|||||||
let channelItem = initItem(chatDto.id, chatDto.name, chatDto.identicon, false, chatDto.color, chatDto.description,
|
let channelItem = initItem(chatDto.id, chatDto.name, chatDto.identicon, false, chatDto.color, chatDto.description,
|
||||||
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
||||||
self.view.appendItem(channelItem)
|
self.view.appendItem(channelItem)
|
||||||
self.addSubmodule(chatDto.id, true, events, chatService, communityService, messageService)
|
self.addSubmodule(chatDto.id, true, false, events, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
# 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):
|
||||||
@ -133,7 +139,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter, chatService: chat_serv
|
|||||||
let channelItem = initSubItem(chatDto.id, cat.id, chatDto.name, chatDto.identicon, false, chatDto.color,
|
let channelItem = initSubItem(chatDto.id, cat.id, chatDto.name, chatDto.identicon, false, chatDto.color,
|
||||||
chatDto.description, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
chatDto.description, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
||||||
categoryChannels.add(channelItem)
|
categoryChannels.add(channelItem)
|
||||||
self.addSubmodule(chatDto.id, true, events, chatService, communityService, messageService)
|
self.addSubmodule(chatDto.id, true, false, events, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
# 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
|
||||||
@ -148,15 +154,16 @@ proc buildCommunityUI(self: Module, events: EventEmitter, chatService: chat_serv
|
|||||||
|
|
||||||
self.setActiveItemSubItem(selectedItemId, selectedSubItemId)
|
self.setActiveItemSubItem(selectedItemId, selectedSubItemId)
|
||||||
|
|
||||||
method load*(self: Module, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
method load*(self: Module, events: EventEmitter, contactService: contact_service.Service,
|
||||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
|
messageService: message_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, chatService, communityService, messageService)
|
self.buildCommunityUI(events, contactService, chatService, communityService, messageService)
|
||||||
else:
|
else:
|
||||||
self.buildChatUI(events, chatService, communityService, messageService)
|
self.buildChatUI(events, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
for cModule in self.chatContentModule.values:
|
for cModule in self.chatContentModule.values:
|
||||||
cModule.load()
|
cModule.load()
|
||||||
@ -230,14 +237,15 @@ method createPublicChat*(self: Module, chatId: string) =
|
|||||||
|
|
||||||
self.controller.createPublicChat(chatId)
|
self.controller.createPublicChat(chatId)
|
||||||
|
|
||||||
method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, contactService: contact_service.Service,
|
||||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
|
messageService: message_service.Service) =
|
||||||
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
|
||||||
let item = initItem(chatDto.id, chatDto.name, chatDto.identicon, true, chatDto.color, chatDto.description,
|
let item = initItem(chatDto.id, chatDto.name, chatDto.identicon, true, chatDto.color, chatDto.description,
|
||||||
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, 0)
|
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, 0)
|
||||||
self.view.appendItem(item)
|
self.view.appendItem(item)
|
||||||
self.addSubmodule(chatDto.id, false, events, chatService, communityService, messageService)
|
self.addSubmodule(chatDto.id, false, true, events, contactService, chatService, communityService, messageService)
|
||||||
|
|
||||||
# make new added chat active one
|
# make new added chat active one
|
||||||
self.setActiveItemSubItem(item.id, "")
|
self.setActiveItemSubItem(item.id, "")
|
@ -1,5 +1,6 @@
|
|||||||
import NimQml
|
import NimQml
|
||||||
|
|
||||||
|
import ../../../../../app_service/service/contacts/service as contact_service
|
||||||
import ../../../../../app_service/service/chat/service_interface as chat_service
|
import ../../../../../app_service/service/chat/service_interface as chat_service
|
||||||
import ../../../../../app_service/service/community/service_interface as community_service
|
import ../../../../../app_service/service/community/service_interface as community_service
|
||||||
import ../../../../../app_service/service/message/service as message_service
|
import ../../../../../app_service/service/message/service as message_service
|
||||||
@ -9,8 +10,9 @@ import eventemitter
|
|||||||
method delete*(self: AccessInterface) {.base.} =
|
method delete*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method load*(self: AccessInterface, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
method load*(self: AccessInterface, events: EventEmitter, contactService: contact_service.Service,
|
||||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) {.base.} =
|
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||||
|
messageService: message_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.} =
|
||||||
|
@ -2,6 +2,6 @@ method activeItemSubItemSet*(self: AccessInterface, itemId: string, subItemId: s
|
|||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method addNewPublicChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter,
|
method addNewPublicChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter,
|
||||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
contactService: contact_service.Service, chatService: chat_service.ServiceInterface,
|
||||||
messageService: message_service.Service) {.base.} =
|
communityService: community_service.ServiceInterface, messageService: message_service.Service) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
@ -84,8 +84,8 @@ proc newModule*[T](
|
|||||||
result.moduleLoaded = false
|
result.moduleLoaded = false
|
||||||
|
|
||||||
# Submodules
|
# Submodules
|
||||||
result.chatSectionModule = chat_section_module.newModule(result, events, conf.CHAT_SECTION_ID, false, chatService,
|
result.chatSectionModule = chat_section_module.newModule(result, events, conf.CHAT_SECTION_ID, false, contactsService,
|
||||||
communityService, messageService)
|
chatService, communityService, messageService)
|
||||||
result.communitySectionsModule = initOrderedTable[string, chat_section_module.AccessInterface]()
|
result.communitySectionsModule = initOrderedTable[string, chat_section_module.AccessInterface]()
|
||||||
result.walletSectionModule = wallet_section_module.newModule[Module[T]](result, events, tokenService,
|
result.walletSectionModule = wallet_section_module.newModule[Module[T]](result, events, tokenService,
|
||||||
transactionService, collectible_service, walletAccountService, settingsService)
|
transactionService, collectible_service, walletAccountService, settingsService)
|
||||||
@ -114,6 +114,7 @@ method delete*[T](self: Module[T]) =
|
|||||||
method load*[T](
|
method load*[T](
|
||||||
self: Module[T],
|
self: Module[T],
|
||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
|
contactsService: contacts_service.Service,
|
||||||
chatService: chat_service.Service,
|
chatService: chat_service.Service,
|
||||||
communityService: community_service.Service,
|
communityService: community_service.Service,
|
||||||
messageService: message_service.Service
|
messageService: message_service.Service
|
||||||
@ -131,6 +132,7 @@ method load*[T](
|
|||||||
events,
|
events,
|
||||||
c.id,
|
c.id,
|
||||||
true,
|
true,
|
||||||
|
contactsService,
|
||||||
chatService,
|
chatService,
|
||||||
communityService,
|
communityService,
|
||||||
messageService
|
messageService
|
||||||
@ -209,9 +211,9 @@ method load*[T](
|
|||||||
activeSection = profileSettingsSectionItem
|
activeSection = profileSettingsSectionItem
|
||||||
|
|
||||||
# Load all sections
|
# Load all sections
|
||||||
self.chatSectionModule.load(events, chatService, communityService, messageService)
|
self.chatSectionModule.load(events, contactsService, chatService, communityService, messageService)
|
||||||
for cModule in self.communitySectionsModule.values:
|
for cModule in self.communitySectionsModule.values:
|
||||||
cModule.load(events, chatService, communityService, messageService)
|
cModule.load(events, contactsService, chatService, communityService, messageService)
|
||||||
self.walletSectionModule.load()
|
self.walletSectionModule.load()
|
||||||
# self.walletV2SectionModule.load()
|
# self.walletV2SectionModule.load()
|
||||||
self.browserSectionModule.load()
|
self.browserSectionModule.load()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
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/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
|
||||||
@ -10,6 +11,7 @@ method delete*(self: AccessInterface) {.base.} =
|
|||||||
method load*(
|
method load*(
|
||||||
self: AccessInterface,
|
self: AccessInterface,
|
||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
|
contactsService: contacts_service.Service,
|
||||||
chatService: chat_service.Service,
|
chatService: chat_service.Service,
|
||||||
communityService: community_service.Service,
|
communityService: community_service.Service,
|
||||||
messageService: message_service.Service
|
messageService: message_service.Service
|
||||||
|
@ -17,6 +17,14 @@ template getProp(obj: JsonNode, prop: string, value: var typedesc[int64]): bool
|
|||||||
|
|
||||||
success
|
success
|
||||||
|
|
||||||
|
template getProp(obj: JsonNode, prop: string, value: var typedesc[uint64]): bool =
|
||||||
|
var success = false
|
||||||
|
if (obj.kind == JObject and obj.contains(prop)):
|
||||||
|
value = uint64(obj[prop].getBiggestInt)
|
||||||
|
success = true
|
||||||
|
|
||||||
|
success
|
||||||
|
|
||||||
template getProp(obj: JsonNode, prop: string, value: var typedesc[string]): bool =
|
template getProp(obj: JsonNode, prop: string, value: var typedesc[string]): bool =
|
||||||
var success = false
|
var success = false
|
||||||
if (obj.kind == JObject and obj.contains(prop)):
|
if (obj.kind == JObject and obj.contains(prop)):
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import status/ens as status_ens
|
import status/ens as status_ens
|
||||||
|
|
||||||
include ../../common/json_utils
|
include ../../common/json_utils
|
||||||
@ -17,3 +18,16 @@ const lookupContactTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
|||||||
if not id.startsWith("0x"):
|
if not id.startsWith("0x"):
|
||||||
id = status_ens.pubkey(id)
|
id = status_ens.pubkey(id)
|
||||||
arg.finish(id)
|
arg.finish(id)
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Async timer
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
type
|
||||||
|
TimerTaskArg = ref object of QObjectTaskArg
|
||||||
|
timeoutInMilliseconds: int
|
||||||
|
|
||||||
|
const timerTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
|
let arg = decode[TimerTaskArg](argEncoded)
|
||||||
|
sleep(arg.timeoutInMilliseconds)
|
||||||
|
arg.finish("done")
|
||||||
|
@ -17,6 +17,7 @@ type ContactsDto* = object
|
|||||||
alias*: string
|
alias*: string
|
||||||
identicon*: string
|
identicon*: string
|
||||||
lastUpdated*: int64
|
lastUpdated*: int64
|
||||||
|
lastUpdatedLocally*: int64
|
||||||
localNickname*: string
|
localNickname*: string
|
||||||
image*: Images
|
image*: Images
|
||||||
added*: bool
|
added*: bool
|
||||||
@ -36,9 +37,10 @@ proc `$`*(self: ContactsDto): string =
|
|||||||
id: {self.id},
|
id: {self.id},
|
||||||
name: {self.name},
|
name: {self.name},
|
||||||
ensVerified: {self.ensVerified},
|
ensVerified: {self.ensVerified},
|
||||||
alias: {self.alias},
|
alias: {self.alias},
|
||||||
identicon: {self.identicon},
|
identicon: {self.identicon},
|
||||||
lastUpdated: {self.lastUpdated},
|
lastUpdated: {self.lastUpdated},
|
||||||
|
lastUpdatedLocally: {self.lastUpdatedLocally},
|
||||||
localNickname: {self.localNickname},
|
localNickname: {self.localNickname},
|
||||||
image:[
|
image:[
|
||||||
{$self.image}
|
{$self.image}
|
||||||
@ -69,6 +71,7 @@ proc toContactsDto*(jsonObj: JsonNode): ContactsDto =
|
|||||||
discard jsonObj.getProp("alias", result.alias)
|
discard jsonObj.getProp("alias", result.alias)
|
||||||
discard jsonObj.getProp("identicon", result.identicon)
|
discard jsonObj.getProp("identicon", result.identicon)
|
||||||
discard jsonObj.getProp("lastUpdated", result.lastUpdated)
|
discard jsonObj.getProp("lastUpdated", result.lastUpdated)
|
||||||
|
discard jsonObj.getProp("lastUpdatedLocally", result.lastUpdatedLocally)
|
||||||
discard jsonObj.getProp("localNickname", result.localNickname)
|
discard jsonObj.getProp("localNickname", result.localNickname)
|
||||||
|
|
||||||
var imageObj: JsonNode
|
var imageObj: JsonNode
|
||||||
|
26
src/app_service/service/contacts/dto/status_update.nim
Normal file
26
src/app_service/service/contacts/dto/status_update.nim
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import json
|
||||||
|
include ../../../common/json_utils
|
||||||
|
|
||||||
|
type StatusType* {.pure.}= enum
|
||||||
|
Offline = 0,
|
||||||
|
Online
|
||||||
|
DoNotDisturb
|
||||||
|
Idle
|
||||||
|
Invisible
|
||||||
|
|
||||||
|
type StatusUpdateDto* = object
|
||||||
|
publicKey*: string
|
||||||
|
statusType*: StatusType
|
||||||
|
clock*: uint64
|
||||||
|
text*: string
|
||||||
|
|
||||||
|
proc toStatusUpdateDto*(jsonObj: JsonNode): StatusUpdateDto =
|
||||||
|
discard jsonObj.getProp("publicKey", result.publicKey)
|
||||||
|
discard jsonObj.getProp("clock", result.clock)
|
||||||
|
discard jsonObj.getProp("text", result.text)
|
||||||
|
|
||||||
|
result.statusType = StatusType.Offline
|
||||||
|
var statusTypeInt: int
|
||||||
|
if (jsonObj.getProp("statusType", statusTypeInt) and
|
||||||
|
(statusTypeInt >= ord(low(StatusType)) or statusTypeInt <= ord(high(StatusType)))):
|
||||||
|
result.statusType = StatusType(statusTypeInt)
|
@ -1,15 +1,18 @@
|
|||||||
import NimQml, Tables, json, sequtils, strformat, chronicles, strutils
|
import NimQml, Tables, json, sequtils, strformat, chronicles, strutils, times
|
||||||
|
|
||||||
import eventemitter
|
import eventemitter
|
||||||
|
import ../../../app/global/global_singleton
|
||||||
|
import ../../../app/core/signals/types
|
||||||
import ../../../app/core/tasks/[qt, threadpool]
|
import ../../../app/core/tasks/[qt, threadpool]
|
||||||
|
|
||||||
import ./dto/contacts as contacts_dto
|
import ./dto/contacts as contacts_dto
|
||||||
|
import ./dto/status_update as status_update_dto
|
||||||
import status/statusgo_backend_new/contacts as status_contacts
|
import status/statusgo_backend_new/contacts as status_contacts
|
||||||
import status/statusgo_backend_new/accounts as status_accounts
|
import status/statusgo_backend_new/accounts as status_accounts
|
||||||
import status/statusgo_backend_new/chat as status_chat
|
import status/statusgo_backend_new/chat as status_chat
|
||||||
import status/statusgo_backend_new/utils as status_utils
|
import status/statusgo_backend_new/utils as status_utils
|
||||||
|
|
||||||
export contacts_dto
|
export contacts_dto, status_update_dto
|
||||||
|
|
||||||
include async_tasks
|
include async_tasks
|
||||||
|
|
||||||
@ -26,6 +29,17 @@ type
|
|||||||
ContactAddedArgs* = ref object of Args
|
ContactAddedArgs* = ref object of Args
|
||||||
contact*: ContactsDto
|
contact*: ContactsDto
|
||||||
|
|
||||||
|
ContactUpdatedArgs* = ref object of Args
|
||||||
|
contact*: ContactsDto
|
||||||
|
|
||||||
|
ContactsStatusUpdatedArgs* = ref object of Args
|
||||||
|
statusUpdates*: seq[StatusUpdateDto]
|
||||||
|
|
||||||
|
# Local Constants:
|
||||||
|
const CheckStatusIntervalInMilliseconds = 5000 # 5 seconds, this is timeout how often do we check for user status.
|
||||||
|
const OnlineLimitInSeconds = int(5.5 * 60) # 5.5 minutes
|
||||||
|
const IdleLimitInSeconds = int(7 * 60) # 7 minutes
|
||||||
|
|
||||||
# Signals which may be emitted by this service:
|
# Signals which may be emitted by this service:
|
||||||
const SIGNAL_CONTACT_LOOKED_UP* = "SIGNAL_CONTACT_LOOKED_UP"
|
const SIGNAL_CONTACT_LOOKED_UP* = "SIGNAL_CONTACT_LOOKED_UP"
|
||||||
# Remove new when old code is removed
|
# Remove new when old code is removed
|
||||||
@ -34,23 +48,43 @@ const SIGNAL_CONTACT_BLOCKED* = "new-contactBlocked"
|
|||||||
const SIGNAL_CONTACT_UNBLOCKED* = "new-contactUnblocked"
|
const SIGNAL_CONTACT_UNBLOCKED* = "new-contactUnblocked"
|
||||||
const SIGNAL_CONTACT_REMOVED* = "new-contactRemoved"
|
const SIGNAL_CONTACT_REMOVED* = "new-contactRemoved"
|
||||||
const SIGNAL_CONTACT_NICKNAME_CHANGED* = "new-contactNicknameChanged"
|
const SIGNAL_CONTACT_NICKNAME_CHANGED* = "new-contactNicknameChanged"
|
||||||
|
const SIGNAL_CONTACTS_STATUS_UPDATED* = "new-contactsStatusUpdated"
|
||||||
|
const SIGNAL_CONTACT_UPDATED* = "new-contactUpdated"
|
||||||
|
const SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED* = "new-loggedInUserImageChanged"
|
||||||
|
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type Service* = ref object of QObject
|
type Service* = ref object of QObject
|
||||||
threadpool: ThreadPool
|
threadpool: ThreadPool
|
||||||
contacts: Table[string, ContactsDto] # [contact_id, ContactsDto]
|
contacts: Table[string, ContactsDto] # [contact_id, ContactsDto]
|
||||||
|
contactsStatus: Table[string, StatusUpdateDto] # [contact_id, StatusUpdateDto]
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
|
closingApp: bool
|
||||||
|
|
||||||
|
# Forward declaration
|
||||||
|
proc getContactById*(self: Service, id: string): ContactsDto
|
||||||
|
proc saveContact(self: Service, contact: ContactsDto)
|
||||||
|
proc startCheckingContactStatuses(self: Service)
|
||||||
|
|
||||||
proc delete*(self: Service) =
|
proc delete*(self: Service) =
|
||||||
|
self.closingApp = true
|
||||||
|
self.contacts.clear
|
||||||
|
self.contactsStatus.clear
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
|
||||||
proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
|
proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.QObject.setup
|
result.QObject.setup
|
||||||
|
result.closingApp = false
|
||||||
result.events = events
|
result.events = events
|
||||||
result.threadpool = threadpool
|
result.threadpool = threadpool
|
||||||
result.contacts = initTable[string, ContactsDto]()
|
result.contacts = initTable[string, ContactsDto]()
|
||||||
|
signalConnect(singletonInstance.userProfile, "imageChanged()", result, "onLoggedInUserImageChange()", 2)
|
||||||
|
|
||||||
|
proc addContact(self: Service, contact: ContactsDto) =
|
||||||
|
# Private proc, used for adding contacts only.
|
||||||
|
self.contacts[contact.id] = contact
|
||||||
|
self.contactsStatus[contact.id] = StatusUpdateDto(publicKey: contact.id, statusType: StatusType.Offline)
|
||||||
|
|
||||||
proc fetchContacts*(self: Service) =
|
proc fetchContacts*(self: Service) =
|
||||||
try:
|
try:
|
||||||
@ -59,15 +93,45 @@ QtObject:
|
|||||||
let contacts = map(response.result.getElems(), proc(x: JsonNode): ContactsDto = x.toContactsDto())
|
let contacts = map(response.result.getElems(), proc(x: JsonNode): ContactsDto = x.toContactsDto())
|
||||||
|
|
||||||
for contact in contacts:
|
for contact in contacts:
|
||||||
self.contacts[contact.id] = contact
|
self.addContact(contact)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
let errDesription = e.msg
|
let errDesription = e.msg
|
||||||
error "error: ", errDesription
|
error "error: ", errDesription
|
||||||
return
|
return
|
||||||
|
|
||||||
|
proc doConnect(self: Service) =
|
||||||
|
self.events.on(SignalType.Message.event) do(e:Args):
|
||||||
|
var receivedData = MessageSignal(e)
|
||||||
|
if(receivedData.statusUpdates.len > 0):
|
||||||
|
for s in receivedData.statusUpdates:
|
||||||
|
if(not self.contactsStatus.hasKey(s.publicKey)):
|
||||||
|
# we shouldn't be here ever, but the following line ensures we have added a contact before setting status for it
|
||||||
|
discard self.getContactById(s.publicKey)
|
||||||
|
|
||||||
|
self.contactsStatus[s.publicKey] = s
|
||||||
|
|
||||||
|
let data = ContactsStatusUpdatedArgs(statusUpdates: receivedData.statusUpdates)
|
||||||
|
self.events.emit(SIGNAL_CONTACTS_STATUS_UPDATED, data)
|
||||||
|
|
||||||
|
if(receivedData.contacts.len > 0):
|
||||||
|
for c in receivedData.contacts:
|
||||||
|
let localContact = self.getContactById(c.id)
|
||||||
|
var receivedContact = c
|
||||||
|
receivedContact.localNickname = localContact.localNickname
|
||||||
|
self.saveContact(receivedContact)
|
||||||
|
|
||||||
|
let data = ContactUpdatedArgs(contact: receivedContact)
|
||||||
|
self.events.emit(SIGNAL_CONTACT_UPDATED, data)
|
||||||
|
|
||||||
proc init*(self: Service) =
|
proc init*(self: Service) =
|
||||||
self.fetchContacts()
|
self.fetchContacts()
|
||||||
|
self.doConnect()
|
||||||
|
self.startCheckingContactStatuses()
|
||||||
|
|
||||||
|
proc onLoggedInUserImageChange*(self: Service) {.slot.} =
|
||||||
|
let data = Args()
|
||||||
|
self.events.emit(SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED, data)
|
||||||
|
|
||||||
proc getContacts*(self: Service): seq[ContactsDto] =
|
proc getContacts*(self: Service): seq[ContactsDto] =
|
||||||
return toSeq(self.contacts.values)
|
return toSeq(self.contacts.values)
|
||||||
@ -77,7 +141,10 @@ QtObject:
|
|||||||
let response = status_contacts.getContactByID(id)
|
let response = status_contacts.getContactByID(id)
|
||||||
|
|
||||||
result = response.result.toContactsDto()
|
result = response.result.toContactsDto()
|
||||||
self.contacts[result.id] = result
|
if result.id.len == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.addContact(result)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
let errDesription = e.msg
|
let errDesription = e.msg
|
||||||
@ -109,6 +176,15 @@ QtObject:
|
|||||||
blocked: false,
|
blocked: false,
|
||||||
hasAddedUs: false
|
hasAddedUs: false
|
||||||
)
|
)
|
||||||
|
self.addContact(result)
|
||||||
|
|
||||||
|
proc getStatusForContactWithId*(self: Service, publicKey: string): StatusUpdateDto =
|
||||||
|
# This method will fetch current accurate status from `status-go` once we add an api point there for it.
|
||||||
|
if(not self.contactsStatus.hasKey(publicKey)):
|
||||||
|
# following line ensures that we have added a contact before setting status for it
|
||||||
|
discard self.getContactById(publicKey)
|
||||||
|
|
||||||
|
return self.contactsStatus[publicKey]
|
||||||
|
|
||||||
proc getContactNameAndImage*(self: Service, publicKey: string): tuple[name: string, image: string, isIdenticon: bool] =
|
proc getContactNameAndImage*(self: Service, publicKey: string): tuple[name: string, image: string, isIdenticon: bool] =
|
||||||
## This proc should be used accross the app in order to have for the same contact
|
## This proc should be used accross the app in order to have for the same contact
|
||||||
@ -207,6 +283,8 @@ QtObject:
|
|||||||
self.events.emit(SIGNAL_CONTACT_LOOKED_UP, data)
|
self.events.emit(SIGNAL_CONTACT_LOOKED_UP, data)
|
||||||
|
|
||||||
proc lookupContact*(self: Service, value: string) =
|
proc lookupContact*(self: Service, value: string) =
|
||||||
|
if(self.closingApp):
|
||||||
|
return
|
||||||
let arg = LookupContactTaskArg(
|
let arg = LookupContactTaskArg(
|
||||||
tptr: cast[ByteAddress](lookupContactTask),
|
tptr: cast[ByteAddress](lookupContactTask),
|
||||||
vptr: cast[ByteAddress](self.vptr),
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
@ -214,3 +292,42 @@ QtObject:
|
|||||||
value: value
|
value: value
|
||||||
)
|
)
|
||||||
self.threadpool.start(arg)
|
self.threadpool.start(arg)
|
||||||
|
|
||||||
|
proc checkContactsStatus*(self: Service, response: string) {.slot.} =
|
||||||
|
let nowInMyLocalZone = now()
|
||||||
|
let timestampNow = uint64(nowInMyLocalZone.toTime().toUnix())
|
||||||
|
var updatedStatuses: seq[StatusUpdateDto]
|
||||||
|
for status in self.contactsStatus.mvalues:
|
||||||
|
if(timestampNow - status.clock < uint64(OnlineLimitInSeconds)):
|
||||||
|
if(status.statusType == StatusType.Online):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
status.statusType = StatusType.Online
|
||||||
|
updatedStatuses.add(status)
|
||||||
|
elif(timestampNow - status.clock < uint64(IdleLimitInSeconds)):
|
||||||
|
if(status.statusType == StatusType.Idle):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
status.statusType = StatusType.Idle
|
||||||
|
updatedStatuses.add(status)
|
||||||
|
elif(status.statusType != StatusType.Offline):
|
||||||
|
status.statusType = StatusType.Offline
|
||||||
|
updatedStatuses.add(status)
|
||||||
|
|
||||||
|
if(updatedStatuses.len > 0):
|
||||||
|
let data = ContactsStatusUpdatedArgs(statusUpdates: updatedStatuses)
|
||||||
|
self.events.emit(SIGNAL_CONTACTS_STATUS_UPDATED, data)
|
||||||
|
|
||||||
|
self.startCheckingContactStatuses()
|
||||||
|
|
||||||
|
proc startCheckingContactStatuses(self: Service) =
|
||||||
|
if(self.closingApp):
|
||||||
|
return
|
||||||
|
|
||||||
|
let arg = TimerTaskArg(
|
||||||
|
tptr: cast[ByteAddress](timerTask),
|
||||||
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
|
slot: "checkContactsStatus",
|
||||||
|
timeoutInMilliseconds: CheckStatusIntervalInMilliseconds
|
||||||
|
)
|
||||||
|
self.threadpool.start(arg)
|
||||||
|
@ -29,6 +29,18 @@ StatusAppThreePanelLayout {
|
|||||||
// Each `ChatLayout` has its own chatCommunitySectionModule
|
// Each `ChatLayout` has its own chatCommunitySectionModule
|
||||||
// (on the backend chat and community sections share the same module since they are actually the same)
|
// (on the backend chat and community sections share the same module since they are actually the same)
|
||||||
property var chatCommunitySectionModule
|
property var chatCommunitySectionModule
|
||||||
|
// Since qml component doesn't follow encaptulation from the backend side, we're introducing
|
||||||
|
// a method which will return appropriate chat content module for selected chat/channel
|
||||||
|
function currentChatContentModule(){
|
||||||
|
// When we decide to have the same struct as it's on the backend we will remove this function.
|
||||||
|
// So far this is a way to deal with refactord backend from the current qml structure.
|
||||||
|
if(chatCommunitySectionModule.activeItem.isSubItemActive)
|
||||||
|
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.activeSubItem.id)
|
||||||
|
else
|
||||||
|
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||||
|
|
||||||
|
return chatCommunitySectionModule.getChatContentModule()
|
||||||
|
}
|
||||||
|
|
||||||
// Not Refactored
|
// Not Refactored
|
||||||
property var messageStore
|
property var messageStore
|
||||||
@ -72,35 +84,37 @@ StatusAppThreePanelLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showRightPanel: (localAccountSensitiveSettings.expandUsersList && (localAccountSensitiveSettings.showOnlineUsers || chatsModel.communities.activeCommunity.active)
|
showRightPanel: {
|
||||||
&& (chatsModel.channelView.activeChannel.chatType !== Constants.chatTypeOneToOne))
|
// Check if user list is available as an option for particular chat content module.
|
||||||
|
let usersListAvailable = currentChatContentModule().isUsersListAvailable()
|
||||||
|
return localAccountSensitiveSettings.showOnlineUsers && usersListAvailable && localAccountSensitiveSettings.expandUsersList
|
||||||
|
}
|
||||||
|
|
||||||
rightPanel: localAccountSensitiveSettings.communitiesEnabled && chatCommunitySectionModule.isCommunity()? communityUserListComponent : userListComponent
|
rightPanel: localAccountSensitiveSettings.communitiesEnabled && chatCommunitySectionModule.isCommunity()? communityUserListComponent : userListComponent
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: communityUserListComponent
|
id: communityUserListComponent
|
||||||
CommunityUserListPanel {
|
CommunityUserListPanel {
|
||||||
//Not Refactored Yet
|
|
||||||
//currentTime: chatColumn.currentTime
|
|
||||||
messageContextMenu: quickActionMessageOptionsMenu
|
messageContextMenu: quickActionMessageOptionsMenu
|
||||||
// profilePubKey: userProfile.pubKey
|
usersModule: {
|
||||||
// community: root.rootStore.chatsModelInst.communities.activeCommunity
|
if(chatCommunitySectionModule.activeItem.isSubItemActive)
|
||||||
// currentUserName: Utils.removeStatusEns(root.rootStore.profileModelInst.ens.preferredUsername
|
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.activeSubItem.id)
|
||||||
// || root.rootStore.profileModelInst.profile.username)
|
else
|
||||||
// currentUserOnline: root.store.userProfileInst.userStatus
|
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||||
// contactsList: root.rootStore.allContacts
|
|
||||||
|
return chatCommunitySectionModule.getChatContentModule().usersModule
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: userListComponent
|
id: userListComponent
|
||||||
UserListPanel {
|
UserListPanel {
|
||||||
//Not Refactored Yet
|
|
||||||
//currentTime: chatColumn.currentTime
|
|
||||||
//userList: chatColumn.userList
|
|
||||||
messageContextMenu: quickActionMessageOptionsMenu
|
messageContextMenu: quickActionMessageOptionsMenu
|
||||||
// profilePubKey: userProfile.pubKey
|
usersModule: {
|
||||||
// contactsList: root.rootStore.allContacts
|
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||||
// isOnline: root.rootStore.chatsModelInst.isOnline
|
return chatCommunitySectionModule.getChatContentModule().usersModule
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,95 +9,113 @@ 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
|
||||||
|
|
||||||
StatusListItem {
|
Item {
|
||||||
id: root
|
id: wrapper
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 8
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 8
|
height: rectangle.height + 4
|
||||||
implicitHeight: 44
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: 8
|
|
||||||
|
|
||||||
property var contactsList
|
|
||||||
property int statusType: -1
|
|
||||||
property string name: ""
|
|
||||||
property string publicKey: ""
|
property string publicKey: ""
|
||||||
property string profilePubKey: ""
|
property string name: ""
|
||||||
property string identicon: ""
|
property string identicon: ""
|
||||||
property bool isCurrentUser: (publicKey === profilePubKey)
|
property bool isIdenticon: true
|
||||||
property string profileImage: appMain.getProfileImage(publicKey) || ""
|
property int userStatus: Constants.userStatus.offline
|
||||||
property bool highlighted: false
|
|
||||||
property string lastSeen: ""
|
|
||||||
property bool isOnline: false
|
|
||||||
property var currentTime
|
|
||||||
property var messageContextMenu
|
property var messageContextMenu
|
||||||
|
property bool enableMouseArea: true
|
||||||
|
property color color: {
|
||||||
title: isCurrentUser ? qsTr("You") : Emoji.parse(Utils.removeStatusEns(Utils.filterXSS(root.name)))
|
if (wrapper.hovered) {
|
||||||
image.source: profileImage || identicon
|
return Style.current.menuBackgroundHover
|
||||||
image.isIdenticon: !profileImage
|
|
||||||
image.width: 24
|
|
||||||
image.height: 24
|
|
||||||
statusListItemIcon.anchors.topMargin: 10
|
|
||||||
statusListItemTitle.elide: Text.ElideRight
|
|
||||||
statusListItemTitle.wrapMode: Text.NoWrap
|
|
||||||
color: sensor.containsMouse ?
|
|
||||||
Theme.palette.statusChatListItem.hoverBackgroundColor :
|
|
||||||
Theme.palette.baseColor4
|
|
||||||
|
|
||||||
sensor.onClicked: {
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
|
||||||
//TODO remove dynamic scoping
|
|
||||||
openProfilePopup(root.name, root.publicKey, (root.profileImage || root.identicon), "", appMain.getUserNickname(root.publicKey));
|
|
||||||
}
|
|
||||||
else if (mouse.button === Qt.RightButton && !!messageContextMenu) {
|
|
||||||
// Set parent, X & Y positions for the messageContextMenu
|
|
||||||
messageContextMenu.parent = root
|
|
||||||
messageContextMenu.setXPosition = function() { return 0}
|
|
||||||
messageContextMenu.setYPosition = function() { return root.height}
|
|
||||||
messageContextMenu.isProfile = true;
|
|
||||||
messageContextMenu.show(root.name, root.publicKey, (root.profileImage || root.identicon), "", appMain.getUserNickname(root.publicKey))
|
|
||||||
}
|
}
|
||||||
|
return Style.current.transparent
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Rectangle {
|
||||||
enabled: !!root.contactsList
|
id: rectangle
|
||||||
target: root.contactsList
|
width: parent.width
|
||||||
onContactChanged: {
|
height: 40
|
||||||
if (pubkey === root.publicKey) {
|
radius: 8
|
||||||
root.profileImage = !!appMain.getProfileImage(root.publicKey) ?
|
color: wrapper.color
|
||||||
appMain.getProfileImage(root.publicKey) : ""
|
|
||||||
|
StatusSmartIdenticon {
|
||||||
|
id: contactImage
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
image: StatusImageSettings {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
source: wrapper.identicon
|
||||||
|
isIdenticon: wrapper.isIdenticon
|
||||||
|
}
|
||||||
|
icon: StatusIconSettings {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
letterSize: 15
|
||||||
|
}
|
||||||
|
name: wrapper.name
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: contactInfo
|
||||||
|
text: wrapper.name
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.smallPadding
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: Style.current.textColor
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pixelSize: 15
|
||||||
|
anchors.left: contactImage.right
|
||||||
|
anchors.leftMargin: Style.current.halfPadding
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBadge {
|
||||||
|
id: statusBadge
|
||||||
|
width: 15
|
||||||
|
height: 15
|
||||||
|
anchors.left: contactImage.right
|
||||||
|
anchors.leftMargin: -Style.current.smallPadding
|
||||||
|
anchors.bottom: contactImage.bottom
|
||||||
|
visible: wrapper.userStatus !== Constants.userStatus.offline
|
||||||
|
border.width: 3
|
||||||
|
border.color: Theme.palette.statusAppNavBar.backgroundColor
|
||||||
|
color: {
|
||||||
|
if(wrapper.userStatus === Constants.userStatus.online)
|
||||||
|
return Style.current.green
|
||||||
|
else if(wrapper.userStatus === Constants.userStatus.idle)
|
||||||
|
return Style.current.orange
|
||||||
|
else if(wrapper.userStatus === Constants.userStatus.doNotDisturb)
|
||||||
|
return Style.current.red
|
||||||
|
|
||||||
|
return "transparent"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
StatusBadge {
|
MouseArea {
|
||||||
id: statusBadge
|
enabled: enableMouseArea
|
||||||
width: 15
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
height: 15
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
anchors.left: parent.left
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 22
|
hoverEnabled: true
|
||||||
anchors.bottom: parent.bottom
|
onEntered: {
|
||||||
anchors.bottomMargin: 6
|
wrapper.hovered = true
|
||||||
visible: root.isOnline && !((root.statusType === -1) && (lastSeenMinutesAgo > 7))
|
}
|
||||||
border.width: 3
|
onExited: {
|
||||||
border.color: root.sensor.containsMouse ? Theme.palette.baseColor2 : Theme.palette.baseColor4
|
wrapper.hovered = false
|
||||||
property real lastSeenMinutesAgo: ((currentTime/1000 - parseInt(lastSeen)) / 60);
|
}
|
||||||
color: {
|
onClicked: {
|
||||||
if (visible) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
if (statusType === Constants.statusType_DoNotDisturb) {
|
//TODO remove dynamic scoping
|
||||||
return Style.current.red;
|
openProfilePopup(wrapper.name, wrapper.publicKey, wrapper.identicon, "", wrapper.name);
|
||||||
} else if (isCurrentUser || (lastSeenMinutesAgo < 5.5)) {
|
}
|
||||||
return Style.current.green;
|
else if (mouse.button === Qt.RightButton && !!messageContextMenu) {
|
||||||
} else if (((statusType !== -1) && (lastSeenMinutesAgo > 5.5)) ||
|
// Set parent, X & Y positions for the messageContextMenu
|
||||||
((statusType === -1) && (lastSeenMinutesAgo < 7))) {
|
messageContextMenu.parent = rectangle
|
||||||
return Style.current.orange;
|
messageContextMenu.setXPosition = function() { return 0}
|
||||||
} else if ((statusType === -1) && (lastSeenMinutesAgo > 7)) {
|
messageContextMenu.setYPosition = function() { return rectangle.height}
|
||||||
return "transparent";
|
messageContextMenu.isProfile = true;
|
||||||
|
messageContextMenu.show(wrapper.name, wrapper.publicKey, wrapper.identicon, "", wrapper.name)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return "transparent";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
import Qt.labs.platform 1.1
|
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Window 2.13
|
|
||||||
import QtQuick.Layouts 1.13
|
|
||||||
import QtQml.Models 2.13
|
|
||||||
import QtGraphicalEffects 1.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
import shared.panels 1.0
|
import shared.panels 1.0
|
||||||
import shared.status 1.0
|
import shared.status 1.0
|
||||||
@ -17,11 +11,11 @@ import utils 1.0
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
property var userList
|
|
||||||
property var currentTime
|
// Important:
|
||||||
property bool isOnline
|
// Each chat/community has its own ChatContentModule and each ChatContentModule has a single usersModule
|
||||||
property var contactsList
|
// usersModule on the backend contains everything needed for this component
|
||||||
property string profilePubKey
|
property var usersModule
|
||||||
property var messageContextMenu
|
property var messageContextMenu
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@ -52,26 +46,14 @@ Item {
|
|||||||
bottomMargin: Style.current.bigPadding
|
bottomMargin: Style.current.bigPadding
|
||||||
}
|
}
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
model: userListDelegate
|
model: usersModule.model
|
||||||
}
|
|
||||||
|
|
||||||
DelegateModelGeneralized {
|
|
||||||
id: userListDelegate
|
|
||||||
lessThan: [
|
|
||||||
function (left, right) {
|
|
||||||
return (left.lastSeen > right.lastSeen);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
model: root.userList
|
|
||||||
delegate: UserDelegate {
|
delegate: UserDelegate {
|
||||||
name: model.userName
|
publicKey: model.id
|
||||||
publicKey: model.publicKey
|
name: model.name
|
||||||
profilePubKey: root.profilePubKey
|
identicon: model.icon
|
||||||
identicon: model.identicon
|
isIdenticon: model.isIdenticon
|
||||||
contactsList: root.contactsList
|
userStatus: model.onlineStatus
|
||||||
lastSeen: model.lastSeen / 1000
|
messageContextMenu: root.messageContextMenu
|
||||||
currentTime: root.currentTime
|
|
||||||
isOnline: root.isOnline
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
import Qt.labs.platform 1.1
|
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Window 2.13
|
|
||||||
import QtQuick.Layouts 1.13
|
|
||||||
import QtQml.Models 2.13
|
|
||||||
import QtGraphicalEffects 1.13
|
|
||||||
import QtQuick.Dialogs 1.3
|
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
import shared.panels 1.0
|
import shared.panels 1.0
|
||||||
import shared.status 1.0
|
import shared.status 1.0
|
||||||
@ -20,14 +14,12 @@ import StatusQ.Core.Theme 0.1
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
property var userList
|
|
||||||
property var currentTime
|
// Important:
|
||||||
property var contactsList
|
// Each chat/community has its own ChatContentModule and each ChatContentModule has a single usersModule
|
||||||
property string profilePubKey
|
// usersModule on the backend contains everything needed for this component
|
||||||
|
property var usersModule
|
||||||
property var messageContextMenu
|
property var messageContextMenu
|
||||||
property var community
|
|
||||||
property string currentUserName: ""
|
|
||||||
property bool currentUserOnline: true
|
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: titleText
|
id: titleText
|
||||||
@ -59,46 +51,32 @@ Item {
|
|||||||
bottomMargin: Style.current.bigPadding
|
bottomMargin: Style.current.bigPadding
|
||||||
}
|
}
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
model: userListDelegate
|
model: usersModule.model
|
||||||
section.property: "online"
|
|
||||||
section.delegate: (root.width > 58) ? sectionDelegateComponent : null
|
|
||||||
Component {
|
|
||||||
id: sectionDelegateComponent
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
StatusBaseText {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.pixelSize: Style.current.additionalTextSize
|
|
||||||
color: Theme.palette.baseColor1
|
|
||||||
text: section === 'true' ? qsTr("Online") : qsTr("Offline")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateModelGeneralized {
|
|
||||||
id: userListDelegate
|
|
||||||
lessThan: [
|
|
||||||
function(left, right) {
|
|
||||||
return left.sortKey.localeCompare(right.sortKey) < 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
model: community.members
|
|
||||||
delegate: UserDelegate {
|
delegate: UserDelegate {
|
||||||
name: model.userName
|
publicKey: model.id
|
||||||
publicKey: model.pubKey
|
name: model.name
|
||||||
profilePubKey: root.profilePubKey
|
identicon: model.icon
|
||||||
identicon: model.identicon
|
isIdenticon: model.isIdenticon
|
||||||
contactsList: root.contactsList
|
userStatus: model.onlineStatus
|
||||||
lastSeen: model.lastSeen
|
|
||||||
statusType: model.statusType
|
|
||||||
currentTime: root.currentTime
|
|
||||||
isOnline: (model.userName === root.currentUserName) ?
|
|
||||||
root.currentUserOnline : model.online
|
|
||||||
messageContextMenu: root.messageContextMenu
|
messageContextMenu: root.messageContextMenu
|
||||||
}
|
}
|
||||||
|
section.property: "onlineStatus"
|
||||||
|
section.delegate: (root.width > 58) ? sectionDelegateComponent : null
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: sectionDelegateComponent
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
StyledText {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pixelSize: Style.current.additionalTextSize
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
text: model.onlineStatus === Constants.userStatus.online? qsTr("Online") : qsTr("Offline")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,18 @@ Item {
|
|||||||
// Important: we have parent module in this context only cause qml components
|
// Important: we have parent module in this context only cause qml components
|
||||||
// don't follow struct from we have on the backend.
|
// don't follow struct from we have on the backend.
|
||||||
property var parentModule
|
property var parentModule
|
||||||
|
// Since qml component doesn't follow encaptulation from the backend side, we're introducing
|
||||||
|
// a method which will return appropriate chat content module for selected chat/channel
|
||||||
|
function currentChatContentModule(){
|
||||||
|
// When we decide to have the same struct as it's on the backend we will remove this function.
|
||||||
|
// So far this is a way to deal with refactord backend from the current qml structure.
|
||||||
|
if(parentModule.activeItem.isSubItemActive)
|
||||||
|
parentModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.activeSubItem.id)
|
||||||
|
else
|
||||||
|
parentModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||||
|
|
||||||
|
return parentModule.getChatContentModule()
|
||||||
|
}
|
||||||
|
|
||||||
property var rootStore
|
property var rootStore
|
||||||
property alias pinnedMessagesPopupComponent: pinnedMessagesPopupComponent
|
property alias pinnedMessagesPopupComponent: pinnedMessagesPopupComponent
|
||||||
@ -210,8 +222,11 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
membersButton.visible: (localAccountSensitiveSettings.showOnlineUsers || root.rootStore.chatsModelInst.communities.activeCommunity.active)
|
membersButton.visible: {
|
||||||
&& root.rootStore.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatTypeOneToOne
|
// Check if user list is available as an option for particular chat content module.
|
||||||
|
let usersListAvailable = currentChatContentModule().isUsersListAvailable()
|
||||||
|
return localAccountSensitiveSettings.showOnlineUsers && usersListAvailable
|
||||||
|
}
|
||||||
membersButton.highlighted: localAccountSensitiveSettings.expandUsersList
|
membersButton.highlighted: localAccountSensitiveSettings.expandUsersList
|
||||||
notificationButton.visible: localAccountSensitiveSettings.isActivityCenterEnabled
|
notificationButton.visible: localAccountSensitiveSettings.isActivityCenterEnabled
|
||||||
notificationButton.tooltip.offset: localAccountSensitiveSettings.expandUsersList ? 0 : 14
|
notificationButton.tooltip.offset: localAccountSensitiveSettings.expandUsersList ? 0 : 14
|
||||||
|
@ -320,19 +320,18 @@ Item {
|
|||||||
badge.implicitHeight: 15
|
badge.implicitHeight: 15
|
||||||
badge.implicitWidth: 15
|
badge.implicitWidth: 15
|
||||||
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusAppNavBar.backgroundColor
|
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusAppNavBar.backgroundColor
|
||||||
badge.color: {
|
/*
|
||||||
return userProfile.sendUserStatus ? Style.current.green : Style.current.midGrey
|
//This is still not in use. Read a comment for `currentUserStatus` in UserProfile on the nim side.
|
||||||
/*
|
// Use this code once support for custom user status is added
|
||||||
// Use this code once support for custom user status is added
|
switch(userProfile.currentUserStatus){
|
||||||
switch(userProfile.currentUserStatus){
|
case Constants.userStatus.online:
|
||||||
case Constants.statusType_Online:
|
return Style.current.green;
|
||||||
return Style.current.green;
|
case Constants.userStatus.doNotDisturb:
|
||||||
case Constants.statusType_DoNotDisturb:
|
return Style.current.red;
|
||||||
return Style.current.red;
|
default:
|
||||||
default:
|
return Style.current.midGrey;
|
||||||
return Style.current.midGrey;
|
}*/
|
||||||
}*/
|
badge.color: appMain.rootStore.userProfileInst.userStatus ? Style.current.green : Style.current.midGrey
|
||||||
}
|
|
||||||
badge.border.width: 3
|
badge.border.width: 3
|
||||||
onClicked: {
|
onClicked: {
|
||||||
userStatusContextMenu.opened ?
|
userStatusContextMenu.opened ?
|
||||||
|
@ -40,6 +40,13 @@ QtObject {
|
|||||||
readonly property int newMessage: 6
|
readonly property int newMessage: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property QtObject userStatus: QtObject{
|
||||||
|
readonly property int offline: 0
|
||||||
|
readonly property int online: 1
|
||||||
|
readonly property int doNotDisturb: 2
|
||||||
|
readonly property int idle: 3
|
||||||
|
}
|
||||||
|
|
||||||
readonly property int communityImported: 0
|
readonly property int communityImported: 0
|
||||||
readonly property int communityImportingInProgress: 1
|
readonly property int communityImportingInProgress: 1
|
||||||
readonly property int communityImportingError: 2
|
readonly property int communityImportingError: 2
|
||||||
@ -113,10 +120,6 @@ QtObject {
|
|||||||
readonly property string linux: "linux"
|
readonly property string linux: "linux"
|
||||||
readonly property string mac: "mac"
|
readonly property string mac: "mac"
|
||||||
|
|
||||||
readonly property int statusType_Unknown: 0
|
|
||||||
readonly property int statusType_Online: 1
|
|
||||||
readonly property int statusType_DoNotDisturb: 2
|
|
||||||
|
|
||||||
// Transaction states
|
// Transaction states
|
||||||
readonly property int addressRequested: 1
|
readonly property int addressRequested: 1
|
||||||
readonly property int declined: 2
|
readonly property int declined: 2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user