Commit 42faa0a0 authored by Ronan Abhamon's avatar Ronan Abhamon

feat(app): many changes:

  - `ContactEdit` supports emails edition
  - Add a `SmartConnect` component
parent 65dd1940
...@@ -142,6 +142,14 @@ ...@@ -142,6 +142,14 @@
<source>avatarChooserTitle</source> <source>avatarChooserTitle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>companiesInput</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companies</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Contacts</name> <name>Contacts</name>
......
...@@ -134,6 +134,14 @@ ...@@ -134,6 +134,14 @@
<source>avatarChooserTitle</source> <source>avatarChooserTitle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>companiesInput</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companies</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Contacts</name> <name>Contacts</name>
......
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
<file>ui/modules/Common/Popup/PopupShadow.qml</file> <file>ui/modules/Common/Popup/PopupShadow.qml</file>
<file>ui/modules/Common/qmldir</file> <file>ui/modules/Common/qmldir</file>
<file>ui/modules/Common/SearchBox.qml</file> <file>ui/modules/Common/SearchBox.qml</file>
<file>ui/modules/Common/SmartConnect.qml</file>
<file>ui/modules/Common/Styles/Animations/CaterpillarAnimationStyle.qml</file> <file>ui/modules/Common/Styles/Animations/CaterpillarAnimationStyle.qml</file>
<file>ui/modules/Common/Styles/CollapseStyle.qml</file> <file>ui/modules/Common/Styles/CollapseStyle.qml</file>
<file>ui/modules/Common/Styles/DialogStyle.qml</file> <file>ui/modules/Common/Styles/DialogStyle.qml</file>
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "../../app/AvatarProvider.hpp" #include "../../app/AvatarProvider.hpp"
#include "../../app/Database.hpp" #include "../../app/Database.hpp"
#include "../../utils.hpp" #include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "ContactModel.hpp" #include "ContactModel.hpp"
...@@ -34,8 +35,12 @@ void ContactModel::setUsername (const QString &username) { ...@@ -34,8 +35,12 @@ void ContactModel::setUsername (const QString &username) {
if (username.length() == 0 || username == getUsername()) if (username.length() == 0 || username == getUsername())
return; return;
m_linphone_friend->edit();
if (!m_linphone_friend->setName(Utils::qStringToLinphoneString(username))) if (!m_linphone_friend->setName(Utils::qStringToLinphoneString(username)))
emit contactUpdated(); emit contactUpdated();
m_linphone_friend->done();
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -45,15 +50,16 @@ QString ContactModel::getAvatar () const { ...@@ -45,15 +50,16 @@ QString ContactModel::getAvatar () const {
list<shared_ptr<belcard::BelCardPhoto> > photos = list<shared_ptr<belcard::BelCardPhoto> > photos =
m_linphone_friend->getVcard()->getBelcard()->getPhotos(); m_linphone_friend->getVcard()->getBelcard()->getPhotos();
auto it = find_if( auto it = find_if(
photos.begin(), photos.end(), [](const shared_ptr<belcard::BelCardPhoto> &photo) { photos.cbegin(), photos.cend(), [](const shared_ptr<belcard::BelCardPhoto> &photo) {
return !photo->getValue().compare(0, sizeof(VCARD_SCHEME) - 1, VCARD_SCHEME); return !photo->getValue().compare(0, sizeof(VCARD_SCHEME) - 1, VCARD_SCHEME);
} }
); );
// Returns right path. // No path found.
if (it == photos.end()) if (it == photos.cend())
return ""; return "";
// Returns right path.
return QStringLiteral("image://%1/%2") return QStringLiteral("image://%1/%2")
.arg(AvatarProvider::PROVIDER_ID) .arg(AvatarProvider::PROVIDER_ID)
.arg(Utils::linphoneStringToQString( .arg(Utils::linphoneStringToQString(
...@@ -129,8 +135,65 @@ QVariantList ContactModel::getSipAddresses () const { ...@@ -129,8 +135,65 @@ QVariantList ContactModel::getSipAddresses () const {
return list; return list;
} }
void ContactModel::setSipAddresses (const QVariantList &sip_addresses) { void ContactModel::addSipAddress (const QString &sip_address) {
// TODO. shared_ptr<linphone::Address> address =
CoreManager::getInstance()->getCore()->createAddress(
Utils::qStringToLinphoneString(sip_address)
);
if (!address) {
qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address);
return;
}
qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address);
m_linphone_friend->edit();
m_linphone_friend->addAddress(address);
m_linphone_friend->done();
emit contactUpdated();
}
bool ContactModel::removeSipAddress (const QString &sip_address) {
list<shared_ptr<linphone::Address> > addresses = m_linphone_friend->getAddresses();
string match = Utils::qStringToLinphoneString(sip_address);
auto it = find_if(
addresses.cbegin(), addresses.cend(),
[&match](const shared_ptr<linphone::Address> &address) {
return match == address->asString();
}
);
if (it == addresses.cend()) {
qWarning() << QStringLiteral("Unable to found sip address: `%1`.")
.arg(sip_address);
return false;
}
if (addresses.size() == 1) {
qWarning() << QStringLiteral("Unable to remove the only existing sip address: `%1`.")
.arg(sip_address);
return false;
}
qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address);
m_linphone_friend->edit();
m_linphone_friend->removeAddress(*it);
m_linphone_friend->done();
emit contactUpdated();
return true;
}
void ContactModel::updateSipAddress (const QString &old_sip_address, const QString &sip_address) {
if (old_sip_address == sip_address || !removeSipAddress(old_sip_address))
return;
addSipAddress(sip_address);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -144,10 +207,6 @@ QVariantList ContactModel::getCompanies () const { ...@@ -144,10 +207,6 @@ QVariantList ContactModel::getCompanies () const {
return list; return list;
} }
void ContactModel::setCompanies (const QVariantList &companies) {
// TODO.
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
QVariantList ContactModel::getEmails () const { QVariantList ContactModel::getEmails () const {
...@@ -159,8 +218,55 @@ QVariantList ContactModel::getEmails () const { ...@@ -159,8 +218,55 @@ QVariantList ContactModel::getEmails () const {
return list; return list;
} }
void ContactModel::setEmails (const QVariantList &emails) { void ContactModel::addEmail (const QString &email) {
// TODO. shared_ptr<belcard::BelCard> belCard = m_linphone_friend->getVcard()->getBelcard();
shared_ptr<belcard::BelCardEmail> belCardEmail =
belcard::BelCardGeneric::create<belcard::BelCardEmail>();
belCardEmail->setValue(Utils::qStringToLinphoneString(email));
qInfo() << QStringLiteral("Add new email: `%1`.").arg(email);
m_linphone_friend->edit();
belCard->addEmail(belCardEmail);
m_linphone_friend->done();
emit contactUpdated();
}
bool ContactModel::removeEmail (const QString &email) {
shared_ptr<belcard::BelCard> belCard = m_linphone_friend->getVcard()->getBelcard();
list<shared_ptr<belcard::BelCardEmail> > emails = belCard->getEmails();
string match = Utils::qStringToLinphoneString(email);
auto it = find_if(
emails.cbegin(), emails.cend(),
[&match](const shared_ptr<belcard::BelCardEmail> &email) {
return match == email->getValue();
}
);
if (it == emails.cend()) {
qWarning() << QStringLiteral("Unable to remove email: `%1`.")
.arg(email);
return false;
}
qInfo() << QStringLiteral("Remove email: `%1`.").arg(email);
m_linphone_friend->edit();
belCard->removeEmail(*it);
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;
addEmail(email);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -174,20 +280,12 @@ QVariantList ContactModel::getUrls () const { ...@@ -174,20 +280,12 @@ QVariantList ContactModel::getUrls () const {
return list; return list;
} }
void ContactModel::setUrls (const QVariantList &urls) {
// TODO.
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
QList<QVariantMap> ContactModel::getAddresses () const { QList<QVariantMap> ContactModel::getAddresses () const {
} }
void ContactModel::setAddresses (const QList<QVariantMap> &addresses) {
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
Presence::PresenceStatus ContactModel::getPresenceStatus () const { Presence::PresenceStatus ContactModel::getPresenceStatus () const {
......
...@@ -31,35 +31,30 @@ class ContactModel : public QObject { ...@@ -31,35 +31,30 @@ class ContactModel : public QObject {
Q_PROPERTY( Q_PROPERTY(
QVariantList sipAddresses QVariantList sipAddresses
READ getSipAddresses READ getSipAddresses
WRITE setSipAddresses
NOTIFY contactUpdated NOTIFY contactUpdated
); );
Q_PROPERTY( Q_PROPERTY(
QVariantList companies QVariantList companies
READ getCompanies READ getCompanies
WRITE setCompanies
NOTIFY contactUpdated NOTIFY contactUpdated
); );
Q_PROPERTY( Q_PROPERTY(
QVariantList emails QVariantList emails
READ getEmails READ getEmails
WRITE setEmails
NOTIFY contactUpdated NOTIFY contactUpdated
); );
Q_PROPERTY( Q_PROPERTY(
QVariantList urls QVariantList urls
READ getUrls READ getUrls
WRITE setUrls
NOTIFY contactUpdated NOTIFY contactUpdated
); );
Q_PROPERTY( Q_PROPERTY(
QList<QVariantMap> addresses QList<QVariantMap> addresses
READ getAddresses READ getAddresses
WRITE setAddresses
NOTIFY contactUpdated NOTIFY contactUpdated
); );
...@@ -84,6 +79,15 @@ class ContactModel : public QObject { ...@@ -84,6 +79,15 @@ class ContactModel : public QObject {
public: public:
ContactModel (std::shared_ptr<linphone::Friend> linphone_friend); ContactModel (std::shared_ptr<linphone::Friend> 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);
void addEmail (const QString &email);
bool removeEmail (const QString &email);
void updateEmail (const QString &old_email, const QString &email);
signals: signals:
void contactUpdated (); void contactUpdated ();
...@@ -95,13 +99,11 @@ private: ...@@ -95,13 +99,11 @@ private:
void setAvatar (const QString &path); void setAvatar (const QString &path);
QVariantList getSipAddresses () const; QVariantList getSipAddresses () const;
void setSipAddresses (const QVariantList &sip_addresses);
QVariantList getCompanies () const; QVariantList getCompanies () const;
void setCompanies (const QVariantList &companies); void setCompanies (const QVariantList &companies);
QVariantList getEmails () const; QVariantList getEmails () const;
void setEmails (const QVariantList &emails);
QVariantList getUrls () const; QVariantList getUrls () const;
void setUrls (const QVariantList &urls); void setUrls (const QVariantList &urls);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class ContactModel;
class ContactsListModel; class ContactsListModel;
// =================================================================== // ===================================================================
......
...@@ -14,8 +14,20 @@ RowLayout { ...@@ -14,8 +14,20 @@ RowLayout {
property alias title: text.text property alias title: text.text
property var defaultData: [] property var defaultData: []
signal changed (int id, string default_value, string new_value)
signal removed (int id, string value)
// ----------------------------------------------------------------- // -----------------------------------------------------------------
function setData (data) {
var model = values.model
model.clear()
data.forEach(function (data) {
model.append({ $value: data })
})
}
function _addValue (value) { function _addValue (value) {
values.model.append({ $value: value }) values.model.append({ $value: value })
...@@ -26,9 +38,15 @@ RowLayout { ...@@ -26,9 +38,15 @@ RowLayout {
function _handleEditionFinished (index, text) { function _handleEditionFinished (index, text) {
if (text.length === 0) { if (text.length === 0) {
var default_value = values.model.get(index).$value
values.model.remove(index) values.model.remove(index)
if (default_value.length !== 0) {
listForm.removed(index, default_value)
}
} else { } else {
values.model.set(index, { $value: text }) var default_value = values.model.get(index).$value
listForm.changed(index, default_value, text)
} }
addButton.enabled = true addButton.enabled = true
...@@ -148,9 +166,7 @@ RowLayout { ...@@ -148,9 +166,7 @@ RowLayout {
return return
} }
defaultData.forEach(function (data) { setData(defaultData)
model.append({ $value: data })
})
} }
} }
} }
...@@ -4,7 +4,7 @@ import Common 1.0 ...@@ -4,7 +4,7 @@ import Common 1.0
import Common.Styles 1.0 import Common.Styles 1.0
// =================================================================== // ===================================================================
// A editable text that become the content of a box on focus. // A editable text that has a background color on focus.
// =================================================================== // ===================================================================
Item { Item {
...@@ -35,6 +35,13 @@ Item { ...@@ -35,6 +35,13 @@ Item {
var width = textInput.contentWidth + parent.padding * 2 var width = textInput.contentWidth + parent.padding * 2
return width < parent.width ? width : parent.width return width < parent.width ? width : parent.width
} }
InvertedMouseArea {
anchors.fill: parent
enabled: textInput.activeFocus
onPressed: textInput.focus = false
}
} }
TextInput { TextInput {
...@@ -58,11 +65,5 @@ Item { ...@@ -58,11 +65,5 @@ Item {
cursorPosition = 0 cursorPosition = 0
parent.editingFinished() parent.editingFinished()
} }
InvertedMouseArea {
anchors.fill: parent
enabled: textInput.activeFocus
onPressed: textInput.focus = false
}
} }
} }
import QtQuick 2.7
import Utils 1.0
// ===================================================================
Item {
property bool _connected: false
function connect (emitter, signalName, handler) {
Utils.assert(!_connected, 'Smart connect is already connected!')
emitter[signalName].connect(handler)
_connected = true
Component.onDestruction.connect(function () {
emitter[signalName].disconnect(handler)
})
}
}
...@@ -68,6 +68,9 @@ PopupShadow 1.0 Popup/PopupShadow.qml ...@@ -68,6 +68,9 @@ PopupShadow 1.0 Popup/PopupShadow.qml
# SearchBox # SearchBox
SearchBox 1.0 SearchBox.qml SearchBox 1.0 SearchBox.qml
# SmartConnect
SmartConnect 1.0 SmartConnect.qml
# Tooltip # Tooltip
TooltipArea 1.0 Tooltip/TooltipArea.qml TooltipArea 1.0 Tooltip/TooltipArea.qml
......
...@@ -17,9 +17,7 @@ ColumnLayout { ...@@ -17,9 +17,7 @@ ColumnLayout {
property string sipAddress: '' property string sipAddress: ''
property var _contact: ContactsListModel.mapSipAddressToContact( property var _contact
sipAddress
) || sipAddress
// ----------------------------------------------------------------- // -----------------------------------------------------------------
...@@ -37,21 +35,43 @@ ColumnLayout { ...@@ -37,21 +35,43 @@ ColumnLayout {
} }
function _setAvatar (path) { function _setAvatar (path) {
if (!path) { if (Utils.isObject(_contact) && path) {
return _contact.avatar = path.match(/^(?:file:\/\/)?(.*)$/)[1]
} }
}
function _setUsername (username) {
if (Utils.isObject(_contact)) { if (Utils.isObject(_contact)) {
_contact.avatar = path.match(/^(?:file:\/\/)?(.*)$/)[1] _contact.username = username
}
// TODO: Not registered contact. // Update current text with new username.
usernameInput.text = _contact.username
}
} }
// ----------------------------------------------------------------- // -----------------------------------------------------------------
spacing: 0 spacing: 0
Component.onCompleted: {
var contact = ContactsListModel.mapSipAddressToContact(sipAddress)
if (contact) {
infoUpdater.connect(contact, 'onContactUpdated', function () {
addresses.setData(contact.sipAddresses)
companies.setData(contact.companies)
emails.setData(contact.emails)
urls.setData(contact.urls)
})
_contact = contact
} else {
_contact = sipAddress
}
}
// -----------------------------------------------------------------
FileDialog { FileDialog {
id: avatarChooser id: avatarChooser
...@@ -89,15 +109,14 @@ ColumnLayout { ...@@ -89,15 +109,14 @@ ColumnLayout {
id: avatar id: avatar
anchors.fill: parent anchors.fill: parent
image: _contact.avatar image: _contact.avatar
username: LinphoneUtils.getContactUsername(_contact) username: LinphoneUtils.getContactUsername(_contact) || 'John Doe'
visible: isLoaded() && !parent.hovered visible: isLoaded() && !parent.hovered
} }
} }
TransparentTextInput { TransparentTextInput {
id: editUsername id: usernameInput
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: ContactEditStyle.infoBar.buttons.size Layout.preferredHeight: ContactEditStyle.infoBar.buttons.size
...@@ -111,12 +130,7 @@ ColumnLayout { ...@@ -111,12 +130,7 @@ ColumnLayout {
text: avatar.username text: avatar.username
onEditingFinished: { onEditingFinished: _setUsername(text)
_contact.username = text
// Update current text with new username.
text = _contact.username
}
} }
ActionBar { ActionBar {
...@@ -157,28 +171,56 @@ ColumnLayout { ...@@ -157,28 +171,56 @@ ColumnLayout {
contentWidth: width - ScrollBar.vertical.width - leftMargin - rightMargin contentWidth: width - ScrollBar.vertical.width - leftMargin - rightMargin
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
leftMargin: 40 leftMargin: ContactEditStyle.values.leftMargin
rightMargin: 20 rightMargin: ContactEditStyle.values.rightMargin
topMargin: 40 topMargin: ContactEditStyle.values.topMargin
ColumnLayout { ColumnLayout {
id: infoList id: infoList
width: flick.contentWidth width: flick.contentWidth
SmartConnect {
id: infoUpdater
}
ListForm { ListForm {
id: addresses
defaultData: _contact.sipAddresses defaultData: _contact.sipAddresses
placeholder: qsTr('sipAccountsInput') placeholder: qsTr('sipAccountsInput')
title: qsTr('sipAccounts') title: qsTr('sipAccounts')
onChanged: default_value.length === 0
? _contact.addSipAddress(new_value)
: _contact.updateSipAddress(default_value, new_value)
onRemoved: _contact.removeSipAddress(value)
} }
ListForm { ListForm {
id: companies
defaultData: _contact.companies
placeholder: qsTr('companiesInput')
title: qsTr('companies')
}
ListForm {
id: emails
defaultData: _contact.emails defaultData: _contact.emails
placeholder: qsTr('emailsInput') placeholder: qsTr('emailsInput')
title: qsTr('emails') title: qsTr('emails')
onChanged: default_value.length === 0
? _contact.addEmail(new_value)
: _contact.updateEmail(default_value, new_value)
onRemoved: _contact.removeEmail(value)
} }
ListForm { ListForm {
id: urls
defaultData: _contact.urls defaultData: _contact.urls
placeholder: qsTr('webSitesInput') placeholder: qsTr('webSitesInput')
title: qsTr('webSites') title: qsTr('webSites')
......
...@@ -24,4 +24,10 @@ QtObject { ...@@ -24,4 +24,10 @@ QtObject {
property int fontSize: 13 property int fontSize: 13
} }
} }
property QtObject values: QtObject {
property int leftMargin: 40
property int rightMargin: 20
property int topMargin: 40
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment