Warning, /network/neochat/src/qml/main.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 
0008 import org.kde.kirigami as Kirigami
0009 
0010 import org.kde.neochat
0011 import org.kde.neochat.config
0012 import org.kde.neochat.accounts
0013 
0014 Kirigami.ApplicationWindow {
0015     id: root
0016 
0017     property int columnWidth: Kirigami.Units.gridUnit * 13
0018 
0019     property RoomListPage roomListPage
0020     property bool roomListLoaded: false
0021 
0022     property RoomPage roomPage
0023     property SpaceHomePage spaceHomePage
0024 
0025     property NeoChatConnection connection: Controller.activeConnection
0026 
0027     minimumWidth: Kirigami.Units.gridUnit * 20
0028     minimumHeight: Kirigami.Units.gridUnit * 15
0029 
0030     visible: false // Will be overridden in Component.onCompleted
0031     wideScreen: width > columnWidth * 5
0032 
0033     pageStack {
0034         initialPage: WelcomePage {
0035             showExisting: true
0036             onConnectionChosen: {
0037                 pageStack.replace(roomListComponent);
0038                 roomListLoaded = true;
0039                 roomListPage = pageStack.currentItem
0040                 RoomManager.loadInitialRoom();
0041             }
0042         }
0043         globalToolBar.canContainHandles: true
0044         defaultColumnWidth: roomListPage ? roomListPage.currentWidth : 0
0045         globalToolBar {
0046             style: Kirigami.ApplicationHeaderStyle.ToolBar
0047             showNavigationButtons: pageStack.currentIndex > 0 || pageStack.layers.depth > 1 ? Kirigami.ApplicationHeaderStyle.ShowBackButton : 0
0048         }
0049     }
0050 
0051     onConnectionChanged: {
0052         CustomEmojiModel.connection = root.connection
0053         MatrixImageProvider.connection = root.connection
0054         RoomManager.connection = root.connection
0055         SpaceHierarchyCache.connection = root.connection
0056     }
0057 
0058     Connections {
0059         target: LoginHelper
0060         function onLoaded() {
0061             pageStack.replace(roomListComponent);
0062             roomListLoaded = true;
0063             roomListPage = pageStack.currentItem
0064             RoomManager.loadInitialRoom();
0065         }
0066     }
0067 
0068     Connections {
0069         target: root.quitAction
0070         function onTriggered() {
0071             Qt.quit()
0072         }
0073     }
0074 
0075     Loader {
0076         active: Kirigami.Settings.hasPlatformMenuBar && !Kirigami.Settings.isMobile
0077         sourceComponent: GlobalMenu {
0078             connection: root.connection
0079         }
0080     }
0081 
0082     // This timer allows to batch update the window size change to reduce
0083     // the io load and also work around the fact that x/y/width/height are
0084     // changed when loading the page and overwrite the saved geometry from
0085     // the previous session.
0086     Timer {
0087         id: saveWindowGeometryTimer
0088         interval: 1000
0089         onTriggered: WindowController.saveGeometry()
0090     }
0091 
0092     Connections {
0093         id: saveWindowGeometryConnections
0094         enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
0095         target: root
0096 
0097         function onClosing() { WindowController.saveGeometry(); }
0098         function onWidthChanged() { saveWindowGeometryTimer.restart(); }
0099         function onHeightChanged() { saveWindowGeometryTimer.restart(); }
0100         function onXChanged() { saveWindowGeometryTimer.restart(); }
0101         function onYChanged() { saveWindowGeometryTimer.restart(); }
0102     }
0103 
0104 
0105     Loader {
0106         id: quickView
0107         active: !Kirigami.Settings.isMobile
0108         sourceComponent: QuickSwitcher {
0109             connection: root.connection
0110         }
0111     }
0112 
0113     Connections {
0114         target: RoomManager
0115 
0116         function onPushRoom(room, event) {
0117             root.roomPage = pageStack.push("qrc:/org/kde/neochat/qml/RoomPage.qml", {connection: root.connection});
0118             root.roomPage.forceActiveFocus();
0119             if (event.length > 0) {
0120                 roomPage.goToEvent(event);
0121             }
0122         }
0123 
0124         function onPushSpaceHome(room) {
0125             root.spaceHomePage = pageStack.push("qrc:/org/kde/neochat/qml/SpaceHomePage.qml");
0126             root.spaceHomePage.forceActiveFocus();
0127         }
0128 
0129         function onReplaceRoom(room, event) {
0130             if (root.roomPage) {
0131                 pageStack.currentIndex = pageStack.depth - 1;
0132             } else {
0133                 pageStack.pop();
0134                 root.roomPage = pageStack.push("qrc:/org/kde/neochat/qml/RoomPage.qml", {connection: root.connection});
0135                 root.spaceHomePage = null;
0136             }
0137             root.roomPage.forceActiveFocus();
0138             if (event.length > 0) {
0139                 root.roomPage.goToEvent(event);
0140             }
0141         }
0142 
0143         function onReplaceSpaceHome(room) {
0144             if (root.spaceHomePage) {
0145                 pageStack.currentIndex = pageStack.depth - 1;
0146             } else {
0147                 pageStack.pop();
0148                 root.spaceHomePage = pageStack.push("qrc:/org/kde/neochat/qml/SpaceHomePage.qml");
0149                 root.roomPage = null;
0150             }
0151             root.spaceHomePage.forceActiveFocus();
0152         }
0153 
0154         function goToEvent(event) {
0155             if (event.length > 0) {
0156                 roomItem.goToEvent(event);
0157             }
0158             roomItem.forceActiveFocus();
0159         }
0160 
0161         function onOpenRoomInNewWindow(room) {
0162             const secondaryWindow = roomWindow.createObject(undefined, {currentRoom: room, connection: root.connection});
0163             secondaryWindow.width = root.width - pageStack.get(0).width;
0164             secondaryWindow.show();
0165         }
0166 
0167         function onAskDirectChatConfirmation(user) {
0168             askDirectChatConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
0169                 user: user,
0170             }).open();
0171         }
0172     }
0173 
0174     function pushReplaceLayer(page, args) {
0175         if (pageStack.layers.depth === 2) {
0176             pageStack.layers.replace(page, args);
0177         } else {
0178             pageStack.layers.push(page, args);
0179         }
0180     }
0181 
0182     function openRoomDrawer() {
0183         pageStack.push("qrc:/org/kde/neochat/qml/RoomDrawerPage.qml", {
0184             connection: root.connection
0185         })
0186     }
0187 
0188     contextDrawer: RoomDrawer {
0189         id: contextDrawer
0190 
0191         // This is a memory for all user initiated actions on the drawer, i.e. clicking the button
0192         // It is used to ensure that user choice is remembered when changing pages and expanding and contracting the window width
0193         property bool drawerUserState: Config.autoRoomInfoDrawer
0194 
0195         connection: root.connection
0196 
0197         // Connect to the onClicked function of the RoomDrawer handle button
0198         Connections {
0199             target: contextDrawer.handle.children[0]
0200             function onClicked() {
0201                 contextDrawer.drawerUserState = contextDrawer.drawerOpen
0202             }
0203         }
0204 
0205         modal: (!root.wideScreen || !enabled)
0206         onEnabledChanged: drawerOpen = enabled && !modal
0207         onModalChanged: {
0208             if (Config.autoRoomInfoDrawer) {
0209                 drawerOpen = !modal && drawerUserState
0210                 dim = false
0211             }
0212         }
0213         enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3 && (pageStack.visibleItems.length > 1 || pageStack.currentIndex > 0) && !Kirigami.Settings.isMobile && root.pageStack.wideMode
0214         handleVisible: enabled
0215     }
0216 
0217     Component.onCompleted: {
0218         CustomEmojiModel.connection = root.connection
0219         MatrixImageProvider.connection = root.connection
0220         RoomManager.connection = root.connection
0221         SpaceHierarchyCache.connection = root.connection
0222         WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0223         if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
0224             restoreWindowGeometryConnections.enabled = true; // To restore window size and position
0225         } else {
0226             visible = true;
0227             saveWindowGeometryConnections.enabled = true;
0228         }
0229     }
0230     Connections {
0231         target: Config
0232         function onBlurChanged() {
0233             WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0234         }
0235         function onCompactLayoutChanged() {
0236             WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0237         }
0238     }
0239 
0240     // blur effect
0241     color: Config.blur && !Config.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
0242     
0243     // we need to apply the translucency effect separately on top of the color
0244     background: Rectangle {
0245         color: Config.blur && !Config.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - Config.transparency) : "transparent"
0246     }
0247     
0248     Component {
0249         id: roomListComponent
0250         RoomListPage {
0251             id: roomList
0252 
0253             connection: root.connection
0254 
0255             Shortcut {
0256                 sequences: ["Ctrl+PgUp", "Ctrl+Backtab", "Alt+Up"]
0257                 onActivated: {
0258                     roomList.goToPreviousRoom();
0259                 }
0260             }
0261 
0262             Shortcut {
0263                 sequences: ["Ctrl+PgDown", "Ctrl+Tab", "Alt+Down"]
0264                 onActivated: {
0265                     roomList.goToNextRoom();
0266                 }
0267             }
0268 
0269             Shortcut {
0270                 sequence: "Alt+Shift+Up"
0271                 onActivated: {
0272                     roomList.goToPreviousUnreadRoom();
0273                 }
0274             }
0275 
0276             Shortcut {
0277                 sequence: "Alt+Shift+Down"
0278                 onActivated: {
0279                     roomList.goToNextUnreadRoom();
0280                 }
0281             }
0282         }
0283     }
0284 
0285     Connections {
0286         target: AccountRegistry
0287         function onRowsRemoved() {
0288             if (AccountRegistry.rowCount() === 0) {
0289                 RoomManager.reset();
0290                 pageStack.clear();
0291                 roomListLoaded = false;
0292                 pageStack.push("qrc:/org/kde/neochat/qml/WelcomePage.qml");
0293             }
0294         }
0295     }
0296 
0297     Connections {
0298         target: Controller
0299 
0300         function onErrorOccured(error, detail) {
0301             showPassiveNotification(detail.length > 0 ? i18n("%1: %2", error, detail) : error);
0302         }
0303     }
0304 
0305     Connections {
0306         id: restoreWindowGeometryConnections
0307         enabled: false
0308         target: root
0309 
0310         function onVisibleChanged() {
0311             if (!visible) {
0312                 return;
0313             }
0314             Controller.restoreWindowGeometry(root);
0315             restoreWindowGeometryConnections.enabled = false; // Only restore window geometry for the first time
0316             saveWindowGeometryConnections.enabled = true;
0317         }
0318     }
0319 
0320     Component {
0321         id: keyVerificationDialogComponent
0322         KeyVerificationDialog { }
0323     }
0324 
0325     Connections {
0326         target: root.connection
0327 
0328         function onDirectChatAvailable(directChat) {
0329             RoomManager.enterRoom(root.connection.room(directChat.id));
0330         }
0331         function onNewKeyVerificationSession(session) {
0332             applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
0333                 session: session,
0334             }, {
0335                 title: i18nc("@title:window", "Session Verification")
0336             });
0337         }
0338         function onUserConsentRequired(url) {
0339             let consent = consentSheetComponent.createObject(QQC2.ApplicationWindow.overlay)
0340             consent.url = url
0341             consent.open()
0342         }
0343     }
0344 
0345     Component {
0346         id: consentSheetComponent
0347         Kirigami.OverlaySheet {
0348             id: consentSheet
0349 
0350             property string url: ""
0351 
0352             title: i18n("User consent")
0353 
0354             QQC2.Label {
0355                 id: label
0356 
0357                 text: i18n("Your homeserver requires you to agree to its terms and conditions before being able to use it. Please click the button below to read them.")
0358                 wrapMode: Text.WordWrap
0359                 width: parent.width
0360             }
0361             footer: QQC2.Button {
0362                 text: i18n("Open")
0363                 onClicked: UrlHelper.openUrl(consentSheet.url)
0364             }
0365         }
0366     }
0367 
0368     Component {
0369         id: createRoomDialog
0370 
0371         CreateRoomDialog {
0372             connection: root.connection
0373         }
0374     }
0375 
0376     Component {
0377         id: roomWindow
0378         RoomWindow {}
0379     }
0380 
0381     Component {
0382         id: askDirectChatConfirmationComponent
0383 
0384         Kirigami.OverlaySheet {
0385             id: askDirectChatConfirmation
0386             required property var user;
0387 
0388             parent: QQC2.ApplicationWindow.overlay
0389             title: i18n("Start a chat")
0390             contentItem: QQC2.Label {
0391                 text: i18n("Do you want to start a chat with %1?", user.displayName)
0392                 wrapMode: Text.WordWrap
0393             }
0394             footer: QQC2.DialogButtonBox {
0395                 standardButtons: QQC2.DialogButtonBox.Ok | QQC2.DialogButtonBox.Cancel
0396                 onAccepted: {
0397                     user.requestDirectChat();
0398                     askDirectChatConfirmation.close();
0399                 }
0400                 onRejected: askDirectChatConfirmation.close();
0401             }
0402         }
0403     }
0404 
0405     property Item hoverLinkIndicator: QQC2.Control {
0406         parent: overlay.parent 
0407         property string text
0408         opacity: linkText.text.length > 0 ? 1 : 0
0409 
0410         z: 20
0411         x: 0
0412         y: parent.height - implicitHeight
0413         contentItem: QQC2.Label {
0414             id: linkText
0415             text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
0416         }
0417         Kirigami.Theme.colorSet: Kirigami.Theme.View
0418         background: Rectangle {
0419              color: Kirigami.Theme.backgroundColor
0420         }
0421     }
0422 
0423     Shortcut {
0424         sequence: "Ctrl+Shift+,"
0425         onActivated: {
0426             pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/SettingsPage.qml", {connection: root.connection}, { title: i18n("Configure") })
0427         }
0428     }
0429 }