diff --git a/tests/assets/images/generic_error.svg b/tests/assets/images/generic_error.svg new file mode 100644 index 000000000..1a9838722 --- /dev/null +++ b/tests/assets/images/generic_error.svg @@ -0,0 +1,14 @@ + + + + chat_error + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/languages/en.ts b/tests/assets/languages/en.ts index 15241168c..9f9b8563b 100644 --- a/tests/assets/languages/en.ts +++ b/tests/assets/languages/en.ts @@ -144,11 +144,19 @@ companiesInput - + Company companies - + COMPANIES + + + save + SAVE + + + cancel + CANCEL diff --git a/tests/assets/languages/fr.ts b/tests/assets/languages/fr.ts index b43aa3256..3f06d9d8c 100644 --- a/tests/assets/languages/fr.ts +++ b/tests/assets/languages/fr.ts @@ -142,6 +142,14 @@ companies + + save + + + + cancel + ANNULER + Contacts diff --git a/tests/resources.qrc b/tests/resources.qrc index 6fb7514eb..e32ddb67e 100644 --- a/tests/resources.qrc +++ b/tests/resources.qrc @@ -58,6 +58,7 @@ assets/images/fullscreen_hovered.svg assets/images/fullscreen_normal.svg assets/images/fullscreen_pressed.svg + assets/images/generic_error.svg assets/images/hangup_hovered.svg assets/images/hangup_normal.svg assets/images/hangup_pressed.svg diff --git a/tests/src/components/contacts/ContactModel.cpp b/tests/src/components/contacts/ContactModel.cpp index 743f74e3d..c833ef9c3 100644 --- a/tests/src/components/contacts/ContactModel.cpp +++ b/tests/src/components/contacts/ContactModel.cpp @@ -154,7 +154,7 @@ QVariantList ContactModel::getSipAddresses () const { return list; } -void ContactModel::addSipAddress (const QString &sip_address) { +bool ContactModel::addSipAddress (const QString &sip_address) { shared_ptr address = CoreManager::getInstance()->getCore()->createAddress( Utils::qStringToLinphoneString(sip_address) @@ -162,7 +162,7 @@ void ContactModel::addSipAddress (const QString &sip_address) { if (!address) { qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address); - return; + return false; } qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address); @@ -172,9 +172,11 @@ void ContactModel::addSipAddress (const QString &sip_address) { m_linphone_friend->done(); emit contactUpdated(); + + return true; } -bool ContactModel::removeSipAddress (const QString &sip_address) { +void ContactModel::removeSipAddress (const QString &sip_address) { list > addresses = m_linphone_friend->getAddresses(); string match = Utils::qStringToLinphoneString(sip_address); @@ -188,13 +190,13 @@ bool ContactModel::removeSipAddress (const QString &sip_address) { if (it == addresses.cend()) { qWarning() << QStringLiteral("Unable to found sip address: `%1`.") .arg(sip_address); - return false; + return; } if (addresses.size() == 1) { qWarning() << QStringLiteral("Unable to remove the only existing sip address: `%1`.") .arg(sip_address); - return false; + return; } qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address); @@ -205,14 +207,16 @@ bool ContactModel::removeSipAddress (const QString &sip_address) { emit contactUpdated(); - return true; + return; } -void ContactModel::updateSipAddress (const QString &old_sip_address, const QString &sip_address) { - if (old_sip_address == sip_address || !removeSipAddress(old_sip_address)) - return; +bool ContactModel::updateSipAddress (const QString &old_sip_address, const QString &sip_address) { + if (old_sip_address == sip_address || !addSipAddress(sip_address)) + return false; - addSipAddress(sip_address); + removeSipAddress(old_sip_address); + + return true; } // ------------------------------------------------------------------- @@ -241,13 +245,13 @@ void ContactModel::addCompany (const QString &company) { emit contactUpdated(); } -bool ContactModel::removeCompany (const QString &company) { +void ContactModel::removeCompany (const QString &company) { shared_ptr belCard = m_linphone_friend->getVcard()->getBelcard(); shared_ptr value = findBelCardValue(belCard->getRoles(), company); if (!value) { qWarning() << QStringLiteral("Unable to remove company: `%1`.").arg(company); - return false; + return; } qInfo() << QStringLiteral("Remove company: `%1`.").arg(company); @@ -257,15 +261,14 @@ bool ContactModel::removeCompany (const QString &company) { m_linphone_friend->done(); emit contactUpdated(); - - return true; } void ContactModel::updateCompany (const QString &old_company, const QString &company) { - if (old_company == company || !removeCompany(old_company)) + if (old_company == company) return; addCompany(company); + removeCompany(old_company); } // ------------------------------------------------------------------- @@ -279,7 +282,7 @@ QVariantList ContactModel::getEmails () const { return list; } -void ContactModel::addEmail (const QString &email) { +bool ContactModel::addEmail (const QString &email) { shared_ptr belCard = m_linphone_friend->getVcard()->getBelcard(); shared_ptr value = belcard::BelCardGeneric::create(); @@ -292,15 +295,18 @@ void ContactModel::addEmail (const QString &email) { m_linphone_friend->done(); emit contactUpdated(); + + // TODO: Check if email is valid. + return true; } -bool ContactModel::removeEmail (const QString &email) { +void ContactModel::removeEmail (const QString &email) { shared_ptr belCard = m_linphone_friend->getVcard()->getBelcard(); shared_ptr value = findBelCardValue(belCard->getEmails(), email); if (!value) { qWarning() << QStringLiteral("Unable to remove email: `%1`.").arg(email); - return false; + return; } qInfo() << QStringLiteral("Remove email: `%1`.").arg(email); @@ -310,15 +316,15 @@ bool ContactModel::removeEmail (const QString &email) { m_linphone_friend->done(); emit contactUpdated(); - - return true; } -void ContactModel::updateEmail (const QString &old_email, const QString &email) { - if (old_email == email || !removeEmail(old_email)) - return; +bool ContactModel::updateEmail (const QString &old_email, const QString &email) { + if (old_email == email || !addEmail(email)) + return false; - addEmail(email); + removeEmail(old_email); + + return true; } // ------------------------------------------------------------------- @@ -332,7 +338,7 @@ QVariantList ContactModel::getUrls () const { return list; } -void ContactModel::addUrl (const QString &url) { +bool ContactModel::addUrl (const QString &url) { shared_ptr belCard = m_linphone_friend->getVcard()->getBelcard(); shared_ptr value = belcard::BelCardGeneric::create(); @@ -345,15 +351,18 @@ void ContactModel::addUrl (const QString &url) { m_linphone_friend->done(); emit contactUpdated(); + + // TODO: Check if url is valid. + return true; } -bool ContactModel::removeUrl (const QString &url) { +void ContactModel::removeUrl (const QString &url) { shared_ptr belCard = m_linphone_friend->getVcard()->getBelcard(); shared_ptr value = findBelCardValue(belCard->getURLs(), url); if (!value) { qWarning() << QStringLiteral("Unable to remove url: `%1`.").arg(url); - return false; + return; } qInfo() << QStringLiteral("Remove url: `%1`.").arg(url); @@ -363,20 +372,25 @@ bool ContactModel::removeUrl (const QString &url) { m_linphone_friend->done(); emit contactUpdated(); +} + +bool ContactModel::updateUrl (const QString &old_url, const QString &url) { + if (old_url == url || !addUrl(url)) + return false; + + removeUrl(old_url); return true; } -void ContactModel::updateUrl (const QString &old_url, const QString &url) { - if (old_url == url || !removeUrl(old_url)) - return; - - addUrl(url); -} - // ------------------------------------------------------------------- -QList ContactModel::getAddresses () const { +QVariantMap ContactModel::getAddress () const { + +} + +void ContactModel::setAddress (const QVariantMap &address) { + } diff --git a/tests/src/components/contacts/ContactModel.hpp b/tests/src/components/contacts/ContactModel.hpp index f337673ee..d6adbde6d 100644 --- a/tests/src/components/contacts/ContactModel.hpp +++ b/tests/src/components/contacts/ContactModel.hpp @@ -20,12 +20,7 @@ class ContactModel : public QObject { Q_PROPERTY(QVariantList companies READ getCompanies NOTIFY contactUpdated); Q_PROPERTY(QVariantList emails READ getEmails NOTIFY contactUpdated); Q_PROPERTY(QVariantList urls READ getUrls NOTIFY contactUpdated); - - Q_PROPERTY( - QList addresses - READ getAddresses - NOTIFY contactUpdated - ); + Q_PROPERTY(QVariantMap address READ getAddress WRITE setAddress NOTIFY contactUpdated); Q_PROPERTY( Presence::PresenceStatus presenceStatus @@ -49,21 +44,21 @@ public: ContactModel (std::shared_ptr linphone_friend); public slots: - void addSipAddress (const QString &sip_address); - bool removeSipAddress (const QString &sip_address); - void updateSipAddress (const QString &old_sip_address, const QString &sip_address); + bool addSipAddress (const QString &sip_address); + void removeSipAddress (const QString &sip_address); + bool updateSipAddress (const QString &old_sip_address, const QString &sip_address); void addCompany (const QString &company); - bool removeCompany (const QString &company); + void removeCompany (const QString &company); void updateCompany (const QString &old_company, const QString &company); - void addEmail (const QString &email); - bool removeEmail (const QString &email); - void updateEmail (const QString &old_email, const QString &email); + bool addEmail (const QString &email); + void removeEmail (const QString &email); + bool updateEmail (const QString &old_email, const QString &email); - void addUrl (const QString &url); - bool removeUrl (const QString &url); - void updateUrl (const QString &old_url, const QString &url); + bool addUrl (const QString &url); + void removeUrl (const QString &url); + bool updateUrl (const QString &old_url, const QString &url); signals: void contactUpdated (); @@ -80,12 +75,13 @@ private: QVariantList getEmails () const; QVariantList getUrls () const; - QList getAddresses () const; - void setAddresses (const QList &addresses); + QVariantMap getAddress () const; + void setAddress (const QVariantMap &address); Presence::PresenceStatus getPresenceStatus () const; Presence::PresenceLevel getPresenceLevel () const; + // TODO: Remove!!! QString getSipAddress () const; Presence::PresenceStatus m_presence_status = Presence::Offline; diff --git a/tests/ui/modules/Common/Form/ListForm.qml b/tests/ui/modules/Common/Form/ListForm.qml index 07f0248d8..211094810 100644 --- a/tests/ui/modules/Common/Form/ListForm.qml +++ b/tests/ui/modules/Common/Form/ListForm.qml @@ -14,22 +14,31 @@ RowLayout { property alias title: text.text property var defaultData: [] - signal changed (int id, string default_value, string new_value) - signal removed (int id, string value) + signal changed (int index, string default_value, string new_value) + signal removed (int index, string value) // ----------------------------------------------------------------- + function setInvalid (index, status) { + Utils.assert( + index >= 0 && index < values.model.count, + 'Index ' + index + 'not exists.' + ) + + values.model.setProperty(index, '$isInvalid', status) + } + function setData (data) { var model = values.model model.clear() data.forEach(function (data) { - model.append({ $value: data }) + model.append({ $value: data, $isInvalid: false }) }) } function _addValue (value) { - values.model.append({ $value: value }) + values.model.append({ $value: value, $isInvalid: false }) if (value.length === 0) { addButton.enabled = false @@ -46,7 +55,10 @@ RowLayout { } } else { var default_value = values.model.get(index).$value - listForm.changed(index, default_value, text) + + if (text !== default_value) { + listForm.changed(index, default_value, text) + } } addButton.enabled = true @@ -130,12 +142,14 @@ RowLayout { implicitHeight: textInput.height width: parent.width - TransparentTextInput { + TransparentTextInput { id: textInput + isInvalid: $isInvalid text: $value - width: parent.width + height: ListFormStyle.lineHeight + width: parent.width onEditingFinished: _handleEditionFinished(index, text) } diff --git a/tests/ui/modules/Common/Form/TransparentTextInput.qml b/tests/ui/modules/Common/Form/TransparentTextInput.qml index c6aa53be3..77c793805 100644 --- a/tests/ui/modules/Common/Form/TransparentTextInput.qml +++ b/tests/ui/modules/Common/Form/TransparentTextInput.qml @@ -12,6 +12,7 @@ Item { property alias font: textInput.font property alias readOnly: textInput.readOnly property alias text: textInput.text + property bool isInvalid: false property int padding: TransparentTextInputStyle.padding signal editingFinished @@ -25,6 +26,8 @@ Item { } Rectangle { + id: background + color: textInput.activeFocus && !textInput.readOnly ? TransparentTextInputStyle.backgroundColor : // No Style constant, see component name. @@ -44,6 +47,16 @@ Item { } } + Icon { + id: icon + + anchors.left: background.right + height: background.height + icon: 'generic_error' + iconSize: 12 + visible: parent.isInvalid + } + TextInput { id: textInput diff --git a/tests/ui/modules/Common/SmartConnect.qml b/tests/ui/modules/Common/SmartConnect.qml index bd9fa3a4e..6869c9b33 100644 --- a/tests/ui/modules/Common/SmartConnect.qml +++ b/tests/ui/modules/Common/SmartConnect.qml @@ -8,7 +8,7 @@ Item { property bool _connected: false function connect (emitter, signalName, handler) { - Utils.assert(!_connected, 'Smart connect is already connected!') + Utils.assert(!_connected, 'SmartConnect is already connected!') emitter[signalName].connect(handler) _connected = true diff --git a/tests/ui/scripts/Utils/utils.js b/tests/ui/scripts/Utils/utils.js index b8e35f26a..c8e59722a 100644 --- a/tests/ui/scripts/Utils/utils.js +++ b/tests/ui/scripts/Utils/utils.js @@ -74,6 +74,13 @@ function encodeTextToQmlRichFormat (text, options) { return images.concat('

' + text + '

') } +function extractFirstUri (str) { + var res = str.match(UriTools.URI_REGEX) + return res == null || startsWith(res[0], 'www') + ? undefined + : res[0] +} + // ------------------------------------------------------------------- // Returns the top (root) parent of one object. diff --git a/tests/ui/views/App/MainWindow/ContactEdit.qml b/tests/ui/views/App/MainWindow/ContactEdit.qml index 3208c0849..a2757794a 100644 --- a/tests/ui/views/App/MainWindow/ContactEdit.qml +++ b/tests/ui/views/App/MainWindow/ContactEdit.qml @@ -49,6 +49,39 @@ ColumnLayout { } } + function _handleSipAddressChanged (index, default_value, new_value) { + if (!Utils.startsWith(new_value, 'sip:')) { + new_value = 'sip:' + new_value + + if (new_value === default_value) { + return + } + } + + var so_far_so_good = (default_value.length === 0) + ? _contact.addSipAddress(new_value) + : _contact.updateSipAddress(default_value, new_value) + + if (!so_far_so_good) { + addresses.setInvalid(index, true) + } + } + + function _handleUrlChanged (index, default_value, new_value) { + var url = Utils.extractFirstUri(new_value) + if (url === default_value) { + return + } + + var so_far_so_good = (default_value.length === 0) + ? url && _contact.addUrl(new_value) + : url && _contact.updateUrl(default_value, new_value) + + if (!so_far_so_good) { + urls.setInvalid(index, true) + } + } + // ----------------------------------------------------------------- spacing: 0 @@ -168,13 +201,9 @@ ColumnLayout { boundsBehavior: Flickable.StopAtBounds clip: true contentHeight: infoList.height - contentWidth: width - ScrollBar.vertical.width - leftMargin - rightMargin + contentWidth: width - ScrollBar.vertical.width flickableDirection: Flickable.VerticalFlick - leftMargin: ContactEditStyle.values.leftMargin - rightMargin: ContactEditStyle.values.rightMargin - topMargin: ContactEditStyle.values.topMargin - ColumnLayout { id: infoList @@ -187,19 +216,30 @@ ColumnLayout { ListForm { id: addresses + Layout.leftMargin: ContactEditStyle.values.leftMargin + Layout.rightMargin: ContactEditStyle.values.rightMargin + Layout.topMargin: ContactEditStyle.values.topMargin + defaultData: _contact.sipAddresses placeholder: qsTr('sipAccountsInput') title: qsTr('sipAccounts') - onChanged: default_value.length === 0 - ? _contact.addSipAddress(new_value) - : _contact.updateSipAddress(default_value, new_value) + onChanged: _handleSipAddressChanged(index, default_value, new_value) onRemoved: _contact.removeSipAddress(value) } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: ContactEditStyle.values.separator.height + color: ContactEditStyle.values.separator.color + } + ListForm { id: companies + Layout.leftMargin: ContactEditStyle.values.leftMargin + Layout.rightMargin: ContactEditStyle.values.rightMargin + defaultData: _contact.companies placeholder: qsTr('companiesInput') title: qsTr('companies') @@ -210,9 +250,18 @@ ColumnLayout { onRemoved: _contact.removeCompany(value) } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: ContactEditStyle.values.separator.height + color: ContactEditStyle.values.separator.color + } + ListForm { id: emails + Layout.leftMargin: ContactEditStyle.values.leftMargin + Layout.rightMargin: ContactEditStyle.values.rightMargin + defaultData: _contact.emails placeholder: qsTr('emailsInput') title: qsTr('emails') @@ -223,23 +272,52 @@ ColumnLayout { onRemoved: _contact.removeEmail(value) } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: ContactEditStyle.values.separator.height + color: ContactEditStyle.values.separator.color + } + ListForm { id: urls + Layout.leftMargin: ContactEditStyle.values.leftMargin + Layout.rightMargin: ContactEditStyle.values.rightMargin + defaultData: _contact.urls placeholder: qsTr('webSitesInput') title: qsTr('webSites') - onChanged: default_value.length === 0 - ? _contact.addUrl(new_value) - : _contact.updateUrl(default_value, new_value) + onChanged: _handleUrlChanged(index, default_value, new_value) onRemoved: _contact.removeUrl(value) } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: ContactEditStyle.values.separator.height + color: ContactEditStyle.values.separator.color + } + + Loader { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: ContactEditStyle.buttons.topMargin + + sourceComponent: Row { + spacing: ContactEditStyle.buttons.spacing + + TextButtonB { + text: qsTr('save') + } + + TextButtonA { + text: qsTr('cancel') + } + } + } + + Item { + Layout.bottomMargin: ContactEditStyle.values.bottomMargin + } } } } - -/* ListForm { */ -/* title: qsTr('address') */ -/* placeholder: qsTr('addressInput') */ -/* } */ diff --git a/tests/ui/views/App/Styles/MainWindow/ContactEditStyle.qml b/tests/ui/views/App/Styles/MainWindow/ContactEditStyle.qml index 1f2ce88cc..9fbbc29d2 100644 --- a/tests/ui/views/App/Styles/MainWindow/ContactEditStyle.qml +++ b/tests/ui/views/App/Styles/MainWindow/ContactEditStyle.qml @@ -25,9 +25,20 @@ QtObject { } } + property QtObject buttons: QtObject { + property int spacing: 20 + property int topMargin: 20 + } + property QtObject values: QtObject { + property int bottomMargin: 20 property int leftMargin: 40 property int rightMargin: 20 - property int topMargin: 40 + property int topMargin: 20 + + property QtObject separator: QtObject { + property color color: '#E8E8E8' + property int height: 1 + } } }