mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-12 23:35:32 +00:00
feat: create community categories
This commit is contained in:
parent
a79400e023
commit
0ded790f0d
70
src/app/chat/views/category_list.nim
Normal file
70
src/app/chat/views/category_list.nim
Normal file
@ -0,0 +1,70 @@
|
||||
import NimQml, Tables
|
||||
import ../../../status/chat/[chat, message]
|
||||
import ../../../status/status
|
||||
import ../../../status/ens
|
||||
import ../../../status/accounts
|
||||
import strutils
|
||||
|
||||
type
|
||||
CategoryRoles {.pure.} = enum
|
||||
Id = UserRole + 1
|
||||
Name = UserRole + 2
|
||||
Position = UserRole + 3
|
||||
|
||||
QtObject:
|
||||
type
|
||||
CategoryList* = ref object of QAbstractListModel
|
||||
categories*: seq[CommunityCategory]
|
||||
status: Status
|
||||
|
||||
proc setup(self: CategoryList) = self.QAbstractListModel.setup
|
||||
|
||||
proc delete(self: CategoryList) =
|
||||
self.categories = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc newCategoryList*(status: Status): CategoryList =
|
||||
new(result, delete)
|
||||
result.categories = @[]
|
||||
result.status = status
|
||||
result.setup()
|
||||
|
||||
method rowCount*(self: CategoryList, index: QModelIndex = nil): int = self.categories.len
|
||||
|
||||
method data(self: CategoryList, index: QModelIndex, role: int): QVariant =
|
||||
if not index.isValid:
|
||||
return
|
||||
if index.row < 0 or index.row >= self.categories.len:
|
||||
return
|
||||
let catItem = self.categories[index.row]
|
||||
let catItemRole = role.CategoryRoles
|
||||
case catItemRole:
|
||||
of CategoryRoles.Id: result = newQVariant(catItem.id)
|
||||
of CategoryRoles.Name: result = newQVariant(catItem.name)
|
||||
of CategoryRoles.Position: result = newQVariant(catItem.position)
|
||||
|
||||
method roleNames(self: CategoryList): Table[int, string] =
|
||||
{
|
||||
CategoryRoles.Name.int:"name",
|
||||
CategoryRoles.Position.int:"position",
|
||||
CategoryRoles.Id.int: "categoryId"
|
||||
}.toTable
|
||||
|
||||
proc setCategories*(self: CategoryList, categories: seq[CommunityCategory]) =
|
||||
self.beginResetModel()
|
||||
self.categories = categories
|
||||
self.endResetModel()
|
||||
|
||||
proc addCategoryToList*(self: CategoryList, category: CommunityCategory): int =
|
||||
self.beginInsertRows(newQModelIndex(), 0, 0)
|
||||
self.categories.insert(category, 0)
|
||||
self.endInsertRows()
|
||||
result = self.categories.len
|
||||
|
||||
proc removeCategoryFromList*(self: CategoryList, categoryId: string): int =
|
||||
let idx = self.categories.findIndexById(categoryId)
|
||||
if idx == -1: return
|
||||
self.beginRemoveRows(newQModelIndex(), idx, idx)
|
||||
self.categories.delete(idx)
|
||||
self.endRemoveRows()
|
||||
result = self.categories.len
|
@ -19,6 +19,7 @@ type
|
||||
Muted = UserRole + 10
|
||||
Id = UserRole + 11
|
||||
Description = UserRole + 12
|
||||
CategoryId = UserRole + 13
|
||||
|
||||
QtObject:
|
||||
type
|
||||
@ -76,6 +77,7 @@ QtObject:
|
||||
of ChannelsRoles.HasMentions: result = newQVariant(chatItem.hasMentions)
|
||||
of ChannelsRoles.Muted: result = newQVariant(chatItem.muted.bool)
|
||||
of ChannelsRoles.Id: result = newQVariant($chatItem.id)
|
||||
of ChannelsRoles.CategoryId: result = newQVariant(chatItem.categoryId)
|
||||
of ChannelsRoles.Description: result = newQVariant(chatItem.description)
|
||||
|
||||
method roleNames(self: ChannelsList): Table[int, string] =
|
||||
@ -91,7 +93,8 @@ QtObject:
|
||||
ChannelsRoles.ContentType.int: "contentType",
|
||||
ChannelsRoles.Muted.int: "muted",
|
||||
ChannelsRoles.Id.int: "id",
|
||||
ChannelsRoles.Description.int: "description"
|
||||
ChannelsRoles.Description.int: "description",
|
||||
ChannelsRoles.CategoryId.int: "categoryId"
|
||||
}.toTable
|
||||
|
||||
proc setChats*(self: ChannelsList, chats: seq[Chat]) =
|
||||
|
@ -54,8 +54,9 @@ QtObject:
|
||||
var found = false
|
||||
for chat in community.chats:
|
||||
if (chat.id == newChat.id):
|
||||
# canPost is not available in the newChat so we need to check what we had before
|
||||
# canPost and categoryId are not available in the newChat so we need to check what we had before
|
||||
newChat.canPost = community.chats[i].canPost
|
||||
newChat.categoryId = community.chats[i].categoryId
|
||||
community.chats[i] = newChat
|
||||
found = true
|
||||
i = i + 1
|
||||
@ -225,7 +226,7 @@ QtObject:
|
||||
error "Error creating the community", msg = e.msg
|
||||
result = fmt"Error creating the community: {e.msg}"
|
||||
|
||||
proc createCommunityChannel*(self: CommunitiesView, communityId: string, name: string, description: string): string {.slot.} =
|
||||
proc createCommunityChannel*(self: CommunitiesView, communityId: string, name: string, description: string, categoryId: string): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
let chat = self.status.chat.createCommunityChannel(communityId, name, description)
|
||||
@ -233,12 +234,38 @@ QtObject:
|
||||
if (chat.id == ""):
|
||||
return "Chat was not created. Please try again later"
|
||||
|
||||
if categoryId != "":
|
||||
self.status.chat.reorderCommunityChannel(communityId, categoryId, chat.id.replace(communityId, ""), 0)
|
||||
|
||||
chat.categoryId = categoryId
|
||||
self.joinedCommunityList.addChannelToCommunity(communityId, chat)
|
||||
self.activeCommunity.addChatItemToList(chat)
|
||||
except Exception as e:
|
||||
error "Error creating the channel", msg = e.msg
|
||||
result = fmt"Error creating the channel: {e.msg}"
|
||||
|
||||
proc createCommunityCategory*(self: CommunitiesView, communityId: string, name: string, channels: string): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
let channelSeq = map(parseJson(channels).getElems(), proc(x:JsonNode):string = x.getStr().replace(communityId, ""))
|
||||
let category = self.status.chat.createCommunityCategory(communityId, name, channelSeq)
|
||||
self.joinedCommunityList.addCategoryToCommunity(communityId, category)
|
||||
self.activeCommunity.addCategoryToList(category)
|
||||
except Exception as e:
|
||||
error "Error creating the category", msg = e.msg
|
||||
result = fmt"Error creating the category: {e.msg}"
|
||||
|
||||
proc deleteCommunityCategory*(self: CommunitiesView, communityId: string, categoryId: string): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
self.status.chat.deleteCommunityCategory(communityId, categoryId)
|
||||
self.joinedCommunityList.removeCategoryFromCommunity(communityId, categoryId)
|
||||
self.activeCommunity.removeCategoryFromList(categoryId)
|
||||
except Exception as e:
|
||||
error "Error creating the category", msg = e.msg
|
||||
result = fmt"Error creating the category: {e.msg}"
|
||||
|
||||
|
||||
proc observedCommunityChanged*(self: CommunitiesView) {.signal.}
|
||||
|
||||
proc setObservedCommunity*(self: CommunitiesView, communityId: string) {.slot.} =
|
||||
|
@ -3,6 +3,7 @@ import ../../../status/[chat/chat, status]
|
||||
import channels_list
|
||||
import ../../../eventemitter
|
||||
import community_members_list
|
||||
import category_list
|
||||
import community_membership_request_list
|
||||
|
||||
QtObject:
|
||||
@ -10,6 +11,7 @@ QtObject:
|
||||
communityItem*: Community
|
||||
communityMembershipRequestList*: CommunityMembershipRequestList
|
||||
chats*: ChannelsList
|
||||
categories*: CategoryList
|
||||
members*: CommunityMembersView
|
||||
status*: Status
|
||||
active*: bool
|
||||
@ -19,6 +21,7 @@ QtObject:
|
||||
|
||||
proc delete*(self: CommunityItemView) =
|
||||
if not self.chats.isNil: self.chats.delete
|
||||
if not self.categories.isNil: self.categories.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newCommunityItemView*(status: Status): CommunityItemView =
|
||||
@ -27,6 +30,7 @@ QtObject:
|
||||
result.status = status
|
||||
result.active = false
|
||||
result.chats = newChannelsList(status)
|
||||
result.categories = newCategoryList(status)
|
||||
result.communityMembershipRequestList = newCommunityMembershipRequestList()
|
||||
result.members = newCommunityMembersView(status)
|
||||
result.setup
|
||||
@ -36,6 +40,7 @@ QtObject:
|
||||
proc setCommunityItem*(self: CommunityItemView, communityItem: Community) =
|
||||
self.communityItem = communityItem
|
||||
self.chats.setChats(communityItem.chats)
|
||||
self.categories.setCategories(communityItem.categories)
|
||||
self.members.setMembers(communityItem.members)
|
||||
self.nbMembersChanged()
|
||||
self.communityMembershipRequestList.setNewData(communityItem.membershipRequests)
|
||||
@ -135,6 +140,9 @@ QtObject:
|
||||
proc getChats*(self: CommunityItemView): QVariant {.slot.} =
|
||||
result = newQVariant(self.chats)
|
||||
|
||||
proc getCategories*(self: CommunityItemView): QVariant {.slot.} =
|
||||
result = newQVariant(self.categories)
|
||||
|
||||
proc changeChats*(self: CommunityItemView, chats: seq[Chat]) =
|
||||
self.communityItem.chats = chats
|
||||
self.chats.setChats(chats)
|
||||
@ -145,10 +153,26 @@ QtObject:
|
||||
discard self.chats.addChatItemToList(chat)
|
||||
self.chatsChanged()
|
||||
|
||||
proc addCategoryToList*(self: CommunityItemView, category: CommunityCategory) =
|
||||
self.communityItem.categories.add(category)
|
||||
discard self.categories.addCategoryToList(category)
|
||||
self.chatsChanged()
|
||||
|
||||
proc removeCategoryFromList*(self: CommunityItemView, categoryId: string) =
|
||||
discard self.categories.removeCategoryFromList(categoryId)
|
||||
let idx = self.communityItem.categories.findIndexById(categoryId)
|
||||
self.chatsChanged()
|
||||
if idx == -1: return
|
||||
self.communityItem.categories.delete(idx)
|
||||
|
||||
QtProperty[QVariant] chats:
|
||||
read = getChats
|
||||
notify = chatsChanged
|
||||
|
||||
QtProperty[QVariant] categories:
|
||||
read = getCategories
|
||||
notify = chatsChanged
|
||||
|
||||
proc getMembers*(self: CommunityItemView): QVariant {.slot.} =
|
||||
result = newQVariant(self.members)
|
||||
|
||||
|
@ -129,6 +129,13 @@ QtObject:
|
||||
let index = self.communities.findIndexById(communityId)
|
||||
self.communities[index] = community
|
||||
|
||||
proc addCategoryToCommunity*(self: CommunityList, communityId: string, category: CommunityCategory) =
|
||||
var community = self.getCommunityById(communityId)
|
||||
community.categories.add(category)
|
||||
|
||||
let index = self.communities.findIndexById(communityId)
|
||||
self.communities[index] = community
|
||||
|
||||
proc replaceCommunity*(self: CommunityList, community: Community) =
|
||||
let index = self.communities.findIndexById(community.id)
|
||||
if (index == -1):
|
||||
@ -136,4 +143,12 @@ QtObject:
|
||||
let topLeft = self.createIndex(index, index, nil)
|
||||
let bottomRight = self.createIndex(index, index, nil)
|
||||
self.communities[index] = community
|
||||
self.dataChanged(topLeft, bottomRight, @[CommunityRoles.Name.int, CommunityRoles.Description.int, CommunityRoles.UnviewedMessagesCount.int])
|
||||
self.dataChanged(topLeft, bottomRight, @[CommunityRoles.Name.int, CommunityRoles.Description.int, CommunityRoles.UnviewedMessagesCount.int])
|
||||
|
||||
proc removeCategoryFromCommunity*(self: CommunityList, communityId: string, categoryId:string) =
|
||||
var community = self.getCommunityById(communityId)
|
||||
let idx = community.categories.findIndexById(categoryId)
|
||||
if idx == -1: return
|
||||
community.categories.delete(idx)
|
||||
let index = self.communities.findIndexById(communityId)
|
||||
self.communities[index] = community
|
@ -447,6 +447,15 @@ proc createCommunity*(self: ChatModel, name: string, description: string, access
|
||||
proc createCommunityChannel*(self: ChatModel, communityId: string, name: string, description: string): Chat =
|
||||
result = status_chat.createCommunityChannel(communityId, name, description)
|
||||
|
||||
proc createCommunityCategory*(self: ChatModel, communityId: string, name: string, channels: seq[string]): CommunityCategory =
|
||||
result = status_chat.createCommunityCategory(communityId, name, channels)
|
||||
|
||||
proc deleteCommunityCategory*(self: ChatModel, communityId: string, categoryId: string) =
|
||||
status_chat.deleteCommunityCategory(communityId, categoryId)
|
||||
|
||||
proc reorderCommunityChannel*(self: ChatModel, communityId: string, categoryId: string, chatId: string, position: int) =
|
||||
status_chat.reorderCommunityChat(communityId, categoryId, chatId, position)
|
||||
|
||||
proc joinCommunity*(self: ChatModel, communityId: string) =
|
||||
status_chat.joinCommunity(communityId)
|
||||
|
||||
|
@ -60,6 +60,7 @@ proc toJsonNode*(self: seq[ChatMembershipEvent]): seq[JsonNode] =
|
||||
type Chat* = ref object
|
||||
id*: string # ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one is the hex encoded public key and for group chats is a random uuid appended with the hex encoded pk of the creator of the chat
|
||||
communityId*: string
|
||||
categoryId*: string
|
||||
name*: string
|
||||
description*: string
|
||||
color*: string
|
||||
@ -93,12 +94,18 @@ type CommunityMembershipRequest* = object
|
||||
state*: int
|
||||
our*: string
|
||||
|
||||
type CommunityCategory* = object
|
||||
id*: string
|
||||
name*: string
|
||||
position*: int
|
||||
|
||||
type Community* = object
|
||||
id*: string
|
||||
name*: string
|
||||
lastChannelSeen*: string
|
||||
description*: string
|
||||
chats*: seq[Chat]
|
||||
categories*: seq[CommunityCategory]
|
||||
members*: seq[string]
|
||||
access*: int
|
||||
unviewedMessagesCount*: int
|
||||
@ -164,6 +171,15 @@ proc findIndexById*(self: seq[CommunityMembershipRequest], id: string): int =
|
||||
result = idx
|
||||
break
|
||||
|
||||
proc findIndexById*(self: seq[CommunityCategory], id: string): int =
|
||||
result = -1
|
||||
var idx = -1
|
||||
for item in self:
|
||||
inc idx
|
||||
if(item.id == id):
|
||||
result = idx
|
||||
break
|
||||
|
||||
proc isMember*(self: Chat, pubKey: string): bool =
|
||||
for member in self.members:
|
||||
if member.id == pubKey and member.joined: return true
|
||||
|
@ -332,6 +332,42 @@ proc createCommunityChannel*(communityId: string, name: string, description: str
|
||||
if rpcResult{"result"}.kind != JNull:
|
||||
result = rpcResult["result"]["chats"][0].toChat()
|
||||
|
||||
proc createCommunityCategory*(communityId: string, name: string, channels: seq[string]): CommunityCategory =
|
||||
let rpcResult = callPrivateRPC("createCommunityCategory".prefix, %*[
|
||||
{
|
||||
"communityId": communityId,
|
||||
"categoryName": name,
|
||||
"chatIds": channels
|
||||
}]).parseJSON()
|
||||
|
||||
if rpcResult.contains("error"):
|
||||
raise newException(StatusGoException, rpcResult["error"]["message"].getStr())
|
||||
else:
|
||||
for k, v in rpcResult["result"]["communityChanges"].getElems()[0]["categoriesAdded"].pairs():
|
||||
result.id = v["category_id"].getStr()
|
||||
result.name = v["name"].getStr()
|
||||
result.position = v{"position"}.getInt()
|
||||
|
||||
proc reorderCommunityChat*(communityId: string, categoryId: string, chatId: string, position: int) =
|
||||
let rpcResult = callPrivateRPC("reorderCommunityChat".prefix, %*[
|
||||
{
|
||||
"communityId": communityId,
|
||||
"categoryId": categoryId,
|
||||
"chatId": chatId,
|
||||
"position": position
|
||||
}]).parseJSON()
|
||||
if rpcResult.contains("error"):
|
||||
raise newException(StatusGoException, rpcResult["error"]["message"].getStr())
|
||||
|
||||
proc deleteCommunityCategory*(communityId: string, categoryId: string) =
|
||||
let rpcResult = callPrivateRPC("deleteCommunityCategory".prefix, %*[
|
||||
{
|
||||
"communityId": communityId,
|
||||
"categoryId": categoryId
|
||||
}]).parseJSON()
|
||||
if rpcResult.contains("error"):
|
||||
raise newException(StatusGoException, rpcResult["error"]["message"].getStr())
|
||||
|
||||
proc requestCommunityInfo*(communityId: string) =
|
||||
discard callPrivateRPC("requestCommunityInfoFromMailserver".prefix, %*[communityId])
|
||||
|
||||
|
@ -201,6 +201,7 @@ proc toCommunity*(jsonCommunity: JsonNode): Community =
|
||||
for chatId, chat in jsonCommunity{"chats"}:
|
||||
result.chats.add(Chat(
|
||||
id: result.id & chatId,
|
||||
categoryId: chat{"categoryID"}.getStr(),
|
||||
communityId: result.id,
|
||||
name: chat{"name"}.getStr,
|
||||
canPost: chat{"canPost"}.getBool,
|
||||
@ -209,6 +210,14 @@ proc toCommunity*(jsonCommunity: JsonNode): Community =
|
||||
#chat{"permissions"}{"access"}.getInt,
|
||||
))
|
||||
|
||||
if jsonCommunity.hasKey("categories") and jsonCommunity["categories"].kind != JNull:
|
||||
for catId, cat in jsonCommunity{"categories"}:
|
||||
result.categories.add(CommunityCategory(
|
||||
id: catId,
|
||||
name: cat{"name"}.getStr,
|
||||
position: cat{"position"}.getInt
|
||||
))
|
||||
|
||||
if jsonCommunity.hasKey("members") and jsonCommunity["members"].kind != JNull:
|
||||
# memberInfo is empty for now
|
||||
for memberPubKey, memeberInfo in jsonCommunity{"members"}:
|
||||
|
@ -26,6 +26,15 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: createCategoryPopup
|
||||
CreateCategoryPopup {
|
||||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: transferOwnershipPopup
|
||||
TransferOwnershipPopup {}
|
||||
@ -82,6 +91,17 @@ Rectangle {
|
||||
onTriggered: openPopup(createChannelPopup, {communityId: chatsModel.communities.activeCommunity.id})
|
||||
}
|
||||
|
||||
Action {
|
||||
enabled: chatsModel.communities.activeCommunity.admin
|
||||
text: qsTr("Create category")
|
||||
icon.source: "../../img/create-category.svg"
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
onTriggered: openPopup(createCategoryPopup, {communityId: chatsModel.communities.activeCommunity.id})
|
||||
}
|
||||
|
||||
Separator {}
|
||||
|
||||
Action {
|
||||
text: qsTr("Invite People")
|
||||
enabled: chatsModel.communities.activeCommunity.canManageUsers
|
||||
@ -152,21 +172,28 @@ Rectangle {
|
||||
leftPadding: Style.current.halfPadding
|
||||
rightPadding: Style.current.halfPadding
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
contentHeight: channelList.height + emptyViewAndSuggestionsLoader.height + backUpBannerLoader.height + 2 * Style.current.padding
|
||||
contentHeight: categoryList.height + channelList.height + emptyViewAndSuggestionsLoader.height + backUpBannerLoader.height + 2 * Style.current.padding
|
||||
clip: true
|
||||
|
||||
ChannelList {
|
||||
id: channelList
|
||||
searchStr: ""
|
||||
categoryId: ""
|
||||
channelModel: chatsModel.communities.activeCommunity.chats
|
||||
}
|
||||
|
||||
CategoryList {
|
||||
id: categoryList
|
||||
anchors.top: channelList.bottom
|
||||
categoryModel: chatsModel.communities.activeCommunity.categories
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: emptyViewAndSuggestionsLoader
|
||||
active: chatsModel.communities.activeCommunity.admin && !appSettings.hiddenCommunityWelcomeBanners.includes(chatsModel.communities.activeCommunity.id)
|
||||
width: parent.width
|
||||
height: active ? item.height : 0
|
||||
anchors.top: channelList.bottom
|
||||
anchors.top: categoryList.bottom
|
||||
anchors.topMargin: active ? Style.current.padding : 0
|
||||
sourceComponent: Component {
|
||||
CommunityWelcomeBanner {}
|
||||
|
193
ui/app/AppLayouts/Chat/CommunityComponents/CategoryList.qml
Normal file
193
ui/app/AppLayouts/Chat/CommunityComponents/CategoryList.qml
Normal file
@ -0,0 +1,193 @@
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../../../../imports"
|
||||
import "../components"
|
||||
import "./"
|
||||
import "../ContactsColumn"
|
||||
import "../CommunityComponents"
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
Column {
|
||||
id: categoryListContent
|
||||
property var categoryModel
|
||||
property string categoryToDelete: ""
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: categoryListContent.categoryModel
|
||||
delegate: Item {
|
||||
id: wrapper
|
||||
property bool showCategory: true
|
||||
property color color: {
|
||||
if (hhandler.hovered) {
|
||||
return Style.current.menuBackgroundHover
|
||||
}
|
||||
return Style.current.transparent
|
||||
}
|
||||
height: showCategory ? categoryHeader.height + channelList.height : categoryHeader.height
|
||||
width: categoryListContent.width
|
||||
Rectangle {
|
||||
id: categoryHeader
|
||||
color: wrapper.color
|
||||
radius: 8
|
||||
height: 40
|
||||
width: categoryListContent.width
|
||||
|
||||
StyledText {
|
||||
text: model.name
|
||||
elide: Text.ElideRight
|
||||
color: Style.current.textColor
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
visible: hhandler.hovered && chatsModel.communities.activeCommunity.admin
|
||||
id: addBtn
|
||||
icon.name: "add-category"
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.right: moreBtn.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconColor: Style.current.textColor
|
||||
highlightedIconColor: Style.current.textColor
|
||||
hoveredIconColor: Style.current.textColor
|
||||
highlightedBackgroundColor: Style.current.menuBackgroundHover
|
||||
onClicked: {
|
||||
openPopup(createChannelPopup, {communityId: chatsModel.communities.activeCommunity.id, categoryId: model.categoryId})
|
||||
}
|
||||
StatusToolTip {
|
||||
visible: addBtn.hovered
|
||||
text: qsTr("Add channel inside category")
|
||||
}
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
visible: hhandler.hovered
|
||||
id: moreBtn
|
||||
icon.name: "more"
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.right: showBtn.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconColor: Style.current.textColor
|
||||
highlightedIconColor: Style.current.textColor
|
||||
hoveredIconColor: Style.current.textColor
|
||||
highlightedBackgroundColor: Style.current.menuBackgroundHover
|
||||
onClicked: contextMenu.popup()
|
||||
|
||||
StatusToolTip {
|
||||
visible: moreBtn.hovered
|
||||
text: qsTr("More")
|
||||
}
|
||||
|
||||
PopupMenu {
|
||||
id: contextMenu
|
||||
|
||||
Action {
|
||||
enabled: chatsModel.communities.activeCommunity.admin
|
||||
text: qsTr("Edit category")
|
||||
icon.source: "../../../img/edit.svg"
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
}
|
||||
|
||||
Separator {
|
||||
visible: chatsModel.communities.activeCommunity.admin
|
||||
}
|
||||
|
||||
Action {
|
||||
text: qsTr("Delete category")
|
||||
enabled: chatsModel.communities.activeCommunity.admin
|
||||
icon.source: "../../../img/delete.svg"
|
||||
icon.color: Style.current.red
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
onTriggered: {
|
||||
categoryToDelete = model.categoryId
|
||||
openPopup(deleteCategoryConfirmationDialogComponent, {
|
||||
title: qsTr("Delete %1 category").arg(model.name),
|
||||
confirmationText: qsTr("Are you sure you want to delete %1 category? Channels inside the category won’t be deleted.").arg(model.name)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
visible: hhandler.hovered
|
||||
id: showBtn
|
||||
icon.name: showCategory ? "hide-category" : "show-category"
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconColor: Style.current.textColor
|
||||
highlightedIconColor: Style.current.textColor
|
||||
hoveredIconColor: Style.current.textColor
|
||||
highlightedBackgroundColor: Style.current.menuBackgroundHover
|
||||
onClicked: {
|
||||
showCategory = !showCategory
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hhandler
|
||||
}
|
||||
}
|
||||
|
||||
ChannelList {
|
||||
id: channelList
|
||||
searchStr: ""
|
||||
categoryId: model.categoryId
|
||||
visible: showCategory
|
||||
height: showCategory ? channelList.childrenRect.height : 0
|
||||
anchors.top: categoryHeader.bottom
|
||||
width: categoryListContent.width
|
||||
channelModel: chatsModel.communities.activeCommunity.chats
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: deleteError
|
||||
title: qsTr("Error deleting the category")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: deleteCategoryConfirmationDialogComponent
|
||||
ConfirmationDialog {
|
||||
btnType: "warn"
|
||||
height: 216
|
||||
showCancelButton: true
|
||||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
onCancelButtonClicked: {
|
||||
close();
|
||||
}
|
||||
onConfirmButtonClicked: function(){
|
||||
const error = chatsModel.communities.deleteCommunityCategory(chatsModel.communities.activeCommunity.id, categoryToDelete)
|
||||
if (error) {
|
||||
creatingError.text = error
|
||||
return creatingError.open()
|
||||
}
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
Rectangle {
|
||||
property string name: "channel-name"
|
||||
property string channelId: "channel-id"
|
||||
property string categoryId: ""
|
||||
property var onItemChecked
|
||||
property bool isHovered: false
|
||||
id: container
|
||||
visible: categoryId == ""
|
||||
height: visible ? 52 : 0
|
||||
width: 425
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
border.width: 0
|
||||
radius: Style.current.radius
|
||||
color: isHovered ? Style.current.backgroundHover : Style.current.transparent
|
||||
|
||||
StatusIdenticon {
|
||||
id: channelImage
|
||||
height: 36
|
||||
width: 36
|
||||
chatId: name
|
||||
chatName: name
|
||||
chatType: Constants.chatTypePublic
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: channelName
|
||||
text: "#" + name
|
||||
elide: Text.ElideRight
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
font.pixelSize: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: channelImage.right
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: container.isHovered = true
|
||||
onExited: container.isHovered = false
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
chk.checked = !chk.checked
|
||||
}
|
||||
}
|
||||
|
||||
StatusCheckBox {
|
||||
id: chk
|
||||
anchors.top: channelImage.top
|
||||
anchors.topMargin: 6
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
onClicked: {
|
||||
onItemChecked(container.channelId, chk.checked)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.3
|
||||
import QtGraphicalEffects 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
ModalPopup {
|
||||
property string communityId
|
||||
readonly property int maxDescChars: 140
|
||||
property string nameValidationError: ""
|
||||
property bool isValid: nameInput.isValid
|
||||
|
||||
property var channels: []
|
||||
|
||||
id: popup
|
||||
height: 453
|
||||
|
||||
onOpened: {
|
||||
nameInput.text = "";
|
||||
nameInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
onClosed: destroy()
|
||||
|
||||
function validate() {
|
||||
nameInput.validate()
|
||||
return isValid
|
||||
}
|
||||
|
||||
title: qsTr("New category")
|
||||
|
||||
ScrollView {
|
||||
property ScrollBar vScrollBar: ScrollBar.vertical
|
||||
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
rightPadding: Style.current.padding
|
||||
anchors.rightMargin: - Style.current.halfPadding
|
||||
contentHeight: content.height
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
clip: true
|
||||
|
||||
function scrollBackUp() {
|
||||
vScrollBar.setPosition(0)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: content
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
|
||||
Input {
|
||||
id: nameInput
|
||||
placeholderText: qsTr("Category title")
|
||||
validationError: popup.nameValidationError
|
||||
|
||||
property bool isValid: false
|
||||
|
||||
onTextEdited: {
|
||||
validate()
|
||||
}
|
||||
|
||||
function validate() {
|
||||
validationError = ""
|
||||
if (nameInput.text === "") {
|
||||
//% "You need to enter a name"
|
||||
validationError = qsTrId("you-need-to-enter-a-name")
|
||||
} else if (nameInput.text.length > 100) {
|
||||
//% "Your name needs to be 100 characters or shorter"
|
||||
validationError = qsTrId("your-name-needs-to-be-100-characters-or-shorter")
|
||||
}
|
||||
isValid = validationError === ""
|
||||
return validationError
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: sep
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: nameInput.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.leftMargin: -Style.current.padding
|
||||
anchors.rightMargin: -Style.current.padding
|
||||
}
|
||||
|
||||
StatusSectionHeadline {
|
||||
id: chatsTitle
|
||||
text: qsTr("Chats")
|
||||
anchors.top: sep.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
ListView {
|
||||
height: childrenRect.height
|
||||
model: chatsModel.communities.activeCommunity.chats
|
||||
anchors.top: chatsTitle.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
delegate: CommunityChannel {
|
||||
name: model.name
|
||||
channelId: model.id
|
||||
categoryId: model.categoryId
|
||||
onItemChecked: function(channelId, itemChecked){
|
||||
var idx = channels.indexOf(channelId)
|
||||
if(itemChecked){
|
||||
if(idx === -1){
|
||||
channels.push(channelId)
|
||||
}
|
||||
} else {
|
||||
if(idx > -1){
|
||||
channels.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusButton {
|
||||
enabled: popup.isValid
|
||||
//% "Create"
|
||||
text: qsTrId("create")
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
if (!validate()) {
|
||||
scrollView.scrollBackUp()
|
||||
return
|
||||
}
|
||||
|
||||
const error = chatsModel.communities.createCommunityCategory(communityId, Utils.filterXSS(nameInput.text), JSON.stringify(channels))
|
||||
|
||||
if (error) {
|
||||
creatingError.text = error
|
||||
return creatingError.open()
|
||||
}
|
||||
|
||||
popup.close()
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: creatingError
|
||||
title: qsTr("Error creating the category")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import "../../../../shared/status"
|
||||
|
||||
ModalPopup {
|
||||
property string communityId
|
||||
property string categoryId: ""
|
||||
readonly property int maxDescChars: 140
|
||||
property string nameValidationError: ""
|
||||
property bool isValid:
|
||||
@ -177,7 +178,8 @@ ModalPopup {
|
||||
}
|
||||
const error = chatsModel.communities.createCommunityChannel(communityId,
|
||||
Utils.filterXSS(nameInput.text),
|
||||
Utils.filterXSS(descriptionTextArea.text))
|
||||
Utils.filterXSS(descriptionTextArea.text),
|
||||
categoryId)
|
||||
|
||||
if (error) {
|
||||
creatingError.text = error
|
||||
|
@ -7,6 +7,7 @@ import "../components"
|
||||
|
||||
Item {
|
||||
property string chatId: ""
|
||||
property string categoryId: ""
|
||||
property string name: "channelName"
|
||||
property string lastMessage: "My latest message\n with a return"
|
||||
property string timestamp: "1605212622434"
|
||||
@ -22,6 +23,7 @@ Item {
|
||||
return chatType
|
||||
}
|
||||
|
||||
property string filterCategory: ""
|
||||
property string searchStr: ""
|
||||
property bool isCompact: appSettings.useCompactMode
|
||||
property int contentType: 1
|
||||
@ -41,7 +43,7 @@ Item {
|
||||
property string profileImage: realChatType === Constants.chatTypeOneToOne ? appMain.getProfileImage(chatId) || "" : ""
|
||||
|
||||
// Hide the box if it is filtered out
|
||||
property bool isVisible: searchStr === "" || name.includes(searchStr)
|
||||
property bool isVisible: categoryId == filterCategory && (searchStr === "" || name.includes(searchStr))
|
||||
|
||||
id: wrapper
|
||||
anchors.right: parent.right
|
||||
|
@ -10,6 +10,7 @@ Item {
|
||||
property var channelModel
|
||||
property alias channelListCount: chatGroupsListView.count
|
||||
property string searchStr: ""
|
||||
property string categoryId: ""
|
||||
id: channelListContent
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
@ -39,6 +40,8 @@ Item {
|
||||
hasMentions: model.hasMentions
|
||||
contentType: model.contentType
|
||||
searchStr: channelListContent.searchStr
|
||||
categoryId: model.categoryId
|
||||
filterCategory: channelListContent.categoryId
|
||||
chatId: model.id
|
||||
}
|
||||
onCountChanged: {
|
||||
|
3
ui/app/img/add-category.svg
Normal file
3
ui/app/img/add-category.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9375 5C10.9375 4.48223 10.5178 4.0625 10 4.0625C9.48223 4.0625 9.0625 4.48223 9.0625 5V8.5625C9.0625 8.83864 8.83864 9.0625 8.5625 9.0625H5C4.48223 9.0625 4.0625 9.48223 4.0625 10C4.0625 10.5178 4.48223 10.9375 5 10.9375H8.5625C8.83864 10.9375 9.0625 11.1614 9.0625 11.4375V15C9.0625 15.5178 9.48223 15.9375 10 15.9375C10.5178 15.9375 10.9375 15.5178 10.9375 15V11.4375C10.9375 11.1614 11.1614 10.9375 11.4375 10.9375H15C15.5178 10.9375 15.9375 10.5178 15.9375 10C15.9375 9.48223 15.5178 9.0625 15 9.0625H11.4375C11.1614 9.0625 10.9375 8.83864 10.9375 8.5625V5Z" fill="black" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 753 B |
4
ui/app/img/create-category.svg
Normal file
4
ui/app/img/create-category.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00001 13.6667C11.1296 13.6667 13.6667 11.1297 13.6667 8.00004C13.6667 4.87043 11.1296 2.33337 8.00001 2.33337C4.8704 2.33337 2.33334 4.87043 2.33334 8.00004C2.33334 11.1297 4.8704 13.6667 8.00001 13.6667ZM8.00001 14.6667C11.6819 14.6667 14.6667 11.6819 14.6667 8.00004C14.6667 4.31814 11.6819 1.33337 8.00001 1.33337C4.31811 1.33337 1.33334 4.31814 1.33334 8.00004C1.33334 11.6819 4.31811 14.6667 8.00001 14.6667Z" fill="#4360DF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.65987 5.08224C7.70527 4.80985 7.52126 4.55224 7.24887 4.50684C6.97649 4.46144 6.71887 4.64545 6.67348 4.91784L6.51175 5.88817C6.48497 6.0489 6.3459 6.16671 6.18296 6.16671H5.33334C5.0572 6.16671 4.83334 6.39056 4.83334 6.66671C4.83334 6.94285 5.0572 7.16671 5.33334 7.16671H5.90518C6.11116 7.16671 6.26784 7.35166 6.23398 7.55484L6.06731 8.55484C6.04052 8.71557 5.90146 8.83337 5.73851 8.83337H5.00001C4.72387 8.83337 4.50001 9.05723 4.50001 9.33337C4.50001 9.60952 4.72387 9.83337 5.00001 9.83337H5.46073C5.66671 9.83337 5.82339 10.0183 5.78953 10.2215L5.67348 10.9178C5.62808 11.1902 5.81209 11.4478 6.08447 11.4932C6.35686 11.5386 6.61447 11.3546 6.65987 11.0822L6.82159 10.1119C6.84838 9.95118 6.98744 9.83337 7.15039 9.83337H8.1274C8.33338 9.83337 8.49006 10.0183 8.4562 10.2215L8.34014 10.9178C8.29475 11.1902 8.47876 11.4478 8.75114 11.4932C9.02353 11.5386 9.28114 11.3546 9.32654 11.0822L9.48826 10.1119C9.51505 9.95118 9.65411 9.83337 9.81706 9.83337H10.6667C10.9428 9.83337 11.1667 9.60952 11.1667 9.33337C11.1667 9.05723 10.9428 8.83337 10.6667 8.83337H10.0948C9.88885 8.83337 9.73217 8.64842 9.76604 8.44524L9.9327 7.44524C9.95949 7.28451 10.0986 7.16671 10.2615 7.16671H11C11.2762 7.16671 11.5 6.94285 11.5 6.66671C11.5 6.39056 11.2762 6.16671 11 6.16671H10.5393C10.3333 6.16671 10.1766 5.98175 10.2105 5.77857L10.3265 5.08224C10.3719 4.80985 10.1879 4.55224 9.91554 4.50684C9.64316 4.46144 9.38554 4.64545 9.34014 4.91784L9.17842 5.88817C9.15163 6.0489 9.01257 6.16671 8.84962 6.16671H7.87261C7.66663 6.16671 7.50995 5.98175 7.54381 5.77857L7.65987 5.08224ZM8.40518 8.83337C8.56812 8.83337 8.70719 8.71557 8.73398 8.55484L8.90064 7.55484C8.93451 7.35166 8.77782 7.16671 8.57185 7.16671H7.59483C7.43189 7.16671 7.29282 7.28451 7.26604 7.44524L7.09937 8.44524C7.06551 8.64842 7.22219 8.83337 7.42817 8.83337H8.40518Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
3
ui/app/img/hide-category.svg
Normal file
3
ui/app/img/hide-category.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.58709 8.08709C5.9532 7.72097 6.5468 7.72097 6.91291 8.08709L9.64645 10.8206C9.84171 11.0159 10.1583 11.0159 10.3536 10.8206L13.0871 8.08709C13.4532 7.72097 14.0468 7.72097 14.4129 8.08709C14.779 8.4532 14.779 9.0468 14.4129 9.41291L10.6629 13.1629C10.2968 13.529 9.7032 13.529 9.33709 13.1629L5.58709 9.41291C5.22097 9.0468 5.22097 8.4532 5.58709 8.08709Z" fill="black" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 546 B |
5
ui/app/img/more.svg
Normal file
5
ui/app/img/more.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="4.375" cy="10" r="1.875" fill="black" fill-opacity="0.7"/>
|
||||
<circle cx="15.625" cy="10" r="1.875" fill="black" fill-opacity="0.7"/>
|
||||
<circle cx="10" cy="10" r="1.875" fill="black" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 314 B |
3
ui/app/img/show-category.svg
Normal file
3
ui/app/img/show-category.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.46209 6.21209C7.09597 6.5782 7.09597 7.1718 7.46209 7.53791L10.1072 10.1831C10.3513 10.4271 10.3513 10.8229 10.1072 11.0669L7.46209 13.7121C7.09597 14.0782 7.09597 14.6718 7.46209 15.0379C7.8282 15.404 8.4218 15.404 8.78791 15.0379L12.5379 11.2879C12.904 10.9218 12.904 10.3282 12.5379 9.96209L8.78791 6.21209C8.4218 5.84597 7.8282 5.84597 7.46209 6.21209Z" fill="black" fill-opacity="0.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 547 B |
2
vendor/status-go
vendored
2
vendor/status-go
vendored
@ -1 +1 @@
|
||||
Subproject commit 71f66f68064e9897cd17b6bcecba426a91405034
|
||||
Subproject commit 92032c7158b80908508cd67c08ac6f6c75eee35b
|
Loading…
x
Reference in New Issue
Block a user