From 948e2cc837b8646eace757a782666507485c959c Mon Sep 17 00:00:00 2001
From: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
Date: Mon, 29 May 2023 13:24:19 -0300
Subject: [PATCH] callactionbar: avoid multiple resets

GitLab: #1073

Change-Id: I7dc2ab632170a959b8d29979deacd3fb03ff6671
---
 src/app/calloverlaymodel.cpp                  |  48 +++++--
 src/app/calloverlaymodel.h                    |   9 +-
 src/app/currentcall.cpp                       |   7 +
 src/app/mainview/components/CallActionBar.qml | 120 ++++++++----------
 4 files changed, 104 insertions(+), 80 deletions(-)

diff --git a/src/app/calloverlaymodel.cpp b/src/app/calloverlaymodel.cpp
index dd1ee9c9e..a15496eca 100644
--- a/src/app/calloverlaymodel.cpp
+++ b/src/app/calloverlaymodel.cpp
@@ -35,10 +35,11 @@ IndexRangeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
 {
     auto index = sourceModel()->index(sourceRow, 0, sourceParent);
     bool predicate = true;
+    bool enabled = sourceModel()->data(index, CallControl::Role::Enabled).toBool();
     if (filterRole() != Qt::DisplayRole) {
         predicate = sourceModel()->data(index, filterRole()).toInt() != 0;
     }
-    return sourceRow <= max_ && sourceRow >= min_ && predicate;
+    return sourceRow <= max_ && sourceRow >= min_ && predicate && enabled;
 }
 
 void
@@ -197,10 +198,12 @@ CallControlListModel::data(const QModelIndex& index, int role) const
     auto item = data_.at(index.row());
 
     switch (role) {
-    case Role::ItemAction:
+    case CallControl::Role::ItemAction:
         return QVariant::fromValue(item.itemAction);
-    case Role::UrgentCount:
+    case CallControl::Role::UrgentCount:
         return QVariant::fromValue(item.urgentCount);
+    case CallControl::Role::Enabled:
+        return QVariant::fromValue(item.enabled);
     }
     return QVariant();
 }
@@ -212,6 +215,7 @@ CallControlListModel::roleNames() const
     QHash<int, QByteArray> roles;
     roles[ItemAction] = "ItemAction";
     roles[UrgentCount] = "UrgentCount";
+    roles[Enabled] = "Enabled";
     return roles;
 }
 
@@ -232,6 +236,24 @@ CallControlListModel::setUrgentCount(QVariant item, int count)
     }
 }
 
+void
+CallControlListModel::setEnabled(QObject* obj, bool enabled)
+{
+    beginResetModel();
+    auto it = std::find_if(data_.cbegin(), data_.cend(), [obj](const auto& item) {
+        return item.itemAction == obj;
+    });
+    if (it != data_.cend()) {
+        auto row = std::distance(data_.cbegin(), it);
+        if (row >= rowCount())
+            return;
+        data_[row].enabled = enabled;
+        auto idx = index(row, 0);
+        Q_EMIT dataChanged(idx, idx);
+    }
+    endResetModel();
+}
+
 void
 CallControlListModel::addItem(const CallControl::Item& item)
 {
@@ -264,15 +286,15 @@ CallOverlayModel::CallOverlayModel(LRCInstance* instance, QObject* parent)
 }
 
 void
-CallOverlayModel::addPrimaryControl(const QVariant& action)
+CallOverlayModel::addPrimaryControl(const QVariant& action, bool enabled)
 {
-    primaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
+    primaryModel_->addItem(CallControl::Item {action.value<QObject*>(), enabled});
 }
 
 void
-CallOverlayModel::addSecondaryControl(const QVariant& action)
+CallOverlayModel::addSecondaryControl(const QVariant& action, bool enabled)
 {
-    secondaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
+    secondaryModel_->addItem(CallControl::Item {action.value<QObject*>(), enabled});
     setControlRanges();
 }
 
@@ -282,6 +304,13 @@ CallOverlayModel::setUrgentCount(QVariant row, int count)
     secondaryModel_->setUrgentCount(row, count);
 }
 
