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 "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
@ -97,6 +98,28 @@ void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> 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<CallHistoryCore>();
if (callHistory->mDisplayName.contains(filter) || callHistory->mRemoteAddress.contains(filter)) {
callHistory->remove();
}
}
emit lRemoveEntriesForAddress(filter);
}
void CallHistoryList::remove(const int &row) {

View file

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

View file

@ -38,15 +38,8 @@ void CallHistoryProxy::removeAllEntries() {
mHistoryList->removeAllEntries();
}
void CallHistoryProxy::removeEntriesWithFilter() {
QList<QSharedPointer<CallHistoryCore>> itemList(rowCount());
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::removeEntriesWithFilter(QString filter) {
mHistoryList->removeEntriesWithFilter(filter);
}
void CallHistoryProxy::reload() {

View file

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

View file

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

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 {
fillMode: Image.PreserveAspectFit
source: AppIcons.profile
imageSource: AppIcons.profile
Layout.preferredHeight: 34 * DefaultStyle.dp
Layout.preferredWidth: 34 * DefaultStyle.dp
colorizationColor: DefaultStyle.main2_600

View file

@ -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
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
sectionsPixelSize: 13 * DefaultStyle.dp
sectionsWeight: 700 * DefaultStyle.dp
sectionsSpacing: 5 * DefaultStyle.dp
Control.ScrollBar.vertical: scrollbar
searchBarText: magicSearchBar.text
}
Control.ScrollBar.vertical: scrollbar
searchBarText: magicSearchBar.text
}
}
}
RowLayout {

View file

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

View file

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