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