mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-02-07 06:59:45 +00:00
contact list generic VariantList FriendModel resetAddresses check null default account address list update on save generic item for white background lists ui fix set photo friend protect friendmodel setters remove main splitview to stick to the mock-up (keeping it commented cause it may be useful to be able to resize the panels) default image avatar fix crash when address not set fix ui in call Really fix call variantobject destroying himself before call started fix: display suggestions fix list view current item always reset cause updated every time contact history list changed fix blinking call log list view on content changed fix popup button popup y when exceed window delete contact confirmation popup fix popup call contact list don't show popup if only one address possible set/remove default address if append/remove from address list
520 lines
18 KiB
C++
520 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
|
*
|
|
* This file is part of linphone-desktop
|
|
* (see https://www.linphone.org).
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "FriendCore.hpp"
|
|
#include "core/App.hpp"
|
|
#include "core/proxy/ListProxy.hpp"
|
|
#include "model/tool/ToolModel.hpp"
|
|
#include "tool/Utils.hpp"
|
|
#include "tool/thread/SafeConnection.hpp"
|
|
|
|
DEFINE_ABSTRACT_OBJECT(FriendCore)
|
|
|
|
const QString addressLabel = FriendCore::tr("Adresse SIP");
|
|
const QString phoneLabel = FriendCore::tr("Téléphone");
|
|
|
|
QVariant createFriendAddressVariant(const QString &label, const QString &address) {
|
|
QVariantMap map;
|
|
map.insert("label", label);
|
|
map.insert("address", address);
|
|
return map;
|
|
}
|
|
|
|
QSharedPointer<FriendCore> FriendCore::create(const std::shared_ptr<linphone::Friend> &contact) {
|
|
auto sharedPointer = QSharedPointer<FriendCore>(new FriendCore(contact), &QObject::deleteLater);
|
|
sharedPointer->setSelf(sharedPointer);
|
|
sharedPointer->moveToThread(App::getInstance()->thread());
|
|
return sharedPointer;
|
|
}
|
|
|
|
FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObject(nullptr) {
|
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
|
if (contact) {
|
|
mustBeInLinphoneThread(getClassName());
|
|
mFriendModel = Utils::makeQObject_ptr<FriendModel>(contact);
|
|
mFriendModel->setSelf(mFriendModel);
|
|
mConsolidatedPresence = LinphoneEnums::fromLinphone(contact->getConsolidatedPresence());
|
|
mPresenceTimestamp = mFriendModel->getPresenceTimestamp();
|
|
mPictureUri = Utils::coreStringToAppString(contact->getPhoto());
|
|
auto vcard = contact->getVcard();
|
|
mOrganization = Utils::coreStringToAppString(vcard->getOrganization());
|
|
mJob = Utils::coreStringToAppString(vcard->getJobTitle());
|
|
mGivenName = Utils::coreStringToAppString(vcard->getGivenName());
|
|
mFamilyName = Utils::coreStringToAppString(vcard->getFamilyName());
|
|
auto addresses = contact->getAddresses();
|
|
for (auto &address : addresses) {
|
|
mAddressList.append(
|
|
createFriendAddressVariant(addressLabel, Utils::coreStringToAppString(address->asStringUriOnly())));
|
|
}
|
|
mDefaultAddress =
|
|
contact->getAddress() ? Utils::coreStringToAppString(contact->getAddress()->asStringUriOnly()) : QString();
|
|
auto phoneNumbers = contact->getPhoneNumbersWithLabel();
|
|
for (auto &phoneNumber : phoneNumbers) {
|
|
mPhoneNumberList.append(
|
|
createFriendAddressVariant(Utils::coreStringToAppString(phoneNumber->getLabel()),
|
|
Utils::coreStringToAppString(phoneNumber->getPhoneNumber())));
|
|
}
|
|
|
|
mStarred = contact->getStarred();
|
|
mIsSaved = true;
|
|
} else {
|
|
mIsSaved = false;
|
|
mStarred = false;
|
|
}
|
|
connect(this, &FriendCore::addressChanged, &FriendCore::allAddressesChanged);
|
|
connect(this, &FriendCore::phoneNumberChanged, &FriendCore::allAddressesChanged);
|
|
}
|
|
|
|
FriendCore::FriendCore(const FriendCore &friendCore) {
|
|
// Only copy friend values without models for lambda using and avoid concurrencies.
|
|
mAddressList = friendCore.mAddressList;
|
|
mPhoneNumberList = friendCore.mPhoneNumberList;
|
|
mDefaultAddress = friendCore.mDefaultAddress;
|
|
mGivenName = friendCore.mGivenName;
|
|
mFamilyName = friendCore.mFamilyName;
|
|
mOrganization = friendCore.mOrganization;
|
|
mJob = friendCore.mJob;
|
|
mPictureUri = friendCore.mPictureUri;
|
|
mIsSaved = friendCore.mIsSaved;
|
|
}
|
|
|
|
FriendCore::~FriendCore() {
|
|
mustBeInMainThread("~" + getClassName());
|
|
if (mFriendModel) emit mFriendModel->removeListener();
|
|
}
|
|
|
|
void FriendCore::setSelf(SafeSharedPointer<FriendCore> me) {
|
|
setSelf(me.mQDataWeak.lock());
|
|
}
|
|
void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
|
|
if (me) {
|
|
if (mFriendModel) {
|
|
mCoreModelConnection = nullptr; // No more needed
|
|
mFriendModelConnection = QSharedPointer<SafeConnection<FriendCore, FriendModel>>(
|
|
new SafeConnection<FriendCore, FriendModel>(me, mFriendModel), &QObject::deleteLater);
|
|
mFriendModelConnection->makeConnectToModel(
|
|
&FriendModel::presenceReceived,
|
|
[this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) {
|
|
mFriendModelConnection->invokeToCore([this, consolidatedPresence, presenceTimestamp]() {
|
|
setConsolidatedPresence(consolidatedPresence);
|
|
setPresenceTimestamp(presenceTimestamp);
|
|
});
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](const QString &uri) {
|
|
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::starredChanged, [this](bool starred) {
|
|
mFriendModelConnection->invokeToCore([this, starred]() { this->onStarredChanged(starred); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::givenNameChanged, [this](const QString &name) {
|
|
mFriendModelConnection->invokeToCore([this, name]() { setGivenName(name); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::familyNameChanged, [this](const QString &name) {
|
|
mFriendModelConnection->invokeToCore([this, name]() { setFamilyName(name); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::organizationChanged, [this](const QString &orga) {
|
|
mFriendModelConnection->invokeToCore([this, orga]() { setOrganization(orga); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::jobChanged, [this](const QString &job) {
|
|
mFriendModelConnection->invokeToCore([this, job]() { setJob(job); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::addressesChanged, [this]() {
|
|
auto numbers = mFriendModel->getAddresses();
|
|
QList<QVariant> addr;
|
|
for (auto &num : numbers) {
|
|
addr.append(
|
|
createFriendAddressVariant(addressLabel, Utils::coreStringToAppString(num->asStringUriOnly())));
|
|
}
|
|
mFriendModelConnection->invokeToCore([this, addr]() { resetPhoneNumbers(addr); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(&FriendModel::phoneNumbersChanged, [this]() {
|
|
auto numbers = mFriendModel->getPhoneNumbers();
|
|
QList<QVariant> addr;
|
|
for (auto &num : numbers) {
|
|
addr.append(
|
|
createFriendAddressVariant(phoneLabel, Utils::coreStringToAppString(num->getPhoneNumber())));
|
|
}
|
|
mFriendModelConnection->invokeToCore([this, addr]() { resetPhoneNumbers(addr); });
|
|
});
|
|
mFriendModelConnection->makeConnectToModel(
|
|
&FriendModel::objectNameChanged,
|
|
[this](const QString &objectName) { qDebug() << "object name changed" << objectName; });
|
|
|
|
// From GUI
|
|
mFriendModelConnection->makeConnectToCore(&FriendCore::lSetStarred, [this](bool starred) {
|
|
mFriendModelConnection->invokeToModel([this, starred]() { mFriendModel->setStarred(starred); });
|
|
});
|
|
|
|
} else { // Create
|
|
mCoreModelConnection = QSharedPointer<SafeConnection<FriendCore, CoreModel>>(
|
|
new SafeConnection<FriendCore, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FriendCore::reset(const FriendCore &contact) {
|
|
resetAddresses(contact.getAddresses());
|
|
resetPhoneNumbers(contact.getPhoneNumbers());
|
|
setDefaultAddress(contact.getDefaultAddress());
|
|
setGivenName(contact.getGivenName());
|
|
setFamilyName(contact.getFamilyName());
|
|
setOrganization(contact.getOrganization());
|
|
setJob(contact.getJob());
|
|
setPictureUri(contact.getPictureUri());
|
|
setIsSaved(mFriendModel != nullptr);
|
|
}
|
|
|
|
QString FriendCore::getDisplayName() const {
|
|
return mGivenName + " " + mFamilyName;
|
|
}
|
|
|
|
QString FriendCore::getGivenName() const {
|
|
return mGivenName;
|
|
}
|
|
|
|
void FriendCore::setGivenName(const QString &name) {
|
|
if (mGivenName != name) {
|
|
mGivenName = name;
|
|
emit givenNameChanged(name);
|
|
emit displayNameChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
QString FriendCore::getOrganization() const {
|
|
return mOrganization;
|
|
}
|
|
|
|
void FriendCore::setOrganization(const QString &orga) {
|
|
if (mOrganization != orga) {
|
|
mOrganization = orga;
|
|
emit organizationChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
QString FriendCore::getJob() const {
|
|
return mJob;
|
|
}
|
|
|
|
void FriendCore::setJob(const QString &job) {
|
|
if (mJob != job) {
|
|
mJob = job;
|
|
emit jobChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
QString FriendCore::getFamilyName() const {
|
|
return mFamilyName;
|
|
}
|
|
|
|
void FriendCore::setFamilyName(const QString &name) {
|
|
if (mFamilyName != name) {
|
|
mFamilyName = name;
|
|
emit familyNameChanged(name);
|
|
emit displayNameChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
bool FriendCore::getStarred() const {
|
|
return mStarred;
|
|
}
|
|
|
|
void FriendCore::onStarredChanged(bool starred) {
|
|
mStarred = starred;
|
|
save();
|
|
emit starredChanged();
|
|
}
|
|
|
|
QList<QVariant> FriendCore::getPhoneNumbers() const {
|
|
return mPhoneNumberList;
|
|
}
|
|
|
|
QVariant FriendCore::getPhoneNumberAt(int index) const {
|
|
if (index < 0 || index >= mPhoneNumberList.count()) return QVariant();
|
|
return mPhoneNumberList[index];
|
|
}
|
|
|
|
void FriendCore::setPhoneNumberAt(int index, const QString &label, const QString &phoneNumber) {
|
|
if (index < 0 || index >= mPhoneNumberList.count()) return;
|
|
auto map = mPhoneNumberList[index].toMap();
|
|
auto oldLabel = map["label"].toString();
|
|
if (/*oldLabel != label || */ map["address"] != phoneNumber) {
|
|
mPhoneNumberList.replace(index, createFriendAddressVariant(label.isEmpty() ? oldLabel : label, phoneNumber));
|
|
emit phoneNumberChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
void FriendCore::removePhoneNumber(int index) {
|
|
if (index != -1) mPhoneNumberList.remove(index);
|
|
emit phoneNumberChanged();
|
|
}
|
|
|
|
void FriendCore::appendPhoneNumber(const QString &label, const QString &number) {
|
|
mPhoneNumberList.append(createFriendAddressVariant(label, number));
|
|
emit phoneNumberChanged();
|
|
}
|
|
|
|
void FriendCore::resetPhoneNumbers(QList<QVariant> newList) {
|
|
mPhoneNumberList = newList;
|
|
emit phoneNumberChanged();
|
|
}
|
|
|
|
QList<QVariant> FriendCore::getAddresses() const {
|
|
return mAddressList;
|
|
}
|
|
|
|
QVariant FriendCore::getAddressAt(int index) const {
|
|
if (index < 0 || index >= mAddressList.count()) return QVariant();
|
|
return mAddressList[index];
|
|
}
|
|
|
|
void FriendCore::setAddressAt(int index, const QString &label, const QString &address) {
|
|
if (index < 0 || index >= mAddressList.count()) return;
|
|
auto map = mAddressList[index].toMap();
|
|
auto oldLabel = map["label"].toString();
|
|
if (/*oldLabel != label || */ map["address"] != address) {
|
|
mAddressList.replace(index, createFriendAddressVariant(label.isEmpty() ? oldLabel : label, address));
|
|
emit addressChanged();
|
|
setIsSaved(false);
|
|
}
|
|
}
|
|
|
|
void FriendCore::removeAddress(int index) {
|
|
if (index < 0 && index >= mAddressList.size()) return;
|
|
auto map = mAddressList[index].toMap();
|
|
if (map["address"].toString() == mDefaultAddress) mDefaultAddress.clear();
|
|
mAddressList.remove(index);
|
|
emit addressChanged();
|
|
}
|
|
|
|
void FriendCore::appendAddress(const QString &addr) {
|
|
mAddressList.append(createFriendAddressVariant(addressLabel, addr));
|
|
if (mDefaultAddress.isEmpty()) mDefaultAddress = addr;
|
|
emit addressChanged();
|
|
}
|
|
|
|
void FriendCore::resetAddresses(QList<QVariant> newList) {
|
|
mAddressList = newList;
|
|
emit addressChanged();
|
|
}
|
|
|
|
QList<QVariant> FriendCore::getAllAddresses() const {
|
|
return mAddressList + mPhoneNumberList;
|
|
}
|
|
|
|
QString FriendCore::getDefaultAddress() const {
|
|
return mDefaultAddress;
|
|
}
|
|
|
|
void FriendCore::setDefaultAddress(const QString &address) {
|
|
auto it = std::find_if(mAddressList.begin(), mAddressList.end(),
|
|
[address](const QVariant &a) { return a.toMap()["address"].toString() == address; });
|
|
if (it == mAddressList.end()) appendAddress(address);
|
|
if (mDefaultAddress != address) {
|
|
mDefaultAddress = address;
|
|
emit defaultAddressChanged();
|
|
}
|
|
}
|
|
|
|
LinphoneEnums::ConsolidatedPresence FriendCore::getConsolidatedPresence() const {
|
|
return mConsolidatedPresence;
|
|
}
|
|
|
|
void FriendCore::setConsolidatedPresence(LinphoneEnums::ConsolidatedPresence presence) {
|
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
|
if (mConsolidatedPresence != presence) {
|
|
mConsolidatedPresence = presence;
|
|
emit consolidatedPresenceChanged(mConsolidatedPresence);
|
|
}
|
|
}
|
|
|
|
QDateTime FriendCore::getPresenceTimestamp() const {
|
|
return mPresenceTimestamp;
|
|
}
|
|
|
|
void FriendCore::setPresenceTimestamp(QDateTime presenceTimestamp) {
|
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
|
if (mPresenceTimestamp != presenceTimestamp) {
|
|
mPresenceTimestamp = presenceTimestamp;
|
|
emit presenceTimestampChanged(mPresenceTimestamp);
|
|
}
|
|
}
|
|
|
|
QString FriendCore::getPictureUri() const {
|
|
return mPictureUri;
|
|
}
|
|
|
|
void FriendCore::setPictureUri(const QString &uri) {
|
|
if (mPictureUri != uri) {
|
|
mPictureUri = uri;
|
|
emit pictureUriChanged();
|
|
}
|
|
}
|
|
|
|
void FriendCore::onPictureUriChanged(QString uri) {
|
|
mPictureUri = uri;
|
|
emit pictureUriChanged();
|
|
}
|
|
|
|
bool FriendCore::getIsSaved() const {
|
|
return mIsSaved;
|
|
}
|
|
void FriendCore::setIsSaved(bool data) {
|
|
if (mIsSaved != data) {
|
|
mIsSaved = data;
|
|
emit isSavedChanged(mIsSaved);
|
|
}
|
|
}
|
|
|
|
void FriendCore::writeIntoModel(std::shared_ptr<FriendModel> model) const {
|
|
mustBeInLinphoneThread(QString("[") + gClassName + "] " + Q_FUNC_INFO);
|
|
model->getFriend()->edit();
|
|
// needed to create the vcard if not created yet
|
|
model->setName(mGivenName + (mFamilyName.isEmpty() || mGivenName.isEmpty() ? "" : " ") + mFamilyName);
|
|
auto core = CoreModel::getInstance()->getCore();
|
|
|
|
std::list<std::shared_ptr<linphone::Address>> addresses;
|
|
for (auto &addr : mAddressList) {
|
|
auto friendAddress = addr.toMap();
|
|
auto num =
|
|
linphone::Factory::get()->createAddress(Utils::appStringToCoreString(friendAddress["address"].toString()));
|
|
addresses.push_back(num);
|
|
}
|
|
model->resetAddresses(addresses);
|
|
|
|
model->setAddress(ToolModel::interpretUrl(mDefaultAddress));
|
|
|
|
std::list<std::shared_ptr<linphone::FriendPhoneNumber>> phones;
|
|
for (auto &number : mPhoneNumberList) {
|
|
auto friendAddress = number.toMap();
|
|
auto num = linphone::Factory::get()->createFriendPhoneNumber(
|
|
Utils::appStringToCoreString(friendAddress["address"].toString()),
|
|
Utils::appStringToCoreString(friendAddress["label"].toString()));
|
|
phones.push_back(num);
|
|
}
|
|
model->resetPhoneNumbers(phones);
|
|
|
|
model->setGivenName(mGivenName);
|
|
model->setFamilyName(mFamilyName);
|
|
model->setOrganization(mOrganization);
|
|
model->setJob(mJob);
|
|
model->setPictureUri(mPictureUri);
|
|
model->getFriend()->done();
|
|
}
|
|
|
|
void FriendCore::writeFromModel(const std::shared_ptr<FriendModel> &model) {
|
|
mustBeInLinphoneThread(QString("[") + gClassName + "] " + Q_FUNC_INFO);
|
|
|
|
QList<QVariant> addresses;
|
|
for (auto &addr : model->getAddresses()) {
|
|
addresses.append(
|
|
createFriendAddressVariant(addressLabel, Utils::coreStringToAppString(addr->asStringUriOnly())));
|
|
}
|
|
mAddressList = addresses;
|
|
|
|
QList<QVariant> phones;
|
|
for (auto &number : model->getPhoneNumbers()) {
|
|
phones.append(createFriendAddressVariant(Utils::coreStringToAppString(number->getLabel()),
|
|
Utils::coreStringToAppString(number->getPhoneNumber())));
|
|
}
|
|
mPhoneNumberList = phones;
|
|
mGivenName = model->getGivenName();
|
|
mFamilyName = model->getFamilyName();
|
|
mOrganization = model->getOrganization();
|
|
mJob = model->getJob();
|
|
mPictureUri = model->getPictureUri();
|
|
}
|
|
|
|
void FriendCore::remove() {
|
|
if (mFriendModel) { // Update
|
|
mFriendModelConnection->invokeToModel([this]() {
|
|
auto contact = mFriendModel->getFriend();
|
|
contact->remove();
|
|
emit CoreModel::getInstance()->friendRemoved();
|
|
mFriendModelConnection->invokeToCore([this]() { removed(this); });
|
|
});
|
|
}
|
|
}
|
|
|
|
void FriendCore::save() { // Save Values to model
|
|
FriendCore *thisCopy = new FriendCore(*this); // Pointer to avoid multiple copies in lambdas
|
|
|
|
if (mFriendModel) {
|
|
mFriendModelConnection->invokeToModel([this, thisCopy]() { // Copy values to avoid concurrency
|
|
thisCopy->writeIntoModel(mFriendModel);
|
|
thisCopy->deleteLater();
|
|
mFriendModelConnection->invokeToCore([this]() { saved(); });
|
|
setIsSaved(true);
|
|
});
|
|
} else {
|
|
mCoreModelConnection->invokeToModel([this, thisCopy]() {
|
|
std::shared_ptr<linphone::Friend> contact;
|
|
auto core = CoreModel::getInstance()->getCore();
|
|
for (auto &addr : mAddressList) {
|
|
auto friendAddress = addr.toMap();
|
|
auto linphoneAddr = ToolModel::interpretUrl(friendAddress["address"].toString());
|
|
contact = core->findFriend(linphoneAddr);
|
|
if (contact) break;
|
|
}
|
|
if (contact != nullptr) {
|
|
auto friendModel = Utils::makeQObject_ptr<FriendModel>(contact);
|
|
friendModel->setSelf(friendModel);
|
|
thisCopy->writeIntoModel(friendModel);
|
|
thisCopy->deleteLater();
|
|
if (mFriendModelConnection) mFriendModelConnection->invokeToCore([this] { saved(); });
|
|
else mCoreModelConnection->invokeToCore([this] { saved(); });
|
|
} else {
|
|
auto contact = core->createFriend();
|
|
std::shared_ptr<FriendModel> friendModel;
|
|
friendModel = Utils::makeQObject_ptr<FriendModel>(contact);
|
|
friendModel->setSelf(friendModel);
|
|
thisCopy->writeIntoModel(friendModel);
|
|
thisCopy->deleteLater();
|
|
bool created = (core->getDefaultFriendList()->addFriend(contact) == linphone::FriendList::Status::OK);
|
|
if (created) {
|
|
core->getDefaultFriendList()->updateSubscriptions();
|
|
emit CoreModel::getInstance()->friendAdded();
|
|
}
|
|
mCoreModelConnection->invokeToCore([this, created]() {
|
|
if (created) setSelf(mCoreModelConnection->mCore);
|
|
setIsSaved(created);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void FriendCore::undo() { // Retrieve values from model
|
|
if (mFriendModel) {
|
|
mFriendModelConnection->invokeToModel([this]() {
|
|
FriendCore *contact = new FriendCore(*this);
|
|
contact->writeFromModel(mFriendModel);
|
|
contact->moveToThread(App::getInstance()->thread());
|
|
mFriendModelConnection->invokeToCore([this, contact]() mutable {
|
|
this->reset(*contact);
|
|
contact->deleteLater();
|
|
});
|
|
});
|
|
}
|
|
}
|