parent
5f4000b7a5
commit
f8ecd9dbce
|
@ -166,7 +166,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
|||
statusFoundation.events, statusFoundation.threadpool, result.networkService, result.settingsService,
|
||||
result.activityCenterService
|
||||
)
|
||||
result.chatService = chat_service.newService(statusFoundation.events, result.contactsService)
|
||||
result.chatService = chat_service.newService(statusFoundation.events, statusFoundation.threadpool, result.contactsService)
|
||||
result.tokenService = token_service.newService(
|
||||
statusFoundation.events, statusFoundation.threadpool, result.networkService
|
||||
)
|
||||
|
|
|
@ -86,6 +86,24 @@ proc init*(self: Controller) =
|
|||
let d9 = 9*86400 # 9 days
|
||||
discard self.settingsService.setDefaultSyncPeriod(d9)
|
||||
|
||||
self.events.on(SIGNAL_CHATS_LOADED) do(e:Args):
|
||||
let args = ChannelGroupsArgs(e)
|
||||
self.delegate.onChatsLoaded(
|
||||
args.channelGroups,
|
||||
self.events,
|
||||
self.settingsService,
|
||||
self.nodeConfigurationService,
|
||||
self.contactsService,
|
||||
self.chatService,
|
||||
self.communityService,
|
||||
self.messageService,
|
||||
self.gifService,
|
||||
self.mailserversService,
|
||||
)
|
||||
|
||||
self.events.on(SIGNAL_CHATS_LOADING_FAILED) do(e:Args):
|
||||
self.delegate.onChatsLoadingFailed()
|
||||
|
||||
self.events.on(SIGNAL_ACTIVE_MAILSERVER_CHANGED) do(e:Args):
|
||||
let args = ActiveMailserverChangedArgs(e)
|
||||
if args.nodeAddress == "":
|
||||
|
|
|
@ -68,6 +68,24 @@ method chatSectionDidLoad*(self: AccessInterface) {.base.} =
|
|||
method communitySectionDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onChatsLoaded*(
|
||||
self: AccessInterface,
|
||||
channelGroups: seq[ChannelGroupDto],
|
||||
events: EventEmitter,
|
||||
settingsService: settings_service.Service,
|
||||
nodeConfigurationService: node_configuration_service.Service,
|
||||
contactsService: contacts_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service,
|
||||
messageService: message_service.Service,
|
||||
gifService: gif_service.Service,
|
||||
mailserversService: mailservers_service.Service)
|
||||
{.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onChatsLoadingFailed*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onActiveChatChange*(self: AccessInterface, sectionId: string, chatId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
|
|
@ -334,7 +334,6 @@ proc createChannelGroupItem[T](self: Module[T], c: ChannelGroupDto): SectionItem
|
|||
c.encrypted
|
||||
)
|
||||
|
||||
|
||||
method load*[T](
|
||||
self: Module[T],
|
||||
events: EventEmitter,
|
||||
|
@ -356,30 +355,6 @@ method load*[T](
|
|||
if (activeSectionId == ""):
|
||||
activeSectionId = singletonInstance.userProfile.getPubKey()
|
||||
|
||||
let channelGroups = self.controller.getChannelGroups()
|
||||
for channelGroup in channelGroups:
|
||||
self.channelGroupModules[channelGroup.id] = chat_section_module.newModule(
|
||||
self,
|
||||
events,
|
||||
channelGroup.id,
|
||||
isCommunity = channelGroup.channelGroupType == ChannelGroupType.Community,
|
||||
settingsService,
|
||||
nodeConfigurationService,
|
||||
contactsService,
|
||||
chatService,
|
||||
communityService,
|
||||
messageService,
|
||||
gifService,
|
||||
mailserversService
|
||||
)
|
||||
let channelGroupItem = self.createChannelGroupItem(channelGroup)
|
||||
self.view.model().addItem(channelGroupItem)
|
||||
if(activeSectionId == channelGroupItem.id):
|
||||
activeSection = channelGroupItem
|
||||
|
||||
self.channelGroupModules[channelGroup.id].load(channelGroup, events, settingsService, nodeConfigurationService,
|
||||
contactsService, chatService, communityService, messageService, gifService, mailserversService)
|
||||
|
||||
# Communities Portal Section
|
||||
let communitiesPortalSectionItem = initItem(conf.COMMUNITIESPORTAL_SECTION_ID, SectionType.CommunitiesPortal, conf.COMMUNITIESPORTAL_SECTION_NAME,
|
||||
amISectionAdmin = false,
|
||||
|
@ -485,6 +460,56 @@ method load*[T](
|
|||
else:
|
||||
self.setActiveSection(activeSection)
|
||||
|
||||
method onChatsLoaded*[T](
|
||||
self: Module[T],
|
||||
channelGroups: seq[ChannelGroupDto],
|
||||
events: EventEmitter,
|
||||
settingsService: settings_service.Service,
|
||||
nodeConfigurationService: node_configuration_service.Service,
|
||||
contactsService: contacts_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service,
|
||||
messageService: message_service.Service,
|
||||
gifService: gif_service.Service,
|
||||
mailserversService: mailservers_service.Service,
|
||||
) =
|
||||
var activeSection: SectionItem
|
||||
var activeSectionId = singletonInstance.localAccountSensitiveSettings.getActiveSection()
|
||||
if activeSectionId == "":
|
||||
activeSectionId = singletonInstance.userProfile.getPubKey()
|
||||
|
||||
for channelGroup in channelGroups:
|
||||
self.channelGroupModules[channelGroup.id] = chat_section_module.newModule(
|
||||
self,
|
||||
events,
|
||||
channelGroup.id,
|
||||
isCommunity = channelGroup.channelGroupType == ChannelGroupType.Community,
|
||||
settingsService,
|
||||
nodeConfigurationService,
|
||||
contactsService,
|
||||
chatService,
|
||||
communityService,
|
||||
messageService,
|
||||
gifService,
|
||||
mailserversService
|
||||
)
|
||||
let channelGroupItem = self.createChannelGroupItem(channelGroup)
|
||||
self.view.model().addItem(channelGroupItem)
|
||||
if activeSectionId == channelGroupItem.id:
|
||||
activeSection = channelGroupItem
|
||||
|
||||
self.channelGroupModules[channelGroup.id].load(channelGroup, events, settingsService, nodeConfigurationService,
|
||||
contactsService, chatService, communityService, messageService, gifService, mailserversService)
|
||||
|
||||
# Set active section if it is one of the channel sections
|
||||
if not activeSection.isEmpty():
|
||||
self.setActiveSection(activeSection)
|
||||
|
||||
self.view.chatsLoaded()
|
||||
|
||||
method onChatsLoadingFailed*[T](self: Module[T]) =
|
||||
self.view.chatsLoadingFailed()
|
||||
|
||||
proc checkIfModuleDidLoad [T](self: Module[T]) =
|
||||
if self.moduleLoaded:
|
||||
return
|
||||
|
|
|
@ -14,6 +14,8 @@ QtObject:
|
|||
delegate: io_interface.AccessInterface
|
||||
model: section_model.SectionModel
|
||||
modelVariant: QVariant
|
||||
chatsLoaded: bool
|
||||
chatsLoadingFailed: bool
|
||||
activeSection: ActiveSection
|
||||
activeSectionVariant: QVariant
|
||||
chatSearchModel: chat_search_model.Model
|
||||
|
@ -40,6 +42,8 @@ QtObject:
|
|||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.model = section_model.newModel()
|
||||
result.chatsLoaded = false
|
||||
result.chatsLoadingFailed = false
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
result.activeSection = newActiveSection()
|
||||
result.activeSectionVariant = newQVariant(result.activeSection)
|
||||
|
@ -155,6 +159,30 @@ QtObject:
|
|||
proc setCurrentUserStatus*(self: View, status: int) {.slot.} =
|
||||
self.delegate.setCurrentUserStatus(intToEnum(status, StatusType.Unknown))
|
||||
|
||||
proc chatsLoadedChanged(self: View) {.signal.}
|
||||
|
||||
proc chatsLoaded*(self: View) =
|
||||
self.chatsLoaded = true
|
||||
self.chatsLoadedChanged()
|
||||
|
||||
proc getChatsLoaded(self: View): bool {.slot.} =
|
||||
return self.chatsLoaded
|
||||
QtProperty[bool] chatsLoaded:
|
||||
read = getChatsLoaded
|
||||
notify = chatsLoadedChanged
|
||||
|
||||
proc chatsLoadingFailedChanged(self: View) {.signal.}
|
||||
|
||||
proc chatsLoadingFailed*(self: View) =
|
||||
self.chatsLoadingFailed = true
|
||||
self.chatsLoadingFailedChanged()
|
||||
|
||||
proc getChatsLoadingFailed(self: View): bool {.slot.} =
|
||||
return self.chatsLoadingFailed
|
||||
QtProperty[bool] chatsLoadingFailed:
|
||||
read = getChatsLoadingFailed
|
||||
notify = chatsLoadingFailedChanged
|
||||
|
||||
# Since we cannot return QVariant from the proc which has arguments, so cannot have proc like this:
|
||||
# prepareCommunitySectionModuleForCommunityId(self: View, communityId: string): QVariant {.slot.}
|
||||
# we're using combination of
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#################################################
|
||||
# Async get chats (channel groups)
|
||||
#################################################
|
||||
type
|
||||
AsyncGetChatsTaskArg = ref object of QObjectTaskArg
|
||||
|
||||
const asyncGetChatsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncGetChatsTaskArg](argEncoded)
|
||||
|
||||
let response = status_chat.getChats()
|
||||
|
||||
let responseJson = %*{
|
||||
"channelGroups": response.result
|
||||
}
|
||||
arg.finish(responseJson)
|
|
@ -1,5 +1,7 @@
|
|||
import NimQml, Tables, json, sequtils, strformat, chronicles, os, std/algorithm, strutils, uuids, base64
|
||||
import std/[times, os]
|
||||
|
||||
import ../../../app/core/tasks/[qt, threadpool]
|
||||
import ./dto/chat as chat_dto
|
||||
import ../message/dto/message as message_dto
|
||||
import ../activity_center/dto/notification as notification_dto
|
||||
|
@ -18,24 +20,20 @@ from ../../common/account_constants import ZERO_ADDRESS
|
|||
|
||||
export chat_dto
|
||||
|
||||
|
||||
logScope:
|
||||
topics = "chat-service"
|
||||
|
||||
include ../../common/json_utils
|
||||
include ../../../app/core/tasks/common
|
||||
include async_tasks
|
||||
|
||||
type
|
||||
ChannelGroupsArgs* = ref object of Args
|
||||
channelGroups*: seq[ChannelGroupDto]
|
||||
|
||||
ChatUpdateArgs* = ref object of Args
|
||||
chats*: seq[ChatDto]
|
||||
messages*: seq[MessageDto]
|
||||
# TODO refactor that part
|
||||
# pinnedMessages*: seq[MessageDto]
|
||||
# emojiReactions*: seq[Reaction]
|
||||
# communities*: seq[Community]
|
||||
# communityMembershipRequests*: seq[CommunityMembershipRequest]
|
||||
activityCenterNotifications*: seq[ActivityCenterNotificationDto]
|
||||
# statusUpdates*: seq[StatusUpdate]
|
||||
# deletedMessages*: seq[RemovedMessage]
|
||||
|
||||
CreatedChatArgs* = ref object of Args
|
||||
chat*: ChatDto
|
||||
|
@ -85,6 +83,8 @@ type
|
|||
|
||||
|
||||
# Signals which may be emitted by this service:
|
||||
const SIGNAL_CHATS_LOADED* = "chatsLoaded"
|
||||
const SIGNAL_CHATS_LOADING_FAILED* = "chatsLoadingFailed"
|
||||
const SIGNAL_CHAT_UPDATE* = "chatUpdate"
|
||||
const SIGNAL_CHAT_LEFT* = "channelLeft"
|
||||
const SIGNAL_SENDING_FAILED* = "messageSendingFailed"
|
||||
|
@ -105,19 +105,27 @@ const SIGNAL_CHAT_CREATED* = "chatCreated"
|
|||
|
||||
QtObject:
|
||||
type Service* = ref object of QObject
|
||||
threadpool: ThreadPool
|
||||
events: EventEmitter
|
||||
chats: Table[string, ChatDto] # [chat_id, ChatDto]
|
||||
channelGroups: OrderedTable[string, ChannelGroupDto] # [chatGroup_id, ChannelGroupDto]
|
||||
contactService: contact_service.Service
|
||||
|
||||
proc delete*(self: Service) =
|
||||
discard
|
||||
self.QObject.delete
|
||||
|
||||
proc newService*(events: EventEmitter, contactService: contact_service.Service): Service =
|
||||
proc newService*(
|
||||
events: EventEmitter,
|
||||
threadpool: ThreadPool,
|
||||
contactService: contact_service.Service
|
||||
): Service =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.events = events
|
||||
result.threadpool = threadpool
|
||||
result.contactService = contactService
|
||||
result.chats = initTable[string, ChatDto]()
|
||||
result.channelGroups = initOrderedTable[string, ChannelGroupDto]()
|
||||
|
||||
# Forward declarations
|
||||
proc updateOrAddChat*(self: Service, chat: ChatDto)
|
||||
|
@ -152,19 +160,31 @@ QtObject:
|
|||
if (community.joined):
|
||||
self.updateOrAddChannelGroup(community.toChannelGroupDto())
|
||||
|
||||
proc getChannelGroups*(self: Service): seq[ChannelGroupDto] =
|
||||
return toSeq(self.channelGroups.values)
|
||||
|
||||
proc asyncGetChats*(self: Service) =
|
||||
let arg = AsyncGetChatsTaskArg(
|
||||
tptr: cast[ByteAddress](asyncGetChatsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncGetChatsResponse",
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc sortPersonnalChatAsFirst[T, D](x, y: (T, D)): int =
|
||||
if (x[1].channelGroupType == Personal): return -1
|
||||
if (y[1].channelGroupType == Personal): return 1
|
||||
return 0
|
||||
|
||||
proc init*(self: Service) =
|
||||
self.doConnect()
|
||||
|
||||
proc onAsyncGetChatsResponse*(self: Service, response: string) {.slot.} =
|
||||
try:
|
||||
let response = status_chat.getChats()
|
||||
let rpcResponseObj = response.parseJson
|
||||
|
||||
if(rpcResponseObj["channelGroups"].kind == JNull):
|
||||
raise newException(RpcException, "No channel groups returned")
|
||||
|
||||
var chats: seq[ChatDto] = @[]
|
||||
for (sectionId, section) in response.result.pairs:
|
||||
for (sectionId, section) in rpcResponseObj["channelGroups"].pairs:
|
||||
var channelGroup = section.toChannelGroupDto()
|
||||
channelGroup.id = sectionId
|
||||
self.channelGroups[sectionId] = channelGroup
|
||||
|
@ -181,13 +201,17 @@ QtObject:
|
|||
discard status_chat.deactivateChat(chat.id)
|
||||
else:
|
||||
self.chats[chat.id] = chat
|
||||
|
||||
self.events.emit(SIGNAL_CHATS_LOADED, ChannelGroupsArgs(channelGroups: self.getChannelGroups()))
|
||||
except Exception as e:
|
||||
let errDesription = e.msg
|
||||
error "error: ", errDesription
|
||||
return
|
||||
self.events.emit(SIGNAL_CHATS_LOADING_FAILED, Args())
|
||||
|
||||
proc getChannelGroups*(self: Service): seq[ChannelGroupDto] =
|
||||
return toSeq(self.channelGroups.values)
|
||||
proc init*(self: Service) =
|
||||
self.doConnect()
|
||||
|
||||
self.asyncGetChats()
|
||||
|
||||
proc hasChannel*(self: Service, chatId: string): bool =
|
||||
self.chats.hasKey(chatId)
|
||||
|
|
|
@ -20,7 +20,7 @@ import "../popups/community"
|
|||
|
||||
Item {
|
||||
id: root
|
||||
width: 304
|
||||
width: Constants.chatSectionLeftColumnWidth
|
||||
height: parent.height
|
||||
|
||||
// Important:
|
||||
|
|
|
@ -374,9 +374,13 @@ Item {
|
|||
height: 440
|
||||
}
|
||||
|
||||
StatusStickersPopup {
|
||||
id: statusStickersPopup
|
||||
store: chatLayoutContainer.rootStore
|
||||
Loader {
|
||||
id: statusStickersPopupLoader
|
||||
active: appMain.rootStore.mainModuleInst.chatsLoaded
|
||||
sourceComponent: StatusStickersPopup {
|
||||
id: statusStickersPopup
|
||||
store: personalChatLayoutLoader.item.rootStore
|
||||
}
|
||||
}
|
||||
|
||||
StatusMainLayout {
|
||||
|
@ -874,34 +878,78 @@ Item {
|
|||
// If we ever change stack layout component order we need to updade
|
||||
// Constants.appViewStackIndex accordingly
|
||||
|
||||
ChatLayout {
|
||||
id: chatLayoutContainer
|
||||
Loader {
|
||||
id: personalChatLayoutLoader
|
||||
sourceComponent: {
|
||||
if (appMain.rootStore.mainModuleInst.chatsLoadingFailed) {
|
||||
return errorStateComponent
|
||||
}
|
||||
if (appMain.rootStore.mainModuleInst.chatsLoaded) {
|
||||
return personalChatLayoutComponent
|
||||
}
|
||||
return loadingStateComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingStateComponent
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
chatView.emojiPopup: statusEmojiPopup
|
||||
chatView.stickersPopup: statusStickersPopup
|
||||
|
||||
contactsStore: appMain.rootStore.contactStore
|
||||
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
|
||||
rootStore.openCreateChat: createChatView.opened
|
||||
|
||||
chatView.onProfileButtonClicked: {
|
||||
Global.changeAppSectionBySectionType(Constants.appSection.profile);
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
StatusBaseText {
|
||||
text: qsTr("Loading...")
|
||||
}
|
||||
LoadingAnimation {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: errorStateComponent
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
StatusBaseText {
|
||||
text: qsTr("Error loading chats, try closing the app and restarting")
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chatView.onOpenAppSearch: {
|
||||
appSearch.openSearchPopup()
|
||||
}
|
||||
Component {
|
||||
id: personalChatLayoutComponent
|
||||
|
||||
onImportCommunityClicked: {
|
||||
Global.openPopup(communitiesPortalLayoutContainer.importCommunitiesPopup);
|
||||
}
|
||||
ChatLayout {
|
||||
id: chatLayoutContainer
|
||||
|
||||
onCreateCommunityClicked: {
|
||||
Global.openPopup(communitiesPortalLayoutContainer.createCommunitiesPopup);
|
||||
}
|
||||
chatView.emojiPopup: statusEmojiPopup
|
||||
chatView.stickersPopup: statusStickersPopupLoader.item
|
||||
|
||||
Component.onCompleted: {
|
||||
rootStore.chatCommunitySectionModule = appMain.rootStore.mainModuleInst.getChatSectionModule()
|
||||
contactsStore: appMain.rootStore.contactStore
|
||||
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
|
||||
rootStore.openCreateChat: createChatView.opened
|
||||
|
||||
chatView.onProfileButtonClicked: {
|
||||
Global.changeAppSectionBySectionType(Constants.appSection.profile);
|
||||
}
|
||||
|
||||
chatView.onOpenAppSearch: {
|
||||
appSearch.openSearchPopup()
|
||||
}
|
||||
|
||||
onImportCommunityClicked: {
|
||||
Global.openPopup(communitiesPortalLayoutContainer.importCommunitiesPopup);
|
||||
}
|
||||
|
||||
onCreateCommunityClicked: {
|
||||
Global.openPopup(communitiesPortalLayoutContainer.createCommunitiesPopup);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
rootStore.chatCommunitySectionModule = appMain.rootStore.mainModuleInst.getChatSectionModule()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,7 +1023,7 @@ Item {
|
|||
|
||||
sourceComponent: ChatLayout {
|
||||
chatView.emojiPopup: statusEmojiPopup
|
||||
chatView.stickersPopup: statusStickersPopup
|
||||
chatView.stickersPopup: statusStickersPopupLoader.item
|
||||
|
||||
contactsStore: appMain.rootStore.contactStore
|
||||
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
|
||||
|
@ -1006,7 +1054,7 @@ Item {
|
|||
id: createChatView
|
||||
|
||||
property bool opened: false
|
||||
active: opened
|
||||
active: appMain.rootStore.mainModuleInst.chatsLoaded && opened
|
||||
|
||||
asynchronous: true
|
||||
anchors.top: parent.top
|
||||
|
@ -1014,12 +1062,14 @@ Item {
|
|||
anchors.rightMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: parent.width - chatLayoutContainer.chatView.leftPanel.width - anchors.rightMargin - anchors.leftMargin
|
||||
width: active ?
|
||||
parent.width - Constants.chatSectionLeftColumnWidth -
|
||||
anchors.rightMargin - anchors.leftMargin : 0
|
||||
|
||||
sourceComponent: CreateChatView {
|
||||
rootStore: chatLayoutContainer.rootStore
|
||||
rootStore: personalChatLayoutLoader.item.rootStore
|
||||
emojiPopup: statusEmojiPopup
|
||||
stickersPopup: statusStickersPopup
|
||||
stickersPopup: statusStickersPopupLoader.item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1076,7 +1126,7 @@ Item {
|
|||
x: parent.width - width - Style.current.smallPadding
|
||||
y: parent.y + _buttonSize
|
||||
height: appView.height - _buttonSize * 2
|
||||
store: chatLayoutContainer.rootStore
|
||||
store: personalChatLayoutLoader.item.rootStore
|
||||
activityCenterStore: appMain.activityCenterStore
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,8 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
readonly property int chatSectionLeftColumnWidth: 304
|
||||
|
||||
readonly property QtObject appSection: QtObject {
|
||||
readonly property int chat: 0
|
||||
readonly property int community: 1
|
||||
|
|
Loading…
Reference in New Issue