feat(app): many changes:

- `ContactEdit` supports emails edition
  - Add a `SmartConnect` component
This commit is contained in:
Ronan Abhamon 2016-12-09 11:57:36 +01:00
parent 65dd1940c0
commit 42faa0a03b
12 changed files with 264 additions and 58 deletions

View file

@ -142,6 +142,14 @@
<source>avatarChooserTitle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companiesInput</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companies</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Contacts</name>

View file

@ -134,6 +134,14 @@
<source>avatarChooserTitle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companiesInput</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>companies</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Contacts</name>

View file

@ -157,6 +157,7 @@
<file>ui/modules/Common/Popup/PopupShadow.qml</file>
<file>ui/modules/Common/qmldir</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/CollapseStyle.qml</file>
<file>ui/modules/Common/Styles/DialogStyle.qml</file>

View file

@ -8,6 +8,7 @@
#include "../../app/AvatarProvider.hpp"
#include "../../app/Database.hpp"
#include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "ContactModel.hpp"
@ -34,8 +35,12 @@ void ContactModel::setUsername (const QString &username) {
if (username.length() == 0 || username == getUsername())
return;
m_linphone_friend->edit();
if (!m_linphone_friend->setName(Utils::qStringToLinphoneString(username)))
emit contactUpdated();
m_linphone_friend->done();
}
// -------------------------------------------------------------------
@ -45,15 +50,16 @@ QString ContactModel::getAvatar () const {
list<shared_ptr<belcard::BelCardPhoto> > photos =
m_linphone_friend->getVcard()->getBelcard()->getPhotos();
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);
}
);
// Returns right path.
if (it == photos.end())
// No path found.
if (it == photos.cend())
return "";
// Returns right path.
return QStringLiteral("image://%1/%2")
.arg(AvatarProvider::PROVIDER_ID)
.arg(Utils::linphoneStringToQString(
@ -129,8 +135,65 @@ QVariantList ContactModel::getSipAddresses () const {
return list;
}
void ContactModel::setSipAddresses (const QVariantList &sip_addresses) {
// TODO.
void ContactModel::addSipAddress (const QString &sip_address) {
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 {
return list;
}
void ContactModel::setCompanies (const QVariantList &companies) {
// TODO.
}
// -------------------------------------------------------------------
QVariantList ContactModel::getEmails () const {
@ -159,8 +218,55 @@ QVariantList ContactModel::getEmails () const {
return list;
}
void ContactModel::setEmails (const QVariantList &emails) {
// TODO.
void ContactModel::addEmail (const QString &email) {
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 {
return list;
}
void ContactModel::setUrls (const QVariantList &urls) {
// TODO.
}
// -------------------------------------------------------------------
QList<QVariantMap> ContactModel::getAddresses () const {
}
void ContactModel::setAddresses (const QList<QVariantMap> &addresses) {
}
// -------------------------------------------------------------------
Presence::PresenceStatus ContactModel::getPresenceStatus () const {

View file

@ -31,35 +31,30 @@ class ContactModel : public QObject {
Q_PROPERTY(
QVariantList sipAddresses
READ getSipAddresses
WRITE setSipAddresses
NOTIFY contactUpdated
);
Q_PROPERTY(
QVariantList companies
READ getCompanies
WRITE setCompanies
NOTIFY contactUpdated
);
Q_PROPERTY(
QVariantList emails
READ getEmails
WRITE setEmails
NOTIFY contactUpdated
);
Q_PROPERTY(
QVariantList urls
READ getUrls
WRITE setUrls
NOTIFY contactUpdated
);
Q_PROPERTY(
QList<QVariantMap> addresses
READ getAddresses
WRITE setAddresses
NOTIFY contactUpdated
);
@ -84,6 +79,15 @@ class ContactModel : public QObject {
public:
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:
void contactUpdated ();
@ -95,13 +99,11 @@ private:
void setAvatar (const QString &path);
QVariantList getSipAddresses () const;
void setSipAddresses (const QVariantList &sip_addresses);
QVariantList getCompanies () const;
void setCompanies (const QVariantList &companies);
QVariantList getEmails () const;
void setEmails (const QVariantList &emails);
QVariantList getUrls () const;
void setUrls (const QVariantList &urls);

View file

@ -3,6 +3,7 @@
#include <QSortFilterProxyModel>
class ContactModel;
class ContactsListModel;
// ===================================================================

View file

@ -14,8 +14,20 @@ 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)
// -----------------------------------------------------------------
function setData (data) {
var model = values.model
model.clear()
data.forEach(function (data) {
model.append({ $value: data })
})
}
function _addValue (value) {
values.model.append({ $value: value })
@ -26,9 +38,15 @@ RowLayout {
function _handleEditionFinished (index, text) {
if (text.length === 0) {
var default_value = values.model.get(index).$value
values.model.remove(index)
if (default_value.length !== 0) {
listForm.removed(index, default_value)
}
} else {
values.model.set(index, { $value: text })
var default_value = values.model.get(index).$value
listForm.changed(index, default_value, text)
}
addButton.enabled = true
@ -148,9 +166,7 @@ RowLayout {
return
}
defaultData.forEach(function (data) {
model.append({ $value: data })
})
setData(defaultData)
}
}
}

View file

@ -4,7 +4,7 @@ import Common 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 {
@ -35,6 +35,13 @@ Item {
var width = textInput.contentWidth + parent.padding * 2
return width < parent.width ? width : parent.width
}
InvertedMouseArea {
anchors.fill: parent
enabled: textInput.activeFocus
onPressed: textInput.focus = false
}
}
TextInput {
@ -58,11 +65,5 @@ Item {
cursorPosition = 0
parent.editingFinished()
}
InvertedMouseArea {
anchors.fill: parent
enabled: textInput.activeFocus
onPressed: textInput.focus = false
}
}
}

View file

@ -0,0 +1,20 @@
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)
})
}
}

View file

@ -68,6 +68,9 @@ PopupShadow 1.0 Popup/PopupShadow.qml
# SearchBox
SearchBox 1.0 SearchBox.qml
# SmartConnect
SmartConnect 1.0 SmartConnect.qml
# Tooltip
TooltipArea 1.0 Tooltip/TooltipArea.qml

View file

@ -17,9 +17,7 @@ ColumnLayout {
property string sipAddress: ''
property var _contact: ContactsListModel.mapSipAddressToContact(
sipAddress
) || sipAddress
property var _contact
// -----------------------------------------------------------------
@ -37,21 +35,43 @@ ColumnLayout {
}
function _setAvatar (path) {
if (!path) {
return
}
if (Utils.isObject(_contact)) {
if (Utils.isObject(_contact) && path) {
_contact.avatar = path.match(/^(?:file:\/\/)?(.*)$/)[1]
}
}
// TODO: Not registered contact.
function _setUsername (username) {
if (Utils.isObject(_contact)) {
_contact.username = username
// Update current text with new username.
usernameInput.text = _contact.username
}
}
// -----------------------------------------------------------------
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 {
id: avatarChooser
@ -89,15 +109,14 @@ ColumnLayout {
id: avatar
anchors.fill: parent
image: _contact.avatar
username: LinphoneUtils.getContactUsername(_contact)
username: LinphoneUtils.getContactUsername(_contact) || 'John Doe'
visible: isLoaded() && !parent.hovered
}
}
TransparentTextInput {
id: editUsername
id: usernameInput
Layout.fillWidth: true
Layout.preferredHeight: ContactEditStyle.infoBar.buttons.size
@ -111,12 +130,7 @@ ColumnLayout {
text: avatar.username
onEditingFinished: {
_contact.username = text
// Update current text with new username.
text = _contact.username
}
onEditingFinished: _setUsername(text)
}
ActionBar {
@ -157,28 +171,56 @@ ColumnLayout {
contentWidth: width - ScrollBar.vertical.width - leftMargin - rightMargin
flickableDirection: Flickable.VerticalFlick
leftMargin: 40
rightMargin: 20
topMargin: 40
leftMargin: ContactEditStyle.values.leftMargin
rightMargin: ContactEditStyle.values.rightMargin
topMargin: ContactEditStyle.values.topMargin
ColumnLayout {
id: infoList
width: flick.contentWidth
SmartConnect {
id: infoUpdater
}
ListForm {
id: addresses
defaultData: _contact.sipAddresses
placeholder: qsTr('sipAccountsInput')
title: qsTr('sipAccounts')
onChanged: default_value.length === 0
? _contact.addSipAddress(new_value)
: _contact.updateSipAddress(default_value, new_value)
onRemoved: _contact.removeSipAddress(value)
}
ListForm {
id: companies
defaultData: _contact.companies
placeholder: qsTr('companiesInput')
title: qsTr('companies')
}
ListForm {
id: emails
defaultData: _contact.emails
placeholder: qsTr('emailsInput')
title: qsTr('emails')
onChanged: default_value.length === 0
? _contact.addEmail(new_value)
: _contact.updateEmail(default_value, new_value)
onRemoved: _contact.removeEmail(value)
}
ListForm {
id: urls
defaultData: _contact.urls
placeholder: qsTr('webSitesInput')
title: qsTr('webSites')

View file

@ -24,4 +24,10 @@ QtObject {
property int fontSize: 13
}
}
property QtObject values: QtObject {
property int leftMargin: 40
property int rightMargin: 20
property int topMargin: 40
}
}