diff --git a/src/apps/qml/Desktop.qml b/src/apps/qml/Desktop.qml index 0aa47d48..01b18f4b 100644 --- a/src/apps/qml/Desktop.qml +++ b/src/apps/qml/Desktop.qml @@ -1,510 +1,528 @@ /* * Copyright 2016 Riccardo Iaconelli * Copyright (c) 2017-2019 Montel Laurent * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.5 as QQC2 import KDE.Ruqola.RuqolaUtils 1.0 import KDE.Ruqola.Ruqola 1.0 import KDE.Ruqola.DDPClient 1.0 import KDE.Ruqola.RoomFilterProxyModel 1.0 import org.kde.kirigami 2.7 as Kirigami import KDE.Ruqola.DebugCategory 1.0 import KDE.Ruqola.Channel 1.0 import KDE.Ruqola.AccountManager 1.0 Kirigami.ApplicationWindow { id: appid readonly property int margin: 11 property string selectedRoomID: ""; property QtObject selectedRoom property QtObject messageModel property QtObject userModel property QtObject rocketChatAccount: accountManager.currentAccount() property QtObject filesModel: appid.rocketChatAccount.filesForRoomFilterProxyModel() property QtObject discussionsModel: appid.rocketChatAccount.discussionsFilterProxyModel() property QtObject mentionsModel: appid.rocketChatAccount.mentionsFilterProxyModel() property QtObject accountManager: Ruqola.accountManager() property QtObject accountManagerModel: accountManager.rocketChatAccountModel() property QtObject searchMessageModel: rocketChatAccount.searchMessageFilterProxyModel() property QtObject emojiModel: rocketChatAccount.emoticonModel() property QtObject threadsModel: rocketChatAccount.threadsFilterProxyModel() property QtObject threadMessagesModel: rocketChatAccount.threadMessageModel() property QtObject pinnedMessagesModel: rocketChatAccount.pinnedMessagesFilterProxyModel() property QtObject autotranslateLanguagesModel: rocketChatAccount.autoTranslateLanguagesModel() property string userInputMessageText: ""; width: Kirigami.Units.gridUnit * 55 height: Kirigami.Units.gridUnit * 40 title: i18n("Ruqola") function switchToRoom(roomID) { if (roomID === selectedRoomID) { return; } appid.rocketChatAccount.switchingToRoom(roomID) appid.rocketChatAccount.setUserCurrentMessage(appid.userInputMessageText, selectedRoomID) appid.selectedRoomID = roomID; appid.messageModel = appid.rocketChatAccount.messageModelForRoom(roomID) appid.selectedRoom = appid.rocketChatAccount.getRoomWrapper(roomID) appid.userModel = appid.rocketChatAccount.usersForRoomFilterProxyModel(roomID) } pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.None pageStack.initialPage: [roomsComponent, mainComponent] pageStack.visible: rocketChatAccount.loginStatus === DDPClient.LoggedIn globalDrawer: Kirigami.GlobalDrawer { drawerOpen: false handleVisible: true resetMenuOnTriggered: true topContent: [ QQC2.Label { text: rocketChatAccount.userName === "" ? "" : i18n("Hello, %1", rocketChatAccount.userName) textFormat: Text.PlainText } ] actions: [ Kirigami.Action { text: i18n("About") iconName: ":/icons/systray.png" onTriggered: { aboutDataDialogLoader.active = true } }, Kirigami.Action { text: i18n("Report a Bug") iconName: "tools-report-bug" onTriggered: { Qt.openUrlExternally("https://bugs.kde.org/report.cgi"); } }, Kirigami.Action { text: i18n("Configure Account") iconName: "settings-configure" onTriggered: { configureServerList.visible = true pageStack.visible = false } }, Kirigami.Action { text: i18n("Handbook") iconName: "system-help" onTriggered: { rocketChatAccount.openDocumentation(); } }, Kirigami.Action { separator: true }, Kirigami.Action { text: i18n("Log out") iconName: "system-log-out" onTriggered: { rocketChatAccount.logOut(); appid.globalDrawer.drawerOpen = false; } }, // Kirigami.Action { // text: i18n("autotranslate") // onTriggered: { // rocketChatAccount.getSupportedLanguages(); // } // }, Kirigami.Action { separator: true }, Kirigami.Action { shortcut: StandardKey.Quit text: i18n("Quit") iconName: "application-exit" onTriggered: { Qt.quit(); } } ] } ConfigureServerList { id: configureServerList accountModel: accountManagerModel visible: false onCloseConfigureServer: { configureServerList.visible = false pageStack.visible = true } } LoginPage { id: loginTab rcAccount: rocketChatAccount } Loader { id: aboutDataDialogLoader active: false sourceComponent: AboutDialog { id: aboutDataDialog parent: appid.pageStack applicationData: Ruqola.applicationData() Component.onCompleted: { open() } onOpenurl: { RuqolaUtils.openUrl(link); } onRejected: { aboutDataDialogLoader.active = false } } } + Loader { + id: customUserStatusDialogLoader + active: false + sourceComponent: CustomUserStatusDialog { + id: customUserStatusDialog + parent: appid.pageStack + onRejected: { + customUserStatusDialogLoader.active = false + } + onAccepted: { + customUserStatusDialogLoader.active = false + } + Component.onCompleted: { + open() + } + } + } + Loader { id: privateChannelInfoDialogLoader active: false sourceComponent: PrivateChannelInfoDialog { id: privateChannelInfoDialog roomInfo: appid.selectedRoom parent: appid.pageStack onBlockUser: { rocketChatAccount.blockUser(rid, block) } onRejected: { privateChannelInfoDialogLoader.active = false } onAccepted: { privateChannelInfoDialogLoader.active = false } Component.onCompleted: { initializeAndOpen() } } } Loader { id: notificationsDialogLoader active: false sourceComponent: NotificationOptionsDialog { id: notificationsDialog parent: appid.pageStack onModifyNotificationsSetting: { rocketChatAccount.changeNotificationsSettings(roomId, type, newVal) } Component.onCompleted: { rid = (appid && appid.selectedRoomID) ? appid.selectedRoomID : "" roomInfo = appid ? appid.selectedRoom : "" initializeAndOpen() } onRejected: { notificationsDialogLoader.active = false } onAccepted: { notificationsDialogLoader.active = false } } } Loader { id: channelInfoDialogLoader active: false sourceComponent: ChannelInfoDialog { id: channelInfoDialog parent: appid.pageStack roomInfo: appid.selectedRoom channelName: (appid && appid.selectedRoomID) ? appid.selectedRoomID : "" onDeleteRoom: { rocketChatAccount.eraseRoom(roomId, appid.selectedRoom.channelType) } onModifyChannelSetting: { rocketChatAccount.changeChannelSettings(roomId, type, newVal, channelType) } onRejected: { channelInfoDialogLoader.active = false } onAccepted: { channelInfoDialogLoader.active = false } Component.onCompleted: { initializeAndOpen() } } } Loader { id: leaveChannelDialogLoader active: false property string rid property string roomType sourceComponent: LeaveChannelDialog { id: leaveChannelDialog parent: appid.pageStack onLeaveChannel: { rocketChatAccount.leaveRoom(roomId, channelType) } onRejected: { leaveChannelDialogLoader.active = false } onAccepted: { leaveChannelDialogLoader.active = false } Component.onCompleted: { leaveChannelDialog.rId = leaveChannelDialogLoader.rid leaveChannelDialog.channelType = leaveChannelDialogLoader.roomType open() } } } Loader { id: addUserDialogLoader active: false sourceComponent: AddUserDialog { id: addUserDialog parent: appid.pageStack completerModel: rocketChatAccount.userCompleterFilterModelProxy() roomInfo: appid.selectedRoom roomId: appid.selectedRoomID onSearchUserName: { rocketChatAccount.userAutocomplete(pattern, ""); } onAddUser: { rocketChatAccount.addUserToRoom(userId, rid, channelType) } onRejected: { addUserDialogLoader.active = false } onAccepted: { addUserDialogLoader.active = false } Component.onCompleted: { initializeAndOpen() } } } Loader { id: searchMessageDialogLoader active: false sourceComponent: ShowSearchMessageDialog { id: searchMessageDialog roomId: appid.selectedRoomID parent: appid.pageStack searchMessageModel: appid.searchMessageModel onSearchMessage: { rocketChatAccount.messageSearch(pattern, rid) } onGoToMessage: { console.log(RuqolaDebugCategorySingleton.category, "Show history to message: " + messageId) } onRejected: { rocketChatAccount.clearSearchModel() searchMessageDialogLoader.active = false } onAccepted: { rocketChatAccount.clearSearchModel() searchMessageDialogLoader.active = false } Component.onCompleted: { roomId = appid.selectedRoomID initializeAndOpen() } } } Loader { id: jobErrorMessageDialogLoader property string jobMessageError active: false sourceComponent: JobErrorMessageDialog { id: jobErrorMessageDialog parent: appid.pageStack Component.onCompleted: { jobErrorMessageDialog.jobMessageError = jobErrorMessageDialogLoader.jobMessageError open() } onRejected: { jobErrorMessageDialogLoader.active = false } onAccepted: { jobErrorMessageDialogLoader.active = false } } } Loader { id: createNewChannelDialogLoader active: false sourceComponent: CreateNewChannelDialog { parent: appid.pageStack Component.onCompleted: { encryptedRoomEnabled = appid.rocketChatAccount.encryptedEnabled() initializeAndOpen() } onRejected: { createNewChannelDialogLoader.active = false } onAccepted: { createNewChannelDialogLoader.active = false } onCreateNewChannel: { rocketChatAccount.createNewChannel(name, readOnly, privateRoom, usernames, encryptedRoom, password, broadcast); } } } Loader { id: serverinfodialogLoader active: false sourceComponent: ServerInfoDialog { rcAccount: appid.rocketChatAccount parent: appid.pageStack Component.onCompleted: { open() } onRejected: { serverinfodialogLoader.active = false } } } SearchChannelDialog { id: searchChannelDialog searchChannelModel: rocketChatAccount.searchChannelFilterProxyModel() onSearchChannel: { rocketChatAccount.channelAndPrivateAutocomplete(pattern); } onOpenChannel: { if (channeltype === Channel.Room) { rocketChatAccount.openChannel(channelid) } else if (channeltype === Channel.PrivateChannel) { if (rocketChatAccount.userName !== channelid) { rocketChatAccount.openDirectChannel(channelid) } } else { console.log(RuqolaDebugCategorySingleton.category, "Unknown open channel type : " + channeltype + " channelid : " + channelid + " channelname : " + channelname) } } } Loader { id: takeVideoMessageLoader active: false sourceComponent: TakeVideoMessageDialog { id: takeVideoMessage parent: appid.pageStack rcAccount: rocketChatAccount Component.onCompleted: { open() } onRejected: { takeVideoMessageLoader.active = false } onAccepted: { takeVideoMessageLoader.active = false } } } QQC2.BusyIndicator { id: busy anchors.centerIn: parent visible: rocketChatAccount.loginStatus === DDPClient.LoggingIn } RoomsComponent { id: roomsComponent } MainComponent { id: mainComponent } Loader { id: channelPasswordDialogLoader active: false property string roomId sourceComponent: ChannelPasswordDialog { id: channelPasswordDialog parent: appid.pageStack onJoinRoom: { rocketChatAccount.joinRoom(roomId, password) } Component.onCompleted: { roomId = channelPasswordDialogLoader.roomId initializeAndOpen() } onRejected: { channelPasswordDialogLoader.active = false } onAccepted: { channelPasswordDialogLoader.active = false } } } Connections { target: rocketChatAccount onMissingChannelPassword: { channelPasswordDialogLoader.roomId = roomId channelPasswordDialogLoader.active = true } onJobFailed: { jobErrorMessageDialogLoader.jobMessageError = message jobErrorMessageDialogLoader.active = true } } onClosing: { Qt.quit(); } function toggleShow() { if (visible) { hide(); } else { show(); raise(); requestActivate(); } } Component.onCompleted: { systrayIcon.activateRequested.connect(toggleShow); } } diff --git a/src/apps/qml/RoomsComponent.qml b/src/apps/qml/RoomsComponent.qml index fba042fa..747c4248 100644 --- a/src/apps/qml/RoomsComponent.qml +++ b/src/apps/qml/RoomsComponent.qml @@ -1,184 +1,145 @@ /* * Copyright 2016 Riccardo Iaconelli * Copyright (C) 2017-2019 Laurent Montel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.5 as QQC2 import KDE.Ruqola.RocketChatAccount 1.0 import KDE.Ruqola.Ruqola 1.0 import KDE.Ruqola.RoomFilterProxyModel 1.0 import KDE.Ruqola.UsersForRoomFilterProxyModel 1.0 import org.kde.kirigami 2.7 as Kirigami import KDE.Ruqola.StatusModel 1.0 - +import "common" Component { id: roomsComponent Kirigami.ScrollablePage { id: roomsPage title: i18n("Rooms") actions { contextualActions: [ Kirigami.Action { id: editAction iconName: "list-add" text: i18n("Open room"); onTriggered: { searchChannelDialog.initializeAndOpen(); } }, Kirigami.Action { iconName: "edit-symbolic" text: i18n("Unread on Top"); checkable: true checked: appid.rocketChatAccount.sortUnreadOnTop; onToggled: { appid.rocketChatAccount.setSortUnreadOnTop(checked); } }, Kirigami.Action { iconName: "edit-symbolic" text: i18n("Show Close Icons"); checkable: true onToggled: { appid.rocketChatAccount.switchEditingMode(checked); } }, Kirigami.Action { text: i18n("Create New Channel") onTriggered: { createNewChannelDialogLoader.active = true; } }, Kirigami.Action { separator: true }, Kirigami.Action { text: i18n("Server Info") onTriggered: { serverinfodialogLoader.active = true; } } ] } // Since we can't have actions at the bottom on mobile, force always toolbar mode globalToolBarStyle: Kirigami.ApplicationHeaderStyle.ToolBar titleDelegate: LineEditWithClearButton { id: searchField placeholderText: i18n("Search Room...") Layout.fillWidth: true onTextChanged: { appid.rocketChatAccount.roomFilterProxyModel().setFilterString(text); } focusSequence: "Ctrl+K" } background: Rectangle { color: Kirigami.Theme.backgroundColor } footer: QQC2.ToolBar { Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.Window position: QQC2.ToolBar.Footer RowLayout { anchors { verticalCenter: parent.verticalCenter left: parent.left } QQC2.Label { id: comboboxLabel textFormat: Text.PlainText text: i18n("Status:") } - QQC2.ComboBox { + StatusCombobox { id: statusCombobox - Layout.alignment: Qt.AlignLeft - model: appid.rocketChatAccount.statusModel() - //textRole is removed as a workaround for now to draw our own text - //textRole: "statusi18n" - property variant icon - property string text - onActivated: { - appid.rocketChatAccount.changeDefaultStatus(index, "") - } - currentIndex: model.currentStatus - - delegate: Kirigami.BasicListItem { - property bool current: index === statusCombobox.currentIndex - separatorVisible: false - onCurrentChanged: { - if (current) { - statusCombobox.text = model.statusi18n - statusCombobox.icon = model.icon - } - } - icon: model.icon - label: model.statusi18n - } - //FIXME: QQC2 combobox really, really needs icons support - contentItem: RowLayout { - Kirigami.Icon { - Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium - Layout.fillHeight: true - source: statusCombobox.icon - } - QQC2.Label { - text: statusCombobox.text - verticalAlignment: Text.AlignVCenter - Layout.fillHeight: true - } - Item { - Layout.fillWidth: true - } - } } } } mainItem: RoomsView { id: roomsList editingMode: appid.rocketChatAccount.editingMode implicitWidth: Kirigami.Units.gridUnit * 10 anchors.fill: parent model: appid.rocketChatAccount.roomFilterProxyModel() selectedRoomID: appid.selectedRoomID; onHideRoom: { rocketChatAccount.hideRoom(roomID, roomType) } onLeaveRoom: { //TODO move to desktop.qml leaveChannelDialogLoader.rid = roomID leaveChannelDialogLoader.roomType = roomType leaveChannelDialogLoader.active = true } onRoomSelected: { appid.switchToRoom(roomID) } } //RoomsView } } diff --git a/src/apps/qml/common/StatusCombobox.qml b/src/apps/qml/common/StatusCombobox.qml new file mode 100644 index 00000000..3b144803 --- /dev/null +++ b/src/apps/qml/common/StatusCombobox.qml @@ -0,0 +1,70 @@ +/* + Copyright (c) 2019 Montel Laurent + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.9 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.5 as QQC2 +import KDE.Ruqola.RocketChatAccount 1.0 +import org.kde.kirigami 2.7 as Kirigami +import KDE.Ruqola.StatusModel 1.0 + +QQC2.ComboBox { + id: statusCombobox + Layout.alignment: Qt.AlignLeft + model: appid.rocketChatAccount.statusModel() + //textRole is removed as a workaround for now to draw our own text + //textRole: "statusi18n" + property variant icon + property string text + onActivated: { + appid.rocketChatAccount.changeDefaultStatus(index, "") + } + currentIndex: model.currentStatus + + delegate: Kirigami.BasicListItem { + property bool current: index === statusCombobox.currentIndex + separatorVisible: false + onCurrentChanged: { + if (current) { + statusCombobox.text = model.statusi18n + statusCombobox.icon = model.icon + } + } + icon: model.icon + label: model.statusi18n + } + //FIXME: QQC2 combobox really, really needs icons support + contentItem: RowLayout { + Kirigami.Icon { + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.fillHeight: true + source: statusCombobox.icon + } + QQC2.Label { + text: statusCombobox.text + verticalAlignment: Text.AlignVCenter + Layout.fillHeight: true + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/src/apps/qml/qml.qrc b/src/apps/qml/qml.qrc index 4c0b7369..e238d4d6 100644 --- a/src/apps/qml/qml.qrc +++ b/src/apps/qml/qml.qrc @@ -1,98 +1,99 @@ Desktop.qml FancyMessageDelegate.qml RoomsView.qml RoomDelegate.qml Login.qml UserInput.qml LoginPage.qml ExtraColors.qml ActiveChat.qml AboutDialog.qml PersonsListView.qml LicenseDialog.qml CreateNewChannelDialog.qml OpenDirectChannelDialog.qml ChannelInfoDialog.qml DeleteMessageDialog.qml AddUserDialog.qml DeleteRoomDialog.qml TakeVideoMessageDialog.qml TextFieldEditor.qml DebugCategory.qml ConfigureServerList.qml MainComponent.qml RoomsComponent.qml ArchiveRoomDialog.qml DownloadFileDialog.qml UploadFileDialog.qml DisplayImageDialog.qml EncryptedConversationDialog.qml SearchChannelDialog.qml ShowFilesInRoomDialog.qml MessageLine.qml ShowSearchMessageDialog.qml DeleteAccountDialog.qml OpenChannelDialog.qml PrivateChannelInfoDialog.qml PasswordLineEdit.qml NotificationOptionsDialog.qml LeaveChannelDialog.qml DeleteFileAttachmentDialog.qml UserInfoDialog.qml CreateNewAccountDialog.qml ChannelPasswordDialog.qml ServerInfoDialog.qml NotificationAlertCombobox.qml ShowMentionsInRoomDialog.qml UserMenu.qml JobErrorMessageDialog.qml LineEditWithClearButton.qml ReportMessageDialog.qml CreateDiscussionDialog.qml ShowDiscussionsInRoomDialog.qml ShowThreadsInRoomDialog.qml ShowThreadMessagesDialog.qml DiscussionMessageDelegate.qml DiscussionMessage.qml FilesInRoomDelegate.qml FileInRoom.qml SearchLabel.qml ShowPinnedMessagesDialog.qml AutoTranslateLanguageCombobox.qml AutoTranslateConfigDialog.qml CustomUserStatusDialog.qml messages/SystemMessage.qml messages/UserMessage.qml messages/AttachmentMessageAudio.qml messages/AttachmentMessageImage.qml messages/AttachmentMessageVideo.qml messages/JitsiVideoMessage.qml messages/NewDateLabel.qml messages/MessageBase.qml messages/MessageMenu.qml messages/TimestampText.qml messages/ShowHideButton.qml messages/RepeaterReactions.qml messages/DiscussionLabel.qml messages/ThreadLabel.qml messages/ReactionsPopup.qml common/DownloadButton.qml common/AvatarImage.qml common/DeleteButton.qml common/EmoticonMenu.qml + common/StatusCombobox.qml icons/systray.png icons/attach-button.jpg js/message.js js/convert.js