refactor(chat-model): simplify chat model and put category as an Item

Fixes #9494
This commit is contained in:
Jonathan Rainville 2023-02-08 17:58:14 -05:00
parent d059fb7e1f
commit 5352ba8c6f
8 changed files with 346 additions and 413 deletions

View File

@ -130,9 +130,6 @@ method onReorderChat*(self: AccessInterface, chattId: string, position: int, new
method onReorderCategory*(self: AccessInterface, catId: string, position: int) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityCategoryChannelChanged*(self: AccessInterface, chatId: string, newCategoryIdForChat: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityCategoryCreated*(self: AccessInterface, category: Category, chats: seq[ChatDto]) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -27,7 +27,6 @@ type
active: bool
position: int
categoryId: string
categoryName: string
categoryPosition: int
categoryOpened: bool
highlight: bool
@ -51,7 +50,6 @@ proc initItem*(
active: bool,
position: int,
categoryId: string = "",
categoryName: string = "",
categoryPosition: int = -1,
colorId: int = 0,
colorHash: seq[ColorHashSegment] = @[],
@ -80,7 +78,6 @@ proc initItem*(
result.active = active
result.position = position
result.categoryId = categoryId
result.categoryName = categoryName
result.categoryPosition = categoryPosition
result.highlight = highlight
result.categoryOpened = categoryOpened
@ -106,7 +103,6 @@ proc `$`*(self: Item): string =
active: {$self.active},
position: {$self.position},
categoryId: {$self.categoryId},
categoryName: {$self.categoryName},
categoryPosition: {$self.categoryPosition},
highlight: {$self.highlight},
categoryOpened: {$self.categoryOpened},
@ -132,7 +128,6 @@ proc toJsonNode*(self: Item): JsonNode =
"active": self.active,
"position": self.position,
"categoryId": self.categoryId,
"categoryName": self.categoryName,
"categoryPosition": self.categoryPosition,
"highlight": self.highlight,
"categoryOpened": self.categoryOpened,
@ -236,12 +231,6 @@ proc categoryId*(self: Item): string =
proc `categoryId=`*(self: var Item, value: string) =
self.categoryId = value
proc categoryName*(self: Item): string =
self.categoryName
proc `categoryName=`*(self: var Item, value: string) =
self.categoryName = value
proc categoryPosition*(self: Item): int =
self.categoryPosition

View File

@ -1,6 +1,6 @@
import NimQml, Tables, strutils, strformat, json, sequtils
import ../../../../app_service/common/types
from ../../../../app_service/service/chat/dto/chat import ChatType
import ../../../../app_service/service/chat/dto/chat
from ../../../../app_service/service/contacts/dto/contacts import TrustStatus
import item
@ -24,12 +24,12 @@ type
Active
Position
CategoryId
CategoryName
CategoryPosition
Highlight
CategoryOpened
TrustStatus
OnlineStatus
IsCategory
QtObject:
type
@ -91,12 +91,12 @@ QtObject:
ModelRole.Active.int:"active",
ModelRole.Position.int:"position",
ModelRole.CategoryId.int:"categoryId",
ModelRole.CategoryName.int:"categoryName",
ModelRole.CategoryPosition.int:"categoryPosition",
ModelRole.Highlight.int:"highlight",
ModelRole.CategoryOpened.int:"categoryOpened",
ModelRole.TrustStatus.int:"trustStatus",
ModelRole.OnlineStatus.int:"onlineStatus",
ModelRole.IsCategory.int:"isCategory",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -146,8 +146,6 @@ QtObject:
result = newQVariant(item.position)
of ModelRole.CategoryId:
result = newQVariant(item.categoryId)
of ModelRole.CategoryName:
result = newQVariant(item.categoryName)
of ModelRole.CategoryPosition:
result = newQVariant(item.categoryPosition)
of ModelRole.Highlight:
@ -158,6 +156,8 @@ QtObject:
result = newQVariant(item.trustStatus.int)
of ModelRole.OnlineStatus:
result = newQVariant(item.onlineStatus.int)
of ModelRole.IsCategory:
result = newQVariant(item.`type` == CATEGORY_TYPE)
proc appendItem*(self: Model, item: Item) =
let parentModelIndex = newQModelIndex()
@ -177,32 +177,6 @@ QtObject:
idx.inc
return -1
proc getItemIdxByCategory*(self: Model, categoryId: string): int =
var idx = 0
for it in self.items:
if it.categoryId == categoryId:
return idx
idx.inc
return -1
proc hasEmptyChatItem*(self: Model, categoryId: string): bool =
for it in self.items:
if it.id == categoryId:
return true
return false
proc getCategoryNameForCategoryId*(self: Model, categoryId: string): string {.slot.} =
let index = self.getItemIdxByCategory(categoryId)
if index == -1:
return
return self.items[index].categoryName
proc getCategoryOpenedForCategoryId*(self: Model, categoryId: string): bool {.slot.} =
let index = self.getItemIdxByCategory(categoryId)
if index == -1:
return true # Default to true just in case
return self.items[index].categoryOpened
proc changeCategoryOpened*(self: Model, categoryId: string, opened: bool) {.slot.} =
for i in 0 ..< self.items.len:
if self.items[i].categoryId == categoryId:
@ -245,19 +219,15 @@ QtObject:
if(it.id == id):
return it
proc categoryHasUnreadMessagesChanged*(self: Model, categoryId: string, hasUnread: bool) {.signal.}
proc getCategoryHasUnreadMessages*(self: Model, categoryId: string): bool {.slot.} =
return self.items.anyIt(it.categoryId == categoryId and it.hasUnreadMessages)
proc setCategoryHasUnreadMessages*(self: Model, categoryId: string, value: bool) =
self.categoryHasUnreadMessagesChanged(categoryId, value)
proc getCategoryAndPosition*(self: Model, id: string): (string, int) =
let item = self.getItemById(id)
if (not item.isNil):
return ("", item.position)
return ("", -1)
proc setCategoryHasUnreadMessages*(self: Model, categoryId: string, unread: bool) =
let index = self.getItemIdxById(categoryId)
if index == -1:
return
if self.items[index].hasUnreadMessages == unread:
return
self.items[index].hasUnreadMessages = unread
let modelIndex = self.createIndex(index, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.HasUnreadMessages.int])
proc setActiveItem*(self: Model, id: string) =
for i in 0 ..< self.items.len:
@ -339,65 +309,102 @@ QtObject:
ModelRole.Icon.int,
])
proc updateItemsWithCategoryDetailById*(
proc updateCategoryDetailsById*(
self: Model,
ids: seq[string],
categoryId,
newCategoryName: string,
newCategoryPosition: int,
) =
var indexesToDelete: seq[int] = @[]
let categoryIndex = self.getItemIdxById(categoryId)
if categoryIndex == -1:
return
self.items[categoryIndex].name = newCategoryName
self.items[categoryIndex].categoryPosition = newCategoryPosition
let modelIndex = self.createIndex(categoryIndex, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[
ModelRole.Name.int,
ModelRole.CategoryPosition.int,
])
proc updateItemsWithCategoryDetailsById*(
self: Model,
chats: seq[ChatDto],
categoryId,
newCategoryName: string,
newCategoryPosition: int,
) =
for i in 0 ..< self.items.len:
var item = self.items[i]
var hasCategory = item.categoryId == categoryId
if item.`type` == CATEGORY_TYPE:
continue
var hadCategory = item.categoryId == categoryId
var nowHasCategory = false
var found = false
for id in ids:
if item.id != id:
for chat in chats:
if item.id != chat.id:
continue
found = true
nowHasCategory = chat.categoryId == categoryId
item.position = chat.position
item.categoryId = categoryId
item.categoryName = newCategoryName
item.categoryPosition = newCategoryPosition
let modelIndex = self.createIndex(i, 0, nil)
# Also signal on CategoryId because the section header only knows the categoryId
self.dataChanged(modelIndex, modelIndex, @[
ModelRole.Position.int,
ModelRole.CategoryId.int,
ModelRole.CategoryName.int,
ModelRole.CategoryPosition.int,
])
break
if hasCategory and not found:
# This item was removed from the category
if (item.`type` == CATEGORY_TYPE):
# It was an empty item to show the category and it isn't needed anymore
indexesToDelete.add(i)
continue
if (hadCategory and not found and not nowHasCategory) or (hadCategory and found and not nowHasCategory):
item.categoryId = ""
item.categoryName = ""
item.categoryPosition = -1
echo "category changed ", item.name
item.categoryOpened = true
let modelIndex = self.createIndex(i, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[
ModelRole.CategoryId.int,
ModelRole.CategoryName.int,
ModelRole.CategoryPosition.int,
ModelRole.CategoryOpened.int,
])
# Go through indexesToDelete from the highest to the bottom, that way the indexes stay correct
var f = indexesToDelete.len - 1
while f > -1:
self.removeItemByIndex(indexesToDelete[f])
dec(f)
proc removeCategory*(
self: Model,
categoryId: string,
chats: seq[ChatDto]
) =
self.removeItemById(categoryId)
proc renameCategoryOnItems*(self: Model, categoryId, newName: string) =
for i in 0 ..< self.items.len:
var item = self.items[i]
if item.categoryId != categoryId:
continue
item.categoryName = newName
let modelIndex = self.createIndex(i, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.CategoryId.int, ModelRole.CategoryName.int])
for chat in chats:
if chat.id != item.id:
continue
item.position = chat.position
item.categoryId = ""
item.categoryPosition = -1
item.categoryOpened = true
let modelIndex = self.createIndex(i, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[
ModelRole.Position.int,
ModelRole.CategoryId.int,
ModelRole.CategoryPosition.int,
ModelRole.CategoryOpened.int,
])
break
proc renameCategory*(self: Model, categoryId, newName: string) =
let index = self.getItemIdxById(categoryId)
if index == -1:
return
if self.items[index].name == newName:
return
self.items[index].name = newName
let modelIndex = self.createIndex(index, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.Name.int])
proc renameItemById*(self: Model, id, name: string) =
let index = self.getItemIdxById(id)
@ -451,7 +458,6 @@ QtObject:
chatId: string,
position: int,
newCategoryId: string,
newCategoryName: string,
newCategoryPosition: int,
) =
let index = self.getItemIdxById(chatId)
@ -460,11 +466,9 @@ QtObject:
var roles = @[ModelRole.Position.int]
if(self.items[index].categoryId != newCategoryId):
self.items[index].categoryId = newCategoryId
self.items[index].categoryName = newCategoryName
self.items[index].categoryPosition = newCategoryPosition
roles = roles.concat(@[
ModelRole.CategoryId.int,
ModelRole.CategoryName.int,
ModelRole.CategoryPosition.int,
])
self.items[index].position = position
@ -497,9 +501,3 @@ QtObject:
return
return self.items[index].toJsonNode()
proc getItemPartOfCategoryAsJsonById*(self: Model, categoryId: string): JsonNode =
let index = self.getItemIdxByCategory(categoryId)
if index == -1:
return
return self.items[index].toJsonNode()