+void
+CallOverlayModel::setEnabled(QObject* obj, bool enabled)
+{
+    primaryModel_->setEnabled(obj, enabled);
+    secondaryModel_->setEnabled(obj, enabled);
+}
+
 QVariant
 CallOverlayModel::primaryModel()
 {
@@ -363,7 +392,8 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
 void
 CallOverlayModel::setControlRanges()
 {
+    auto count = secondaryModel_->rowCount();
     overflowModel_->setRange(0, overflowIndex_);
-    overflowVisibleModel_->setRange(overflowIndex_, secondaryModel_->rowCount());
-    overflowHiddenModel_->setRange(overflowIndex_ + 1, secondaryModel_->rowCount());
+    overflowVisibleModel_->setRange(overflowIndex_, count);
+    overflowHiddenModel_->setRange(overflowIndex_ + 1, count);
 }
diff --git a/src/app/calloverlaymodel.h b/src/app/calloverlaymodel.h
index 3193a3a6d..2cbb7d074 100644
--- a/src/app/calloverlaymodel.h
+++ b/src/app/calloverlaymodel.h
@@ -36,12 +36,13 @@
 
 namespace CallControl {
 Q_NAMESPACE
-enum Role { ItemAction = Qt::UserRole + 1, UrgentCount };
+enum Role { ItemAction = Qt::UserRole + 1, UrgentCount, Enabled};
 Q_ENUM_NS(Role)
 
 struct Item
 {
     QObject* itemAction;
+    bool enabled {true};
     int urgentCount {0};
 };
 } // namespace CallControl
@@ -106,6 +107,7 @@ public:
     QHash<int, QByteArray> roleNames() const override;
 
     void setUrgentCount(QVariant item, int count);
+    void setEnabled(QObject* obj, bool enabled);
     void addItem(const CallControl::Item& item);
     void clearData();
 
@@ -121,9 +123,10 @@ class CallOverlayModel : public QObject
 public:
     CallOverlayModel(LRCInstance* instance, QObject* parent = nullptr);
 
-    Q_INVOKABLE void addPrimaryControl(const QVariant& action);
-    Q_INVOKABLE void addSecondaryControl(const QVariant& action);
+    Q_INVOKABLE void addPrimaryControl(const QVariant& action, bool enabled);
+    Q_INVOKABLE void addSecondaryControl(const QVariant& action, bool enabled);
     Q_INVOKABLE void setUrgentCount(QVariant item, int count);
+    Q_INVOKABLE void setEnabled(QObject* obj, bool enabled);
     Q_INVOKABLE void clearControls();
 
     Q_INVOKABLE QVariant primaryModel();
diff --git a/src/app/currentcall.cpp b/src/app/currentcall.cpp
index 8406633e7..ce2bc874c 100644
--- a/src/app/currentcall.cpp
+++ b/src/app/currentcall.cpp
@@ -43,6 +43,13 @@ CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent)
             this,
             &CurrentCall::onShowIncomingCallView);
 
+    try {
+        auto& accInfo = lrcInstance_->getCurrentAccountInfo();
+        set_isSIP(accInfo.profileInfo.type == profile::Type::SIP);
+    } catch (const std::exception& e) {
+        qWarning() << "Can't update current call type" << e.what();
+    }
+
     connectModel();
 }
 
diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml
index efdaff3f6..155f6c021 100644
--- a/src/app/mainview/components/CallActionBar.qml
+++ b/src/app/mainview/components/CallActionBar.qml
@@ -18,6 +18,7 @@
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Layouts
+import SortFilterProxyModel 0.2
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
@@ -299,6 +300,8 @@ Control {
             text: !checked ? JamiStrings.muteCamera : JamiStrings.unmuteCamera
             checked: !CurrentCall.isCapturing
             property var menuAction: videoInputMenuAction
+            enabled: CurrentAccount.videoEnabled_Video
+            onEnabledChanged: CallOverlayModel.setEnabled(this, muteVideoAction.enabled)
         }
     ]
 
