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 }