View File

@ -29,8 +29,6 @@ import ../../../../app_service/service/contacts/dto/contacts as contacts_dto
export io_interface
const CATEGORY_ID_PREFIX = "cat-"
logScope:
topics = "chat-section-module"
@ -129,26 +127,24 @@ proc removeSubmodule(self: Module, chatId: string) =
self.chatContentModules.del(chatId)
proc addEmptyChatItemForCategory(self: Module, category: Category) =
# Add an empty chat item that has the category info
proc addCategoryItem(self: Module, category: Category, amIAdmin: bool) =
let emptyChatItem = chat_item.initItem(
id = CATEGORY_ID_PREFIX & category.id,
name = "",
id = category.id,
category.name,
icon = "",
color = "",
emoji = "",
description = "",
`type` = chat_item.CATEGORY_TYPE,
amIChatAdmin = false,
amIAdmin,
lastMessageTimestamp = 0,
hasUnreadMessages = false,
notificationsCount = 0,
muted = false,
blocked = false,
active = false,
position = 99,
position = -1, # Set position as -1, so that the Category Item is on top of its Channels
category.id,
category.name,
category.position,
)
self.view.chatsModel().appendItem(emptyChatItem)
@ -172,6 +168,10 @@ proc buildChatSectionUI(
# If a category doesn't have a chat, we add it as an empty chat
var categoriesWithAssociatedItems: seq[string] = @[]
for categoryDto in channelGroup.categories:
# Add items for the categories. We use a special type to identify categories
self.addCategoryItem(categoryDto, channelGroup.admin)
for chatDto in channelGroup.chats:
let hasNotification = not chatDto.muted and (chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0)
let notificationsCount = chatDto.unviewedMentionsCount
@ -184,9 +184,7 @@ proc buildChatSectionUI(
let isUsersListAvailable = chatDto.chatType != ChatType.OneToOne
var blocked = false
let belongToCommunity = chatDto.communityId != ""
var categoryName = ""
var categoryPosition = -1
var chatPosition = chatDto.position
if chatDto.chatType == ChatType.OneToOne:
let contactDetails = self.controller.getContactDetails(chatDto.id)
@ -218,11 +216,8 @@ proc buildChatSectionUI(
for category in channelGroup.categories:
if category.id == chatDto.categoryId:
categoriesWithAssociatedItems.add(chatDto.categoryId)
categoryName = category.name
categoryPosition = category.position
break
if categoryName == "":
error "No category found in the channel group for chat", chatName=chatDto.name, categoryId=chatDto.categoryId
let newChatItem = chat_item.initItem(
chatDto.id,
@ -241,7 +236,6 @@ proc buildChatSectionUI(
isActive,
chatDto.position,
chatDto.categoryId,
categoryName,
categoryPosition,
colorId,
colorHash,
@ -265,19 +259,6 @@ proc buildChatSectionUI(
mailserversService,
)
# Loop channelGroup categories to see if some of them don't have a chat
if categoriesWithAssociatedItems.len < channelGroup.categories.len:
for category in channelGroup.categories:
var found = false
for categoryId in categoriesWithAssociatedItems:
if categoryId == category.id:
found = true
break
if found:
continue
self.addEmptyChatItemForCategory(category)
self.setActiveItem(selectedItemId)
proc createItemFromPublicKey(self: Module, publicKey: string): UserItem =
@ -473,7 +454,6 @@ method addNewChat*(
var colorHash: ColorHashDto = @[]
var colorId: int = 0
var onlineStatus = OnlineStatus.Inactive
var categoryName = ""
var categoryPosition = -1
var isUsersListAvailable = true
@ -495,7 +475,6 @@ method addNewChat*(
if category.id == "":
error "No category found for chat", chatName=chatDto.name, categoryId=chatDto.categoryId
else:
categoryName = category.name
categoryPosition = category.position
let chat_item = chat_item.initItem(
@ -515,7 +494,6 @@ method addNewChat*(
setChatAsActive,
chatDto.position,
chatDto.categoryId,
categoryName,
categoryPosition,
colorId,
colorHash,
@ -565,44 +543,36 @@ method removeCommunityChat*(self: Module, chatId: string) =
self.controller.removeCommunityChat(chatId)
method onCommunityCategoryEdited*(self: Module, cat: Category, chats: seq[ChatDto]) =
# Update category itself
self.view.chatsModel().updateCategoryDetailsById(
cat.id,
cat.name,
cat.position
)
# Update chat items that have that category
let chatIds = chats.filterIt(it.categoryId == cat.id).mapIt(it.id)
self.view.chatsModel().updateItemsWithCategoryDetailById(
chatIds,
self.view.chatsModel().updateItemsWithCategoryDetailsById(
chats,
cat.id,
cat.name,
cat.position,
)
if chatIds.len == 0:
# New category with no chats associated, we created an empty chatItem with the category info
self.addEmptyChatItemForCategory(cat)
return
method onCommunityCategoryCreated*(self: Module, cat: Category, chats: seq[ChatDto]) =
if (self.doesCatOrChatExist(cat.id)):
return
if chats.len == 0:
# New category with no chats associated, we created an empty chatItem with the category info
self.addEmptyChatItemForCategory(cat)
return
# Consider the new category as an edit, we just change the chat items with the new cat info
self.onCommunityCategoryEdited(cat, chats)
# TODO get admin status
self.addCategoryItem(cat, false)
# Update chat items that now belong to that category
self.view.chatsModel().updateItemsWithCategoryDetailsById(
chats,
cat.id,
cat.name,
cat.position,
)
method onCommunityCategoryDeleted*(self: Module, cat: Category, chats: seq[ChatDto]) =
# Update chat positions and remove association with category
for chat in chats:
self.view.chatsModel().reorderChatById(
chat.id,
chat.position,
newCategoryId = "",
newCategoryName = "",
newCategoryPosition = -1,
)
self.view.chatsModel().removeItemById(CATEGORY_ID_PREFIX & cat.id)
self.view.chatsModel().removeCategory(cat.id, chats)
method setFirstChannelAsActive*(self: Module) =
if(self.view.chatsModel().getCount() == 0):
@ -611,31 +581,19 @@ method setFirstChannelAsActive*(self: Module) =
let chat_item = self.view.chatsModel().getItemAtIndex(0)
self.setActiveItem(chat_item.id)
method onCommunityCategoryChannelChanged*(self: Module, channelId: string, newCategoryIdForChat: string) =
if channelId == self.controller.getActiveChatId():
if newCategoryIdForChat.len > 0:
self.setActiveItem(channelId)
else:
self.setActiveItem(channelId)
method onReorderChat*(self: Module, chatId: string, position: int, newCategoryIdForChat: string, prevCategoryId: string, prevCategoryDeleted: bool) =
var newCategoryName = ""
var newCategoryPos = -1
if newCategoryIdForChat != "":
let newCategory = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), newCategoryIdForChat)
newCategoryName = newCategory.name
newCategoryPos = newCategory.position
self.view.chatsModel().reorderChatById(chatId, position, newCategoryIdForChat, newCategoryName, newCategoryPos)
if prevCategoryId != "" and not prevCategoryDeleted:
if not self.view.chatsModel().hasEmptyChatItem(CATEGORY_ID_PREFIX & prevCategoryId):
let category = self.controller.getCommunityCategoryDetails(self.controller.getMySectionId(), prevCategoryId)
self.addEmptyChatItemForCategory(category)
self.view.chatsModel().reorderChatById(chatId, position, newCategoryIdForChat, newCategoryPos)
method onReorderCategory*(self: Module, catId: string, position: int) =
self.view.chatsModel().reorderCategoryById(catId, position)
method onCategoryNameChanged*(self: Module, category: Category) =
self.view.chatsModel().renameCategoryOnItems(category.id, category.name)
self.view.chatsModel().renameCategory(category.id, category.name)
method onCommunityChannelDeletedOrChatLeft*(self: Module, chatId: string) =
if(not self.chatContentModules.contains(chatId)):

View File

@ -153,12 +153,6 @@ QtObject:
return
return $jsonObj
proc getItemPartOfCategoryAsJsonById*(self: View, categoryId: string): string {.slot.} =
let jsonObj = self.model.getItemPartOfCategoryAsJsonById(categoryId)
if jsonObj == nil or jsonObj.kind != JObject:
return
return $jsonObj
proc muteChat*(self: View, chatId: string) {.slot.} =
self.delegate.muteChat(chatId)

View File

@ -353,6 +353,26 @@ QtObject:
self.joinedCommunities[settings.id].settings = settings
self.events.emit(SIGNAL_COMMUNITY_EDITED, CommunityArgs(community: self.joinedCommunities[settings.id]))
proc checkForCategoryPropertyUpdates(self: Service, community: CommunityDto, prev_community: CommunityDto) =
for category in community.categories:
# id is present
let index = findIndexById(category.id, prev_community.categories)
if index == -1:
continue
# but something is different
let prev_category = prev_community.categories[index]
if category.position != prev_category.position:
self.events.emit(SIGNAL_COMMUNITY_CATEGORY_REORDERED,
CommunityChatOrderArgs(
communityId: community.id,
categoryId: category.id,
position: category.position))
if category.name != prev_category.name:
self.events.emit(SIGNAL_COMMUNITY_CATEGORY_NAME_EDITED,
CommunityCategoryArgs(communityId: community.id, category: category))
proc handleCommunityUpdates(self: Service, communities: seq[CommunityDto], updatedChats: seq[ChatDto], removedChats: seq[string]) =
try:
var community = communities[0]
@ -403,22 +423,7 @@ QtObject:
# some property has changed
else:
for category in community.categories:
# id is present
let index = findIndexById(category.id, prev_community.categories)
if index == -1:
continue
# but something is different
let prev_category = prev_community.categories[index]
if category.position != prev_category.position:
self.events.emit(SIGNAL_COMMUNITY_CATEGORY_REORDERED,
CommunityChatOrderArgs(
communityId: community.id,
categoryId: category.id,
position: category.position))
if category.name != prev_category.name:
self.events.emit(SIGNAL_COMMUNITY_CATEGORY_NAME_EDITED,
CommunityCategoryArgs(communityId: community.id, category: category))
self.checkForCategoryPropertyUpdates(community, prev_community)
# channel was added
if(community.chats.len > prev_community.chats.len):
@ -1118,6 +1123,11 @@ QtObject:
raise newException(RpcException, "Error creating community category: " & error.message)
if response.result != nil and response.result.kind != JNull:
self.checkForCategoryPropertyUpdates(
response.result["communityChanges"].getElems()[0]["community"].toCommunityDto,
self.joinedCommunities[communityId]
)
var chats: seq[ChatDto] = @[]
for chatId, v in response.result["communityChanges"].getElems()[0]["chatsModified"].pairs():
let fullChatId = communityId & chatId
@ -1168,6 +1178,15 @@ QtObject:
self.chatService.updateOrAddChat(chatDetails) # we have to update chats stored in the chat service.
chats.add(chatDetails)
# Update communities objects
let updatedCommunity = response.result["communities"][0].toCommunityDto
self.checkForCategoryPropertyUpdates(
updatedCommunity,
self.joinedCommunities[communityId]
)
self.joinedCommunities[communityId] = updatedCommunity
self.allCommunities[communityId] = updatedCommunity
for k, v in response.result["communityChanges"].getElems()[0]["categoriesModified"].pairs():
let category = v.toCategory()
self.events.emit(SIGNAL_COMMUNITY_CATEGORY_EDITED,
@ -1184,8 +1203,13 @@ QtObject:
let error = Json.decode($response.error, RpcError)
raise newException(RpcException, "Error deleting community category: " & error.message)
# Update communities objetcts
# Update communities objects
let updatedCommunity = response.result["communities"][0].toCommunityDto
self.checkForCategoryPropertyUpdates(
updatedCommunity,
self.joinedCommunities[communityId]
)
self.joinedCommunities[communityId] = updatedCommunity
self.allCommunities[communityId] = updatedCommunity

View File

@ -39,248 +39,240 @@ Item {
model: root.model
spacing: 0
interactive: height !== contentHeight
section.property: "categoryId"
section.criteria: ViewSection.FullString
section.delegate: Loader {
id: statusChatListCategoryItemLoader
active: !!section
delegate: Loader {
id: chatLoader
required property string section
sourceComponent: StatusChatListCategoryItem {
id: statusChatListCategoryItem
function setupPopup() {
categoryPopupMenuSlot.item.categoryId = statusChatListCategoryItemLoader.section
sourceComponent: {
if (model.isCategory) {
return categoryItemComponent
}
function toggleCategory() {
root.model.sourceModel.changeCategoryOpened(statusChatListCategoryItemLoader.section, !opened)
opened = root.model.sourceModel.getCategoryOpenedForCategoryId(statusChatListCategoryItemLoader.section)
}
Connections {
enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted
target: categoryPopupMenuSlot.item
function onClosed() {
statusChatListCategoryItem.highlighted = false
statusChatListCategoryItem.menuButton.highlighted = false
return channelItemComponent
}
Component {
id: categoryItemComponent
StatusChatListCategoryItem {
id: statusChatListCategoryItem
function setupPopup() {
categoryPopupMenuSlot.item.categoryItem = model
}
}
title: root.model.sourceModel.getCategoryNameForCategoryId(statusChatListCategoryItemLoader.section)
opened: root.model.sourceModel.getCategoryOpenedForCategoryId(statusChatListCategoryItemLoader.section)
sensor.pressAndHoldInterval: 150
propagateTitleClicks: true // title click is handled as a normal click (fallthru)
showAddButton: showCategoryActionButtons
showMenuButton: !!root.popupMenu
highlighted: false//statusChatListCategory.dragged // FIXME DND
hasUnreadMessages: root.model.sourceModel.getCategoryHasUnreadMessages(statusChatListCategoryItemLoader.section)
Connections {
target: root.model.sourceModel
function onCategoryHasUnreadMessagesChanged(categoryId: string, hasUnread: bool) {
if (categoryId === statusChatListCategoryItemLoader.section) {
statusChatListCategoryItem.hasUnreadMessages = hasUnread
Connections {
enabled: categoryPopupMenuSlot.active && statusChatListCategoryItem.highlighted
target: categoryPopupMenuSlot.item
function onClosed() {
statusChatListCategoryItem.highlighted = false
statusChatListCategoryItem.menuButton.highlighted = false
}
}
}
onClicked: {
if (sensor.enabled) {
title: model.name
opened: model.categoryOpened
sensor.pressAndHoldInterval: 150
propagateTitleClicks: true // title click is handled as a normal click (fallthru)
showAddButton: showCategoryActionButtons
showMenuButton: !!root.onPopupMenuChanged
highlighted: false//statusChatListCategory.dragged // FIXME DND
hasUnreadMessages: model.hasUnreadMessages
onClicked: {
if (!sensor.enabled) {
return
}
if (mouse.button === Qt.RightButton && showCategoryActionButtons && !!root.categoryPopupMenu) {
statusChatListCategoryItem.setupPopup()
highlighted = true;
categoryPopupMenuSlot.item.popup()
} else if (mouse.button === Qt.LeftButton) {
toggleCategory()
root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened)
}
}
onToggleButtonClicked: root.model.sourceModel.changeCategoryOpened(model.categoryId, !statusChatListCategoryItem.opened)
onMenuButtonClicked: {
statusChatListCategoryItem.setupPopup()
highlighted = true
menuButton.highlighted = true
let p = menuButton.mapToItem(statusChatListCategoryItem, menuButton.x, menuButton.y)
let menuWidth = categoryPopupMenuSlot.item.width
categoryPopupMenuSlot.item.popup()
}
onAddButtonClicked: {
root.categoryAddButtonClicked(categoryId)
}
}
onToggleButtonClicked: toggleCategory()
onMenuButtonClicked: {
statusChatListCategoryItem.setupPopup()
highlighted = true
menuButton.highlighted = true
let p = menuButton.mapToItem(statusChatListCategoryItem, menuButton.x, menuButton.y)
let menuWidth = categoryPopupMenuSlot.item.width
categoryPopupMenuSlot.item.popup()
}
onAddButtonClicked: {
root.categoryAddButtonClicked(categoryId)
}
}
}
Component {
id: channelItemComponent
QC.Control {
id: draggable
objectName: model.name
width: root.width
height: model.categoryOpened ? statusChatListItem.height : 0
visible: height
verticalPadding: 2
delegate: Loader {
id: chatLoader
active: model.type !== d.chatTypeCategory
height: active && item ? item.height : 0
visible: height
property alias chatListItem: statusChatListItem
sourceComponent: QC.Control {
id: draggable
objectName: model.name
width: root.width
height: model.categoryOpened ? statusChatListItem.height : 0
verticalPadding: 2
contentItem: MouseArea {
id: dragSensor
property alias chatListItem: statusChatListItem
anchors.fill: parent
cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor
hoverEnabled: true
enabled: root.draggableItems
contentItem: MouseArea {
id: dragSensor
property bool active: false
property real startY: 0
property real startX: 0
anchors.fill: parent
cursorShape: active ? Qt.ClosedHandCursor : Qt.PointingHandCursor
hoverEnabled: true
enabled: root.draggableItems
drag.target: draggedListItemLoader.item
drag.filterChildren: true
property bool active: false
property real startY: 0
property real startX: 0
drag.target: draggedListItemLoader.item
drag.filterChildren: true
onPressed: {
startY = mouseY
startX = mouseX
}
onPressAndHold: active = true
onReleased: {
if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) {
root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition)
onPressed: {
startY = mouseY
startX = mouseX
}
active = false
}
onMouseYChanged: {
if ((Math.abs(startY - mouseY) > 1) && pressed) {
active = true
onPressAndHold: active = true
onReleased: {
if (active && d.destinationPosition !== -1 && statusChatListItem.originalOrder !== d.destinationPosition) {
root.chatItemReordered(statusChatListItem.chatId, statusChatListItem.originalOrder, d.destinationPosition)
}
active = false
}
}
onMouseXChanged: {
if ((Math.abs(startX - mouseX) > 1) && pressed) {
active = true
onMouseYChanged: {
if ((Math.abs(startY - mouseY) > 1) && pressed) {
active = true
}
}
}
onActiveChanged: d.destinationPosition = -1
onMouseXChanged: {
if ((Math.abs(startX - mouseX) > 1) && pressed) {
active = true
}
}
onActiveChanged: d.destinationPosition = -1
StatusChatListItem {
id: statusChatListItem
StatusChatListItem {
id: statusChatListItem
width: parent.width
opacity: dragSensor.active ? 0.0 : 1.0
originalOrder: model.position
chatId: model.itemId
categoryId: model.categoryId
name: model.name
type: !!model.type ? model.type : StatusChatListItem.Type.CommunityChat
muted: model.muted
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
highlightWhenCreated: !!model.highlight
selected: (model.active && root.highlightItem)
asset.emoji: !!model.emoji ? model.emoji : ""
asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId]
asset.isImage: model.icon.includes("data")
asset.name: model.icon
ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash
onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive
width: parent.width
opacity: dragSensor.active ? 0.0 : 1.0
originalOrder: model.position
chatId: model.itemId
categoryId: model.categoryId
name: model.name
type: model.type ?? StatusChatListItem.Type.CommunityChat
muted: model.muted
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
highlightWhenCreated: !!model.highlight
selected: (model.active && root.highlightItem)
asset.emoji: !!model.emoji ? model.emoji : ""
asset.color: !!model.color ? model.color : Theme.palette.userCustomizationColors[model.colorId]
asset.isImage: model.icon.includes("data")
asset.name: model.icon
ringSettings.ringSpecModel: type === StatusChatListItem.Type.OneToOneChat && root.isEnsVerified(chatId) ? undefined : model.colorHash
onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive
sensor.cursorShape: dragSensor.cursorShape
sensor.cursorShape: dragSensor.cursorShape
onClicked: {
highlightWhenCreated = false
onClicked: {
highlightWhenCreated = false
if (mouse.button === Qt.RightButton && !!root.popupMenu) {
statusChatListItem.highlighted = true
if (mouse.button === Qt.RightButton && !!root.popupMenu) {
statusChatListItem.highlighted = true
let originalOpenHandler = popupMenuSlot.item.openHandler
let originalCloseHandler = popupMenuSlot.item.closeHandler
const originalOpenHandler = popupMenuSlot.item.openHandler
const originalCloseHandler = popupMenuSlot.item.closeHandler
popupMenuSlot.item.openHandler = function () {
if (!!originalOpenHandler) {
originalOpenHandler(statusChatListItem.chatId)
popupMenuSlot.item.openHandler = function () {
if (!!originalOpenHandler) {
originalOpenHandler(statusChatListItem.chatId)
}
}
popupMenuSlot.item.closeHandler = function () {
if (statusChatListItem) {
statusChatListItem.highlighted = false
}
if (!!originalCloseHandler) {
originalCloseHandler()
}
}
const p = statusChatListItem.mapToItem(root, mouse.x, mouse.y)
popupMenuSlot.item.popup(p.x + 4, p.y + 6)
popupMenuSlot.item.openHandler = originalOpenHandler
return
}
popupMenuSlot.item.closeHandler = function () {
if (statusChatListItem) {
statusChatListItem.highlighted = false
}
if (!!originalCloseHandler) {
originalCloseHandler()
}
if (!statusChatListItem.selected) {
root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId)
}
let p = statusChatListItem.mapToItem(root, mouse.x, mouse.y)
popupMenuSlot.item.popup(p.x + 4, p.y + 6)
popupMenuSlot.item.openHandler = originalOpenHandler
return
}
if (!statusChatListItem.selected) {
root.chatItemSelected(statusChatListItem.categoryId, statusChatListItem.chatId)
}
onUnmute: root.chatItemUnmuted(statusChatListItem.chatId)
}
onUnmute: root.chatItemUnmuted(statusChatListItem.chatId)
}
}
DropArea {
id: dropArea
width: dragSensor.active ? 0 : parent.width
height: dragSensor.active ? 0 : parent.height
keys: ["chat-item-category-" + statusChatListItem.categoryId]
DropArea {
id: dropArea
width: dragSensor.active ? 0 : parent.width
height: dragSensor.active ? 0 : parent.height
keys: ["chat-item-category-" + statusChatListItem.categoryId]
onEntered: reorderDelay.start()
onEntered: reorderDelay.start()
Timer {
id: reorderDelay
interval: 100
repeat: false
onTriggered: {
if (dropArea.containsDrag) {
d.destinationPosition = root.model.get(draggable.DelegateModel.itemsIndex).position
statusChatListItems.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex)
Timer {
id: reorderDelay
interval: 100
repeat: false
onTriggered: {
if (dropArea.containsDrag) {
d.destinationPosition = root.model.get(draggable.DelegateModel.itemsIndex).position
statusChatListItems.items.move(dropArea.drag.source.DelegateModel.itemsIndex, draggable.DelegateModel.itemsIndex)
}
}
}
}
}
Loader {
id: draggedListItemLoader
active: dragSensor.active
sourceComponent: StatusChatListItem {
property var globalPosition: Utils.getAbsolutePosition(draggable)
parent: QC.Overlay.overlay
sensor.cursorShape: dragSensor.cursorShape
Drag.active: dragSensor.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.keys: ["chat-item-category-" + categoryId]
Drag.source: draggable
Loader {
id: draggedListItemLoader
active: dragSensor.active
sourceComponent: StatusChatListItem {
property var globalPosition: Utils.getAbsolutePosition(draggable)
parent: QC.Overlay.overlay
sensor.cursorShape: dragSensor.cursorShape
Drag.active: dragSensor.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.keys: ["chat-item-category-" + categoryId]
Drag.source: draggable
Component.onCompleted: {
x = globalPosition.x
y = globalPosition.y
chatId: draggable.chatListItem.chatId
categoryId: draggable.chatListItem.categoryId
name: draggable.chatListItem.name
type: draggable.chatListItem.type
muted: draggable.chatListItem.muted
dragged: true
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
selected: draggable.chatListItem.selected
asset.color: draggable.chatListItem.asset.color
asset.imgIsIdenticon: draggable.chatListItem.asset.imgIsIdenticon
asset.name: draggable.chatListItem.asset.name
Component.onCompleted: {
x = globalPosition.x
y = globalPosition.y
}
}
chatId: draggable.chatListItem.chatId
categoryId: draggable.chatListItem.categoryId
name: draggable.chatListItem.name
type: draggable.chatListItem.type
muted: draggable.chatListItem.muted
dragged: true
hasUnreadMessages: model.hasUnreadMessages
notificationsCount: model.notificationsCount
selected: draggable.chatListItem.selected
asset.color: draggable.chatListItem.asset.color
asset.imgIsIdenticon: draggable.chatListItem.asset.imgIsIdenticon
asset.name: draggable.chatListItem.asset.name
}
}
}
@ -303,7 +295,6 @@ Item {
id: d
property int destinationPosition: -1
readonly property int chatTypeCategory: -1
}
Loader {

View File

@ -254,35 +254,16 @@ Item {
categoryPopupMenu: StatusMenu {
property var itemWithCategory
property string categoryId
openHandler: function () {
let jsonObj = root.communitySectionModule.getItemPartOfCategoryAsJsonById(categoryId)
try {
let obj = JSON.parse(jsonObj)
if (obj.error) {
console.error("error parsing chat item json object, id: ", categoryId, " error: ", obj.error)
close()
return
}
itemWithCategory = obj
} catch (e) {
console.error("error parsing chat item json object, id: ", categoryId, " error: ", e)
close()
return
}
}
property var categoryItem
StatusAction {
// TODO pass categoryMuted to Item
text: itemWithCategory.muted ? qsTr("Unmute category") : qsTr("Mute category")
text: categoryItem.muted ? qsTr("Unmute category") : qsTr("Mute category")
icon.name: "notification"
onTriggered: {
if (itemWithCategory.muted) {
root.communitySectionModule.unmuteCategory(itemWithCategory.categoryId)
if (categoryItem.muted) {
root.communitySectionModule.unmuteCategory(categoryItem.itemId)
} else {
root.communitySectionModule.muteCategory(itemWithCategory.categoryId)
root.communitySectionModule.muteCategory(categoryItem.itemId)
}
}
}
@ -296,8 +277,8 @@ Item {
Global.openPopup(createCategoryPopup, {
isEdit: true,
channels: [],
categoryId: itemWithCategory.categoryId,
categoryName: itemWithCategory.categoryName
categoryId: categoryItem.itemId,
categoryName: categoryItem.name
})
}
}
@ -314,10 +295,10 @@ Item {
type: StatusAction.Type.Danger
onTriggered: {
Global.openPopup(deleteCategoryConfirmationDialogComponent, {
title: qsTr("Delete %1 category").arg(itemWithCategory.categoryName),
title: qsTr("Delete %1 category").arg(categoryItem.name),
confirmationText: qsTr("Are you sure you want to delete %1 category? Channels inside the category won't be deleted.")
.arg(itemWithCategory.categoryName),
categoryId: itemWithCategory.categoryId
.arg(categoryItem.name),
categoryId: categoryItem.itemId
})
}
}
@ -327,6 +308,7 @@ Item {
id: chatContextMenuView
emojiPopup: root.emojiPopup
// TODO pass the chatModel in its entirety instead of fetching the JSOn using just the id
openHandler: function (id) {
try {
let jsonObj = root.communitySectionModule.getItemAsJson(id)