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         }
0132     }
0133 
0134     Loader {
0135         id: invitationLoader
0136         active: root.currentRoom && root.currentRoom.isInvite
0137         anchors.centerIn: parent
0138         sourceComponent: InvitationView {
0139             currentRoom: root.currentRoom
0140             anchors.centerIn: parent
0141         }
0142     }
0143 
0144     Loader {
0145         active: root.loading && !invitationLoader.active
0146         anchors.centerIn: parent
0147         sourceComponent: Kirigami.LoadingPlaceholder {
0148             anchors.centerIn: parent
0149         }
0150     }
0151 
0152     background: Rectangle {
0153         Kirigami.Theme.colorSet: Kirigami.Theme.View
0154         Kirigami.Theme.inherit: false
0155         color: Config.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
0156     }
0157 
0158     footer: Loader {
0159         id: chatBarLoader
0160         active: timelineViewLoader.active && !root.currentRoom.readOnly
0161         sourceComponent: ChatBar {
0162             id: chatBar
0163             width: parent.width
0164             currentRoom: root.currentRoom
0165             connection: root.connection
0166             actionsHandler: root.actionsHandler
0167             onMessageSent: {
0168                 if (!timelineViewLoader.item.atYEnd) {
0169                     timelineViewLoader.item.goToLastMessage();
0170                 }
0171             }
0172         }
0173     }
0174 
0175     Connections {
0176         target: RoomManager
0177         function onCurrentRoomChanged() {
0178             if (!RoomManager.currentRoom) {
0179                 if (pageStack.lastItem === root) {
0180                     pageStack.pop();
0181                 }
0182             } else if (root.currentRoom.isInvite) {
0183                 root.currentRoom.clearInvitationNotification();
0184             }
0185         }
0186 
0187         function onWarning(title, message) {
0188             root.warning(title, message);
0189         }
0190     }
0191 
0192     Shortcut {
0193         sequence: StandardKey.Cancel
0194         onActivated: {
0195             if (!timelineViewLoader.item.atYEnd || root.currentRoom.hasUnreadMessages) {
0196                 timelineViewLoader.item.goToLastMessage();
0197                 root.currentRoom.markAllMessagesAsRead();
0198             } else {
0199                 applicationWindow().pageStack.get(0).forceActiveFocus();
0200             }
0201         }
0202         enabled: !root.disableCancelShortcut
0203     }
0204 
0205     Connections {
0206         target: root.connection
0207         function onJoinedRoom(room, invited) {
0208             if (root.currentRoom.id === invited.id) {
0209                 RoomManager.resolveResource(room.id);
0210             }
0211         }
0212     }
0213 
0214     Keys.onPressed: event => {
0215         if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
0216             event.accepted = true;
0217             chatBarLoader.item.insertText(event.text);
0218             chatBarLoader.item.forceActiveFocus();
0219             return;
0220         } else if (event.key === Qt.Key_PageUp) {
0221             event.accepted = true;
0222             timelineViewLoader.item.pageUp();
0223         } else if (event.key === Qt.Key_PageDown) {
0224             event.accepted = true;
0225             timelineViewLoader.item.pageDown();
0226         }
0227     }
0228 
0229     Connections {
0230         target: root.currentRoom
0231         function onShowMessage(messageType, message) {
0232             banner.text = message;
0233             banner.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : messageType === ActionsHandler.Positive ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information;
0234             banner.visible = true;
0235         }
0236     }
0237 
0238     function warning(title, message) {
0239         banner.text = `${title}<br />${message}`;
0240         banner.type = Kirigami.MessageType.Warning;
0241         banner.visible = true;
0242     }
0243 
0244     Connections {
0245         target: RoomManager
0246         function onShowUserDetail(user) {
0247             root.showUserDetail(user);
0248         }
0249 
0250         function onShowEventSource(eventId) {
0251             applicationWindow().pageStack.pushDialogLayer('qrc:/org/kde/neochat/qml/MessageSourceSheet.qml', {
0252                 sourceText: root.currentRoom.getEventJsonSource(eventId)
0253             }, {
0254                 title: i18n("Message Source"),
0255                 width: Kirigami.Units.gridUnit * 25
0256             });
0257         }
0258 
0259         function onShowMessageMenu(eventId, author, delegateType, plainText, htmlText, selectedText) {
0260             const contextMenu = messageDelegateContextMenu.createObject(root, {
0261                 selectedText: selectedText,
0262                 author: author,
0263                 eventId: eventId,
0264                 delegateType: delegateType,
0265                 plainText: plainText,
0266                 htmlText: htmlText
0267             });
0268             contextMenu.open();
0269         }
0270 
0271         function onShowFileMenu(eventId, author, delegateType, plainText, mimeType, progressInfo) {
0272             const contextMenu = fileDelegateContextMenu.createObject(root, {
0273                 author: author,
0274                 eventId: eventId,
0275                 delegateType: delegateType,
0276                 plainText: plainText,
0277                 mimeType: mimeType,
0278                 progressInfo: progressInfo
0279             });
0280             contextMenu.open();
0281         }
0282 
0283         function onShowMaximizedMedia(index) {
0284             var popup = maximizeComponent.createObject(QQC2.Overlay.overlay, {
0285                 initialIndex: index
0286             });
0287             popup.closed.connect(() => {
0288                 timelineViewLoader.item.interactive = true;
0289                 popup.destroy();
0290             });
0291             popup.open();
0292         }
0293     }
0294 
0295     function showUserDetail(user) {
0296         userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
0297             room: root.currentRoom,
0298             user: root.currentRoom.getUser(user.id)
0299         }).open();
0300     }
0301 
0302     Component {
0303         id: userDetailDialog
0304         UserDetailDialog {}
0305     }
0306 
0307     Component {
0308         id: messageDelegateContextMenu
0309         MessageDelegateContextMenu {
0310             connection: root.connection
0311         }
0312     }
0313 
0314     Component {
0315         id: fileDelegateContextMenu
0316         FileDelegateContextMenu {
0317             connection: root.connection
0318         }
0319     }
0320 
0321     Component {
0322         id: maximizeComponent
0323         NeochatMaximizeComponent {
0324             currentRoom: root.currentRoom
0325             model: root.mediaMessageFilterModel
0326         }
0327     }
0328 }