From 38a718ca0aa987d7b3eb1d05e4749503f94f7d43 Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Wed, 5 Feb 2025 15:46:09 +0100 Subject: [PATCH] fix #LINQT-1573 delete all history for one address + spinner call history --- .../core/call-history/CallHistoryList.cpp | 34 ++ .../core/call-history/CallHistoryList.hpp | 3 + .../core/call-history/CallHistoryProxy.cpp | 11 +- .../core/call-history/CallHistoryProxy.hpp | 2 +- Linphone/view/CMakeLists.txt | 1 + .../Display/Call/CallHistoryListView.qml | 227 +++++++++++ .../view/Page/Form/Login/SIPLoginPage.qml | 2 +- Linphone/view/Page/Layout/Main/MainLayout.qml | 40 +- .../Settings/AdvancedSettingsLayout.qml | 1 + Linphone/view/Page/Main/Call/CallPage.qml | 373 +++++------------- 10 files changed, 387 insertions(+), 307 deletions(-) create mode 100644 Linphone/view/Control/Display/Call/CallHistoryListView.qml diff --git a/Linphone/core/call-history/CallHistoryList.cpp b/Linphone/core/call-history/CallHistoryList.cpp index ff9a3356..e3fe4a3d 100644 --- a/Linphone/core/call-history/CallHistoryList.cpp +++ b/Linphone/core/call-history/CallHistoryList.cpp @@ -22,6 +22,7 @@ #include "CallHistoryGui.hpp" #include "core/App.hpp" #include "model/object/VariantObject.hpp" +#include "model/tool/ToolModel.hpp" #include #include @@ -97,6 +98,28 @@ void CallHistoryList::setSelf(QSharedPointer me) { delete[] callLogs; }); }); + mModelConnection->makeConnectToCore(&CallHistoryList::lRemoveEntriesForAddress, [this](QString address) { + mModelConnection->invokeToModel([this, address]() { + if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) { + auto linAddress = ToolModel::interpretUrl(address); + if (linAddress) { + auto core = CoreModel::getInstance()->getCore(); + auto accountAddress = account->getParams() ? account->getParams()->getIdentityAddress() : nullptr; + if (accountAddress) + for (auto &callLog : core->getCallHistory(linAddress, accountAddress)) { + core->removeCallLog(callLog); + } + } + } + }); + }); + mModelConnection->makeConnectToCore(&CallHistoryList::lRemoveAllEntries, [this]() { + mModelConnection->invokeToModel([this]() { + if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) { + account->clearCallLogs(); + } + }); + }); emit lUpdate(); } @@ -112,6 +135,17 @@ void CallHistoryList::removeAllEntries() { } mList.clear(); endResetModel(); + emit lRemoveAllEntries(); +} + +void CallHistoryList::removeEntriesWithFilter(QString filter) { + for (auto it = mList.rbegin(); it != mList.rend(); ++it) { + auto callHistory = it->objectCast(); + if (callHistory->mDisplayName.contains(filter) || callHistory->mRemoteAddress.contains(filter)) { + callHistory->remove(); + } + } + emit lRemoveEntriesForAddress(filter); } void CallHistoryList::remove(const int &row) { diff --git a/Linphone/core/call-history/CallHistoryList.hpp b/Linphone/core/call-history/CallHistoryList.hpp index bdef270a..e650af76 100644 --- a/Linphone/core/call-history/CallHistoryList.hpp +++ b/Linphone/core/call-history/CallHistoryList.hpp @@ -44,6 +44,7 @@ public: void toConnect(CallHistoryCore *data); void removeAllEntries(); + void removeEntriesWithFilter(QString filter); void remove(const int &row); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -59,6 +60,8 @@ public: signals: void lUpdate(); + void lRemoveEntriesForAddress(QString address); + void lRemoveAllEntries(); private: // Check the state from CallHistoryCore: sender() must be a CallHistoryCore. diff --git a/Linphone/core/call-history/CallHistoryProxy.cpp b/Linphone/core/call-history/CallHistoryProxy.cpp index bdaae52d..657d1eb4 100644 --- a/Linphone/core/call-history/CallHistoryProxy.cpp +++ b/Linphone/core/call-history/CallHistoryProxy.cpp @@ -38,15 +38,8 @@ void CallHistoryProxy::removeAllEntries() { mHistoryList->removeAllEntries(); } -void CallHistoryProxy::removeEntriesWithFilter() { - QList> itemList(rowCount()); - for (auto i = rowCount() - 1; i >= 0; --i) { - auto item = getItemAt(i); - itemList[i] = item; - } - for (auto item : itemList) { - item->remove(); - } +void CallHistoryProxy::removeEntriesWithFilter(QString filter) { + mHistoryList->removeEntriesWithFilter(filter); } void CallHistoryProxy::reload() { diff --git a/Linphone/core/call-history/CallHistoryProxy.hpp b/Linphone/core/call-history/CallHistoryProxy.hpp index b209286f..309a6c45 100644 --- a/Linphone/core/call-history/CallHistoryProxy.hpp +++ b/Linphone/core/call-history/CallHistoryProxy.hpp @@ -38,7 +38,7 @@ public: ~CallHistoryProxy(); Q_INVOKABLE void removeAllEntries(); - Q_INVOKABLE void removeEntriesWithFilter(); + Q_INVOKABLE void removeEntriesWithFilter(QString filter); Q_INVOKABLE void reload(); protected: diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 0371f552..d3f271ab 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -45,6 +45,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Display/Text.qml view/Control/Display/ToolTip.qml view/Control/Display/Call/CallListView.qml + view/Control/Display/Call/CallHistoryListView.qml view/Control/Display/Call/CallStatistics.qml view/Control/Display/Contact/Avatar.qml view/Control/Display/Contact/Contact.qml diff --git a/Linphone/view/Control/Display/Call/CallHistoryListView.qml b/Linphone/view/Control/Display/Call/CallHistoryListView.qml new file mode 100644 index 00000000..d8090388 --- /dev/null +++ b/Linphone/view/Control/Display/Call/CallHistoryListView.qml @@ -0,0 +1,227 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control +import Linphone +import UtilsCpp +import SettingsCpp +import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle +import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils + +ListView { + id: mainItem + clip: true + + property SearchBar searchBar + property bool loading: false + property string searchText: searchBar?.text + + signal resultsReceived() + + onResultsReceived: { + loading = false + // contentY = 0 + } + onSearchTextChanged: loading = true + + model: CallHistoryProxy { + id: callHistoryProxy + filterText: mainItem.searchText + onFilterTextChanged: maxDisplayItems = initialDisplayItems + initialDisplayItems: Math.max(20, 2 * mainItem.height / (56 * DefaultStyle.dp)) + displayItemsStep: 3 * initialDisplayItems / 2 + onModelReset: { + mainItem.resultsReceived() + } + } + flickDeceleration: 10000 + spacing: 10 * DefaultStyle.dp + + Keys.onPressed: (event) => { + if(event.key == Qt.Key_Escape){ + console.log("Back") + searchBar.forceActiveFocus() + event.accepted = true + } + } + + Component.onCompleted: cacheBuffer = Math.max(contentHeight,0)//contentHeight>0 ? contentHeight : 0// cache all items + // remove binding loop + onContentHeightChanged: Qt.callLater(function(){ + if (mainItem) mainItem.cacheBuffer = Math?.max(contentHeight,0) || 0 + }) + + onActiveFocusChanged: if(activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 + onCountChanged: { + if(currentIndex < 0 && count > 0){ + mainItem.currentIndex = 0 // Select first item after loading model + } + if(atYBeginning) + positionViewAtBeginning()// Stay at beginning + } + Connections { + target: deleteHistoryPopup + function onAccepted() { + mainItem.model.removeAllEntries() + } + } + + onAtYEndChanged: { + if(atYEnd && count > 0){ + callHistoryProxy.displayMore() + } + } + //---------------------------------------------------------------- + function moveToCurrentItem(){ + if( mainItem.currentIndex >= 0) + Utils.updatePosition(mainItem, mainItem) + } + onCurrentItemChanged: { + moveToCurrentItem() + } + // Update position only if we are moving to current item and its position is changing. + property var _currentItemY: currentItem?.y + on_CurrentItemYChanged: if(_currentItemY && moveAnimation.running){ + moveToCurrentItem() + } + Behavior on contentY{ + NumberAnimation { + id: moveAnimation + duration: 500 + easing.type: Easing.OutExpo + alwaysRunToEnd: true + } + } + //---------------------------------------------------------------- + + + onVisibleChanged: { + if (!visible) currentIndex = -1 + } + // Qt bug: sometimes, containsMouse may not be send and update on each MouseArea. + // So we need to use this variable to switch off all hovered items. + property int lastMouseContainsIndex: -1 + delegate: FocusScope { + width:mainItem.width + height: 56 * DefaultStyle.dp + visible: !!modelData + + RowLayout { + z: 1 + anchors.fill: parent + anchors.leftMargin: 10 * DefaultStyle.dp + spacing: 10 * DefaultStyle.dp + Avatar { + id: historyAvatar + property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.remoteAddress) + contact: contactObj?.value || null + _address: modelData.core.conferenceInfo + ? modelData.core.conferenceInfo.core.subject + : modelData.core.remoteAddress + secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified + width: 45 * DefaultStyle.dp + height: 45 * DefaultStyle.dp + isConference: modelData.core.isConference + } + ColumnLayout { + Layout.fillHeight: true + Layout.fillWidth: true + spacing: 5 * DefaultStyle.dp + Text { + id: friendAddress + Layout.fillWidth: true + maximumLineCount: 1 + text: historyAvatar.displayNameVal + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + capitalization: Font.Capitalize + } + } + RowLayout { + spacing: 6 * DefaultStyle.dp + EffectImage { + id: statusIcon + imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + ? AppIcons.arrowElbow + : modelData.core.isOutgoing + ? AppIcons.arrowUpRight + : AppIcons.arrowDownLeft + colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + || modelData.core.status === LinphoneEnums.CallStatus.Missed + ? DefaultStyle.danger_500main + : modelData.core.isOutgoing + ? DefaultStyle.info_500_main + : DefaultStyle.success_500main + Layout.preferredWidth: 12 * DefaultStyle.dp + Layout.preferredHeight: 12 * DefaultStyle.dp + transform: Rotation { + angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 + origin { + x: statusIcon.width/2 + y: statusIcon.height/2 + } + } + } + Text { + // text: modelData.core.date + text: UtilsCpp.formatDate(modelData.core.date) + font { + pixelSize: 12 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp + } + } + } + } + BigButton { + style: ButtonStyle.noBackground + icon.source: AppIcons.phone + focus: true + activeFocusOnTab: false + onClicked: { + if (modelData.core.isConference) { + var callsWindow = UtilsCpp.getCallsWindow() + callsWindow.setupConference(modelData.core.conferenceInfo) + UtilsCpp.smartShowWindow(callsWindow) + } + else { + UtilsCpp.createCall(modelData.core.remoteAddress) + } + } + } + } + MouseArea { + hoverEnabled: true + anchors.fill: parent + focus: true + onContainsMouseChanged: { + if(containsMouse) + mainItem.lastMouseContainsIndex = index + else if( mainItem.lastMouseContainsIndex == index) + mainItem.lastMouseContainsIndex = -1 + } + Rectangle { + anchors.fill: parent + opacity: 0.7 + radius: 8 * DefaultStyle.dp + color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 + visible: mainItem.lastMouseContainsIndex === index || mainItem.currentIndex === index + } + onPressed: { + mainItem.currentIndex = model.index + mainItem.forceActiveFocus() + } + } + } + + Control.ScrollBar.vertical: scrollbar +} diff --git a/Linphone/view/Page/Form/Login/SIPLoginPage.qml b/Linphone/view/Page/Form/Login/SIPLoginPage.qml index e2752504..068fce59 100644 --- a/Linphone/view/Page/Form/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Form/Login/SIPLoginPage.qml @@ -32,7 +32,7 @@ LoginLayout { } EffectImage { fillMode: Image.PreserveAspectFit - source: AppIcons.profile + imageSource: AppIcons.profile Layout.preferredHeight: 34 * DefaultStyle.dp Layout.preferredWidth: 34 * DefaultStyle.dp colorizationColor: DefaultStyle.main2_600 diff --git a/Linphone/view/Page/Layout/Main/MainLayout.qml b/Linphone/view/Page/Layout/Main/MainLayout.qml index aaf6ddb1..2104b97b 100644 --- a/Linphone/view/Page/Layout/Main/MainLayout.qml +++ b/Linphone/view/Page/Layout/Main/MainLayout.qml @@ -233,26 +233,26 @@ Item { } } contentItem: AllContactListView { - id: contactList - visible: !loading && magicSearchBar.text.length != 0 - Layout.preferredHeight: visible ? contentHeight : 0 - Layout.fillWidth: true - itemsRightMargin: 5 * DefaultStyle.dp //(Actions have already 10 of margin) - showInitials: false - showContactMenu: false - showActions: true - showFavorites: false - selectionEnabled: false - showDefaultAddress: true - searchOnEmpty: false - - sectionsPixelSize: 13 * DefaultStyle.dp - sectionsWeight: 700 * DefaultStyle.dp - sectionsSpacing: 5 * DefaultStyle.dp - - Control.ScrollBar.vertical: scrollbar - searchBarText: magicSearchBar.text - } + id: contactList + visible: !loading && magicSearchBar.text.length != 0 + Layout.preferredHeight: visible ? contentHeight : 0 + Layout.fillWidth: true + itemsRightMargin: 5 * DefaultStyle.dp //(Actions have already 10 of margin) + showInitials: false + showContactMenu: false + showActions: true + showFavorites: false + selectionEnabled: false + showDefaultAddress: true + searchOnEmpty: false + + sectionsPixelSize: 13 * DefaultStyle.dp + sectionsWeight: 700 * DefaultStyle.dp + sectionsSpacing: 5 * DefaultStyle.dp + + Control.ScrollBar.vertical: scrollbar + searchBarText: magicSearchBar.text + } } } RowLayout { diff --git a/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml b/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml index eda7b3ce..60f2d803 100644 --- a/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml +++ b/Linphone/view/Page/Layout/Settings/AdvancedSettingsLayout.qml @@ -156,6 +156,7 @@ AbstractSettingsLayout { Component { id: videoCodecsComponent ColumnLayout { + spacing: 20 * DefaultStyle.dp ListView { Layout.preferredHeight: contentHeight Layout.fillWidth: true diff --git a/Linphone/view/Page/Main/Call/CallPage.qml b/Linphone/view/Page/Main/Call/CallPage.qml index 27734be0..7f638d97 100644 --- a/Linphone/view/Page/Main/Call/CallPage.qml +++ b/Linphone/view/Page/Main/Call/CallPage.qml @@ -214,213 +214,32 @@ AbstractMainPage { weight: 800 * DefaultStyle.dp } } - ListView { + BusyIndicator { + Layout.alignment: Qt.AlignCenter + Layout.preferredHeight: visible ? 60 * DefaultStyle.dp : 0 + Layout.preferredWidth: 60 * DefaultStyle.dp + indicatorHeight: 60 * DefaultStyle.dp + indicatorWidth: 60 * DefaultStyle.dp + visible: historyListView.loading + indicatorColor: DefaultStyle.main1_500_main + } + CallHistoryListView { id: historyListView - clip: true Layout.fillWidth: true - Layout.fillHeight: true - model: CallHistoryProxy { - id: callHistoryProxy - filterText: searchBar.text - onFilterTextChanged: maxDisplayItems = initialDisplayItems - initialDisplayItems: Math.max(20, 2 * historyListView.height / (56 * DefaultStyle.dp)) - displayItemsStep: 3 * initialDisplayItems / 2 - } - cacheBuffer: contentHeight>0 ? contentHeight : 0// cache all items - flickDeceleration: 10000 - spacing: 10 * DefaultStyle.dp - - Keys.onPressed: (event) => { - if(event.key == Qt.Key_Escape){ - console.log("Back") - searchBar.forceActiveFocus() - event.accepted = true - } - } - // remove binding loop - onContentHeightChanged: Qt.callLater(function(){ - historyListView.cacheBuffer = Math.max(contentHeight,0) - }) - onActiveFocusChanged: if(activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 - onCountChanged: { - if(currentIndex < 0 && count > 0){ - historyListView.currentIndex = 0 // Select first item after loading model - } - if(atYBeginning) - positionViewAtBeginning()// Stay at beginning - } - Connections { - target: deleteHistoryPopup - function onAccepted() { - historyListView.model.removeAllEntries() - } - } + Layout.fillHeight: true + searchBar: searchBar Connections{ target: mainItem function onListViewUpdated(){ - callHistoryProxy.reload() + historyListView.model.reload() } } - onAtYEndChanged: { - if(atYEnd && count > 0){ - callHistoryProxy.displayMore() - } - } - //---------------------------------------------------------------- - function moveToCurrentItem(){ - if( historyListView.currentIndex >= 0) - Utils.updatePosition(historyListView, historyListView) - } - onCurrentItemChanged: { - moveToCurrentItem() - } - // Update position only if we are moving to current item and its position is changing. - property var _currentItemY: currentItem?.y - on_CurrentItemYChanged: if(_currentItemY && moveAnimation.running){ - moveToCurrentItem() - } - Behavior on contentY{ - NumberAnimation { - id: moveAnimation - duration: 500 - easing.type: Easing.OutExpo - alwaysRunToEnd: true - } - } - //---------------------------------------------------------------- - onCurrentIndexChanged: { mainItem.selectedRowHistoryGui = model.getAt(currentIndex) } - onVisibleChanged: { - if (!visible) currentIndex = -1 + onCountChanged: { + mainItem.selectedRowHistoryGui = model.getAt(currentIndex) } - // Qt bug: sometimes, containsMouse may not be send and update on each MouseArea. - // So we need to use this variable to switch off all hovered items. - property int lastMouseContainsIndex: -1 - delegate: FocusScope { - width:historyListView.width - height: 56 * DefaultStyle.dp - visible: !!modelData - - RowLayout { - z: 1 - anchors.fill: parent - anchors.leftMargin: 10 * DefaultStyle.dp - spacing: 10 * DefaultStyle.dp - Avatar { - id: historyAvatar - property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.remoteAddress) - contact: contactObj?.value || null - _address: modelData.core.conferenceInfo - ? modelData.core.conferenceInfo.core.subject - : modelData.core.remoteAddress - secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified - width: 45 * DefaultStyle.dp - height: 45 * DefaultStyle.dp - isConference: modelData.core.isConference - } - ColumnLayout { - Layout.fillHeight: true - Layout.fillWidth: true - spacing: 5 * DefaultStyle.dp - Text { - id: friendAddress - Layout.fillWidth: true - maximumLineCount: 1 - text: historyAvatar.displayNameVal - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - capitalization: Font.Capitalize - } - } - RowLayout { - spacing: 6 * DefaultStyle.dp - EffectImage { - id: statusIcon - imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - ? AppIcons.arrowElbow - : modelData.core.isOutgoing - ? AppIcons.arrowUpRight - : AppIcons.arrowDownLeft - colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - || modelData.core.status === LinphoneEnums.CallStatus.Missed - ? DefaultStyle.danger_500main - : modelData.core.isOutgoing - ? DefaultStyle.info_500_main - : DefaultStyle.success_500main - Layout.preferredWidth: 12 * DefaultStyle.dp - Layout.preferredHeight: 12 * DefaultStyle.dp - transform: Rotation { - angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 - origin { - x: statusIcon.width/2 - y: statusIcon.height/2 - } - } - } - Text { - // text: modelData.core.date - text: UtilsCpp.formatDate(modelData.core.date) - font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp - } - } - } - } - BigButton { - style: ButtonStyle.noBackground - icon.source: AppIcons.phone - focus: true - activeFocusOnTab: false - onClicked: { - if (modelData.core.isConference) { - var callsWindow = UtilsCpp.getCallsWindow() - callsWindow.setupConference(modelData.core.conferenceInfo) - UtilsCpp.smartShowWindow(callsWindow) - } - else { - UtilsCpp.createCall(modelData.core.remoteAddress) - } - } - } - } - MouseArea { - hoverEnabled: true - anchors.fill: parent - focus: true - onContainsMouseChanged: { - if(containsMouse) - historyListView.lastMouseContainsIndex = index - else if( historyListView.lastMouseContainsIndex == index) - historyListView.lastMouseContainsIndex = -1 - } - Rectangle { - anchors.fill: parent - opacity: 0.7 - radius: 8 * DefaultStyle.dp - color: historyListView.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 - visible: historyListView.lastMouseContainsIndex === index || historyListView.currentIndex === index - } - onPressed: { - historyListView.currentIndex = model.index - historyListView.forceActiveFocus() - } - } - } - - Control.ScrollBar.vertical: scrollbar } } } @@ -711,7 +530,7 @@ AbstractMainPage { Connections { target: deleteForUserPopup function onAccepted() { - detailListView.model.removeEntriesWithFilter() + detailListView.model.removeEntriesWithFilter(detailListView.searchText) mainItem.listViewUpdated() } } @@ -726,107 +545,109 @@ AbstractMainPage { detailContent: RoundedPane { id: detailControl Layout.preferredWidth: 360 * DefaultStyle.dp - implicitHeight: 430 * DefaultStyle.dp + topPadding + bottomPadding background: Rectangle { id: detailListBackground - width: parent.width - height: detailListView.height + anchors.fill: parent color: DefaultStyle.grey_0 radius: 15 * DefaultStyle.dp } - - ListView { - id: detailListView - width: parent.width - height: Math.min(detailControl.implicitHeight, contentHeight) - - spacing: 20 * DefaultStyle.dp - clip: true - - model: CallHistoryProxy { - id: detailsHistoryProxy - filterText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : "" - onFilterTextChanged: maxDisplayItems = initialDisplayItems - initialDisplayItems: Math.max(20, 2 * detailListView.height / (56 * DefaultStyle.dp)) - displayItemsStep: 3 * initialDisplayItems / 2 + + contentItem: StackLayout { + currentIndex: detailListView.loading ? 0 : 1 + height: Math.min(430 * DefaultStyle.dp, detailListView.contentHeight) + BusyIndicator { + Layout.alignment: Qt.AlignCenter + Layout.preferredHeight: visible ? 60 * DefaultStyle.dp : 0 + Layout.preferredWidth: 60 * DefaultStyle.dp + indicatorHeight: 60 * DefaultStyle.dp + indicatorWidth: 60 * DefaultStyle.dp + indicatorColor: DefaultStyle.main1_500_main } - onAtYEndChanged: if(atYEnd) detailsHistoryProxy.displayMore() - delegate: Item { - width:detailListView.width - height: 56 * DefaultStyle.dp - RowLayout { - anchors.fill: parent - anchors.leftMargin: 20 * DefaultStyle.dp - anchors.rightMargin: 20 * DefaultStyle.dp - anchors.verticalCenter: parent.verticalCenter - ColumnLayout { - Layout.alignment: Qt.AlignVCenter - RowLayout { - EffectImage { - id: statusIcon - imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - ? AppIcons.arrowElbow - : modelData.core.isOutgoing - ? AppIcons.arrowUpRight - : AppIcons.arrowDownLeft - colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - || modelData.core.status === LinphoneEnums.CallStatus.Missed - ? DefaultStyle.danger_500main - : modelData.core.isOutgoing - ? DefaultStyle.info_500_main - : DefaultStyle.success_500main - Layout.preferredWidth: 16 * DefaultStyle.dp - Layout.preferredHeight: 16 * DefaultStyle.dp - transform: Rotation { - angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 - origin { - x: statusIcon.width/2 - y: statusIcon.height/2 + CallHistoryListView { + id: detailListView + Layout.fillWidth: true + Layout.preferredHeight: Math.min(detailControl.implicitHeight, contentHeight) + spacing: 14 * DefaultStyle.dp + clip: true + searchText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : "" + + delegate: Item { + width:detailListView.width + height: 56 * DefaultStyle.dp + RowLayout { + anchors.fill: parent + anchors.leftMargin: 20 * DefaultStyle.dp + anchors.rightMargin: 20 * DefaultStyle.dp + anchors.verticalCenter: parent.verticalCenter + ColumnLayout { + Layout.alignment: Qt.AlignVCenter + RowLayout { + EffectImage { + id: statusIcon + imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + ? AppIcons.arrowElbow + : modelData.core.isOutgoing + ? AppIcons.arrowUpRight + : AppIcons.arrowDownLeft + colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + || modelData.core.status === LinphoneEnums.CallStatus.Missed + ? DefaultStyle.danger_500main + : modelData.core.isOutgoing + ? DefaultStyle.info_500_main + : DefaultStyle.success_500main + Layout.preferredWidth: 16 * DefaultStyle.dp + Layout.preferredHeight: 16 * DefaultStyle.dp + transform: Rotation { + angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 + origin { + x: statusIcon.width/2 + y: statusIcon.height/2 + } + } + } + Text { + text: modelData.core.status === LinphoneEnums.CallStatus.Missed + ? qsTr("Appel manqué") + : modelData.core.isOutgoing + ? qsTr("Appel sortant") + : qsTr("Appel entrant") + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp } } } Text { - text: modelData.core.status === LinphoneEnums.CallStatus.Missed - ? qsTr("Appel manqué") - : modelData.core.isOutgoing - ? qsTr("Appel sortant") - : qsTr("Appel entrant") + text: UtilsCpp.formatDate(modelData.core.date) + color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp + pixelSize: 12 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp } } } + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } Text { - text: UtilsCpp.formatDate(modelData.core.date) - color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main + text: UtilsCpp.formatElapsedTime(modelData.core.duration, false) font { pixelSize: 12 * DefaultStyle.dp weight: 300 * DefaultStyle.dp } } } - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - Text { - text: UtilsCpp.formatElapsedTime(modelData.core.duration, false) - font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp - } - } } } }