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 }