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') +'*' 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'+'*'; 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'+'*'; 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 : '* '+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() } } } } } }