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() {
0098             WindowController.saveGeometry();
0099         }
0100         function onWidthChanged() {
0101             saveWindowGeometryTimer.restart();
0102         }
0103         function onHeightChanged() {
0104             saveWindowGeometryTimer.restart();
0105         }
0106         function onXChanged() {
0107             saveWindowGeometryTimer.restart();
0108         }
0109         function onYChanged() {
0110             saveWindowGeometryTimer.restart();
0111         }
0112     }
0113 
0114     Loader {
0115         id: quickView
0116         active: !Kirigami.Settings.isMobile
0117         sourceComponent: QuickSwitcher {
0118             connection: root.connection
0119         }
0120     }
0121 
0122     Connections {
0123         target: RoomManager
0124 
0125         function onPushRoom(room, event) {
0126             root.roomPage = pageStack.push("qrc:/org/kde/neochat/qml/RoomPage.qml", {
0127                 connection: root.connection
0128             });
0129             root.roomPage.forceActiveFocus();
0130             if (event.length > 0) {
0131                 roomPage.goToEvent(event);
0132             }
0133         }
0134 
0135         function onPushSpaceHome(room) {
0136             root.spaceHomePage = pageStack.push("qrc:/org/kde/neochat/qml/SpaceHomePage.qml");
0137             root.spaceHomePage.forceActiveFocus();
0138         }
0139 
0140         function onReplaceRoom(room, event) {
0141             if (root.roomPage) {
0142                 pageStack.currentIndex = pageStack.depth - 1;
0143             } else {
0144                 pageStack.pop();
0145                 root.roomPage = pageStack.push("qrc:/org/kde/neochat/qml/RoomPage.qml", {
0146                     connection: root.connection
0147                 });
0148                 root.spaceHomePage = null;
0149             }
0150             root.roomPage.forceActiveFocus();
0151             if (event.length > 0) {
0152                 root.roomPage.goToEvent(event);
0153             }
0154         }
0155 
0156         function onReplaceSpaceHome(room) {
0157             if (root.spaceHomePage) {
0158                 pageStack.currentIndex = pageStack.depth - 1;
0159             } else {
0160                 pageStack.pop();
0161                 root.spaceHomePage = pageStack.push("qrc:/org/kde/neochat/qml/SpaceHomePage.qml");
0162                 root.roomPage = null;
0163             }
0164             root.spaceHomePage.forceActiveFocus();
0165         }
0166 
0167         function goToEvent(event) {
0168             if (event.length > 0) {
0169                 roomItem.goToEvent(event);
0170             }
0171             roomItem.forceActiveFocus();
0172         }
0173 
0174         function onOpenRoomInNewWindow(room) {
0175             const secondaryWindow = roomWindow.createObject(undefined, {
0176                 currentRoom: room,
0177                 connection: root.connection
0178             });
0179             secondaryWindow.width = root.width - pageStack.get(0).width;
0180             secondaryWindow.show();
0181         }
0182 
0183         function onAskDirectChatConfirmation(user) {
0184             askDirectChatConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
0185                 user: user
0186             }).open();
0187         }
0188     }
0189 
0190     function pushReplaceLayer(page, args) {
0191         if (pageStack.layers.depth === 2) {
0192             pageStack.layers.replace(page, args);
0193         } else {
0194             pageStack.layers.push(page, args);
0195         }
0196     }
0197 
0198     function openRoomDrawer() {
0199         pageStack.push("qrc:/org/kde/neochat/qml/RoomDrawerPage.qml", {
0200             connection: root.connection
0201         });
0202     }
0203 
0204     contextDrawer: RoomDrawer {
0205         id: contextDrawer
0206 
0207         // This is a memory for all user initiated actions on the drawer, i.e. clicking the button
0208         // It is used to ensure that user choice is remembered when changing pages and expanding and contracting the window width
0209         property bool drawerUserState: Config.autoRoomInfoDrawer
0210 
0211         connection: root.connection
0212 
0213         // Connect to the onClicked function of the RoomDrawer handle button
0214         Connections {
0215             target: contextDrawer.handle.children[0]
0216             function onClicked() {
0217                 contextDrawer.drawerUserState = contextDrawer.drawerOpen;
0218             }
0219         }
0220 
0221         modal: (!root.wideScreen || !enabled)
0222         onEnabledChanged: drawerOpen = enabled && !modal
0223         onModalChanged: {
0224             if (Config.autoRoomInfoDrawer) {
0225                 drawerOpen = !modal && drawerUserState;
0226                 dim = false;
0227             }
0228         }
0229         enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3 && (pageStack.visibleItems.length > 1 || pageStack.currentIndex > 0) && !Kirigami.Settings.isMobile && root.pageStack.wideMode
0230         handleVisible: enabled
0231     }
0232 
0233     Component.onCompleted: {
0234         CustomEmojiModel.connection = root.connection;
0235         MatrixImageProvider.connection = root.connection;
0236         RoomManager.connection = root.connection;
0237         SpaceHierarchyCache.connection = root.connection;
0238         WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0239         if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
0240             restoreWindowGeometryConnections.enabled = true; // To restore window size and position
0241         } else {
0242             visible = true;
0243             saveWindowGeometryConnections.enabled = true;
0244         }
0245     }
0246     Connections {
0247         target: Config
0248         function onBlurChanged() {
0249             WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0250         }
0251         function onCompactLayoutChanged() {
0252             WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
0253         }
0254     }
0255 
0256     // blur effect
0257     color: Config.blur && !Config.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
0258 
0259     // we need to apply the translucency effect separately on top of the color
0260     background: Rectangle {
0261         color: Config.blur && !Config.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - Config.transparency) : "transparent"
0262     }
0263 
0264     Component {
0265         id: roomListComponent
0266         RoomListPage {
0267             id: roomList
0268 
0269             connection: root.connection
0270 
0271             Shortcut {
0272                 sequences: ["Ctrl+PgUp", "Ctrl+Backtab", "Alt+Up"]
0273                 onActivated: {
0274                     roomList.goToPreviousRoom();
0275                 }
0276             }
0277 
0278             Shortcut {
0279                 sequences: ["Ctrl+PgDown", "Ctrl+Tab", "Alt+Down"]
0280                 onActivated: {
0281                     roomList.goToNextRoom();
0282                 }
0283             }
0284 
0285             Shortcut {
0286                 sequence: "Alt+Shift+Up"
0287                 onActivated: {
0288                     roomList.goToPreviousUnreadRoom();
0289                 }
0290             }
0291 
0292             Shortcut {
0293                 sequence: "Alt+Shift+Down"
0294                 onActivated: {
0295                     roomList.goToNextUnreadRoom();
0296                 }
0297             }
0298         }
0299     }
0300 
0301     Connections {
0302         target: AccountRegistry
0303         function onRowsRemoved() {
0304             if (AccountRegistry.rowCount() === 0) {
0305                 RoomManager.reset();
0306                 pageStack.clear();
0307                 roomListLoaded = false;
0308                 pageStack.push("qrc:/org/kde/neochat/qml/WelcomePage.qml");
0309             }
0310         }
0311     }
0312 
0313     Connections {
0314         target: Controller
0315 
0316         function onErrorOccured(error, detail) {
0317             showPassiveNotification(detail.length > 0 ? i18n("%1: %2", error, detail) : error);
0318         }
0319     }
0320 
0321     Connections {
0322         id: restoreWindowGeometryConnections
0323         enabled: false
0324         target: root
0325 
0326         function onVisibleChanged() {
0327             if (!visible) {
0328                 return;
0329             }
0330             Controller.restoreWindowGeometry(root);
0331             restoreWindowGeometryConnections.enabled = false; // Only restore window geometry for the first time
0332             saveWindowGeometryConnections.enabled = true;
0333         }
0334     }
0335 
0336     Component {
0337         id: keyVerificationDialogComponent
0338         KeyVerificationDialog {}
0339     }
0340 
0341     Connections {
0342         target: root.connection
0343 
0344         function onDirectChatAvailable(directChat) {
0345             RoomManager.resolveResource(directChat.id);
0346         }
0347         function onNewKeyVerificationSession(session) {
0348             applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
0349                 session: session
0350             }, {
0351                 title: i18nc("@title:window", "Session Verification")
0352             });
0353         }
0354         function onUserConsentRequired(url) {
0355             let consent = consentSheetComponent.createObject(QQC2.ApplicationWindow.overlay);
0356             consent.url = url;
0357             consent.open();
0358         }
0359     }
0360 
0361     Component {
0362         id: consentSheetComponent
0363         Kirigami.OverlaySheet {
0364             id: consentSheet
0365 
0366             property string url: ""
0367 
0368             title: i18n("User consent")
0369 
0370             QQC2.Label {
0371                 id: label
0372 
0373                 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.")
0374                 wrapMode: Text.WordWrap
0375                 width: parent.width
0376             }
0377             footer: QQC2.Button {
0378                 text: i18n("Open")
0379                 onClicked: UrlHelper.openUrl(consentSheet.url)
0380             }
0381         }
0382     }
0383 
0384     Component {
0385         id: createRoomDialog
0386 
0387         CreateRoomDialog {
0388             connection: root.connection
0389         }
0390     }
0391 
0392     Component {
0393         id: roomWindow
0394         RoomWindow {}
0395     }
0396 
0397     Component {
0398         id: askDirectChatConfirmationComponent
0399 
0400         Kirigami.OverlaySheet {
0401             id: askDirectChatConfirmation
0402             required property var user
0403 
0404             parent: QQC2.ApplicationWindow.overlay
0405             title: i18n("Start a chat")
0406             contentItem: QQC2.Label {
0407                 text: i18n("Do you want to start a chat with %1?", user.displayName)
0408                 wrapMode: Text.WordWrap
0409             }
0410             footer: QQC2.DialogButtonBox {
0411                 standardButtons: QQC2.DialogButtonBox.Ok | QQC2.DialogButtonBox.Cancel
0412                 onAccepted: {
0413                     user.requestDirectChat();
0414                     askDirectChatConfirmation.close();
0415                 }
0416                 onRejected: askDirectChatConfirmation.close()
0417             }
0418         }
0419     }
0420 
0421     property Item hoverLinkIndicator: QQC2.Control {
0422         parent: overlay.parent
0423         property string text
0424         opacity: linkText.text.length > 0 ? 1 : 0
0425 
0426         z: 20
0427         x: 0
0428         y: parent.height - implicitHeight
0429         contentItem: QQC2.Label {
0430             id: linkText
0431             text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
0432         }
0433         Kirigami.Theme.colorSet: Kirigami.Theme.View
0434         background: Rectangle {
0435             color: Kirigami.Theme.backgroundColor
0436         }
0437     }
0438 
0439     Shortcut {
0440         sequence: "Ctrl+Shift+,"
0441         onActivated: {
0442             pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/SettingsPage.qml", {
0443                 connection: root.connection
0444             }, {
0445                 title: i18n("Configure")
0446             });
0447         }
0448     }
0449 }