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

0001 // SPDX-FileCopyrightText: 2018-2020 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 QtQuick.Window
0009 
0010 import org.kde.kirigamiaddons.labs.components as KirigamiComponents
0011 import org.kde.kirigami as Kirigami
0012 import org.kde.kitemmodels
0013 
0014 import org.kde.neochat
0015 import org.kde.neochat.config
0016 
0017 Kirigami.Page {
0018     id: root
0019 
0020     /// Not readonly because of the separate window view.
0021     property NeoChatRoom currentRoom: RoomManager.currentRoom
0022 
0023     required property NeoChatConnection connection
0024 
0025     /**
0026      * @brief The TimelineModel to use.
0027      *
0028      * Required so that new events can be requested when the end of the current
0029      * local timeline is reached.
0030      *
0031      * @note For loading a room in a different window, override this with a new
0032      *       TimelineModel set with the room to be shown.
0033      *
0034      * @sa TimelineModel
0035      */
0036     property TimelineModel timelineModel: RoomManager.timelineModel
0037 
0038     /**
0039      * @brief The MessageFilterModel to use.
0040      *
0041      * This model has the filtered list of events that should be shown in the timeline.
0042      *
0043      * @note For loading a room in a different window, override this with a new
0044      *       MessageFilterModel with the new TimelineModel as the source model.
0045      *
0046      * @sa TimelineModel, MessageFilterModel
0047      */
0048     property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
0049 
0050     /**
0051      * @brief The MediaMessageFilterModel to use.
0052      *
0053      * This model has the filtered list of media events that should be shown in
0054      * the timeline.
0055      *
0056      * @note For loading a room in a different window, override this with a new
0057      *       MediaMessageFilterModel with the new MessageFilterModel as the source model.
0058      *
0059      * @sa TimelineModel, MessageFilterModel
0060      */
0061     property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
0062 
0063     /**
0064      * @brief The ActionsHandler object to use.
0065      */
0066     property ActionsHandler actionsHandler: ActionsHandler {
0067         room: root.currentRoom
0068     }
0069 
0070     property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
0071 
0072     /// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
0073     property bool disableCancelShortcut: false
0074 
0075     title: root.currentRoom.displayName
0076     focus: true
0077     padding: 0
0078 
0079     actions: [
0080         Kirigami.Action {
0081             visible: Kirigami.Settings.isMobile || !applicationWindow().pageStack.wideMode
0082             icon.name: "view-right-new"
0083             onTriggered: applicationWindow().openRoomDrawer()
0084         }
0085     ]
0086 
0087     KeyNavigation.left: pageStack.get(0)
0088 
0089     onCurrentRoomChanged: {
0090         banner.visible = false;
0091         if (!Kirigami.Settings.isMobile && chatBarLoader.item) {
0092             chatBarLoader.item.forceActiveFocus();
0093         }
0094     }
0095 
0096     Connections {
0097         target: root.connection
0098         function onIsOnlineChanged() {
0099             if (!root.connection.isOnline) {
0100                 banner.text = i18n("NeoChat is offline. Please check your network connection.");
0101                 banner.visible = true;
0102                 banner.type = Kirigami.MessageType.Error;
0103             } else {
0104                 banner.visible = false;
0105             }
0106         }
0107     }
0108 
0109     header: KirigamiComponents.Banner {
0110         id: banner
0111 
0112         showCloseButton: true
0113         visible: false
0114     }
0115 
0116     Loader {
0117         id: timelineViewLoader
0118         anchors.fill: parent
0119         active: root.currentRoom && !root.currentRoom.isInvite && !root.loading
0120         sourceComponent: TimelineView {
0121             id: timelineView
0122             currentRoom: root.currentRoom
0123             timelineModel: root.timelineModel
0124             messageFilterModel: root.messageFilterModel
0125             actionsHandler: root.actionsHandler
0126             onFocusChatBar: {
0127                 if (chatBarLoader.item) {
0128                     chatBarLoader.item.forceActiveFocus()
0129                 }
0130             }
0131             connection: root.connection
0132         }
0133     }
0134 
0135     Loader {
0136         id: invitationLoader
0137         active: root.currentRoom && root.currentRoom.isInvite
0138         anchors.centerIn: parent
0139         sourceComponent: InvitationView {
0140             currentRoom: root.currentRoom
0141             anchors.centerIn: parent
0142         }
0143     }
0144 
0145     Loader {
0146         active: root.loading && !invitationLoader.active
0147         anchors.centerIn: parent
0148         sourceComponent: Kirigami.LoadingPlaceholder {
0149             anchors.centerIn: parent
0150         }
0151     }
0152 
0153     background: Rectangle {
0154         Kirigami.Theme.colorSet: Kirigami.Theme.View
0155         Kirigami.Theme.inherit: false
0156         color: Config.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
0157     }
0158 
0159     footer: Loader {
0160         id: chatBarLoader
0161         active: timelineViewLoader.active && !root.currentRoom.readOnly
0162         sourceComponent: ChatBar {
0163             id: chatBar
0164             width: parent.width
0165             currentRoom: root.currentRoom
0166             connection: root.connection
0167             actionsHandler: root.actionsHandler
0168             onMessageSent: {
0169                 if (!timelineViewLoader.item.atYEnd) {
0170                     timelineViewLoader.item.goToLastMessage();
0171                 }
0172             }
0173         }
0174     }
0175 
0176     Connections {
0177         target: RoomManager
0178         function onCurrentRoomChanged() {
0179             if(!RoomManager.currentRoom) {
0180                 if(pageStack.lastItem === root) {
0181                     pageStack.pop()
0182                 }
0183             } else if (root.currentRoom.isInvite) {
0184                 root.currentRoom.clearInvitationNotification();
0185             }
0186         }
0187 
0188         function onWarning(title, message) {
0189             root.warning(title, message);
0190         }
0191     }
0192 
0193     Shortcut {
0194         sequence: StandardKey.Cancel
0195         onActivated: {
0196             if (!timelineViewLoader.item.atYEnd || root.currentRoom.hasUnreadMessages) {
0197                 timelineViewLoader.item.goToLastMessage();
0198                 root.currentRoom.markAllMessagesAsRead();
0199             } else {
0200                 applicationWindow().pageStack.get(0).forceActiveFocus();
0201             }
0202         }
0203         enabled: !root.disableCancelShortcut
0204     }
0205 
0206     Connections {
0207         target: root.connection
0208         function onJoinedRoom(room, invited) {
0209             if(root.currentRoom.id === invited.id) {
0210                 RoomManager.enterRoom(room);
0211             }
0212         }
0213     }
0214 
0215     Keys.onPressed: event => {
0216         if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
0217             event.accepted = true;
0218             chatBarLoader.item.insertText(event.text);
0219             chatBarLoader.item.forceActiveFocus();
0220             return;
0221         } else if (event.key === Qt.Key_PageUp) {
0222             event.accepted = true;
0223             timelineViewLoader.item.pageUp()
0224         } else if (event.key === Qt.Key_PageDown) {
0225             event.accepted = true;
0226             timelineViewLoader.item.pageDown()
0227         }
0228     }
0229 
0230     Connections {
0231         target: root.currentRoom
0232         function onShowMessage(messageType, message) {
0233             banner.text = message;
0234             banner.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : messageType === ActionsHandler.Positive ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information;
0235             banner.visible = true;
0236         }
0237     }
0238 
0239     function warning(title, message) {
0240         banner.text = `${title}<br />${message}`;
0241         banner.type =  Kirigami.MessageType.Warning;
0242         banner.visible = true;
0243     }
0244 
0245     Connections {
0246         target: RoomManager
0247         function onShowUserDetail(user) {
0248             root.showUserDetail(user)
0249         }
0250 
0251         function onShowEventSource(eventId) {
0252             applicationWindow().pageStack.pushDialogLayer('qrc:/org/kde/neochat/qml/MessageSourceSheet.qml', {
0253                 sourceText: root.currentRoom.getEventJsonSource(eventId)
0254             }, {
0255                 title: i18n("Message Source"),
0256                 width: Kirigami.Units.gridUnit * 25
0257             });
0258         }
0259 
0260         function onShowMessageMenu(eventId, author, delegateType, plainText, htmlText, selectedText) {
0261             const contextMenu = messageDelegateContextMenu.createObject(root, {
0262                 selectedText: selectedText,
0263                 author: author,
0264                 eventId: eventId,
0265                 delegateType: delegateType,
0266                 plainText: plainText,
0267                 htmlText: htmlText
0268             });
0269             contextMenu.open();
0270         }
0271 
0272         function onShowFileMenu(eventId, author, delegateType, plainText, mimeType, progressInfo) {
0273             const contextMenu = fileDelegateContextMenu.createObject(root, {
0274                 author: author,
0275                 eventId: eventId,
0276                 delegateType: delegateType,
0277                 plainText: plainText,
0278                 mimeType: mimeType,
0279                 progressInfo: progressInfo
0280             });
0281             contextMenu.open();
0282         }
0283 
0284         function onShowMaximizedMedia(index) {
0285             var popup = maximizeComponent.createObject(QQC2.Overlay.overlay, {
0286                 initialIndex: index
0287             })
0288             popup.closed.connect(() => {
0289                 timelineViewLoader.item.interactive = true
0290                 popup.destroy()
0291             })
0292             popup.open()
0293         }
0294     }
0295 
0296     function showUserDetail(user) {
0297         userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
0298             room: root.currentRoom,
0299             user: root.currentRoom.getUser(user.id),
0300         }).open();
0301     }
0302 
0303     Component {
0304         id: userDetailDialog
0305         UserDetailDialog {}
0306     }
0307 
0308     Component {
0309         id: messageDelegateContextMenu
0310         MessageDelegateContextMenu {
0311             connection: root.connection
0312         }
0313     }
0314 
0315     Component {
0316         id: fileDelegateContextMenu
0317         FileDelegateContextMenu {
0318             connection: root.connection
0319         }
0320     }
0321 
0322     Component {
0323         id: maximizeComponent
0324         NeochatMaximizeComponent {
0325             currentRoom: root.currentRoom
0326             model: root.mediaMessageFilterModel
0327         }
0328     }
0329 }