feat(@desktop/chats): Keep only last 5 chats/channels in the memory
This commit is contained in:
parent
c8ef6bcd9c
commit
21d2c00b40
|
@ -6,6 +6,7 @@ import local_app_settings
|
|||
import user_profile
|
||||
import utils
|
||||
import global_events
|
||||
import loader_deactivator
|
||||
|
||||
export local_account_settings
|
||||
export local_account_sensitive_settings
|
||||
|
@ -13,6 +14,7 @@ export local_app_settings
|
|||
export user_profile
|
||||
export utils
|
||||
export global_events
|
||||
export loader_deactivator
|
||||
|
||||
type
|
||||
GlobalSingleton = object
|
||||
|
@ -63,9 +65,16 @@ proc globalEvents*(self: GlobalSingleton): GlobalEvents =
|
|||
globalEvents = newGlobalEvents()
|
||||
return globalEvents
|
||||
|
||||
proc loaderDeactivator*(self: GlobalSingleton): LoaderDeactivator =
|
||||
var loaderDeactivator {.global.}: LoaderDeactivator
|
||||
if (loaderDeactivator.isNil):
|
||||
loaderDeactivator = newLoaderDeactivator()
|
||||
return loaderDeactivator
|
||||
|
||||
proc delete*(self: GlobalSingleton) =
|
||||
self.engine.delete()
|
||||
self.localAccountSettings.delete()
|
||||
self.localAccountSensitiveSettings.delete()
|
||||
self.localAppSettings.delete()
|
||||
self.userProfile.delete()
|
||||
self.loaderDeactivator.delete()
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import NimQml
|
||||
import std/deques
|
||||
|
||||
const MAX_CHATS_IN_MEMORY = 5
|
||||
|
||||
type
|
||||
ChatInMemory = tuple
|
||||
sectionId: string
|
||||
chatId: string
|
||||
|
||||
QtObject:
|
||||
type LoaderDeactivator* = ref object of QObject
|
||||
keepInMemory: Deque[ChatInMemory]
|
||||
|
||||
proc setup(self: LoaderDeactivator) =
|
||||
self.QObject.setup
|
||||
self.keepInMemory = initDeque[ChatInMemory]()
|
||||
|
||||
proc delete*(self: LoaderDeactivator) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newLoaderDeactivator*():
|
||||
LoaderDeactivator =
|
||||
new(result, delete)
|
||||
result.setup
|
||||
|
||||
proc newChatInMemory(sectionId, chatId: string): ChatInMemory =
|
||||
(sectionId, chatId)
|
||||
|
||||
proc unloadSection*(self: LoaderDeactivator, searchSectionId: string): bool =
|
||||
if searchSectionId.len == 0:
|
||||
return false
|
||||
for (sectionId, _) in self.keepInMemory.items:
|
||||
if sectionId == searchSectionId:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc addChatInMemory*(self: LoaderDeactivator, sectionId, chatId: string): ChatInMemory =
|
||||
if self.keepInMemory.contains(newChatInMemory(sectionId, chatId)):
|
||||
return
|
||||
|
||||
self.keepInMemory.addFirst(newChatInMemory(sectionId, chatId))
|
||||
|
||||
if self.keepInMemory.len > MAX_CHATS_IN_MEMORY:
|
||||
result = self.keepInMemory.popLast()
|
|
@ -374,3 +374,6 @@ method onUserAuthenticated*(self: AccessInterface, pin: string, password: string
|
|||
|
||||
method requestToJoinCommunity*(self: AccessInterface, communityId: string, ensName: string) =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onDeactivateChatLoader*(self: AccessInterface, chatId: string) =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -32,6 +32,7 @@ type
|
|||
highlight: bool
|
||||
trustStatus: TrustStatus
|
||||
onlineStatus: OnlineStatus
|
||||
loaderActive: bool
|
||||
|
||||
proc initItem*(
|
||||
id,
|
||||
|
@ -56,7 +57,8 @@ proc initItem*(
|
|||
highlight: bool = false,
|
||||
categoryOpened: bool = true,
|
||||
trustStatus: TrustStatus = TrustStatus.Unknown,
|
||||
onlineStatus = OnlineStatus.Inactive
|
||||
onlineStatus = OnlineStatus.Inactive,
|
||||
loaderActive = false
|
||||
): Item =
|
||||
result = Item()
|
||||
result.id = id
|
||||
|
@ -83,6 +85,7 @@ proc initItem*(
|
|||
result.categoryOpened = categoryOpened
|
||||
result.trustStatus = trustStatus
|
||||
result.onlineStatus = onlineStatus
|
||||
result.loaderActive = loaderActive
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""chat_section/Item(
|
||||
|
@ -108,6 +111,7 @@ proc `$`*(self: Item): string =
|
|||
categoryOpened: {$self.categoryOpened},
|
||||
trustStatus: {$self.trustStatus},
|
||||
onlineStatus: {$self.onlineStatus},
|
||||
loaderActive: {$self.loaderActive},
|
||||
]"""
|
||||
|
||||
proc toJsonNode*(self: Item): JsonNode =
|
||||
|
@ -132,7 +136,8 @@ proc toJsonNode*(self: Item): JsonNode =
|
|||
"highlight": self.highlight,
|
||||
"categoryOpened": self.categoryOpened,
|
||||
"trustStatus": self.trustStatus,
|
||||
"onlineStatus": self.onlineStatus
|
||||
"onlineStatus": self.onlineStatus,
|
||||
"loaderActive": self.loaderActive
|
||||
}
|
||||
|
||||
proc delete*(self: Item) =
|
||||
|
@ -263,3 +268,9 @@ proc `onlineStatus=`*(self: var Item, value: OnlineStatus) =
|
|||
|
||||
proc setHasUnreadMessages*(self: Item, value: bool) =
|
||||
self.hasUnreadMessages = value
|
||||
|
||||
proc loaderActive*(self: Item): bool =
|
||||
self.loaderActive
|
||||
|
||||
proc `loaderActive=`*(self: var Item, value: bool) =
|
||||
self.loaderActive = value
|
|
@ -30,6 +30,7 @@ type
|
|||
TrustStatus
|
||||
OnlineStatus
|
||||
IsCategory
|
||||
LoaderActive
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -97,6 +98,7 @@ QtObject:
|
|||
ModelRole.TrustStatus.int:"trustStatus",
|
||||
ModelRole.OnlineStatus.int:"onlineStatus",
|
||||
ModelRole.IsCategory.int:"isCategory",
|
||||
ModelRole.LoaderActive.int:"loaderActive",
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -158,6 +160,8 @@ QtObject:
|
|||
result = newQVariant(item.onlineStatus.int)
|
||||
of ModelRole.IsCategory:
|
||||
result = newQVariant(item.`type` == CATEGORY_TYPE)
|
||||
of ModelRole.LoaderActive:
|
||||
result = newQVariant(item.loaderActive)
|
||||
|
||||
proc appendItem*(self: Model, item: Item) =
|
||||
let parentModelIndex = newQModelIndex()
|
||||
|
@ -236,7 +240,11 @@ QtObject:
|
|||
let index = self.createIndex(i, 0, nil)
|
||||
# Set active channel to true and others to false
|
||||
self.items[i].active = isChannelToSetActive
|
||||
self.dataChanged(index, index, @[ModelRole.Active.int])
|
||||
if (isChannelToSetActive):
|
||||
self.items[i].loaderActive = true
|
||||
self.dataChanged(index, index, @[ModelRole.Active.int, ModelRole.LoaderActive.int])
|
||||
else:
|
||||
self.dataChanged(index, index, @[ModelRole.Active.int])
|
||||
|
||||
proc changeMutedOnItemById*(self: Model, id: string, muted: bool) =
|
||||
let index = self.getItemIdxById(id)
|
||||
|
@ -501,3 +509,13 @@ QtObject:
|
|||
return
|
||||
|
||||
return self.items[index].toJsonNode()
|
||||
|
||||
proc disableChatLoader*(self: Model, chatId: string) =
|
||||
let index = self.getItemIdxById(chatId)
|
||||
if index == -1:
|
||||
return
|
||||
|
||||
self.items[index].loaderActive = false
|
||||
self.items[index].active = false
|
||||
let modelIndex = self.createIndex(index, 0, nil)
|
||||
self.dataChanged(modelIndex, modelIndex, @[ModelRole.Active.int, ModelRole.LoaderActive.int])
|
|
@ -226,7 +226,7 @@ proc buildChatSectionUI(
|
|||
var isActive = false
|
||||
# restore on a startup last open channel for the section or
|
||||
# make the first channel which doesn't belong to any category active
|
||||
if selectedItemId.len == 0 or chatDto.id == sectionLastOpenChat:
|
||||
if (selectedItemId.len == 0 and sectionLastOpenChat.len == 0) or chatDto.id == sectionLastOpenChat:
|
||||
selectedItemId = chatDto.id
|
||||
isActive = true
|
||||
|
||||
|
@ -258,6 +258,7 @@ proc buildChatSectionUI(
|
|||
colorId,
|
||||
colorHash,
|
||||
onlineStatus = onlineStatus,
|
||||
loaderActive = isActive
|
||||
)
|
||||
|
||||
self.view.chatsModel().appendItem(newChatItem)
|
||||
|
@ -477,9 +478,12 @@ method activeItemSet*(self: Module, itemId: string) =
|
|||
|
||||
# save last open chat in settings for restore on the next app launch
|
||||
singletonInstance.localAccountSensitiveSettings.setSectionLastOpenChat(mySectionId, activeChatId)
|
||||
|
||||
let (deactivateSectionId, deactivateChatId) = singletonInstance.loaderDeactivator.addChatInMemory(mySectionId, activeChatId)
|
||||
|
||||
# notify parent module about active chat/channel
|
||||
self.delegate.onActiveChatChange(mySectionId, activeChatId)
|
||||
self.delegate.onDeactivateSectionAndChatLoader(deactivateSectionId, deactivateChatId)
|
||||
|
||||
method getModuleAsVariant*(self: Module): QVariant =
|
||||
return self.viewVariant
|
||||
|
@ -591,6 +595,7 @@ method addNewChat*(
|
|||
colorHash,
|
||||
chatDto.highlight,
|
||||
onlineStatus = onlineStatus,
|
||||
loaderActive = setChatAsActive
|
||||
)
|
||||
self.addSubmodule(
|
||||
chatDto.id,
|
||||
|
@ -667,8 +672,12 @@ method setFirstChannelAsActive*(self: Module) =
|
|||
if(self.view.chatsModel().getCount() == 0):
|
||||
self.setActiveItem("")
|
||||
return
|
||||
let chat_item = self.view.chatsModel().getItemAtIndex(0)
|
||||
self.setActiveItem(chat_item.id)
|
||||
|
||||
let chat_items = self.view.chatsModel().items()
|
||||
for chat_item in chat_items:
|
||||
if chat_item.`type` != CATEGORY_TYPE:
|
||||
self.setActiveItem(chat_item.id)
|
||||
break
|
||||
|
||||
method onReorderChat*(self: Module, chatId: string, position: int, newCategoryIdForChat: string, prevCategoryId: string, prevCategoryDeleted: bool) =
|
||||
var newCategoryName = ""
|
||||
|
@ -1222,3 +1231,6 @@ proc buildTokenPermissionItem*(self: Module, tokenPermission: CommunityTokenPerm
|
|||
|
||||
return tokenPermissionItem
|
||||
|
||||
method onDeactivateChatLoader*(self: Module, chatId: string) =
|
||||
self.view.chatsModel().disableChatLoader(chatId)
|
||||
|
||||
|
|
|
@ -435,4 +435,4 @@ QtObject:
|
|||
|
||||
QtProperty[bool] allTokenRequirementsMet:
|
||||
read = getAllTokenRequirementsMet
|
||||
notify = allTokenRequirementsMetChanged
|
||||
notify = allTokenRequirementsMetChanged
|
|
@ -298,6 +298,9 @@ method onAcceptRequestToJoinLoading*(self: AccessInterface, communityId: string,
|
|||
method onAcceptRequestToJoinSuccess*(self: AccessInterface, communityId: string, memberKey: string, requestId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onDeactivateSectionAndChatLoader*(self: AccessInterface, sectionId: string, chatId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
# This way (using concepts) is used only for the modules managed by AppController
|
||||
type
|
||||
DelegateInterface* = concept c
|
||||
|
|
|
@ -369,7 +369,8 @@ proc createChannelGroupItem[T](self: Module[T], c: ChannelGroupDto): SectionItem
|
|||
)
|
||||
) else: @[],
|
||||
c.encrypted,
|
||||
communityTokensItems
|
||||
communityTokensItems,
|
||||
loaderActive = active
|
||||
)
|
||||
|
||||
method load*[T](
|
||||
|
@ -733,8 +734,8 @@ method setActiveSection*[T](self: Module[T], item: SectionItem, skipSavingInSett
|
|||
self.controller.setActiveSection(item.id, skipSavingInSettings)
|
||||
|
||||
method setActiveSectionById*[T](self: Module[T], id: string) =
|
||||
let item = self.view.model().getItemById(id)
|
||||
self.setActiveSection(item)
|
||||
let item = self.view.model().getItemById(id)
|
||||
self.setActiveSection(item)
|
||||
|
||||
proc notifySubModulesAboutChange[T](self: Module[T], sectionId: string) =
|
||||
for cModule in self.channelGroupModules.values:
|
||||
|
@ -1187,3 +1188,9 @@ method onDisplayKeycardSharedModuleFlow*[T](self: Module[T]) =
|
|||
method activateStatusDeepLink*[T](self: Module[T], statusDeepLink: string) =
|
||||
let linkToActivate = self.urlsManager.convertExternalLinkToInternal(statusDeepLink)
|
||||
self.urlsManager.onUrlActivated(linkToActivate)
|
||||
|
||||
method onDeactivateSectionAndChatLoader*[T](self: Module[T], sectionId: string, chatId: string) =
|
||||
if (sectionId.len > 0 and self.channelGroupModules.contains(sectionId)):
|
||||
self.channelGroupModules[sectionId].onDeactivateChatLoader(chatId)
|
||||
if (singletonInstance.loaderDeactivator.unloadSection(sectionId)):
|
||||
self.view.model().disableSectionLoader(sectionId)
|
|
@ -55,6 +55,7 @@ type
|
|||
declinedMemberRequestsModel: member_model.Model
|
||||
encrypted: bool
|
||||
communityTokensModel: community_tokens_model.TokenModel
|
||||
loaderActive: bool
|
||||
|
||||
proc initItem*(
|
||||
id: string,
|
||||
|
@ -91,6 +92,7 @@ proc initItem*(
|
|||
declinedMemberRequests: seq[MemberItem] = @[],
|
||||
encrypted: bool = false,
|
||||
communityTokens: seq[TokenItem] = @[],
|
||||
loaderActive = false,
|
||||
): SectionItem =
|
||||
result.id = id
|
||||
result.sectionType = sectionType
|
||||
|
@ -132,6 +134,7 @@ proc initItem*(
|
|||
result.encrypted = encrypted
|
||||
result.communityTokensModel = newTokenModel()
|
||||
result.communityTokensModel.setItems(communityTokens)
|
||||
result.loaderActive = loaderActive
|
||||
|
||||
proc isEmpty*(self: SectionItem): bool =
|
||||
return self.id.len == 0
|
||||
|
@ -171,6 +174,7 @@ proc `$`*(self: SectionItem): string =
|
|||
declinedMemberRequests:{self.declinedMemberRequestsModel},
|
||||
encrypted:{self.encrypted},
|
||||
communityTokensModel:{self.communityTokensModel},
|
||||
loaderActive:{self.loaderActive},
|
||||
]"""
|
||||
|
||||
proc id*(self: SectionItem): string {.inline.} =
|
||||
|
@ -322,3 +326,9 @@ proc communityTokens*(self: SectionItem): community_tokens_model.TokenModel {.in
|
|||
|
||||
proc updatePendingRequestLoadingState*(self: SectionItem, memberKey: string, loading: bool) {.inline.} =
|
||||
self.pendingMemberRequestsModel.updateLoadingState(memberKey, loading)
|
||||
|
||||
proc loaderActive*(self: SectionItem): bool {.inline.} =
|
||||
self.loaderActive
|
||||
|
||||
proc `loaderActive=`*(self: var SectionItem, value: bool) {.inline.} =
|
||||
self.loaderActive = value
|
||||
|
|
|
@ -43,6 +43,7 @@ type
|
|||
PendingMemberRequestsModel
|
||||
DeclinedMemberRequestsModel
|
||||
AmIBanned
|
||||
LoaderActive
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -114,7 +115,8 @@ QtObject:
|
|||
ModelRole.CommunityTokensModel.int:"communityTokens",
|
||||
ModelRole.PendingMemberRequestsModel.int:"pendingMemberRequests",
|
||||
ModelRole.DeclinedMemberRequestsModel.int:"declinedMemberRequests",
|
||||
ModelRole.AmIBanned.int:"amIBanned"
|
||||
ModelRole.AmIBanned.int:"amIBanned",
|
||||
ModelRole.LoaderActive.int:"loaderActive"
|
||||
}.toTable
|
||||
|
||||
method data(self: SectionModel, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -198,6 +200,8 @@ QtObject:
|
|||
result = newQVariant(item.declinedMemberRequests)
|
||||
of ModelRole.AmIBanned:
|
||||
result = newQVariant(item.amIBanned)
|
||||
of ModelRole.LoaderActive:
|
||||
result = newQVariant(item.loaderActive)
|
||||
|
||||
proc isItemExist(self: SectionModel, id: string): bool =
|
||||
for it in self.items:
|
||||
|
@ -293,7 +297,8 @@ QtObject:
|
|||
ModelRole.CommunityTokensModel.int,
|
||||
ModelRole.PendingMemberRequestsModel.int,
|
||||
ModelRole.DeclinedMemberRequestsModel.int,
|
||||
ModelRole.AmIBanned.int
|
||||
ModelRole.AmIBanned.int,
|
||||
ModelRole.LoaderActive.int
|
||||
])
|
||||
|
||||
proc getNthEnabledItem*(self: SectionModel, nth: int): SectionItem =
|
||||
|
@ -331,7 +336,9 @@ QtObject:
|
|||
if(self.items[i].id == id):
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
self.items[i].active = true
|
||||
self.dataChanged(index, index, @[ModelRole.Active.int])
|
||||
self.items[i].loaderActive = true
|
||||
|
||||
self.dataChanged(index, index, @[ModelRole.Active.int, ModelRole.LoaderActive.int])
|
||||
|
||||
proc sectionVisibilityUpdated*(self: SectionModel) {.signal.}
|
||||
|
||||
|
@ -428,5 +435,13 @@ QtObject:
|
|||
"ensOnly": item.ensOnly,
|
||||
"nbMembers": item.members.getCount(),
|
||||
"encrypted": item.encrypted,
|
||||
"loaderActive": item.loaderActive,
|
||||
}
|
||||
return $jsonObj
|
||||
|
||||
proc disableSectionLoader*(self: SectionModel, sectionId: string) =
|
||||
for i in 0 ..< self.items.len:
|
||||
if(self.items[i].id == sectionId):
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
self.items[i].loaderActive = false
|
||||
self.dataChanged(index, index, @[ModelRole.LoaderActive.int])
|
|
@ -124,21 +124,11 @@ Item {
|
|||
id: chatLoader
|
||||
|
||||
// Channels/chats are not loaded by default and only load when first put active
|
||||
active: false
|
||||
active: model.loaderActive
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: model.active
|
||||
|
||||
// Removing the binding in order not to unload the content:
|
||||
// It is done for keeping:
|
||||
// - the last channel/chat scroll position
|
||||
// - the last typed but not sent text
|
||||
Binding on active {
|
||||
when: !chatLoader.active
|
||||
restoreMode: Binding.RestoreNone
|
||||
value: model.itemId && root.isSectionActive && (model.itemId === root.activeChatId || model.itemId === root.activeSubItemId)
|
||||
}
|
||||
|
||||
sourceComponent: ChatContentView {
|
||||
visible: !root.rootStore.openCreateChat && isActiveChannel
|
||||
rootStore: root.rootStore
|
||||
|
|
|
@ -977,7 +977,7 @@ Item {
|
|||
readonly property string sectionId: model.id
|
||||
|
||||
asynchronous: true
|
||||
active: sectionId === appMain.rootStore.mainModuleInst.activeSection.id
|
||||
active: model.loaderActive
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
|
@ -1015,13 +1015,6 @@ Item {
|
|||
onOpenAppSearch: {
|
||||
appSearch.openSearchPopup()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Do not unload section data from the memory in order not
|
||||
// to reset scroll, not send text input and etc during the
|
||||
// sections switching
|
||||
communityLoader.active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue