From 762cbbff38f816f21c4eda139eceedf24b74c231 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Mon, 2 Oct 2023 13:56:55 -0400
Subject: [PATCH] mainview: prevent details panel from opening maximized

also-misc: Removes SplitPanel state-saving

Gitlab: #1370
Change-Id: I71f5c209922ad69fb701e935a03078fb8e5ad2de
---
 src/app/commoncomponents/JamiSplitView.qml    |   1 +
 src/app/mainview/ConversationView.qml         |  20 +--
 src/app/mainview/components/ChatView.qml      | 122 +++++++++++-------
 .../mainview/components/ChatViewHeader.qml    |  25 ++--
 .../components/ConversationExtrasPanel.qml    |  68 +++++-----
 .../mainview/components/OngoingCallPage.qml   |  40 +++---
 .../mainview/components/SwarmDetailsPanel.qml |   1 +
 7 files changed, 155 insertions(+), 122 deletions(-)

diff --git a/src/app/commoncomponents/JamiSplitView.qml b/src/app/commoncomponents/JamiSplitView.qml
index e21b94623..0442116b9 100644
--- a/src/app/commoncomponents/JamiSplitView.qml
+++ b/src/app/commoncomponents/JamiSplitView.qml
@@ -19,6 +19,7 @@ import QtQuick.Controls
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
 
