fix #LINQT-1573 delete all history for one address

+ spinner call history
This commit is contained in:
Gaelle Braud 2025-02-05 15:46:09 +01:00
parent 19db70ca75
commit 38a718ca0a
10 changed files with 387 additions and 307 deletions

View file

@ -22,6 +22,7 @@
#include "CallHistoryGui.hpp" #include "CallHistoryGui.hpp"
#include "core/App.hpp" #include "core/App.hpp"
#include "model/object/VariantObject.hpp" #include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include <QSharedPointer> #include <QSharedPointer>
#include <linphone++/linphone.hh> #include <linphone++/linphone.hh>
@ -97,6 +98,28 @@ void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> me) {
delete[] callLogs; 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(); emit lUpdate();
} }
@ -112,6 +135,17 @@ void CallHistoryList::removeAllEntries() {
} }
mList.clear(); mList.clear();
endResetModel(); endResetModel();
emit lRemoveAllEntries();
}
void CallHistoryList::removeEntriesWithFilter(QString filter) {
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto callHistory = it->objectCast<CallHistoryCore>();
if (callHistory->mDisplayName.contains(filter) || callHistory->mRemoteAddress.contains(filter)) {
callHistory->remove();
}
}
emit lRemoveEntriesForAddress(filter);
} }
void CallHistoryList::remove(const int &row) { void CallHistoryList::remove(const int &row) {

View file

@ -44,6 +44,7 @@ public:
void toConnect(CallHistoryCore *data); void toConnect(CallHistoryCore *data);
void removeAllEntries(); void removeAllEntries();
void removeEntriesWithFilter(QString filter);
void remove(const int &row); void remove(const int &row);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -59,6 +60,8 @@ public:
signals: signals:
void lUpdate(); void lUpdate();
void lRemoveEntriesForAddress(QString address);
void lRemoveAllEntries();
private: private:
// Check the state from CallHistoryCore: sender() must be a CallHistoryCore. // Check the state from CallHistoryCore: sender() must be a CallHistoryCore.

View file

@ -38,15 +38,8 @@ void CallHistoryProxy::removeAllEntries() {
mHistoryList->removeAllEntries(); mHistoryList->removeAllEntries();
} }
void CallHistoryProxy::removeEntriesWithFilter() { void CallHistoryProxy::removeEntriesWithFilter(QString filter) {
QList<QSharedPointer<CallHistoryCore>> itemList(rowCount()); mHistoryList->removeEntriesWithFilter(filter);
for (auto i = rowCount() - 1; i >= 0; --i) {
auto item = getItemAt<SortFilterList, CallHistoryList, CallHistoryCore>(i);
itemList[i] = item;
}
for (auto item : itemList) {
item->remove();
}
} }
void CallHistoryProxy::reload() { void CallHistoryProxy::reload() {

View file

@ -38,7 +38,7 @@ public:
~CallHistoryProxy(); ~CallHistoryProxy();
Q_INVOKABLE void removeAllEntries(); Q_INVOKABLE void removeAllEntries();
Q_INVOKABLE void removeEntriesWithFilter(); Q_INVOKABLE void removeEntriesWithFilter(QString filter);
Q_INVOKABLE void reload(); Q_INVOKABLE void reload();
protected: protected:

View file

@ -45,6 +45,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Display/Text.qml view/Control/Display/Text.qml
view/Control/Display/ToolTip.qml view/Control/Display/ToolTip.qml
view/Control/Display/Call/CallListView.qml view/Control/Display/Call/CallListView.qml
view/Control/Display/Call/CallHistoryListView.qml
view/Control/Display/Call/CallStatistics.qml view/Control/Display/Call/CallStatistics.qml
view/Control/Display/Contact/Avatar.qml view/Control/Display/Contact/Avatar.qml
view/Control/Display/Contact/Contact.qml view/Control/Display/Contact/Contact.qml

View file

@ -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
}

View file

@ -32,7 +32,7 @@ LoginLayout {
} }
EffectImage { EffectImage {
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: AppIcons.profile imageSource: AppIcons.profile
Layout.preferredHeight: 34 * DefaultStyle.dp Layout.preferredHeight: 34 * DefaultStyle.dp
Layout.preferredWidth: 34 * DefaultStyle.dp Layout.preferredWidth: 34 * DefaultStyle.dp
colorizationColor: DefaultStyle.main2_600 colorizationColor: DefaultStyle.main2_600

View file

@ -233,26 +233,26 @@ Item {
} }
} }
contentItem: AllContactListView { contentItem: AllContactListView {
id: contactList id: contactList
visible: !loading && magicSearchBar.text.length != 0 visible: !loading && magicSearchBar.text.length != 0
Layout.preferredHeight: visible ? contentHeight : 0 Layout.preferredHeight: visible ? contentHeight : 0
Layout.fillWidth: true Layout.fillWidth: true
itemsRightMargin: 5 * DefaultStyle.dp //(Actions have already 10 of margin) itemsRightMargin: 5 * DefaultStyle.dp //(Actions have already 10 of margin)
showInitials: false showInitials: false
showContactMenu: false showContactMenu: false
showActions: true showActions: true
showFavorites: false showFavorites: false
selectionEnabled: false selectionEnabled: false
showDefaultAddress: true showDefaultAddress: true
searchOnEmpty: false searchOnEmpty: false
sectionsPixelSize: 13 * DefaultStyle.dp sectionsPixelSize: 13 * DefaultStyle.dp
sectionsWeight: 700 * DefaultStyle.dp sectionsWeight: 700 * DefaultStyle.dp
sectionsSpacing: 5 * DefaultStyle.dp sectionsSpacing: 5 * DefaultStyle.dp
Control.ScrollBar.vertical: scrollbar Control.ScrollBar.vertical: scrollbar
searchBarText: magicSearchBar.text searchBarText: magicSearchBar.text
} }
} }
} }
RowLayout { RowLayout {

View file

@ -156,6 +156,7 @@ AbstractSettingsLayout {
Component { Component {
id: videoCodecsComponent id: videoCodecsComponent
ColumnLayout { ColumnLayout {
spacing: 20 * DefaultStyle.dp
ListView { ListView {
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -214,213 +214,32 @@ AbstractMainPage {
weight: 800 * DefaultStyle.dp 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 id: historyListView
clip: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: CallHistoryProxy { searchBar: searchBar
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()
}
}
Connections{ Connections{
target: mainItem target: mainItem
function onListViewUpdated(){ 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: { onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt(currentIndex) mainItem.selectedRowHistoryGui = model.getAt(currentIndex)
} }
onVisibleChanged: { onCountChanged: {
if (!visible) currentIndex = -1 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 { Connections {
target: deleteForUserPopup target: deleteForUserPopup
function onAccepted() { function onAccepted() {
detailListView.model.removeEntriesWithFilter() detailListView.model.removeEntriesWithFilter(detailListView.searchText)
mainItem.listViewUpdated() mainItem.listViewUpdated()
} }
} }
@ -726,107 +545,109 @@ AbstractMainPage {
detailContent: RoundedPane { detailContent: RoundedPane {
id: detailControl id: detailControl
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
implicitHeight: 430 * DefaultStyle.dp + topPadding + bottomPadding
background: Rectangle { background: Rectangle {
id: detailListBackground id: detailListBackground
width: parent.width anchors.fill: parent
height: detailListView.height
color: DefaultStyle.grey_0 color: DefaultStyle.grey_0
radius: 15 * DefaultStyle.dp radius: 15 * DefaultStyle.dp
} }
ListView { contentItem: StackLayout {
id: detailListView currentIndex: detailListView.loading ? 0 : 1
width: parent.width height: Math.min(430 * DefaultStyle.dp, detailListView.contentHeight)
height: Math.min(detailControl.implicitHeight, contentHeight) BusyIndicator {
Layout.alignment: Qt.AlignCenter
spacing: 20 * DefaultStyle.dp Layout.preferredHeight: visible ? 60 * DefaultStyle.dp : 0
clip: true Layout.preferredWidth: 60 * DefaultStyle.dp
indicatorHeight: 60 * DefaultStyle.dp
model: CallHistoryProxy { indicatorWidth: 60 * DefaultStyle.dp
id: detailsHistoryProxy indicatorColor: DefaultStyle.main1_500_main
filterText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
onFilterTextChanged: maxDisplayItems = initialDisplayItems
initialDisplayItems: Math.max(20, 2 * detailListView.height / (56 * DefaultStyle.dp))
displayItemsStep: 3 * initialDisplayItems / 2
} }
onAtYEndChanged: if(atYEnd) detailsHistoryProxy.displayMore() CallHistoryListView {
delegate: Item { id: detailListView
width:detailListView.width Layout.fillWidth: true
height: 56 * DefaultStyle.dp Layout.preferredHeight: Math.min(detailControl.implicitHeight, contentHeight)
RowLayout { spacing: 14 * DefaultStyle.dp
anchors.fill: parent clip: true
anchors.leftMargin: 20 * DefaultStyle.dp searchText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
anchors.rightMargin: 20 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter delegate: Item {
ColumnLayout { width:detailListView.width
Layout.alignment: Qt.AlignVCenter height: 56 * DefaultStyle.dp
RowLayout { RowLayout {
EffectImage { anchors.fill: parent
id: statusIcon anchors.leftMargin: 20 * DefaultStyle.dp
imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined anchors.rightMargin: 20 * DefaultStyle.dp
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere anchors.verticalCenter: parent.verticalCenter
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted ColumnLayout {
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted Layout.alignment: Qt.AlignVCenter
? AppIcons.arrowElbow RowLayout {
: modelData.core.isOutgoing EffectImage {
? AppIcons.arrowUpRight id: statusIcon
: AppIcons.arrowDownLeft imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined
colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere || modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted ? AppIcons.arrowElbow
|| modelData.core.status === LinphoneEnums.CallStatus.Missed : modelData.core.isOutgoing
? DefaultStyle.danger_500main ? AppIcons.arrowUpRight
: modelData.core.isOutgoing : AppIcons.arrowDownLeft
? DefaultStyle.info_500_main colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined
: DefaultStyle.success_500main || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere
Layout.preferredWidth: 16 * DefaultStyle.dp || modelData.core.status === LinphoneEnums.CallStatus.Aborted
Layout.preferredHeight: 16 * DefaultStyle.dp || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted
transform: Rotation { || modelData.core.status === LinphoneEnums.CallStatus.Missed
angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined ? DefaultStyle.danger_500main
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere : modelData.core.isOutgoing
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted ? DefaultStyle.info_500_main
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 : DefaultStyle.success_500main
origin { Layout.preferredWidth: 16 * DefaultStyle.dp
x: statusIcon.width/2 Layout.preferredHeight: 16 * DefaultStyle.dp
y: statusIcon.height/2 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 {
text: modelData.core.status === LinphoneEnums.CallStatus.Missed text: UtilsCpp.formatDate(modelData.core.date)
? qsTr("Appel manqué") color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main
: modelData.core.isOutgoing
? qsTr("Appel sortant")
: qsTr("Appel entrant")
font { font {
pixelSize: 14 * DefaultStyle.dp pixelSize: 12 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp weight: 300 * DefaultStyle.dp
} }
} }
} }
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Text { Text {
text: UtilsCpp.formatDate(modelData.core.date) text: UtilsCpp.formatElapsedTime(modelData.core.duration, false)
color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main
font { font {
pixelSize: 12 * DefaultStyle.dp pixelSize: 12 * DefaultStyle.dp
weight: 300 * 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
}
}
} }
} }
} }