Commit d308d0a3 authored by Ronan Abhamon's avatar Ronan Abhamon

feat(ui/views/App/MainWindow/ContactEdit): supports the add of new contacts

parent 338fe8c0
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_over</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_over">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#4B5964"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_default</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_default">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#96A6B1"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_clic</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_clic">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#FF5E00"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -53,6 +53,9 @@
<file>assets/images/delete_hovered.svg</file>
<file>assets/images/delete_normal.svg</file>
<file>assets/images/delete_pressed.svg</file>
<file>assets/images/edit_hovered.svg</file>
<file>assets/images/edit_normal.svg</file>
<file>assets/images/edit_pressed.svg</file>
<file>assets/images/ended_call.svg</file>
<file>assets/images/filter.svg</file>
<file>assets/images/fullscreen_hovered.svg</file>
......
#include <QtDebug>
#include "../../app/App.hpp"
#include "ContactModel.hpp"
......@@ -16,6 +18,18 @@ ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) {
App::getInstance()->getEngine()->setObjectOwnership(m_vcard.get(), QQmlEngine::CppOwnership);
}
ContactModel::ContactModel (VcardModel *vcard) {
QQmlEngine *engine = App::getInstance()->getEngine();
if (engine->objectOwnership(vcard) == QQmlEngine::CppOwnership)
throw std::invalid_argument("A contact is already linked to this vcard.");
m_linphone_friend = linphone::Friend::newFromVcard(vcard->m_vcard);
m_linphone_friend->setData(NAME, *this);
m_vcard.reset(vcard);
engine->setObjectOwnership(vcard, QQmlEngine::CppOwnership);
}
Presence::PresenceStatus ContactModel::getPresenceStatus () const {
return Presence::PresenceStatus::Offline;
}
......
......@@ -22,6 +22,7 @@ class ContactModel : public QObject {
public:
ContactModel (std::shared_ptr<linphone::Friend> linphone_friend);
ContactModel (VcardModel *vcard);
static const char *NAME;
......
......@@ -109,8 +109,7 @@ bool VcardModel::setAvatar (const QString &path) {
}
// 4. Update.
shared_ptr<belcard::BelCardPhoto> photo =
belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
shared_ptr<belcard::BelCardPhoto> photo = belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
photo->setValue(VCARD_SCHEME + ::Utils::qStringToLinphoneString(file_id));
if (!belcard->addPhoto(photo))
......@@ -145,8 +144,7 @@ bool VcardModel::setAddress (const QVariantMap &address) {
while (!addresses.empty())
belcard->removeAddress(addresses.front());
shared_ptr<belcard::BelCardAddress> belcard_address =
belcard::BelCardGeneric::create<belcard::BelCardAddress>();
shared_ptr<belcard::BelCardAddress> belcard_address = belcard::BelCardGeneric::create<belcard::BelCardAddress>();
belcard_address->setStreet(::Utils::qStringToLinphoneString(address["street"].toString()));
belcard_address->setLocality(::Utils::qStringToLinphoneString(address["locality"].toString()));
......@@ -163,42 +161,35 @@ bool VcardModel::setAddress (const QVariantMap &address) {
QVariantList VcardModel::getSipAddresses () const {
QVariantList list;
for (const auto &address : m_vcard->getSipAddresses())
list.append(::Utils::linphoneStringToQString(address->asString()));
for (const auto &address : m_vcard->getBelcard()->getImpp())
list.append(::Utils::linphoneStringToQString(address->getValue()));
return list;
}
bool VcardModel::addSipAddress (const QString &sip_address) {
shared_ptr<linphone::Address> address = CoreManager::getInstance()->getCore()->createAddress(
::Utils::qStringToLinphoneString(sip_address)
);
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
shared_ptr<belcard::BelCardImpp> value = belcard::BelCardGeneric::create<belcard::BelCardImpp>();
value->setValue(::Utils::qStringToLinphoneString(sip_address));
qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address);
if (!address) {
qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address);
if (!belcard->addImpp(value)) {
qWarning() << QStringLiteral("Unable to add sip address: `%1`.").arg(sip_address);
return false;
}
qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address);
m_vcard->addSipAddress(address->asStringUriOnly());
emit vcardUpdated();
return true;
}
void VcardModel::removeSipAddress (const QString &sip_address) {
list<shared_ptr<linphone::Address> > addresses = m_vcard->getSipAddresses();
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();
}
);
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
list<shared_ptr<belcard::BelCardImpp> > addresses = belcard->getImpp();
shared_ptr<belcard::BelCardImpp> value = findBelCardValue(addresses, sip_address);
if (it == addresses.cend()) {
qWarning() << QStringLiteral("Unable to found sip address: `%1`.")
.arg(sip_address);
if (!value) {
qWarning() << QStringLiteral("Unable to remove sip address: `%1`.").arg(sip_address);
return;
}
......@@ -209,7 +200,7 @@ void VcardModel::removeSipAddress (const QString &sip_address) {
}
qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address);
m_vcard->removeSipAddress((*it)->asStringUriOnly());
belcard->removeImpp(value);
emit vcardUpdated();
}
......@@ -241,8 +232,10 @@ bool VcardModel::addCompany (const QString &company) {
qInfo() << QStringLiteral("Add new company: `%1`.").arg(company);
if (!belcard->addRole(value))
if (!belcard->addRole(value)) {
qWarning() << QStringLiteral("Unable to add company: `%1`.").arg(company);
return false;
}
emit vcardUpdated();
return true;
......@@ -285,14 +278,15 @@ QVariantList VcardModel::getEmails () const {
bool VcardModel::addEmail (const QString &email) {
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
shared_ptr<belcard::BelCardEmail> value =
belcard::BelCardGeneric::create<belcard::BelCardEmail>();
shared_ptr<belcard::BelCardEmail> value = belcard::BelCardGeneric::create<belcard::BelCardEmail>();
value->setValue(::Utils::qStringToLinphoneString(email));
qInfo() << QStringLiteral("Add new email: `%1`.").arg(email);
if (!belcard->addEmail(value))
if (!belcard->addEmail(value)) {
qWarning() << QStringLiteral("Unable to add email: `%1`.").arg(email);
return false;
}
emit vcardUpdated();
return true;
......@@ -340,8 +334,10 @@ bool VcardModel::addUrl (const QString &url) {
qInfo() << QStringLiteral("Add new url: `%1`.").arg(url);
if (!belcard->addURL(value))
if (!belcard->addURL(value)) {
qWarning() << QStringLiteral("Unable to add url: `%1`.").arg(url);
return false;
}
emit vcardUpdated();
return true;
......
......@@ -17,11 +17,13 @@ class VcardModel : public QObject {
Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated);
Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated);
friend class ContactsListProxyModel;
friend class ContactModel;
public:
VcardModel (std::shared_ptr<linphone::Vcard> vcard) : m_vcard(vcard) {}
QString getUsername () const;
~VcardModel () = default;
public slots:
......@@ -45,7 +47,6 @@ signals:
void vcardUpdated ();
private:
QString getUsername () const;
void setUsername (const QString &username);
QString getAvatar () const;
......
......@@ -27,7 +27,7 @@ ContactsListModel::ContactsListModel (QObject *parent) : QAbstractListModel(pare
}
}
int ContactsListModel::rowCount (const QModelIndex&) const {
int ContactsListModel::rowCount (const QModelIndex &) const {
return m_list.count();
}
......@@ -49,8 +49,8 @@ QVariant ContactsListModel::data (const QModelIndex &index, int role) const {
return QVariant();
}
bool ContactsListModel::removeRow (int row, const QModelIndex&) {
return removeRows(row, 1);
bool ContactsListModel::removeRow (int row, const QModelIndex &parent) {
return removeRows(row, 1, parent);
}
bool ContactsListModel::removeRows (int row, int count, const QModelIndex &parent) {
......@@ -76,19 +76,38 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren
// -----------------------------------------------------------------------------
ContactModel *ContactsListModel::mapSipAddressToContact (const QString &sipAddress) const {
// TODO: Maybe use a hashtable in future version to get a lower cost?
shared_ptr<linphone::Friend> friend_ = m_linphone_friends->findFriendByUri(
::Utils::qStringToLinphoneString(sipAddress)
);
if (!friend_) {
qInfo() << QStringLiteral("Unable to map sip address: `%1`.").arg(sipAddress);
if (!friend_)
return nullptr;
}
return &friend_->getData<ContactModel>(ContactModel::NAME);
}
void ContactsListModel::addContact (VcardModel *vcard) {
ContactModel *contact = new ContactModel(vcard);
App::getInstance()->getEngine()->setObjectOwnership(contact, QQmlEngine::CppOwnership);
qInfo() << "Add contact:" << contact;
if (
m_linphone_friends->addFriend(contact->m_linphone_friend) !=
linphone::FriendListStatus::FriendListStatusOK
) {
qWarning() << "Unable to add friend from vcard:" << vcard;
delete contact;
return;
}
int row = rowCount();
beginInsertRows(QModelIndex(), row, row);
m_list << contact;
endInsertRows();
}
void ContactsListModel::removeContact (ContactModel *contact) {
qInfo() << "Removing contact:" << contact;
......
......@@ -27,6 +27,7 @@ public:
public slots:
ContactModel *mapSipAddressToContact (const QString &sipAddress) const;
void addContact (VcardModel *vcard);
void removeContact (ContactModel *contact);
private:
......
......@@ -6,12 +6,15 @@
CoreManager *CoreManager::m_instance = nullptr;
CoreManager::CoreManager (QObject *parent) : QObject(parent), m_core(
linphone::Factory::get()->createCore(nullptr, "", "", nullptr)
) {
CoreManager::CoreManager (QObject *parent) : QObject(parent),
m_core(linphone::Factory::get()->createCore(nullptr, "", "", nullptr)) {
setDatabasesPaths();
}
VcardModel *CoreManager::createDetachedVcardModel () {
return new VcardModel(linphone::Factory::get()->createVcard());
}
void CoreManager::setDatabasesPaths () {
std::string database_path;
......
......@@ -4,6 +4,8 @@
#include <QObject>
#include <linphone++/linphone.hh>
#include "../contact/VcardModel.hpp"
// ===================================================================
class CoreManager : public QObject {
......@@ -24,14 +26,16 @@ public:
return m_core;
}
public slots:
VcardModel *createDetachedVcardModel ();
private:
CoreManager (QObject *parent = Q_NULLPTR);
void setDatabasesPaths ();
static CoreManager *m_instance;
std::shared_ptr<linphone::Core> m_core;
static CoreManager *m_instance;
};
#endif // CORE_MANAGER_H_
pragma Singleton
import QtQuick 2.7
// ===================================================================
// =============================================================================
QtObject {
property color a: 'transparent'
property color o: '#232323' // TextButtonA Hovered.
property color q: '#E6E6E6'
property color d: '#5A585B'
......@@ -24,6 +22,7 @@ QtObject {
property color g20: '#336B7A86'
property color h: '#687680'
property color i: '#FE5E00'
property color i30: '#4DFE5E00'
property color j: '#4B5964'
property color j75: '#BF4B5964'
property color k: '#FFFFFF'
......@@ -31,6 +30,7 @@ QtObject {
property color l: '#000000'
property color m: '#D1D1D1'
property color n: '#C0C0C0'
property color o: '#232323'
property color p: '#E2E9EF'
property color r: '#595759'
property color s: '#D64D00'
......
......@@ -3,48 +3,79 @@ import QtQuick.Controls 2.0
import Common.Styles 1.0
// ===================================================================
// =============================================================================
Button {
id: button
Item {
id: wrappedButton
property color colorDisabled
property color colorHovered
property color colorNormal
property color colorPressed
// By default textColorNormal is the hovered/pressed text color.
property color textColorDisabled
property color textColorHovered: textColorNormal
property color textColorNormal
property color textColorPressed: textColorNormal
background: Rectangle {
color: button.down
property alias text: button.text
property bool enabled: true
signal clicked
// ---------------------------------------------------------------------------
function _getBackgroundColor () {
if (!wrappedButton.enabled) {
return colorDisabled
}
return button.down
? colorPressed
: (button.hovered
? colorHovered
: colorNormal
)
implicitHeight: AbstractTextButtonStyle.background.height
implicitWidth: AbstractTextButtonStyle.background.width
radius: AbstractTextButtonStyle.background.radius
: (button.hovered ? colorHovered : colorNormal)
}
contentItem: Text {
color: button.down
function _getTextColor () {
if (!wrappedButton.enabled) {
return textColorDisabled
}
return button.down
? textColorPressed
: (button.hovered
? textColorHovered
: textColorNormal
)
font {
bold: true
pointSize: AbstractTextButtonStyle.text.fontSize
: (button.hovered ? textColorHovered : textColorNormal)
}
// ---------------------------------------------------------------------------
implicitHeight: button.height
implicitWidth: button.width
// ---------------------------------------------------------------------------
Button {
id: button
background: Rectangle {
color: _getBackgroundColor()
implicitHeight: AbstractTextButtonStyle.background.height
implicitWidth: AbstractTextButtonStyle.background.width
radius: AbstractTextButtonStyle.background.radius
}
contentItem: Text {
color: _getTextColor()
font {
bold: true
pointSize: AbstractTextButtonStyle.text.fontSize
}
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.text
verticalAlignment: Text.AlignVCenter
}
hoverEnabled: true
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.text
verticalAlignment: Text.AlignVCenter
onClicked: wrappedButton.enabled && parent.clicked()
}
hoverEnabled: true
}
......@@ -5,21 +5,23 @@ import Common 1.0
import Common.Styles 1.0
import Utils 1.0
// ===================================================================
// =============================================================================
RowLayout {
id: listForm
property alias placeholder: placeholder.text
property alias title: text.text
property bool readOnly: false
property int inputMethodHints
property var defaultData: []
property var minValues
readonly property int count: values.count
signal changed (int index, string default_value, string new_value)
signal changed (int index, string defaultValue, string newValue)
signal removed (int index, string value)
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
function setInvalid (index, status) {
Utils.assert(
......@@ -48,47 +50,41 @@ RowLayout {
}
function _handleEditionFinished (index, text) {
if (text.length === 0) {
// Remove.
var default_value = values.model.get(index).$value
if (minValues != null && minValues >= values.model.count) {
var model = values.model
var model = values.model
var defaultValue = model.get(index).$value
if (text.length === 0) {
// No changes. It must exists at least n min values.
if (minValues != null && minValues >= model.count) {
// Unable to set property directly. Qt uses a cache of the value.
model.remove(index)
model.insert(index, {
$isInvalid: false,
$value: default_value
$value: defaultValue
})
return
}
values.model.remove(index)
model.remove(index)
if (default_value.length !== 0) {
listForm.removed(index, default_value)
}
} else {
// Update.
var default_value = values.model.get(index).$value
// If no changes, no signal.
if (text !== default_value) {
listForm.changed(index, default_value, text)
if (defaultValue.length !== 0) {
listForm.removed(index, defaultValue)
}
} else if (text !== defaultValue) {
// Update changes.
listForm.changed(index, defaultValue, text)
}
addButton.enabled = true
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
spacing: 0
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Title area.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
RowLayout {
Layout.alignment: Qt.AlignTop
......@@ -100,8 +96,9 @@ RowLayout {
icon: 'add'
iconSize: ListFormStyle.titleArea.iconSize
opacity: _edition ? 1 : 0
onClicked: _addValue('')
onClicked: _edition && _addValue('')
}
Text {
......@@ -118,9 +115,9 @@ RowLayout {
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Placeholder.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Text {
id: placeholder
......@@ -135,7 +132,7 @@ RowLayout {
}
padding: ListFormStyle.value.text.padding
visible: values.model.count === 0
visible: values.model.count === 0 && !listForm.readOnly
verticalAlignment: Text.AlignVCenter
MouseArea {
......@@ -144,9 +141,9 @@ RowLayout {
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Values.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
ListView {
id: values
......@@ -165,6 +162,7 @@ RowLayout {
inputMethodHints: listForm.inputMethodHints
isInvalid: $isInvalid
readOnly: listForm.readOnly
text: $value
height: ListFormStyle.lineHeight
......@@ -190,16 +188,14 @@ RowLayout {
model: ListModel {}
// ---------------------------------------------------------------
// -------------------------------------------------------------------------
// Init values.
// ---------------------------------------------------------------
// -------------------------------------------------------------------------
Component.onCompleted: {
if (!defaultData) {
return
if (defaultData) {
setData(defaultData)
}
setData(defaultData)
}
}
}
import Common.Styles 1.0
// ===================================================================
// =============================================================================
AbstractTextButton {
colorDisabled: TextButtonAStyle.backgroundColor.disabled
colorHovered: TextButtonAStyle.backgroundColor.hovered
colorNormal: TextButtonAStyle.backgroundColor.normal
colorPressed: TextButtonAStyle.backgroundColor.pressed
textColorDisabled: TextButtonAStyle.textColor.disabled
textColorHovered: TextButtonAStyle.textColor.hovered
textColorNormal: TextButtonAStyle.textColor.normal
textColorPressed: TextButtonAStyle.textColor.pressed
......
import Common.Styles 1.0
// ===================================================================
// =============================================================================
AbstractTextButton {
colorDisabled: TextButtonBStyle.backgroundColor.disabled
colorHovered: TextButtonBStyle.backgroundColor.hovered
colorNormal: TextButtonBStyle.backgroundColor.normal
colorPressed: TextButtonBStyle.backgroundColor.pressed
textColorDisabled: TextButtonBStyle.textColor.disabled
textColorHovered: TextButtonBStyle.textColor.hovered
textColorNormal: TextButtonBStyle.textColor.normal
textColorPressed: TextButtonBStyle.textColor.pressed
......
......@@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0
// ===================================================================
// =============================================================================
QtObject {
property QtObject backgroundColor: QtObject {
property color disabled: Colors.o
property color hovered: Colors.o
property color normal: Colors.j
property color pressed: Colors.i
}
property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k
property color normal: Colors.k
property color pressed: Colors.k
......
......@@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0
// ===================================================================
// =============================================================================
QtObject {
property QtObject backgroundColor: QtObject {
property color disabled: Colors.i30
property color hovered: Colors.s
property color normal: Colors.i
property color pressed: Colors.t
}
property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k
property color normal: Colors.k
property color pressed: Colors.k
......
......@@ -5,22 +5,45 @@ import QtQuick.Layouts 1.3
import Common 1.0
import Linphone 1.0
import LinphoneUtils 1.0
import Utils 1.0
import App.Styles 1.0
// ===================================================================
// =============================================================================
ColumnLayout {
id: contactEdit
property string sipAddress: ''
property string sipAddress
property bool _edition: false
property var _contact
property var _vcard
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
function _editContact () {
_contact.startEdit()
_edition = true
}
function _save () {
if (_contact) {
_contact.endEdit()
_edition = false
} else {
_contact = ContactsListModel.addContact(_vcard)
}
}
function _cancel () {
if (_contact) {
_contact.abortEdit()
_edition = false
} else {
window.setView('Contacts')
}
}
function _removeContact () {
Utils.openConfirmDialog(window, {
......@@ -46,45 +69,55 @@ ColumnLayout {
usernameInput.text = _vcard.username
}
function _handleSipAddressChanged (index, default_value, new_value) {
if (!Utils.startsWith(new_value, 'sip:')) {
new_value = 'sip:' + new_value
function _handleSipAddressChanged (index, defaultValue, newValue) {
if (!Utils.startsWith(newValue, 'sip:')) {
newValue = 'sip:' + newValue
if (new_value === default_value) {
if (newValue === defaultValue) {
return
}
}
var so_far_so_good = (default_value.length === 0)
? _vcard.addSipAddress(new_value)
: _vcard.updateSipAddress(default_value, new_value)
var so_far_so_good = (defaultValue.length === 0)
? _vcard.addSipAddress(newValue)
: _vcard.updateSipAddress(defaultValue, newValue)
addresses.setInvalid(index, !so_far_so_good)
}
function _handleUrlChanged (index, default_value, new_value) {
var url = Utils.extractFirstUri(new_value)
if (url === default_value) {
function _handleUrlChanged (index, defaultValue, newValue) {
var url = Utils.extractFirstUri(newValue)
if (url === defaultValue) {
return
}
var so_far_so_good = (default_value.length === 0)
? url && _vcard.addUrl(new_value)
: url && _vcard.updateUrl(default_value, new_value)
var so_far_so_good = (defaultValue.length === 0)
? url && _vcard.addUrl(newValue)
: url && _vcard.updateUrl(defaultValue, newValue)
urls.setInvalid(index, !so_far_so_good)
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
spacing: 0
Component.onCompleted: {
_contact = ContactsListModel.mapSipAddressToContact(sipAddress)
_vcard = _contact.vcard
if (!_contact) {
_vcard = CoreManager.createDetachedVcardModel()
_edition = true
} else {
_vcard = _contact.vcard
}
}
// -----------------------------------------------------------------
Component.onDestruction: {
// TODO: Remove photo if contact not created.
}
// ---------------------------------------------------------------------------
FileDialog {
id: avatarChooser
......@@ -95,9 +128,9 @@ ColumnLayout {
onAccepted: _setAvatar(fileUrls[0])
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Info bar.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Rectangle {
Layout.fillWidth: true
......@@ -124,7 +157,7 @@ ColumnLayout {
anchors.fill: parent
image: _vcard.avatar
username: LinphoneUtils.getContactUsername(_contact) || 'John Doe'
username: _vcard.username
visible: isLoaded() && !parent.hovered
}
}
......@@ -155,22 +188,30 @@ ColumnLayout {
ActionButton {
icon: 'history'
onClicked: window.setView('Conversation', {
sipAddress: contactEdit.sipAddress
})
}
ActionButton {
icon: 'edit'
visible: !_edition
onClicked: _editContact()
}
ActionButton {
icon: 'delete'
onClicked: _removeContact()
}
}
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Info list.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Flickable {
id: flick
......@@ -197,12 +238,13 @@ ColumnLayout {
Layout.rightMargin: ContactEditStyle.values.rightMargin
Layout.topMargin: ContactEditStyle.values.topMargin
defaultData: _vcard.sipAddresses
minValues: 1
defaultData: _vcard.sipAddresses
minValues: _contact ? 1 : 0
placeholder: qsTr('sipAccountsInput')
readOnly: !_edition
title: qsTr('sipAccounts')
onChanged: _handleSipAddressChanged(index, default_value, new_value)
onChanged: _handleSipAddressChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeSipAddress(value)
}
......@@ -220,11 +262,12 @@ ColumnLayout {
defaultData: _vcard.companies
placeholder: qsTr('companiesInput')
readOnly: !_edition
title: qsTr('companies')
onChanged: default_value.length === 0
? _vcard.addCompany(new_value)
: _vcard.updateCompany(default_value, new_value)
onChanged: defaultValue.length === 0
? _vcard.addCompany(newValue)
: _vcard.updateCompany(defaultValue, newValue)
onRemoved: _vcard.removeCompany(value)
}
......@@ -243,11 +286,12 @@ ColumnLayout {
defaultData: _vcard.emails
inputMethodHints: Qt.ImhEmailCharactersOnly
placeholder: qsTr('emailsInput')
readOnly: !_edition
title: qsTr('emails')
onChanged: default_value.length === 0
? _vcard.addEmail(new_value)
: _vcard.updateEmail(default_value, new_value)
onChanged: defaultValue.length === 0
? _vcard.addEmail(newValue)
: _vcard.updateEmail(defaultValue, newValue)
onRemoved: _vcard.removeEmail(value)
}
......@@ -266,9 +310,10 @@ ColumnLayout {
defaultData: _vcard.urls
inputMethodHints: Qt.ImhUrlCharactersOnly
placeholder: qsTr('webSitesInput')
readOnly: !_edition
title: qsTr('webSites')
onChanged: _handleUrlChanged(index, default_value, new_value)
onChanged: _handleUrlChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeUrl(value)
}
......@@ -278,20 +323,21 @@ ColumnLayout {
color: ContactEditStyle.values.separator.color
}
Loader {
Row {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: ContactEditStyle.buttons.topMargin
spacing: ContactEditStyle.buttons.spacing
visible: _edition
sourceComponent: Row {
spacing: ContactEditStyle.buttons.spacing
TextButtonB {
text: qsTr('save')
}
TextButtonB {
enabled: _vcard.sipAddresses.length > 0
text: qsTr('save')
onClicked: _save()
}
TextButtonA {
text: qsTr('cancel')
}
TextButtonA {
text: qsTr('cancel')
onClicked: _cancel()
}
}
......
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