+// A SplitView that supports dynamic RTL and splitView state saving.
 SplitView {
     id: root
 
diff --git a/src/app/mainview/ConversationView.qml b/src/app/mainview/ConversationView.qml
index 0f1c8e8f0..3bc72cd0d 100644
--- a/src/app/mainview/ConversationView.qml
+++ b/src/app/mainview/ConversationView.qml
@@ -32,6 +32,9 @@ ListSelectionView {
     splitViewStateKey: "Main"
     hasValidSelection: CurrentConversation.id !== ''
 
+    visible: false
+    onPresented: visible = true
+
     Connections {
         target: CurrentConversation
         function onReloadInteractions() {
@@ -75,7 +78,7 @@ ListSelectionView {
                 anchors.fill: parent
                 inCallView: parent == callStackView.chatViewContainer
 
-                property string currentConvId: CurrentConversation.id
+                readonly property string currentConvId: CurrentConversation.id
                 onCurrentConvIdChanged: {
                     if (!CurrentConversation.hasCall) {
                         Qt.callLater(focusChatView);
@@ -86,7 +89,7 @@ ListSelectionView {
                 }
 
                 onDismiss: {
-                    if (parent == chatViewContainer) {
+                    if (!inCallView) {
                         viewNode.dismiss();
                     } else {
                         callStackView.chatViewContainer.visible = false;
@@ -94,13 +97,14 @@ ListSelectionView {
                     }
                 }
 
+                // Handle visibility change for the in-call chat only.
                 onVisibleChanged: {
-                    if (!inCallView)
-                        return;
-                    if (visible && !parent.showDetails) {
-                        focusChatView();
-                    } else {
-                        callStackView.contentView.forceActiveFocus();
+                    if (inCallView) {
+                        if (visible) {
+                            focusChatView();
+                        } else {
+                            callStackView.contentView.forceActiveFocus();
+                        }
                     }
                 }
             }
diff --git a/src/app/mainview/components/ChatView.qml b/src/app/mainview/components/ChatView.qml
index 9182c823d..0bb64705f 100644
--- a/src/app/mainview/components/ChatView.qml
+++ b/src/app/mainview/components/ChatView.qml
@@ -21,6 +21,7 @@ import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
 import net.jami.Enums 1.1
+
 import "../../commoncomponents"
 import "../js/pluginhandlerpickercreation.js" as PluginHandlerPickerCreation
 
@@ -28,7 +29,7 @@ Rectangle {
     id: root
 
     // An enum to make the details panels more readable.
-    enum Panel {
+    enum ExtrasPanel {
         SwarmDetailsPanel,
         MessagesResearchPanel,
         AddMemberPanel
@@ -40,6 +41,18 @@ Rectangle {
 
     required property bool inCallView
 
+    // Hide the extrasPanel when going into a call view, but save the previous
+    // state to restore it when leaving the call view.
+    property int chatExtrasPanelIndex: extrasPanel.currentIndex
+    onInCallViewChanged: {
+        if (inCallView) {
+            chatExtrasPanelIndex = extrasPanel.currentIndex;
+            extrasPanel.closePanel();
+        } else if (chatExtrasPanelIndex >= 0) {
+            extrasPanel.openPanel(chatExtrasPanelIndex);
+        }
+    }
+
     signal dismiss
 
     function focusChatView() {
@@ -61,6 +74,16 @@ Rectangle {
         }
     }
 
+    // Used externally to switch to a extras panel.
+    function switchToPanel(panel, toggle = true) {
+        extrasPanel.switchToPanel(panel, toggle);
+    }
+
+    // Used externally to close the extras panel.
+    function closePanel() {
+        extrasPanel.closePanel();
+    }
+
     Connections {
         target: PositionManager
         function onOpenNewMap() {
@@ -71,23 +94,13 @@ Rectangle {
     Connections {
         target: CurrentConversation
         function onIdChanged() {
-            extrasPanel.restoreState();
             MessagesAdapter.loadMoreMessages();
         }
     }
 
-    Component.onCompleted: extrasPanel.restoreState()
-
     onVisibleChanged: {
         if (visible) {
             chatViewSplitView.resolvePanes(true);
-            if (root.parent.objectName === "CallViewChatViewContainer") {
-                if (root.parent.showDetails) {
-                    extrasPanel.switchToPanel(ChatView.SwarmDetailsPanel);
-                } else {
-                    extrasPanel.closePanel();
-                }
-            }
         }
     }
 
@@ -99,9 +112,6 @@ Rectangle {
         ChatViewHeader {
             id: chatViewHeader
 
-            addParticipantOpened: extrasPanel.currentIndex === ChatView.AddMemberPanel
-            swarmDetailsOpened: extrasPanel.currentIndex === ChatView.SwarmDetailsPanel
-
             Layout.alignment: Qt.AlignHCenter
             Layout.fillWidth: true
             Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
@@ -114,9 +124,6 @@ Rectangle {
             }
 
             onBackClicked: root.dismiss()
-            onShowDetailsClicked: extrasPanel.switchToPanel(ChatView.SwarmDetailsPanel)
-            onSearchClicked: extrasPanel.switchToPanel(ChatView.MessagesResearchPanel)
-            onAddToConversationClicked: extrasPanel.switchToPanel(ChatView.AddMemberPanel)
 
             Connections {
                 target: CurrentConversation
@@ -190,47 +197,69 @@ Rectangle {
             Layout.fillWidth: true
             Layout.fillHeight: true
 
-            splitViewStateKey: "Chat"
-
-            property real previousDetailsWidth: extrasPanel.width
             property real previousWidth: width
-            onWidthChanged: resolvePanes()
-            // This function governs the visibility of the chatContents and tracks the
-            // the width of the SplitView and the details panel. This function should be
-            // called when the width of the SplitView changes, when the SplitView is shown,
-            // and when the details panel is shown. When called with force=true, it is being
-            // called from a visibleChanged event, and we should not update the previous widths.
+            onWidthChanged: {
+                resolvePanes();
+                // Track the previous width of the split view.
+                previousWidth = width;
+            }
+
+            // Track the previous width of the split view.
+            property real extrasPanelWidth: extrasPanel.width
+            // The previousExtrasPanelWidth is initialized to the minimum width
+            // of the extras panel. The value is updated within the "open"-state
+            // range of the panel (e.g. not 0 or maximized).
+            property real previousExtrasPanelWidth: JamiTheme.extrasPanelMinWidth
+            onExtrasPanelWidthChanged: {
+                resolvePanes();
+                // This range should ensure that the panel won't restore to maximized.
+                if (extrasPanelWidth != 0 && extrasPanelWidth != width) {
+                    console.debug("Saving previous extras panel width: %1".arg(extrasPanelWidth));
+                    previousExtrasPanelWidth = extrasPanelWidth;
+                }
+            }
+
+            // Respond to visibility changes for the extras panel in order to
+            // determine the structure of the split view.
+            property bool extrasPanelVisible: extrasPanel.visible
+            onExtrasPanelVisibleChanged: {
+                if (extrasPanelVisible) {
+                    extrasPanelWidth = previousExtrasPanelWidth;
+                } else {
+                    previousExtrasPanelWidth = extrasPanelWidth;
+                }
+                resolvePanes();
+            }
+
             function resolvePanes(force = false) {
+                if (!viewNode.visible) {
+                    return;
+                }
+
                 // If the details panel is not visible, then show the chatContents.
                 if (!extrasPanel.visible) {
                     chatContents.visible = true;
                     return;
                 }
 
-                // Next we compute whether the SplitView is expanding or shrinking.
                 const isExpanding = width > previousWidth;
 
+                // Provide a detailed log here, as this function seems problematic.
+                console.debug("ChatViewSplitView.resolvePanes: f: %1 w: %2 pw: %3 epw: %4 pepw: %5 ie: %6"
+                              .arg(force).arg(width).arg(previousWidth)
+                              .arg(extrasPanelWidth).arg(previousExtrasPanelWidth).arg(isExpanding));
+
+                const maximizePredicate = (!isExpanding || force) && chatContents.visible;
+                const minimizePredicate = (isExpanding || force) && !chatContents.visible;
+                const mainViewMinWidth = JamiTheme.mainViewPaneMinWidth;
+
                 // If the SplitView is not wide enough to show both the chatContents
                 // and the details panel, then hide the chatContents.
-                if (width < JamiTheme.mainViewPaneMinWidth + extrasPanel.width && (!isExpanding || force) && chatContents.visible) {
-                    if (!force)
-                        previousDetailsWidth = extrasPanel.width;
+                if (maximizePredicate && width < mainViewMinWidth + extrasPanelWidth) {
                     chatContents.visible = false;
-                } else if (width >= JamiTheme.mainViewPaneMinWidth + previousDetailsWidth && (isExpanding || force) && !chatContents.visible) {
+                } else if (minimizePredicate && width >= mainViewMinWidth + previousExtrasPanelWidth) {
                     chatContents.visible = true;
                 }
-                if (!force)
-                    previousWidth = width;
-            }
-
-            Connections {
-                target: viewNode
-                function onPresented() {
-                    chatViewSplitView.restoreSplitViewState();
-                }
-                function onDismissed() {
-                    chatViewSplitView.saveSplitViewState();
-                }
             }
 
             ColumnLayout {
@@ -315,19 +344,12 @@ Rectangle {
                 }
             }
 
-            onResizingChanged: if (chatContents.visible)
-                extrasPanel.previousWidth = extrasPanel.width
-
             ConversationExtrasPanel {
                 id: extrasPanel
 
-                property int previousWidth: JamiTheme.extrasPanelMinWidth
-
                 SplitView.maximumWidth: root.width
                 SplitView.minimumWidth: JamiTheme.extrasPanelMinWidth
                 SplitView.preferredWidth: JamiTheme.extrasPanelMinWidth
-
-                onVisibleChanged: chatViewSplitView.resolvePanes(true)
             }
         }
     }
diff --git a/src/app/mainview/components/ChatViewHeader.qml b/src/app/mainview/components/ChatViewHeader.qml
index 52bdcca98..5b89c34cf 100644
--- a/src/app/mainview/components/ChatViewHeader.qml
+++ b/src/app/mainview/components/ChatViewHeader.qml
@@ -23,16 +23,14 @@ import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
 import net.jami.Enums 1.1
 import net.jami.Models 1.1
+
 import "../../commoncomponents"
 
 Rectangle {
     id: root
 
     signal backClicked
-    signal addToConversationClicked
     signal pluginSelector
-    signal showDetailsClicked
-    signal searchClicked
 
     Connections {
         target: CurrentConversation
@@ -44,7 +42,7 @@ Rectangle {
             description.eText = CurrentConversation.description;
         }
         function onShowSwarmDetails() {
-            root.showDetailsClicked();
+            extrasPanel.switchToPanel(ChatView.SwarmDetailsPanel);
         }
     }
 
@@ -60,11 +58,10 @@ Rectangle {
         return true;
     }
 
-    property bool addParticipantOpened: false
-    property bool swarmDetailsOpened: false
-
     property bool addMemberVisibility: {
-        return swarmDetailsVisibility && !CurrentConversation.isCoreDialog && !CurrentConversation.isRequest;
+        return swarmDetailsVisibility
+                && !CurrentConversation.isCoreDialog
+                && !CurrentConversation.isRequest;
     }
 
     property bool swarmDetailsVisibility: {
@@ -171,9 +168,7 @@ Rectangle {
                 MessagesAdapter.searchbarPrompt = text;
             }
 
-            onSearchClicked: {
-                root.searchClicked();
-            }
+            onSearchClicked: extrasPanel.switchToPanel(ChatView.MessagesResearchPanel)
 
             Shortcut {
                 sequence: "Ctrl+Shift+F"
@@ -209,12 +204,12 @@ Rectangle {
             id: addParticipantsButton
 
             checkable: true
-            checked: root.addParticipantOpened
+            checked: extrasPanel.isOpen(ChatView.AddMemberPanel)
             visible: interactionButtonsVisibility && addMemberVisibility
             source: JamiResources.add_people_24dp_svg
             toolTipText: JamiStrings.addParticipants
 
-            onClicked: addToConversationClicked()
+            onClicked: extrasPanel.switchToPanel(ChatView.AddMemberPanel)
         }
 
         JamiPushButton {
@@ -241,12 +236,12 @@ Rectangle {
             id: detailsButton
 
             checkable: true
-            checked: root.swarmDetailsOpened
+            checked: extrasPanel.isOpen(ChatView.SwarmDetailsPanel)
             visible: interactionButtonsVisibility && (swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
             source: JamiResources.swarm_details_panel_svg
             toolTipText: JamiStrings.details
 
-            onClicked: showDetailsClicked()
+            onClicked: extrasPanel.switchToPanel(ChatView.SwarmDetailsPanel)
         }
     }
 
diff --git a/src/app/mainview/components/ConversationExtrasPanel.qml b/src/app/mainview/components/ConversationExtrasPanel.qml
index 9bc2aed81..292133f0d 100644
--- a/src/app/mainview/components/ConversationExtrasPanel.qml
+++ b/src/app/mainview/components/ConversationExtrasPanel.qml
@@ -21,24 +21,46 @@ import net.jami.Adapters 1.1
 StackLayout {
     id: root
 
+    // We need to set the currentIndex to -1 to make sure the
+    // panel is closed when the application starts.
+    Component.onCompleted: closePanel()
+
+    // The index of the tab in the swarm details panel.
     property int detailsIndex: -1
 
-    function isOpen(panel) {
-        return visible && currentIndex === panel;
+    // Best to avoid using the visible property directly.
+    // Pass through the following open/close wrappers instead.
+    function openPanel(panel) {
+        currentIndex = panel;
+        visible = true;
     }
 
-    visible: currentIndex > -1
+    function closePanel() {
+        currentIndex = -1;
+        visible = false;
+    }
 
-    property bool detailsShouldOpen: false
-    onVisibleChanged: if (visible)
-        detailsShouldOpen = true
+    function isOpen(panel) {
+        return visible && currentIndex === panel;
+    }
 
-    function restoreState() {
-        // Only applies to Jami accounts, and we musn't be in a call.
-        if (detailsShouldOpen && !inCallView && !CurrentConversation.needsSyncing && !CurrentConversation.isRequest) {
-            switchToPanel(ChatView.SwarmDetailsPanel, false);
+    // This will open the details panel if it's not already visible.
+    // Additionally, `toggle` being true (default) will close the panel
+    // if it is already open to `panel`.
+    function switchToPanel(panel, toggle = true) {
+        console.debug("switchToPanel: %1, toggle: %2".arg(panel).arg(toggle));
+        if (visible) {
+            // We need to close the panel if it's open and we're switching to
+            // the same panel.
+            if (toggle && currentIndex === panel) {
+                // Toggle off.
+                closePanel();
+            } else {
+                // Switch to the new panel.
+                openPanel(panel);
+            }
         } else {
-            closePanel();
+            openPanel(panel);
         }
     }
 
@@ -54,24 +76,6 @@ StackLayout {
         }
     }
 
-    // This will open the details panel if it's not already visible.
-    // Additionally, `toggle` being true (default) will close the panel
-    // if it is already open to `panel`.
-    function switchToPanel(panel, toggle = true) {
-        if (visible && toggle && currentIndex === panel) {
-            closePanel();
-        } else {
-            currentIndex = panel;
-        }
-    }
-
-    function closePanel() {
-        // We need to close the panel, but not save it when appropriate.
-        currentIndex = -1;
-        if (!inCallView)
-            detailsShouldOpen = false;
-    }
-
     SwarmDetailsPanel {
         id: detailsPanel
 
@@ -81,8 +85,6 @@ StackLayout {
         // Save it when it changes.
         onTabBarIndexChanged: root.detailsIndex = tabBarIndex
     }
-    MessagesResearchPanel {
-    }
-    AddMemberPanel {
-    }
+    MessagesResearchPanel {}
+    AddMemberPanel {}
 }
diff --git a/src/app/mainview/components/OngoingCallPage.qml b/src/app/mainview/components/OngoingCallPage.qml
index c5f75426a..ecb247dcb 100644
--- a/src/app/mainview/components/OngoingCallPage.qml
+++ b/src/app/mainview/components/OngoingCallPage.qml
@@ -39,6 +39,9 @@ Rectangle {
     property alias chatViewContainer: chatViewContainer
     property string callPreviewId
 
+    // A link to the first child will provide access to the chat view.
+    property var chatView: chatViewContainer.children[0]
+
     onCallPreviewIdChanged: {
         controlPreview.start();
     }
@@ -61,6 +64,20 @@ Rectangle {
         }
     }
 
+    function setCallChatVisibility(visible) {
+        if (visible) {
+            mainColumnLayout.isHorizontal = UtilsAdapter.getAppValue(Settings.Key.ShowChatviewHorizontally);
+            chatViewContainer.visible = false;
+            chatViewContainer.visible = true;
+        } else {
+            chatViewContainer.visible = false;
+        }
+    }
+
+    function toggleCallChatVisibility() {
+        setCallChatVisibility(!chatViewContainer.visible);
+    }
+
     function openInCallConversation() {
         mainColumnLayout.isHorizontal = UtilsAdapter.getAppValue(Settings.Key.ShowChatviewHorizontally);
         chatViewContainer.visible = false;
@@ -291,10 +308,7 @@ Rectangle {
                 anchors.fill: parent
 
                 function toggleConversation() {
-                    if (inCallMessageWebViewStack.visible)
-                        closeInCallConversation();
-                    else
-                        openInCallConversation();
+                    toggleCallChatVisibility();
                 }
 
                 Connections {
@@ -322,19 +336,13 @@ Rectangle {
                     participantsLayer.hoveredOverVideoMuted = true;
                 }
 
-                onChatButtonClicked: {
-                    var detailsVisible = chatViewContainer.showDetails;
-                    chatViewContainer.showDetails = false;
-                    !chatViewContainer.visible || detailsVisible ? openInCallConversation() : closeInCallConversation();
-                }
-
-                onFullScreenClicked: {
-                    callStackView.toggleFullScreen();
-                }
-
+                onChatButtonClicked: toggleCallChatVisibility()
+                onFullScreenClicked: callStackView.toggleFullScreen()
                 onSwarmDetailsClicked: {
-                    chatViewContainer.showDetails = !chatViewContainer.showDetails;
-                    chatViewContainer.showDetails ? openInCallConversation() : closeInCallConversation();
+                    toggleCallChatVisibility();
+                    if (chatViewContainer.visible) {
+                        chatView.switchToPanel(ChatView.SwarmDetailsPanel);
+                    }
                 }
             }
 
diff --git a/src/app/mainview/components/SwarmDetailsPanel.qml b/src/app/mainview/components/SwarmDetailsPanel.qml
index 09471a6d5..ef8d4e23d 100644
--- a/src/app/mainview/components/SwarmDetailsPanel.qml
+++ b/src/app/mainview/components/SwarmDetailsPanel.qml
@@ -23,6 +23,7 @@ import Qt5Compat.GraphicalEffects
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
+
 import "../../commoncomponents"
 import "../../settingsview/components"
 
-- 
GitLab