diff --git a/src/app/LayoutManager.qml b/src/app/LayoutManager.qml index 774389766e9de3a46cfc28c0489bd657c43801f0..cb2b7f7d9a7d65181fccbcb28a41db25529d437b 100644 --- a/src/app/LayoutManager.qml +++ b/src/app/LayoutManager.qml @@ -126,7 +126,7 @@ QtObject { // Adds an item to the fullscreen item stack. Automatically puts // the main window in fullscreen mode if needed. Callbacks should be used // to perform component-specific tasks upon successful transitions. - function pushFullScreenItem(item, originalParent, pushedCb, removedCb) { + function pushFullScreenItem(item, prevParent, pushedCb, removedCb) { if (item === null || item === undefined || priv.fullScreenItems.length >= 3) { return @@ -138,10 +138,12 @@ QtObject { // Add the item to our list and reparent it to appContainer. priv.fullScreenItems.push({ "item": item, - "originalParent": originalParent, + "prevParent": prevParent, + "prevAnchorsFill": item.anchors.fill, "removedCb": removedCb }) item.parent = appContainer + item.anchors.fill = item.parent if (pushedCb) { pushedCb() } @@ -164,7 +166,8 @@ QtObject { } if (obj !== undefined) { if (obj.item !== appWindow) { - obj.item.parent = obj.originalParent + obj.item.anchors.fill = obj.prevAnchorsFill + obj.item.parent = obj.prevParent if (obj.removedCb) { obj.removedCb() } diff --git a/src/app/MainApplicationWindow.qml b/src/app/MainApplicationWindow.qml index a64ea58265fdcd654b9a1700f38dcab94a55ea88..74bba482895f603d16d9b514db75dcba6b0e4896 100644 --- a/src/app/MainApplicationWindow.qml +++ b/src/app/MainApplicationWindow.qml @@ -51,6 +51,16 @@ ApplicationWindow { property LayoutManager layoutManager: LayoutManager { appContainer: appContainer } + property ViewCoordinator viewCoordinator: ViewCoordinator { + resources: { + "WelcomePage": "mainview/components/WelcomePage.qml", + "SidePanel": "mainview/components/SidePanel.qml", + "ConversationView": "mainview/ConversationView.qml", + "NewSwarmPage": "mainview/components/NewSwarmPage.qml", + "WizardView": "wizardview/WizardView.qml", + "SettingsView": "settingsview/SettingsView.qml", + } + } property bool windowSettingsLoaded: false property bool allowVisibleWindow: true @@ -85,6 +95,7 @@ ApplicationWindow { !UtilsAdapter.getAccountListSize()) { // Save the window geometry and state before quitting. layoutManager.saveWindowSettings() + viewCoordinator.dismissAll() Qt.quit() } else { layoutManager.closeToTray() @@ -105,10 +116,6 @@ ApplicationWindow { anchors.fill: parent } - DaemonReconnectPopup { - id: daemonReconnectPopup - } - Loader { id: mainApplicationLoader @@ -117,7 +124,14 @@ ApplicationWindow { asynchronous: true visible: status == Loader.Ready - source: "" + + Connections { + target: viewCoordinator + + function onRequestAppWindowWizardView() { + mainApplicationLoader.setSource(JamiQmlUtils.wizardViewLoadPath) + } + } Connections { target: mainApplicationLoader.item @@ -149,6 +163,13 @@ ApplicationWindow { // Main window, load any valid app settings, and allow the // layoutManager to handle as much as possible. layoutManager.restoreWindowSettings() + + // Present the welcome view once the viewCoordinator is setup. + viewCoordinator.initialized.connect(function() { + viewCoordinator.present("WelcomePage") + }) + // Set the viewCoordinator's root item. + viewCoordinator.setRootView(item) } if (Qt.platform.os.toString() === "osx") { MainApplication.setEventFilter() @@ -206,14 +227,11 @@ ApplicationWindow { ignoreUnknownSignals: true function onShowDaemonReconnectPopup(visible) { - if (visible) - daemonReconnectPopup.open() - else - daemonReconnectPopup.close() - } - - function onDaemonReconnectFailed() { - daemonReconnectPopup.connectionFailed = true + if (visible) { + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/DaemonReconnectPopup.qml") + } } } diff --git a/src/app/ViewCoordinator.qml b/src/app/ViewCoordinator.qml new file mode 100644 index 0000000000000000000000000000000000000000..d924ccf9da16b34a6298a701907e2f3d906675c5 --- /dev/null +++ b/src/app/ViewCoordinator.qml @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 <https://www.gnu.org/licenses/>. + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import net.jami.Constants 1.1 +import net.jami.Models 1.1 + +// This object should be implemented as a QML singleton, or be instantiated +// once in the main application window component. The top-level application window +// contains a loader[mainview, wizardview] and "rootView" MUST parent a horizontal +// SplitView with a StackView in each pane. +QtObject { + id: root + + signal requestAppWindowWizardView + + // A map of view names to file paths for QML files that define each view. + required property variant resources + + // Maybe this state needs to be toggled because the SidePanel content is replaced. + // This makes it so the state can't be inferred from loaded views in single pane mode. + property bool inSettings: viewManager.hasView("SettingsView") + property bool inNewSwarm: viewManager.hasView("NewSwarmPage") + + property bool busy: false + + // The `main` view of the application window. + property Item rootView: null + + // HACKS. + property real mainViewWidth: rootView ? rootView.width : 0 + property real previousWidth: mainViewWidth + property real mainViewSidePanelRectWidth: sv1 ? sv1.width : 0 + property real lastSideBarSplitSize: mainViewSidePanelRectWidth + onMainViewWidthChanged: resolvePanes() + + function resolvePanes(force=false) { + if (forceSinglePane) return + const isExpanding = previousWidth < mainViewWidth + if (mainViewWidth < JamiTheme.chatViewHeaderMinimumWidth + mainViewSidePanelRectWidth + && sv2.visible && (!isExpanding || force)) { + lastSideBarSplitSize = mainViewSidePanelRectWidth + singlePane = true + } else if (mainViewWidth >= lastSideBarSplitSize + JamiTheme.chatViewHeaderMinimumWidth + && !sv2.visible && (isExpanding || force) && !layoutManager.isFullScreen) { + singlePane = false + } + previousWidth = mainViewWidth + } + + // Must be the child of `rootView`. + property Item splitView: null + + // StackView objects, which are children of `splitView`. + property StackView sv1: null + property StackView sv2: null + + // The StackView object that is currently active, determined by the value + // of singlePane. + readonly property StackView activeStackView: singlePane ? sv1 : sv2 + + readonly property string currentViewName: { + if (activeStackView == null || activeStackView.depth === 0) return '' + return activeStackView.currentItem.objectName + } + + readonly property var currentView: { + return activeStackView ? activeStackView.currentItem : null + } + + // Handle single/dual pane mode. + property bool forceSinglePane: false + property bool singlePane + onForceSinglePaneChanged: { + if (forceSinglePane) singlePane = true + else resolvePanes(true) + } + + onSinglePaneChanged: { + // Hiding sv2 before moving items from, and after moving + // items to, reduces stack item visibility change events. + if (singlePane) { + sv2.visible = false + if (forceSinglePane) Qt.callLater(move, sv2, sv1) + else move(sv2, sv1) + } else { + move(sv1, sv2) + sv2.visible = true + } + } + + // Emitted once at the end of setRootView. + signal initialized() + + // Create, present, and return a dialog object. + function presentDialog(parent, path, props={}) { + // Open the dialog once the object is created + return viewManager.createView(path, parent, function(obj) { + const doneCb = function() { viewManager.destroyView(path) } + if (obj.closed !== undefined) { + obj.closed.connect(doneCb) + } else { + if (obj.accepted !== undefined) { obj.accepted.connect(doneCb) } + if (obj.rejected !== undefined) { obj.rejected.connect(doneCb) } + } + obj.open() + }, props) + } + + // Dismiss all views. + function dismissAll() { + for (var path in viewManager.views) { + viewManager.destroyView(path) + } + } + + // Get a view regardless of whether it is currently active. + function getView(viewName) { + if (!viewManager.hasView(viewName)) { + return null + } + return viewManager.views[viewManager.viewPaths[viewName]] + } + + // A private object used to manage the lifecycle of views. + property QtObject viewManager: QtObject { + id: viewManager + + // A map of path strings to view objects. + property variant views: ({}) + // A map of view names to path strings. + property variant viewPaths: ({}) + // The number of views. + property int nViews: 0 + + function createView(path, parent=null, cb=null, props={}) { + if (views[path] !== undefined) { + // an instance of <path> already exists + return views[path] + } + + const component = Qt.createComponent(Qt.resolvedUrl(path)) + if (component.status === Component.Ready) { + const obj = component.createObject(parent, props) + if (obj === null) { + print("error creating object") + return null + } + views[path] = obj + // Set the view name to the object name if it has one. + const viewName = obj.objectName.toString() !== '' ? + obj.objectName : + path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "") + viewPaths[viewName] = path + nViews = Object.keys(views).length + if (cb !== null) { + cb(obj) + } + return views[path] + } + print("error creating component", path) + console.error(component.errorString()) + Qt.exit(1) + return null + } + + function destroyView(path) { + if (views[path] === undefined) { + print(path, "instance does not exist", Object.keys(views)) + return false + } + views[path].destroy() + views[path] = undefined + // QObject::destroy is queued, and we can't connect to its completion, + // so we queue the resulting mutation to our view storage. + Qt.callLater(function() { + delete views[path] + // Remove the view name from the viewPaths map. + for (var viewName in viewPaths) { + if (viewPaths[viewName] === path) { + delete viewPaths[viewName] + break + } + } + nViews = Object.keys(views).length + }) + return true + } + + function hasView(viewName) { + return nViews && viewPaths[viewName] !== undefined + } + } + + // Sets object references, onInitialized is a good time to present views. + function setRootView(obj) { + rootView = obj + splitView = rootView.splitView + sv1 = rootView.sv1 + sv1.parent = Qt.binding(() => singlePane ? rootView : splitView) + sv1.anchors.fill = Qt.binding(() => singlePane ? rootView : undefined) + sv2 = rootView.sv2 + + initialized() + resolvePanes() + } + + // Finds a view and gets its index within the StackView it's in. + function getStackIndex(viewName) { + for (const [key, value] of Object.entries(viewManager.views)) { + if (value.objectName === viewName) { + return value.StackView.index + } + } + return -1 + } + + // This function presents the view with the given viewName in the + // specified StackView. Return the view if successful. + function present(viewName, sv=activeStackView) { + if (!rootView) return + + // If the view already exists in the StackView, the function will attempt + // to navigate to its StackView position by dismissing elevated views. + if (sv.find(function(item) { + return item.objectName === viewName; + })) { + const viewIndex = getStackIndex(viewName) + if (viewIndex >= 0) { + for (var i = (sv.depth - 1); i > viewIndex; i--) { + dismissObj(sv.get(i, StackView.DontLoad)) + } + return true + } + return false + } + + // If we are in single-pane mode and the view was previously forced into + // sv2, we can move it back to the top of sv1. + if (singlePane && sv === sv1) { + // See if the item is at the top of sv2 + if (sv2.currentItem && sv2.currentItem.objectName === viewName) { + // Move it to the top of sv1 + const view = sv2.pop(StackView.Immediate) + sv1.push(view, StackView.Immediate) + view.presented() + return view + } + } + + const obj = viewManager.createView(resources[viewName], appWindow) + if (!obj) { + print("could not create view:", viewName) + return null + } + if (obj === currentView) { + print("view is current:", viewName) + return null + } + + // If we are in single-pane mode and the view should start hidden + // (requiresIndex), we can push it into sv2. + if (singlePane && sv === sv1 && obj.requiresIndex) { + sv = sv2 + } else { + forceSinglePane = obj.singlePaneOnly + } + + const view = sv.push(obj, StackView.Immediate) + if (!view) { + return null + } + if (view.objectName === '') { + view.objectName = viewName + } + view.presented() + return view + } + + // Dismiss by object. + function dismissObj(obj, sv=activeStackView) { + if (obj.StackView.view !== sv) { + print("view not in the stack:", obj) + return + } + + // If we are dismissing a view that is not at the top of the stack, + // we need to store each of the views on top into a temporary stack + // and then restore them after the view is dismissed. + // So we get the index of the view we are dismissing. + const viewIndex = obj.StackView.index + var tempStack = [] + for (var i = (sv.depth - 1); i > viewIndex; i--) { + var item = sv.pop(StackView.Immediate) + tempStack.push(item) + } + // And we define a function to restore and resolve the views. + var resolveStack = () => { + for (var i = 0; i < tempStack.length; i++) { + sv.push(tempStack[i], StackView.Immediate) + } + + forceSinglePane = sv.currentItem.singlePaneOnly + sv.currentItem.presented() + } + + // Now we can dismiss the view at the top of the stack. + const depth = sv.depth + if (obj === sv.get(depth - 1, StackView.DontLoad)) { + var view = sv.pop(StackView.Immediate) + if (!view) { + print("could not pop view:", obj.objectName) + resolveStack() + return + } + + // If the view is managed, we can destroy it, otherwise, it can + // be reused and destroyed by it's parent. + if (view.managed) { + var objectName = view ? view.objectName : obj.objectName + if (!viewManager.destroyView(resources[objectName])) { + print("could not destroy view:", objectName) + } + } else { + view.dismissed() + } + } + resolveStack() + } + + // Dismiss by view name. + function dismiss(viewName) { + const depth = activeStackView.depth + for (var i = (depth - 1); i >= 0; i--) { + const view = activeStackView.get(i, StackView.DontLoad) + if (view.objectName === viewName) { + dismissObj(view) + return + } + } + + // Check if the view is hidden on the top of sv2 (if in single-pane mode), + // and dismiss it in that case. + if (singlePane && sv2.currentItem && sv2.currentItem.objectName === viewName) { + dismissObj(sv2.currentItem, sv2) + } + } + + // Move items from one stack to another. We avoid the recursive technique to + // avoid visibility change events. + function move(from, to, depth=1) { + busy = true + var tempStack = [] + while (from.depth > depth) { + var item = from.pop(StackView.Immediate) + tempStack.push(item) + } + while (tempStack.length) { + to.push(tempStack.pop(), StackView.Immediate) + } + busy = false + } + + // Effectively hide the current view by moving it to the other StackView. + // This function only works when in single-pane mode. + function hideCurrentView() { + if (singlePane) move(sv1, sv2) + } +} diff --git a/src/app/accountadapter.cpp b/src/app/accountadapter.cpp index 66ec7625971cb18ed7dea8fd33136b16e6c4d137..c48aac2f87d4995d1fec2314a6185c78d97835db 100644 --- a/src/app/accountadapter.cpp +++ b/src/app/accountadapter.cpp @@ -327,23 +327,6 @@ AccountAdapter::getDefaultModerators(const QString& accountId) return lrcInstance_->accountModel().getDefaultModerators(accountId); } -bool -AccountAdapter::hasPassword() -{ - auto confProps = lrcInstance_->accountModel().getAccountConfig( - lrcInstance_->get_currentAccountId()); - return confProps.archiveHasPassword; -} - -void -AccountAdapter::setArchiveHasPassword(bool isHavePassword) -{ - auto confProps = lrcInstance_->accountModel().getAccountConfig( - lrcInstance_->get_currentAccountId()); - confProps.archiveHasPassword = isHavePassword; - lrcInstance_->accountModel().setAccountConfig(lrcInstance_->get_currentAccountId(), confProps); -} - bool AccountAdapter::exportToFile(const QString& accountId, const QString& path, diff --git a/src/app/accountadapter.h b/src/app/accountadapter.h index 8b141983555c2ae098602238097c36b3580a5f0a..8b474941677dcf80ee1b8b781e89345bdd9bb43d 100644 --- a/src/app/accountadapter.h +++ b/src/app/accountadapter.h @@ -50,8 +50,6 @@ public: QObject* parent = nullptr); ~AccountAdapter() = default; - void safeInit() override {} - // Change to account corresponding to combox box index. Q_INVOKABLE void changeAccount(int row); @@ -64,8 +62,6 @@ public: Q_INVOKABLE void deleteCurrentAccount(); // Conf property - Q_INVOKABLE bool hasPassword(); - Q_INVOKABLE void setArchiveHasPassword(bool isHavePassword); Q_INVOKABLE bool exportToFile(const QString& accountId, const QString& path, const QString& password = {}) const; diff --git a/src/app/avadapter.h b/src/app/avadapter.h index 23fdd18a57b0d66c234afc715f33b67f20fb03c6..641dfc27ae82929c5f462f4673eb82571e7b297a 100644 --- a/src/app/avadapter.h +++ b/src/app/avadapter.h @@ -50,8 +50,6 @@ Q_SIGNALS: void audioDeviceListChanged(int inputs, int outputs); protected: - void safeInit() override {}; - /** * Check if user is sharing a media */ @@ -89,7 +87,9 @@ protected: Q_INVOKABLE void shareWindow(const QString& windowProcessId, const QString& windowId); // Returns the screensharing resource - Q_INVOKABLE QString getSharingResource(int screenId = -2, const QString& windowProcessId = "", const QString& key = ""); + Q_INVOKABLE QString getSharingResource(int screenId = -2, + const QString& windowProcessId = "", + const QString& key = ""); Q_INVOKABLE void getListWindows(); diff --git a/src/app/calladapter.h b/src/app/calladapter.h index 84b93c94459fe76a306ac609f212427a69e6e039..ff1112bc99cd30f9184553ade9427bd071053066 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -49,9 +49,6 @@ public: explicit CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent = nullptr); ~CallAdapter() = default; -protected: - void safeInit() override {}; - public: Q_INVOKABLE void startTimerInformation(); Q_INVOKABLE void stopTimerInformation(); diff --git a/src/app/calloverlaymodel.cpp b/src/app/calloverlaymodel.cpp index ea7b04aa2521d5f78f9780538fa94950e71a22fa..dd1ee9c9e041fca6efa9107f7e7fd3eff2eda33b 100644 --- a/src/app/calloverlaymodel.cpp +++ b/src/app/calloverlaymodel.cpp @@ -54,7 +54,9 @@ PendingConferenceesListModel::PendingConferenceesListModel(LRCInstance* instance , lrcInstance_(instance) { connectSignals(); - connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, [this]() { connectSignals(); }); + connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, this, [this]() { + connectSignals(); + }); } int @@ -128,10 +130,14 @@ PendingConferenceesListModel::connectSignals() return; using namespace PendingConferences; - callsStatusChanged_ - = connect(currentCallModel, &CallModel::callStatusChanged, [this](const QString&, int) { - Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1), {Role::CallStatus}); - }); + callsStatusChanged_ = connect(currentCallModel, + &CallModel::callStatusChanged, + this, + [this](const QString&, int) { + Q_EMIT dataChanged(index(0, 0), + index(rowCount() - 1), + {Role::CallStatus}); + }); beginInsertPendingConferencesRows_ = connect( currentCallModel, diff --git a/src/app/commoncomponents/BaseView.qml b/src/app/commoncomponents/BaseView.qml new file mode 100644 index 0000000000000000000000000000000000000000..cdd51ce9ca91178f08fdf78d59b0f030068da3b0 --- /dev/null +++ b/src/app/commoncomponents/BaseView.qml @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 <https://www.gnu.org/licenses/>. + */ + +import QtQuick + +Rectangle { + id: viewNode + + // True if this view is managed by the view coordinator. + // False if this view is managed by its parent view, and will + // only be destroyed when its parent is destroyed. + property bool managed: true + + // True if this view functions in a single-pane context only. + property bool singlePaneOnly: false + + // True if this view requires and initial selection from + // a group of menu options when in single-pane mode (e.g. settings). + property bool requiresIndex: false + + function dismiss() { viewCoordinator.dismiss(objectName) } + + signal presented + signal dismissed + + Component.onCompleted: { if (managed) presented() } + Component.onDestruction: { if (managed) dismissed() } +} diff --git a/src/app/commoncomponents/ConfirmDialog.qml b/src/app/commoncomponents/ConfirmDialog.qml index 535b11d205a8f0d7f6b74a751fc2d11de54d2186..2a2acb1e490c27fb0e9c5405c9ca0d1f7af4c708 100644 --- a/src/app/commoncomponents/ConfirmDialog.qml +++ b/src/app/commoncomponents/ConfirmDialog.qml @@ -29,11 +29,13 @@ BaseModalDialog { signal accepted - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, + JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, + JamiTheme.preferredDialogHeight) - property var confirmLabel: "" - property var textLabel: "" + property string confirmLabel: "" + property string textLabel: "" popupContent: ColumnLayout { id: column diff --git a/src/app/commoncomponents/DaemonReconnectPopup.qml b/src/app/commoncomponents/DaemonReconnectPopup.qml index d777c27667c1bf112e57efb1ed46aa27546c3025..df4299b179d97d4e5d00e454373259e88a77bcf2 100644 --- a/src/app/commoncomponents/DaemonReconnectPopup.qml +++ b/src/app/commoncomponents/DaemonReconnectPopup.qml @@ -31,6 +31,26 @@ BaseModalDialog { autoClose: false + Connections { + target: { + if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx") + return DBusErrorHandler + return null + } + ignoreUnknownSignals: true + + function onShowDaemonReconnectPopup(visible) { + if (!visible) { + viewCoordinator.dismiss(this) + } + } + + function onDaemonReconnectFailed() { + connectionFailed = true + } + } + + onPopupContentLoadStatusChanged: { if (popupContentLoadStatus === Loader.Ready) { root.height = Qt.binding(function() { diff --git a/src/app/commoncomponents/DeleteAccountDialog.qml b/src/app/commoncomponents/DeleteAccountDialog.qml index 987f719c2e1f3821fafa9c259c96f4948fea8e99..a4c2b54113002b96e0817f2b4e373824bb0422c3 100644 --- a/src/app/commoncomponents/DeleteAccountDialog.qml +++ b/src/app/commoncomponents/DeleteAccountDialog.qml @@ -35,8 +35,8 @@ BaseModalDialog { title: JamiStrings.deleteAccount - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) popupContent: ColumnLayout { id: deleteAccountContentColumnLayout diff --git a/src/app/commoncomponents/JamiFileDialog.qml b/src/app/commoncomponents/JamiFileDialog.qml index 8306aace41fe8cbd2ef2f320259837a8a78cfe32..611dc765f419ff3eb50dc85f1345a29e46e01335 100644 --- a/src/app/commoncomponents/JamiFileDialog.qml +++ b/src/app/commoncomponents/JamiFileDialog.qml @@ -27,6 +27,17 @@ FileDialog { // Use enum to avoid importing Qt.labs.platform when using JamiFileDialog. property int mode: JamiFileDialog.Mode.OpenFile + signal fileAccepted(string file) + signal filesAccepted(var files) + + onAccepted: { + switch(fileMode) { + case FileDialog.OpenFile: fileAccepted(file); break + case FileDialog.OpenFiles: filesAccepted(files); break + default: fileAccepted(file) + } + } + enum Mode { OpenFile = 0, OpenFiles, diff --git a/src/app/commoncomponents/LocalVideo.qml b/src/app/commoncomponents/LocalVideo.qml index 2e7067fad87cb460c1aa4be35d7cf7639b48dc8a..d657b54e0cd82709a7e87ca49b6fc9389205c672 100644 --- a/src/app/commoncomponents/LocalVideo.qml +++ b/src/app/commoncomponents/LocalVideo.qml @@ -28,7 +28,7 @@ VideoView { crop: true function startWithId(id, force = false) { - if (id.length === 0) { + if (id !== undefined && id.length === 0) { VideoDevices.stopDevice(rendererId) rendererId = id } else { diff --git a/src/app/commoncomponents/PasswordDialog.qml b/src/app/commoncomponents/PasswordDialog.qml index 5fb05edd3cbaebb550239607b72e5d1e913855ce..b0451b5849a7e3b4432939d3f9046a599123266d 100644 --- a/src/app/commoncomponents/PasswordDialog.qml +++ b/src/app/commoncomponents/PasswordDialog.qml @@ -35,17 +35,10 @@ BaseModalDialog { property string path: "" property int purpose: PasswordDialog.ChangePassword - signal doneSignal(bool success, int currentPurpose) + signal done(bool success, int purpose) - function openDialog(purposeIn, exportPathIn = "") { - purpose = purposeIn - path = exportPathIn - - open() - } - - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) title: { switch(purpose){ @@ -58,6 +51,32 @@ BaseModalDialog { } } + function reportStatus(success) { + const title = success ? JamiStrings.success : JamiStrings.error + var info + switch(purpose) { + case PasswordDialog.ExportAccount: + info = success ? JamiStrings.backupSuccessful : JamiStrings.backupFailed + break + case PasswordDialog.ChangePassword: + info = success ? JamiStrings.changePasswordSuccess : JamiStrings.changePasswordFailed + break + case PasswordDialog.SetPassword: + info = success ? JamiStrings.setPasswordSuccess : JamiStrings.setPasswordFailed + break + } + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: title, + infoText: info, + buttonTitles: [JamiStrings.optionOk], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] + }) + done(success, purpose) + } + popupContent: ColumnLayout { id: popupContentColumnLayout @@ -86,20 +105,17 @@ BaseModalDialog { path, currentPasswordEdit.text) } - doneSignal(success, purpose) + reportStatus(success) + close() } function savePasswordQML() { - var success = false - success = AccountAdapter.savePassword( + var success = AccountAdapter.savePassword( LRCInstance.currentAccountId, currentPasswordEdit.text, passwordEdit.text) - if (success) { - AccountAdapter.setArchiveHasPassword(passwordEdit.text.length !== 0) - } - doneSignal(success, purpose) + reportStatus(success) close() } diff --git a/src/app/commoncomponents/PhotoboothView.qml b/src/app/commoncomponents/PhotoboothView.qml index b76875131657433a52a54510a80d64ef13080653..85105c4ca68c183702097accab9610ee611100d9 100644 --- a/src/app/commoncomponents/PhotoboothView.qml +++ b/src/app/commoncomponents/PhotoboothView.qml @@ -84,47 +84,6 @@ Item { } } - JamiFileDialog { - id: importFromFileDialog - - objectName: "photoboothImportFromFileDialog" - - mode: JamiFileDialog.OpenFile - title: JamiStrings.chooseAvatarImage - folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation) - - nameFilters: [ - JamiStrings.imageFiles, - JamiStrings.allFiles - ] - - onVisibleChanged: { - if (!visible) { - rejected() - } - } - - onAccepted: { - if (importButton.focusAfterFileDialogClosed) { - importButton.focusAfterFileDialogClosed = false - importButton.forceActiveFocus() - } - - var filePath = UtilsAdapter.getAbsPath(file) - if (!root.newItem) - AccountAdapter.setCurrentAccountAvatarFile(filePath) - else - UtilsAdapter.setTempCreationImageFromFile(filePath, root.imageId) - } - - onRejected: { - if (importButton.focusAfterFileDialogClosed) { - importButton.focusAfterFileDialogClosed = false - importButton.forceActiveFocus() - } - } - } - Rectangle { id: imageLayer @@ -246,8 +205,6 @@ Item { objectName: "photoboothViewImportButton" - property bool focusAfterFileDialogClosed: false - Layout.alignment: Qt.AlignHCenter visible: parent.visible @@ -267,7 +224,6 @@ Item { Keys.onPressed: function (keyEvent) { if (keyEvent.key === Qt.Key_Enter || keyEvent.key === Qt.Key_Return) { - focusAfterFileDialogClosed = true clicked() keyEvent.accepted = true } else if (keyEvent.key === Qt.Key_Down || @@ -282,11 +238,28 @@ Item { onClicked: { stopBooth() buttonsRowLayout.backToAvatar() - importFromFileDialog.open() + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.chooseAvatarImage, + fileMode: JamiFileDialog.OpenFile, + folder: StandardPaths.writableLocation( + StandardPaths.PicturesLocation), + nameFilters: [JamiStrings.imageFiles, + JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function(file) { + var filePath = UtilsAdapter.getAbsPath(file) + if (!root.newItem) { + AccountAdapter.setCurrentAccountAvatarFile(filePath) + } else { + UtilsAdapter.setTempCreationImageFromFile(filePath, root.imageId) + } + }) } } - PushButton { id: clearButton diff --git a/src/app/commoncomponents/PreferenceItemDelegate.qml b/src/app/commoncomponents/PreferenceItemDelegate.qml index 244c49e4fa10c18abc495be05e40d73fdff02e35..609e70b3e57657fa01664ea2d7258a8547bf1c78 100644 --- a/src/app/commoncomponents/PreferenceItemDelegate.qml +++ b/src/app/commoncomponents/PreferenceItemDelegate.qml @@ -19,6 +19,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import Qt.labs.platform import net.jami.Models 1.1 import net.jami.Adapters 1.1 @@ -53,10 +54,20 @@ ItemDelegate { break case PreferenceItemListModel.PATH: if (index === 0) { - preferenceFilePathDialog.title = JamiStrings.selectAnImage.arg(preferenceName) - preferenceFilePathDialog.nameFilters = fileFilters - preferenceFilePathDialog.selectedNameFilter.index = fileFilters.length - 1 - preferenceFilePathDialog.open() + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.selectAnImage.arg(preferenceName), + fileMode: JamiFileDialog.OpenFile, + folder: JamiQmlUtils.qmlFilePrefix + currentPath, + nameFilters: fileFilters + }) + dlg.fileAccepted.connect(function (file) { + var url = UtilsAdapter.getAbsPath(file.toString()) + preferenceNewValue = url + btnPreferenceClicked() + }) } else btnPreferenceClicked() @@ -74,19 +85,6 @@ ItemDelegate { } } - JamiFileDialog { - id: preferenceFilePathDialog - - title: JamiStrings.selectFile - folder: JamiQmlUtils.qmlFilePrefix + currentPath - - onAccepted: { - var url = UtilsAdapter.getAbsPath(file.toString()) - preferenceNewValue = url - btnPreferenceClicked() - } - } - RowLayout{ anchors.fill: parent diff --git a/src/app/commoncomponents/SBSMessageBase.qml b/src/app/commoncomponents/SBSMessageBase.qml index a7e45f32e51a7e72d15e20fb1fbefeb4877f82d9..1914ef0667eba65e92f71393462902c896110932 100644 --- a/src/app/commoncomponents/SBSMessageBase.qml +++ b/src/app/commoncomponents/SBSMessageBase.qml @@ -187,7 +187,6 @@ Control { messageOptionPopup.open() messageOptionPopup.x = messageOptionPopup.setXposition(messageOptionPopup.width) messageOptionPopup.y = messageOptionPopup.setYposition(messageOptionPopup.height) - } } @@ -246,7 +245,6 @@ Control { emojiPicker.x = setXposition(JamiTheme.emojiPickerWidth) messageOptionPopup.x = setXposition(width) messageOptionPopup.y = setYposition(height) - } Connections { @@ -268,8 +266,9 @@ Control { x: setXposition(JamiTheme.emojiPickerWidth), y: setYposition(JamiTheme.emojiPickerHeight) }); - messageOptionPopup.emojiPicker.open() - if (messageOptionPopup.emojiPicker === null) { + if (messageOptionPopup.emojiPicker !== null) { + messageOptionPopup.emojiPicker.open() + } else { console.log("Error creating emojiPicker in SBSMessageBase"); } } @@ -292,14 +291,22 @@ Control { } function setYposition(height) { - var mappedCoord = bubble.mapToItem(appWindow.contentItem,0, 0) - var distBottomScreen = appWindow.height - mappedCoord.y - height - JamiQmlUtils.messageBarButtonsRowObj.height + var bottomOffset = 0 + if (JamiQmlUtils.messageBarButtonsRowObj) { + bottomOffset = JamiQmlUtils.messageBarButtonsRowObj.height + } + var mappedCoord = bubble.mapToItem(appWindow.contentItem, 0, 0) + var distBottomScreen = appWindow.height - mappedCoord.y - height - bottomOffset if (distBottomScreen < 0) { return distBottomScreen } - var distTopScreen = mappedCoord.y - JamiQmlUtils.messagingHeaderRectRowLayout.height + var topOffset = 0 + if (JamiQmlUtils.messagingHeaderRectRowLayout) { + topOffset = JamiQmlUtils.messagingHeaderRectRowLayout.height + } + var distTopScreen = mappedCoord.y - topOffset if (distTopScreen < 0) - return - distTopScreen + return -distTopScreen return 0 } } diff --git a/src/app/commoncomponents/TextMessageDelegate.qml b/src/app/commoncomponents/TextMessageDelegate.qml index 84e8eb9c6eb77612f31ae5db0f57f3aaa343ccd4..7b3f756c5a72d209deb572d2f2210cceb264a98b 100644 --- a/src/app/commoncomponents/TextMessageDelegate.qml +++ b/src/app/commoncomponents/TextMessageDelegate.qml @@ -46,12 +46,6 @@ SBSMessageBase { extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage textHovered: textHoverhandler.hovered - EditedPopup { - id: editedPopup - - previousBodies: PreviousBodies - } - innerContent.children: [ TextEdit { id: textEditId @@ -155,7 +149,10 @@ SBSMessageBase { TapHandler { acceptedButtons: Qt.LeftButton onTapped: { - editedPopup.open() + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/EditedPopup.qml", + {previousBodies: PreviousBodies}) } } } diff --git a/src/app/constant/JamiQmlUtils.qml b/src/app/constant/JamiQmlUtils.qml index 22f487b084cb17245f0f5383c2083966be1cb9d9..fff14ba4398285422bb3a89e74f7d3d9f6f5eec3 100644 --- a/src/app/constant/JamiQmlUtils.qml +++ b/src/app/constant/JamiQmlUtils.qml @@ -53,6 +53,8 @@ Item { property point videoRecordMessageButtonInMainViewPoint property var emojiPickerButtonInMainViewPoint + signal settingsPageRequested(int index) + function updateMessageBarButtonsPoints() { if (messageBarButtonsRowObj && audioRecordMessageButtonObj && videoRecordMessageButtonObj) { audioRecordMessageButtonInMainViewPoint = diff --git a/src/app/contactadapter.h b/src/app/contactadapter.h index 5058c1e75175284208843b273bdba8c4d28c137c..ebe49a3e4fd9f7a5d55fe91ad7a22da297ecbab9 100644 --- a/src/app/contactadapter.h +++ b/src/app/contactadapter.h @@ -57,7 +57,7 @@ public: filterPredicate_ = filterPredicate; } - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override { // Accept all contacts in conversation list filtered with account type, except those in a call. auto index = sourceModel()->index(sourceRow, 0, sourceParent); @@ -86,8 +86,6 @@ public: using Role = ConversationList::Role; - void safeInit() override {} - Q_INVOKABLE QVariant getContactSelectableModel(int type); Q_INVOKABLE void setSearchFilter(const QString& filter); Q_INVOKABLE void contactSelected(int index); diff --git a/src/app/conversationlistmodel.h b/src/app/conversationlistmodel.h index 8320d7aca3e8870ee1a89b2eb24b94acd045595a..8fd672048663e6fab8b74a15292cbebc984de51d 100644 --- a/src/app/conversationlistmodel.h +++ b/src/app/conversationlistmodel.h @@ -22,8 +22,6 @@ #include "conversationlistmodelbase.h" #include "selectablelistproxymodel.h" -#include "api/profile.h" - #include <QSortFilterProxyModel> // A wrapper view model around ConversationModel's underlying data diff --git a/src/app/conversationsadapter.cpp b/src/app/conversationsadapter.cpp index 4cdd3344d0e171485034874ccc51ad99a239501e..8f7e035c25b22a59009295a6e4e19304d306d70b 100644 --- a/src/app/conversationsadapter.cpp +++ b/src/app/conversationsadapter.cpp @@ -19,7 +19,6 @@ #include "conversationsadapter.h" -#include "utils.h" #include "qtutils.h" #include "systemtray.h" #include "qmlregister.h" @@ -49,7 +48,7 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, convModel_->setFilterRequests(filterRequests_); }); - connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() { + connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, this, [this]() { auto convId = lrcInstance_->get_selectedConvUid(); if (convId.isEmpty()) { // deselected @@ -76,7 +75,7 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, } }); - connect(lrcInstance_, &LRCInstance::draftSaved, [this](const QString& convId) { + connect(lrcInstance_, &LRCInstance::draftSaved, this, [this](const QString& convId) { auto row = lrcInstance_->indexOf(convId); const auto index = convSrcModel_->index(row, 0); Q_EMIT convSrcModel_->dataChanged(index, index); @@ -103,13 +102,7 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, accInfo.conversationModel->removeConversation(convUid); }); #endif -} -void -ConversationsAdapter::safeInit() -{ - // TODO: remove these safeInits, they are possibly called - // multiple times during qml component inits connect(&lrcInstance_->behaviorController(), &BehaviorController::newUnreadInteraction, this, @@ -580,11 +573,14 @@ ConversationsAdapter::openDialogConversationWith(const QString& peerUri) lrcInstance_->selectConversation(convInfo.uid); } -bool +void ConversationsAdapter::connectConversationModel() { // Signal connections auto currentConversationModel = lrcInstance_->getCurrentConversationModel(); + if (currentConversationModel == nullptr) { + return; + } QObject::connect(currentConversationModel, &ConversationModel::modelChanged, @@ -657,8 +653,6 @@ ConversationsAdapter::connectConversationModel() searchModel_->bindSourceModel(searchSrcModel_.get()); updateConversationFilterData(); - - return true; } void @@ -672,4 +666,4 @@ ConversationsAdapter::createSwarm(const QString& title, {{"title", title}, {"description", description}, {"avatar", avatar}}); -} \ No newline at end of file +} diff --git a/src/app/conversationsadapter.h b/src/app/conversationsadapter.h index bb8fd82c5b30017978dc78b94e649defad6d7655..f05e39fbe854ba4b0a95a1e903f46f5b362819e0 100644 --- a/src/app/conversationsadapter.h +++ b/src/app/conversationsadapter.h @@ -21,7 +21,6 @@ #include "lrcinstance.h" #include "qmladapterbase.h" -#include "smartlistmodel.h" #include "conversationlistmodel.h" #include "searchresultslistmodel.h" @@ -43,11 +42,9 @@ public: QObject* parent = nullptr); ~ConversationsAdapter() = default; -protected: - void safeInit() override; - public: - Q_INVOKABLE bool connectConversationModel(); + void connectConversationModel(); + Q_INVOKABLE void createSwarm(const QString& title, const QString& description, const QString& avatar, diff --git a/src/app/currentaccount.cpp b/src/app/currentaccount.cpp index eb26e559e3a34129b818dbd3de6088cecb47e920..df68a6023582bb68467eccbf5a3ec4d768a24728 100644 --- a/src/app/currentaccount.cpp +++ b/src/app/currentaccount.cpp @@ -131,6 +131,8 @@ CurrentAccount::updateData() set_publishedPort(accConfig.publishedPort, true); set_registrationExpire(accConfig.registrationExpire, true); + set_hasArchivePassword(accConfig.archiveHasPassword); + // DHT set_PublicInCallsDHT(accConfig.DHT.PublicInCalls, true); diff --git a/src/app/currentaccount.h b/src/app/currentaccount.h index 554450d2d1fa357555a83621087461ae513232b0..8b345e24ca32ab1b2d49627a4e5b20c5400f34f3 100644 --- a/src/app/currentaccount.h +++ b/src/app/currentaccount.h @@ -132,6 +132,8 @@ class CurrentAccount final : public QObject QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(int, publishedPort) QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(int, registrationExpire) + QML_RO_PROPERTY(bool, hasArchivePassword) + // Moderator settings Q_PROPERTY(bool isAllModeratorsEnabled READ get_isAllModeratorsEnabled WRITE set_isAllModeratorsEnabled NOTIFY isAllModeratorsEnabledChanged) diff --git a/src/app/currentcall.cpp b/src/app/currentcall.cpp index 6eb4fbd50920d7c890f5ed452f897b471551cbe0..c813ebafa4e066edd35c563c283067688844c6f5 100644 --- a/src/app/currentcall.cpp +++ b/src/app/currentcall.cpp @@ -33,6 +33,11 @@ CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent) this, &CurrentCall::onCurrentConvIdChanged); + connect(&lrcInstance_->behaviorController(), + &BehaviorController::showIncomingCallView, + this, + &CurrentCall::onShowIncomingCallView); + connectModel(); } @@ -57,9 +62,7 @@ CurrentCall::updateId(QString callId) // Set the current id_ if there is a call. auto& accInfo = lrcInstance_->getCurrentAccountInfo(); - if (accInfo.callModel->hasCall(callId)) { - set_id(callId); - } + set_id((accInfo.callModel->hasCall(callId) ? callId : QString())); } void @@ -102,6 +105,7 @@ CurrentCall::updateCallInfo() auto callInfo = callModel->getCall(id_); + set_isOutgoing(callInfo.isOutgoing); set_isGrid(callInfo.layout == call::Layout::GRID); set_isAudioOnly(callInfo.isAudioOnly); @@ -181,41 +185,41 @@ CurrentCall::updateRecordingState(bool state) void CurrentCall::connectModel() { - try { - auto& accInfo = lrcInstance_->getCurrentAccountInfo(); - connect(accInfo.callModel.get(), - &CallModel::callStatusChanged, - this, - &CurrentCall::onCallStatusChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::callInfosChanged, - this, - &CurrentCall::onCallInfosChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::currentCallChanged, - this, - &CurrentCall::onCurrentCallChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::participantsChanged, - this, - &CurrentCall::onParticipantsChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::remoteRecordersChanged, - this, - &CurrentCall::onRemoteRecordersChanged, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), - &CallModel::recordingStateChanged, - this, - &CurrentCall::onRecordingStateChanged, - Qt::UniqueConnection); - } catch (const std::exception& e) { - qWarning() << "Exception getting account info." << e.what(); + auto callModel = lrcInstance_->getCurrentCallModel(); + if (callModel == nullptr) { + return; } + + connect(callModel, + &CallModel::callStatusChanged, + this, + &CurrentCall::onCallStatusChanged, + Qt::UniqueConnection); + connect(callModel, + &CallModel::callInfosChanged, + this, + &CurrentCall::onCallInfosChanged, + Qt::UniqueConnection); + connect(callModel, + &CallModel::currentCallChanged, + this, + &CurrentCall::onCurrentCallChanged, + Qt::UniqueConnection); + connect(callModel, + &CallModel::participantsChanged, + this, + &CurrentCall::onParticipantsChanged, + Qt::UniqueConnection); + connect(callModel, + &CallModel::remoteRecordersChanged, + this, + &CurrentCall::onRemoteRecordersChanged, + Qt::UniqueConnection); + connect(callModel, + &CallModel::recordingStateChanged, + this, + &CurrentCall::onRecordingStateChanged, + Qt::UniqueConnection); } void @@ -277,7 +281,7 @@ CurrentCall::onCurrentCallChanged(const QString& callId) // If this status change's callId is not the current, it's possible that // the current value of id_ is stale, and needs to be updated after checking // the current conversation's getCallId(). Other slots need not do this, as the - // id_ is updated here. + // id_ is updated in CurrentCall::updateId. if (id_ == callId) { return; } @@ -317,3 +321,18 @@ CurrentCall::onRecordingStateChanged(const QString& callId, bool state) updateRecordingState(state); } + +void +CurrentCall::onShowIncomingCallView(const QString& accountId, const QString& convUid) +{ + if (accountId != lrcInstance_->get_currentAccountId() + || convUid != lrcInstance_->get_selectedConvUid()) { + return; + } + + // Update the id in case the current conversation now has a call + // that matches the current id. + updateId(); + updateCallStatus(); + updateCallInfo(); +} diff --git a/src/app/currentcall.h b/src/app/currentcall.h index 4b128b9b1443e9945e17a0555df98960222f03e7..622ba5c511ed326bacf4dab9ffd11c90e9705afb 100644 --- a/src/app/currentcall.h +++ b/src/app/currentcall.h @@ -47,9 +47,9 @@ class CurrentCall final : public QObject QML_RO_PROPERTY(bool, isHandRaised) QML_RO_PROPERTY(bool, isConference) QML_RO_PROPERTY(bool, isModerator) - QML_PROPERTY(bool, hideSelf) QML_PROPERTY(bool, hideSpectators) + QML_RO_PROPERTY(bool, isOutgoing) public: explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr); @@ -73,6 +73,7 @@ private Q_SLOTS: void onParticipantsChanged(const QString& callId); void onRemoteRecordersChanged(const QString& callId, const QStringList& recorders); void onRecordingStateChanged(const QString& callId, bool state); + void onShowIncomingCallView(const QString& accountId, const QString& convUid); private: LRCInstance* lrcInstance_; diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp index 8a12636f7a3194a1649d47a72fef6a2b25401793..b4e4bb45be9343f8bc1f8a621ad91aed9615fc3d 100644 --- a/src/app/currentconversation.cpp +++ b/src/app/currentconversation.cpp @@ -17,7 +17,6 @@ */ #include "currentconversation.h" -#include "qmlregister.h" #include <api/conversationmodel.h> @@ -38,6 +37,12 @@ CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* pare &LRCInstance::selectedConvUidChanged, this, &CurrentConversation::updateData); + + connect(&lrcInstance_->behaviorController(), + &BehaviorController::showIncomingCallView, + this, + &CurrentConversation::onShowIncomingCallView); + updateData(); } @@ -45,9 +50,9 @@ void CurrentConversation::updateData() { auto convId = lrcInstance_->get_selectedConvUid(); + set_id(convId); if (convId.isEmpty()) return; - set_id(convId); try { auto accountId = lrcInstance_->get_currentAccountId(); const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); @@ -67,8 +72,10 @@ CurrentConversation::updateData() if (accInfo.callModel->hasCall(callId_)) { auto call = accInfo.callModel->getCall(callId_); set_callState(call.status); + set_hasCall(callState_ != call::Status::ENDED); } else { set_callState(call::Status::INVALID); + set_hasCall(false); } set_inCall(callState_ == call::Status::CONNECTED || callState_ == call::Status::IN_PROGRESS @@ -93,7 +100,6 @@ CurrentConversation::updateData() } set_isContact(isContact); - QString modeString; if (convInfo.mode == conversation::Mode::ONE_TO_ONE) { set_modeString(tr("Private")); } else if (convInfo.mode == conversation::Mode::ADMIN_INVITES_ONLY) { @@ -137,7 +143,7 @@ CurrentConversation::setPreference(const QString& key, const QString& value) QString CurrentConversation::getPreference(const QString& key) const { - return getPreferences()[key]; + return getPreferences().value(key); } MapStringString @@ -147,7 +153,6 @@ CurrentConversation::getPreferences() const const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); auto convId = lrcInstance_->get_selectedConvUid(); if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) { - auto& convInfo = optConv->get(); auto preferences = accInfo.conversationModel->getConversationPreferences(convId); return preferences; } @@ -212,7 +217,6 @@ CurrentConversation::updateConversationPreferences(const QString& convId) const auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) { auto& convInfo = optConv->get(); - auto preferences = convInfo.preferences; auto color = Utils::getAvatarColor(convId).name(); if (convInfo.preferences.contains("color")) { color = convInfo.preferences["color"]; @@ -261,10 +265,15 @@ CurrentConversation::connectModel() this, &CurrentConversation::onNeedsHost, Qt::UniqueConnection); + connect(lrcInstance_->getCurrentCallModel(), + &CallModel::callStatusChanged, + this, + &CurrentConversation::onCallStatusChanged, + Qt::UniqueConnection); } void -CurrentConversation::showSwarmDetails() const +CurrentConversation::showSwarmDetails() { Q_EMIT showDetails(); } @@ -275,11 +284,11 @@ CurrentConversation::updateErrors(const QString& convId) if (convId != id_) return; try { + QStringList newErrors; + QStringList newBackendErr; const auto& convModel = lrcInstance_->getCurrentConversationModel(); if (auto optConv = convModel->getConversationForUid(convId)) { auto& convInfo = optConv->get(); - QStringList newErrors; - QStringList newBackendErr; for (const auto& [code, error] : convInfo.errors) { if (code == 1) { newErrors.append(tr("An error occurred while fetching this repository")); @@ -295,9 +304,9 @@ CurrentConversation::updateErrors(const QString& convId) } newBackendErr.push_back(error); } - set_backendErrors(newBackendErr); - set_errors(newErrors); } + set_backendErrors(newBackendErr); + set_errors(newErrors); } catch (...) { } } @@ -338,6 +347,32 @@ CurrentConversation::updateActiveCalls(const QString&, const QString& convId) } } +void +CurrentConversation::onCallStatusChanged(const QString& callId, int) +{ + if (callId != callId_) { + return; + } + auto callModel = lrcInstance_->getCurrentCallModel(); + if (callModel->hasCall(callId_)) { + auto callInfo = callModel->getCall(callId_); + set_hasCall(callInfo.status != call::Status::ENDED); + } +} + +void +CurrentConversation::onShowIncomingCallView(const QString& accountId, const QString& convUid) +{ + if (accountId != lrcInstance_->get_currentAccountId()) { + return; + } + const auto& convModel = lrcInstance_->getCurrentConversationModel(); + if (auto optConv = convModel->getConversationForUid(convUid)) { + auto& convInfo = optConv->get(); + set_hasCall(!convInfo.getCallId().isEmpty()); + } +} + void CurrentConversation::scrollToMsg(const QString& msg) { diff --git a/src/app/currentconversation.h b/src/app/currentconversation.h index 76054d94670f928e93f1533a970f4e18091ec1b5..84f640f64281b245d54fcb98474449afcd8babc4 100644 --- a/src/app/currentconversation.h +++ b/src/app/currentconversation.h @@ -55,12 +55,13 @@ class CurrentConversation final : public QObject QML_PROPERTY(QStringList, errors) QML_PROPERTY(QStringList, backendErrors) QML_PROPERTY(QString, lastSelfMessageId) + QML_RO_PROPERTY(bool, hasCall) public: explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr); ~CurrentConversation() = default; Q_INVOKABLE void scrollToMsg(const QString& msgId); - Q_INVOKABLE void showSwarmDetails() const; + Q_INVOKABLE void showSwarmDetails(); Q_INVOKABLE void setPreference(const QString& key, const QString& value); Q_INVOKABLE QString getPreference(const QString& key) const; Q_INVOKABLE MapStringString getPreferences() const; @@ -68,7 +69,7 @@ public: Q_SIGNALS: void scrollTo(const QString& msgId); - void showDetails() const; + void showDetails(); private Q_SLOTS: void updateData(); @@ -78,6 +79,8 @@ private Q_SLOTS: void updateErrors(const QString& convId); void updateConversationPreferences(const QString& convId); void updateActiveCalls(const QString&, const QString& convId); + void onCallStatusChanged(const QString& callId, int code); + void onShowIncomingCallView(const QString& accountId, const QString& convUid); Q_SIGNALS: void needsHost(); diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp index df4b25b927e53fc9f777394eb754754e5f7dba65..122fb1b05bbfac90e906bf508bc58e3c2a788cae 100644 --- a/src/app/lrcinstance.cpp +++ b/src/app/lrcinstance.cpp @@ -339,6 +339,10 @@ LRCInstance::setContentDraft(const QString& convUid, const QString& accountId, const QString& content) { + if (accountId.isEmpty() || convUid.isEmpty()) { + return; + } + auto draftKey = accountId + "_" + convUid; // prevent a senseless dataChanged signal from the @@ -359,6 +363,7 @@ LRCInstance::selectConversation(const QString& convId, const QString& accountId) Q_EMIT conversationUpdated(convId, accountId); return; } + // if the account is not currently selected, do that first, then // proceed to select the conversation if (!accountId.isEmpty() && accountId != get_currentAccountId()) { diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp index 1e7c115c2172b55daad54ac59b2d6bc234c312ea..64ad3b27f52f481368cadc2ed36b1ef56284281a 100644 --- a/src/app/mainapplication.cpp +++ b/src/app/mainapplication.cpp @@ -104,7 +104,7 @@ MainApplication::MainApplication(int& argc, char** argv) } parseArguments(); - QObject::connect(this, &QApplication::aboutToQuit, [this] { cleanup(); }); + QObject::connect(this, &QApplication::aboutToQuit, this, &MainApplication::cleanup); } MainApplication::~MainApplication() @@ -158,7 +158,7 @@ MainApplication::init() } #endif - connect(connectivityMonitor_.get(), &ConnectivityMonitor::connectivityChanged, [this] { + connect(connectivityMonitor_.get(), &ConnectivityMonitor::connectivityChanged, this, [this] { QTimer::singleShot(500, this, [&]() { lrcInstance_->connectivityChanged(); }); }); diff --git a/src/app/mainview/ConversationView.qml b/src/app/mainview/ConversationView.qml new file mode 100644 index 0000000000000000000000000000000000000000..f8963ba69f89c0e5d8769a3fe98d290df357bcf0 --- /dev/null +++ b/src/app/mainview/ConversationView.qml @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 <https://www.gnu.org/licenses/>. + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects + +import net.jami.Models 1.1 +import net.jami.Adapters 1.1 +import net.jami.Constants 1.1 + +import "../commoncomponents" +import "components" + +BaseView { + id: root + objectName: "ConversationView" + managed: false + + onPresented: { + if (!visible && viewCoordinator.singlePane && + CurrentConversation.id !== '') { + viewCoordinator.present(objectName) + } + } + + onDismissed: { + callStackView.needToCloseInCallConversationAndPotentialWindow() + LRCInstance.deselectConversation() + } + + property string currentAccountId: CurrentAccount.id + onCurrentAccountIdChanged: dismiss() + + onVisibleChanged: { + if (visible) return + UtilsAdapter.clearInteractionsCache(CurrentAccount.id, CurrentConversation.id) + } + + color: JamiTheme.transparentColor + + StackLayout { + currentIndex: !CurrentConversation.hasCall ? 0 : 1 + onCurrentIndexChanged: chatView.parent = currentIndex == 1 ? + callStackView.chatViewContainer : + chatViewContainer + + anchors.fill: root + + Item { + id: chatViewContainer + + Layout.fillWidth: true + Layout.fillHeight: true + + ChatView { + id: chatView + anchors.fill: parent + inCallView: parent == callStackView.chatViewContainer + + property string currentConvId: CurrentConversation.id + onCurrentConvIdChanged: { + if (!CurrentConversation.hasCall) { + resetPanels() + Qt.callLater(focusChatView) + } + } + + onDismiss: { + if (parent == chatViewContainer) { + root.dismiss() + } else { + callStackView.chatViewContainer.visible = false + } + } + } + } + + CallStackView { + id: callStackView + Layout.fillWidth: true + Layout.fillHeight: true + } + } +} diff --git a/src/app/mainview/MainView.qml b/src/app/mainview/MainView.qml index 31e17fd70b14ba3ed6824061fd38c44d876911cf..6f5717e171acddaf235d32b6ed97bb36ae7f6340 100644 --- a/src/app/mainview/MainView.qml +++ b/src/app/mainview/MainView.qml @@ -39,223 +39,39 @@ Rectangle { objectName: "mainView" - property int sidePanelViewStackCurrentWidth: 300 - property int mainViewStackPreferredWidth: sidePanelViewStackCurrentWidth + JamiTheme.chatViewHeaderMinimumWidth - property int settingsViewPreferredWidth: 460 - property int onWidthChangedTriggerDistance: 5 - property int lastSideBarSplitSize: sidePanelViewStackCurrentWidth - - property bool sidePanelOnly: (!mainViewStack.visible) && sidePanelViewStack.visible - property int previousWidth: width - // To calculate tab bar bottom border hidden rect left margin. property int tabBarLeftMargin: 8 property int tabButtonShrinkSize: 8 - property bool inSettingsView: false - - // For updating msgWebView - property string currentConvUID: "" + property bool inSettingsView: viewCoordinator.inSettings signal loaderSourceChangeRequested(int sourceToLoad) - property string currentAccountId: LRCInstance.currentAccountId - onCurrentAccountIdChanged: { - if (inSettingsView) { - settingsView.setSelected(settingsView.selectedMenu, true) - } else { - backToMainView(true) - } - } - - function isPageInStack(objectName, stackView) { - var foundItem = stackView.find(function (item, index) { - return item.objectName === objectName - }) - - return foundItem ? true : false - } - - function showWelcomeView() { - currentConvUID = "" - callStackView.needToCloseInCallConversationAndPotentialWindow() - LRCInstance.deselectConversation() - if (isPageInStack("callStackViewObject", sidePanelViewStack) || - isPageInStack("chatView", sidePanelViewStack) || - isPageInStack("chatView", mainViewStack) || - isPageInStack("newSwarmPage", sidePanelViewStack) || - isPageInStack("newSwarmPage", mainViewStack) || - isPageInStack("callStackViewObject", mainViewStack)) { - sidePanelViewStack.pop(StackView.Immediate) - mainViewStack.pop(welcomePage, StackView.Immediate) - } - } - - function pushCallStackView() { - chatView.inCallView = true - if (sidePanelOnly) { - sidePanelViewStack.pop(StackView.Immediate) - sidePanelViewStack.push(callStackView, StackView.Immediate) - } else { - sidePanelViewStack.pop(StackView.Immediate) - mainViewStack.pop(welcomePage, StackView.Immediate) - mainViewStack.push(callStackView, StackView.Immediate) - } - } - - function pushCommunicationMessageWebView() { - chatView.inCallView = false - if (sidePanelOnly) { - sidePanelViewStack.pop(StackView.Immediate) - sidePanelViewStack.push(chatView, StackView.Immediate) - } else { - mainViewStack.pop(welcomePage, StackView.Immediate) - mainViewStack.push(chatView, StackView.Immediate) - } - } - - function pushNewSwarmPage() { - if (sidePanelOnly) { - sidePanelViewStack.pop(StackView.Immediate) - sidePanelViewStack.push(newSwarmPage, StackView.Immediate) - } else { - mainViewStack.pop(welcomePage, StackView.Immediate) - mainViewStack.push(newSwarmPage, StackView.Immediate) - } - } - - function startWizard() { - mainViewStackLayout.currentIndex = 1 - } - - function currentAccountIsCalling() { - return UtilsAdapter.hasCall(LRCInstance.currentAccountId) - } - - // Only called onWidthChanged - function recursionStackViewItemMove(stackOne, stackTwo, depth=1) { - // Move all items (expect the bottom item) to stacktwo by the same order in stackone. - if (stackOne.depth === depth) { - return - } - - var tempItem = stackOne.pop(StackView.Immediate) - recursionStackViewItemMove(stackOne, stackTwo, depth) - stackTwo.push(tempItem, StackView.Immediate) - } - - // Back to WelcomeView required, but can also check, i. e., on account switch or - // settings exit, if there is need to switch to a current call - function backToMainView(checkCurrentCall = false) { - if (inSettingsView) - return - if (checkCurrentCall && currentAccountIsCalling()) { - var callConv = UtilsAdapter.getCallConvForAccount( - LRCInstance.currentAccountId) - LRCInstance.selectConversation(callConv, currentAccountId) - CallAdapter.updateCall(callConv, currentAccountId) - } else { - showWelcomeView() - } - } - - function toggleSettingsView() { - inSettingsView = !inSettingsView - - if (inSettingsView) { - if (sidePanelOnly) - sidePanelViewStack.push(settingsMenu, StackView.Immediate) - else { - mainViewStack.pop(welcomePage, StackView.Immediate) - mainViewStack.push(settingsView, StackView.Immediate) - sidePanelViewStack.push(settingsMenu, StackView.Immediate) - } + property string currentConvId: CurrentConversation.id + onCurrentConvIdChanged: { + if (currentConvId !== '') { + viewCoordinator.present("ConversationView") } else { - sidePanelViewStack.pop(StackView.Immediate) - mainViewStack.pop(StackView.Immediate) - backToMainView(true) - } - } - - function setMainView(convId) { - var item = ConversationsAdapter.getConvInfoMap(convId) - if (item.convId === undefined) - return - if (item.callStackViewShouldShow) { - if (inSettingsView) { - toggleSettingsView() - } - MessagesAdapter.setupChatView(item) - callStackView.setLinkedWebview(chatView) - callStackView.responsibleAccountId = LRCInstance.currentAccountId - callStackView.responsibleConvUid = convId - currentConvUID = convId - - if (item.callState === Call.Status.IN_PROGRESS || - item.callState === Call.Status.PAUSED) { - CallAdapter.updateCall(convId, LRCInstance.currentAccountId) - callStackView.showOngoingCallPage() - } else { - callStackView.showInitialCallPage(item.callState, item.isAudioOnly) - } - pushCallStackView() - - } else if (!inSettingsView) { - if (currentConvUID !== convId) { - callStackView.needToCloseInCallConversationAndPotentialWindow() - MessagesAdapter.setupChatView(item) - pushCommunicationMessageWebView() - chatView.focusChatView() - currentConvUID = convId - } else if (isPageInStack("callStackViewObject", sidePanelViewStack) - || isPageInStack("callStackViewObject", mainViewStack)) { - callStackView.needToCloseInCallConversationAndPotentialWindow() - pushCommunicationMessageWebView() - chatView.focusChatView() - } + viewCoordinator.present("WelcomePage") } } color: JamiTheme.backgroundColor - Connections { - target: LRCInstance - - function onSelectedConvUidChanged() { - mainView.setMainView(LRCInstance.selectedConvUid) - } - - function onConversationUpdated(convUid, accountId) { - if (convUid === LRCInstance.selectedConvUid && - accountId === currentAccountId) - mainView.setMainView(convUid) - } - } - - Connections { - target: WizardViewStepModel + onWidthChanged: Qt.callLater(JamiQmlUtils.updateMessageBarButtonsPoints) + onHeightChanged: Qt.callLater(JamiQmlUtils.updateMessageBarButtonsPoints) - function onCloseWizardView() { - mainViewStackLayout.currentIndex = 0 - backToMainView() - } - } - - StackLayout { - id: mainViewStackLayout + // Needed by ViewCoordinator. + property alias splitView: splitView + property alias sv1: sv1 + property alias sv2: sv2 + StackView { + id: mainStackView anchors.fill: parent - currentIndex: 0 - - SplitView { + initialItem: SplitView { id: splitView - Layout.fillWidth: true - Layout.fillHeight: true - - width: mainView.width - height: mainView.height - handle: Rectangle { implicitWidth: JamiTheme.splitViewHandlePreferredWidth implicitHeight: splitView.height @@ -267,264 +83,33 @@ Rectangle { } } - Rectangle { - id: mainViewSidePanelRect - - SplitView.maximumWidth: splitView.width - SplitView.minimumWidth: sidePanelViewStackCurrentWidth - SplitView.preferredWidth: sidePanelViewStackCurrentWidth + StackView { + id: sv1 + objectName: "sv1" + SplitView.minimumWidth: 300 + SplitView.preferredWidth: 300 SplitView.fillHeight: true - color: JamiTheme.backgroundColor - - // AccountComboBox is not a ComboBox - AccountComboBox { - id: accountComboBox - - anchors.top: mainViewSidePanelRect.top - width: mainViewSidePanelRect.width - height: JamiTheme.accountListItemHeight - - visible: (mainViewSidePanel.visible || settingsMenu.visible) - - onSettingBtnClicked: { - toggleSettingsView() - } - - Component.onCompleted: { - AccountAdapter.setQmlObject(this) - } - } - - StackView { - id: sidePanelViewStack - - initialItem: mainViewSidePanel - - anchors.top: accountComboBox.visible ? accountComboBox.bottom : - mainViewSidePanelRect.top - width: mainViewSidePanelRect.width - height: accountComboBox.visible ? mainViewSidePanelRect.height - accountComboBox.height : - mainViewSidePanelRect.height - - clip: true - } + clip: true + initialItem: SidePanel {} } StackView { - id: mainViewStack - - initialItem: welcomePage - - SplitView.maximumWidth: splitView.width - SplitView.minimumWidth: JamiTheme.chatViewHeaderMinimumWidth - SplitView.preferredWidth: mainViewStackPreferredWidth + id: sv2 + objectName: "sv2" SplitView.fillHeight: true - clip: true } } - - WizardView { - id: wizardView - - Layout.fillWidth: true - Layout.fillHeight: true - - onLoaderSourceChangeRequested: { - mainViewStackLayout.currentIndex = 0 - backToMainView() - } - } } - SettingsMenu { - id: settingsMenu - - objectName: "settingsMenu" - - visible: false - - width: mainViewSidePanelRect.width - height: mainViewSidePanelRect.height - - onItemSelected: function (index) { - settingsView.setSelected(index) - if (sidePanelOnly) - sidePanelViewStack.push(settingsView, StackView.Immediate) - } - } - - SidePanel { - id: mainViewSidePanel - - Connections { - target: ConversationsAdapter - - function onNavigateToWelcomePageRequested() { - backToMainView() - } - - } - - onCreateSwarmClicked: { - if (newSwarmPage.visible) { - backToMainView() - mainViewSidePanel.showSwarmListView(false) - } else { - pushNewSwarmPage() - } - } - - onHighlightedMembersChanged: { - newSwarmPage.members = mainViewSidePanel.highlightedMembers - } - } - - CallStackView { - id: callStackView - - visible: false - objectName: "callStackViewObject" - } - - WelcomePage { - id: welcomePage - - visible: false - } - - SettingsView { - id: settingsView - - visible: false - - onSettingsViewNeedToShowMainView: { - AccountAdapter.changeAccount(0) - toggleSettingsView() - } - - onSettingsViewNeedToShowNewWizardWindow: loaderSourceChangeRequested( - MainApplicationWindow.LoadedSource.WizardView) - - onSettingsBackArrowClicked: sidePanelViewStack.pop(StackView.Immediate) - } - - ChatView { - id: chatView - - objectName: "chatView" - visible: false - inCallView: false - Component.onCompleted: { - MessagesAdapter.setQmlObject(this) - PositionManager.setQmlObject(this) - } - } - - NewSwarmPage { - id: newSwarmPage - - objectName: "newSwarmPage" - visible: false - - onVisibleChanged: { - mainViewSidePanel.showSwarmListView(newSwarmPage.visible) - } - - onRemoveMember: function(convId, member) { - mainViewSidePanel.removeMember(convId, member) - } - - onCreateSwarmClicked: function(title, description, avatar) { - var uris = [] - for (var idx in newSwarmPage.members) { - var uri = newSwarmPage.members[idx].uri - if (uris.indexOf(uri) === -1) { - uris.push(uri) - } - } - ConversationsAdapter.createSwarm(title, description, avatar, uris) - backToMainView() - } - } - - onWidthChanged: { - // Hide unnecessary stackview when width is changed. - var isExpanding = previousWidth < mainView.width - - if (mainView.width < JamiTheme.chatViewHeaderMinimumWidth + mainViewSidePanelRect.width - && mainViewStack.visible && !isExpanding) { - lastSideBarSplitSize = mainViewSidePanelRect.width - mainViewStack.visible = false - - // The find callback function is called for each item in the stack. - var inWelcomeViewStack = mainViewStack.find( - function (item, index) { - return index > 0 - }) - - if (inSettingsView) { - mainViewStack.pop(StackView.Immediate) - sidePanelViewStack.push(settingsView, StackView.Immediate) - } - else if (inWelcomeViewStack) - recursionStackViewItemMove(mainViewStack, sidePanelViewStack) - } else if (mainView.width >= lastSideBarSplitSize + JamiTheme.chatViewHeaderMinimumWidth - && !mainViewStack.visible && isExpanding && !layoutManager.isFullScreen) { - mainViewStack.visible = true - - var inSidePanelViewStack = sidePanelViewStack.find( - function (item, index) { - return index > 0 - }) - - if (inSettingsView) { - if (sidePanelViewStack.currentItem.objectName !== settingsMenu.objectName) - sidePanelViewStack.pop(StackView.Immediate) - mainViewStack.push(settingsView, StackView.Immediate) - } else if (inSidePanelViewStack) { - recursionStackViewItemMove(sidePanelViewStack, mainViewStack) - if (currentAccountIsCalling()) - pushCallStackView() - } - } - - previousWidth = mainView.width - - JamiQmlUtils.updateMessageBarButtonsPoints() - } - - onHeightChanged: JamiQmlUtils.updateMessageBarButtonsPoints() - Component.onCompleted: { JamiQmlUtils.mainViewRectObj = mainView } - AboutPopUp { - id: aboutPopUpDialog - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - } - - WelcomePageQrDialog { - id: qrDialog - } - - UserProfile { - id: userProfile - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - } - Shortcut { sequence: "Ctrl+M" context: Qt.ApplicationShortcut - onActivated: { - if (!inSettingsView) { - toggleSettingsView() - } - settingsMenu.buttonSelectedManually(SettingsView.Media) - } + onActivated: JamiQmlUtils.settingsPageRequested(SettingsView.Media) } WheelHandler { @@ -571,42 +156,25 @@ Rectangle { Shortcut { sequence: "Ctrl+0" context: Qt.ApplicationShortcut - onActivated: { - UtilsAdapter.setAppValue(Settings.BaseZoom, 1.0) - } + onActivated: UtilsAdapter.setAppValue(Settings.BaseZoom, 1.0) } Shortcut { sequence: "Ctrl+G" context: Qt.ApplicationShortcut - onActivated: { - if (!inSettingsView) { - toggleSettingsView() - } - settingsMenu.buttonSelectedManually(SettingsView.General) - } + onActivated: JamiQmlUtils.settingsPageRequested(SettingsView.General) } Shortcut { sequence: "Ctrl+I" context: Qt.ApplicationShortcut - onActivated: { - if (!inSettingsView) { - toggleSettingsView() - } - settingsMenu.buttonSelectedManually(SettingsView.Account) - } + onActivated: JamiQmlUtils.settingsPageRequested(SettingsView.Account) } Shortcut { sequence: "Ctrl+P" context: Qt.ApplicationShortcut - onActivated: { - if (!inSettingsView) { - toggleSettingsView() - } - settingsMenu.buttonSelectedManually(SettingsView.Plugin) - } + onActivated: JamiQmlUtils.settingsPageRequested(SettingsView.Plugin) } Shortcut { @@ -650,7 +218,7 @@ Rectangle { Shortcut { sequence: "Ctrl+Shift+N" context: Qt.ApplicationShortcut - onActivated: startWizard() + onActivated: viewCoordinator.present("WizardView") } Shortcut { diff --git a/src/app/mainview/components/AboutPopUp.qml b/src/app/mainview/components/AboutPopUp.qml index b58f4bf8bd4bf883f7bea6a529761652b2dd0866..bebd79718c6bc86e4083cd2e7f3dd0b59d2cda5c 100644 --- a/src/app/mainview/components/AboutPopUp.qml +++ b/src/app/mainview/components/AboutPopUp.qml @@ -31,6 +31,9 @@ import "../../commoncomponents" BaseModalDialog { id: root + width: Math.min(parent.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) + height: Math.min(parent.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) + popupContentMargins: 14 PushButton { diff --git a/src/app/mainview/components/AccountComboBox.qml b/src/app/mainview/components/AccountComboBox.qml index 1b8b1ff47b0d33409f84107a83ac277f99ad38a9..58f3a3f37a6e394c7b6bc62a835efb445232762b 100644 --- a/src/app/mainview/components/AccountComboBox.qml +++ b/src/app/mainview/components/AccountComboBox.qml @@ -33,6 +33,8 @@ Label { signal settingBtnClicked property alias popup: comboBoxPopup + property bool inSettingsView: viewCoordinator.inSettings + // TODO: remove these refresh hacks use QAbstractItemModels correctly Connections { target: AccountAdapter @@ -194,23 +196,22 @@ Label { normalColor: JamiTheme.backgroundColor imageColor: JamiTheme.textColor - onClicked: { - if (visible) - qrDialog.open() - } + onClicked: viewCoordinator.presentDialog( + appWindow, + "mainview/components/WelcomePageQrDialog.qml") } PushButton { id: settingsButton anchors.verticalCenter: parent.verticalCenter - source: !mainView.inSettingsView ? + source: !inSettingsView ? JamiResources.settings_24dp_svg : JamiResources.round_close_24dp_svg normalColor: JamiTheme.backgroundColor imageColor: JamiTheme.textColor - toolTipText: !mainView.inSettingsView ? + toolTipText: !inSettingsView ? JamiStrings.openSettings : JamiStrings.closeSettings diff --git a/src/app/mainview/components/AccountComboBoxPopup.qml b/src/app/mainview/components/AccountComboBoxPopup.qml index 80e5059b1fef57132b53504bec19daf207fd7d10..068ae75a565f1424afbe01276285cd3b94c42fee 100644 --- a/src/app/mainview/components/AccountComboBoxPopup.qml +++ b/src/app/mainview/components/AccountComboBoxPopup.qml @@ -39,7 +39,7 @@ Popup { return visible ? Math.min( JamiTheme.accountListItemHeight * Math.min( 5, listView.model.count + 1), - mainViewSidePanelRect.height) : 0 + appWindow.height - parent.height) : 0 } padding: 0 modal: true @@ -101,7 +101,7 @@ Popup { onClicked: { root.close() - mainView.startWizard() + viewCoordinator.present("WizardView") } } } diff --git a/src/app/mainview/components/BackupTipBox.qml b/src/app/mainview/components/BackupTipBox.qml index 38012c15aa9eef2ad96233d20964c2b6926a560b..1dbe2b7ad0777b608e256cb413bb5499b076d401 100644 --- a/src/app/mainview/components/BackupTipBox.qml +++ b/src/app/mainview/components/BackupTipBox.qml @@ -33,50 +33,6 @@ Item { signal ignore - PasswordDialog { - id: passwordDialog - - visible: false - purpose: PasswordDialog.ExportAccount - - onDoneSignal: function (success) { - root.ignore() - } - } - - // JamiFileDialog for exporting account - JamiFileDialog { - id: exportDialog - - mode: JamiFileDialog.SaveFile - - title: JamiStrings.backupAccountHere - folder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop" - - nameFilters: [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles] - - onAccepted: { - // Is there password? If so, go to password dialog, else, go to following directly - if (AccountAdapter.hasPassword()) { - passwordDialog.path = UtilsAdapter.getAbsPath(file) - passwordDialog.open() - } else { - if (file.toString().length > 0) - root.ignore() - } - } - - onVisibleChanged: { - if (!visible) { - rejected() - } - } - - onRejected: { - backupBtn.forceActiveFocus() - } - } - ColumnLayout { id: backupLayout @@ -156,7 +112,40 @@ Item { hoveredColor: JamiTheme.buttonTintedGreyHovered pressedColor: JamiTheme.buttonTintedGreyPressed - onClicked: exportDialog.open() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + //objectName: "exportDialog", + title: JamiStrings.backupAccountHere, + fileMode: JamiFileDialog.SaveFile, + folder: StandardPaths.writableLocation( + StandardPaths.HomeLocation) + "/Desktop", + nameFilters: [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function (file) { + // Is there password? If so, go to password dialog, else, go to following directly + if (CurrentAccount.hasArchivePassword) { + var pwdDlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/PasswordDialog.qml", + { + //objectName: "passwordDialog", + path: UtilsAdapter.getAbsPath(file), + purpose: PasswordDialog.ExportAccount + }) + pwdDlg.done.connect(function () { root.ignore() }) + } else { + if (file.toString().length > 0) { + root.ignore() + } + } + }) + dlg.rejected.connect(function () { + backupBtn.forceActiveFocus() + }) + } } } -} \ No newline at end of file +} diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml index 48c4f0c3bc21084db0f0c88ab8434fd30de0b851..2d6fb8060a7790551e24fc84bd9c026f23cadf32 100644 --- a/src/app/mainview/components/CallActionBar.qml +++ b/src/app/mainview/components/CallActionBar.qml @@ -473,6 +473,8 @@ Control { spacing: 0 ListView { + id: itemListView + property bool centeredGroup: true orientation: ListView.Horizontal diff --git a/src/app/mainview/components/CallButtonDelegate.qml b/src/app/mainview/components/CallButtonDelegate.qml index 054b7e0a7ffcb48a72142a973eb4bce813e303f0..5726555764639573c69922cba008ea2a893d8c00 100644 --- a/src/app/mainview/components/CallButtonDelegate.qml +++ b/src/app/mainview/components/CallButtonDelegate.qml @@ -299,8 +299,8 @@ ItemDelegate { if (isVertical) return -implicitWidth - 12 var xValue = -(implicitWidth - root.width) / 2 - 18 - var mainPoint = mapToItem(mainView, xValue, y) - var diff = mainPoint.x + itemListView.implicitWidth - mainView.width + var mainPoint = mapToItem(viewCoordinator.rootView, xValue, y) + var diff = mainPoint.x + itemListView.implicitWidth - viewCoordinator.rootView.width return diff > 0 ? xValue - diff - 24 : xValue } diff --git a/src/app/mainview/components/CallOverlay.qml b/src/app/mainview/components/CallOverlay.qml index 3e51c48cf407c049ed5f2f59f98be3e0777558b1..d6123739a13f0ff2a7a496b78df4974e1a4793c7 100644 --- a/src/app/mainview/components/CallOverlay.qml +++ b/src/app/mainview/components/CallOverlay.qml @@ -42,7 +42,6 @@ Item { signal closeClicked function closeContextMenuAndRelatedWindows() { - ContactPickerCreation.closeContactPicker() sipInputPanel.close() ScreenRubberBandCreation.destroyScreenRubberBandWindow() PluginHandlerPickerCreation.closePluginHandlerPicker() @@ -91,14 +90,17 @@ Item { } } - JamiFileDialog { - id: jamiFileDialog - - mode: JamiFileDialog.Mode.OpenFile - - onAccepted: { - AvAdapter.shareFile(jamiFileDialog.file) - } + function openShareFileDialog() { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + fileMode: JamiFileDialog.OpenFile, + nameFilters: [JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function(file) { + AvAdapter.shareFile(file) + }) } ResponsiveImage { @@ -116,8 +118,7 @@ Item { } function openContactPicker(type) { - ContactPickerCreation.createContactPickerObjects(type, root) - ContactPickerCreation.openContactPicker() + ContactPickerCreation.presentContactPickerPopup(type, root) } function openShareScreen() { @@ -168,7 +169,7 @@ Item { function onStopSharingClicked() { AvAdapter.stopSharing(CurrentCall.sharingSource) } function onShareScreenAreaClicked() { openShareScreenArea() } function onRecordCallClicked() { CallAdapter.recordThisCallToggle() } - function onShareFileClicked() { jamiFileDialog.open() } + function onShareFileClicked() { openShareFileDialog() } function onPluginsClicked() { openPluginsMenu() } function onFullScreenClicked() { root.fullScreenClicked() } } diff --git a/src/app/mainview/components/CallStackView.qml b/src/app/mainview/components/CallStackView.qml index a9bc0178c46141db6d0a965ff23c71b545b7ab95..c5b4419fe1793489c3b0254e18fc254304a2c19e 100644 --- a/src/app/mainview/components/CallStackView.qml +++ b/src/app/mainview/components/CallStackView.qml @@ -18,6 +18,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import net.jami.Models 1.1 import net.jami.Adapters 1.1 @@ -25,9 +26,11 @@ import net.jami.Constants 1.1 import "../../commoncomponents" -Rectangle { +Item { id: root + property alias chatViewContainer: ongoingCallPage.chatViewContainer + property var sipKeys: [ "1", "2", "3", "A", "4", "5", "6", "B", @@ -35,11 +38,6 @@ Rectangle { "*", "0", "#", "D" ] - enum StackNumber { - InitialPageStack, - OngoingPageStack - } - Shortcut { sequence: "Ctrl+D" context: Qt.ApplicationShortcut @@ -58,54 +56,13 @@ Rectangle { } } - // When selected conversation is changed, - // these values will also be changed. - property string responsibleConvUid: "" - property string responsibleAccountId: "" - // TODO: this should all be done by listening to // parent visibility change or parent `Component.onDestruction` function needToCloseInCallConversationAndPotentialWindow() { - // Close potential window, context menu releated windows. ongoingCallPage.closeInCallConversation() ongoingCallPage.closeContextMenuAndRelatedWindows() } - function setLinkedWebview(webViewId) { - ongoingCallPage.setLinkedWebview(webViewId) - } - - function getItemFromStack(itemNumber) { - return callStackMainView.find(function (item) { - return item.stackNumber === itemNumber - }) - } - - function showInitialCallPage(callState, isAudioOnly) { - var itemToFind = getItemFromStack(CallStackView.InitialPageStack) - if (!itemToFind) { - callStackMainView.push(initialCallPage, StackView.Immediate) - } else { - callStackMainView.pop(itemToFind, StackView.Immediate) - } - initialCallPage.callStatus = callState - initialCallPage.isAudioOnly = isAudioOnly - if (initialCallPage.callStatus === Call.Status.INCOMING_RINGING) - initialCallPage.isIncoming = true - else - initialCallPage.isIncoming = false - } - - function showOngoingCallPage() { - var itemToFind = getItemFromStack(CallStackView.OngoingPageStack) - if (!itemToFind) { - callStackMainView.push(ongoingCallPage, StackView.Immediate) - } else { - callStackMainView.pop(itemToFind, StackView.Immediate) - } - ongoingCallPage.accountPeerPair = [responsibleAccountId, responsibleConvUid] - } - function toggleFullScreen() { if (!layoutManager.isCallFullscreen) { layoutManager.pushFullScreenItem( @@ -119,47 +76,29 @@ Rectangle { } } - Connections { - target: CallAdapter - - function onCallStatusChanged(status, accountId, convUid) { - if (callStackMainView.currentItem.stackNumber === CallStackView.InitialPageStack - && responsibleConvUid === convUid && responsibleAccountId === accountId) { - initialCallPage.callStatus = status - } - } - } - - OngoingCallPage { - id: ongoingCallPage - - property int stackNumber: CallStackView.OngoingPageStack - - visible: callStackMainView.currentItem.stackNumber === stackNumber - } - - InitialCallPage { - id: initialCallPage - - property int stackNumber: CallStackView.InitialPageStack - - onCallAccepted: { - CallAdapter.acceptACall(responsibleAccountId, responsibleConvUid) - mainViewSidePanel.selectTab(SidePanelTabBar.Conversations) - } - - onCallCanceled: { - CallAdapter.hangUpACall(responsibleAccountId, responsibleConvUid) - } - - visible: callStackMainView.currentItem.stackNumber === stackNumber - } - - StackView { + StackLayout { id: callStackMainView anchors.fill: parent - initialItem: initialCallPage + property Item currentItem: itemAt(currentIndex) + + currentIndex: { + switch (CurrentCall.status) { + case Call.Status.IN_PROGRESS: + case Call.Status.CONNECTED: + case Call.Status.PAUSED: + return 1 + case Call.Status.SEARCHING: + case Call.Status.CONNECTING: + case Call.Status.INCOMING_RINGING: + case Call.Status.OUTGOING_RINGING: + default: + return 0 + } + } + + InitialCallPage {} + OngoingCallPage { id: ongoingCallPage } } } diff --git a/src/app/mainview/components/ChatView.qml b/src/app/mainview/components/ChatView.qml index f2a207035eb38c3c47d349754effcabedc859a4a..a56e328a1433c4440c0ab4ec727a619b65d0ae91 100644 --- a/src/app/mainview/components/ChatView.qml +++ b/src/app/mainview/components/ChatView.qml @@ -33,35 +33,26 @@ import "../js/pluginhandlerpickercreation.js" as PluginHandlerPickerCreation Rectangle { id: root - property bool allMessagesLoaded + color: JamiTheme.chatviewBgColor + property var mapPositions: PositionManager.mapStatus - property var inCallView: false property int lastContentsSplitSize: JamiTheme.detailsPageMinWidth property int lastDetailsSplitSize: JamiTheme.detailsPageMinWidth property int previousWidth: width + required property bool inCallView - signal needToHideConversationInCall - signal messagesCleared - signal messagesLoaded + signal dismiss - onInCallViewChanged: { - notificationArea.visible = CurrentConversation.activeCalls.length > 0 && !root.inCallView + function focusChatView() { + chatViewFooter.updateMessageDraft() + chatViewFooter.textInput.forceActiveFocus() } - onVisibleChanged: { - if (visible) - return + function resetPanels() { swarmDetailsPanel.visible = false addMemberPanel.visible = false chatContents.visible = true - UtilsAdapter.clearInteractionsCache(CurrentAccount.id, CurrentConversation.id) - } - - function focusChatView() { - chatViewFooter.textInput.forceActiveFocus() - swarmDetailsPanel.visible = false - addMemberPanel.visible = false } function instanceMapObject() { @@ -75,20 +66,19 @@ Rectangle { } } } + Connections { target: PositionManager - function onOpenNewMap() { instanceMapObject() } } - color: JamiTheme.chatviewBgColor - - property string currentConvId: CurrentConversation.id - - HostPopup { - id: hostPopup + Connections { + target: CurrentConversation + function onIdChanged() { + MessagesAdapter.loadMoreMessages() + } } ColumnLayout { @@ -110,13 +100,7 @@ Rectangle { onDropped: chatViewFooter.setFilePathsToSend(drop.urls) } - onBackClicked: { - mainView.showWelcomeView() - } - - onNeedToHideConversationInCall: { - root.needToHideConversationInCall() - } + onBackClicked: root.dismiss() onShowDetailsClicked: { addMemberPanel.visible = false @@ -130,15 +114,15 @@ Rectangle { } onWidthChanged: { - var isExpanding = previousWidth < width - + const isExpanding = previousWidth < width if (!swarmDetailsPanel.visible && !addMemberPanel.visible) return - if (chatViewHeader.width < JamiTheme.detailsPageMinWidth + JamiTheme.chatViewHeaderMinimumWidth && !isExpanding && chatContents.visible) { lastContentsSplitSize = chatContents.width - lastDetailsSplitSize = swarmDetailsPanel.visible ? swarmDetailsPanel.width : addMemberPanel.width + lastDetailsSplitSize = Math.min(JamiTheme.detailsPageMinWidth, (swarmDetailsPanel.visible ? + swarmDetailsPanel.width : + addMemberPanel.width)) chatContents.visible = false } else if (chatViewHeader.width >= JamiTheme.chatViewHeaderMinimumWidth + lastDetailsSplitSize && isExpanding && !layoutManager.isFullScreen && !chatContents.visible) { @@ -158,7 +142,9 @@ Rectangle { } function onNeedsHost() { - hostPopup.open() + viewCoordinator.presentDialog( + appWindow, + "mainview/components/HostPopup.qml") } } @@ -245,18 +231,18 @@ Rectangle { handle: Rectangle { implicitWidth: JamiTheme.splitViewHandlePreferredWidth - implicitHeight: splitView.height + implicitHeight: viewCoordinator.splitView.height color: JamiTheme.primaryBackgroundColor Rectangle { implicitWidth: 1 - implicitHeight: splitView.height + implicitHeight: viewCoordinator.splitView.height color: JamiTheme.tabbarBorderColor } } ColumnLayout { id: chatContents - SplitView.maximumWidth: splitView.width + SplitView.maximumWidth: viewCoordinator.splitView.width SplitView.minimumWidth: JamiTheme.chatViewHeaderMinimumWidth SplitView.preferredWidth: chatViewHeader.width - @@ -333,7 +319,7 @@ Rectangle { id: swarmDetailsPanel visible: false - SplitView.maximumWidth: splitView.width + SplitView.maximumWidth: viewCoordinator.splitView.width SplitView.preferredWidth: JamiTheme.detailsPageMinWidth SplitView.minimumWidth: JamiTheme.detailsPageMinWidth } @@ -342,7 +328,7 @@ Rectangle { id: addMemberPanel visible: false - SplitView.maximumWidth: splitView.width + SplitView.maximumWidth: viewCoordinator.splitView.width SplitView.preferredWidth: JamiTheme.detailsPageMinWidth SplitView.minimumWidth: JamiTheme.detailsPageMinWidth } diff --git a/src/app/mainview/components/ChatViewFooter.qml b/src/app/mainview/components/ChatViewFooter.qml index 5dcbe0f0138de55d556a681184acaf84835eef8b..ce840d554644b4ce8aaed7a2804c9b85ed5980d7 100644 --- a/src/app/mainview/components/ChatViewFooter.qml +++ b/src/app/mainview/components/ChatViewFooter.qml @@ -30,8 +30,8 @@ Rectangle { id: root property alias textInput: messageBar.textAreaObj - property string previousConvId: "" - property string previousAccountId: "" + property string previousConvId + property string previousAccountId function setFilePathsToSend(filePaths) { for (var index = 0; index < filePaths.length; ++index) { @@ -44,39 +44,34 @@ Rectangle { color: JamiTheme.primaryBackgroundColor - Connections { - target: LRCInstance + function updateMessageDraft() { + LRCInstance.setContentDraft(previousConvId, + previousAccountId, + messageBar.text); - function onSelectedConvUidChanged() { - // Handle Draft - if (previousConvId !== "" && previousAccountId != "") { - LRCInstance.setContentDraft(previousConvId, previousAccountId, - messageBar.text); - } + previousConvId = CurrentConversation.id + previousAccountId = CurrentAccount.id - // turn off the button animations when switching convs - messageBar.animate = false + // turn off the button animations when switching convs + messageBar.animate = false + messageBar.textAreaObj.clearText() - messageBar.textAreaObj.clearText() - previousConvId = LRCInstance.selectedConvUid - previousAccountId = LRCInstance.currentAccountId + var restoredContent = LRCInstance.getContentDraft(CurrentConversation.id, + CurrentAccount.id); + if (restoredContent) { + messageBar.textAreaObj.insertText(restoredContent) + } + } - var restoredContent = LRCInstance.getContentDraft(LRCInstance.selectedConvUid, - LRCInstance.currentAccountId); - if (restoredContent) - messageBar.textAreaObj.insertText(restoredContent) + Connections { + target: CurrentConversation - messageBar.animate = true - } + function onIdChanged() { messageBar.animate = true } } Connections { target: MessagesAdapter - function onNewMessageBarPlaceholderText(placeholderText) { - messageBar.textAreaObj.placeholderText = JamiStrings.writeTo.arg(placeholderText) - } - function onNewFilePasted(filePath) { dataTransferSendContainer.filesToSendListModel.addToPending(filePath) } @@ -106,14 +101,6 @@ Rectangle { visible: false } - JamiFileDialog { - id: jamiFileDialog - - mode: JamiFileDialog.Mode.OpenFiles - - onAccepted: setFilePathsToSend(jamiFileDialog.files) - } - ColumnLayout { id: footerColumnLayout @@ -193,7 +180,19 @@ Rectangle { PositionManager.setMapActive(CurrentAccount.id) } - onSendFileButtonClicked: jamiFileDialog.open() + onSendFileButtonClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + fileMode: JamiFileDialog.OpenFiles, + nameFilters: [JamiStrings.allFiles] + }) + dlg.filesAccepted.connect(function(files) { + setFilePathsToSend(files) + }) + } + onSendMessageButtonClicked: { // Send text message if (messageBar.text) { diff --git a/src/app/mainview/components/ChatViewHeader.qml b/src/app/mainview/components/ChatViewHeader.qml index dbb3b5c1a8c0809677821643455ab264dc68a869..66a820928f6981d19a6f7749b2552f2eb5224f08 100644 --- a/src/app/mainview/components/ChatViewHeader.qml +++ b/src/app/mainview/components/ChatViewHeader.qml @@ -31,7 +31,6 @@ Rectangle { id: root signal backClicked - signal needToHideConversationInCall signal addToConversationClicked signal pluginSelector signal showDetailsClicked @@ -87,9 +86,7 @@ Rectangle { normalColor: JamiTheme.chatviewBgColor imageColor: JamiTheme.chatviewButtonColor - onClicked: CurrentConversation.inCall ? - root.needToHideConversationInCall() : - root.backClicked() + onClicked: root.backClicked() } Rectangle { @@ -133,7 +130,7 @@ Rectangle { Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft visible: text.length && - CurrentConversation.title != CurrentConversation.description + CurrentConversation.title !== CurrentConversation.description font.pointSize: JamiTheme.textFontSize color: JamiTheme.faddedLastInteractionFontColor diff --git a/src/app/mainview/components/ConversationListView.qml b/src/app/mainview/components/ConversationListView.qml index 06a2421d64856664514473dd97103358f94d9f4e..363dff5d02e4ff61bf698d81e9583b7d15399cd9 100644 --- a/src/app/mainview/components/ConversationListView.qml +++ b/src/app/mainview/components/ConversationListView.qml @@ -75,11 +75,6 @@ JamiListView { onCountChanged: positionViewAtBeginning() - Component.onCompleted: { - // TODO: remove this - ConversationsAdapter.setQmlObject(this) - } - add: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1.0 @@ -113,42 +108,31 @@ JamiListView { ConversationSmartListContextMenu { id: contextMenu - property var index: -1 + property int index: -1 function openMenuAt(x, y) { contextMenu.x = x contextMenu.y = y - // TODO: - // - accountId, convId only - // - userProfile dialog should use a loader/popup - - var row = root.indexAt(x, y + root.contentY) - index = row - var item = { - "convId": model.dataForRow(row, ConversationList.UID), - "displayId": model.dataForRow(row, ConversationList.BestId), - "title": model.dataForRow(row, ConversationList.Title), - "uri": model.dataForRow(row, ConversationList.URI), - "isBanned": model.dataForRow(row, ConversationList.IsBanned), - "mode": model.dataForRow(row, ConversationList.Mode), - "isCoreDialog": model.dataForRow(row, ConversationList.IsCoreDialog), - "isTemporary": model.dataForRow(row, ConversationList.ContactType) === Profile.Type.TEMPORARY, - } + index = root.indexAt(x, y + root.contentY) + // TODO: use accountId and convId only responsibleAccountId = LRCInstance.currentAccountId - responsibleConvUid = item.convId - isBanned = item.isBanned - mode = item.mode - isCoreDialog = item.isCoreDialog + responsibleConvUid = model.dataForRow(index, ConversationList.UID) + isBanned = model.dataForRow(index, ConversationList.IsBanned) + mode = model.dataForRow(index, ConversationList.Mode) + isCoreDialog = model.dataForRow(index, ConversationList.IsCoreDialog) contactType = LRCInstance.currentAccountType - readOnly = mode === Conversation.Mode.NON_SWARM && !item.isTemporary && CurrentAccount.type !== Profile.Type.SIP - - if (model.dataForRow(row, ConversationList.IsCoreDialog)) { - userProfile.aliasText = item.title - userProfile.registeredNameText = item.displayId - userProfile.idText = item.uri - userProfile.convId = item.convId + readOnly = mode === Conversation.Mode.NON_SWARM && + (model.dataForRow(index, ConversationList.ContactType) !== + Profile.Type.TEMPORARY) && + CurrentAccount.type !== Profile.Type.SIP + + // For UserProfile dialog. + if (isCoreDialog) { + aliasText = model.dataForRow(index, ConversationList.Title) + registeredNameText = model.dataForRow(index, ConversationList.BestId) + idText = model.dataForRow(index, ConversationList.URI) } openMenu() @@ -182,18 +166,16 @@ JamiListView { context: Qt.ApplicationShortcut enabled: root.visible onActivated: MessagesAdapter.clearConversationHistory( - LRCInstance.currentAccountId, - LRCInstance.selectedConvUid) + CurrentAccount.id, + CurrentConversation.id) } Shortcut { sequence: "Ctrl+Shift+B" context: Qt.ApplicationShortcut enabled: root.visible - onActivated: { - MessagesAdapter.blockConversation( - LRCInstance.selectedConvUid) - } + onActivated: MessagesAdapter.blockConversation( + CurrentConversation.id) } Shortcut { diff --git a/src/app/mainview/components/ConversationSmartListContextMenu.qml b/src/app/mainview/components/ConversationSmartListContextMenu.qml index ccaf083735f84e3fa462eb48ea93a54958c2d152..291c8b5786cfcf17d02a5a4212410439ec21a92c 100644 --- a/src/app/mainview/components/ConversationSmartListContextMenu.qml +++ b/src/app/mainview/components/ConversationSmartListContextMenu.qml @@ -30,29 +30,6 @@ ContextMenuAutoLoader { signal showSwarmDetails - ConfirmDialog { - id: rmDialog - - title: JamiStrings.confirmAction - textLabel: JamiStrings.confirmRmConversation - confirmLabel: JamiStrings.optionRemove - onAccepted: { - if (!isCoreDialog) - MessagesAdapter.removeConversation(responsibleConvUid) - else - MessagesAdapter.removeContact(responsibleConvUid) - } - } - - ConfirmDialog { - id: blockDialog - - title: JamiStrings.confirmAction - textLabel: JamiStrings.confirmBlockConversation - confirmLabel: JamiStrings.optionBlock - onAccepted: MessagesAdapter.blockConversation(responsibleConvUid) - } - property string responsibleAccountId: "" property string responsibleConvUid: "" property bool isBanned: false @@ -67,6 +44,11 @@ ContextMenuAutoLoader { } property bool readOnly + // For UserProfile dialog. + property string aliasText + property string registeredNameText + property string idText + property list<GeneralMenuItem> menuItems: [ GeneralMenuItem { id: startVideoCallItem @@ -114,7 +96,22 @@ ContextMenuAutoLoader { return JamiStrings.removeContact } iconSource: JamiResources.ic_hangup_participant_24dp_svg - onClicked: rmDialog.open() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/ConfirmDialog.qml", + { + title: JamiStrings.confirmAction, + textLabel: JamiStrings.confirmRmConversation, + confirmLabel: JamiStrings.optionRemove + }) + dlg.accepted.connect(function() { + if (!isCoreDialog) + MessagesAdapter.removeConversation(responsibleConvUid) + else + MessagesAdapter.removeContact(responsibleConvUid) + }) + } }, GeneralMenuItem { id: hangup @@ -151,7 +148,19 @@ ContextMenuAutoLoader { itemName: !(mode && isCoreDialog) ? JamiStrings.blockContact : JamiStrings.blockSwarm iconSource: JamiResources.block_black_24dp_svg addMenuSeparatorAfter: canTrigger - onClicked: blockDialog.open() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/ConfirmDialog.qml", + { + title: JamiStrings.confirmAction, + textLabel: JamiStrings.confirmBlockConversation, + confirmLabel: JamiStrings.optionBlock + }) + dlg.accepted.connect(function() { + MessagesAdapter.blockConversation(responsibleConvUid) + }) + } }, GeneralMenuItem { id: unblockContact @@ -169,10 +178,19 @@ ContextMenuAutoLoader { itemName: isCoreDialog ? JamiStrings.contactDetails : JamiStrings.convDetails iconSource: JamiResources.person_24dp_svg onClicked: { - if (isCoreDialog) - userProfile.open() - else + if (isCoreDialog) { + viewCoordinator.presentDialog( + appWindow, + "mainview/components/UserProfile.qml", + { + aliasText: aliasText, + registeredNameText: registeredNameText, + idText: idText, + convId: responsibleConvUid + }) + } else { root.showSwarmDetails() + } } } ] diff --git a/src/app/mainview/components/FilterTabButton.qml b/src/app/mainview/components/FilterTabButton.qml index a57872dd34892be672344c61d16b2485a3a365c3..caa40094c6cd1701c1c4ac6829e4062ba914152f 100644 --- a/src/app/mainview/components/FilterTabButton.qml +++ b/src/app/mainview/components/FilterTabButton.qml @@ -84,7 +84,9 @@ TabButton { } Rectangle { - width: underlineContentOnly ? informations.width + JamiTheme.menuBorderPreferredHeight : contentRect.width + width: underlineContentOnly ? + informations.width + JamiTheme.menuBorderPreferredHeight : + contentRect.width anchors.horizontalCenter: contentRect.horizontalCenter anchors.bottom: contentRect.bottom height: borderWidth diff --git a/src/app/mainview/components/HostPopup.qml b/src/app/mainview/components/HostPopup.qml index ec8593ec1c93691ce52b07f2e42fb575f9d1a6bc..ad65a86e5b0b0643316e64ab2e25ee02fcb36093 100644 --- a/src/app/mainview/components/HostPopup.qml +++ b/src/app/mainview/components/HostPopup.qml @@ -108,4 +108,4 @@ BaseModalDialog { } } } -} \ No newline at end of file +} diff --git a/src/app/mainview/components/InitialCallPage.qml b/src/app/mainview/components/InitialCallPage.qml index dac1050fc928fb195e2f9c4e0b2e0781d8521157..fb52052f319c20baf883a5086e1f6954b958c970 100644 --- a/src/app/mainview/components/InitialCallPage.qml +++ b/src/app/mainview/components/InitialCallPage.qml @@ -30,25 +30,24 @@ import "../../commoncomponents" Rectangle { id: root - property bool isIncoming: false - property bool isAudioOnly: CurrentCall.isAudioOnly - property int callStatus: 0 - signal callCanceled signal callAccepted + onCallAccepted: CallAdapter.acceptACall(CurrentAccount.id, CurrentConversation.id) + onCallCanceled: CallAdapter.hangUpACall(CurrentAccount.id, CurrentConversation.id) + color: "black" LocalVideo { id: previewRenderer anchors.centerIn: parent anchors.fill: parent - visible: !root.isAudioOnly && + visible: !CurrentCall.isAudioOnly && CurrentAccount.videoEnabled_Video && VideoDevices.listSize !== 0 && - ((callStatus >= Call.Status.INCOMING_RINGING - && callStatus <= Call.Status.SEARCHING) - || callStatus === Call.Status.CONNECTED) + ((CurrentCall.status >= Call.Status.INCOMING_RINGING + && CurrentCall.status <= Call.Status.SEARCHING) + || CurrentCall.status === Call.Status.CONNECTED) opacity: 0.5 // HACK: this is a workaround to the preview video starting @@ -150,8 +149,8 @@ Rectangle { property string title: CurrentConversation.title text: { - if (root.isIncoming) - return root.isAudioOnly ? + if (!CurrentCall.isOutgoing) + return CurrentCall.isAudioOnly ? JamiStrings.incomingAudioCallFrom.replace("{}", title) : JamiStrings.incomingVideoCallFrom.replace("{}", title) else @@ -159,7 +158,7 @@ Rectangle { } wrapMode: Text.WordWrap elide: Text.ElideRight - maximumLineCount: root.isIncoming ? 2 : 1 + maximumLineCount: !CurrentCall.isOutgoing ? 2 : 1 color: "white" } @@ -173,9 +172,9 @@ Rectangle { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - text: UtilsAdapter.getCallStatusStr(callStatus) + "…" + text: UtilsAdapter.getCallStatusStr(CurrentCall.status) + "…" color: JamiTheme.whiteColor - visible: !root.isIncoming + visible: CurrentCall.isOutgoing } RowLayout { @@ -184,10 +183,10 @@ Rectangle { Repeater { id: controlButtons - model: root.isIncoming ? incomingControlsModel : outgoingControlsModel + model: !CurrentCall.isOutgoing ? incomingControlsModel : outgoingControlsModel delegate: ColumnLayout { - visible: (type === "cam" && root.isAudioOnly) ? false : true; + visible: (type === "cam" && CurrentCall.isAudioOnly) ? false : true PushButton { id: actionButton @@ -223,7 +222,7 @@ Rectangle { acceptVideoMedia = true else if ( type === "mic" ) acceptVideoMedia = false - CallAdapter.setCallMedia(responsibleAccountId, responsibleConvUid, acceptVideoMedia) + CallAdapter.setCallMedia(CurrentAccount.id, CurrentConversation.id, acceptVideoMedia) callAccepted() } else { callCanceled() @@ -248,7 +247,7 @@ Rectangle { else if (type === "cam") return JamiStrings.acceptVideo else if (type === "mic") - return root.isAudioOnly ? JamiStrings.accept : JamiStrings.acceptAudio + return CurrentCall.isAudioOnly ? JamiStrings.accept : JamiStrings.acceptAudio else if (type === "cancel") return JamiStrings.endCall return "" diff --git a/src/app/mainview/components/JamiIdentifier.qml b/src/app/mainview/components/JamiIdentifier.qml index 83f9fa950fc1468623de3ae2e8d35a4db5d6d217..a7664cf919c40edcd717d1c3d049e4afcf5267a0 100644 --- a/src/app/mainview/components/JamiIdentifier.qml +++ b/src/app/mainview/components/JamiIdentifier.qml @@ -30,12 +30,6 @@ import "../../settingsview/components" Item { id: root - NameRegistrationDialog { - id : nameRegistrationDialog - onAccepted: usernameTextEdit.nameRegistrationState = - UsernameLineEdit.NameRegistrationState.BLANK - } - width: childrenRect.width height: controlsLayout.height + usernameTextEdit.height + 2 * JamiTheme.preferredMarginSize @@ -154,7 +148,19 @@ Item { Layout.rightMargin: JamiTheme.preferredMarginSize fontPointSize: JamiTheme.textFontSize + 1 - onAccepted: nameRegistrationDialog.openNameRegistrationDialog(dynamicText) + onAccepted: { + if (dynamicText === '') { + return + } + var dlg = viewCoordinator.presentDialog( + appWindow, + "settingsview/components/NameRegistrationDialog.qml", + { registeredName: dynamicText }) + dlg.accepted.connect(function() { + currentRegisteredID.nameRegistrationState = + UsernameLineEdit.NameRegistrationState.BLANK + }) + } } } } diff --git a/src/app/mainview/components/MainOverlay.qml b/src/app/mainview/components/MainOverlay.qml index e5cd2cb38263449b0fefc9ad0d6ca75c5fea9184..86d6b282a71b5d59c58fee47e8abbe2d51cadfbd 100644 --- a/src/app/mainview/components/MainOverlay.qml +++ b/src/app/mainview/components/MainOverlay.qml @@ -68,10 +68,10 @@ Item { // (un)subscribe to an app-wide mouse move event trap filtered // for the overlay's geometry - onVisibleChanged: visible ? CallOverlayModel.registerFilter( - appWindow, - this) : CallOverlayModel.unregisterFilter( - appWindow, this) + onVisibleChanged: { + visible ? CallOverlayModel.registerFilter(appWindow, this) : + CallOverlayModel.unregisterFilter(appWindow, this) + } Connections { target: CallOverlayModel @@ -84,7 +84,6 @@ Item { } } - Shortcut { sequence: "M" enabled: root.visible @@ -129,7 +128,7 @@ Item { root.timeText = CallAdapter.getCallDurationTime( LRCInstance.currentAccountId, LRCInstance.selectedConvUid) - if (root.opacity === 0 && !CurrentCall.isRecordingRemotely) + if (!root.opacity && !CurrentCall.isRecordingRemotely) root.remoteRecordingLabel = "" } } diff --git a/src/app/mainview/components/MessageBar.qml b/src/app/mainview/components/MessageBar.qml index a856ecb437834427605602f10abb6e8d3d1dea4b..3ca1e60313233f1405b615b7acbe39a259cea362 100644 --- a/src/app/mainview/components/MessageBar.qml +++ b/src/app/mainview/components/MessageBar.qml @@ -162,6 +162,8 @@ ColumnLayout { textAreaObj.forceActiveFocus() } + placeholderText: JamiStrings.writeTo.arg(CurrentConversation.title) + Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.margins: marginSize / 2 diff --git a/src/app/mainview/components/MessageListView.qml b/src/app/mainview/components/MessageListView.qml index 2e79c2f256dd5d4fab0cf5167eb7b105a06a3f64..ce5698d7c428ad2d0ce7affabd3fffc8730f55dc 100644 --- a/src/app/mainview/components/MessageListView.qml +++ b/src/app/mainview/components/MessageListView.qml @@ -168,9 +168,6 @@ JamiListView { topMargin: 12 spacing: 2 - anchors.centerIn: parent - height: parent.height - width: parent.width // this offscreen caching is pretty huge // displayMarginEnd may be removed diff --git a/src/app/mainview/components/NewSwarmPage.qml b/src/app/mainview/components/NewSwarmPage.qml index 20f195ffccf224d30cec45a2a3d54cb95382922f..a529cf1d580d09fbcdede5585cad99166b1a3539 100644 --- a/src/app/mainview/components/NewSwarmPage.qml +++ b/src/app/mainview/components/NewSwarmPage.qml @@ -26,8 +26,7 @@ import net.jami.Constants 1.1 import "../../commoncomponents" - -Rectangle { +BaseView { id: root color: JamiTheme.chatviewBgColor @@ -217,9 +216,9 @@ Rectangle { text: JamiStrings.createTheSwarm - onClicked: { - createSwarmClicked(title.text, description.text, UtilsAdapter.tempCreationImage()) - } + onClicked: createSwarmClicked(title.text, + description.text, + UtilsAdapter.tempCreationImage()) } } } diff --git a/src/app/mainview/components/OngoingCallPage.qml b/src/app/mainview/components/OngoingCallPage.qml index 21c079fa4421d1fefff0c16c00fd5a0cd9c3160e..ebafe082312cc73d38f687d2aabee9d977efd2be 100644 --- a/src/app/mainview/components/OngoingCallPage.qml +++ b/src/app/mainview/components/OngoingCallPage.qml @@ -33,16 +33,13 @@ import "../../commoncomponents" Rectangle { id: root - anchors.fill: parent - - property var accountPeerPair: ["", ""] - property variant clickPos: "1,1" + property point clickPos property int previewMargin: 15 property int previewMarginYTop: previewMargin + 42 property int previewMarginYBottom: previewMargin + 84 property int previewToX: 0 property int previewToY: 0 - property var linkedWebview: null + property alias chatViewContainer: chatViewContainer property string callPreviewId onCallPreviewIdChanged: { @@ -51,18 +48,11 @@ Rectangle { color: "black" - onAccountPeerPairChanged: { - if (accountPeerPair[0] === "" || accountPeerPair[1] === "") - return - contactImage.imageId = accountPeerPair[1] - } - - function setLinkedWebview(webViewId) { - linkedWebview = webViewId - linkedWebview.needToHideConversationInCall.disconnect( - closeInCallConversation) - linkedWebview.needToHideConversationInCall.connect( - closeInCallConversation) + Connections { + target: CurrentConversation + function onIdChanged() { + if (CurrentConversation.id !== "") contactImage.imageId = CurrentConversation.id + } } Connections { @@ -75,13 +65,11 @@ Rectangle { function openInCallConversation() { mainColumnLayout.isHorizontal = UtilsAdapter.getAppValue(Settings.Key.ShowChatviewHorizontally) - inCallMessageWebViewStack.visible = true - inCallMessageWebViewStack.push(linkedWebview) + chatViewContainer.visible = true } function closeInCallConversation() { - inCallMessageWebViewStack.visible = false - inCallMessageWebViewStack.clear() + chatViewContainer.visible = false } function closeContextMenuAndRelatedWindows() { @@ -329,10 +317,12 @@ Rectangle { function onNewInteraction(id, interactionType) { // Ignore call notifications, as we are in the call. - if (interactionType !== Interaction.Type.CALL && !inCallMessageWebViewStack.visible) + if (interactionType !== Interaction.Type.CALL && + !chatViewContainer.visible) openInCallConversation() } } + onCloseClicked: { participantsLayer.hoveredOverlayUri = "" participantsLayer.hoveredOverlaySinkId = "" @@ -340,7 +330,7 @@ Rectangle { } onChatButtonClicked: { - inCallMessageWebViewStack.visible ? + chatViewContainer.visible ? closeInCallConversation() : openInCallConversation() } @@ -352,6 +342,7 @@ Rectangle { ColumnLayout { id: audioCallPageRectCentralRect + anchors.centerIn: parent anchors.left: parent.left anchors.right: parent.right @@ -390,15 +381,16 @@ Rectangle { color: "transparent" } - StackView { - id: inCallMessageWebViewStack - - SplitView.preferredHeight: mainColumnLayout.isHorizontal ? root.height : root.height / 3 - SplitView.preferredWidth: mainColumnLayout.isHorizontal ? root.width / 3 : root.width - SplitView.fillWidth: false + Item { + id: chatViewContainer + SplitView.preferredHeight: mainColumnLayout.isHorizontal ? + root.height : + root.height / 3 + SplitView.preferredWidth: mainColumnLayout.isHorizontal ? + root.width / 3 : + root.width visible: false - clip: true } } diff --git a/src/app/mainview/components/SidePanel.qml b/src/app/mainview/components/SidePanel.qml index 4a6e00a0acf4f7e47b6ac9d66e6c066ab014b218..dca69069cb8aa3bae6c2770313488c9b9bbfc963 100644 --- a/src/app/mainview/components/SidePanel.qml +++ b/src/app/mainview/components/SidePanel.qml @@ -27,14 +27,14 @@ import net.jami.Enums 1.1 import net.jami.Models 1.1 import "../../commoncomponents" +import "../../settingsview/components" -Rectangle { +BaseView { id: root + objectName: "SidePanel" color: JamiTheme.backgroundColor - signal createSwarmClicked - Connections { target: LRCInstance @@ -52,6 +52,43 @@ Rectangle { } } + Connections { + target: ConversationsAdapter + + function onShowSearchStatus(status) { + searchStatusText.text = status + } + + function onTextFilterChanged(text) { + // In the swarm details, "Go to conversation" can + // change the search bar. Be sure to be synced + contactSearchBar.textContent = text + } + } + + function toggleCreateSwarmView() { + if (!viewCoordinator.inNewSwarm) { + viewCoordinator.present("NewSwarmPage") + const newSwarmPage = viewCoordinator.currentView + newSwarmPage.removeMember.connect((convId, member) => { + removeMember(convId, member) + }) + newSwarmPage.createSwarmClicked.connect((title, description, avatar) => { + var uris = [] + for (var idx in newSwarmPage.members) { + var uri = newSwarmPage.members[idx].uri + if (uris.indexOf(uri) === -1) { + uris.push(uri) + } + } + ConversationsAdapter.createSwarm(title, description, avatar, uris) + viewCoordinator.dismiss("NewSwarmPage") + }) + } else { + viewCoordinator.dismiss("NewSwarmPage") + } + } + function clearContactSearchBar() { contactSearchBar.clearText() } @@ -62,6 +99,12 @@ Rectangle { property var highlighted: [] property var highlightedMembers: [] + onHighlightedMembersChanged: { + if (viewCoordinator.inNewSwarm) { + const newSwarmPage = viewCoordinator.getView("NewSwarmPage") + newSwarmPage.members = highlightedMembers + } + } function refreshHighlighted(convId, highlightedStatus) { var newH = root.highlighted @@ -72,7 +115,8 @@ Rectangle { var added = false for (var idx in item.uris) { var uri = item.uris[idx] - if (!Array.from(newHm).find(r => r.uri === uri) && uri != CurrentAccount.uri) { + if (!Array.from(newHm).find(r => r.uri === uri) && + uri !== CurrentAccount.uri) { newHm.push({"uri": uri, "convId": convId}) added = true } @@ -106,9 +150,9 @@ Rectangle { var newHm = [] for (var hm in root.highlightedMembers) { var m = root.highlightedMembers[hm] - if (m.convId == convId && m.uri == member) { + if (m.convId === convId && m.uri === member) { continue; - } else if (m.convId == convId) { + } else if (m.convId === convId) { refreshHighlighted = false } newHm.push(m) @@ -119,296 +163,318 @@ Rectangle { // Remove highlighted status if necessary for (var d in swarmCurrentConversationList.contentItem.children) { var delegate = swarmCurrentConversationList.contentItem.children[d] - if (delegate.convId == convId) + if (delegate.convId === convId) delegate.highlighted = false } } } - function showSwarmListView(v) { - smartListLayout.visible = !v - swarmMemberSearchList.visible = v - } + Page { + id: page - RowLayout { - id: titleBar + anchors.fill: parent - visible: swarmMemberSearchList.visible - - height: 40 - anchors.top: root.top - anchors.topMargin: 10 - anchors.left: root.left - anchors.leftMargin: 15 - anchors.right: root.right - anchors.rightMargin: 15 - - Label { - id: title + background: Rectangle { + color: JamiTheme.backgroundColor + } - height: parent.height - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + header: AccountComboBox { + width: parent.width + height: JamiTheme.accountListItemHeight + onSettingBtnClicked: { + !inSettingsView ? + viewCoordinator.present("SettingsView") : + viewCoordinator.dismiss("SettingsView")} + } - color: JamiTheme.textColor + StackLayout { + anchors.fill: parent - font.bold: true - font.pointSize: JamiTheme.contactEventPointSize + currentIndex: viewCoordinator.inSettings ? 0 : 1 - text: JamiStrings.createSwarm - } + SettingsMenu { + id: settingsMenu + objectName: "settingsMenu" - PushButton { - radius: JamiTheme.primaryRadius + Layout.fillWidth: true + Layout.fillHeight: true + } - imageColor: JamiTheme.textColor - imagePadding: 8 - normalColor: JamiTheme.secondaryBackgroundColor + Item { + Layout.fillWidth: true + Layout.fillHeight: true - preferredSize: titleBar.height + RowLayout { + id: titleBar - source: JamiResources.round_close_24dp_svg - toolTipText: JamiStrings.cancel + visible: swarmMemberSearchList.visible - onClicked: createSwarmClicked() - } - } + height: 40 + anchors.top: parent.top + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.right: parent.right + anchors.rightMargin: 15 - RowLayout { - id: startBar - - height: 40 - anchors.top: titleBar.visible ? titleBar.bottom : root.top - anchors.topMargin: 10 - anchors.left: root.left - anchors.leftMargin: 15 - anchors.right: root.right - anchors.rightMargin: 15 - - ContactSearchBar { - id: contactSearchBar - - Layout.fillHeight: true - Layout.fillWidth: true - - onContactSearchBarTextChanged: function (text) { - // not calling positionViewAtBeginning will cause - // sort animation visual bugs - conversationListView.positionViewAtBeginning() - ConversationsAdapter.ignoreFiltering(root.highlighted) - ConversationsAdapter.setFilter(text) - } + Label { + id: title - onReturnPressedWhileSearching: { - var listView = searchResultsListView.count ? - searchResultsListView : - conversationListView - if (listView.count) - listView.model.select(0) - } - } + height: parent.height + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter - PushButton { - id: startConversation + color: JamiTheme.textColor - Layout.alignment: Qt.AlignLeft - radius: JamiTheme.primaryRadius + font.bold: true + font.pointSize: JamiTheme.contactEventPointSize - imageColor: JamiTheme.textColor - imagePadding: 8 - normalColor: JamiTheme.secondaryBackgroundColor + text: JamiStrings.createSwarm + } - preferredSize: startBar.height + PushButton { + radius: JamiTheme.primaryRadius - visible: !swarmMemberSearchList.visible && CurrentAccount.type !== Profile.Type.SIP + imageColor: JamiTheme.textColor + imagePadding: 8 + normalColor: JamiTheme.secondaryBackgroundColor - source: smartListLayout.visible ? JamiResources.create_swarm_svg : JamiResources.round_close_24dp_svg - toolTipText: smartListLayout.visible ? JamiStrings.startSwarm : JamiStrings.cancel + preferredSize: titleBar.height - onClicked: createSwarmClicked() - } - } + source: JamiResources.round_close_24dp_svg + toolTipText: JamiStrings.cancel - SidePanelTabBar { - id: sidePanelTabBar + onClicked: toggleCreateSwarmView() + } + } - visible: ConversationsAdapter.pendingRequestCount && - !contactSearchBar.textContent && smartListLayout.visible - anchors.top: startBar.bottom - anchors.topMargin: visible ? 10 : 0 - width: root.width - height: visible ? 42 : 0 - contentHeight: visible ? 42 : 0 - } + RowLayout { + id: startBar + + height: 40 + anchors.top: titleBar.visible ? titleBar.bottom : parent.top + anchors.topMargin: 10 + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.right: parent.right + anchors.rightMargin: 15 + + ContactSearchBar { + id: contactSearchBar + + Layout.fillHeight: true + Layout.fillWidth: true + + onContactSearchBarTextChanged: function (text) { + print(text) + // not calling positionViewAtBeginning will cause + // sort animation visual bugs + conversationListView.positionViewAtBeginning() + ConversationsAdapter.ignoreFiltering(root.highlighted) + ConversationsAdapter.setFilter(text) + } + + onReturnPressedWhileSearching: { + var listView = searchResultsListView.count ? + searchResultsListView : + conversationListView + if (listView.count) + listView.model.select(0) + } + } - Rectangle { - id: searchStatusRect + PushButton { + id: startConversation - visible: searchStatusText.text !== "" && smartListLayout.visible + Layout.alignment: Qt.AlignLeft + radius: JamiTheme.primaryRadius - anchors.top: sidePanelTabBar.bottom - anchors.topMargin: visible ? 10 : 0 - width: parent.width - height: visible ? 42 : 0 + imageColor: JamiTheme.textColor + imagePadding: 8 + normalColor: JamiTheme.secondaryBackgroundColor - color: JamiTheme.backgroundColor + preferredSize: startBar.height - Text { - id: searchStatusText + visible: !swarmMemberSearchList.visible && CurrentAccount.type !== Profile.Type.SIP - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 32 - anchors.right: parent.right - anchors.rightMargin: 32 - color: JamiTheme.textColor - wrapMode: Text.WordWrap - font.pointSize: JamiTheme.filterItemFontSize - } - } + source: smartListLayout.visible ? JamiResources.create_swarm_svg : JamiResources.round_close_24dp_svg + toolTipText: smartListLayout.visible ? JamiStrings.startSwarm : JamiStrings.cancel - Connections { - target: ConversationsAdapter + onClicked: toggleCreateSwarmView() + } + } - function onShowSearchStatus(status) { - searchStatusText.text = status - } + SidePanelTabBar { + id: sidePanelTabBar - function onTextFilterChanged(text) { - // In the swarm details, "Go to conversation" can - // change the search bar. Be sure to be synced - contactSearchBar.textContent = text - } - } + visible: ConversationsAdapter.pendingRequestCount && + !contactSearchBar.textContent && smartListLayout.visible + anchors.top: startBar.bottom + anchors.topMargin: visible ? 10 : 0 + width: page.width + height: visible ? 42 : 0 + contentHeight: visible ? 42 : 0 + } - ColumnLayout { - id: smartListLayout - - width: parent.width - anchors.top: searchStatusRect.bottom - anchors.topMargin: (sidePanelTabBar.visible || - searchStatusRect.visible) ? 0 : 12 - anchors.bottom: parent.bottom - - spacing: 4 - - ConversationListView { - id: searchResultsListView - - visible: count - opacity: visible ? 1 : 0 - - Layout.topMargin: 10 - Layout.alignment: Qt.AlignTop - Layout.fillWidth: true - Layout.preferredHeight: visible ? contentHeight : 0 - Layout.maximumHeight: { - var otherContentHeight = conversationListView.contentHeight + 16 - if (conversationListView.visible) - if (otherContentHeight < parent.height / 2) - return parent.height - otherContentHeight - else - return parent.height / 2 - else - return parent.height - } + Rectangle { + id: searchStatusRect - model: SearchResultsListModel - headerLabel: JamiStrings.searchResults - headerVisible: visible - } + visible: searchStatusText.text !== "" && smartListLayout.visible - ConversationListView { - id: conversationListView + anchors.top: sidePanelTabBar.bottom + anchors.topMargin: visible ? 10 : 0 + width: parent.width + height: visible ? 42 : 0 - visible: count + color: JamiTheme.backgroundColor - Layout.preferredWidth: parent.width - Layout.fillHeight: true + Text { + id: searchStatusText - model: ConversationListModel - headerLabel: JamiStrings.conversations - headerVisible: searchResultsListView.visible - } - } + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + color: JamiTheme.textColor + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.filterItemFontSize + } + } - ColumnLayout { - id: swarmMemberSearchList + ColumnLayout { + id: smartListLayout + + width: parent.width + anchors.top: searchStatusRect.bottom + anchors.topMargin: (sidePanelTabBar.visible || + searchStatusRect.visible) ? 0 : 12 + anchors.bottom: parent.bottom + + spacing: 4 + + visible: !swarmMemberSearchList.visible + + ConversationListView { + id: searchResultsListView + + visible: count + opacity: visible ? 1 : 0 + + Layout.topMargin: 10 + Layout.alignment: Qt.AlignTop + Layout.fillWidth: true + Layout.preferredHeight: visible ? contentHeight : 0 + Layout.maximumHeight: { + var otherContentHeight = conversationListView.contentHeight + 16 + if (conversationListView.visible) + if (otherContentHeight < parent.height / 2) + return parent.height - otherContentHeight + else + return parent.height / 2 + else + return parent.height + } + + model: SearchResultsListModel + headerLabel: JamiStrings.searchResults + headerVisible: visible + } - visible: false + ConversationListView { + id: conversationListView - width: parent.width - anchors.top: searchStatusRect.bottom - anchors.topMargin: (sidePanelTabBar.visible || - searchStatusRect.visible) ? 0 : 12 - anchors.bottom: parent.bottom + visible: count - spacing: 4 + Layout.preferredWidth: parent.width + Layout.fillHeight: true - Text { - font.bold: true - font.pointSize: JamiTheme.contactEventPointSize + model: ConversationListModel + headerLabel: JamiStrings.conversations + headerVisible: searchResultsListView.visible + } + } - Layout.margins: 16 - Layout.maximumHeight: 24 - Layout.alignment: Qt.AlignTop - Layout.fillWidth: true + ColumnLayout { + id: swarmMemberSearchList - wrapMode: Text.Wrap + visible: viewCoordinator.inNewSwarm - text: { - if (highlightedMembers.length === 0) - return JamiStrings.youCanAdd7 - return JamiStrings.youCanAddMore.arg(7 - Math.min(highlightedMembers.length, 7)) - } - color: JamiTheme.textColor - } + width: parent.width + anchors.top: searchStatusRect.bottom + anchors.topMargin: (sidePanelTabBar.visible || + searchStatusRect.visible) ? 0 : 12 + anchors.bottom: parent.bottom - JamiListView { - id: swarmCurrentConversationList + spacing: 4 - Layout.preferredWidth: parent.width - Layout.fillHeight: true + Text { + font.bold: true + font.pointSize: JamiTheme.contactEventPointSize - model: ConversationListModel - delegate: SmartListItemDelegate { - interactive: false + Layout.margins: 16 + Layout.maximumHeight: 24 + Layout.alignment: Qt.AlignTop + Layout.fillWidth: true - onVisibleChanged: { - if (!swarmCurrentConversationList.visible) { - highlighted = false - root.clearHighlighted() - } - } + wrapMode: Text.Wrap - Component.onCompleted: { - // Note: when scrolled down, this delegate will be - // destroyed from the memory. So, re-add the highlighted - // status if necessary - if (Array.from(root.highlighted).find(r => r === UID)) { - highlighted = true + text: { + if (highlightedMembers.length === 0) + return JamiStrings.youCanAdd7 + return JamiStrings.youCanAddMore.arg(7 - Math.min(highlightedMembers.length, 7)) + } + color: JamiTheme.textColor } - } - onHighlightedChanged: function onHighlightedChanged() { - if (highlighted && Array.from(root.highlighted).find(r => r === UID)) { - // Due to scrolling destruction/reconstruction - return - } - var currentHighlighted = root.highlighted - if (!root.refreshHighlighted(UID, highlighted)) { - highlighted = false - return - } - if (highlighted) { - root.highlighted.push(UID) - } else { - root.highlighted = Array.from(root.highlighted).filter(r => r !== UID) + JamiListView { + id: swarmCurrentConversationList + + Layout.preferredWidth: parent.width + Layout.fillHeight: true + + model: ConversationListModel + delegate: SmartListItemDelegate { + interactive: false + + onVisibleChanged: { + if (!swarmCurrentConversationList.visible) { + highlighted = false + root.clearHighlighted() + } + } + + Component.onCompleted: { + // Note: when scrolled down, this delegate will be + // destroyed from the memory. So, re-add the highlighted + // status if necessary + if (Array.from(root.highlighted).find(r => r === UID)) { + highlighted = true + } + } + + onHighlightedChanged: function onHighlightedChanged() { + if (highlighted && Array.from(root.highlighted).find(r => r === UID)) { + // Due to scrolling destruction/reconstruction + return + } + var currentHighlighted = root.highlighted + if (!root.refreshHighlighted(UID, highlighted)) { + highlighted = false + return + } + if (highlighted) { + root.highlighted.push(UID) + } else { + root.highlighted = Array.from(root.highlighted).filter(r => r !== UID) + } + root.clearContactSearchBar() + } + } + currentIndex: model.currentFilteredRow } - root.clearContactSearchBar() } } - currentIndex: model.currentFilteredRow } } } diff --git a/src/app/mainview/components/SwarmDetailsPanel.qml b/src/app/mainview/components/SwarmDetailsPanel.qml index e6ad2053426a038aa3ff6c9befb9be720ecee17c..50db6f3156730ada657cabd59ecc32859f0760e8 100644 --- a/src/app/mainview/components/SwarmDetailsPanel.qml +++ b/src/app/mainview/components/SwarmDetailsPanel.qml @@ -34,12 +34,9 @@ Rectangle { color: CurrentConversation.color property var isAdmin: !CurrentConversation.isCoreDialog && - UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, CurrentAccount.uri) === Member.Role.ADMIN - - - DevicesListPopup { - id: devicesListPopup - } + UtilsAdapter.getParticipantRole(CurrentAccount.id, + CurrentConversation.id, + CurrentAccount.uri) === Member.Role.ADMIN ColumnLayout { id: swarmProfileDetails @@ -184,7 +181,8 @@ Rectangle { onEditingFinished: { if (text !== CurrentConversation.description) - ConversationsAdapter.updateConversationDescription(LRCInstance.selectedConvUid, text) + ConversationsAdapter.updateConversationDescription( + LRCInstance.selectedConvUid, text) } onSecondIcoClicked: {editable = !editable} @@ -268,22 +266,16 @@ Rectangle { } } - ColorDialog { - id: colorDialog - title: JamiStrings.chooseAColor - onAccepted: { - CurrentConversation.setPreference("color", colorDialog.color) - } - } - - ConfirmDialog { - id: rmDialog - - title: JamiStrings.confirmAction - textLabel: JamiStrings.confirmRmConversation - confirmLabel: JamiStrings.optionRemove - onAccepted: { - MessagesAdapter.removeConversation(LRCInstance.selectedConvUid) + Component { + id: colorDialogComp + ColorDialog { + id: colorDialog + title: JamiStrings.chooseAColor + onAccepted: { + CurrentConversation.setPreference("color", colorDialog.color) + this.destroy() + } + onRejected: this.destroy() } } @@ -354,7 +346,17 @@ Rectangle { target: parent enabled: parent.visible onTapped: function onTapped(eventPoint) { - rmDialog.open() + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/ConfirmDialog.qml", + { + title: JamiStrings.confirmAction, + textLabel: JamiStrings.confirmRmConversation, + confirmLabel: JamiStrings.optionRemove + }) + dlg.accepted.connect(function() { + MessagesAdapter.removeConversation(LRCInstance.selectedConvUid) + }) } } } @@ -399,7 +401,7 @@ Rectangle { target: parent enabled: parent.visible onTapped: function onTapped(eventPoint) { - colorDialog.open() + colorDialogComp.createObject(appWindow).open() } } } @@ -507,7 +509,9 @@ Rectangle { enabled: parent.visible && root.isAdmin onTapped: function onTapped(eventPoint) { - devicesListPopup.open() + viewCoordinator.presentDialog( + appWindow, + "mainview/components/DevicesListPopup.qml") } } } diff --git a/src/app/mainview/components/UserProfile.qml b/src/app/mainview/components/UserProfile.qml index 4b05cf335b9d6deb28b4de50d69fda9a57f0332b..7f4562d3415dd8a8128f99c97214d9760b34c2d5 100644 --- a/src/app/mainview/components/UserProfile.qml +++ b/src/app/mainview/components/UserProfile.qml @@ -27,6 +27,9 @@ import "../../commoncomponents" BaseModalDialog { id: root + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) + property string convId property string aliasText property string registeredNameText diff --git a/src/app/mainview/components/WelcomePage.qml b/src/app/mainview/components/WelcomePage.qml index b609be8b7419c331c778cf3a4836f7cdcb7330f1..5edf09544c6986e69c417ad4cb8cecd20fc4d898 100644 --- a/src/app/mainview/components/WelcomePage.qml +++ b/src/app/mainview/components/WelcomePage.qml @@ -27,11 +27,10 @@ import net.jami.Models 1.1 import "../../commoncomponents" import "../js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation -Rectangle { - +BaseView { id: root - color: JamiTheme.secondaryBackgroundColor + color: JamiTheme.secondaryBackgroundColor JamiFlickable { id: welcomeView @@ -136,8 +135,6 @@ Rectangle { anchors.margins: JamiTheme.preferredMarginSize } - - Image { id: welcomeLogo @@ -203,7 +200,6 @@ Rectangle { } } - Item { id: bottomRow width: Math.max(300, root.width) @@ -218,7 +214,9 @@ Rectangle { preferredWidth: JamiTheme.aboutButtonPreferredWidthth text: JamiStrings.aboutJami - onClicked: aboutPopUpDialog.open() + onClicked: viewCoordinator.presentDialog( + appWindow, + "mainview/components/AboutPopUp.qml") } PushButton { diff --git a/src/app/mainview/js/contactpickercreation.js b/src/app/mainview/js/contactpickercreation.js index 2de3ddf3c83e11f12adbaff69ba37a22dd76992f..a87cf687721a264c8d1a9a52e9557928589d380c 100644 --- a/src/app/mainview/js/contactpickercreation.js +++ b/src/app/mainview/js/contactpickercreation.js @@ -16,55 +16,20 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ - -/* - * Global contact picker component, object variable for creation. - */ -var contactPickerComponent -var contactPickerObject - -function createContactPickerObjects(type, parent) { - if (contactPickerObject) { - - /* - * If already created, reset parameters, since object cannot be destroyed. - */ - contactPickerObject.parent = parent - contactPickerObject.type = type - return - } - contactPickerComponent = Qt.createComponent( +function presentContactPickerPopup(type, parent) { + var comp = Qt.createComponent( "../components/ContactPicker.qml") - if (contactPickerComponent.status === Component.Ready) - finishCreation(type, parent) - else if (contactPickerComponent.status === Component.Error) - console.log("Error loading component:", - contactPickerComponent.errorString()) -} - -function finishCreation(type, parent) { - contactPickerObject = contactPickerComponent.createObject(parent, { - "type": type - }) - if (contactPickerObject === null) { - /* - * Error Handling. - */ - console.log("Error creating object for contact picker") - } else { - contactPickerObject.x = Qt.binding(function(){ - return parent.width/2 - contactPickerObject.width / 2}) - contactPickerObject.y = Qt.binding(function(){ - return parent.height/2 - contactPickerObject.height / 2}) + if (comp.status === Component.Ready) { + var obj = comp.createObject(parent, { type: type }) + if (obj === null) { + console.log("Error creating object for contact picker") + } else { + obj.x = Qt.binding(() => parent.width / 2 - obj.width / 2) + obj.y = Qt.binding(() => parent.height / 2 - obj.height / 2) + obj.closed.connect(() => obj.destroy()) + obj.open() + } + } else if (comp.status === Component.Error) { + console.log("Error loading component:", comp.errorString()) } } - -function openContactPicker() { - if (contactPickerObject) - contactPickerObject.open() -} - -function closeContactPicker() { - if (contactPickerObject) - contactPickerObject.close() -} diff --git a/src/app/messagesadapter.cpp b/src/app/messagesadapter.cpp index 053f73f99cf3ff408f4b54fbbab8181aa5fb7810..e10bf358368b5b54b1962a3641be1b1cecc788e4 100644 --- a/src/app/messagesadapter.cpp +++ b/src/app/messagesadapter.cpp @@ -53,7 +53,11 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager, , mediaInteractions_(std::make_unique<MessageListModel>()) , timestampTimer_(new QTimer(this)) { - connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() { + setObjectName(typeid(*this).name()); + + set_messageListModel(QVariant::fromValue(filteredMsgListModel_)); + + connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, this, [this]() { set_replyToId(""); set_editId(""); const QString& convId = lrcInstance_->get_selectedConvUid(); @@ -61,7 +65,6 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager, // Reset the source model for the proxy model. filteredMsgListModel_->setSourceModel(conversation.interactions.get()); - set_messageListModel(QVariant::fromValue(filteredMsgListModel_)); set_currentConvComposingList(conversationTypersUrlToName(conversation.typers)); }); @@ -71,28 +74,12 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager, connect(timestampTimer_, &QTimer::timeout, this, &MessagesAdapter::timestampUpdated); timestampTimer_->start(timestampUpdateIntervalMs_); -} -void -MessagesAdapter::safeInit() -{ - connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, [this]() { + connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, this, [this]() { connectConversationModel(); }); - connectConversationModel(); -} -void -MessagesAdapter::setupChatView(const QVariantMap& convInfo) -{ - auto* convModel = lrcInstance_->getCurrentConversationModel(); - auto convId = convInfo["convId"].toString(); - if (convInfo["isSwarm"].toBool()) { - convModel->loadConversationMessages(convId, loadChunkSize_); - } - - // TODO: current conv observe - Q_EMIT newMessageBarPlaceholderText(convInfo["title"].toString()); + connectConversationModel(); } void @@ -136,6 +123,9 @@ void MessagesAdapter::connectConversationModel() { auto currentConversationModel = lrcInstance_->getCurrentConversationModel(); + if (currentConversationModel == nullptr) { + return; + } QObject::connect(currentConversationModel, &ConversationModel::newInteraction, @@ -184,7 +174,6 @@ void MessagesAdapter::editMessage(const QString& convId, const QString& newBody, const QString& messageId) { try { - const auto convUid = lrcInstance_->get_selectedConvUid(); auto editId = !messageId.isEmpty() ? messageId : editId_; if (editId.isEmpty()) { return; @@ -202,7 +191,6 @@ MessagesAdapter::removeEmojiReaction(const QString& convId, const QString& messageId) { try { - const auto convUid = lrcInstance_->get_selectedConvUid(); const auto authorUri = lrcInstance_->getCurrentAccountInfo().profileInfo.uri; // check if this emoji has already been added by this author auto emojiId = lrcInstance_->getConversationFromConvUid(convId) diff --git a/src/app/messagesadapter.h b/src/app/messagesadapter.h index afb299820cf889d36f97be5bb6da8fcad9e12fad..8356b4a51839fcb7ce0849a010bb5d43bb101707 100644 --- a/src/app/messagesadapter.h +++ b/src/app/messagesadapter.h @@ -70,7 +70,6 @@ public: Q_SIGNALS: void newInteraction(const QString& id, int type); - void newMessageBarPlaceholderText(QString placeholderText); void newFilePasted(QString filePath); void newTextPasted(); void previewInformationToQML(QString messageId, QStringList previewInformation); @@ -78,9 +77,6 @@ Q_SIGNALS: void timestampUpdated(); protected: - void safeInit() override; - - Q_INVOKABLE void setupChatView(const QVariantMap& convInfo); Q_INVOKABLE void loadMoreMessages(); Q_INVOKABLE void loadConversationUntil(const QString& to); Q_INVOKABLE void connectConversationModel(); diff --git a/src/app/pluginadapter.h b/src/app/pluginadapter.h index b60761fa5f37b97b8ad2b5aae3584f4c5e3249c2..cb640100cf78607fb7d700295d117922d55616e8 100644 --- a/src/app/pluginadapter.h +++ b/src/app/pluginadapter.h @@ -40,8 +40,6 @@ public: ~PluginAdapter() = default; protected: - void safeInit() override {}; - Q_INVOKABLE QVariant getMediaHandlerSelectableModel(const QString& callId); Q_INVOKABLE QVariant getChatHandlerSelectableModel(const QString& accountId, const QString& peerId); diff --git a/src/app/positionmanager.cpp b/src/app/positionmanager.cpp index 3ba52cf92d075fc01ad1cd3365e916b1ff38b52b..7c1a204a3c1ef112a38f7fe76149275986ade06a 100644 --- a/src/app/positionmanager.cpp +++ b/src/app/positionmanager.cpp @@ -38,11 +38,7 @@ PositionManager::PositionManager(AppSettingsManager* settingsManager, onPositionReceived(accountId, peerId, body, -1, ""); }, Qt::QueuedConnection); -} -void -PositionManager::safeInit() -{ localPositioning_.reset(new Positioning()); connectAccountModel(); } diff --git a/src/app/positionmanager.h b/src/app/positionmanager.h index 3c2e5cef48664ad1e5e017bf6c3e66e0355747e1..e54d0dac9bb9f39d5fa3588bb4aef99dfbfcf158 100644 --- a/src/app/positionmanager.h +++ b/src/app/positionmanager.h @@ -57,8 +57,6 @@ Q_SIGNALS: void sendCountdownUpdate(const QString& accountId, const int remainingTime); protected: - void safeInit() override; - QString getAvatar(const QString& accountId, const QString& peerId); QVariantMap parseJsonPosition(const QString& accountId, const QString& peerId, diff --git a/src/app/previewengine.cpp b/src/app/previewengine.cpp index 3f3540b2ca08312374d49143af76707792b4b8cc..ab91116d9e6bb7210e8496f15024798b25e57a98 100644 --- a/src/app/previewengine.cpp +++ b/src/app/previewengine.cpp @@ -19,6 +19,8 @@ #include "previewengine.h" +#include "utils.h" + #include <QWebEngineScript> #include <QWebEngineProfile> #include <QWebEngineSettings> @@ -111,6 +113,3 @@ PreviewEngine::emitLinkified(const QString& messageId, const QString& linkifiedS { Q_EMIT linkified(messageId, linkifiedStr); } - -#include "moc_previewengine.cpp" -#include "previewengine.moc" diff --git a/src/app/previewengine.h b/src/app/previewengine.h index c95d42ed8d795a49aaa3b2bba62bd2351f69dc27..be9e3f6b5c44ac6e239362ea66a40fdb4bf6d611 100644 --- a/src/app/previewengine.h +++ b/src/app/previewengine.h @@ -19,7 +19,7 @@ #pragma once -#include "utils.h" +#include <QColor> #include <QObject> class PreviewEngine : public QObject diff --git a/src/app/qmladapterbase.h b/src/app/qmladapterbase.h index c0c77dc72e82bb8f087640b9ad8a35d9bfce9080..0ffc3cd59a35e19a2815d0e0e038eafcad3656a9 100644 --- a/src/app/qmladapterbase.h +++ b/src/app/qmladapterbase.h @@ -22,34 +22,18 @@ class LRCInstance; -// The main purpose of this class is to operate on qml objects, -// or provide api calls to qml objects that cannot be done directly in qml. +// Base class for adapters that need an LRC instance. class QmlAdapterBase : public QObject { Q_OBJECT public: explicit QmlAdapterBase(LRCInstance* instance, QObject* parent = nullptr) : QObject(parent) - , qmlObj_(nullptr) , lrcInstance_(instance) {}; virtual ~QmlAdapterBase() = default; - // This function should be called in the Component.onCompleted slot - // in the qml component that this adapter should attach to. - Q_INVOKABLE void setQmlObject(QObject* obj) - { - qmlObj_ = obj; - safeInit(); - }; - protected: - // Once the qml object is set, Qml method invokation can be done - virtual void safeInit() = 0; - - // Object pointer. - QObject* qmlObj_; - // LRCInstance pointer LRCInstance* lrcInstance_ {nullptr}; }; diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp index f6937331784c5b90432319ccf3d4a1ca87810703..80faf8f8f257db7d7e7534c479c2accc6d351e0b 100644 --- a/src/app/qmlregister.cpp +++ b/src/app/qmlregister.cpp @@ -57,7 +57,6 @@ #include "pluginlistmodel.h" #include "pluginlistpreferencemodel.h" #include "preferenceitemlistmodel.h" -#include "version.h" #include "wizardviewstepmodel.h" #include "api/peerdiscoverymodel.h" @@ -126,6 +125,8 @@ registerTypes(QQmlEngine* engine, auto tipsModel = new TipsModel(settingsManager, parent); auto videoDevices = new VideoDevices(lrcInstance, parent); auto currentAccountToMigrate = new CurrentAccountToMigrate(lrcInstance, parent); + auto avatarRegistry = new AvatarRegistry(lrcInstance, parent); + auto wizardViewStepModel = new WizardViewStepModel(lrcInstance, accountAdapter, settingsManager, parent); // qml adapter registration QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, callAdapter, "CallAdapter"); @@ -143,6 +144,8 @@ registerTypes(QQmlEngine* engine, QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccountToMigrate, "CurrentAccountToMigrate") + QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, avatarRegistry, "AvatarRegistry"); + QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, wizardViewStepModel, "WizardViewStepModel") // TODO: remove these QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel()) @@ -174,7 +177,6 @@ registerTypes(QQmlEngine* engine, QML_REGISTERTYPE(NS_MODELS, CallInformationListModel); QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel); - // Roles & type enums for models QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList"); QML_REGISTERNAMESPACE(NS_MODELS, ConversationList::staticMetaObject, "ConversationList"); @@ -194,11 +196,6 @@ registerTypes(QQmlEngine* engine, QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance") QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager") - auto avatarRegistry = new AvatarRegistry(lrcInstance, parent); - auto wizardViewStepModel = new WizardViewStepModel(lrcInstance, accountAdapter, settingsManager, parent); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, avatarRegistry, "AvatarRegistry"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, wizardViewStepModel, "WizardViewStepModel") - // C++ singletons // TODO: remove this QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory); diff --git a/src/app/selectablelistproxymodel.cpp b/src/app/selectablelistproxymodel.cpp index 7b6678355e44b514b814ac1b7d00bc3f44108f2e..51410fdb048f28ef4dae2a943f4f1ca936f6fce1 100644 --- a/src/app/selectablelistproxymodel.cpp +++ b/src/app/selectablelistproxymodel.cpp @@ -30,6 +30,9 @@ void SelectableListProxyModel::bindSourceModel(QAbstractListModel* model) { setSourceModel(model); + if (!model) { + return; + } connect(sourceModel(), &QAbstractListModel::dataChanged, this, @@ -159,7 +162,7 @@ SelectableListProxyGroupModel::SelectableListProxyGroupModel(QList<SelectableLis , models_(models) { Q_FOREACH (auto* m, models_) { - connect(m, &SelectableListProxyModel::validSelectionChanged, [this, m] { + connect(m, &SelectableListProxyModel::validSelectionChanged, this, [this, m] { // deselct all other lists in the group Q_FOREACH (auto* otherM, models_) { if (m != otherM) { diff --git a/src/app/settingsview/SettingsView.qml b/src/app/settingsview/SettingsView.qml index 4ecb7e5dddcf15f3a99bfbca236ec0d123b8e56f..c60b49a330604a863471e8da107681c6b61163da 100644 --- a/src/app/settingsview/SettingsView.qml +++ b/src/app/settingsview/SettingsView.qml @@ -29,8 +29,19 @@ import "components" import "../commoncomponents" import "../mainview/js/contactpickercreation.js" as ContactPickerCreation -Rectangle { +BaseView { id: root + objectName: "SettingsView" + requiresIndex: true + + onDismissed: { + settingsViewRect.stopBooth() + if (UtilsAdapter.getAccountListSize() === 0) { + viewCoordinator.requestAppWindowWizardView() + } else { + AccountAdapter.changeAccount(0) + } + } enum SettingsMenu { Account, @@ -39,50 +50,22 @@ Rectangle { Plugin } - onVisibleChanged: { - if(visible){ - setSelected(selectedMenu,true) - } - } + onVisibleChanged: if(visible) setSelected(selectedMenu, true) - function setSelected(sel, recovery = false) { - if(selectedMenu === sel && (!recovery)) { return } - switch(sel) { - case SettingsView.Account: - selectedMenu = sel - pageIdCurrentAccountSettings.updateAccountInfoDisplayed() - break - case SettingsView.General: - selectedMenu = sel - break - case SettingsView.Media: - selectedMenu = sel - avSettings.populateAVSettings() - break - case SettingsView.Plugin: - selectedMenu = sel - break + property int selectedMenu: SettingsView.Account + onSelectedMenuChanged: { + if (selectedMenu === SettingsView.Account) { + pageIdCurrentAccountSettings.updateAccountInfoDisplayed() + } else if (selectedMenu === SettingsView.Media) { + avSettings.populateAVSettings() } } - // slots - function leaveSettingsSlot(showMainView) { - settingsViewRect.stopBooth() - if (showMainView) - settingsViewNeedToShowMainView() - else - settingsViewNeedToShowNewWizardWindow() + function setSelected(idx, recovery = false) { + if (selectedMenu === idx && !recovery) return + selectedMenu = idx } - property int selectedMenu: SettingsView.Account - // signal to redirect the page to main view - signal settingsViewNeedToShowMainView() - signal settingsViewNeedToShowNewWizardWindow - - signal settingsBackArrowClicked - - visible: true - Rectangle { id: settingsViewRect @@ -126,7 +109,7 @@ Rectangle { } } - onBackArrowClicked: root.settingsBackArrowClicked() + onBackArrowClicked: viewCoordinator.hideCurrentView() } JamiFlickable { @@ -182,13 +165,8 @@ Rectangle { isSIP: settingsViewRect.isSIP - onNavigateToMainView: { - leaveSettingsSlot(true) - } - - onNavigateToNewWizardView: { - leaveSettingsSlot(false) - } + onNavigateToMainView: dismiss() + onNavigateToNewWizardView: dismiss() onAdvancedSettingsToggled: function (settingsVisible) { if (settingsVisible) diff --git a/src/app/settingsview/components/AdvancedCallSettings.qml b/src/app/settingsview/components/AdvancedCallSettings.qml index aa5d4c0dd7b1c8f1e6bed674ca56f44f1130373b..324f270fd7408b13b10f4a5f8e34e9db5a121623 100644 --- a/src/app/settingsview/components/AdvancedCallSettings.qml +++ b/src/app/settingsview/components/AdvancedCallSettings.qml @@ -47,25 +47,6 @@ ColumnLayout { } } - JamiFileDialog { - id: ringtonePath_Dialog - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectNewRingtone - folder: JamiQmlUtils.qmlFilePrefix + UtilsAdapter.toFileAbsolutepath( - CurrentAccount.ringtonePath_Ringtone) - - nameFilters: [JamiStrings.audioFile, - JamiStrings.allFiles] - - onAccepted: { - var url = UtilsAdapter.getAbsPath(file.toString()) - - if(url.length !== 0) - CurrentAccount.ringtonePath_Ringtone = url - } - } - ElidedTextLabel { Layout.fillWidth: true @@ -125,7 +106,26 @@ ColumnLayout { titleField: JamiStrings.selectCustomRingtone source: JamiResources.round_folder_24dp_svg itemWidth: root.itemWidth - onClick: ringtonePath_Dialog.open() + + onClick: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.selectNewRingtone, + fileMode: JamiFileDialog.OpenFile, + folder: JamiQmlUtils.qmlFilePrefix + + UtilsAdapter.toFileAbsolutepath( + CurrentAccount.ringtonePath_Ringtone), + nameFilters: [JamiStrings.audioFile, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function (file) { + var url = UtilsAdapter.getAbsPath(file.toString()) + if(url.length !== 0) { + CurrentAccount.ringtonePath_Ringtone = url + } + }) + } } ToggleSwitch { @@ -213,10 +213,9 @@ ColumnLayout { text: JamiStrings.addDefaultModerator onClicked: { - ContactPickerCreation.createContactPickerObjects( + ContactPickerCreation.presentContactPickerPopup( ContactList.CONVERSATION, - mainView) - ContactPickerCreation.openContactPicker() + appWindow) } } diff --git a/src/app/settingsview/components/AdvancedJamiSecuritySettings.qml b/src/app/settingsview/components/AdvancedJamiSecuritySettings.qml index 1c2af23c6e3bf43c4734709b48ebd396daa7014e..e3046345ac6bf626dae8a8d179bff697c5d00c7c 100644 --- a/src/app/settingsview/components/AdvancedJamiSecuritySettings.qml +++ b/src/app/settingsview/components/AdvancedJamiSecuritySettings.qml @@ -30,58 +30,20 @@ ColumnLayout { property int itemWidth - JamiFileDialog { - id: caCert_Dialog - - property string oldPath: CurrentAccount.certificateListFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectCACert - folder: openPath - nameFilters: [JamiStrings.certificateFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.certificateListFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) - } - - JamiFileDialog { - id: userCert_Dialog - - property string oldPath: CurrentAccount.certificateFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectUserCert - folder: openPath - nameFilters: [JamiStrings.certificateFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.certificateFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) - } - - JamiFileDialog { - id: privateKey_Dialog - - property string oldPath: CurrentAccount.privateKeyFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectPrivateKey - folder: openPath - nameFilters: [JamiStrings.keyFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.privateKeyFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) + function openFileDialog(title, oldPath, fileType, onAcceptedCb) { + var openPath = oldPath === "" ? + (UtilsAdapter.getCurrentPath() + "/ringtones/") : + (UtilsAdapter.toFileAbsolutepath(oldPath)) + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: title, + fileMode: JamiFileDialog.OpenFile, + folder: openPath, + nameFilters: [fileType, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(onAcceptedCb) } ElidedTextLabel { @@ -109,7 +71,13 @@ ColumnLayout { source: JamiResources.round_folder_24dp_svg itemWidth: root.itemWidth - onClick: caCert_Dialog.open() + onClick: openFileDialog(JamiStrings.selectCACert, + CurrentAccount.certificateListFile_TLS, + JamiStrings.certificateFile, + function (file) { + CurrentAccount.certificateListFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } SettingMaterialButton { @@ -124,7 +92,13 @@ ColumnLayout { source: JamiResources.round_folder_24dp_svg itemWidth: root.itemWidth - onClick: userCert_Dialog.open() + onClick: openFileDialog(JamiStrings.selectUserCert, + CurrentAccount.certificateFile_TLS, + JamiStrings.certificateFile, + function (file) { + CurrentAccount.certificateFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } SettingMaterialButton { @@ -139,7 +113,13 @@ ColumnLayout { source: JamiResources.round_folder_24dp_svg itemWidth: root.itemWidth - onClick: privateKey_Dialog.open() + onClick: openFileDialog(JamiStrings.selectPrivateKey, + CurrentAccount.privateKeyFile_TLS, + JamiStrings.keyFile, + function (file) { + CurrentAccount.privateKeyFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } SettingsMaterialLineEdit { diff --git a/src/app/settingsview/components/AdvancedSIPSecuritySettings.qml b/src/app/settingsview/components/AdvancedSIPSecuritySettings.qml index 685361bdcbfdb4e120c1543703c85d08b3f92f42..906324753ca8f9547179bd6f438e6707cedc7a3a 100644 --- a/src/app/settingsview/components/AdvancedSIPSecuritySettings.qml +++ b/src/app/settingsview/components/AdvancedSIPSecuritySettings.qml @@ -31,58 +31,20 @@ ColumnLayout { property int itemWidth - JamiFileDialog { - id: caCert_Dialog_SIP - - property string oldPath: CurrentAccount.certificateListFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectCACert - folder: openPath - nameFilters: [JamiStrings.certificateFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.certificateListFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) - } - - JamiFileDialog { - id: userCert_Dialog_SIP - - property string oldPath: CurrentAccount.certificateFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectUserCert - folder: openPath - nameFilters: [JamiStrings.certificateFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.certificateFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) - } - - JamiFileDialog { - id: privateKey_Dialog_SIP - - property string oldPath: CurrentAccount.privateKeyFile_TLS - property string openPath: oldPath === "" ? - (UtilsAdapter.getCurrentPath() + "/ringtones/") : - (UtilsAdapter.toFileAbsolutepath(oldPath)) - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectPrivateKey - folder: openPath - nameFilters: [JamiStrings.keyFile, - JamiStrings.allFiles] - - onAccepted: CurrentAccount.privateKeyFile_TLS = - UtilsAdapter.getAbsPath(file.toString()) + function openFileDialog(title, oldPath, fileType, onAcceptedCb) { + var openPath = oldPath === "" ? + (UtilsAdapter.getCurrentPath() + "/ringtones/") : + (UtilsAdapter.toFileAbsolutepath(oldPath)) + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: title, + fileMode: JamiFileDialog.OpenFile, + folder: openPath, + nameFilters: [fileType, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(onAcceptedCb) } ElidedTextLabel { @@ -160,7 +122,13 @@ ColumnLayout { textField: UtilsAdapter.toFileInfoName(CurrentAccount.certificateListFile_TLS) - onClick: caCert_Dialog_SIP.open() + onClick: openFileDialog(JamiStrings.selectCACert, + CurrentAccount.certificateListFile_TLS, + JamiStrings.certificateFile, + function (file) { + CurrentAccount.certificateListFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } SettingMaterialButton { @@ -177,7 +145,13 @@ ColumnLayout { textField: UtilsAdapter.toFileInfoName(CurrentAccount.certificateFile_TLS) - onClick: userCert_Dialog_SIP.open() + onClick: openFileDialog(JamiStrings.selectUserCert, + CurrentAccount.certificateFile_TLS, + JamiStrings.certificateFile, + function (file) { + CurrentAccount.certificateFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } SettingMaterialButton { @@ -194,7 +168,13 @@ ColumnLayout { textField: UtilsAdapter.toFileInfoName(CurrentAccount.privateKeyFile_TLS) - onClick: privateKey_Dialog_SIP.open() + onClick: openFileDialog(JamiStrings.selectPrivateKey, + CurrentAccount.privateKeyFile_TLS, + JamiStrings.keyFile, + function (file) { + CurrentAccount.privateKeyFile_TLS = + UtilsAdapter.getAbsPath(file.toString()) + }) } // Private key password diff --git a/src/app/settingsview/components/CurrentAccountSettings.qml b/src/app/settingsview/components/CurrentAccountSettings.qml index d1483e8d4d3b0429cd23155eaaa7e2c9f30f721e..219daace01b32c8dfcc90cd3f306989cb6af9ea6 100644 --- a/src/app/settingsview/components/CurrentAccountSettings.qml +++ b/src/app/settingsview/components/CurrentAccountSettings.qml @@ -42,118 +42,14 @@ Rectangle { function updateAccountInfoDisplayed() { bannedContacts.updateAndShowBannedContactsSlot() - setPasswordButtonText() - } - - function passwordClicked() { - if (AccountAdapter.hasPassword()) { - passwordDialog.openDialog(PasswordDialog.ChangePassword) - } else { - passwordDialog.openDialog(PasswordDialog.SetPassword) - } - } - - function exportAccountSlot() { - exportBtn_Dialog.open() - } - - function delAccountSlot() { - deleteAccountDialog.isSIP = CurrentAccount.type === Profile.Type.SIP - deleteAccountDialog.bestName = CurrentAccount.bestName - deleteAccountDialog.accountId = CurrentAccount.uri - deleteAccountDialog.open() } function getAdvancedSettingsScrollPosition() { return advancedSettings.y } - function setPasswordButtonText() { - var hasPassword = AccountAdapter.hasPassword() - passwdPushButton.toolTipText = hasPassword ? - JamiStrings.changeCurrentPassword : - JamiStrings.setAPassword - - passwdPushButton.text = hasPassword ? - JamiStrings.changePassword : - JamiStrings.setPassword - } - color: JamiTheme.secondaryBackgroundColor - SimpleMessageDialog { - id: msgDialog - - buttonTitles: [JamiStrings.optionOk] - buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] - buttonCallBacks: [setPasswordButtonText] - } - - DeleteAccountDialog { - id: deleteAccountDialog - - onAccepted: { - if(UtilsAdapter.getAccountListSize() > 0) { - navigateToMainView() - } else { - navigateToNewWizardView() - } - } - } - - PasswordDialog { - id: passwordDialog - - onDoneSignal: function (success, currentPurpose) { - var title = success ? JamiStrings.success : JamiStrings.error - - var info - switch(currentPurpose) { - case PasswordDialog.ExportAccount: - info = success ? JamiStrings.backupSuccessful : JamiStrings.backupFailed - break - case PasswordDialog.ChangePassword: - info = success ? JamiStrings.changePasswordSuccess : JamiStrings.changePasswordFailed - break - case PasswordDialog.SetPassword: - info = success ? JamiStrings.setPasswordSuccess : JamiStrings.setPasswordFailed - passwdPushButton.text = success ? JamiStrings.changePassword : JamiStrings.setPassword - break - } - - msgDialog.openWithParameters(title, info) - } - } - - JamiFileDialog { - id: exportBtn_Dialog - - mode: JamiFileDialog.SaveFile - - title: JamiStrings.backupAccountHere - folder: StandardPaths.writableLocation(StandardPaths.DesktopLocation) - - nameFilters: [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles] - - onAccepted: { - // is there password? If so, go to password dialog, else, go to following directly - var exportPath = UtilsAdapter.getAbsPath(file.toString()) - if (AccountAdapter.hasPassword()) { - passwordDialog.openDialog(PasswordDialog.ExportAccount,exportPath) - return - } else { - if (exportPath.length > 0) { - var isSuccessful = AccountAdapter.model.exportToFile(LRCInstance.currentAccountId, - exportPath, "") - var title = isSuccessful ? JamiStrings.success : JamiStrings.error - var info = isSuccessful ? JamiStrings.backupSuccessful : JamiStrings.backupFailed - - msgDialog.openWithParameters(title,info) - } - } - } - } - ColumnLayout { id: currentAccountSettingsColumnLayout @@ -212,16 +108,21 @@ Rectangle { pressedColor: JamiTheme.buttonTintedBlackPressed secondary: true - toolTipText: AccountAdapter.hasPassword() ? + toolTipText: CurrentAccount.hasArchivePassword ? JamiStrings.changeCurrentPassword : JamiStrings.setAPassword - text: AccountAdapter.hasPassword() ? + text: CurrentAccount.hasArchivePassword ? JamiStrings.changePassword : JamiStrings.setPassword iconSource: JamiResources.round_edit_24dp_svg - onClicked: passwordClicked() + onClicked: viewCoordinator.presentDialog( + appWindow, + "commoncomponents/PasswordDialog.qml", + { purpose: CurrentAccount.hasArchivePassword ? + PasswordDialog.ChangePassword : + PasswordDialog.SetPassword }) } MaterialButton { @@ -243,7 +144,42 @@ Rectangle { iconSource: JamiResources.round_save_alt_24dp_svg - onClicked: exportAccountSlot() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.backupAccountHere, + fileMode: FileDialog.SaveFile, + folder: StandardPaths.writableLocation(StandardPaths.DesktopLocation), + nameFilters: [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function (file) { + // is there password? If so, go to password dialog, else, go to following directly + var exportPath = UtilsAdapter.getAbsPath(file.toString()) + if (CurrentAccount.hasArchivePassword) { + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/PasswordDialog.qml", + { + purpose: PasswordDialog.ExportAccount, + path: exportPath + }) + return + } else if (exportPath.length > 0) { + var success = AccountAdapter.model.exportToFile(LRCInstance.currentAccountId, exportPath) + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: success ? JamiStrings.success : JamiStrings.error, + infoText: success ? JamiStrings.backupSuccessful : JamiStrings.backupFailed, + buttonTitles: [JamiStrings.optionOk], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] + }) + } + }) + } } MaterialButton { @@ -262,7 +198,17 @@ Rectangle { iconSource: JamiResources.delete_forever_24dp_svg - onClicked: delAccountSlot() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/DeleteAccountDialog.qml", + { + isSIP: CurrentAccount.type === Profile.Type.SIP, + bestName: CurrentAccount.bestName, + accountId: CurrentAccount.uri + }) + dlg.accepted.connect(navigateToMainView) + } } LinkedDevices { diff --git a/src/app/settingsview/components/JamiUserIdentity.qml b/src/app/settingsview/components/JamiUserIdentity.qml index 5032375d74b992253c3f497acf2eb59ffdd64c27..a3acde9503235bce31ebc0600496247a131b50af 100644 --- a/src/app/settingsview/components/JamiUserIdentity.qml +++ b/src/app/settingsview/components/JamiUserIdentity.qml @@ -31,13 +31,6 @@ ColumnLayout { property int itemWidth - NameRegistrationDialog { - id : nameRegistrationDialog - - onAccepted: currentRegisteredID.nameRegistrationState = - UsernameLineEdit.NameRegistrationState.BLANK - } - // Identity Row { Layout.fillWidth: true @@ -162,6 +155,18 @@ ColumnLayout { hoveredColor: JamiTheme.buttonTintedGreyHovered pressedColor: JamiTheme.buttonTintedGreyPressed - onClicked: nameRegistrationDialog.openNameRegistrationDialog(currentRegisteredID.text) + onClicked: { + if (currentRegisteredID.text === '') { + return + } + var dlg = viewCoordinator.presentDialog( + appWindow, + "settingsview/components/NameRegistrationDialog.qml", + { registeredName: currentRegisteredID.text }) + dlg.accepted.connect(function() { + currentRegisteredID.nameRegistrationState = + UsernameLineEdit.NameRegistrationState.BLANK + }) + } } } diff --git a/src/app/settingsview/components/LinkDeviceDialog.qml b/src/app/settingsview/components/LinkDeviceDialog.qml index 2211f155c740665370071d26a7cbde27aa4b30b5..cae0ded44b2ece38c6ce5644b71472347ed0c9ed 100644 --- a/src/app/settingsview/components/LinkDeviceDialog.qml +++ b/src/app/settingsview/components/LinkDeviceDialog.qml @@ -34,14 +34,14 @@ BaseModalDialog { title: JamiStrings.addDevice - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) popupContent: StackLayout { id: stackedWidget function setGeneratingPage() { - if(passwordEdit.length === 0 && AccountAdapter.hasPassword()){ + if(passwordEdit.length === 0 && CurrentAccount.hasArchivePassword){ setExportPage(NameDirectory.ExportOnRingStatus.WRONG_PASSWORD, "") return } @@ -102,7 +102,7 @@ BaseModalDialog { infoLabel.text = JamiStrings.pinTimerInfos passwordEdit.clear() - if(AccountAdapter.hasPassword()) { + if(CurrentAccount.hasArchivePassword) { stackedWidget.currentIndex = enterPasswordPage.pageIndex passwordEdit.forceActiveFocus() } else { diff --git a/src/app/settingsview/components/LinkedDevices.qml b/src/app/settingsview/components/LinkedDevices.qml index 109b1945b91ee1dbd306d7b6db4b5a4b1f0afd3e..f59d1b84e32ff5925a36e1d3bf0993d311ba6885 100644 --- a/src/app/settingsview/components/LinkedDevices.qml +++ b/src/app/settingsview/components/LinkedDevices.qml @@ -33,44 +33,30 @@ ColumnLayout { id:root function removeDeviceSlot(index){ - var idOfDevice = settingsListView.model.data(settingsListView.model.index(index,0), + var deviceId = settingsListView.model.data(settingsListView.model.index(index,0), DeviceItemListModel.DeviceID) - if(AccountAdapter.hasPassword()){ - revokeDevicePasswordDialog.openRevokeDeviceDialog(idOfDevice) + if(CurrentAccount.hasArchivePassword){ + viewCoordinator.presentDialog( + appWindow, + "settingsview/components/RevokeDevicePasswordDialog.qml", + { deviceId: deviceId }) } else { - revokeDeviceMessageBox.idOfDev = idOfDevice - revokeDeviceMessageBox.open() + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: JamiStrings.removeDevice, + infoText: JamiStrings.sureToRemoveDevice, + buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue, + SimpleMessageDialog.ButtonStyle.TintedBlack], + buttonCallBacks: [ + function() { DeviceItemListModel.revokeDevice(deviceId, "") } + ] + }) } } - LinkDeviceDialog { - id: linkDeviceDialog - } - - RevokeDevicePasswordDialog{ - id: revokeDevicePasswordDialog - - onRevokeDeviceWithPassword: function(idOfDevice, password) { - DeviceItemListModel.revokeDevice(idOfDevice, password) - } - } - - SimpleMessageDialog { - id: revokeDeviceMessageBox - - property string idOfDev: "" - - title: JamiStrings.removeDevice - infoText: JamiStrings.sureToRemoveDevice - - buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel] - buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue, - SimpleMessageDialog.ButtonStyle.TintedBlack] - buttonCallBacks: [ - function() { DeviceItemListModel.revokeDevice(idOfDev, "") } - ] - } - Label { Layout.preferredHeight: JamiTheme.preferredFieldHeight @@ -132,6 +118,8 @@ ColumnLayout { text: JamiStrings.linkAnotherDevice - onClicked: linkDeviceDialog.open() + onClicked: viewCoordinator.presentDialog( + appWindow, + "settingsview/components/LinkDeviceDialog.qml") } } diff --git a/src/app/settingsview/components/NameRegistrationDialog.qml b/src/app/settingsview/components/NameRegistrationDialog.qml index fc65042d26918271a1dec379ed04f89d0e238b41..d61125fb45bc5b0582182eb9bb6ac41e21a0bf00 100644 --- a/src/app/settingsview/components/NameRegistrationDialog.qml +++ b/src/app/settingsview/components/NameRegistrationDialog.qml @@ -30,19 +30,12 @@ import "../../commoncomponents" BaseModalDialog { id: root - property string registerdName : "" + property string registeredName : "" signal accepted - function openNameRegistrationDialog(registerNameIn) { - if (registerNameIn === '') - return - registerdName = registerNameIn - open() - } - - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) title: JamiStrings.setUsername @@ -64,7 +57,7 @@ BaseModalDialog { onTriggered: { AccountAdapter.model.registerName(LRCInstance.currentAccountId, - passwordEdit.text, registerdName) + passwordEdit.text, registeredName) } } @@ -96,7 +89,7 @@ BaseModalDialog { lblRegistrationError.text = JamiStrings.somethingWentWrong passwordEdit.clear() - if (AccountAdapter.hasPassword()){ + if (CurrentAccount.hasArchivePassword){ stackedWidget.currentIndex = nameRegisterEnterPasswordPage.pageIndex passwordEdit.forceActiveFocus() } else { diff --git a/src/app/settingsview/components/PluginItemDelegate.qml b/src/app/settingsview/components/PluginItemDelegate.qml index 21af2f5ea2a724154fd44531f3f458209883bdec..8c8886c8f8fd31779e3fd249ee72187b1a62e621 100644 --- a/src/app/settingsview/components/PluginItemDelegate.qml +++ b/src/app/settingsview/components/PluginItemDelegate.qml @@ -34,21 +34,15 @@ ItemDelegate { property string pluginIcon: "" property bool isLoaded: false property string activeId: "" - height: pluginPreferencesView.visible ? implicitHeight + pluginPreferencesView.childrenRect.height : implicitHeight + height: pluginPreferencesView.visible ? + implicitHeight + pluginPreferencesView.childrenRect.height : + implicitHeight signal settingsClicked - onActiveIdChanged: { - pluginPreferencesView.visible = activeId != pluginId ? false : !pluginPreferencesView.visible - } - - SimpleMessageDialog { - id: msgDialog - - buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel] - buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue, - SimpleMessageDialog.ButtonStyle.TintedBlack] - } + onActiveIdChanged: pluginPreferencesView.visible = activeId != pluginId ? + false : + !pluginPreferencesView.visible ColumnLayout { width: parent.width diff --git a/src/app/settingsview/components/PluginListView.qml b/src/app/settingsview/components/PluginListView.qml index 85afec4b6efa37da3ea57bddbef6a5815c1ca289..b130fc60f2f7b6edfa963e59851f7051935e2898 100644 --- a/src/app/settingsview/components/PluginListView.qml +++ b/src/app/settingsview/components/PluginListView.qml @@ -35,22 +35,6 @@ Rectangle { visible: false color: JamiTheme.secondaryBackgroundColor - JamiFileDialog { - id: pluginPathDialog - - mode: JamiFileDialog.OpenFile - title: JamiStrings.selectPluginInstall - folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation) - - nameFilters: [JamiStrings.pluginFiles, JamiStrings.allFiles] - - onAccepted: { - var url = UtilsAdapter.getAbsPath(file.toString()) - PluginModel.installPlugin(url, true) - installedPluginsModel.addPlugin() - } - } - ColumnLayout { anchors.left: root.left anchors.right: root.right @@ -88,7 +72,22 @@ Rectangle { text: JamiStrings.installPlugin - onClicked: pluginPathDialog.open() + onClicked: { + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.selectPluginInstall, + fileMode: JamiFileDialog.OpenFile, + folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation), + nameFilters: [JamiStrings.pluginFiles, JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function (file) { + var url = UtilsAdapter.getAbsPath(file.toString()) + PluginModel.installPlugin(url, true) + installedPluginsModel.addPlugin() + }) + } } ListView { diff --git a/src/app/settingsview/components/PluginPreferencesListView.qml b/src/app/settingsview/components/PluginPreferencesListView.qml index c833310ba7ab08360b99279fc59b5bbef95a44bb..3cefe4015d57bbfb5c13f19e4230583125ca64db 100644 --- a/src/app/settingsview/components/PluginPreferencesListView.qml +++ b/src/app/settingsview/components/PluginPreferencesListView.qml @@ -280,21 +280,27 @@ Rectangle { text: JamiStrings.reset - onClicked: { - msgDialog.buttonCallBacks = [function () { - if (isLoaded) { - PluginModel.unloadPlugin(pluginId) - PluginModel.resetPluginPreferencesValues(pluginId, accountId) - PluginModel.loadPlugin(pluginId) - } else { - PluginModel.resetPluginPreferencesValues(pluginId, accountId) - } - preferencesPerCategoryModel.reset() - generalPreferencesModel.reset() - }] - msgDialog.openWithParameters(JamiStrings.resetPreferences, - JamiStrings.pluginResetConfirmation.arg(pluginName)) - } + onClicked: viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: JamiStrings.resetPreferences, + infoText: JamiStrings.pluginResetConfirmation.arg(pluginName), + buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue, + SimpleMessageDialog.ButtonStyle.TintedBlack], + buttonCallBacks: [function () { + if (isLoaded) { + PluginModel.unloadPlugin(pluginId) + PluginModel.resetPluginPreferencesValues(pluginId, accountId) + PluginModel.loadPlugin(pluginId) + } else { + PluginModel.resetPluginPreferencesValues(pluginId, accountId) + } + preferencesPerCategoryModel.reset() + generalPreferencesModel.reset() + }] + }) } } } diff --git a/src/app/settingsview/components/PluginPreferencesView.qml b/src/app/settingsview/components/PluginPreferencesView.qml index 78021d267e592a7138ccde40bd24435789247574..33d267c5890a3cb209c2c10eaba8da00b89b1b7f 100644 --- a/src/app/settingsview/components/PluginPreferencesView.qml +++ b/src/app/settingsview/components/PluginPreferencesView.qml @@ -173,15 +173,21 @@ Rectangle { text: JamiStrings.uninstall - onClicked: { - msgDialog.buttonCallBacks = [function () { - pluginPreferencesView.visible = false - PluginModel.uninstallPlugin(pluginId) - installedPluginsModel.removePlugin(index) - }] - msgDialog.openWithParameters(JamiStrings.uninstallPlugin, - JamiStrings.pluginUninstallConfirmation.arg(pluginName)) - } + onClicked: viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: JamiStrings.uninstallPlugin, + infoText: JamiStrings.pluginUninstallConfirmation.arg(pluginName), + buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue, + SimpleMessageDialog.ButtonStyle.TintedBlack], + buttonCallBacks: [function () { + pluginPreferencesView.visible = false + PluginModel.uninstallPlugin(pluginId) + installedPluginsModel.removePlugin(index) + }] + }) } Rectangle { diff --git a/src/app/settingsview/components/RevokeDevicePasswordDialog.qml b/src/app/settingsview/components/RevokeDevicePasswordDialog.qml index 67fbe55b1dd9e6d0b07cac5f9de6ab3ae2c15bb9..c32f0742b5fc10b0aee45a6ac1b79f9873e6ea23 100644 --- a/src/app/settingsview/components/RevokeDevicePasswordDialog.qml +++ b/src/app/settingsview/components/RevokeDevicePasswordDialog.qml @@ -22,24 +22,17 @@ import QtQuick.Controls import QtQuick.Layouts import net.jami.Constants 1.1 +import net.jami.Models 1.1 import "../../commoncomponents" BaseModalDialog { id: root - property string deviceId : "" + required property string deviceId - signal revokeDeviceWithPassword(string idOfDevice, string password) - - function openRevokeDeviceDialog(deviceIdIn) { - deviceId = deviceIdIn - - open() - } - - width: Math.min(mainView.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) - height: Math.min(mainView.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) + width: Math.min(appWindow.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogWidth) + height: Math.min(appWindow.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.preferredDialogHeight) title: JamiStrings.removeDevice @@ -106,7 +99,7 @@ BaseModalDialog { text: JamiStrings.optionRemove onClicked: { - revokeDeviceWithPassword(deviceId, passwordEdit.text) + DeviceItemListModel.revokeDevice(deviceId, passwordEdit.text) close() } } @@ -130,6 +123,6 @@ BaseModalDialog { onClicked: close() } - } + } } } diff --git a/src/app/settingsview/components/SettingsHeader.qml b/src/app/settingsview/components/SettingsHeader.qml index d48324782c01be1b81e806161356063eb7044cc8..2d299af07a693d44403433ef44a30dfdf6cad977 100644 --- a/src/app/settingsview/components/SettingsHeader.qml +++ b/src/app/settingsview/components/SettingsHeader.qml @@ -37,7 +37,7 @@ RowLayout { Layout.preferredWidth: JamiTheme.preferredFieldHeight Layout.preferredHeight: JamiTheme.preferredFieldHeight - visible: mainView.sidePanelOnly + visible: viewCoordinator.singlePane onClicked: backArrowClicked() } diff --git a/src/app/settingsview/components/SettingsMenu.qml b/src/app/settingsview/components/SettingsMenu.qml index 17533734eef94ff24fffb5c84683698792cca134..77910d9a44fc5a559703ffcac87b083f50879e31 100644 --- a/src/app/settingsview/components/SettingsMenu.qml +++ b/src/app/settingsview/components/SettingsMenu.qml @@ -22,96 +22,81 @@ import QtQuick.Controls import net.jami.Models 1.1 import net.jami.Constants 1.1 -// TODO: these includes should generally be resource uris import "../../commoncomponents" import "../../settingsview" Rectangle { id: root - signal itemSelected(int index) - signal buttonSelectedManually(int index) - color: JamiTheme.backgroundColor + // The following bindings provide the settings menu selection persistence behavior. + property bool singlePane: viewCoordinator.singlePane + onSinglePaneChanged: { + if (!viewCoordinator.singlePane && viewCoordinator.inSettings) { + const idx = viewCoordinator.currentView.selectedMenu + buttonGroup.checkedButton = buttonGroup.buttons[idx] + } + } + onVisibleChanged: buttonGroup.checkedButton = visible && !viewCoordinator.singlePane ? + buttonGroup.buttons[0] : + null + + // Bind to requests for a settings page to be selected via shorcut. + Connections { + target: JamiQmlUtils + function onSettingsPageRequested(index) { + buttonGroup.checkedButton = buttonGroup.buttons[index] + } + } + ButtonGroup { id: buttonGroup - - buttons: buttons.children - onCheckedButtonChanged: itemSelected(checkedButton.menuType) + buttons: settingsButtons.children + + // When the selection changes, we present the SettingsView at + // the selected index. + onCheckedButtonChanged: { + for (var i = 0; i < buttons.length; i++) + if (buttons[i] === checkedButton) { + if (viewCoordinator.singlePane) { + viewCoordinator.present("SettingsView").selectedMenu = i + } else if (!viewCoordinator.busy) { + var settingsView = viewCoordinator.getView("SettingsView") + settingsView.selectedMenu = i + } + } + } } Column { - id: buttons + id: settingsButtons spacing: 0 anchors.left: parent.left anchors.right: parent.right height: childrenRect.height - SettingsMenuButton { - id: accountPushButton - property int menuType: SettingsView.Account - Connections { - target: root + component SMB: SettingsMenuButton { normalColor: root.color } - function onButtonSelectedManually(index) { - if (accountPushButton.menuType === index) - buttonGroup.checkedButton = accountPushButton - } - } - checked: true + SMB { buttonText: JamiStrings.accountSettingsMenuTitle source: JamiResources.account_24dp_svg - normalColor: root.color } - SettingsMenuButton { - id: generalPushButton - property int menuType: SettingsView.General - Connections { - target: root - - function onButtonSelectedManually(index) { - if (generalPushButton.menuType === index) - buttonGroup.checkedButton = generalPushButton - } - } + SMB { buttonText: JamiStrings.generalSettingsTitle source: JamiResources.gear_black_24dp_svg - normalColor: root.color } - SettingsMenuButton { - id: mediaPushButton - property int menuType: SettingsView.Media - Connections { - target: root - - function onButtonSelectedManually(index) { - if (mediaPushButton.menuType === index) - buttonGroup.checkedButton = mediaPushButton - } - } + SMB { buttonText: JamiStrings.avSettingsMenuTitle source: JamiResources.media_black_24dp_svg - normalColor: root.color } - SettingsMenuButton { - id: pluginPushButton - property int menuType: SettingsView.Plugin - Connections { - target: root - - function onButtonSelectedManually(index) { - if (pluginPushButton.menuType === index) - buttonGroup.checkedButton = pluginPushButton - } - } + SMB { buttonText: JamiStrings.pluginSettingsTitle source: JamiResources.plugin_settings_black_24dp_svg - normalColor: root.color } } } diff --git a/src/app/settingsview/components/UpdateDownloadDialog.qml b/src/app/settingsview/components/UpdateDownloadDialog.qml new file mode 100644 index 0000000000000000000000000000000000000000..88cf9f351020f3f1b585c946d87ee82ea27a955a --- /dev/null +++ b/src/app/settingsview/components/UpdateDownloadDialog.qml @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import net.jami.Adapters 1.1 +import net.jami.Enums 1.1 +import net.jami.Models 1.1 +import net.jami.Helpers 1.1 +import net.jami.Constants 1.1 + +import "../../commoncomponents" + +SimpleMessageDialog { + id: downloadDialog + + property int bytesRead: 0 + property int totalBytes: 0 + property string hSizeRead: UtilsAdapter.humanFileSize(bytesRead) + property string hTotalBytes: UtilsAdapter.humanFileSize(totalBytes) + property alias progressBarValue: progressBar.value + + Connections { + target: UpdateManager + + function onUpdateDownloadProgressChanged(bytesRead, totalBytes) { + downloadDialog.setDownloadProgress(bytesRead, totalBytes) + } + + function onUpdateDownloadErrorOccurred(error) { + downloadDialog.close() + } + + function onUpdateDownloadFinished() { + downloadDialog.close() + } + } + + function setDownloadProgress(bytesRead, totalBytes) { + downloadDialog.bytesRead = bytesRead + downloadDialog.totalBytes = totalBytes + } + + infoText: JamiStrings.updateDownloading + + " (%1 / %2)".arg(hSizeRead).arg(hTotalBytes) + + innerContentData: ProgressBar { + id: progressBar + + value: downloadDialog.bytesRead / + downloadDialog.totalBytes + + anchors.left: parent.left + anchors.leftMargin: JamiTheme.preferredMarginSize + anchors.right: parent.right + anchors.rightMargin: JamiTheme.preferredMarginSize + + background: Rectangle { + implicitWidth: parent.width + implicitHeight: 24 + color: JamiTheme.darkGrey + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: 22 + + Rectangle { + width: progressBar.visualPosition * parent.width + height: parent.height + color: JamiTheme.selectionBlue + } + Label { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + color: JamiTheme.whiteColor + font.bold: true + font.pointSize: JamiTheme.textFontSize + 1 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: Math.ceil(progressBar.value * 100).toString() + "%" + } + } + } + + buttonTitles: [JamiStrings.optionCancel] + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] + buttonCallBacks: [function() { UpdateManager.cancelUpdate() }] + onVisibleChanged: { + if (!visible) + UpdateManager.cancelUpdate() + } +} diff --git a/src/app/settingsview/components/UpdateSettings.qml b/src/app/settingsview/components/UpdateSettings.qml index 1f198fe499a739bd259b18cc32868efbdda2a3cf..124e7e31c2272c294e73035d31d034640f38657c 100644 --- a/src/app/settingsview/components/UpdateSettings.qml +++ b/src/app/settingsview/components/UpdateSettings.qml @@ -103,11 +103,36 @@ ColumnLayout { toolTipText: JamiStrings.betaInstall text: JamiStrings.betaInstall - onClicked: { - confirmInstallDialog.beta = true - confirmInstallDialog.openWithParameters(JamiStrings.updateDialogTitle, - JamiStrings.confirmBeta) - } + onClicked: presentConfirmInstallDialog(JamiStrings.confirmBeta, true) + } + + function presentInfoDialog(infoText) { + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: JamiStrings.updateDialogTitle, + infoText: infoText, + buttonTitles: [JamiStrings.optionOk], + buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue], + buttonCallBacks: [] + }) + } + + function presentConfirmInstallDialog(infoText, beta) { + viewCoordinator.presentDialog( + appWindow, + "commoncomponents/SimpleMessageDialog.qml", + { + title: JamiStrings.updateDialogTitle, + infoText: infoText, + buttonTitles: [JamiStrings.optionUpgrade, JamiStrings.optionLater], + buttonStyles: [ + SimpleMessageDialog.ButtonStyle.TintedBlue, + SimpleMessageDialog.ButtonStyle.TintedBlue + ], + buttonCallBacks: [function() {UpdateManager.applyUpdates(beta)}] + }) } Connections { @@ -129,128 +154,31 @@ ColumnLayout { } } + function onUpdateDownloadStarted() { + viewCoordinator.presentDialog( + appWindow, + "settingsview/components/UpdateDownloadDialog.qml", + {title: JamiStrings.updateDialogTitle}) + } + function onUpdateCheckReplyReceived(ok, found) { if (!ok) { - issueDialog.openWithParameters(JamiStrings.updateDialogTitle, - JamiStrings.updateCheckError) + presentInfoDialog(JamiStrings.updateCheckError) return } if (!found) { - issueDialog.openWithParameters(JamiStrings.updateDialogTitle, - JamiStrings.updateNotFound) + presentInfoDialog(JamiStrings.updateNotFound) } else { - confirmInstallDialog.openWithParameters(JamiStrings.updateDialogTitle, - JamiStrings.updateFound) + presentConfirmInstallDialog(JamiStrings.confirmUpdate, false) } } - function onUpdateCheckErrorOccurred(error) { - issueDialog.openWithParameters(JamiStrings.updateDialogTitle, - errorToString(error)) - } - - function onUpdateDownloadStarted() { - downloadDialog.setDownloadProgress(0, 0) - downloadDialog.openWithParameters(JamiStrings.updateDialogTitle) - } - - function onUpdateDownloadProgressChanged(bytesRead, totalBytes) { - downloadDialog.setDownloadProgress(bytesRead, totalBytes) - } - function onUpdateDownloadErrorOccurred(error) { - downloadDialog.close() - issueDialog.openWithParameters(JamiStrings.updateDialogTitle, - errorToString(error)) + presentInfoDialog(errorToString(error)) } - function onUpdateDownloadFinished() { downloadDialog.close() } - } - - SimpleMessageDialog { - id: confirmInstallDialog - - property bool beta: false - - buttonTitles: [JamiStrings.optionUpgrade, JamiStrings.optionLater] - buttonStyles: [ - SimpleMessageDialog.ButtonStyle.TintedBlue, - SimpleMessageDialog.ButtonStyle.TintedBlue - ] - buttonCallBacks: [function() {UpdateManager.applyUpdates(beta)}] - } - - SimpleMessageDialog { - id: issueDialog - - buttonTitles: [JamiStrings.optionOk] - buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] - buttonCallBacks: [] - } - - SimpleMessageDialog { - id: downloadDialog - - property int bytesRead: 0 - property int totalBytes: 0 - property string hSizeRead: UtilsAdapter.humanFileSize(bytesRead) - property string hTotalBytes: UtilsAdapter.humanFileSize(totalBytes) - property alias progressBarValue: progressBar.value - - function setDownloadProgress(bytesRead, totalBytes) { - downloadDialog.bytesRead = bytesRead - downloadDialog.totalBytes = totalBytes - } - - infoText: JamiStrings.updateDownloading + - " (%1 / %2)".arg(hSizeRead).arg(hTotalBytes) - - innerContentData: ProgressBar { - id: progressBar - - value: downloadDialog.bytesRead / - downloadDialog.totalBytes - - anchors.left: parent.left - anchors.leftMargin: JamiTheme.preferredMarginSize - anchors.right: parent.right - anchors.rightMargin: JamiTheme.preferredMarginSize - - background: Rectangle { - implicitWidth: parent.width - implicitHeight: 24 - color: JamiTheme.darkGrey - } - - contentItem: Item { - implicitWidth: parent.width - implicitHeight: 22 - - Rectangle { - width: progressBar.visualPosition * parent.width - height: parent.height - color: JamiTheme.selectionBlue - } - Label { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - - color: JamiTheme.whiteColor - font.bold: true - font.pointSize: JamiTheme.textFontSize + 1 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: Math.ceil(progressBar.value * 100).toString() + "%" - } - } - } - - buttonTitles: [JamiStrings.optionCancel] - buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] - buttonCallBacks: [function() {UpdateManager.cancelUpdate()}] - onVisibleChanged: { - if (!visible) - UpdateManager.cancelUpdate() + function onUpdateCheckErrorOccurred(error) { + presentInfoDialog(errorToString(error)) } } } diff --git a/src/app/updatemanager.cpp b/src/app/updatemanager.cpp index 7e4f97f29915f63f8a0efa738b09e105401caac4..d81383ce70f1636295a9d1e72aeee1fbfa2fc7d9 100644 --- a/src/app/updatemanager.cpp +++ b/src/app/updatemanager.cpp @@ -48,7 +48,7 @@ struct UpdateManager::Impl : public QObject , tempPath_(QDir::tempPath()) , updateTimer_(new QTimer(this)) { - connect(updateTimer_, &QTimer::timeout, [this] { + connect(updateTimer_, &QTimer::timeout, this, [this] { // Quiet period update check. parent_.checkForUpdates(true); }); @@ -96,7 +96,7 @@ struct UpdateManager::Impl : public QObject &NetWorkManager::errorOccured, &parent_, &UpdateManager::updateDownloadErrorOccurred); - connect(&parent_, &NetWorkManager::statusChanged, [this](GetStatus status) { + connect(&parent_, &NetWorkManager::statusChanged, this, [this](GetStatus status) { switch (status) { case GetStatus::STARTED: connect(&parent_, diff --git a/src/app/utilsadapter.h b/src/app/utilsadapter.h index 4db0723c7df9b1a0a77c73a37ccd8a9aba8f6534..89fad25139d8d2ce09d162cab3e775fead1fc4e3 100644 --- a/src/app/utilsadapter.h +++ b/src/app/utilsadapter.h @@ -42,7 +42,6 @@ class SystemTray; #define LOGSLIMIT 10000 - #if defined(WIN32) && __has_include(<winrt/Windows.Foundation.h>) /** * @brief Read if "AppsUseLightTheme" registry exists and its value @@ -66,8 +65,6 @@ public: QObject* parent = nullptr); ~UtilsAdapter() = default; - void safeInit() override {} - Q_INVOKABLE const QString getProjectCredits(); Q_INVOKABLE const QString getVersionStr(); Q_INVOKABLE void setClipboardText(QString text); diff --git a/src/app/wizardview/WizardView.qml b/src/app/wizardview/WizardView.qml index fded8ff5758ec8c16da4479d5c50fc3ac4522f0a..bd0d7514534a5389f2ff15b789e87b578736827b 100644 --- a/src/app/wizardview/WizardView.qml +++ b/src/app/wizardview/WizardView.qml @@ -30,8 +30,10 @@ import "../" import "../commoncomponents" import "components" -Rectangle { +BaseView { id: root + objectName: "WizardView" + singlePaneOnly: true // signal to redirect the page to main view signal loaderSourceChangeRequested(int sourceToLoad) @@ -59,6 +61,7 @@ Rectangle { function onCloseWizardView() { loaderSourceChangeRequested(MainApplicationWindow.LoadedSource.MainView) + root.dismiss() } } diff --git a/src/app/wizardview/components/ImportFromBackupPage.qml b/src/app/wizardview/components/ImportFromBackupPage.qml index 191d76180ec89e11e3081717e0b5e8a2637a3d7b..534f3d6e1e8e11b65e6e858d55eed9e56f30848a 100644 --- a/src/app/wizardview/components/ImportFromBackupPage.qml +++ b/src/app/wizardview/components/ImportFromBackupPage.qml @@ -67,35 +67,6 @@ Rectangle { color: JamiTheme.secondaryBackgroundColor - JamiFileDialog { - id: importFromFileDialog - - mode: JamiFileDialog.OpenFile - title: JamiStrings.openFile - folder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop" - - nameFilters: [JamiStrings.jamiArchiveFiles, JamiStrings.allFiles] - - onVisibleChanged: { - if (!visible) { - rejected() - } - } - - onRejected: { - fileImportBtn.forceActiveFocus() - } - - onAccepted: { - filePath = file - if (file.length !== "") { - fileImportBtnText = UtilsAdapter.toFileInfoName(file) - } else { - fileImportBtnText = JamiStrings.archive - } - } - } - ColumnLayout { id: importFromBackupPageColumnLayout @@ -157,7 +128,28 @@ Rectangle { onClicked: { errorText = "" - importFromFileDialog.open() + var dlg = viewCoordinator.presentDialog( + appWindow, + "commoncomponents/JamiFileDialog.qml", + { + title: JamiStrings.openFile, + fileMode: JamiFileDialog.OpenFile, + folder: StandardPaths.writableLocation( + StandardPaths.HomeLocation) + "/Desktop", + nameFilters: [JamiStrings.jamiArchiveFiles, + JamiStrings.allFiles] + }) + dlg.fileAccepted.connect(function(file) { + filePath = file + if (file.length !== "") { + fileImportBtnText = UtilsAdapter.toFileInfoName(file) + } else { + fileImportBtnText = JamiStrings.archive + } + }) + dlg.rejected.connect(function() { + fileImportBtn.forceActiveFocus() + }) } } diff --git a/src/app/wizardview/components/WelcomePage.qml b/src/app/wizardview/components/WelcomePage.qml index 1dcb2dfcdf9af9e58d91d79ce9e580e8543de728..ee914f42dbbfc03e399820be8fd5711ce6e5d368 100644 --- a/src/app/wizardview/components/WelcomePage.qml +++ b/src/app/wizardview/components/WelcomePage.qml @@ -61,7 +61,6 @@ Rectangle { KeyNavigation.up: newAccountButton KeyNavigation.down: KeyNavigation.tab - ColumnLayout { id: welcomePageColumnLayout @@ -72,13 +71,6 @@ Rectangle { anchors.topMargin: JamiTheme.wizardViewLayoutTopMargin width: Math.max(508, root.width - 100) - AboutPopUp { - id: aboutPopUpDialog - - width: Math.min(parent.width - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - height: Math.min(parent.height - 2 * JamiTheme.preferredMarginSize, JamiTheme.secondaryDialogDimension) - } - ResponsiveImage { id: welcomeLogo @@ -231,7 +223,6 @@ Rectangle { onClicked: WizardViewStepModel.startAccountCreationFlow(WizardViewStepModel.AccountCreationOption.ImportFromBackup) } - MaterialButton { id: showAdvancedButton @@ -337,7 +328,6 @@ Rectangle { WizardViewStepModel.AccountCreationOption.CreateSipAccount) } - MaterialButton { id: btnAboutPopUp @@ -355,7 +345,9 @@ Rectangle { text: JamiStrings.aboutJami - onClicked: aboutPopUpDialog.open() + onClicked: viewCoordinator.presentDialog( + parent, + "mainview/components/AboutPopUp.qml") } } @@ -385,7 +377,5 @@ Rectangle { KeyNavigation.down: KeyNavigation.tab onClicked: WizardViewStepModel.previousStep() - } - } diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp index ce0b779c9a69785255a0070cba90f6d670341873..c87311cf48c515ab392c13f12d175e051af1f119 100644 --- a/src/libclient/callmodel.cpp +++ b/src/libclient/callmodel.cpp @@ -400,7 +400,7 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin } #ifdef ENABLE_LIBWRAP auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); -#else // dbus +#else // dbus // do not use auto here (QDBusPendingReply<QString>) QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); #endif // ENABLE_LIBWRAP