display recordings in native dialog

This commit is contained in:
Gaelle Braud 2026-04-02 18:27:39 +02:00
parent be6211778f
commit 1cdc35bebd
7 changed files with 1257 additions and 3 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

View file

@ -629,6 +629,24 @@ QString Utils::getOsProduct() {
return product + "/" + version;
}
QString Utils::getCaptureDirpaths() {
return Paths::getCapturesDirPath();
}
void Utils::openNativeDialog(QString currentFolder) {
const QString path = currentFolder.isEmpty() ? QDir::homePath() : currentFolder;
#if defined(Q_OS_WIN)
// Explorer avec le dossier sélectionné
QProcess::startDetached("explorer.exe", {QDir::toNativeSeparators(path)});
#elif defined(Q_OS_MACOS)
QProcess::startDetached("open", {path});
#else // Linux
QProcess::startDetached("xdg-open", {path});
#endif
}
QString Utils::getCountryName(const QLocale::Territory &p_country) {
QString countryName;
switch (p_country) {
@ -2012,7 +2030,8 @@ QDateTime Utils::getOffsettedUTC(const QDateTime &date) {
}
QString Utils::toTimeString(QDateTime date, const QString &format) {
// Issue : date.toString() will not print the good time in timezones. Get it from date and add ourself the offset.
// Issue : date.toString() will not print the good time in timezones. Get it from date and add ourself the
// offset.
return getOffsettedUTC(date).toString(format);
}
QString Utils::getSafeFilePath(const QString &filePath, bool *soFarSoGood) {

View file

@ -203,6 +203,9 @@ public:
static QString getApplicationProduct();
static QString getOsProduct();
Q_INVOKABLE static QString getCaptureDirpaths();
Q_INVOKABLE static void openNativeDialog(QString currentFolder = QString());
static QList<QSharedPointer<DownloadablePayloadTypeCore>> getDownloadableVideoPayloadTypes();
static void checkDownloadedCodecsUpdates();

View file

@ -86,6 +86,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Display/Participant/ParticipantDeviceListView.qml
view/Control/Display/Participant/ParticipantInfoListView.qml
view/Control/Display/Participant/ParticipantListView.qml
view/Control/Display/Record/RecordListView.qml
view/Control/Display/Settings/SettingsMenuItem.qml
view/Control/Form/Login/LoginForm.qml
@ -182,6 +183,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Page/Main/Contact/ContactPage.qml
view/Page/Main/Help/HelpPage.qml
view/Page/Main/Meeting/MeetingPage.qml
view/Page/Main/Record/RecordPage.qml
view/Page/Main/Start/WelcomePage.qml
view/Page/Window/AbstractWindow.qml

View file

@ -0,0 +1,309 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Effects
import QtQuick.Controls.Basic
import Linphone
import QtQml
import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
ListView {
id: mainItem
property string searchBarText
property bool hoverEnabled: true
property var delegateButtons
property ConferenceInfoGui selectedConference
property bool _moveToIndex: false
property bool loading: false
property real busyIndicatorSize: Utils.getSizeWithScreenRatio(60)
clip: true
cacheBuffer: height/2
spacing: Utils.getSizeWithScreenRatio(8)
highlightFollowsCurrentItem: false
function selectIndex(index){
mainItem.currentIndex = index
}
function resetSelections(){
mainItem.selectedConference = null
mainItem.currentIndex = -1
}
function scrollToCurrentDate() {
currentIndex = -1
confInfoProxy.selectData(confInfoProxy.getCurrentDateConfInfo())
moveToCurrentItem()
}
//----------------------------------------------------------------
function moveToCurrentItem(){
if(mainItem.currentIndex >= 0)
mainItem.positionViewAtIndex(mainItem.currentIndex, ListView.Contain)
}
onCurrentItemChanged: {
moveToCurrentItem()
if(currentItem) {
mainItem.selectedConference = currentItem.itemGui
currentItem.forceActiveFocus()
}
}
// 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
}
}
//----------------------------------------------------------------
onAtYEndChanged: if(atYEnd) confInfoProxy.displayMore()
Keys.onPressed: (event)=> {
if(event.key == Qt.Key_Up) {
if(currentIndex > 0 ) {
selectIndex(mainItem.currentIndex-1)
event.accepted = true
} else {
selectIndex(model.count - 1)
event.accepted = true
}
}else if(event.key == Qt.Key_Down){
if(currentIndex < model.count - 1) {
selectIndex(currentIndex+1)
event.accepted = true
} else {
selectIndex(0)
event.accepted = true
}
}
}
// Let some space for better UI
footer: Item{height: Utils.getSizeWithScreenRatio(38)}
// model: RecordProxy {
// id: confInfoProxy
// filterText: searchBarText
// filterType: RecordProxy.None
// initialDisplayItems: Math.max(20, Math.round(2 * mainItem.height / (Utils.getSizeWithScreenRatio(63))))
// displayItemsStep: initialDisplayItems/2
// onModelAboutToBeReset: {
// mainItem.loading = true
// }
// onModelReset: {
// mainItem.loading = !confInfoProxy.accountConnected
// selectData(getCurrentDateConfInfo())
// }
// function selectData(confInfoGui){
// mainItem.currentIndex = loadUntil(confInfoGui)
// }
// }
BusyIndicator {
anchors.horizontalCenter: mainItem.horizontalCenter
visible: mainItem.loading
height: visible ? mainItem.busyIndicatorSize : 0
width: mainItem.busyIndicatorSize
indicatorHeight: mainItem.busyIndicatorSize
indicatorWidth: mainItem.busyIndicatorSize
indicatorColor: DefaultStyle.main1_500_main
}
ScrollBar.vertical: ScrollBar {
id: scrollbar
rightPadding: Utils.getSizeWithScreenRatio(8)
active: true
interactive: true
policy: mainItem.contentHeight > mainItem.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
}
section {
criteria: ViewSection.FullString
delegate: Text {
topPadding: Utils.getSizeWithScreenRatio(24)
bottomPadding: Utils.getSizeWithScreenRatio(16)
text: section
height: Utils.getSizeWithScreenRatio(29) + topPadding + bottomPadding
wrapMode: Text.NoWrap
font {
pixelSize: Utils.getSizeWithScreenRatio(20)
weight: Utils.getSizeWithScreenRatio(800)
capitalization: Font.Capitalize
}
}
property: '$sectionMonth'
}
delegate: FocusScope {
id: itemDelegate
visible: !mainItem.loading
height: Utils.getSizeWithScreenRatio(63) + (!isFirst && dateDay.visible ? topOffset : 0)
width: mainItem.width
enabled: !isCanceled && haveModel
property var itemGui: $modelData
// Do not use itemAtIndex because of caching items. Using getAt ensure to have a GUI
property var previousConfInfoGui : mainItem.model.getAt(index-1)
property var dateTime: itemGui.core ? itemGui.core.dateTime : UtilsCpp.getCurrentDateTime()
property string day : UtilsCpp.toDateDayNameString(dateTime)
property string dateString: UtilsCpp.toDateString(dateTime)
property string previousDateString: previousConfInfoGui ? UtilsCpp.toDateString(previousConfInfoGui.core ? previousConfInfoGui.core.dateTime : UtilsCpp.getCurrentDateTime()) : ''
property bool isFirst : ListView.previousSection !== ListView.section
property real topOffset: (dateDay.visible && !isFirst? Utils.getSizeWithScreenRatio(8) : 0)
property var endDateTime: itemGui.core ? itemGui.core.endDateTime : UtilsCpp.getCurrentDateTime()
property bool haveModel: itemGui.core ? itemGui.core.haveModel : false
property bool isCanceled: itemGui.core ? itemGui.core.state === LinphoneEnums.ConferenceInfoState.Cancelled : false
property bool isSelected: itemGui.core == mainItem.selectedConference?.core
RowLayout{
id: delegateIn
anchors.fill: parent
anchors.topMargin: !itemDelegate.isFirst && dateDay.visible ? itemDelegate.topOffset : 0
spacing: 0
Item{
Layout.preferredWidth: Utils.getSizeWithScreenRatio(32)
visible: !dateDay.visible
}
ColumnLayout {
id: dateDay
Layout.fillWidth: false
Layout.preferredWidth: Utils.getSizeWithScreenRatio(32)
Layout.minimumWidth: Utils.getSizeWithScreenRatio(32)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(51)
visible: previousDateString.length == 0 || previousDateString != dateString
spacing: 0
Text {
Layout.preferredHeight: Utils.getSizeWithScreenRatio(19)
Layout.alignment: Qt.AlignCenter
text: day.substring(0,3) + '.'
color: DefaultStyle.main2_500_main
wrapMode: Text.NoWrap
elide: Text.ElideNone
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
capitalization: Font.Capitalize
}
}
Rectangle {
id: dayNum
Layout.preferredWidth: Utils.getSizeWithScreenRatio(32)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(32)
Layout.alignment: Qt.AlignCenter
radius: height/2
property var isCurrentDay: UtilsCpp.isCurrentDay(dateTime)
color: isCurrentDay ? DefaultStyle.main1_500_main : "transparent"
Text {
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: UtilsCpp.toDateDayString(dateTime)
color: dayNum.isCurrentDay ? DefaultStyle.grey_0 : DefaultStyle.main2_500_main
wrapMode: Text.NoWrap
font {
pixelSize: Utils.getSizeWithScreenRatio(20)
weight: Utils.getSizeWithScreenRatio(800)
}
}
}
Item{Layout.fillHeight:true;Layout.fillWidth: true}
}
Item {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(265)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(63)
Layout.leftMargin: Utils.getSizeWithScreenRatio(23)
Rectangle {
id: conferenceInfoDelegate
anchors.fill: parent
anchors.rightMargin: 5 // margin to avoid clipping shadows at right
radius: Utils.getSizeWithScreenRatio(10)
visible: itemDelegate.haveModel || itemDelegate.activeFocus
color: itemDelegate.isSelected ? DefaultStyle.main2_200 : DefaultStyle.grey_0 // mainItem.currentIndex === index
ColumnLayout {
anchors.fill: parent
anchors.left: parent.left
anchors.leftMargin: Utils.getSizeWithScreenRatio(16)
anchors.rightMargin: Utils.getSizeWithScreenRatio(16)
anchors.topMargin: Utils.getSizeWithScreenRatio(10)
anchors.bottomMargin: Utils.getSizeWithScreenRatio(10)
spacing: Utils.getSizeWithScreenRatio(2)
visible: itemDelegate.haveModel
RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
imageSource: AppIcons.usersThree
colorizationColor: DefaultStyle.main2_600
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
}
Text {
text: itemGui.core? itemGui.core.subject : ""
Layout.fillWidth: true
maximumLineCount: 1
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
}
}
}
Text {
//: "Réunion annulée"
text: itemDelegate.isCanceled ? qsTr("meeting_info_cancelled") : UtilsCpp.toDateHourString(dateTime) + " - " + UtilsCpp.toDateHourString(endDateTime)
color: itemDelegate.isCanceled ? DefaultStyle.danger_500_main : DefaultStyle.main2_500_main
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
}
}
MultiEffect {
source: conferenceInfoDelegate
anchors.fill: conferenceInfoDelegate
visible: itemDelegate.haveModel
shadowEnabled: true
shadowBlur: 0.7
shadowOpacity: 0.2
}
Text {
anchors.fill: parent
anchors.rightMargin: Utils.getSizeWithScreenRatio(5) // margin to avoid clipping shadows at right
anchors.leftMargin: Utils.getSizeWithScreenRatio(16)
verticalAlignment: Text.AlignVCenter
visible: !itemDelegate.haveModel
//: "Aucune réunion aujourd'hui"
text: qsTr("meetings_list_no_meeting_for_today")
lineHeightMode: Text.FixedHeight
lineHeight: Utils.getSizeWithScreenRatio(18)
font {
pixelSize: Typography.p2.pixelSize
weight: Typography.p2.weight
}
}
MouseArea {
hoverEnabled: mainItem.hoverEnabled
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
visible: itemDelegate.haveModel
onClicked: {
mainItem.selectIndex(index)
}
}
}
}
}
}

View file

@ -2,6 +2,7 @@ import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Qt.labs.platform 1.1
import QtQuick.Effects
import Linphone
@ -514,15 +515,26 @@ Item {
KeyNavigation.up: visibleChildren.length != 0 ? settingsMenuButton.getPreviousItem(2) : null
KeyNavigation.down: visibleChildren.length != 0 ? settingsMenuButton.getNextItem(2) : null
}
// FileDialog {
// id: fileDialog
// folder: UtilsCpp.getCaptureDirpaths()
// }
IconLabelButton {
id: recordsButton
Layout.fillWidth: true
visible: false// !SettingsCpp.disableCallRecordings
visible: !SettingsCpp.disableCallRecordings
icon.width: Utils.getSizeWithScreenRatio(32)
icon.height: Utils.getSizeWithScreenRatio(32)
//: "Enregistrements"
text: qsTr("recordings_title")
icon.source: AppIcons.micro
icon.source: AppIcons.recordFill
onClicked: {
UtilsCpp.openNativeDialog(UtilsCpp.getCaptureDirpaths())
// fileDialog.folder = UtilsCpp.getCaptureDirpaths()
// fileDialog.open()
// var page = recordingsPageComponent.createObject(parent);
// openContextualMenuComponent(page)
}
KeyNavigation.up: visibleChildren.length != 0 ? settingsMenuButton.getPreviousItem(3) : null
KeyNavigation.down: visibleChildren.length != 0 ? settingsMenuButton.getNextItem(3) : null
}
@ -749,6 +761,17 @@ Item {
}
}
}
Component {
id: recordingsPageComponent
RecordPage {
// onGoBack: {
// closeContextualMenuComponent()
// if(FocusNavigator.doesLastFocusWasKeyboard()){
// mainItem.nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
// }
// }
}
}
Component {
id: helpPageComponent
HelpPage {

View file

@ -0,0 +1,796 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
AbstractMainPage {
id: mainItem
property ConferenceInfoGui selectedConference
property int meetingListCount: 0
signal returnRequested()
signal addParticipantsValidated(list<string> selectedParticipants)
//: "Aucun enregistrement"
emptyListText: qsTr("record_list_empty")
newItemIconSource: AppIcons.plusCircle
showDefaultItem: false
onVisibleChanged: if (!visible) {
leftPanelStackView.clear()
leftPanelStackView.push(leftPanelStackView.initialItem)
}
onSelectedConferenceChanged: {
// While a conference is being edited, we need to stay on the edit page
if (overridenRightPanelStackView.currentItem && (overridenRightPanelStackView.currentItem.objectName === "editConf" || overridenRightPanelStackView.currentItem.objectName === "createConf")) return
overridenRightPanelStackView.clear()
if (selectedConference && selectedConference.core && selectedConference.core.haveModel) {
if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate)
}
}
leftPanelContent: Control.StackView {
id: leftPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: Utils.getSizeWithScreenRatio(45)
initialItem: listLayout
clip: true
}
Component {
id: listLayout
FocusScope{
objectName: "listLayout"
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.rightMargin: Utils.getSizeWithScreenRatio(38)
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
spacing: 0
Text {
Layout.fillWidth: true
//: "Records"
text: qsTr("record_list_title")
color: DefaultStyle.main2_700
font.pixelSize: Typography.h2.pixelSize
font.weight: Typography.h2.weight
}
Item{Layout.fillWidth: true}
}
SearchBar {
id: searchBar
Layout.topMargin: Utils.getSizeWithScreenRatio(18)
Layout.rightMargin: Utils.getSizeWithScreenRatio(38)
visible: recordList.count !== 0 || searchBar.text.length !== 0
Layout.fillWidth: true
//: "Rechercher une réunion"
placeholderText: qsTr("meetings_search_hint")
KeyNavigation.up: scrollToCurrentDateButton
KeyNavigation.down: recordList
}
Image {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Utils.getSizeWithScreenRatio(46)
source: AppIcons.noRecordImage
Layout.preferredWidth: Utils.getSizeWithScreenRatio(359)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(314)
fillMode: Image.PreserveAspectFit
}
Text {
visible: recordList.count === 0 && !recordList.loading
Layout.topMargin: searchBar.text.length !== 0
? Utils.getSizeWithScreenRatio(137)
: Utils.getSizeWithScreenRatio(39)
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
//: "Aucun résultat"
text: searchBar.text.length !== 0 ? qsTr("list_filter_no_result_found")
//: "Aucun enregistrement"
: qsTr("meetings_empty_list")
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
// RecordListView {
// id: recordList
// // Remove 24 from first section padding because we cannot know that it is the first section. 24 is the margins between sections.
// Layout.topMargin: Utils.getSizeWithScreenRatio(38) - Utils.getSizeWithScreenRatio(24)
// Layout.fillWidth: true
// Layout.fillHeight: true
// searchBarText: searchBar.text
// onCountChanged: {
// mainItem.meetingListCount = count
// }
// Binding {
// target: mainItem
// property: "showDefaultItem"
// when: recordList.loading
// value: false
// }
// onSelectedConferenceChanged: {
// mainItem.selectedConference = selectedConference
// }
// Keys.onPressed: (event) => {
// if(event.key == Qt.Key_Escape){
// searchBar.forceActiveFocus()
// event.accepted = true
// }else if(event.key == Qt.Key_Right){
// overridenRightPanelStackView.currentItem.forceActiveFocus()
// event.accepted = true
// }
// }
// }
}
}
}
Component {
id: createConf
FocusScope{
id: createConfLayout
objectName: "createConf"
property ConferenceInfoGui conferenceInfoGui
ColumnLayout {
spacing: Utils.getSizeWithScreenRatio(33)
anchors.fill: parent
RowLayout {
spacing: Utils.getSizeWithScreenRatio(5)
Layout.rightMargin: Utils.getSizeWithScreenRatio(35)
Button {
id: backButton
style: ButtonStyle.noBackground
icon.source: AppIcons.leftArrow
focus: true
icon.width: Utils.getSizeWithScreenRatio(24)
icon.height: Utils.getSizeWithScreenRatio(24)
KeyNavigation.right: createButton
KeyNavigation.down: meetingSetup
onClicked: {
meetingSetup.conferenceInfoGui.core.undo()
leftPanelStackView.pop()
}
}
Text {
//: "Nouvelle réunion"
text: qsTr("meeting_schedule_title")
color: DefaultStyle.main2_700
font {
pixelSize: Typography.h3.pixelSize
weight: Typography.h3.weight
}
Layout.fillWidth: true
}
Item {Layout.fillWidth: true}
SmallButton {
id: createButton
text: qsTr("create")
style: ButtonStyle.main
KeyNavigation.left: backButton
KeyNavigation.down: meetingSetup
onClicked: {
if (meetingSetup.conferenceInfoGui.core.subject.length === 0 || meetingSetup.conferenceInfoGui.core.participantCount === 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: Veuillez saisir un titre et sélectionner au moins un participant
qsTr("meeting_schedule_mandatory_field_not_filled_toast"), false)
} else if (meetingSetup.conferenceInfoGui.core.duration <= 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "La fin de la conférence doit être plus récente que son début"
qsTr("meeting_schedule_duration_error_toast"), false)
} else {
meetingSetup.conferenceInfoGui.core.save()
//: "Création de la réunion en cours "
mainWindow.showLoadingPopup(qsTr("meeting_schedule_creation_in_progress"), true, function () {
meetingSetup.conferenceInfoGui.core.cancelCreation()
})
}
}
}
}
Control.ScrollView {
Layout.fillHeight: true
Layout.fillWidth: true
contentHeight: meetingSetup.height
Control.ScrollBar.vertical: ScrollBar {
id: meetingScrollBar
visible: parent.contentHeight > parent.height
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: Utils.getSizeWithScreenRatio(8)
}
MeetingForm {
id: meetingSetup
conferenceInfoGui: createConfLayout.conferenceInfoGui
isCreation: true
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: Utils.getSizeWithScreenRatio(35)
Connections {
target: meetingSetup.conferenceInfoGui ? meetingSetup.conferenceInfoGui.core : null
function onConferenceSchedulerStateChanged() {
var mainWin = UtilsCpp.getMainWindow()
if (meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Ready) {
leftPanelStackView.pop()
//: "Nouvelle réunion"
UtilsCpp.showInformationPopup(qsTr("meeting_schedule_title"),
//: "Réunion planifiée avec succès"
qsTr("meeting_info_created_toast"), true)
mainWindow.closeLoadingPopup()
}
else if (meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.AllocationPending
|| meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Updating) {
mainWin.showLoadingPopup(qsTr("meeting_schedule_creation_in_progress"), true, function () {
leftPanelStackView.pop()
})
} else {
if (meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Error) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Échec de création de la réunion !"
qsTr("meeting_failed_to_schedule_toast"), false)
}
mainWin.closeLoadingPopup()
}
createConfLayout.enabled = meetingSetup.conferenceInfoGui.core.schedulerState != LinphoneEnums.ConferenceSchedulerState.AllocationPending
}
function onSaveFailed() {
var mainWin = UtilsCpp.getMainWindow()
mainWin.closeLoadingPopup()
}
}
onAddParticipantsRequested: {
leftPanelStackView.push(addParticipants, {"conferenceInfoGui": conferenceInfoGui, "container": leftPanelStackView})
}
Connections {
target: mainItem
onAddParticipantsValidated: (selectedParticipants) => {
meetingSetup.conferenceInfoGui.core.resetParticipants(selectedParticipants)
leftPanelStackView.pop()
}
}
}
}
}
}
}
Component {
id: editConf
FocusScope{
id: editFocusScope
objectName: "editConf"
property ConferenceInfoGui conferenceInfoGui
width: overridenRightPanelStackView.width
ColumnLayout {
id: editLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: childrenRect.height
spacing: 0
Section {
Layout.fillWidth: true
content: RowLayout {
spacing: Utils.getSizeWithScreenRatio(16)
Layout.preferredWidth: overridenRightPanelStackView.width
Button {
id: backButton
icon.source: AppIcons.leftArrow
icon.width: Utils.getSizeWithScreenRatio(24)
icon.height: Utils.getSizeWithScreenRatio(24)
style: ButtonStyle.noBackground
KeyNavigation.left: saveButton
KeyNavigation.right: titleText
KeyNavigation.down: conferenceEdit
KeyNavigation.up: conferenceEdit
onClicked: {
conferenceEdit.conferenceInfoGui.core.undo()
overridenRightPanelStackView.pop()
}
}
RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage{
imageSource: AppIcons.usersThree
colorizationColor: DefaultStyle.main2_600
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
}
TextInput {
id: titleText
Layout.fillWidth: true
color: DefaultStyle.main2_600
clip: true
font {
pixelSize: Utils.getSizeWithScreenRatio(20)
weight: Typography.h4.weight
}
KeyNavigation.left: backButton
KeyNavigation.right: saveButton
KeyNavigation.down: conferenceEdit
KeyNavigation.up: conferenceEdit
onActiveFocusChanged: if(activeFocus==true) selectAll()
onTextEdited: mainItem.selectedConference.core.subject = text
Component.onCompleted: {
text = mainItem.selectedConference.core.subject
}
}
SmallButton {
id: saveButton
style: ButtonStyle.main
focus: true
text: qsTr("save")
KeyNavigation.left: titleText
KeyNavigation.right: backButton
KeyNavigation.down: conferenceEdit
KeyNavigation.up: conferenceEdit
onClicked: {
if (mainItem.selectedConference.core.subject.length === 0 || mainItem.selectedConference.core.participantCount === 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
qsTr("meeting_schedule_mandatory_field_not_filled_toast"), false)
} else if (mainItem.selectedConference.core.duration <= 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
qsTr("meeting_schedule_duration_error_toast"), false)
} else {
mainItem.selectedConference.core.save()
}
}
}
}
}
}
MeetingForm {
id: conferenceEdit
isCreation: false
conferenceInfoGui: editFocusScope.conferenceInfoGui
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
onAddParticipantsRequested: {
overridenRightPanelStackView.push(addParticipants, {"conferenceInfoGui": conferenceInfoGui, "container": overridenRightPanelStackView})
}
Connections {
target: mainItem
function onAddParticipantsValidated(selectedParticipants) {
conferenceEdit.conferenceInfoGui.core.resetParticipants(selectedParticipants)
overridenRightPanelStackView.pop()
}
}
Connections {
enabled: conferenceEdit.conferenceInfoGui
target: conferenceEdit.conferenceInfoGui ? conferenceEdit.conferenceInfoGui.core : null
ignoreUnknownSignals: true
function onSaveFailed() {
UtilsCpp.getMainWindow().closeLoadingPopup()
}
function onSchedulerStateChanged() {
editFocusScope.enabled = conferenceInfoGui.core.schedulerState != LinphoneEnums.ConferenceSchedulerState.AllocationPending
if (conferenceEdit.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Ready) {
overridenRightPanelStackView.pop()
UtilsCpp.getMainWindow().closeLoadingPopup()
//: "Enregistré"
UtilsCpp.showInformationPopup(qsTr("saved"),
//: "Réunion mise à jour"
qsTr("meeting_info_updated_toast"), true)
}
else if (conferenceEdit.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.AllocationPending
|| conferenceEdit.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Updating) {
//: "Modification de la réunion en cours"
UtilsCpp.getMainWindow().showLoadingPopup(qsTr("meeting_schedule_edit_in_progress"))
} else if (conferenceEdit.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Error) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Échec de la modification de la réunion !"
qsTr("meeting_failed_to_edit_toast"), false)
UtilsCpp.getMainWindow().closeLoadingPopup()
}
}
}
}
}
}
}
Component {
id: addParticipants
FocusScope{
id: addParticipantInItem
property Control.StackView container
property ConferenceInfoGui conferenceInfoGui
ColumnLayout {
id: addParticipantsLayout
spacing: Utils.getSizeWithScreenRatio(18)
anchors.rightMargin: Utils.getSizeWithScreenRatio(8)
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
ColumnLayout {
id: title
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
spacing: Utils.getSizeWithScreenRatio(4)
RowLayout {
id: addParticipantsButtons
spacing: Utils.getSizeWithScreenRatio(10)
Button {
id: addParticipantsBackButton
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.leftArrow
icon.width: Utils.getSizeWithScreenRatio(24)
icon.height: Utils.getSizeWithScreenRatio(24)
KeyNavigation.right: addButton
KeyNavigation.down: addParticipantLayout
onClicked: container.pop()
}
Text {
//: "Ajouter des participants"
text: qsTr("meeting_schedule_add_participants_title")
color: DefaultStyle.main1_500_main
maximumLineCount: 1
font {
pixelSize: Utils.getSizeWithScreenRatio(18)
weight: Typography.h4.weight
}
Layout.fillWidth: true
}
SmallButton {
id: addButton
enabled: addParticipantLayout.selectedParticipantsCount.length != 0
focus: enabled
style: ButtonStyle.main
text: qsTr("meeting_schedule_add_participants_apply")
KeyNavigation.left: addParticipantsBackButton
KeyNavigation.down: addParticipantLayout
onClicked: {
mainItem.addParticipantsValidated(addParticipantLayout.selectedParticipants)
}
}
}
Text {
//: "%n participant(s) sélectionné(s)"
text: qsTr("group_call_participant_selected", '', addParticipantLayout.selectedParticipantsCount).arg(addParticipantLayout.selectedParticipantsCount)
color: DefaultStyle.main2_500_main
Layout.leftMargin: addParticipantsBackButton.width + addParticipantsButtons.spacing
maximumLineCount: 1
font {
pixelSize: Utils.getSizeWithScreenRatio(12)
weight: Utils.getSizeWithScreenRatio(300)
}
Layout.fillWidth: true
}
}
AddParticipantsForm {
id: addParticipantLayout
Layout.fillWidth: true
Layout.fillHeight: true
height: addParticipantInItem.height - title.height
conferenceInfoGui: addParticipantInItem.conferenceInfoGui
participantscSrollBarRightMargin: 0
}
}
}
}
Component {
id: meetingDetail
FocusScope{
width: overridenRightPanelStackView.width
height: meetingDetailsLayout.childrenRect.height
ColumnLayout {
id: meetingDetailsLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
// anchors.fill: parent
visible: mainItem.selectedConference
spacing: Utils.getSizeWithScreenRatio(16)
Section {
visible: mainItem.selectedConference
Layout.fillWidth: true
content: RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
imageSource: AppIcons.usersThree
colorizationColor: DefaultStyle.main2_600
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
}
Text {
Layout.fillWidth: true
text: mainItem.selectedConference && mainItem.selectedConference.core? mainItem.selectedConference.core.subject : ""
maximumLineCount: 1
font {
pixelSize: Utils.getSizeWithScreenRatio(20)
weight: Typography.h4.weight
}
}
Item {
Layout.fillWidth: true
}
RoundButton {
id: editButton
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress)
visible: mainItem.selectedConference && isMeObj && isMeObj.value || false
icon.source: AppIcons.pencil
style: ButtonStyle.noBackgroundOrange
KeyNavigation.left: leftPanelStackView.currentItem
KeyNavigation.right: deletePopup
KeyNavigation.up: joinButton
KeyNavigation.down: shareNetworkButton
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
onClicked: mainItem.editConference(mainItem.selectedConference)
}
PopupButton {
id: deletePopup
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
contentImageColor: DefaultStyle.main1_500_main
KeyNavigation.left: editButton.visible ? editButton : leftPanelStackView.currentItem
KeyNavigation.right: leftPanelStackView.currentItem
KeyNavigation.up: joinButton
KeyNavigation.down: shareNetworkButton
popup.contentItem: IconLabelButton {
style: ButtonStyle.hoveredBackgroundRed
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress)
property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference?.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
icon.source: AppIcons.trashCan
//: "Supprimer la réunion"
text: qsTr("meeting_info_delete")
onClicked: {
if (mainItem.selectedConference) {
cancelAndDeleteConfDialog.cancel = canCancel
cancelAndDeleteConfDialog.open()
// mainItem.contactDeletionRequested(mainItem.selectedConference)
deletePopup.close()
}
}
Connections {
target: cancelAndDeleteConfDialog
function onCancelRequested() {
mainItem.selectedConference.core.lCancelConferenceInfo()
}
function onAccepted() {
mainItem.selectedConference.core.lDeleteConferenceInfo()
}
}
}
}
}
}
Section {
content: ColumnLayout {
spacing: Utils.getSizeWithScreenRatio(15)
width: parent.width
RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
Layout.fillWidth: true
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
colorizationColor: DefaultStyle.main2_600
imageSource: AppIcons.videoCamera
}
SmallButton {
id: linkButton
Layout.fillWidth: true
text: mainItem.selectedConference ? mainItem.selectedConference.core?.uri : ""
textSize: Typography.p1.pixelSize
textWeight: Typography.p1.weight
underline: true
style: ButtonStyle.noBackground
Keys.onPressed: (event)=> {
if (event.key == Qt.Key_Space || event.key == Qt.Key_Enter || event.key == Qt.Key_Return) {
clicked(undefined)
event.accepted = true;
}
}
KeyNavigation.left: shareNetworkButton
KeyNavigation.right: shareNetworkButton
KeyNavigation.up: deletePopup
KeyNavigation.down: joinButton
onClicked: {
// TODO : voir si c'est en audio only quand on clique sur le lien
UtilsCpp.createCall(mainItem.selectedConference.core.uri)
}
}
RoundButton {
id: shareNetworkButton
style: ButtonStyle.noBackground
icon.source: AppIcons.shareNetwork
KeyNavigation.left: linkButton
KeyNavigation.right: linkButton
KeyNavigation.up: deletePopup
KeyNavigation.down: joinButton
onClicked: {
var success = UtilsCpp.copyToClipboard(mainItem.selectedConference.core.uri)
if (success) UtilsCpp.showInformationPopup(qsTr("saved"),
//: "Adresse de la réunion copiée"
qsTr("meeting_address_copied_to_clipboard_toast"))
}
}
}
RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
imageSource: AppIcons.clock
colorizationColor: DefaultStyle.main2_600
}
Text {
text: mainItem.selectedConference && mainItem.selectedConference.core
? UtilsCpp.toDateString(mainItem.selectedConference.core.dateTime)
+ " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core.dateTime)
+ " - "
+ UtilsCpp.toDateHourString(mainItem.selectedConference.core.endDateTime)
: ''
font {
pixelSize: Utils.getSizeWithScreenRatio(14)
capitalization: Font.Capitalize
}
}
}
RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
imageSource: AppIcons.globe
colorizationColor: DefaultStyle.main2_600
}
Text {
Layout.fillWidth: true
//: "Fuseau horaire"
text: "%1: %2".arg(qsTr("meeting_schedule_timezone_title")).arg(mainItem.selectedConference && mainItem.selectedConference.core ? (mainItem.selectedConference.core.timeZoneModel.displayName + ", " + mainItem.selectedConference.core.timeZoneModel.countryName) : "")
font {
pixelSize: Utils.getSizeWithScreenRatio(14)
capitalization: Font.Capitalize
}
}
}
}
}
Section {
visible: mainItem.selectedConference && mainItem.selectedConference.core?.description.length != 0
content: RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
imageSource: AppIcons.note
colorizationColor: DefaultStyle.main2_600
}
Text {
text: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.description : ""
Layout.fillWidth: true
font {
pixelSize: Utils.getSizeWithScreenRatio(14)
capitalization: Font.Capitalize
}
}
}
}
Section {
content: RowLayout {
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
imageSource: AppIcons.userRectangle
colorizationColor: DefaultStyle.main2_600
}
Avatar {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(45)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(45)
_address: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.organizerAddress : ""
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
}
Text {
text: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.organizerName : ""
font {
pixelSize: Utils.getSizeWithScreenRatio(14)
capitalization: Font.Capitalize
}
}
}
}
Section {
visible: participantList.count > 0
content: RowLayout {
Layout.preferredHeight: participantList.contentHeight
width: Utils.getSizeWithScreenRatio(393)
spacing: Utils.getSizeWithScreenRatio(8)
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(24)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(24)
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.topMargin: Utils.getSizeWithScreenRatio(20)
imageSource: AppIcons.usersTwo
colorizationColor: DefaultStyle.main2_600
}
ListView {
id: participantList
Layout.preferredHeight: contentHeight
Layout.fillWidth: true
model: mainItem.selectedConference && mainItem.selectedConference.core ? mainItem.selectedConference.core.participants : []
clip: true
Control.ScrollBar.vertical: ScrollBar {
id: participantScrollBar
anchors.right: participantList.right
anchors.top: participantList.top
anchors.bottom: participantList.bottom
visible: participantList.height < participantList.contentHeight
}
delegate: RowLayout {
height: Utils.getSizeWithScreenRatio(56)
width: participantList.width - participantScrollBar.width - Utils.getSizeWithScreenRatio(5)
Avatar {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(45)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(45)
_address: modelData.address
secured: friendSecurityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
shadowEnabled: false
}
Text {
property var displayNameObj: UtilsCpp.getDisplayName(modelData.address)
text: displayNameObj?.value || ""
maximumLineCount: 1
Layout.fillWidth: true
font {
pixelSize: Utils.getSizeWithScreenRatio(14)
capitalization: Font.Capitalize
}
}
Text {
//: "Organisateur"
text: qsTr("meeting_info_organizer_label")
visible: mainItem.selectedConference && mainItem.selectedConference.core?.organizerAddress === modelData.address
color: DefaultStyle.main2_400
font {
pixelSize: Utils.getSizeWithScreenRatio(12)
weight: Utils.getSizeWithScreenRatio(300)
}
}
}
}
}
}
BigButton {
id: joinButton
visible: mainItem.selectedConference && mainItem.selectedConference.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.bottomMargin: Utils.getSizeWithScreenRatio(5)
//: "Rejoindre la réunion"
text: qsTr("meeting_info_join_title")
focus: true
KeyNavigation.up: shareNetworkButton
KeyNavigation.down: deletePopup
KeyNavigation.left: leftPanelStackView.currentItem
KeyNavigation.right: leftPanelStackView.currentItem
onClicked: {
console.log(mainItem.selectedConference.core.uri)
var callsWindow = UtilsCpp.getOrCreateCallsWindow()
callsWindow.setupConference(mainItem.selectedConference)
UtilsCpp.smartShowWindow(callsWindow)
}
}
}
}
}
}