@@ -319,6 +322,8 @@ Control {
             icon.source: JamiResources.add_people_black_24dp_svg
             icon.color: "white"
             text: JamiStrings.addParticipants
+            enabled: CurrentCall.isModerator && !CurrentCall.isSIP
+            onEnabledChanged: CallOverlayModel.setEnabled(this, addPersonAction.enabled)
         },
         Action {
             id: chatAction
@@ -333,6 +338,8 @@ Control {
             icon.source: CurrentCall.isPaused ? JamiResources.play_circle_outline_24dp_svg : JamiResources.pause_circle_outline_24dp_svg
             icon.color: "white"
             text: CurrentCall.isPaused ? JamiStrings.resumeCall : JamiStrings.pauseCall
+            enabled: CurrentCall.isSIP
+            onEnabledChanged: CallOverlayModel.setEnabled(this, resumePauseCallAction.enabled)
         },
         Action {
             id: inputPanelSIPAction
@@ -340,6 +347,8 @@ Control {
             icon.source: JamiResources.ic_keypad_svg
             icon.color: "white"
             text: JamiStrings.sipInputPanel
+            enabled: CurrentCall.isSIP
+            onEnabledChanged: CallOverlayModel.setEnabled(this, inputPanelSIPAction.enabled)
         },
         Action {
             id: callTransferAction
@@ -347,6 +356,8 @@ Control {
             icon.source: JamiResources.phone_forwarded_24dp_svg
             icon.color: "white"
             text: JamiStrings.transferCall
+            enabled: CurrentCall.isSIP
+            onEnabledChanged: CallOverlayModel.setEnabled(this, callTransferAction.enabled)
         },
         Action {
             id: shareAction
@@ -361,6 +372,8 @@ Control {
             text: CurrentCall.isSharing ? JamiStrings.stopSharing : JamiStrings.shareScreen
             property real size: 34
             property var menuAction: shareMenuAction
+            enabled: CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP
+            onEnabledChanged: CallOverlayModel.setEnabled(this, shareAction.enabled)
         },
         Action {
             id: raiseHandAction
@@ -371,6 +384,8 @@ Control {
             text: checked ? JamiStrings.lowerHand : JamiStrings.raiseHand
             checked: CurrentCall.isHandRaised
             property real size: 34
+            enabled: CurrentCall.isConference
+            onEnabledChanged: CallOverlayModel.setEnabled(this, raiseHandAction.enabled)
         },
         Action {
             id: layoutAction
@@ -406,6 +421,7 @@ Control {
             icon.color: "white"
             text: JamiStrings.viewPlugin
             enabled: PluginAdapter.isEnabled && PluginAdapter.callMediaHandlersListCount
+            onEnabledChanged: CallOverlayModel.setEnabled(this, pluginsAction.enabled)
         },
         Action {
             id: swarmDetailsAction
@@ -414,7 +430,7 @@ Control {
             icon.color: "white"
             text: JamiStrings.details
             enabled: {
-                if (LRCInstance.currentAccountType === Profile.Type.SIP)
+                if (CurrentCall.isSIP)
                     return true;
                 if (!CurrentConversation.isTemporary && !CurrentConversation.isSwarm)
                     return false;
@@ -422,81 +438,35 @@ Control {
                     return false;
                 return true;
             }
+            onEnabledChanged: CallOverlayModel.setEnabled(this, swarmDetailsAction.enabled)
         }
     ]
 
     property var overflowItemCount
 
-    Connections {
-        target: CurrentCall
-
-        function onIsActiveChanged() {
-            if (CurrentCall.isActive)
-                reset();
-        }
-        function onIsRecordingLocallyChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsHandRaisedChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsConferenceChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsModeratorChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsSIPChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsAudioOnlyChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsAudioMutedChanged() {
-            Qt.callLater(reset);
-        }
-        function onIsVideoMutedChanged() {
-            Qt.callLater(reset);
-        }
-    }
-
-    Connections {
-        target: CurrentAccount
-        function onVideoEnabledVideoChanged() {
-            reset();
-        }
-    }
-
-    function reset() {
+    Component.onCompleted: {
         CallOverlayModel.clearControls();
 
         // centered controls
-        CallOverlayModel.addPrimaryControl(muteAudioAction);
-        CallOverlayModel.addPrimaryControl(hangupAction);
-        if (CurrentAccount.videoEnabled_Video)
-            CallOverlayModel.addPrimaryControl(muteVideoAction);
+        CallOverlayModel.addPrimaryControl(muteAudioAction, muteAudioAction.enabled);
+        CallOverlayModel.addPrimaryControl(hangupAction, hangupAction.enabled);
+        CallOverlayModel.addPrimaryControl(muteVideoAction, muteVideoAction.enabled);
 
         // overflow controls
-        CallOverlayModel.addSecondaryControl(audioOutputAction);
-        if (CurrentCall.isConference) {
-            CallOverlayModel.addSecondaryControl(raiseHandAction);
-        }
-        if (CurrentCall.isModerator && !CurrentCall.isSIP)
-            CallOverlayModel.addSecondaryControl(addPersonAction);
-        if (CurrentCall.isSIP) {
-            CallOverlayModel.addSecondaryControl(resumePauseCallAction);
-            CallOverlayModel.addSecondaryControl(inputPanelSIPAction);
-            CallOverlayModel.addSecondaryControl(callTransferAction);
-        }
-        CallOverlayModel.addSecondaryControl(chatAction);
-        if (CurrentAccount.videoEnabled_Video && !CurrentCall.isSIP)
-            CallOverlayModel.addSecondaryControl(shareAction);
-        CallOverlayModel.addSecondaryControl(layoutAction);
-        CallOverlayModel.addSecondaryControl(recordAction);
-        if (pluginsAction.enabled)
-            CallOverlayModel.addSecondaryControl(pluginsAction);
-        if (swarmDetailsAction.enabled)
-            CallOverlayModel.addSecondaryControl(swarmDetailsAction);
+        CallOverlayModel.addSecondaryControl(audioOutputAction, audioOutputAction.enabled);
+        CallOverlayModel.addSecondaryControl(raiseHandAction, raiseHandAction.enabled);
+        CallOverlayModel.addSecondaryControl(addPersonAction, addPersonAction.enabled);
+
+        CallOverlayModel.addSecondaryControl(resumePauseCallAction, resumePauseCallAction.enabled);
+        CallOverlayModel.addSecondaryControl(inputPanelSIPAction, inputPanelSIPAction.enabled);
+        CallOverlayModel.addSecondaryControl(callTransferAction, callTransferAction.enabled);
+
+        CallOverlayModel.addSecondaryControl(chatAction, chatAction.enabled);
+        CallOverlayModel.addSecondaryControl(shareAction, shareAction.enabled);
+        CallOverlayModel.addSecondaryControl(layoutAction, layoutAction.enabled);
+        CallOverlayModel.addSecondaryControl(recordAction, recordAction.enabled);
+        CallOverlayModel.addSecondaryControl(pluginsAction, pluginsAction.enabled);
+        CallOverlayModel.addSecondaryControl(swarmDetailsAction, swarmDetailsAction.enabled);
         overflowItemCount = CallOverlayModel.secondaryModel().rowCount();
     }
 
@@ -519,7 +489,13 @@ Control {
                 implicitHeight: contentHeight
                 interactive: false
 
-                model: CallOverlayModel.primaryModel()
+                model: SortFilterProxyModel {
+                    sourceModel: CallOverlayModel.primaryModel()
+                    filters: ValueFilter {
+                        roleName: "Enabled"
+                        value: true
+                    }
+                }
                 delegate: buttonDelegate
             }
         }
@@ -547,7 +523,15 @@ Control {
 
                 property int overflowIndex: {
                     var maxItems = Math.floor((overflowRect.remainingSpace) / (root.height + itemSpacing)) - 2;
-                    return Math.min(overflowItemCount, maxItems);
+                    var idx = Math.min(overflowItemCount, maxItems);
+                    idx = Math.max(0, idx);
+                    if (CallOverlayModel.overflowModel().rowCount() > 0 || CallOverlayModel.overflowHiddenModel().rowCount() > 0) {
+                        var visibleIdx = CallOverlayModel.overflowModel().mapToSource(CallOverlayModel.overflowModel().index(idx, 0)).row;
+                        var hiddenIdx = CallOverlayModel.overflowHiddenModel().mapToSource(CallOverlayModel.overflowHiddenModel().index(idx - CallOverlayModel.overflowModel().rowCount(), 0)).row;
+                        if (visibleIdx >= 0 || hiddenIdx >= 0)
+                            idx = Math.max(visibleIdx, hiddenIdx);
+                    }
+                    return idx;
                 }
                 property int nOverflowItems: overflowItemCount - overflowIndex
                 onNOverflowItemsChanged: {
-- 
GitLab