Warning, /network/neochat/src/qml/RoomListPage.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
0003 // SPDX-License-Identifier: GPL-3.0-only
0004 
0005 import QtQuick
0006 import QtQuick.Controls as QQC2
0007 import QtQuick.Layouts
0008 import QtQml.Models
0009 
0010 import org.kde.kirigami as Kirigami
0011 import org.kde.kirigamiaddons.components as KirigamiComponents
0012 
0013 import org.kde.neochat
0014 import org.kde.neochat.config
0015 import org.kde.neochat.accounts
0016 
0017 Kirigami.Page {
0018     id: root
0019 
0020     /**
0021      * @brief The current width of the room list.
0022      *
0023      * @note Other objects can access the value but the private function makes sure
0024      *       that only the internal members can modify it.
0025      */
0026     readonly property int currentWidth: _private.currentWidth + spaceListWidth
0027     readonly property alias spaceListWidth: spaceDrawer.width
0028 
0029     required property NeoChatConnection connection
0030 
0031     readonly property RoomListModel roomListModel: RoomListModel {
0032         connection: root.connection
0033     }
0034 
0035     readonly property bool collapsed: Config.collapsed
0036 
0037     property var enteredRoom: null
0038 
0039     onCollapsedChanged: if (collapsed) {
0040         sortFilterRoomListModel.filterText = "";
0041     }
0042 
0043     Component.onCompleted: Runner.roomListModel = root.roomListModel
0044 
0045     Connections {
0046         target: RoomManager
0047         function onCurrentRoomChanged() {
0048             itemSelection.setCurrentIndex(roomListModel.index(roomListModel.rowForRoom(RoomManager.currentRoom), 0), ItemSelectionModel.SelectCurrent)
0049         }
0050     }
0051 
0052     function goToNextRoomFiltered(condition) {
0053         let index = listView.currentIndex;
0054         while (index++ !== listView.count - 1) {
0055             if (condition(listView.itemAtIndex(index))) {
0056                 listView.currentIndex = index;
0057                 listView.currentItem.clicked();
0058                 return;
0059             }
0060         }
0061     }
0062 
0063     function goToPreviousRoomFiltered(condition) {
0064         let index = listView.currentIndex;
0065         while (index-- !== 0) {
0066             if (condition(listView.itemAtIndex(index))) {
0067                 listView.currentIndex = index;
0068                 listView.currentItem.clicked();
0069                 return;
0070             }
0071         }
0072     }
0073 
0074     function goToNextRoom() {
0075         goToNextRoomFiltered((item) => item.visible);
0076     }
0077 
0078     function goToPreviousRoom() {
0079         goToPreviousRoomFiltered((item) => item.visible);
0080     }
0081 
0082     function goToNextUnreadRoom() {
0083         goToNextRoomFiltered((item) => (item.visible && item.hasUnread));
0084     }
0085 
0086     function goToPreviousUnreadRoom() {
0087         goToPreviousRoomFiltered((item) => (item.visible && item.hasUnread));
0088     }
0089 
0090     titleDelegate: Loader {
0091         Layout.fillWidth: true
0092         sourceComponent:  Kirigami.Settings.isMobile ? userInfo : exploreComponent
0093     }
0094 
0095     padding: 0
0096 
0097     RowLayout {
0098         anchors.fill: parent
0099         spacing: 0
0100 
0101         SpaceDrawer {
0102             id: spaceDrawer
0103             Layout.preferredWidth: spaceDrawer.enabled ? Kirigami.Units.gridUnit * 3 : 0
0104             Layout.fillHeight: true
0105 
0106             connection: root.connection
0107         }
0108 
0109         Kirigami.Separator {
0110             Layout.fillHeight: true
0111             Layout.preferredWidth: 1
0112         }
0113 
0114         QQC2.ScrollView {
0115             id: scrollView
0116             Layout.fillWidth: true
0117             Layout.fillHeight: true
0118 
0119             background: Rectangle {
0120                 color: Kirigami.Theme.backgroundColor
0121                 Kirigami.Theme.colorSet: Kirigami.Theme.View
0122             }
0123 
0124             ListView {
0125                 id: listView
0126 
0127                 activeFocusOnTab: true
0128                 clip: true
0129 
0130                 topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
0131 
0132                 KirigamiComponents.FloatingButton {
0133                     icon.name: "notifications"
0134                     text: i18n("View notifications")
0135                     anchors.right: parent.right
0136                     anchors.rightMargin: Kirigami.Units.largeSpacing
0137                     anchors.bottom: parent.bottom
0138                     anchors.bottomMargin: Kirigami.Units.largeSpacing
0139                     width: Kirigami.Units.gridUnit * 2
0140                     height: width
0141                     visible: !root.collapsed
0142                     QQC2.ToolTip.text: text
0143                     QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0144                     QQC2.ToolTip.visible: hovered
0145                     onClicked: pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/NotificationsView.qml", {connection: root.connection}, {
0146                         title: i18nc("@title", "Notifications")
0147                     });
0148                 }
0149 
0150                 header: QQC2.ItemDelegate {
0151                     width: visible ? ListView.view.width : 0
0152                     height: visible ? Kirigami.Units.gridUnit * 2 : 0
0153 
0154                     visible: root.collapsed
0155 
0156                     topPadding: Kirigami.Units.largeSpacing
0157                     leftPadding: Kirigami.Units.largeSpacing
0158                     rightPadding: Kirigami.Units.largeSpacing
0159                     bottomPadding: Kirigami.Units.largeSpacing
0160 
0161                     onClicked: quickView.item.open();
0162 
0163                     Kirigami.Icon {
0164                         anchors.centerIn: parent
0165                         width: Kirigami.Units.iconSizes.smallMedium
0166                         height: Kirigami.Units.iconSizes.smallMedium
0167                         source: "search"
0168                     }
0169 
0170                     Kirigami.Separator {
0171                         width: parent.width
0172                         anchors.bottom: parent.bottom
0173                     }
0174                 }
0175 
0176                 Kirigami.PlaceholderMessage {
0177                     anchors.centerIn: parent
0178                     width: parent.width - (Kirigami.Units.largeSpacing * 4)
0179                     visible: listView.count == 0
0180                     text: sortFilterRoomListModel.filterText.length > 0 ? i18n("No rooms found") : i18n("Join some rooms to get started")
0181                     helpfulAction: Kirigami.Action {
0182                         icon.name: sortFilterRoomListModel.filterText.length > 0 ? "search" : "list-add"
0183                         text: sortFilterRoomListModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
0184                         onTriggered: {
0185                             let dialog = pageStack.layers.push("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {
0186                                 connection: root.connection,
0187                                 keyword: sortFilterRoomListModel.filterText
0188                             }, {
0189                                 title: i18nc("@title", "Explore Rooms")
0190                             })
0191                             dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
0192                                 if (isJoined) {
0193                                     RoomManager.enterRoom(root.connection.room(roomId))
0194                                 } else {
0195                                     RoomManager.resolveResource(roomId, "join")
0196                                 }
0197                             })
0198                         }
0199                     }
0200                 }
0201 
0202                 ItemSelectionModel {
0203                     id: itemSelection
0204                     model: root.roomListModel
0205                     onCurrentChanged: (current, previous) => listView.currentIndex = sortFilterRoomListModel.mapFromSource(current).row
0206                 }
0207 
0208                 model: SortFilterRoomListModel {
0209                     id: sortFilterRoomListModel
0210 
0211                     sourceModel: root.roomListModel
0212                     roomSortOrder: Config.mergeRoomList ? SortFilterRoomListModel.LastActivity : SortFilterRoomListModel.Categories
0213                     onLayoutChanged: {
0214                         listView.currentIndex = sortFilterRoomListModel.mapFromSource(itemSelection.currentIndex).row
0215                     }
0216                     activeSpaceId: spaceDrawer.selectedSpaceId
0217                 }
0218 
0219                 section {
0220                     property: sortFilterRoomListModel.filterText.length === 0 && !Config.mergeRoomList ? "category" : null
0221                     delegate: root.collapsed ? foldButton : sectionHeader
0222                 }
0223 
0224                 Component {
0225                     id: sectionHeader
0226                     Kirigami.ListSectionHeader {
0227                         height: implicitHeight
0228                         width: listView.width
0229                         label: roomListModel.categoryName(section)
0230                         action: Kirigami.Action {
0231                             onTriggered: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section))
0232                         }
0233 
0234                         QQC2.ToolButton {
0235                             icon {
0236                                 name: roomListModel.categoryVisible(section) ? "go-up" : "go-down"
0237                                 width: Kirigami.Units.iconSizes.small
0238                                 height: Kirigami.Units.iconSizes.small
0239                             }
0240                             text: roomListModel.categoryVisible(section) ? i18nc("Collapse <section name>", "Collapse %1", roomListModel.categoryName(section)) : i18nc("Expand <section name", "Expand %1", roomListModel.categoryName(section))
0241                             display: QQC2.Button.IconOnly
0242 
0243                             QQC2.ToolTip.text: text
0244                             QQC2.ToolTip.visible: hovered
0245                             QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0246 
0247                             onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section))
0248                         }
0249                     }
0250                 }
0251                 Component {
0252                     id: foldButton
0253                     Item {
0254                         width: ListView.view.width
0255                         height: visible ? width : 0
0256                         QQC2.ToolButton {
0257                             id: button
0258                             anchors.centerIn: parent
0259 
0260                             icon {
0261                                 name: hovered ? (roomListModel.categoryVisible(section) ? "go-up" : "go-down") : roomListModel.categoryIconName(section)
0262                                 width: Kirigami.Units.iconSizes.smallMedium
0263                                 height: Kirigami.Units.iconSizes.smallMedium
0264                             }
0265 
0266                             onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section))
0267 
0268                             QQC2.ToolTip.text: roomListModel.categoryName(section)
0269                             QQC2.ToolTip.visible: hovered
0270                             QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0271                         }
0272                     }
0273                 }
0274 
0275                 reuseItems: true
0276                 currentIndex: -1 // we don't want any room highlighted by default
0277 
0278                 delegate: root.collapsed ? collapsedModeListComponent : normalModeListComponent
0279 
0280                 Component {
0281                     id: collapsedModeListComponent
0282 
0283                     CollapsedRoomDelegate {
0284                         filterText: sortFilterRoomListModel.filterText
0285                     }
0286                 }
0287 
0288                 Component {
0289                     id: normalModeListComponent
0290 
0291                     RoomDelegate {
0292                         filterText: sortFilterRoomListModel.filterText
0293 
0294                         connection: root.connection
0295 
0296                         height: visible ? implicitHeight : 0
0297 
0298                         visible: categoryVisible || filterText.length > 0 || Config.mergeRoomList
0299 
0300                         onSelected: RoomManager.enterRoom(currentRoom)
0301 
0302                         Keys.onEnterPressed: RoomManager.enterRoom(currentRoom)
0303                         Keys.onReturnPressed: RoomManager.enterRoom(currentRoom)
0304                     }
0305                 }
0306             }
0307         }
0308     }
0309 
0310     footer: Loader {
0311         width: parent.width
0312         sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop
0313     }
0314 
0315     MouseArea {
0316         anchors.top: parent.top
0317         anchors.bottom: parent.bottom
0318         parent: applicationWindow().overlay.parent
0319 
0320         x: root.currentWidth - width / 2
0321         width: Kirigami.Units.smallSpacing * 2
0322         z: root.z + 1
0323         enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35
0324         visible: enabled
0325         cursorShape: Qt.SplitHCursor
0326 
0327         property int _lastX
0328 
0329         onPressed: mouse => {
0330             _lastX = mouse.x;
0331         }
0332         onPositionChanged: mouse => {
0333             if (_lastX == -1) {
0334                 return;
0335             }
0336             if (mouse.x > _lastX) {
0337                 // we moved to the right
0338                 if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) {
0339                     // Here we get back directly to a more wide mode.
0340                     _private.currentWidth = _private.defaultWidth;
0341                     Config.collapsed = false;
0342                 } else if (_private.currentWidth >= _private.collapseWidth) {
0343                     // Increase page width
0344                     _private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX));
0345                 }
0346             } else if (mouse.x < _lastX) {
0347                 const tmpWidth = _private.currentWidth - (_lastX - mouse.x);
0348                 if (tmpWidth < _private.collapseWidth) {
0349                     _private.currentWidth = Qt.binding(() => _private.collapsedSize);
0350                     Config.collapsed = true;
0351                 } else {
0352                     _private.currentWidth = tmpWidth;
0353                 }
0354             }
0355         }
0356     }
0357 
0358     Component {
0359         id: userInfo
0360         UserInfo {
0361             visible: !root.collapsed
0362             bottomEdge: false
0363             connection: root.connection
0364         }
0365     }
0366 
0367     Component {
0368         id: userInfoDesktop
0369         UserInfoDesktop {
0370             visible: !root.collapsed
0371             connection: root.connection
0372         }
0373     }
0374 
0375     Component {
0376         id: exploreComponent
0377         ExploreComponent {
0378             desiredWidth: root.width - Kirigami.Units.largeSpacing
0379             collapsed: root.collapsed
0380             connection: root.connection
0381         }
0382     }
0383 
0384     Component {
0385         id: exploreComponentMobile
0386         ExploreComponentMobile {
0387             connection: root.connection
0388 
0389             onTextChanged: (newText) => {
0390                 sortFilterRoomListModel.filterText = newText
0391             }
0392         }
0393     }
0394 
0395     /*
0396      * Hold the modifiable currentWidth in a private object so that only internal
0397      * members can modify it.
0398      */
0399     QtObject {
0400         id: _private
0401         property int currentWidth: Config.collapsed ? collapsedSize : defaultWidth
0402         readonly property int defaultWidth: Kirigami.Units.gridUnit * 17
0403         readonly property int collapseWidth: Kirigami.Units.gridUnit * 10
0404         readonly property int collapsedSize: Kirigami.Units.gridUnit * 3 - Kirigami.Units.smallSpacing * 3 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0)
0405     }
0406 }