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
|
||||
self.mainModule.load(
|
||||
self.statusFoundation.status.events,
|
||||
self.contactsService,
|
||||
self.chatService,
|
||||
self.communityService,
|
||||
self.messageService
|
||||
|
|
|
@ -4,7 +4,7 @@ import
|
|||
proc handleSignals(self: ChatController) =
|
||||
self.status.events.on(SignalType.Message.event) do(e:Args):
|
||||
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):
|
||||
## Handle mailserver peers being added and removed
|
||||
|
@ -39,8 +39,9 @@ proc handleSignals(self: ChatController) =
|
|||
self.view.messageView.messageList[chatId].checkTimeout(messageId)
|
||||
|
||||
self.status.events.on(SignalType.CommunityFound.event) do(e: Args):
|
||||
var data = CommunitySignal(e)
|
||||
self.view.communities.addCommunityToList(data.community)
|
||||
discard
|
||||
# var data = CommunitySignal(e)
|
||||
# self.view.communities.addCommunityToList(data.community)
|
||||
|
||||
self.status.events.on(SignalType.MailserverRequestCompleted.event) do(e:Args):
|
||||
# TODO: if the signal contains a cursor, request additional messages
|
||||
|
|
|
@ -2,13 +2,13 @@ import json
|
|||
|
||||
import base
|
||||
|
||||
import status/types/community
|
||||
import ../../../../app_service/service/community/dto/[community]
|
||||
import signal_type
|
||||
|
||||
type CommunitySignal* = ref object of Signal
|
||||
community*: Community
|
||||
community*: CommunityDto
|
||||
|
||||
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
|
||||
result = CommunitySignal()
|
||||
result.signalType = SignalType.CommunityFound
|
||||
result.community = event["event"].toCommunity()
|
||||
result.community = event["event"].toCommunityDto()
|
||||
|
|
|
@ -2,20 +2,26 @@ import json
|
|||
|
||||
import base
|
||||
|
||||
import status/types/[message, chat, community, profile, installation,
|
||||
activity_center_notification, status_update, removed_message]
|
||||
# Step by step we should remove all these types from `status-lib`
|
||||
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
|
||||
messages*: seq[Message]
|
||||
pinnedMessages*: seq[Message]
|
||||
chats*: seq[Chat]
|
||||
contacts*: seq[Profile]
|
||||
messages*: seq[MessageDto]
|
||||
pinnedMessages*: seq[PinnedMessageDto]
|
||||
chats*: seq[ChatDto]
|
||||
contacts*: seq[ContactsDto]
|
||||
installations*: seq[Installation]
|
||||
emojiReactions*: seq[Reaction]
|
||||
communities*: seq[Community]
|
||||
membershipRequests*: seq[CommunityMembershipRequest]
|
||||
emojiReactions*: seq[ReactionDto]
|
||||
communities*: seq[CommunityDto]
|
||||
membershipRequests*: seq[old_community.CommunityMembershipRequest]
|
||||
activityCenterNotification*: seq[ActivityCenterNotification]
|
||||
statusUpdates*: seq[StatusUpdate]
|
||||
statusUpdates*: seq[StatusUpdateDto]
|
||||
deletedMessages*: seq[RemovedMessage]
|
||||
|
||||
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:
|
||||
for jsonContact in event["event"]["contacts"]:
|
||||
signal.contacts.add(jsonContact.toProfile())
|
||||
|
||||
var chatsWithMentions: seq[string] = @[]
|
||||
signal.contacts.add(jsonContact.toContactsDto())
|
||||
|
||||
if event["event"]{"messages"} != nil:
|
||||
for jsonMsg in event["event"]["messages"]:
|
||||
var message = jsonMsg.toMessage()
|
||||
if message.hasMention:
|
||||
chatsWithMentions.add(message.chatId)
|
||||
var message = jsonMsg.toMessageDto()
|
||||
signal.messages.add(message)
|
||||
|
||||
if event["event"]{"chats"} != nil:
|
||||
for jsonChat in event["event"]["chats"]:
|
||||
var chat = jsonChat.toChat
|
||||
if chatsWithMentions.contains(chat.id):
|
||||
chat.mentionsCount.inc
|
||||
var chat = jsonChat.toChatDto()
|
||||
signal.chats.add(chat)
|
||||
|
||||
if event["event"]{"statusUpdates"} != nil:
|
||||
for jsonStatusUpdate in event["event"]["statusUpdates"]:
|
||||
var statusUpdate = jsonStatusUpdate.toStatusUpdate
|
||||
var statusUpdate = jsonStatusUpdate.toStatusUpdateDto()
|
||||
signal.statusUpdates.add(statusUpdate)
|
||||
|
||||
if event["event"]{"installations"} != nil:
|
||||
|
@ -54,11 +54,11 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
|||
|
||||
if event["event"]{"emojiReactions"} != nil:
|
||||
for jsonReaction in event["event"]["emojiReactions"]:
|
||||
signal.emojiReactions.add(jsonReaction.toReaction)
|
||||
signal.emojiReactions.add(jsonReaction.toReactionDto())
|
||||
|
||||
if event["event"]{"communities"} != nil:
|
||||
for jsonCommunity in event["event"]["communities"]:
|
||||
signal.communities.add(jsonCommunity.toCommunity)
|
||||
signal.communities.add(jsonCommunity.toCommunityDto())
|
||||
|
||||
if event["event"]{"requestsToJoinCommunity"} != nil:
|
||||
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
||||
|
@ -73,23 +73,25 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
|
|||
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification())
|
||||
|
||||
if event["event"]{"pinMessages"} != nil:
|
||||
for jsonPinnedMessage in event["event"]["pinMessages"]:
|
||||
var contentType: ContentType
|
||||
try:
|
||||
contentType = ContentType(jsonPinnedMessage{"contentType"}.getInt)
|
||||
except:
|
||||
contentType = ContentType.Message
|
||||
signal.pinnedMessages.add(Message(
|
||||
id: jsonPinnedMessage{"message_id"}.getStr,
|
||||
chatId: jsonPinnedMessage{"chat_id"}.getStr,
|
||||
localChatId: jsonPinnedMessage{"localChatId"}.getStr,
|
||||
pinnedBy: jsonPinnedMessage{"from"}.getStr,
|
||||
identicon: jsonPinnedMessage{"identicon"}.getStr,
|
||||
alias: jsonPinnedMessage{"alias"}.getStr,
|
||||
clock: jsonPinnedMessage{"clock"}.getInt,
|
||||
isPinned: jsonPinnedMessage{"pinned"}.getBool,
|
||||
contentType: contentType
|
||||
))
|
||||
discard
|
||||
# Need to refactor this
|
||||
# for jsonPinnedMessage in event["event"]["pinMessages"]:
|
||||
# var contentType: ContentType
|
||||
# try:
|
||||
# contentType = ContentType(jsonPinnedMessage{"contentType"}.getInt)
|
||||
# except:
|
||||
# contentType = ContentType.Message
|
||||
# signal.pinnedMessages.add(Message(
|
||||
# id: jsonPinnedMessage{"message_id"}.getStr,
|
||||
# chatId: jsonPinnedMessage{"chat_id"}.getStr,
|
||||
# localChatId: jsonPinnedMessage{"localChatId"}.getStr,
|
||||
# pinnedBy: jsonPinnedMessage{"from"}.getStr,
|
||||
# identicon: jsonPinnedMessage{"identicon"}.getStr,
|
||||
# alias: jsonPinnedMessage{"alias"}.getStr,
|
||||
# clock: jsonPinnedMessage{"clock"}.getInt,
|
||||
# isPinned: jsonPinnedMessage{"pinned"}.getBool,
|
||||
# contentType: contentType
|
||||
# ))
|
||||
|
||||
result = signal
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ QtObject:
|
|||
self.activeSubItemChanged()
|
||||
|
||||
proc getId(self: ActiveItem): string {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return ""
|
||||
return self.item.id
|
||||
|
||||
QtProperty[string] id:
|
||||
|
@ -48,54 +50,72 @@ QtObject:
|
|||
read = getIsSubItemActive
|
||||
|
||||
proc getName(self: ActiveItem): string {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return ""
|
||||
return self.item.name
|
||||
|
||||
QtProperty[string] name:
|
||||
read = getName
|
||||
|
||||
proc getIcon(self: ActiveItem): string {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return ""
|
||||
return self.item.icon
|
||||
|
||||
QtProperty[string] icon:
|
||||
read = getIcon
|
||||
|
||||
proc getColor(self: ActiveItem): string {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return ""
|
||||
return self.item.color
|
||||
|
||||
QtProperty[string] color:
|
||||
read = getColor
|
||||
|
||||
proc getDescription(self: ActiveItem): string {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return ""
|
||||
return self.item.description
|
||||
|
||||
QtProperty[string] description:
|
||||
read = getDescription
|
||||
|
||||
proc getType(self: ActiveItem): int {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return 0
|
||||
return self.item.`type`
|
||||
|
||||
QtProperty[int] type:
|
||||
read = getType
|
||||
|
||||
proc getHasUnreadMessages(self: ActiveItem): bool {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return false
|
||||
return self.item.hasUnreadMessages
|
||||
|
||||
QtProperty[bool] hasUnreadMessages:
|
||||
read = getHasUnreadMessages
|
||||
|
||||
proc getNotificationCount(self: ActiveItem): int {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return 0
|
||||
return self.item.notificationsCount
|
||||
|
||||
QtProperty[int] notificationCount:
|
||||
read = getNotificationCount
|
||||
|
||||
proc getMuted(self: ActiveItem): bool {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return false
|
||||
return self.item.muted
|
||||
|
||||
QtProperty[bool] muted:
|
||||
read = getMuted
|
||||
|
||||
proc getPosition(self: ActiveItem): int {.slot.} =
|
||||
if(self.item.isNil):
|
||||
return 0
|
||||
return self.item.position
|
||||
|
||||
QtProperty[int] position:
|
||||
|
|
|
@ -18,18 +18,20 @@ type
|
|||
events: EventEmitter
|
||||
chatId: string
|
||||
belongsToCommunity: bool
|
||||
isUsersListAvailable: bool #users list is not available for 1:1 chat
|
||||
chatService: chat_service.ServiceInterface
|
||||
communityService: community_service.ServiceInterface
|
||||
messageService: message_service.Service
|
||||
|
||||
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 =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.chatId = chatId
|
||||
result.belongsToCommunity = belongsToCommunity
|
||||
result.isUsersListAvailable = isUsersListAvailable
|
||||
result.chatService = chatService
|
||||
result.communityService = communityService
|
||||
result.messageService = messageService
|
||||
|
@ -67,4 +69,7 @@ method unpinMessage*(self: Controller, messageId: string) =
|
|||
|
||||
method getMessageDetails*(self: Controller, messageId: 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):
|
||||
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")
|
|
@ -10,6 +10,7 @@ import input_area/module as input_area_module
|
|||
import messages/module as messages_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/community/service as community_service
|
||||
import ../../../../../app_service/service/message/service as message_service
|
||||
|
@ -32,7 +33,8 @@ type
|
|||
usersModule: users_module.AccessInterface
|
||||
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,
|
||||
messageService: message_service.Service):
|
||||
Module =
|
||||
|
@ -40,14 +42,15 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
|
|||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, chatId, belongsToCommunity, chatService, communityService,
|
||||
messageService)
|
||||
result.controller = controller.newController(result, events, chatId, belongsToCommunity, isUsersListAvailable,
|
||||
chatService, communityService, messageService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
result.inputAreaModule = input_area_module.newModule(result, chatId, belongsToCommunity, chatService, communityService)
|
||||
result.messagesModule = messages_module.newModule(result, events, chatId, belongsToCommunity, chatService,
|
||||
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) =
|
||||
self.inputAreaModule.delete
|
||||
|
@ -147,4 +150,6 @@ method onPinMessage*(self: Module, messageId: string) =
|
|||
return
|
||||
|
||||
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")
|
||||
|
||||
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")
|
|
@ -1,33 +1,80 @@
|
|||
import sequtils, sugar
|
||||
import controller_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/message/service as message_service
|
||||
|
||||
import eventemitter
|
||||
|
||||
export controller_interface
|
||||
|
||||
type
|
||||
Controller* = ref object of controller_interface.AccessInterface
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
sectionId: string
|
||||
chatId: string
|
||||
belongsToCommunity: bool
|
||||
isUsersListAvailable: bool #users list is not available for 1:1 chat
|
||||
contactService: contact_service.Service
|
||||
communityService: community_service.ServiceInterface
|
||||
messageService: message_service.Service
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface, chatId: string, belongsToCommunity: bool,
|
||||
communityService: community_service.ServiceInterface): Controller =
|
||||
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.sectionId = sectionId
|
||||
result.chatId = chatId
|
||||
result.belongsToCommunity = belongsToCommunity
|
||||
result.isUsersListAvailable = isUsersListAvailable
|
||||
result.contactService = contactService
|
||||
result.communityService = communityService
|
||||
result.messageService = messageService
|
||||
|
||||
method delete*(self: Controller) =
|
||||
discard
|
||||
|
||||
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 =
|
||||
return self.chatId
|
||||
self.delegate.newMessagesLoaded(args.messages)
|
||||
|
||||
method belongsToCommunity*(self: Controller): bool =
|
||||
return self.belongsToCommunity
|
||||
self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args):
|
||||
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
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
@ -13,7 +13,12 @@ method init*(self: AccessInterface) {.base.} =
|
|||
method getChatId*(self: AccessInterface): string {.base.} =
|
||||
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")
|
||||
|
||||
|
||||
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
|
||||
OnlineStatus* {.pure.} = enum
|
||||
Online = 0
|
||||
Idle
|
||||
Offline = 0
|
||||
Online
|
||||
DoNotDisturb
|
||||
Idle
|
||||
Invisible
|
||||
Offline
|
||||
|
||||
|
||||
type
|
||||
Item* = ref object
|
||||
id: string
|
||||
name: string
|
||||
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.id = id
|
||||
result.name = name
|
||||
result.onlineStatus = onlineStatus
|
||||
result.identicon = identicon
|
||||
result.icon = icon
|
||||
result.isIdenticon = isidenticon
|
||||
|
||||
proc id*(self: Item): string {.inline.} =
|
||||
self.id
|
||||
|
@ -35,5 +37,14 @@ proc onlineStatus*(self: Item): OnlineStatus {.inline.} =
|
|||
proc `onlineStatus=`*(self: Item, value: OnlineStatus) {.inline.} =
|
||||
self.onlineStatus = value
|
||||
|
||||
proc identicon*(self: Item): string {.inline.} =
|
||||
self.identicon
|
||||
proc icon*(self: Item): string {.inline.} =
|
||||
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
|
||||
Name
|
||||
OnlineStatus
|
||||
Identicon
|
||||
Icon
|
||||
IsIdenticon
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -33,7 +34,8 @@ QtObject:
|
|||
ModelRole.Id.int:"id",
|
||||
ModelRole.Name.int:"name",
|
||||
ModelRole.OnlineStatus.int:"onlineStatus",
|
||||
ModelRole.Identicon.int:"identicon",
|
||||
ModelRole.Icon.int:"icon",
|
||||
ModelRole.IsIdenticon.int:"isIdenticon",
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -53,13 +55,30 @@ QtObject:
|
|||
result = newQVariant(item.name)
|
||||
of ModelRole.OnlineStatus:
|
||||
result = newQVariant(item.onlineStatus.int)
|
||||
of ModelRole.Identicon:
|
||||
result = newQVariant(item.identicon)
|
||||
of ModelRole.Icon:
|
||||
result = newQVariant(item.icon)
|
||||
of ModelRole.IsIdenticon:
|
||||
result = newQVariant(item.isIdenticon)
|
||||
|
||||
proc setItems*(self: Model, items: seq[Item]) =
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.endResetModel()
|
||||
proc addItem*(self: Model, item: Item) =
|
||||
# we need to maintain online contact on top, that means
|
||||
# if we add an item online status we add it as the last online item (before the first offline item)
|
||||
# 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 =
|
||||
for i in 0 ..< self.items.len:
|
||||
|
@ -68,6 +87,17 @@ QtObject:
|
|||
|
||||
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) =
|
||||
let ind = self.findIndexForMessageId(id)
|
||||
if(ind == -1):
|
||||
|
@ -78,12 +108,38 @@ QtObject:
|
|||
let index = self.createIndex(ind, 0, nil)
|
||||
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) =
|
||||
let ind = self.findIndexForMessageId(id)
|
||||
if(ind == -1):
|
||||
return
|
||||
|
||||
self.items[ind].onlineStatus = onlineStatus
|
||||
|
||||
let index = self.createIndex(ind, 0, nil)
|
||||
self.dataChanged(index, index, @[ModelRole.OnlineStatus.int])
|
||||
if(self.items[ind].onlineStatus == onlineStatus):
|
||||
return
|
||||
|
||||
var item = self.items[ind]
|
||||
item.onlineStatus = onlineStatus
|
||||
self.removeItemWithIndex(ind)
|
||||
self.addItem(item)
|
|
@ -1,11 +1,14 @@
|
|||
import NimQml
|
||||
import io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
import view, controller
|
||||
import view, item, model, controller
|
||||
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/message/service as message_service
|
||||
|
||||
import eventemitter
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -17,14 +20,16 @@ type
|
|||
controller: controller.AccessInterface
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, chatId: string, belongsToCommunity: bool,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface):
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||
belongsToCommunity: bool, isUsersListAvailable: bool, contactService: contact_service.Service,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
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
|
||||
|
||||
method delete*(self: Module) =
|
||||
|
@ -33,8 +38,6 @@ method delete*(self: Module) =
|
|||
self.controller.delete
|
||||
|
||||
method load*(self: Module) =
|
||||
singletonInstance.engine.setRootContextProperty("usersModule", self.viewVariant)
|
||||
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
|
@ -42,8 +45,56 @@ method isLoaded*(self: Module): bool =
|
|||
return self.moduleLoaded
|
||||
|
||||
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.delegate.usersDidLoad()
|
||||
|
||||
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
|
||||
delegate: io_interface.AccessInterface
|
||||
model: Model
|
||||
modelVariant: QVariant
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.model.delete
|
||||
self.modelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
|
@ -17,6 +19,19 @@ QtObject:
|
|||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.model = newModel()
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
|
||||
proc model*(self: View): Model =
|
||||
return self.model
|
||||
|
||||
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
|
||||
|
||||
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 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/community/service_interface as community_service
|
||||
import ../../../../app_service/service/message/service as message_service
|
||||
|
@ -19,19 +20,20 @@ type
|
|||
activeItemId: string
|
||||
activeSubItemId: string
|
||||
events: EventEmitter
|
||||
contactService: contact_service.Service
|
||||
chatService: chat_service.ServiceInterface
|
||||
communityService: community_service.ServiceInterface
|
||||
messageService: message_service.Service
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface, sectionId: string, isCommunity: bool, events: EventEmitter,
|
||||
chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service): Controller =
|
||||
contactService: contact_service.Service, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.sectionId = sectionId
|
||||
result.isCommunitySection = isCommunity
|
||||
result.events = events
|
||||
result.contactService = contactService
|
||||
result.chatService = chatService
|
||||
result.communityService = communityService
|
||||
result.messageService = messageService
|
||||
|
@ -87,5 +89,5 @@ method getOneToOneChatNameAndImage*(self: Controller, chatId: string):
|
|||
method createPublicChat*(self: Controller, chatId: string) =
|
||||
let response = self.chatService.createPublicChat(chatId)
|
||||
if(response.success):
|
||||
self.delegate.addNewPublicChat(response.chatDto, self.events, self.chatService, self.communityService,
|
||||
self.messageService)
|
||||
self.delegate.addNewPublicChat(response.chatDto, self.events, self.contactService, self.chatService,
|
||||
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 ../../../../app_service/service/contacts/service as contact_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/message/service as message_service
|
||||
|
@ -31,6 +32,7 @@ proc newModule*(
|
|||
events: EventEmitter,
|
||||
sectionId: string,
|
||||
isCommunity: bool,
|
||||
contactService: contact_service.Service,
|
||||
chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service
|
||||
|
@ -39,8 +41,8 @@ proc newModule*(
|
|||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, sectionId, isCommunity, events, chatService, communityService,
|
||||
messageService)
|
||||
result.controller = controller.newController(result, sectionId, isCommunity, events, contactService, chatService,
|
||||
communityService, messageService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
result.chatContentModule = initOrderedTable[string, chat_content_module.AccessInterface]()
|
||||
|
@ -56,14 +58,15 @@ method delete*(self: Module) =
|
|||
method isCommunity*(self: Module): bool =
|
||||
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,
|
||||
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 chats = self.controller.getChatDetailsForChatTypes(types)
|
||||
|
||||
|
@ -74,13 +77,15 @@ proc buildChatUI(self: Module, events: EventEmitter, chatService: chat_service.S
|
|||
var chatName = c.name
|
||||
var chatImage = c.identicon
|
||||
var isIdenticon = false
|
||||
var isUsersListAvailable = true
|
||||
if(c.chatType == ChatType.OneToOne):
|
||||
isUsersListAvailable = false
|
||||
(chatName, chatImage, isIdenticon) = self.controller.getOneToOneChatNameAndImage(c.id)
|
||||
|
||||
let item = initItem(c.id, chatName, chatImage, isIdenticon, c.color, c.description, c.chatType.int, hasNotification,
|
||||
notificationsCount, c.muted, false, 0)
|
||||
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
|
||||
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, "")
|
||||
|
||||
proc buildCommunityUI(self: Module, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
||||
proc buildCommunityUI(self: Module, events: EventEmitter, contactService: contact_service.Service,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service) =
|
||||
var selectedItemId = ""
|
||||
var selectedSubItemId = ""
|
||||
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,
|
||||
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
||||
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
|
||||
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,
|
||||
chatDto.description, hasNotification, notificationsCount, chatDto.muted, false, c.position)
|
||||
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,
|
||||
# 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)
|
||||
|
||||
method load*(self: Module, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
||||
method load*(self: Module, events: EventEmitter, contactService: contact_service.Service,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service) =
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
if(self.controller.isCommunity()):
|
||||
self.buildCommunityUI(events, chatService, communityService, messageService)
|
||||
self.buildCommunityUI(events, contactService, chatService, communityService, messageService)
|
||||
else:
|
||||
self.buildChatUI(events, chatService, communityService, messageService)
|
||||
self.buildChatUI(events, contactService, chatService, communityService, messageService)
|
||||
|
||||
for cModule in self.chatContentModule.values:
|
||||
cModule.load()
|
||||
|
@ -230,14 +237,15 @@ method createPublicChat*(self: Module, chatId: string) =
|
|||
|
||||
self.controller.createPublicChat(chatId)
|
||||
|
||||
method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) =
|
||||
method addNewPublicChat*(self: Module, chatDto: ChatDto, events: EventEmitter, contactService: contact_service.Service,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service) =
|
||||
let hasNotification = chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0
|
||||
let notificationsCount = chatDto.unviewedMentionsCount
|
||||
let item = initItem(chatDto.id, chatDto.name, chatDto.identicon, true, chatDto.color, chatDto.description,
|
||||
chatDto.chatType.int, hasNotification, notificationsCount, chatDto.muted, false, 0)
|
||||
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
|
||||
self.setActiveItemSubItem(item.id, "")
|
|
@ -1,5 +1,6 @@
|
|||
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/community/service_interface as community_service
|
||||
import ../../../../../app_service/service/message/service as message_service
|
||||
|
@ -9,8 +10,9 @@ import eventemitter
|
|||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method load*(self: AccessInterface, events: EventEmitter, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) {.base.} =
|
||||
method load*(self: AccessInterface, events: EventEmitter, contactService: contact_service.Service,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method isLoaded*(self: AccessInterface): bool {.base.} =
|
||||
|
|
|
@ -2,6 +2,6 @@ method activeItemSubItemSet*(self: AccessInterface, itemId: string, subItemId: s
|
|||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addNewPublicChat*(self: AccessInterface, chatDto: ChatDto, events: EventEmitter,
|
||||
chatService: chat_service.ServiceInterface, communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service) {.base.} =
|
||||
contactService: contact_service.Service, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -84,8 +84,8 @@ proc newModule*[T](
|
|||
result.moduleLoaded = false
|
||||
|
||||
# Submodules
|
||||
result.chatSectionModule = chat_section_module.newModule(result, events, conf.CHAT_SECTION_ID, false, chatService,
|
||||
communityService, messageService)
|
||||
result.chatSectionModule = chat_section_module.newModule(result, events, conf.CHAT_SECTION_ID, false, contactsService,
|
||||
chatService, communityService, messageService)
|
||||
result.communitySectionsModule = initOrderedTable[string, chat_section_module.AccessInterface]()
|
||||
result.walletSectionModule = wallet_section_module.newModule[Module[T]](result, events, tokenService,
|
||||
transactionService, collectible_service, walletAccountService, settingsService)
|
||||
|
@ -114,6 +114,7 @@ method delete*[T](self: Module[T]) =
|
|||
method load*[T](
|
||||
self: Module[T],
|
||||
events: EventEmitter,
|
||||
contactsService: contacts_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service,
|
||||
messageService: message_service.Service
|
||||
|
@ -131,6 +132,7 @@ method load*[T](
|
|||
events,
|
||||
c.id,
|
||||
true,
|
||||
contactsService,
|
||||
chatService,
|
||||
communityService,
|
||||
messageService
|
||||
|
@ -209,9 +211,9 @@ method load*[T](
|
|||
activeSection = profileSettingsSectionItem
|
||||
|
||||
# Load all sections
|
||||
self.chatSectionModule.load(events, chatService, communityService, messageService)
|
||||
self.chatSectionModule.load(events, contactsService, chatService, communityService, messageService)
|
||||
for cModule in self.communitySectionsModule.values:
|
||||
cModule.load(events, chatService, communityService, messageService)
|
||||
cModule.load(events, contactsService, chatService, communityService, messageService)
|
||||
self.walletSectionModule.load()
|
||||
# self.walletV2SectionModule.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/community/service as community_service
|
||||
import ../../../../app_service/service/message/service as message_service
|
||||
|
@ -10,6 +11,7 @@ method delete*(self: AccessInterface) {.base.} =
|
|||
method load*(
|
||||
self: AccessInterface,
|
||||
events: EventEmitter,
|
||||
contactsService: contacts_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service,
|
||||
messageService: message_service.Service
|
||||
|
|
|
@ -17,6 +17,14 @@ template getProp(obj: JsonNode, prop: string, value: var typedesc[int64]): bool
|
|||
|
||||
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 =
|
||||
var success = false
|
||||
if (obj.kind == JObject and obj.contains(prop)):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import status/ens as status_ens
|
||||
|
||||
include ../../common/json_utils
|
||||
|
@ -17,3 +18,16 @@ const lookupContactTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
|||
if not id.startsWith("0x"):
|
||||
id = status_ens.pubkey(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
|
||||
identicon*: string
|
||||
lastUpdated*: int64
|
||||
lastUpdatedLocally*: int64
|
||||
localNickname*: string
|
||||
image*: Images
|
||||
added*: bool
|
||||
|
@ -36,9 +37,10 @@ proc `$`*(self: ContactsDto): string =
|
|||
id: {self.id},
|
||||
name: {self.name},
|
||||
ensVerified: {self.ensVerified},
|
||||
alias: {self.alias},
|
||||
identicon: {self.identicon},
|
||||
lastUpdated: {self.lastUpdated},
|
||||
alias: {self.alias},
|
||||
identicon: {self.identicon},
|
||||
lastUpdated: {self.lastUpdated},
|
||||
lastUpdatedLocally: {self.lastUpdatedLocally},
|
||||
localNickname: {self.localNickname},
|
||||
image:[
|
||||
{$self.image}
|
||||
|
@ -69,6 +71,7 @@ proc toContactsDto*(jsonObj: JsonNode): ContactsDto =
|
|||
discard jsonObj.getProp("alias", result.alias)
|
||||
discard jsonObj.getProp("identicon", result.identicon)
|
||||
discard jsonObj.getProp("lastUpdated", result.lastUpdated)
|
||||
discard jsonObj.getProp("lastUpdatedLocally", result.lastUpdatedLocally)
|
||||
discard jsonObj.getProp("localNickname", result.localNickname)
|
||||
|
||||
var imageObj: JsonNode
|
||||
|
|
|
@ -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 ../../../app/global/global_singleton
|
||||
import ../../../app/core/signals/types
|
||||
import ../../../app/core/tasks/[qt, threadpool]
|
||||
|
||||
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/accounts as status_accounts
|
||||
import status/statusgo_backend_new/chat as status_chat
|
||||
import status/statusgo_backend_new/utils as status_utils
|
||||
|
||||
export contacts_dto
|
||||
export contacts_dto, status_update_dto
|
||||
|
||||
include async_tasks
|
||||
|
||||
|
@ -26,6 +29,17 @@ type
|
|||
ContactAddedArgs* = ref object of Args
|
||||
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:
|
||||
const SIGNAL_CONTACT_LOOKED_UP* = "SIGNAL_CONTACT_LOOKED_UP"
|
||||
# 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_REMOVED* = "new-contactRemoved"
|
||||
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:
|
||||
type Service* = ref object of QObject
|
||||
threadpool: ThreadPool
|
||||
contacts: Table[string, ContactsDto] # [contact_id, ContactsDto]
|
||||
contactsStatus: Table[string, StatusUpdateDto] # [contact_id, StatusUpdateDto]
|
||||
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) =
|
||||
self.closingApp = true
|
||||
self.contacts.clear
|
||||
self.contactsStatus.clear
|
||||
self.QObject.delete
|
||||
|
||||
proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.closingApp = false
|
||||
result.events = events
|
||||
result.threadpool = threadpool
|
||||
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) =
|
||||
try:
|
||||
|
@ -59,15 +93,45 @@ QtObject:
|
|||
let contacts = map(response.result.getElems(), proc(x: JsonNode): ContactsDto = x.toContactsDto())
|
||||
|
||||
for contact in contacts:
|
||||
self.contacts[contact.id] = contact
|
||||
self.addContact(contact)
|
||||
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
error "error: ", errDesription
|
||||
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) =
|
||||
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] =
|
||||
return toSeq(self.contacts.values)
|
||||
|
@ -77,7 +141,10 @@ QtObject:
|
|||
let response = status_contacts.getContactByID(id)
|
||||
|
||||
result = response.result.toContactsDto()
|
||||
self.contacts[result.id] = result
|
||||
if result.id.len == 0:
|
||||
return
|
||||
|
||||
self.addContact(result)
|
||||
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
|
@ -109,6 +176,15 @@ QtObject:
|
|||
blocked: 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] =
|
||||
## 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)
|
||||
|
||||
proc lookupContact*(self: Service, value: string) =
|
||||
if(self.closingApp):
|
||||
return
|
||||
let arg = LookupContactTaskArg(
|
||||
tptr: cast[ByteAddress](lookupContactTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
|
@ -214,3 +292,42 @@ QtObject:
|
|||
value: value
|
||||
)
|
||||
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
|
||||
// (on the backend chat and community sections share the same module since they are actually the same)
|
||||
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
|
||||
property var messageStore
|
||||
|
@ -72,35 +84,37 @@ StatusAppThreePanelLayout {
|
|||
}
|
||||
}
|
||||
|
||||
showRightPanel: (localAccountSensitiveSettings.expandUsersList && (localAccountSensitiveSettings.showOnlineUsers || chatsModel.communities.activeCommunity.active)
|
||||
&& (chatsModel.channelView.activeChannel.chatType !== Constants.chatTypeOneToOne))
|
||||
showRightPanel: {
|
||||
// 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
|
||||
|
||||
Component {
|
||||
id: communityUserListComponent
|
||||
CommunityUserListPanel {
|
||||
//Not Refactored Yet
|
||||
//currentTime: chatColumn.currentTime
|
||||
messageContextMenu: quickActionMessageOptionsMenu
|
||||
// profilePubKey: userProfile.pubKey
|
||||
// community: root.rootStore.chatsModelInst.communities.activeCommunity
|
||||
// currentUserName: Utils.removeStatusEns(root.rootStore.profileModelInst.ens.preferredUsername
|
||||
// || root.rootStore.profileModelInst.profile.username)
|
||||
// currentUserOnline: root.store.userProfileInst.userStatus
|
||||
// contactsList: root.rootStore.allContacts
|
||||
usersModule: {
|
||||
if(chatCommunitySectionModule.activeItem.isSubItemActive)
|
||||
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.activeSubItem.id)
|
||||
else
|
||||
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||
|
||||
return chatCommunitySectionModule.getChatContentModule().usersModule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userListComponent
|
||||
UserListPanel {
|
||||
//Not Refactored Yet
|
||||
//currentTime: chatColumn.currentTime
|
||||
//userList: chatColumn.userList
|
||||
messageContextMenu: quickActionMessageOptionsMenu
|
||||
// profilePubKey: userProfile.pubKey
|
||||
// contactsList: root.rootStore.allContacts
|
||||
// isOnline: root.rootStore.chatsModelInst.isOnline
|
||||
usersModule: {
|
||||
chatCommunitySectionModule.prepareChatContentModuleForChatId(chatCommunitySectionModule.activeItem.id)
|
||||
return chatCommunitySectionModule.getChatContentModule().usersModule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,95 +9,113 @@ import StatusQ.Components 0.1
|
|||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core 0.1
|
||||
|
||||
StatusListItem {
|
||||
id: root
|
||||
Item {
|
||||
id: wrapper
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
implicitHeight: 44
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
height: rectangle.height + 4
|
||||
|
||||
property var contactsList
|
||||
property int statusType: -1
|
||||
property string name: ""
|
||||
property string publicKey: ""
|
||||
property string profilePubKey: ""
|
||||
property string name: ""
|
||||
property string identicon: ""
|
||||
property bool isCurrentUser: (publicKey === profilePubKey)
|
||||
property string profileImage: appMain.getProfileImage(publicKey) || ""
|
||||
property bool highlighted: false
|
||||
property string lastSeen: ""
|
||||
property bool isOnline: false
|
||||
property var currentTime
|
||||
property bool isIdenticon: true
|
||||
property int userStatus: Constants.userStatus.offline
|
||||
property var messageContextMenu
|
||||
|
||||
|
||||
title: isCurrentUser ? qsTr("You") : Emoji.parse(Utils.removeStatusEns(Utils.filterXSS(root.name)))
|
||||
image.source: profileImage || identicon
|
||||
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))
|
||||
property bool enableMouseArea: true
|
||||
property color color: {
|
||||
if (wrapper.hovered) {
|
||||
return Style.current.menuBackgroundHover
|
||||
}
|
||||
return Style.current.transparent
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: !!root.contactsList
|
||||
target: root.contactsList
|
||||
onContactChanged: {
|
||||
if (pubkey === root.publicKey) {
|
||||
root.profileImage = !!appMain.getProfileImage(root.publicKey) ?
|
||||
appMain.getProfileImage(root.publicKey) : ""
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
width: parent.width
|
||||
height: 40
|
||||
radius: 8
|
||||
color: wrapper.color
|
||||
|
||||
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 {
|
||||
id: statusBadge
|
||||
width: 15
|
||||
height: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 22
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 6
|
||||
visible: root.isOnline && !((root.statusType === -1) && (lastSeenMinutesAgo > 7))
|
||||
border.width: 3
|
||||
border.color: root.sensor.containsMouse ? Theme.palette.baseColor2 : Theme.palette.baseColor4
|
||||
property real lastSeenMinutesAgo: ((currentTime/1000 - parseInt(lastSeen)) / 60);
|
||||
color: {
|
||||
if (visible) {
|
||||
if (statusType === Constants.statusType_DoNotDisturb) {
|
||||
return Style.current.red;
|
||||
} else if (isCurrentUser || (lastSeenMinutesAgo < 5.5)) {
|
||||
return Style.current.green;
|
||||
} else if (((statusType !== -1) && (lastSeenMinutesAgo > 5.5)) ||
|
||||
((statusType === -1) && (lastSeenMinutesAgo < 7))) {
|
||||
return Style.current.orange;
|
||||
} else if ((statusType === -1) && (lastSeenMinutesAgo > 7)) {
|
||||
return "transparent";
|
||||
MouseArea {
|
||||
enabled: enableMouseArea
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
wrapper.hovered = true
|
||||
}
|
||||
onExited: {
|
||||
wrapper.hovered = false
|
||||
}
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
//TODO remove dynamic scoping
|
||||
openProfilePopup(wrapper.name, wrapper.publicKey, wrapper.identicon, "", wrapper.name);
|
||||
}
|
||||
else if (mouse.button === Qt.RightButton && !!messageContextMenu) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
messageContextMenu.parent = rectangle
|
||||
messageContextMenu.setXPosition = function() { return 0}
|
||||
messageContextMenu.setYPosition = function() { return rectangle.height}
|
||||
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 Qt.labs.platform 1.1
|
||||
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.panels 1.0
|
||||
import shared.status 1.0
|
||||
|
@ -17,11 +11,11 @@ import utils 1.0
|
|||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
property var userList
|
||||
property var currentTime
|
||||
property bool isOnline
|
||||
property var contactsList
|
||||
property string profilePubKey
|
||||
|
||||
// Important:
|
||||
// Each chat/community has its own ChatContentModule and each ChatContentModule has a single usersModule
|
||||
// usersModule on the backend contains everything needed for this component
|
||||
property var usersModule
|
||||
property var messageContextMenu
|
||||
|
||||
StyledText {
|
||||
|
@ -52,26 +46,14 @@ Item {
|
|||
bottomMargin: Style.current.bigPadding
|
||||
}
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: userListDelegate
|
||||
}
|
||||
|
||||
DelegateModelGeneralized {
|
||||
id: userListDelegate
|
||||
lessThan: [
|
||||
function (left, right) {
|
||||
return (left.lastSeen > right.lastSeen);
|
||||
}
|
||||
]
|
||||
model: root.userList
|
||||
model: usersModule.model
|
||||
delegate: UserDelegate {
|
||||
name: model.userName
|
||||
publicKey: model.publicKey
|
||||
profilePubKey: root.profilePubKey
|
||||
identicon: model.identicon
|
||||
contactsList: root.contactsList
|
||||
lastSeen: model.lastSeen / 1000
|
||||
currentTime: root.currentTime
|
||||
isOnline: root.isOnline
|
||||
publicKey: model.id
|
||||
name: model.name
|
||||
identicon: model.icon
|
||||
isIdenticon: model.isIdenticon
|
||||
userStatus: model.onlineStatus
|
||||
messageContextMenu: root.messageContextMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import QtQuick 2.13
|
||||
import Qt.labs.platform 1.1
|
||||
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.panels 1.0
|
||||
import shared.status 1.0
|
||||
|
@ -20,14 +14,12 @@ import StatusQ.Core.Theme 0.1
|
|||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
property var userList
|
||||
property var currentTime
|
||||
property var contactsList
|
||||
property string profilePubKey
|
||||
|
||||
// Important:
|
||||
// Each chat/community has its own ChatContentModule and each ChatContentModule has a single usersModule
|
||||
// usersModule on the backend contains everything needed for this component
|
||||
property var usersModule
|
||||
property var messageContextMenu
|
||||
property var community
|
||||
property string currentUserName: ""
|
||||
property bool currentUserOnline: true
|
||||
|
||||
StatusBaseText {
|
||||
id: titleText
|
||||
|
@ -59,46 +51,32 @@ Item {
|
|||
bottomMargin: Style.current.bigPadding
|
||||
}
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: userListDelegate
|
||||
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
|
||||
model: usersModule.model
|
||||
delegate: UserDelegate {
|
||||
name: model.userName
|
||||
publicKey: model.pubKey
|
||||
profilePubKey: root.profilePubKey
|
||||
identicon: model.identicon
|
||||
contactsList: root.contactsList
|
||||
lastSeen: model.lastSeen
|
||||
statusType: model.statusType
|
||||
currentTime: root.currentTime
|
||||
isOnline: (model.userName === root.currentUserName) ?
|
||||
root.currentUserOnline : model.online
|
||||
publicKey: model.id
|
||||
name: model.name
|
||||
identicon: model.icon
|
||||
isIdenticon: model.isIdenticon
|
||||
userStatus: model.onlineStatus
|
||||
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
|
||||
// don't follow struct from we have on the backend.
|
||||
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 alias pinnedMessagesPopupComponent: pinnedMessagesPopupComponent
|
||||
|
@ -210,8 +222,11 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
membersButton.visible: (localAccountSensitiveSettings.showOnlineUsers || root.rootStore.chatsModelInst.communities.activeCommunity.active)
|
||||
&& root.rootStore.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatTypeOneToOne
|
||||
membersButton.visible: {
|
||||
// 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
|
||||
notificationButton.visible: localAccountSensitiveSettings.isActivityCenterEnabled
|
||||
notificationButton.tooltip.offset: localAccountSensitiveSettings.expandUsersList ? 0 : 14
|
||||
|
|
|
@ -320,19 +320,18 @@ Item {
|
|||
badge.implicitHeight: 15
|
||||
badge.implicitWidth: 15
|
||||
badge.border.color: hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusAppNavBar.backgroundColor
|
||||
badge.color: {
|
||||
return userProfile.sendUserStatus ? Style.current.green : Style.current.midGrey
|
||||
/*
|
||||
// Use this code once support for custom user status is added
|
||||
switch(userProfile.currentUserStatus){
|
||||
case Constants.statusType_Online:
|
||||
return Style.current.green;
|
||||
case Constants.statusType_DoNotDisturb:
|
||||
return Style.current.red;
|
||||
default:
|
||||
return 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
|
||||
switch(userProfile.currentUserStatus){
|
||||
case Constants.userStatus.online:
|
||||
return Style.current.green;
|
||||
case Constants.userStatus.doNotDisturb:
|
||||
return Style.current.red;
|
||||
default:
|
||||
return Style.current.midGrey;
|
||||
}*/
|
||||
badge.color: appMain.rootStore.userProfileInst.userStatus ? Style.current.green : Style.current.midGrey
|
||||
badge.border.width: 3
|
||||
onClicked: {
|
||||
userStatusContextMenu.opened ?
|
||||
|
|
|
@ -40,6 +40,13 @@ QtObject {
|
|||
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 communityImportingInProgress: 1
|
||||
readonly property int communityImportingError: 2
|
||||
|
@ -113,10 +120,6 @@ QtObject {
|
|||
readonly property string linux: "linux"
|
||||
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
|
||||
readonly property int addressRequested: 1
|
||||
readonly property int declined: 2
|
||||
|
|
Loading…
Reference in New Issue