linphone-desktop/Linphone/view/Page/Main/CallPage.qml
Gaelle Braud f9abfb9fbc Record call + fixes (
remove friend listener
fix call history layout (sometimes the details exceeded row bottom)
do not open terminate calls popup when all call ended already
change '.' with ' ' only if display name is the address
initials headers in contact list delegate
confirmation dialog delete history
use intermediate variable
try to fix variant object crash
)

recordable option
fix effect image not colorized on visible change
2024-01-11 11:01:17 +01:00

706 lines
21 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick 2.15
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
import UtilsCpp 1.0
AbstractMainPage {
id: mainItem
noItemButtonText: qsTr("Nouvel appel")
emptyListText: qsTr("Historique d'appel vide")
newItemIconSource: AppIcons.newCall
property var selectedRowHistoryGui
signal listViewUpdated()
onNoItemButtonPressed: goToNewCall()
showDefaultItem: listStackView.currentItem.listView ? listStackView.currentItem.listView.count === 0 : true
function goToNewCall() {
listStackView.push(newCallItem)
}
Dialog {
id: deleteHistoryPopup
width: 278 * DefaultStyle.dp
text: qsTr("L'historique d'appel sera supprimé. Souhaitez-vous continuer ?")
}
Dialog {
id: deleteForUserPopup
width: 278 * DefaultStyle.dp
text: qsTr("L'historique d'appel de l'utilisateur sera supprimé. Souhaitez-vous continuer ?")
}
leftPanelContent: Item {
id: leftPanel
Layout.fillWidth: true
Layout.fillHeight: true
Control.StackView {
id: listStackView
clip: true
initialItem: historyListItem
anchors.fill: parent
property int sideMargin: 25 * DefaultStyle.dp
// anchors.leftMargin: 25
// anchors.rightMargin: 25
}
Component {
id: historyListItem
ColumnLayout {
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
Text {
text: qsTr("Appels")
color: DefaultStyle.main2_700
font.pixelSize: 29 * DefaultStyle.dp
font.weight: 800 * DefaultStyle.dp
}
Item {
Layout.fillWidth: true
}
PopupButton {
id: removeHistory
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
popup.contentItem: Button {
background: Item{}
contentItem: RowLayout {
EffectImage {
source: AppIcons.trashCan
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
Text {
text: qsTr("Supprimer lhistorique")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
Connections {
target: deleteHistoryPopup
onAccepted: historyListView.model.removeAllEntries()
}
onClicked: {
removeHistory.close()
deleteHistoryPopup.open()
}
}
}
Control.Button {
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.newCall
width: 30 * DefaultStyle.dp
sourceSize.width: 30 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: {
console.debug("[CallPage]User: create new call")
listStackView.push(newCallItem)
}
}
}
ColumnLayout {
SearchBar {
id: searchBar
Layout.fillWidth: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
placeholderText: qsTr("Rechercher un appel")
}
RowLayout {
Layout.topMargin: 30 * DefaultStyle.dp
Control.Control {
id: listLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
background: Rectangle {
anchors.fill: parent
}
ColumnLayout {
anchors.fill: parent
ColumnLayout {
Text {
text: qsTr("Aucun appel")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
visible: historyListView.count === 0
Layout.alignment: Qt.AlignHCenter
}
ListView {
id: historyListView
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
model: CallHistoryProxy{
filterText: searchBar.text
}
currentIndex: -1
spacing: 10 * DefaultStyle.dp
highlightMoveDuration: 10
highlightMoveVelocity: -1
// highlightFollowsCurrentItem: true
highlight: Rectangle {
x: historyListView.x
width: historyListView.width
height: historyListView.height
color: DefaultStyle.main2_100
y: historyListView.currentItem? historyListView.currentItem.y : 0
}
delegate: Item {
width:historyListView.width
height: 56 * DefaultStyle.dp
anchors.topMargin: 5 * DefaultStyle.dp
anchors.bottomMargin: 5 * DefaultStyle.dp
RowLayout {
z: 1
anchors.fill: parent
Item {
Layout.preferredWidth: historyAvatar.width
Layout.preferredHeight: historyAvatar.height
Layout.leftMargin: 5 * DefaultStyle.dp
MultiEffect {
source: historyAvatar
anchors.fill: historyAvatar
shadowEnabled: true
shadowBlur: 1
shadowColor: DefaultStyle.grey_900
shadowOpacity: 0.1
}
Avatar {
id: historyAvatar
address: modelData.core.remoteAddress
width: 45 * DefaultStyle.dp
height: 45 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
spacing: 5 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
Text {
id: friendAddress
property var remoteAddress: modelData ? UtilsCpp.getDisplayName(modelData.core.remoteAddress) : undefined
text: remoteAddress ? remoteAddress.value : ""
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
RowLayout {
spacing: 3 * DefaultStyle.dp
Image {
source: 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.isOutgoing
? AppIcons.outgoingCallRejected
: AppIcons.incomingCallRejected
: modelData.core.status === LinphoneEnums.CallStatus.Missed
? modelData.core.isOutgoing
? AppIcons.outgoingCallMissed
: AppIcons.incomingCallMissed
: modelData.core.isOutgoing
? AppIcons.outgoingCall
: AppIcons.incomingCall
Layout.preferredWidth: 5 * DefaultStyle.dp
Layout.preferredHeight: 5 * DefaultStyle.dp
sourceSize.width: 5 * DefaultStyle.dp
sourceSize.height: 5 * DefaultStyle.dp
RectangleTest{anchors.fill: parent}
}
Text {
// text: modelData.core.date
text: UtilsCpp.formatDateElapsedTime(modelData.core.date)
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
}
// Item {
// Layout.fillWidth: true
// }
Control.Button {
implicitWidth: 24 * DefaultStyle.dp
implicitHeight: 24 * DefaultStyle.dp
Layout.rightMargin: 5 * DefaultStyle.dp
padding: 0
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.phone
width: 24 * DefaultStyle.dp
sourceSize.width: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: {
var addr = modelData.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
UtilsCpp.createCall(addr)
}
}
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
Rectangle {
anchors.fill: parent
opacity: 0.1
color: DefaultStyle.main2_500main
visible: parent.containsMouse
}
onPressed: {
historyListView.currentIndex = model.index
}
}
}
onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt(currentIndex)
}
onCountChanged: {
mainItem.showDefaultItem = historyListView.count === 0 && historyListView.visible
}
onVisibleChanged: {
mainItem.showDefaultItem = historyListView.count === 0 && historyListView.visible
if (!visible) currentIndex = -1
}
Connections {
target: mainItem
onShowDefaultItemChanged: mainItem.showDefaultItem = mainItem.showDefaultItem && historyListView.count === 0 && historyListView.visible
onListViewUpdated: {
historyListView.model.updateView()
}
}
Control.ScrollBar.vertical: scrollbar
}
}
}
}
Control.ScrollBar {
id: scrollbar
active: true
policy: Control.ScrollBar.AlwaysOn
Layout.fillHeight: true
}
}
}
}
}
Component {
id: newCallItem
ColumnLayout {
Control.StackView.onActivating: {
mainItem.showDefaultItem = false
}
Control.StackView.onDeactivating: mainItem.showDefaultItem = true
RowLayout {
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
Control.Button {
background: Item {
}
contentItem: Image {
source: AppIcons.returnArrow
}
onClicked: {
console.debug("[CallPage]User: return to call history")
listStackView.pop()
}
}
Text {
text: qsTr("Nouvel appel")
color: DefaultStyle.main2_700
font.pixelSize: 29 * DefaultStyle.dp
font.weight: 800 * DefaultStyle.dp
}
Item {
Layout.fillWidth: true
}
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
// Layout.maximumWidth: parent.width
CallContactsLists {
Layout.fillWidth: true
Layout.fillHeight: true
// Layout.leftMargin: listStackView.sideMargin
// Layout.rightMargin: listStackView.sideMargin
groupCallVisible: true
searchBarColor: DefaultStyle.grey_100
onCallButtonPressed: (address) => {
var addressEnd = "@sip.linphone.org"
if (!address.endsWith(addressEnd)) address += addressEnd
UtilsCpp.createCall(address)
// var window = UtilsCpp.getCallsWindow()
}
}
}
}
}
}
rightPanelContent: RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
visible: mainItem.selectedRowHistoryGui != undefined
Layout.topMargin: 45 * DefaultStyle.dp
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
ColumnLayout {
spacing: 30 * DefaultStyle.dp
Layout.fillWidth: true
Layout.fillHeight: true
Item {
Layout.preferredHeight: detailAvatar.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Avatar {
id: detailAvatar
anchors.centerIn: parent
width: 100 * DefaultStyle.dp
height: 100 * DefaultStyle.dp
address: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.remoteAddress || ""
}
PopupButton {
id: detailOptions
anchors.left: detailAvatar.right
anchors.leftMargin: 30 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
popup.x: width
popup.contentItem: ColumnLayout {
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Ajouter aux contacts")
iconSource: AppIcons.plusCircle
}
onClicked: {
// console.debug("[CallPage.qml] TODO : add to contact")
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{}', detailAvatar)
friendGui.core.name = contactName.text
friendGui.core.address = contactAddress.text
friendGui.core.save()
}
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Copier l'adresse SIP")
iconSource: AppIcons.copy
}
onClicked: console.debug("[CallPage.qml] TODO : copy SIP address")
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Bloquer")
iconSource: AppIcons.empty
}
onClicked: console.debug("[CallPage.qml] TODO : block user")
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 2 * DefaultStyle.dp
color: DefaultStyle.main2_400
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Supprimer l'historique")
iconSource: AppIcons.trashCan
colorizationColor: DefaultStyle.danger_500main
}
Connections {
target: deleteForUserPopup
onAccepted: detailListView.model.removeEntriesWithFilter()
}
onClicked: {
detailOptions.close()
deleteForUserPopup.open()
}
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Text {
id: contactName
property var remoteAddress: mainItem.selectedRowHistoryGui ? UtilsCpp.getDisplayName(mainItem.selectedRowHistoryGui.core.remoteAddress) : undefined
Layout.alignment: Qt.AlignHCenter
text: remoteAddress ? remoteAddress.value : ""
horizontalAlignment: Text.AlignHCenter
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
Text {
id: contactAddress
text: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
horizontalAlignment: Text.AlignHCenter
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
Text {
// connection status
}
}
RowLayout {
spacing: 40 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
// Layout.fillHeight: true
Item {Layout.fillWidth: true}
LabelButton {
Layout.preferredWidth: 24 * DefaultStyle.dp//image.width
Layout.preferredHeight: image.height
image.source: AppIcons.phone
label: qsTr("Appel")
button.onClicked: {
var addr = mainItem.selectedRowHistoryGui.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
UtilsCpp.createCall(addr)
}
}
LabelButton {
Layout.preferredWidth: image.width
Layout.preferredHeight: image.height
image.source: AppIcons.chatTeardropText
label: qsTr("Message")
button.onClicked: console.debug("[CallPage.qml] TODO : open conversation")
}
LabelButton {
Layout.preferredWidth: image.width
Layout.preferredHeight: image.height
image.source: AppIcons.videoCamera
label: qsTr("Appel Video")
button.onClicked: {
var addr = mainItem.selectedRowHistoryGui.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if(!addr.endsWith(addressEnd)) addr += addressEnd
UtilsCpp.createCall(addr)
console.log("[CallPage.qml] TODO : enable video")
}
}
Item {Layout.fillWidth: true}
}
Control.Control {
id: detailControl
Layout.preferredWidth: detailListView.width + leftPadding + rightPadding
implicitHeight: 430 * DefaultStyle.dp + topPadding + bottomPadding
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
topPadding: 16 * DefaultStyle.dp
bottomPadding: 21 * DefaultStyle.dp
leftPadding: 17 * DefaultStyle.dp
rightPadding: 17 * DefaultStyle.dp
background: Rectangle {
id: detailListBackground
width: parent.width
height: detailListView.height
color: DefaultStyle.grey_0
radius: 15 * DefaultStyle.dp
}
ListView {
id: detailListView
width: 360 * DefaultStyle.dp
height: Math.min(detailControl.implicitHeight, contentHeight)
anchors.centerIn: detailListBackground
anchors.bottomMargin: 21 * DefaultStyle.dp
spacing: 10 * DefaultStyle.dp
clip: true
onCountChanged: {
mainItem.listViewUpdated()
}
model: CallHistoryProxy {
filterText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
}
delegate: Item {
width:detailListView.width
height: 56 * DefaultStyle.dp
// anchors.topMargin: 5 * DefaultStyle.dp
// anchors.bottomMargin: 5 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
RowLayout {
Image {
source: 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.isOutgoing
? AppIcons.outgoingCallRejected
: AppIcons.incomingCallRejected
: modelData.core.status === LinphoneEnums.CallStatus.Missed
? modelData.core.isOutgoing
? AppIcons.outgoingCallMissed
: AppIcons.incomingCallMissed
: modelData.core.isOutgoing
? AppIcons.outgoingCall
: AppIcons.incomingCall
Layout.preferredWidth: 6.67 * DefaultStyle.dp
Layout.preferredHeight: 6.67 * DefaultStyle.dp
sourceSize.width: 6.67 * DefaultStyle.dp
sourceSize.height: 6.67 * DefaultStyle.dp
}
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: UtilsCpp.formatDate(modelData.core.date)
color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
Item {
Layout.fillWidth: true
}
Text {
text: UtilsCpp.formatElapsedTime(modelData.core.duration, false)
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
component LabelButton: ColumnLayout {
id: labelButton
property alias image: buttonImg
property alias button: button
property string label
spacing: 8 * DefaultStyle.dp
Button {
id: button
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 56 * DefaultStyle.dp
Layout.preferredHeight: 56 * DefaultStyle.dp
topPadding: 16 * DefaultStyle.dp
bottomPadding: 16 * DefaultStyle.dp
leftPadding: 16 * DefaultStyle.dp
rightPadding: 16 * DefaultStyle.dp
background: Rectangle {
anchors.fill: parent
radius: 40 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
contentItem: Image {
id: buttonImg
source: labelButton.source
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
sourceSize.width: 24 * DefaultStyle.dp
sourceSize.height: 24 * DefaultStyle.dp
}
}
Text {
Layout.alignment: Qt.AlignHCenter
text: labelButton.label
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
component IconLabel: RowLayout {
id: iconLabel
property string text
property string iconSource
property color colorizationColor: DefaultStyle.main2_500main
EffectImage {
source: iconLabel.iconSource
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: iconLabel.colorizationColor
}
Text {
text: iconLabel.text
color: iconLabel.colorizationColor
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
}