feat(Components): introduce `StatusChatListAndCategories` component

This is a wrapping component that can be used to render community chat
lists and categories. It takes care of rendering categories, the top
chat list, as well as becominng scrollable in case the content outgrows
the available space.

Usage:

```qml
import StatusQ.Components 0.1

StatusChatListAndCategories {

    chatList.model: ... // non-categorized chat items, pass all chat items here, the component will take care of filtering categorized items out
    categoryListModel: ... // available categories (need to have `id` and `name`)

    selectedChatId: ...

    showCategoryActionButtons: true // default `false` - useful when only admin users can create and mutate categories/channels

    onChatItemSelected: ... // `id` is available for selected chat id

    categoryPopupMenu: StatusPopupMenu { // optional popup menu for category items

        property string categoryId // define this property to have it hydrated with correct id and make it available inside menu items
        ...
    }

    popupMenu: StatusPopupMenu { ... } // optional popup menu for whole list, will be triggered with right-click
}
```

Closes #133
This commit is contained in:
Pascal Precht 2021-06-15 16:27:39 +02:00
parent 9c76688929
commit de1282b40e
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
3 changed files with 165 additions and 60 deletions

View File

@ -157,23 +157,18 @@ Rectangle {
StatusAppTwoPanelLayout {
leftPanel: Item {
leftPanel: StatusChatListAndCategories {
anchors.fill: parent
anchors.topMargin: 64
StatusChatList {
anchors.top: parent.top
anchors.topMargin: 64
anchors.horizontalCenter: parent.horizontalCenter
selectedChatId: "0"
chatListItems.model: demoChatListItems
onChatItemSelected: selectedChatId = id
onChatItemUnmuted: {
for (var i = 0; i < demoChatListItems.count; i++) {
let item = demoChatListItems.get(i);
if (item.chatId === id) {
demoChatListItems.setProperty(i, "muted", false)
}
chatList.model: demoChatListItems
selectedChatId: "0"
onChatItemSelected: selectedChatId = id
onChatItemUnmuted: {
for (var i = 0; i < demoChatListItems.count; i++) {
let item = demoChatListItems.get(i);
if (item.chatId === id) {
demoChatListItems.setProperty(i, "muted", false)
}
}
}
@ -233,69 +228,65 @@ Rectangle {
StatusAppTwoPanelLayout {
leftPanel: Item {
leftPanel: StatusChatListAndCategories {
anchors.topMargin: 64
anchors.fill: parent
Column {
anchors.top: parent.top
anchors.topMargin: 64
anchors.horizontalCenter: parent.horizontalCenter
spacing: 4
chatList.model: demoCommunityChatListItems
categoryList.model: demoCommunityCategoryItems
StatusChatList {
id: statusChatList
anchors.horizontalCenter: parent.horizontalCenter
chatListItems.model: demoCommunityChatListItems
showCategoryActionButtons: true
onChatItemSelected: selectedChatId = id
categoryPopupMenu: StatusPopupMenu {
property string categoryId
StatusMenuItem {
text: "Mute Category"
icon.name: "notification"
}
StatusChatListCategory {
name: "Public"
showActionButtons: true
chatList.chatListItems.model: demoCommunityChatListItems
chatList.selectedChatId: "0"
chatList.onChatItemSelected: chatList.selectedChatId = id
popupMenu: categoryPopupCmp
StatusMenuItem {
text: "Mark as Read"
icon.name: "checkmark-circle"
}
StatusChatListCategory {
name: "Development"
StatusMenuItem {
text: "Edit Category"
icon.name: "edit"
}
showActionButtons: true
chatList.chatListItems.model: demoCommunityChatListItems
chatList.onChatItemSelected: chatList.selectedChatId = id
popupMenu: categoryPopupCmp
StatusMenuSeparator {}
StatusMenuItem {
text: "Delete Category"
icon.name: "delete"
type: StatusMenuItem.Type.Danger
}
}
Component {
id: categoryPopupCmp
StatusPopupMenu {
StatusMenuItem {
text: "Mute Category"
icon.name: "notification"
}
popupMenu: StatusPopupMenu {
StatusMenuItem {
text: "Create channel"
icon.name: "channel"
}
StatusMenuItem {
text: "Mark as Read"
icon.name: "checkmark-circle"
}
StatusMenuItem {
text: "Create category"
icon.name: "channel-category"
}
StatusMenuItem {
text: "Edit Category"
icon.name: "edit"
}
StatusMenuSeparator {}
StatusMenuSeparator {}
StatusMenuItem {
text: "Delete Category"
icon.name: "delete"
type: StatusMenuItem.Type.Danger
}
StatusMenuItem {
text: "Invite people"
icon.name: "share-ios"
}
}
}
rightPanel: Item {
anchors.fill: parent
@ -384,6 +375,7 @@ Rectangle {
hasMention: false
unreadMessagesCount: 0
iconColor: "orange"
categoryId: "public"
}
ListElement {
chatId: "2"
@ -394,6 +386,30 @@ Rectangle {
hasMention: false
unreadMessagesCount: 0
iconColor: "orange"
categoryId: "public"
}
ListElement {
chatId: "3"
name: "language-design"
chatType: StatusChatListItem.Type.CommunityChat
muted: false
hasUnreadMessages: false
hasMention: false
unreadMessagesCount: 0
iconColor: "orange"
categoryId: "dev"
}
}
ListModel {
id: demoCommunityCategoryItems
ListElement {
categoryId: "public"
name: "Public"
}
ListElement {
categoryId: "dev"
name: "Development"
}
}
}

View File

@ -0,0 +1,88 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
ScrollView {
id: statusChatListAndCategories
clip: true
contentHeight: chatListsAndCategories.height + 8
property string selectedChatId: ""
property bool showCategoryActionButtons: false
property alias chatList: statusChatList.chatListItems
property alias categoryList: statusChatListCategories
property alias sensor: sensor
property Component categoryPopupMenu
property Component popupMenu
signal chatItemSelected(string id)
signal chatItemUnmuted(string id)
signal categoryAddButtonClicked(string id)
onPopupMenuChanged: {
if (!!popupMenu) {
popupMenuSlot.sourceComponent = popupMenu
}
}
MouseArea {
id: sensor
anchors.top: parent.top
width: parent.width
height: statusChatListAndCategories.height
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.RightButton && !!statusChatListAndCategories.popupMenu) {
popupMenuSlot.item.popup(mouse.x + 4, mouse.y + 6)
return
}
}
Column {
id: chatListsAndCategories
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
spacing: 4
StatusChatList {
id: statusChatList
anchors.horizontalCenter: parent.horizontalCenter
visible: !!chatListItems.model && chatListItems.model.count > 0
selectedChatId: statusChatListAndCategories.selectedChatId
onChatItemSelected: statusChatListAndCategories.chatItemSelected(id)
onChatItemUnmuted: statusChatListAndCategories.chatItemUnmuted(id)
filterFn: function (model) {
return !!!model.categoryId
}
}
Repeater {
id: statusChatListCategories
visible: !!model && model.count > 0
delegate: StatusChatListCategory {
categoryId: model.categoryId
name: model.name
showActionButtons: statusChatListAndCategories.showCategoryActionButtons
addButton.onClicked: statusChatListAndCategories.categoryAddButtonClicked(model.categoryId)
chatList.chatListItems.model: statusChatListAndCategories.chatList.model
chatList.selectedChatId: statusChatListAndCategories.selectedChatId
chatList.onChatItemSelected: statusChatListAndCategories.chatItemSelected(id)
popupMenu: statusChatListAndCategories.categoryPopupMenu
}
}
}
}
Loader {
id: popupMenuSlot
active: !!statusChatListAndCategories.popupMenu
}
}

View File

@ -5,6 +5,7 @@ StatusChatList 0.1 StatusChatList.qml
StatusChatListItem 0.1 StatusChatListItem.qml
StatusChatListCategory 0.1 StatusChatListCategory.qml
StatusChatListCategoryItem 0.1 StatusChatListCategoryItem.qml
StatusChatListAndCategories 0.1 StatusChatListAndCategories.qml
StatusChatToolBar 0.1 StatusChatToolBar.qml
StatusDescriptionListItem 0.1 StatusDescriptionListItem.qml
StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml