linphone-desktop/linphone-app/ui/views/App/Dialog/NewConference.qml
Julien Wadel a8339bebc3 Timezones and prepare conference view with them.
Fix ICS views (better filterring).
Factorization of isMe() to test lcoal address.
New ComboBox design with better scrolling (like phone numbers in creation page).
2022-05-13 17:38:43 +02:00

579 lines
20 KiB
QML

import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.3
import Common 1.0
import Linphone 1.0
import LinphoneEnums 1.0
import App.Styles 1.0
import Common.Styles 1.0
import Units 1.0
import UtilsCpp 1.0
import ColorsList 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
DialogPlus {
id: conferenceManager
property bool isNew: !conferenceInfoModel || conferenceInfoModel.uri === ''
onIsNewChanged: console.log("Is New:"+isNew+", "+conferenceInfoModel + ", " + (conferenceInfoModel? conferenceInfoModel.uri : 'noUri'))
Component.onCompleted: console.log("Completed: Is New:"+isNew+", "+conferenceInfoModel + ", " + (conferenceInfoModel? conferenceInfoModel.uri : 'noUri'))
property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{}
onConferenceInfoModelChanged: selectedParticipants.setAddresses(conferenceInfoModel)
Connections{
target: conferenceInfoModel
onConferenceCreated: {
console.log("Conference has been created.")
creationStatus.icon = 'led_green'
}
onConferenceCreationFailed:{ console.log("Conference failed.")
creationStatus.icon = 'led_red'
}
onInvitationsSent: {
console.log("Conference => invitations sent. Check in log for invite states.")
exit(1)
}
}
readonly property int minParticipants: 1
buttons: [
ColumnLayout{
Layout.fillWidth: true
Layout.topMargin:15
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 15
spacing:4
visible: false // TODO
Text {
Layout.fillWidth: true
//: 'Would you like to encrypt your conference?' : Ask about setting the conference as secured.
text:qsTr('askEncryption')
color: NewConferenceStyle.askEncryptionColor
font.pointSize: NewConferenceStyle.titles.pointSize
font.weight: Font.DemiBold
}
Item{
Layout.fillWidth: true
Layout.preferredHeight: 50
Icon{
id:secureOff
anchors.left:parent.left
anchors.leftMargin : 0
anchors.verticalCenter: parent.verticalCenter
width:20
height:20
icon: 'secure_off'
iconSize:20
}
Switch{
id:secureSwitch
anchors.left:secureOff.right
anchors.leftMargin : 5
anchors.verticalCenter: parent.verticalCenter
width:50
enabled:true
checked: !SettingsModel.standardChatEnabled && SettingsModel.secureChatEnabled
onClicked: {
var newCheck = checked
if(SettingsModel.standardChatEnabled && checked || SettingsModel.secureChatEnabled && !checked)
newCheck = !checked;
checked = newCheck;
}
indicatorStyle: SwitchStyle.aux
}
Icon{
id:secureOn
anchors.left:secureSwitch.right
anchors.leftMargin : 15
anchors.verticalCenter: parent.verticalCenter
width:20
height:20
icon: 'secure_on'
iconSize:20
}
}
},
TextButtonA {
//: 'Cancel' : Cancel button
text: qsTr('cancelButton')
capitalization: Font.AllUppercase
onClicked: exit(0)
},
TextButtonB {
enabled: selectedParticipants.count >= conferenceManager.minParticipants && subject.text != '' && AccountSettingsModel.conferenceURI != ''
//: 'Launch' : Start button
text: conferenceManager.isNew ? qsTr('startButton') : 'Mettre à jour'
capitalization: Font.AllUppercase
function getInviteMode(){
//return inviteAppAccountCheckBox.checked ? LinphoneEnums::
return 0;
}
onClicked: {
creationStatus.icon = 'led_orange'
conferenceInfoModel.isScheduled = scheduledSwitch.checked
if( scheduledSwitch.checked){
var startDateTime = Utils.buildDate(dateField.getDate(), timeField.getTime())
startDateTime.setSeconds(0)
conferenceInfoModel.dateTime = startDateTime
conferenceInfoModel.duration = durationField.text
}
conferenceInfoModel.subject = subject.text
conferenceInfoModel.description = description.text
conferenceInfoModel.setParticipants(selectedParticipants.participantListModel)
//var callsWindow = App.getCallsWindow()
//App.smartShowWindow(callsWindow)
//callsWindow.openConference()
//CallsListModel.createConference(conferenceInfoModel, secureSwitch.checked, getInviteMode(), false )
conferenceInfoModel.createConference(false && secureSwitch.checked, getInviteMode()) // TODO remove false when Encryption is ready to use
}
TooltipArea{
visible: AccountSettingsModel.conferenceURI == '' || subject.text == '' || selectedParticipants.count < conferenceManager.minParticipants
maxWidth: participantView.width
delay:0
text: {
var txt = '\n';
if( subject.text == '')
//: 'You need to fill a subject.' : Tooltip to warn a user on missing field.
txt ='- ' + qsTr('missingSubject') + '\n'
if( selectedParticipants.count < conferenceManager.minParticipants)
//: 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation.
txt += '- ' + qsTr('missingParticipants', '', conferenceManager.minParticipants).arg(conferenceManager.minParticipants) + '\n'
if( AccountSettingsModel.conferenceURI == '')
//: 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration.
txt += '- ' + qsTr('missingConferenceURI') + '\n'
return txt;
}
}
}
, Icon{
id: creationStatus
height: 10
width: 10
visible: icon != ''
icon: ''
}
]
buttonsAlignment: Qt.AlignRight
buttonsLeftMargin: 15
//: 'Start a video conference' : Title of a popup about creation of a video conference
title: conferenceManager.isNew ? qsTr('newConferenceTitle') : 'Changer la conférence'
height: window.height - 100
width: window.width - 100
expandHeight: true
// ---------------------------------------------------------------------------
RowLayout {
height: parent.height
width: parent.width
spacing: 0
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: 10
spacing: 10
ColumnLayout {
Layout.fillWidth: true
Layout.rightMargin: 15
spacing:5
Text{
textFormat: Text.RichText
//: 'Subject' : Label of a text field about the subject of the chat room
text :qsTr('subjectLabel') +'<span style="color:red">*</span>'
color: NewConferenceStyle.titles.textColor
font.pointSize: NewConferenceStyle.titles.pointSize
font.weight: NewConferenceStyle.titles.weight
}
TextField {
id:subject
Layout.fillWidth: true
//: 'Give a subject' : Placeholder in a form about setting a subject
placeholderText : qsTr('subjectPlaceholder')
text: conferenceInfoModel && conferenceInfoModel.subject || ''
Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()
TooltipArea{
//: 'Current subject of the Chat Room. It cannot be empty'
//~ Tooltip Explanation about the subject of the chat room
text : qsTr('subjectTooltip')
}
}
}
Rectangle{
Layout.fillWidth: true
Layout.preferredHeight: scheduledSwitch.checked ? 120 : 50
Layout.rightMargin: 15
color: '#F7F7F7'
ColumnLayout{
anchors.fill: parent
spacing: 0
RowLayout{
Layout.fillWidth: true
Layout.preferredHeight: 50
Switch{
id:scheduledSwitch
Layout.leftMargin : 5
Layout.alignment: Qt.AlignVCenter
width:50
enabled: true
checked: conferenceInfoModel.isScheduled
onClicked: {
checked = !checked
}
indicatorStyle: SwitchStyle.aux
}
Text {
Layout.fillWidth: true
Layout.rightMargin: 15
//: 'Would you like to schedule your conference?' : Ask about setting the conference as scheduled.
text: 'Souhaitez-vous programmer cette conférence pour plus tard ?'
color: NewConferenceStyle.titles.textColor
font.pointSize: NewConferenceStyle.titles.pointSize
font.weight: NewConferenceStyle.titles.weight
wrapMode: Text.WordWrap
}
}
GridLayout{
id: scheduleForm
visible: scheduledSwitch.checked
Layout.fillWidth: true
Layout.fillHeight: true
columns: 4
property var locale: Qt.locale()
property date currentDate: new Date()
property int cellWidth: (parent.width-15)/columns
//Component.onCompleted: scheduleForm.updateDateTime()
Text{textFormat: Text.RichText; text: 'Date'+'<span style="color:red">*</span>'; Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
Text{textFormat: Text.RichText; text: 'Heure de début'+'<span style="color:red">*</span>'; Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
Text{textFormat: Text.RichText; text: 'Durée'; Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
Text{textFormat: Text.RichText; text: 'Fuseau horaire'; Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
TextField{id: dateField; Layout.preferredWidth: parent.cellWidth
color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
function getDate(){
return Date.fromLocaleDateString(scheduleForm.locale, text,'yyyy/MM/dd')
}
function setDate(date){
text = date.toLocaleDateString(scheduleForm.locale, 'yyyy/MM/dd')
}
text: conferenceManager.conferenceInfoModel? conferenceManager.conferenceInfoModel.dateTime.toLocaleDateString(scheduleForm.locale, 'yyyy/MM/dd') : ''
MouseArea{
anchors.fill: parent
onClicked: {
if( rightStackView.currentItemType === 1) {
rightStackView.currentItemType = 0
rightStackView.pop()// Cancel
}else {
if( rightStackView.depth > 1 )
rightStackView.pop()//Remove previous request
rightStackView.currentItemType = 1
rightStackView.push(datePicker, {selectedDate: new Date(dateField.getDate())})
}
}
}
}
TextField{id: timeField; Layout.preferredWidth: parent.cellWidth
color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
function getTime(){
return Date.fromLocaleTimeString(scheduleForm.locale, timeField.text, 'hh:mm')
}
function setTime(date){
text = date.toLocaleTimeString(scheduleForm.locale, 'hh:mm')
}
text: conferenceManager.conferenceInfoModel? conferenceManager.conferenceInfoModel.dateTime.toLocaleTimeString(scheduleForm.locale, 'hh:mm') : ''
MouseArea{
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width-50
onClicked: {
if( rightStackView.currentItemType === 2) {
rightStackView.currentItemType = 0
rightStackView.pop()// Cancel
}else {
if( rightStackView.depth > 1 )
rightStackView.pop()//Remove previous request
rightStackView.currentItemType = 2
rightStackView.push(timePicker,{selectedTime: new Date(timeField.getTime())})
}
}
}
}
NumericField{id: durationField; Layout.preferredWidth: parent.cellWidth; color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
text: conferenceManager.conferenceInfoModel ? conferenceManager.conferenceInfoModel.duration : '1200'
}
ComboBox{
Layout.preferredWidth: parent.cellWidth;
//color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
currentIndex: model.defaultIndex
model: TimeZoneProxyModel{}
onActivated: console.log("activated : " +index)
textRole: "displayText"
selectionWidth: 500
rootItem: conferenceManager
}
//TextField{ text: 'Paris'; readOnly: true; Layout.preferredWidth: parent.cellWidth; color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize}
function updateDateTime(){
var storedDate
if( dateField.text != '' && timeField.text != ''){
storedDate = Utils.buildDate(dateField.getDate(), timeField.getTime() )
}else
storedDate = new Date()
var currentDate = new Date()
if(currentDate >= storedDate){
var nextStoredDate = UtilsCpp.addMinutes(new Date(), 1)
dateField.setDate(nextStoredDate)
timeField.setTime(nextStoredDate)
}
}
Timer{
running: scheduleForm.visible && conferenceManager.isNew
repeat: true
interval: 1000
triggeredOnStart: true
onTriggered: {
if(conferenceManager.isNew)
scheduleForm.updateDateTime()
}
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.rightMargin: 15
spacing:5
Text{
Layout.fillWidth: true
Layout.preferredHeight: 20
textFormat: Text.RichText
//: 'Add a description' : Label of a text field about the description of the conference
text : 'Ajouter une description'
color: NewConferenceStyle.titles.textColor
font.pointSize: NewConferenceStyle.titles.pointSize
font.weight: NewConferenceStyle.titles.weight
}
TextAreaField {
id: description
Layout.fillWidth: true
Layout.fillHeight: true
//: 'Description' : Placeholder in a form about setting a description
placeholderText : 'Description'
text: conferenceManager.conferenceInfoModel ? conferenceManager.conferenceInfoModel.description : ''
Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()
TooltipArea{
//: 'This description will describe the conference' : Explanation about the description of the conference
text : 'This description will describe the conference'
}
}
}
ColumnLayout{
Layout.fillWidth: true
//Layout.preferredHeight: 60
spacing: 5
CheckBoxText {
id: inviteAppAccountCheckBox
text: 'Envoyer l\'invitation via mon compte Linphone'
width: parent.width
checked: true
onClicked: {
console.log('Send invite with Linphone account: ' + checked)
}
}
CheckBoxText {
id: inviteEmailCheckBox
visible: false // TODO
text: 'Envoyer l\'invitation via mon adresse mail'
width: parent.width
onClicked: {
console.log('Send invite with email: ' + checked)
}
}
}
}
StackView{
id: rightStackView
property int currentItemType: 0
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumWidth: 200
Layout.topMargin: 10
Layout.bottomMargin: 10
clip: true
// -------------------------------------------------------------------------
// See and remove selected addresses.
// -------------------------------------------------------------------------
initialItem: ColumnLayout{
//anchors.fill: parent
Rectangle{
Layout.fillHeight: true
Layout.fillWidth: true
border.width: 1
border.color: NewConferenceStyle.addressesBorderColor
ColumnLayout {
anchors.fill: parent
anchors.topMargin: 15
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 0
SmartSearchBar {
id: smartSearchBar
Layout.fillWidth: true
Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing
showHeader:false
maxMenuHeight: MainWindowStyle.searchBox.maxHeight
//: 'Select participants' : Placeholder for a search on participant to add them in selection.
placeholderText: qsTr('participantSelectionPlaceholder')
//: 'Search in your contacts or add a custom one to the chat room.'
tooltipText: qsTr('participantSelectionTooltip')
actions:[{
colorSet: NewConferenceStyle.addParticipant,
secure: secureSwitch.checked,
visible: true,
secureIconVisibleHandler : function(entry) {
return UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh)
},
handler: function (entry) {
selectedParticipants.addAddress(entry.sipAddress)
smartSearchBar.addAddressToIgnore(entry.sipAddress);
},
}]
onEntryClicked: {
selectedParticipants.addAddress(entry.sipAddress)
smartSearchBar.addAddressToIgnore(entry.sipAddress);
}
}
Text{
Layout.preferredHeight: 20
Layout.rightMargin: 65
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing
//: 'Admin' : Admin(istrator)
//~ one word for admin status
text : qsTr('adminStatus')
color: NewConferenceStyle.addressesAdminColor
font.pointSize: Units.dp * 11
font.weight: Font.Light
visible: participantView.count > 0
}
ScrollableListViewField {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.bottomMargin: 5
textFieldStyle: TextFieldStyle.unbordered
ParticipantsView {
id: participantView
anchors.fill: parent
showContactAddress:false
showSwitch : conferenceManager.isNew
showSeparator: false
isSelectable: false
showInvitingIndicator: false
function removeParticipant(entry){
smartSearchBar.removeAddressToIgnore(entry.sipAddress)
selectedParticipants.removeModel(entry)
++lastContacts.reloadCount
}
actions: [{
colorSet: NewConferenceStyle.removeParticipant,
secure:0,
visible:true,
//: 'Remove this participant from the selection' : Explanation about removing participant from a selection
//~ Tooltip This is a tooltip
tooltipText: qsTr('removeParticipantSelection'),
handler: function (entry) {
removeParticipant(entry)
}
}]
genSipAddress: ''
model: ParticipantProxyModel {
id:selectedParticipants
chatRoomModel:null
}
onEntryClicked: actions[0].handler(entry)
}
}
}
}
Item{
Layout.fillWidth: true
Layout.preferredHeight: 20
Text{
anchors.fill:parent
textFormat: Text.RichText
//: 'Required' : Word relative to a star to explain that it is a requirement (Field form)
text : '<span style="color:red">*</span> '+qsTr('requiredField')
//font.weight: Font.DemiBold
color: NewConferenceStyle.requiredColor
font.pointSize: Units.dp * 8
}
}
}
//----------------------------------------------------
// STACKVIEWS
//----------------------------------------------------
Component{
id: datePicker
DatePicker{
onClicked: {
dateField.setDate(date)
rightStackView.currentItemType = 0
rightStackView.pop()
}
}
}
Component{
id: timePicker
TimePicker{
onClicked: {
timeField.setTime(date)
rightStackView.currentItemType = 0
rightStackView.pop()
}
}
}
}
}
}