conf creation loading+error; fix info popup layout
move contact edition in contact page (switch to contact tab if creation requested)
fix contact creation + select new contact on creation
conference info list : creation signal (to finish when conference scheduler is updated, see comment)
fix crash if no vcard
fix calendar ui
+spacings
layout polish (! on string in meetinglist)
This commit is contained in:
Gaelle Braud 2024-04-23 09:23:00 +02:00
parent abba6cffaa
commit eb5b3b5141
48 changed files with 809 additions and 510 deletions

View file

@ -102,7 +102,7 @@ public:
void onLoggerInitialized();
QQuickWindow *getCallsWindow(QVariant callGui);
QQuickWindow *getCallsWindow(QVariant callGui = QVariant());
void setCallsWindowProperty(const char *id, QVariant property);
void closeCallsWindow();

View file

@ -136,7 +136,15 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mCallModelConnection->invokeToModel([this]() { mCallModel->stopRecording(); });
});
mCallModelConnection->makeConnectToModel(&CallModel::recordingChanged, [this](bool recording) {
mCallModelConnection->invokeToCore([this, recording]() { setRecording(recording); });
mCallModelConnection->invokeToCore([this, recording]() {
setRecording(recording);
if (recording == false) {
Utils::showInformationPopup(tr("Enregistrement terminé"),
tr("L'appel a été enregistré dans le fichier : %1")
.arg(QString::fromStdString(mCallModel->getRecordFile())),
true, App::getInstance()->getCallsWindow());
}
});
});
mCallModelConnection->makeConnectToCore(&CallCore::lVerifyAuthenticationToken, [this](bool verified) {
mCallModelConnection->invokeToModel(

View file

@ -194,33 +194,37 @@ void ConferenceInfoCore::setSelf(QSharedPointer<ConferenceInfoCore> me) {
&ConferenceInfoModel::schedulerStateChanged, [this](linphone::ConferenceScheduler::State state) {
auto confInfoState = mConferenceInfoModel->getState();
QString uri;
if (state == linphone::ConferenceScheduler::State::Ready)
if (state == linphone::ConferenceScheduler::State::Ready) {
uri = mConferenceInfoModel->getConferenceScheduler()->getUri();
}
mConfInfoModelConnection->invokeToCore([this, state = LinphoneEnums::fromLinphone(state),
infoState = LinphoneEnums::fromLinphone(confInfoState),
uri] {
lDebug() << "scheduler state changed" << state;
setConferenceSchedulerState(state);
setConferenceInfoState(infoState);
if (state == LinphoneEnums::ConferenceSchedulerState::Ready) {
setUri(uri);
mConfInfoModelConnection->invokeToModel([this, uri] {
CoreModel::getInstance()->conferenceInfoCreated(
mConferenceInfoModel->getConferenceInfo());
});
}
setConferenceSchedulerState(state);
});
});
mConfInfoModelConnection->makeConnectToModel(
&ConferenceInfoModel::infoStateChanged, [this](linphone::ConferenceInfo::State state) {
auto uri = mConferenceInfoModel->getConferenceScheduler()->getUri();
mConfInfoModelConnection->invokeToCore([this, infoState = LinphoneEnums::fromLinphone(state), uri] {
setConferenceInfoState(infoState);
});
});
mConfInfoModelConnection->makeConnectToModel(
&ConferenceInfoModel::invitationsSent,
[this](const std::list<std::shared_ptr<linphone::Address>> &failedInvitations) {
lDebug() << "invitations sent";
});
[this](const std::list<std::shared_ptr<linphone::Address>> &failedInvitations) {});
} else { // Create
mCoreModelConnection = QSharedPointer<SafeConnection<ConferenceInfoCore, CoreModel>>(
new SafeConnection<ConferenceInfoCore, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoReceived,
[this](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<const linphone::ConferenceInfo> &conferenceInfo) {
lDebug() << "CONF INFO RECEIVED ==================";
});
}
}
}
@ -566,6 +570,11 @@ void ConferenceInfoCore::save() {
});
} else {
mCoreModelConnection->invokeToModel([this, thisCopy]() {
if (CoreModel::getInstance()->getCore()->getDefaultAccount()->getState() !=
linphone::RegistrationState::Ok) {
Utils::showInformationPopup(tr("Erreur"), tr("Votre compte est déconnecté"), false);
return;
}
auto linphoneConf =
CoreModel::getInstance()->getCore()->findConferenceInformationFromUri(ToolModel::interpretUrl(mUri));

View file

@ -68,7 +68,17 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
int currentDateIndex = sort(*items);
add(*items);
updateHaveCurrentDate();
setCurrentDateIndex(mHaveCurrentDate ? currentDateIndex + 1 : currentDateIndex);
if (mLastConfInfoInserted) {
int index = -1;
// TODO : uncomment when linphone conference scheduler updated
// and model returns the scheduler conferenceInfo uri
index = findConfInfoIndexByUri(mLastConfInfoInserted->getUri());
// int index2;
// get(mLastConfInfoInserted.get(), &index2);
if (index != -1) setCurrentDateIndex(index);
else setCurrentDateIndex(mHaveCurrentDate ? currentDateIndex + 1 : currentDateIndex);
mLastConfInfoInserted = nullptr;
} else setCurrentDateIndex(mHaveCurrentDate ? currentDateIndex + 1 : currentDateIndex);
delete items;
});
});
@ -76,10 +86,20 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, &ConferenceInfoList::lUpdate);
mCoreModelConnection->makeConnectToModel(&CoreModel::conferenceInfoReceived, &ConferenceInfoList::lUpdate);
mCoreModelConnection->makeConnectToModel(&CoreModel::conferenceStateChanged, [this] {
lDebug() << "list: conf state changed";
lUpdate();
});
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoCreated, [this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) {
auto confInfoCore = ConferenceInfoCore::create(confInfo);
auto haveConf =
std::find_if(mList.begin(), mList.end(), [confInfoCore](const QSharedPointer<QObject> &item) {
auto isConfInfo = item.objectCast<ConferenceInfoCore>();
if (!isConfInfo) return false;
return isConfInfo->getUri() == confInfoCore->getUri();
});
if (haveConf == mList.end()) {
mLastConfInfoInserted = confInfoCore;
emit lUpdate();
}
});
mCoreModelConnection->makeConnectToModel(
&CoreModel::callCreated, [this](const std::shared_ptr<linphone::Call> &call) {
lDebug() << "call created" << Utils::coreStringToAppString(call->getRemoteAddress()->asString());
@ -128,19 +148,13 @@ void ConferenceInfoList::setCurrentDateIndex(int index) {
}
}
QSharedPointer<ConferenceInfoCore>
ConferenceInfoList::get(std::shared_ptr<linphone::ConferenceInfo> conferenceInfo) const {
auto uri = Utils::coreStringToAppString(conferenceInfo->getUri()->asStringUriOnly());
for (auto item : mList) {
auto model = item.objectCast<ConferenceInfoCore>();
if (model) {
auto confUri = model->getUri();
if (confUri == uri) {
return model;
}
}
int ConferenceInfoList::findConfInfoIndexByUri(const QString &uri) {
auto items = getSharedList<ConferenceInfoCore>();
for (int i = 0; i < items.size(); ++i) {
if (!items[i]) continue;
if (items[i]->getUri() == uri) return i;
}
return nullptr;
return -1;
}
QSharedPointer<ConferenceInfoCore>
@ -160,13 +174,6 @@ ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &confe
} else return nullptr;
}
void ConferenceInfoList::remove(const int &row) {
// List is modified asynchronously
// so no need to specify the begin/endRemoveRows
auto item = mList[row].objectCast<ConferenceInfoCore>();
if (item) emit item->lDeleteConferenceInfo();
}
QHash<int, QByteArray> ConferenceInfoList::roleNames() const {
QHash<int, QByteArray> roles;
roles[Qt::DisplayRole] = "$modelData";

View file

@ -46,10 +46,9 @@ public:
int getCurrentDateIndex() const;
void setCurrentDateIndex(int index);
QSharedPointer<ConferenceInfoCore> get(std::shared_ptr<linphone::ConferenceInfo> conferenceInfo) const;
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo) const;
int findConfInfoIndexByUri(const QString &uri);
void remove(const int &row);
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo) const;
QHash<int, QByteArray> roleNames() const override;
@ -64,6 +63,8 @@ signals:
private:
QSharedPointer<SafeConnection<ConferenceInfoList, CoreModel>> mCoreModelConnection;
std::shared_ptr<CoreModel> mCoreModel;
QSharedPointer<ConferenceInfoCore> mLastConfInfoInserted;
bool mHaveCurrentDate = false;
int mCurrentDateIndex = -1;
DECLARE_ABSTRACT_OBJECT

View file

@ -32,7 +32,6 @@ ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : SortFilterProxy(pare
invalidate();
updateCurrentDateIndex();
});
connect(this, &ConferenceInfoProxy::lUpdate, mList.get(), &ConferenceInfoList::lUpdate);
connect(mList.get(), &ConferenceInfoList::haveCurrentDateChanged, [this] {
invalidate();
updateCurrentDateIndex();
@ -77,10 +76,9 @@ bool ConferenceInfoProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sou
if (ciCore) {
if (!ciCore->getSubject().contains(mSearchText)) return false;
QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
// TODO : use enums
if (mFilterType == 0) {
if (mFilterType == int(ConferenceInfoProxy::ConferenceInfoFiltering::None)) {
return true;
} else if (mFilterType == 1) {
} else if (mFilterType == int(ConferenceInfoProxy::ConferenceInfoFiltering::Future)) {
auto res = ciCore->getEndDateTimeUtc() >= currentDateTime;
return res;
} else return mFilterType == -1;
@ -88,4 +86,4 @@ bool ConferenceInfoProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sou
return !mList->haveCurrentDate();
}
return false;
}
}

View file

@ -33,30 +33,31 @@ class ConferenceInfoProxy : public SortFilterProxy, public AbstractObject {
Q_PROPERTY(bool haveCurrentDate READ haveCurrentDate NOTIFY haveCurrentDateChanged)
Q_PROPERTY(int currentDateIndex READ getCurrentDateIndex NOTIFY currentDateIndexChanged)
public:
enum ConferenceInfoFiltering { None = 0, Future = 1 };
Q_ENUM(ConferenceInfoFiltering)
public:
ConferenceInfoProxy(QObject *parent = Q_NULLPTR);
~ConferenceInfoProxy();
QString getSearchText() const;
void setSearchText(const QString &search);
bool haveCurrentDate() const;
int getCurrentDateIndex() const;
void updateCurrentDateIndex();
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
// Do not use the sort feature. We cannot retrieve indexes with mapToSource because Qt return -1 for items that are not displayed.
// We need it to know where
// The workaround is to implement ourself the sort into the List.
//bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
// Do not use the sort feature. We cannot retrieve indexes with mapToSource because Qt return -1 for items that are
// not displayed. We need it to know where The workaround is to implement ourself the sort into the List.
// bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
signals:
void searchTextChanged();
void haveCurrentDateChanged();
void currentDateIndexChanged();
void lUpdate();
private:
QString mSearchText;

View file

@ -54,10 +54,12 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObje
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());
if (vcard) {
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(
@ -451,8 +453,8 @@ void FriendCore::remove() {
if (mFriendModel) { // Update
mFriendModelConnection->invokeToModel([this]() {
auto contact = mFriendModel->getFriend();
// emit CoreModel::getInstance()->friendRemoved(contact);
contact->remove();
emit CoreModel::getInstance()->friendRemoved();
mFriendModelConnection->invokeToCore([this]() { removed(this); });
});
}
@ -506,22 +508,24 @@ void FriendCore::save() { // Save Values to model
auto contact = core->createFriend();
auto friendModel = Utils::makeQObject_ptr<FriendModel>(contact);
friendModel->setSelf(friendModel);
mCoreModelConnection->invokeToCore([this, thisCopy, friendModel] {
mCoreModelConnection->invokeToCore([this, thisCopy, friendModel, contact] {
mFriendModel = friendModel;
mCoreModelConnection->invokeToModel([this, thisCopy] {
mCoreModelConnection->invokeToModel([this, thisCopy, contact] {
auto core = CoreModel::getInstance()->getCore();
thisCopy->writeIntoModel(mFriendModel);
thisCopy->deleteLater();
bool created =
(core->getDefaultFriendList()->addFriend(contact) == linphone::FriendList::Status::OK);
if (created) {
core->getDefaultFriendList()->updateSubscriptions();
emit CoreModel::getInstance()->friendCreated(contact);
}
mCoreModelConnection->invokeToCore([this, created]() {
if (created) setSelf(mCoreModelConnection->mCore);
setIsSaved(created);
});
});
});
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);
});
}
});
}

View file

@ -72,7 +72,6 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
&MagicSearchModel::aggregationFlagChanged, [this](LinphoneEnums::MagicSearchAggregation flag) {
mModelConnection->invokeToCore([this, flag]() { setAggregationFlag(flag); });
});
mModelConnection->makeConnectToModel(
&MagicSearchModel::searchResultsReceived,
[this](const std::list<std::shared_ptr<linphone::SearchResult>> &results) {
@ -83,12 +82,14 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
contact = FriendCore::create(it->getFriend());
contacts->append(contact);
} else if (auto address = it->getAddress()) {
contact = FriendCore::create(nullptr);
auto linphoneFriend = CoreModel::getInstance()->getCore()->createFriend();
contact = FriendCore::create(linphoneFriend);
contact->setGivenName(Utils::coreStringToAppString(address->asStringUriOnly()));
contact->appendAddress(Utils::coreStringToAppString(address->asStringUriOnly()));
contacts->append(contact);
} else if (!it->getPhoneNumber().empty()) {
contact = FriendCore::create(it->getFriend());
auto linphoneFriend = CoreModel::getInstance()->getCore()->createFriend();
contact = FriendCore::create(linphoneFriend);
contact->setGivenName(Utils::coreStringToAppString(it->getPhoneNumber()));
contact->appendPhoneNumber(tr("Phone"), Utils::coreStringToAppString(it->getPhoneNumber()));
contacts->append(contact);
@ -99,6 +100,26 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
delete contacts;
});
});
mCoreModelConnection = QSharedPointer<SafeConnection<MagicSearchList, CoreModel>>(
new SafeConnection<MagicSearchList, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
mCoreModelConnection->makeConnectToModel(
&CoreModel::friendCreated, [this](const std::shared_ptr<linphone::Friend> &f) {
auto friendCore = FriendCore::create(f);
auto haveContact =
std::find_if(mList.begin(), mList.end(), [friendCore](const QSharedPointer<QObject> &item) {
return item.objectCast<FriendCore>()->getDefaultAddress() == friendCore->getDefaultAddress();
});
if (haveContact == mList.end()) {
connect(friendCore.get(), &FriendCore::removed, this, qOverload<QObject *>(&MagicSearchList::remove));
add(friendCore);
int index = -1;
get(friendCore.get(), &index);
if (index != -1) {
emit friendCreated(index);
}
}
});
}
void MagicSearchList::setResults(const QList<QSharedPointer<FriendCore>> &contacts) {
@ -109,6 +130,9 @@ void MagicSearchList::setResults(const QList<QSharedPointer<FriendCore>> &contac
add(contacts);
}
void MagicSearchList::addResult(const QSharedPointer<FriendCore> &contact) {
}
void MagicSearchList::setSearch(const QString &search) {
if (!search.isEmpty()) {
lSearch(search);

View file

@ -42,6 +42,7 @@ public:
void setSelf(QSharedPointer<MagicSearchList> me);
void setSearch(const QString &search);
void setResults(const QList<QSharedPointer<FriendCore>> &contacts);
void addResult(const QSharedPointer<FriendCore> &contact);
int getSourceFlags() const;
void setSourceFlags(int flags);
@ -59,6 +60,8 @@ signals:
void sourceFlagsChanged(int sourceFlags);
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation flag);
void friendCreated(int index);
private:
int mSourceFlags;
LinphoneEnums::MagicSearchAggregation mAggregationFlag;

View file

@ -20,14 +20,17 @@
#include "MagicSearchProxy.hpp"
#include "MagicSearchList.hpp"
#include "core/friend/FriendGui.hpp"
MagicSearchProxy::MagicSearchProxy(QObject *parent) : SortFilterProxy(parent) {
mList = MagicSearchList::create();
connect(mList.get(), &MagicSearchList::sourceFlagsChanged, this, &MagicSearchProxy::sourceFlagsChanged);
connect(mList.get(), &MagicSearchList::aggregationFlagChanged, this, &MagicSearchProxy::aggregationFlagChanged);
connect(mList.get(), &MagicSearchList::friendCreated, this, [this](int index) {
auto proxyIndex = mapFromSource(sourceModel()->index(index, 0));
emit friendCreated(proxyIndex.row());
});
setSourceModel(mList.get());
connect(CoreModel::getInstance().get(), &CoreModel::friendRemoved, this,
[this] { emit mList->lSearch(mSearchText); });
connect(this, &MagicSearchProxy::forceUpdate, [this] { emit mList->lSearch(mSearchText); });
sort(0);
}
@ -58,4 +61,19 @@ LinphoneEnums::MagicSearchAggregation MagicSearchProxy::getAggregationFlag() con
void MagicSearchProxy::setAggregationFlag(LinphoneEnums::MagicSearchAggregation flag) {
qobject_cast<MagicSearchList *>(sourceModel())->lSetAggregationFlag(flag);
}
bool MagicSearchProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
auto l = sourceModel()->data(left);
auto r = sourceModel()->data(right);
auto lIsFriend = l.value<FriendGui *>();
auto rIsFriend = r.value<FriendGui *>();
if (lIsFriend && rIsFriend) {
auto lName = lIsFriend->getCore()->getDisplayName().toLower();
auto rName = rIsFriend->getCore()->getDisplayName().toLower();
return lName < rName;
}
return true;
}

View file

@ -55,10 +55,12 @@ signals:
void sourceFlagsChanged(int sourceFlags);
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag);
void forceUpdate();
void friendCreated(int index);
protected:
QString mSearchText;
QSharedPointer<MagicSearchList> mList;
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
};
#endif

View file

@ -0,0 +1,32 @@
<svg width="28" height="26" viewBox="0 0 28 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3638_14815)" filter="url(#filter0_d_3638_14815)">
<g filter="url(#filter1_d_3638_14815)">
<path d="M21.125 9.25C21.125 9.4725 21.059 9.69001 20.9354 9.87502C20.8118 10.06 20.6361 10.2042 20.4305 10.2894C20.225 10.3745 19.9988 10.3968 19.7805 10.3534C19.5623 10.31 19.3618 10.2028 19.2045 10.0455C19.0472 9.88816 18.94 9.68771 18.8966 9.46948C18.8532 9.25125 18.8755 9.02505 18.9606 8.81948C19.0458 8.61391 19.19 8.43821 19.375 8.3146C19.56 8.19098 19.7775 8.125 20 8.125C20.2984 8.125 20.5845 8.24353 20.7955 8.4545C21.0065 8.66548 21.125 8.95163 21.125 9.25ZM20 15.625C19.7775 15.625 19.56 15.691 19.375 15.8146C19.19 15.9382 19.0458 16.1139 18.9606 16.3195C18.8755 16.525 18.8532 16.7512 18.8966 16.9695C18.94 17.1877 19.0472 17.3882 19.2045 17.5455C19.3618 17.7028 19.5623 17.81 19.7805 17.8534C19.9988 17.8968 20.225 17.8745 20.4305 17.7894C20.6361 17.7042 20.8118 17.56 20.9354 17.375C21.059 17.19 21.125 16.9725 21.125 16.75C21.125 16.4516 21.0065 16.1655 20.7955 15.9545C20.5845 15.7435 20.2984 15.625 20 15.625ZM11 10.75C10.7033 10.75 10.4133 10.838 10.1666 11.0028C9.91997 11.1676 9.72771 11.4019 9.61418 11.676C9.50065 11.9501 9.47094 12.2517 9.52882 12.5426C9.5867 12.8336 9.72956 13.1009 9.93934 13.3107C10.1491 13.5204 10.4164 13.6633 10.7074 13.7212C10.9983 13.7791 11.2999 13.7494 11.574 13.6358C11.8481 13.5223 12.0824 13.33 12.2472 13.0834C12.412 12.8367 12.5 12.5467 12.5 12.25C12.5 11.8522 12.342 11.4706 12.0607 11.1893C11.7794 10.908 11.3978 10.75 11 10.75ZM23.75 6.25V19.75C23.75 20.1478 23.592 20.5294 23.3107 20.8107C23.0294 21.092 22.6478 21.25 22.25 21.25H5.75C5.35218 21.25 4.97064 21.092 4.68934 20.8107C4.40804 20.5294 4.25 20.1478 4.25 19.75V6.25C4.25 5.85218 4.40804 5.47064 4.68934 5.18934C4.97064 4.90804 5.35218 4.75 5.75 4.75H22.25C22.6478 4.75 23.0294 4.90804 23.3107 5.18934C23.592 5.47064 23.75 5.85218 23.75 6.25ZM17.75 12.25H22.25V6.25H17.75V12.25ZM14.7266 16.5625C14.4839 15.6563 13.9084 14.875 13.115 14.3744C13.5363 13.9555 13.8238 13.421 13.941 12.8386C14.0582 12.2562 13.9999 11.652 13.7733 11.1028C13.5468 10.5536 13.1623 10.084 12.6686 9.75358C12.1748 9.42315 11.5941 9.24675 11 9.24675C10.4059 9.24675 9.82517 9.42315 9.33143 9.75358C8.8377 10.084 8.45319 10.5536 8.22666 11.1028C8.00012 11.652 7.94175 12.2562 8.05896 12.8386C8.17616 13.421 8.46366 13.9555 8.885 14.3744C8.09211 14.8755 7.51681 15.6566 7.27344 16.5625C7.22371 16.7552 7.25257 16.9598 7.35366 17.1312C7.45475 17.3026 7.6198 17.4268 7.8125 17.4766C8.0052 17.5263 8.20975 17.4974 8.38118 17.3963C8.55259 17.2952 8.67683 17.1302 8.72656 16.9375C8.97406 15.9766 9.95094 15.25 11 15.25C12.0491 15.25 13.0269 15.9747 13.2734 16.9375C13.3232 17.1302 13.4474 17.2952 13.6188 17.3963C13.7902 17.4974 13.9948 17.5263 14.1875 17.4766C14.3802 17.4268 14.5452 17.3026 14.6463 17.1312C14.7474 16.9598 14.7763 16.7552 14.7266 16.5625ZM22.25 19.75V13.75H17.75V19.75H22.25Z" fill="white"/>
</g>
</g>
<defs>
<filter id="filter0_d_3638_14815" x="-2" y="-3" width="32" height="32" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3638_14815"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3638_14815" result="shape"/>
</filter>
<filter id="filter1_d_3638_14815" x="0.25" y="0.75" width="27.5" height="24.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3638_14815"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3638_14815" result="shape"/>
</filter>
<clipPath id="clip0_3638_14815">
<rect width="24" height="24" fill="white" transform="translate(2 1)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,80H168V56h48ZM40,56H152V200H40ZM216,200H168V136h48v64ZM180,88a12,12,0,1,1,12,12A12,12,0,0,1,180,88Zm24,80a12,12,0,1,1-12-12A12,12,0,0,1,204,168Zm-68.25-2a39.76,39.76,0,0,0-17.19-23.34,32,32,0,1,0-45.12,0A39.84,39.84,0,0,0,56.25,166a8,8,0,0,0,15.5,4c2.64-10.25,13.06-18,24.25-18s21.62,7.73,24.25,18a8,8,0,1,0,15.5-4ZM80,120a16,16,0,1,1,16,16A16,16,0,0,1,80,120Z"></path></svg>

After

Width:  |  Height:  |  Size: 582 B

View file

@ -185,7 +185,6 @@ void CallModel::stopRecording() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->stopRecording();
emit recordingChanged(mMonitor->getParams()->isRecording());
// TODO : display notification
}
void CallModel::setRecordFile(const std::string &path) {

View file

@ -45,6 +45,10 @@ void ConferenceInfoModel::createConferenceScheduler() {
mustBeInLinphoneThread(getClassName() + "::createConferenceScheduler()");
}
std::shared_ptr<linphone::ConferenceInfo> ConferenceInfoModel::getConferenceInfo() const {
return mConferenceInfo;
}
std::shared_ptr<ConferenceSchedulerModel> ConferenceInfoModel::getConferenceScheduler() const {
return mConferenceSchedulerModel;
}
@ -106,7 +110,9 @@ QString ConferenceInfoModel::getOrganizerName() const {
}
QString ConferenceInfoModel::getOrganizerAddress() const {
return Utils::coreStringToAppString(mConferenceInfo->getOrganizer()->asStringUriOnly());
if (auto organizer = mConferenceInfo->getOrganizer())
return Utils::coreStringToAppString(organizer->asStringUriOnly());
return QString();
}
QString ConferenceInfoModel::getDescription() const {
@ -118,7 +124,7 @@ QString ConferenceInfoModel::getUri() const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (auto uriAddr = mConferenceInfo->getUri()) {
return Utils::coreStringToAppString(uriAddr->asString());
} else return QString();
} else return mConferenceSchedulerModel->getUri();
}
std::list<std::shared_ptr<linphone::ParticipantInfo>> ConferenceInfoModel::getParticipantInfos() const {

View file

@ -36,6 +36,8 @@ public:
void createConferenceScheduler();
std::shared_ptr<linphone::ConferenceInfo> getConferenceInfo() const;
std::shared_ptr<ConferenceSchedulerModel> getConferenceScheduler() const;
void setConferenceScheduler(const std::shared_ptr<ConferenceSchedulerModel> &model);
QDateTime getDateTime() const;

View file

@ -86,8 +86,6 @@ void ConferenceModel::startRecording() {
void ConferenceModel::stopRecording() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->stopRecording();
// emit recordingChanged(mMonitor->getParams()->isRecording());
// TODO : display notification
}
void ConferenceModel::setRecordFile(const std::string &path) {

View file

@ -48,6 +48,10 @@ QString ConferenceSchedulerModel::getUri() {
} else return QString();
}
linphone::ConferenceScheduler::State ConferenceSchedulerModel::getState() const {
return mState;
}
void ConferenceSchedulerModel::setInfo(const std::shared_ptr<linphone::ConferenceInfo> &confInfo) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->setInfo(confInfo);
@ -60,6 +64,7 @@ void ConferenceSchedulerModel::cancelConference(const std::shared_ptr<linphone::
void ConferenceSchedulerModel::onStateChanged(const std::shared_ptr<linphone::ConferenceScheduler> &conferenceScheduler,
linphone::ConferenceScheduler::State state) {
mState = state;
emit stateChanged(state);
}

View file

@ -39,6 +39,7 @@ public:
~ConferenceSchedulerModel();
QString getUri();
linphone::ConferenceScheduler::State getState() const;
void setInfo(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
void cancelConference(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
@ -48,6 +49,7 @@ signals:
private:
DECLARE_ABSTRACT_OBJECT
linphone::ConferenceScheduler::State mState;
//--------------------------------------------------------------------------------
// LINPHONE

View file

@ -56,8 +56,9 @@ public:
signals:
void loggerInitialized();
void friendAdded();
void friendRemoved();
void friendCreated(const std::shared_ptr<linphone::Friend> &f);
void friendRemoved(const std::shared_ptr<linphone::Friend> &f);
void conferenceInfoCreated(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
void unreadNotificationsChanged();
private:

View file

@ -150,70 +150,90 @@ void FriendModel::setName(const QString &name) {
QString FriendModel::getGivenName() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
return Utils::coreStringToAppString(mMonitor->getVcard()->getGivenName());
if (mMonitor->getVcard()) return Utils::coreStringToAppString(mMonitor->getVcard()->getGivenName());
else return QString();
}
void FriendModel::setGivenName(const QString &name) {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
if (mMonitor->getVcard()) {
mMonitor->getVcard()->setGivenName(Utils::appStringToCoreString(name));
emit givenNameChanged(name);
}
mMonitor->getVcard()->setGivenName(Utils::appStringToCoreString(name));
emit givenNameChanged(name);
}
QString FriendModel::getFamilyName() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
return Utils::coreStringToAppString(mMonitor->getVcard()->getFamilyName());
if (mMonitor->getVcard()) return Utils::coreStringToAppString(mMonitor->getVcard()->getFamilyName());
else return QString();
}
void FriendModel::setFamilyName(const QString &name) {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
if (mMonitor->getVcard()) {
mMonitor->getVcard()->setFamilyName(Utils::appStringToCoreString(name));
emit familyNameChanged(name);
}
mMonitor->getVcard()->setFamilyName(Utils::appStringToCoreString(name));
emit familyNameChanged(name);
}
QString FriendModel::getOrganization() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
return Utils::coreStringToAppString(mMonitor->getVcard()->getOrganization());
if (mMonitor->getVcard()) return Utils::coreStringToAppString(mMonitor->getVcard()->getOrganization());
else return QString();
}
void FriendModel::setOrganization(const QString &orga) {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
if (mMonitor->getVcard()) {
mMonitor->getVcard()->setOrganization(Utils::appStringToCoreString(orga));
emit organizationChanged(orga);
}
mMonitor->getVcard()->setOrganization(Utils::appStringToCoreString(orga));
emit organizationChanged(orga);
}
QString FriendModel::getJob() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
return Utils::coreStringToAppString(mMonitor->getVcard()->getJobTitle());
if (mMonitor->getVcard()) return Utils::coreStringToAppString(mMonitor->getVcard()->getJobTitle());
else return QString();
}
void FriendModel::setJob(const QString &job) {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
if (mMonitor->getVcard()) {
mMonitor->getVcard()->setJobTitle(Utils::appStringToCoreString(job));
emit jobChanged(job);
}
mMonitor->getVcard()->setJobTitle(Utils::appStringToCoreString(job));
emit jobChanged(job);
}
bool FriendModel::getStarred() const {
@ -231,10 +251,12 @@ void FriendModel::onPresenceReceived(const std::shared_ptr<linphone::Friend> &co
QString FriendModel::getPictureUri() const {
auto vcard = mMonitor->getVcard();
bool created = false;
if (!vcard) {
mMonitor->createVcard(mMonitor->getName());
created = mMonitor->createVcard(mMonitor->getName());
}
return Utils::coreStringToAppString(mMonitor->getVcard()->getPhoto());
if (mMonitor->getVcard()) return Utils::coreStringToAppString(mMonitor->getVcard()->getPhoto());
else return QString();
}
void FriendModel::setPictureUri(const QString &uri) {

View file

@ -30,11 +30,6 @@ DEFINE_ABSTRACT_OBJECT(MagicSearchModel)
MagicSearchModel::MagicSearchModel(const std::shared_ptr<linphone::MagicSearch> &data, QObject *parent)
: ::Listener<linphone::MagicSearch, linphone::MagicSearchListener>(data, parent) {
mustBeInLinphoneThread(getClassName());
// Removed is managed by FriendCore that allow to remove result automatically.
// No need to restart a new search in this case
connect(CoreModel::getInstance().get(), &CoreModel::friendAdded, this, [this]() {
if (!mLastSearch.isEmpty()) search(mLastSearch);
});
}
MagicSearchModel::~MagicSearchModel() {

View file

@ -187,9 +187,9 @@ linphone::ConferenceInfo::State toLinphone(const LinphoneEnums::ConferenceInfoSt
LinphoneEnums::ConferenceInfoState fromLinphone(const linphone::ConferenceInfo::State &state);
enum class ConferenceSchedulerState {
AllocationPending = int(linphone::ConferenceScheduler::State::AllocationPending),
Error = int(linphone::ConferenceScheduler::State::Error),
Idle = int(linphone::ConferenceScheduler::State::Idle),
Error = int(linphone::ConferenceScheduler::State::Error),
AllocationPending = int(linphone::ConferenceScheduler::State::AllocationPending),
Ready = int(linphone::ConferenceScheduler::State::Ready),
Updating = int(linphone::ConferenceScheduler::State::Updating)
};

View file

@ -111,6 +111,9 @@ void Utils::createCall(const QString &sipAddress,
// TODO : change conf info only from qml
// (bug si on est déjà en appel et qu'on lance une conf)
// demander à jonhatan pour le design : quand on est déjà en appel
// et qu'on join une conf on retourne donc sur la waiting room
// Comment on annule ? Si on ferme la fenêtre ça va finir l'appel en cours
void Utils::setupConference(ConferenceInfoGui *confGui) {
if (!confGui) return;
auto window = App::getInstance()->getCallsWindow(QVariant());
@ -1124,6 +1127,10 @@ QString Utils::toDateMonthString(const QDateTime &date) {
return QLocale().toString(date, "MMMM");
}
QString Utils::toDateMonthAndYearString(const QDateTime &date) {
return QLocale().toString(date, "MMMM yyyy");
}
bool Utils::isCurrentDay(QDateTime date) {
auto dateDayNum = date.date().day();
auto currentDate = QDateTime::currentDateTime();
@ -1152,6 +1159,10 @@ bool Utils::datesAreEqual(const QDate &a, const QDate &b) {
return a.month() == b.month() && a.year() == b.year() && a.day() == b.day();
}
bool Utils::dateisInMonth(const QDate &a, int month, int year) {
return a.month() == month && a.year() == year;
}
QDateTime Utils::createDateTime(const QDate &date, int hour, int min) {
QTime time(hour, min);
return QDateTime(date, time);
@ -1177,6 +1188,11 @@ QDateTime Utils::addSecs(QDateTime date, int secs) {
return date;
}
QDateTime Utils::addYears(QDateTime date, int years) {
date = date.addYears(years);
return date;
}
int Utils::getYear(const QDate &date) {
return date.year();
}

View file

@ -87,17 +87,20 @@ public:
Q_INVOKABLE static QString toDateHourString(const QDateTime &date);
Q_INVOKABLE static QString toDateDayNameString(const QDateTime &date);
Q_INVOKABLE static QString toDateMonthString(const QDateTime &date);
Q_INVOKABLE static QString toDateMonthAndYearString(const QDateTime &date);
Q_INVOKABLE static bool isCurrentDay(QDateTime date);
Q_INVOKABLE static bool isCurrentDay(QDate date);
Q_INVOKABLE static bool isCurrentMonth(QDate date);
Q_INVOKABLE static bool isBeforeToday(QDate date);
Q_INVOKABLE static bool datesAreEqual(const QDate &a, const QDate &b);
Q_INVOKABLE static bool dateisInMonth(const QDate &a, int month, int year);
Q_INVOKABLE static QDateTime createDateTime(const QDate &date, int hour, int min);
Q_INVOKABLE static QDateTime getCurrentDateTime();
Q_INVOKABLE static QDateTime getCurrentDateTimeUtc();
Q_INVOKABLE static int getYear(const QDate &date);
Q_INVOKABLE static int secsTo(const QString &start, const QString &end);
Q_INVOKABLE static QDateTime addSecs(QDateTime date, int secs);
Q_INVOKABLE static QDateTime addYears(QDateTime date, int years);
Q_INVOKABLE static QString generateLinphoneSipAddress(const QString &uri);
Q_INVOKABLE static QString findAvatarByAddress(const QString &address);
static QString generateSavedFilename(const QString &from, const QString &to);

View file

@ -275,7 +275,6 @@ Window {
Layout.preferredWidth: 30 * DefaultStyle.dp
Layout.preferredHeight: 30 * DefaultStyle.dp
// TODO : change with broadcast or meeting icon when available
// +
imageSource: !mainWindow.call
? AppIcons.meeting
: (mainWindow.callState === LinphoneEnums.CallState.End
@ -725,18 +724,16 @@ Window {
Connections {
target: participantsStack
onCurrentItemChanged: {
console.log("changing title", participantsStack.currentItem == participantList)
if (participantsStack.currentItem == participantList) rightPanel.headerTitleText = qsTr("Participants (%1)").arg(participantList.count)
}
}
Connections {
target: rightPanel
// TODO : chercher comment relier ces infos pour faire le add des participants
//onValidateRequested: {
// participantList.model.addAddresses(participantsStack.selectedParticipants)
// participantsStack.pop()
// participantsStack.participantAdded()
//}
onValidateRequested: {
participantList.model.addAddresses(participantsStack.selectedParticipants)
participantsStack.pop()
participantsStack.participantAdded()
}
}
}
}

View file

@ -26,6 +26,11 @@ Item {
transferSucceedPopup.open()
}
function createContact(name, address) {
tabbar.currentIndex = 1
contactPage.createContact(name, address)
}
Timer {
id: autoClosePopup
interval: 5000
@ -119,7 +124,7 @@ Item {
{icon: AppIcons.phone, selectedIcon: AppIcons.phoneSelected, label: qsTr("Appels")},
{icon: AppIcons.adressBook, selectedIcon: AppIcons.adressBookSelected, label: qsTr("Contacts")},
{icon: AppIcons.chatTeardropText, selectedIcon: AppIcons.chatTeardropTextSelected, label: qsTr("Conversations")},
{icon: AppIcons.usersThree, selectedIcon: AppIcons.usersThreeSelected, label: qsTr("Réunions")}
{icon: AppIcons.videoconference, selectedIcon: AppIcons.videoconferenceSelected, label: qsTr("Réunions")}
]
}
@ -269,9 +274,8 @@ Item {
Item {Layout.fillWidth: true}
}
onClicked: {
var currentItem = mainStackLayout.children[mainStackLayout.currentIndex]
mainItem.createContact(magicSearchBar.text, sipAddr.text)
listPopup.close()
currentItem.createContact(magicSearchBar.text, sipAddr.text)
}
}
}
@ -360,8 +364,13 @@ Item {
Layout.topMargin: 24 * DefaultStyle.dp
CallPage {
id: callPage
onCreateContactRequested: (name, address) => {
mainItem.createContact(name, address)
}
}
ContactPage{
id: contactPage
}
ContactPage{}
Item{}
//ConversationPage{}
MeetingPage{}

View file

@ -33,6 +33,10 @@ ApplicationWindow {
mainWindowStackView.currentItem.transferCallSucceed()
}
function removeFromPopupLayout(index) {
popupLayout.popupList.splice(index, 1)
}
Component {
id: popupComp
InformationPopup{}
@ -42,26 +46,44 @@ ApplicationWindow {
infoPopup.index = popupLayout.popupList.length
popupLayout.popupList.push(infoPopup)
infoPopup.open()
infoPopup.closePopup.connect(removeFromPopupLayout)
}
function showLoadingPopup(text) {
loadingPopup.text = text
loadingPopup.open()
}
function closeLoadingPopup() {
loadingPopup.close()
}
ColumnLayout {
id: popupLayout
anchors.fill: parent
Layout.alignment: Qt.AlignBottom
property int nextY: mainWindow.height
property list<Popup> popupList
property list<InformationPopup> popupList
property int popupCount: popupList.length
spacing: 15
spacing: 15 * DefaultStyle.dp
onPopupCountChanged: {
nextY = mainWindow.height
for(var i = 0; i < popupCount; ++i) {
popupList[i].y = nextY - popupList[i].height
popupList[i].index = i
nextY = nextY - popupList[i].height - 15
}
}
}
LoadingPopup {
id: loadingPopup
modal: true
closePolicy: Popup.NoAutoClose
anchors.centerIn: parent
padding: 20 * DefaultStyle.dp
underlineColor: DefaultStyle.main1_500_main
radius: 15 * DefaultStyle.dp
}
AccountProxy {
// TODO : change this so it does not display the main page for one second
// when we fail trying to connect the first account (account is added and

View file

@ -14,6 +14,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Layout/FormItemLayout.qml
view/Layout/Mosaic.qml
view/Layout/RightPanelLayout.qml
view/Layout/Section.qml
view/Item/Account/Accounts.qml
@ -58,6 +59,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/ErrorText.qml
view/Item/IconLabelButton.qml
view/Item/InformationPopup.qml
view/Item/LoadingPopup.qml
view/Item/MovableMouseArea.qml
view/Item/NumericPad.qml
view/Item/PhoneNumberComboBox.qml

View file

@ -7,35 +7,39 @@ import Linphone
import ConstantsCpp 1.0
import UtilsCpp 1.0
// TODO : spacing
ListView {
id: mainItem
// width: 400 * DefaultStyle.dp
// height: 400 * DefaultStyle.dp
snapMode: ListView.SnapOneItem
orientation: Qt.Horizontal
clip: true
property int maxYears: 5
readonly property var currentDate: new Date()
highlightMoveDuration: 100
// height: contentHeight
property var selectedDate
property int currentMonth: calendarModel.monthAt(currentIndex) + 1 //january is index 0
property int currentYear: calendarModel.yearAt(currentIndex)
onCurrentYearChanged: console.log("currentyear", currentYear)
onCurrentMonthChanged: console.log("current month", currentMonth)
model: Control.CalendarModel {
id: calendarModel
from: new Date()
// TODO : dynamically add 5 years
to: new Date(2025, 12, 31)
to: UtilsCpp.addYears(new Date(), 5)
}
delegate: ColumnLayout {
width: mainItem.width
height: mainItem.height
property int currentMonth: model.month
spacing: 18 * DefaultStyle.dp
RowLayout {
Layout.fillWidth: true
spacing: 38 * DefaultStyle.dp
Text {
// TODO (high prio): don't use javascript, C++
text: new Date(model.year, model.month, 15).toLocaleString(Qt.locale(ConstantsCpp.DefaultLocale), 'MMMM yyyy')// 15 because of timezones that can change the date for localeString
text: UtilsCpp.toDateMonthAndYearString(new Date(model.year, model.month, 15))// 15 because of timezones that can change the date for localeString
font {
pixelSize: 14 * DefaultStyle.dp
weight: 700 * DefaultStyle.dp
@ -52,7 +56,7 @@ ListView {
icon.height: height
background: Item{}
icon.source: AppIcons.leftArrow
onClicked: if (mainItem.currentIndex > 0) --mainItem.currentIndex
onClicked: if (mainItem.currentIndex > 0) mainItem.currentIndex = mainItem.currentIndex - 1
}
Button {
Layout.preferredWidth: 20 * DefaultStyle.dp
@ -61,62 +65,69 @@ ListView {
icon.height: height
background: Item{}
icon.source: AppIcons.rightArrow
onClicked: if (mainItem.currentIndex < mainItem.count) ++mainItem.currentIndex
}
}
Control.DayOfWeekRow {
locale: monthGrid.locale
Layout.column: 1
Layout.fillWidth: true
delegate: Text {
text: model.shortName
color: DefaultStyle.main2_400
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
onClicked: if (mainItem.currentIndex < mainItem.count) mainItem.currentIndex = mainItem.currentIndex + 1
}
}
Control.MonthGrid {
id: monthGrid
Layout.fillWidth: true
Layout.fillHeight: true
year: model.year
month: model.month
// locale: Qt.locale("en_US")
delegate: Item {
property bool isSelectedDay: mainItem.selectedDate ? UtilsCpp.datesAreEqual(mainItem.selectedDate, model.date) : false
Rectangle {
anchors.centerIn: parent
width: 30 * DefaultStyle.dp
height: 30 * DefaultStyle.dp
radius: 50 * DefaultStyle.dp
color: isSelectedDay ? DefaultStyle.main1_500_main : "transparent"
}
Text {
anchors.centerIn: parent
text: monthGrid.locale.toString(model.date, "d")
color: isSelectedDay
? DefaultStyle.grey_0
: UtilsCpp.isCurrentDay(model.date)
? DefaultStyle.main1_500_main
: UtilsCpp.isCurrentMonth(model.date)
? DefaultStyle.main2_700
: DefaultStyle.main2_400
ColumnLayout {
spacing: 12 * DefaultStyle.dp
Control.DayOfWeekRow {
locale: monthGrid.locale
Layout.column: 1
Layout.fillWidth: true
delegate: Text {
text: model.shortName
color: DefaultStyle.main2_400
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
onClicked: (date) => {
if (UtilsCpp.isBeforeToday(date)) return;
mainItem.selectedDate = date
}
}
Control.MonthGrid {
id: monthGrid
Layout.fillWidth: true
Layout.fillHeight: true
year: model.year
month: model.month
property var curDate: model.date
onMonthChanged: console.log("cur date changed", month)
locale: Qt.locale(ConstantsCpp.DefaultLocale)
delegate: Item {
property bool isSelectedDay: mainItem.selectedDate ? UtilsCpp.datesAreEqual(mainItem.selectedDate, model.date) : false
// width: 30 * DefaultStyle.dp
// height: 30 * DefaultStyle.dp
Rectangle {
anchors.centerIn: parent
width: 30 * DefaultStyle.dp
height: 30 * DefaultStyle.dp
radius: 50 * DefaultStyle.dp
color: isSelectedDay ? DefaultStyle.main1_500_main : "transparent"
}
Text {
anchors.centerIn: parent
text: UtilsCpp.toDateDayString(model.date)
color: isSelectedDay
? DefaultStyle.grey_0
: UtilsCpp.isCurrentDay(model.date)
? DefaultStyle.main1_500_main
: UtilsCpp.dateisInMonth(model.date, mainItem.currentMonth, mainItem.currentYear)
? DefaultStyle.main2_700
: DefaultStyle.main2_400
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
onClicked: (date) => {
if (UtilsCpp.isBeforeToday(date)) return;
mainItem.selectedDate = date
}
}
}
}
}

View file

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls as Control
import QtQuick.Effects
import QtQuick.Layouts
import Linphone
ComboBox {
@ -25,7 +26,12 @@ ComboBox {
width: 321 * DefaultStyle.dp
height: 270 * DefaultStyle.dp
closePolicy: Popup.NoAutoClose
topPadding: 25 * DefaultStyle.dp
bottomPadding: 24 * DefaultStyle.dp
leftPadding: 21 * DefaultStyle.dp
rightPadding: 19 * DefaultStyle.dp
background: Item {
anchors.fill: parent
Rectangle {
id: calendarBg
anchors.fill: parent

View file

@ -19,7 +19,7 @@ Button {
background: Rectangle {
anchors.fill: parent
color: mainItem.backgroundColor
radius: mainItem.width * 1.29
radius: mainItem.width /2
}
icon.source: checkedIconUrl && mainItem.checked ? checkedIconUrl : iconUrl
icon.width: width * 0.58

View file

@ -7,265 +7,281 @@ import QtQuick.Layouts
import Linphone
import UtilsCpp 1.0
ColumnLayout {
RightPanelLayout {
id: mainItem
property FriendGui contact
Connections {
target: contact.core
onIsSavedChanged: {
if (contact.core.isSaved) {
var mainWin = UtilsCpp.getMainWindow()
UtilsCpp.smartShowWindow(mainWin)
mainWin.goToContactDetail(contact)
}
}
}
property string title: qsTr("Modifier contact")
property string saveButtonText: qsTr("Enregistrer")
property string oldPictureUri
signal closeEdition()
Rectangle {
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.fillWidth: true
Layout.preferredHeight: 40 * DefaultStyle.dp
Text {
anchors.left: parent.left
anchors.leftMargin: 10 * DefaultStyle.dp
text: mainItem.title
font {
pixelSize: 20 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
headerContent: [
Text {
anchors.left: parent.left
anchors.leftMargin: 31 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
text: mainItem.title
font {
pixelSize: 20 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
},
Button {
background: Item{}
anchors.right: parent.right
anchors.rightMargin: 41 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.source: AppIcons.closeX
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: {
// contact.core.pictureUri = mainItem.oldPictureUri
mainItem.contact.core.undo()
mainItem.closeEdition()
}
}
}
Button {
background: Item{}
anchors.right: parent.right
anchors.rightMargin: 10 * DefaultStyle.dp
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.source: AppIcons.closeX
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: {
// contact.core.pictureUri = mainItem.oldPictureUri
mainItem.contact.core.undo()
mainItem.closeEdition()
}
}
]
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 69 * DefaultStyle.dp
Avatar {
contact: mainItem.contact
Layout.preferredWidth: 72 * DefaultStyle.dp
Layout.preferredHeight: 72 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
}
IconLabelButton {
visible: !mainItem.contact || mainItem.contact.core.pictureUri.length === 0
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.camera
iconSize: 17 * DefaultStyle.dp
text: qsTr("Ajouter une image")
onClicked: fileDialog.open()
}
RowLayout {
visible: mainItem.contact && mainItem.contact.core.pictureUri.length != 0
content: ColumnLayout {
anchors.centerIn: parent
// anchors.leftMargin: 103 * DefaultStyle.dp
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 69 * DefaultStyle.dp
Avatar {
contact: mainItem.contact
Layout.preferredWidth: 72 * DefaultStyle.dp
Layout.preferredHeight: 72 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
}
IconLabelButton {
visible: !mainItem.contact || mainItem.contact.core.pictureUri.length === 0
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.pencil
iconSource: AppIcons.camera
iconSize: 17 * DefaultStyle.dp
text: qsTr("Modifier")
text: qsTr("Ajouter une image")
onClicked: fileDialog.open()
}
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
mainItem.oldPictureUri = mainItem.contact.core.pictureUri
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
mainItem.contact.core.pictureUri = avatarPath
RowLayout {
visible: mainItem.contact && mainItem.contact.core.pictureUri.length != 0
Layout.alignment: Qt.AlignHCenter
IconLabelButton {
Layout.preferredWidth: width
Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.pencil
iconSize: 17 * DefaultStyle.dp
text: qsTr("Modifier")
onClicked: fileDialog.open()
}
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
mainItem.oldPictureUri = mainItem.contact.core.pictureUri
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
mainItem.contact.core.pictureUri = avatarPath
}
}
}
}
IconLabelButton {
Layout.preferredHeight: 17 * DefaultStyle.dp
Layout.preferredWidth: width
iconSize: 17 * DefaultStyle.dp
iconSource: AppIcons.trashCan
text: qsTr("Supprimer")
onClicked: mainItem.contact.core.pictureUri = ""
IconLabelButton {
Layout.preferredHeight: 17 * DefaultStyle.dp
Layout.preferredWidth: width
iconSize: 17 * DefaultStyle.dp
iconSource: AppIcons.trashCan
text: qsTr("Supprimer")
onClicked: mainItem.contact.core.pictureUri = ""
}
}
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 100 * DefaultStyle.dp
Layout.topMargin: 50 * DefaultStyle.dp
Layout.bottomMargin: 50 * DefaultStyle.dp
ColumnLayout {
spacing: 20 * DefaultStyle.dp
FormItemLayout {
label: qsTr("Prénom")
contentItem: TextField {
initialText: contact.core.givenName
onEditingFinished: contact.core.givenName = text
backgroundColor: DefaultStyle.grey_0
}
}
FormItemLayout {
label: qsTr("Nom")
contentItem: TextField {
initialText: contact.core.familyName
onEditingFinished: contact.core.familyName = text
backgroundColor: DefaultStyle.grey_0
}
}
FormItemLayout {
label: qsTr("Entreprise")
contentItem: TextField {
initialText: contact.core.organization
onEditingFinished: contact.core.organization = text
backgroundColor: DefaultStyle.grey_0
}
}
FormItemLayout {
label: qsTr("Fonction")
contentItem: TextField {
initialText: contact.core.job
onEditingFinished: contact.core.job = text
backgroundColor: DefaultStyle.grey_0
}
}
Item{Layout.fillHeight: true}
}
Control.ScrollView {
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
contentHeight: content.height
Layout.fillWidth: true
spacing: 100 * DefaultStyle.dp
Layout.topMargin: 50 * DefaultStyle.dp
Layout.bottomMargin: 50 * DefaultStyle.dp
ColumnLayout {
id: content
anchors.rightMargin: 10 * DefaultStyle.dp
spacing: 20 * DefaultStyle.dp
Repeater {
model: VariantList {
model: mainItem.contact && mainItem.contact.core.addresses || []
}
delegate: RowLayout {
FormItemLayout {
label: modelData.label
contentItem: TextField {
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.setAddressAt(index, qsTr("Address SIP"), text)
}
initialText: modelData.address
backgroundColor: DefaultStyle.grey_0
}
}
Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
background: Item{}
icon.source: AppIcons.closeX
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: mainItem.contact.core.removeAddress(index)
}
FormItemLayout {
label: qsTr("Prénom")
contentItem: TextField {
initialText: contact.core.givenName
onEditingFinished: contact.core.givenName = text
backgroundColor: DefaultStyle.grey_0
}
}
RowLayout {
FormItemLayout {
label: qsTr("Adresse SIP")
contentItem: TextField {
backgroundColor: DefaultStyle.grey_0
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.appendAddress(text)
text = ""
}
}
}
Item {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
FormItemLayout {
label: qsTr("Nom")
contentItem: TextField {
initialText: contact.core.familyName
onEditingFinished: contact.core.familyName = text
backgroundColor: DefaultStyle.grey_0
}
}
Repeater {
// phone numbers
model: VariantList {
model: mainItem.contact && mainItem.contact.core.phoneNumbers || []
}
delegate: RowLayout {
FormItemLayout {
label: modelData.label
contentItem: TextField {
initialText: modelData.address
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.setPhoneNumberAt(index, qsTr("Téléphone"), text)
}
backgroundColor: DefaultStyle.grey_0
}
}
Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
background: Item{}
icon.source: AppIcons.closeX
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: mainItem.contact.core.removePhoneNumber(index)
}
FormItemLayout {
label: qsTr("Entreprise")
contentItem: TextField {
initialText: contact.core.organization
onEditingFinished: contact.core.organization = text
backgroundColor: DefaultStyle.grey_0
}
}
RowLayout {
FormItemLayout {
label: qsTr("Phone")
contentItem: TextField {
backgroundColor: DefaultStyle.grey_0
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.appendPhoneNumber(label, text)
setText("")
}
}
}
Item {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
FormItemLayout {
label: qsTr("Fonction")
contentItem: TextField {
initialText: contact.core.job
onEditingFinished: contact.core.job = text
backgroundColor: DefaultStyle.grey_0
}
}
Item{Layout.fillHeight: true}
}
Control.ScrollBar.vertical: Control.ScrollBar{
id: scrollbar
active: true
interactive: true
policy: Control.ScrollBar.AsNeeded
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.leftMargin: 15 * DefaultStyle.dp
}
Control.ScrollBar.horizontal: Control.ScrollBar{
visible: false
Control.ScrollView {
Layout.fillHeight: true
contentHeight: content.height
ColumnLayout {
id: content
anchors.rightMargin: 10 * DefaultStyle.dp
spacing: 20 * DefaultStyle.dp
Repeater {
model: VariantList {
model: mainItem.contact && mainItem.contact.core.addresses || []
}
delegate: FormItemLayout {
label: modelData.label
contentItem: RowLayout {
TextField {
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.setAddressAt(index, qsTr("Address SIP"), text)
}
initialText: modelData.address
backgroundColor: DefaultStyle.grey_0
Layout.preferredWidth: width
Layout.preferredHeight: height
}
Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignVCenter
background: Item{}
icon.source: AppIcons.closeX
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: mainItem.contact.core.removeAddress(index)
}
}
}
}
RowLayout {
FormItemLayout {
label: qsTr("Adresse SIP")
contentItem: TextField {
backgroundColor: DefaultStyle.grey_0
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.appendAddress(text)
text = ""
}
}
}
Item {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
}
Repeater {
// phone numbers
model: VariantList {
model: mainItem.contact && mainItem.contact.core.phoneNumbers || []
}
delegate: RowLayout {
FormItemLayout {
label: modelData.label
contentItem: TextField {
initialText: modelData.address
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.setPhoneNumberAt(index, qsTr("Téléphone"), text)
}
backgroundColor: DefaultStyle.grey_0
}
}
Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
background: Item{}
icon.source: AppIcons.closeX
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: mainItem.contact.core.removePhoneNumber(index)
}
}
}
RowLayout {
FormItemLayout {
label: qsTr("Phone")
contentItem: TextField {
backgroundColor: DefaultStyle.grey_0
onEditingFinished: {
if (text.length != 0) mainItem.contact.core.appendPhoneNumber(label, text)
setText("")
}
}
}
Item {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
}
Item{Layout.fillHeight: true}
}
Control.ScrollBar.vertical: Control.ScrollBar{
id: scrollbar
active: true
interactive: true
policy: Control.ScrollBar.AsNeeded
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.leftMargin: 15 * DefaultStyle.dp
}
Control.ScrollBar.horizontal: Control.ScrollBar{
visible: false
}
}
}
}
Button {
Layout.bottomMargin: 100 * DefaultStyle.dp
Layout.preferredWidth: 165 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
enabled: mainItem.contact && mainItem.contact.core.givenName.length > 0
text: mainItem.saveButtonText
leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp
topPadding: 11 * DefaultStyle.dp
bottomPadding: 11 * DefaultStyle.dp
onClicked: {
mainItem.contact.core.save()
mainItem.closeEdition()
Button {
Layout.bottomMargin: 100 * DefaultStyle.dp
Layout.preferredWidth: 165 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
enabled: mainItem.contact && mainItem.contact.core.givenName.length > 0
text: mainItem.saveButtonText
leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp
topPadding: 11 * DefaultStyle.dp
bottomPadding: 11 * DefaultStyle.dp
onClicked: {
mainItem.contact.core.save()
mainItem.closeEdition()
}
}
}
}
}

View file

@ -4,6 +4,7 @@ import QtQuick.Controls as Control
import Linphone
import UtilsCpp 1.0
import ConstantsCpp 1.0
ListView {
id: mainItem
@ -40,11 +41,7 @@ ListView {
property FriendGui selectedContact: model.getAt(currentIndex) || null
onCurrentIndexChanged: selectedContact = model.getAt(currentIndex) || null
onCountChanged: {
selectedContact = model.getAt(currentIndex) || null
}
// signal contactSelected(var contact)
signal contactStarredChanged()
signal contactDeletionRequested(FriendGui contact)
signal contactAddedToSelection()
@ -66,6 +63,9 @@ ListView {
model: MagicSearchProxy {
searchText: searchBarText.length === 0 ? "*" : searchBarText
onFriendCreated: (index) => {
mainItem.currentIndex = index
}
}
Control.ScrollBar.vertical: ScrollBar {
@ -85,9 +85,10 @@ ListView {
property var previousDisplayName: previousItem ? previousItem.core.displayName : ""
property var displayName: modelData.core.displayName
property bool display: !mainItem.showOnlyFavourites || modelData.core.starred
visible: display
Connections {
enabled: modelData.core
target: modelData.core
onStarredChanged: mainItem.contactStarredChanged()
}
@ -98,7 +99,7 @@ ListView {
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
width: 20 * DefaultStyle.dp
opacity: (!previousItem || !previousDisplayName.startsWith(displayName[0])) ? 1 : 0
opacity: (!previousItem || !previousDisplayName.toLocaleLowerCase(ConstantsCpp.DefaultLocale).startsWith(displayName[0].toLocaleLowerCase(ConstantsCpp.DefaultLocale))) ? 1 : 0
text: displayName[0]
color: DefaultStyle.main2_400
font {

View file

@ -9,12 +9,11 @@ Popup {
property string title
property string description
property int index
signal closePopup(int index)
onClosed: closePopup(index)
onAboutToShow: {
autoClosePopup.restart()
}
onAboutToHide: {
popupLayout.popupList.splice(mainItem.index, 1)
}
closePolicy: Popup.NoAutoClose
x : parent.x + parent.width - width
// y : parent.y + parent.height - height

View file

@ -0,0 +1,32 @@
import QtQuick 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls
import Linphone
Popup {
id: mainItem
property string text
modal: true
closePolicy: Control.Popup.NoAutoClose
anchors.centerIn: parent
padding: 20 * DefaultStyle.dp
underlineColor: DefaultStyle.main1_500_main
radius: 15 * DefaultStyle.dp
// onAboutToShow: width = contentText.implicitWidth
contentItem: ColumnLayout {
spacing: 15 * DefaultStyle.dp
BusyIndicator{
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 33 * DefaultStyle.dp
Layout.preferredHeight: 33 * DefaultStyle.dp
}
Text {
id: contentText
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.fillHeight: true
text: mainItem.text
font.pixelSize: 14 * DefaultStyle.dp
}
}
}

View file

@ -14,14 +14,14 @@ ListView {
property string searchBarText
property bool hoverEnabled: true
property var delegateButtons
property ConferenceInfoGui selectedConference: model.getAt(currentIndex) || null
property ConferenceInfoGui selectedConference: currentIndex != -1 ? model.getAt(currentIndex) : null
spacing: 8 * DefaultStyle.dp
currentIndex: confInfoProxy.currentDateIndex
onCountChanged: selectedConference = model.getAt(currentIndex) || null
onCountChanged: selectedConference = currentIndex != -1 ? model.getAt(currentIndex) : null
onCurrentIndexChanged: {
selectedConference = currentIndex != confInfoProxy.currentDateIndex ? model.getAt(currentIndex) : null
selectedConference = model.getAt(currentIndex)
}
onVisibleChanged: if( visible) {
mainItem.positionViewAtIndex(currentIndex, ListView.Center)// First approximative move
@ -34,16 +34,13 @@ ListView {
}
// using highlight doesn't center, take time before moving and don't work for not visible item (like not loaded)
highlightFollowsCurrentItem: false
function forceUpdate() {
confInfoProxy.lUpdate()
}
signal conferenceSelected(var contact)
model: ConferenceInfoProxy {
id: confInfoProxy
searchText: searchBarText.length === 0 ? "" : searchBarText
filterType: ConferenceInfoProxy.None
}
section {
@ -91,12 +88,12 @@ ListView {
Layout.fillWidth: false
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.minimumWidth: 32 * DefaultStyle.dp
height: 51 * DefaultStyle.dp
visible: !previousDateString || previousDateString != dateString
Layout.preferredHeight: 51 * DefaultStyle.dp
visible: previousDateString.length == 0 || previousDateString != dateString
spacing: 0
//anchors.leftMargin: 45 * DefaultStyle.dp
Text {
Layout.preferredHeight: 19 * DefaultStyle.dp
Layout.alignment: Qt.AlignCenter
text: day.substring(0,3) + '.'
color: DefaultStyle.main2_500main
wrapMode: Text.NoWrap
@ -109,8 +106,8 @@ ListView {
}
Rectangle {
id: dayNum
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
Layout.alignment: Qt.AlignCenter
radius: height/2
property var isCurrentDay: UtilsCpp.isCurrentDay(dateTime)
@ -118,9 +115,9 @@ ListView {
color: isCurrentDay ? DefaultStyle.main1_500_main : "transparent"
Text {
id: dayNumText
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: UtilsCpp.toDateDayString(dateTime)
color: dayNum.isCurrentDay ? DefaultStyle.grey_0 : DefaultStyle.main2_500main
wrapMode: Text.NoWrap
@ -208,18 +205,16 @@ ListView {
}
}
MouseArea {
id: confArea
hoverEnabled: mainItem.hoverEnabled
visible: !dateDay.visible && itemDelegate.haveModel
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
mainItem.currentIndex = index
mainItem.conferenceSelected($modelData)
}
}
// MouseArea {
// id: confArea
// hoverEnabled: mainItem.hoverEnabled
// visible: !dateDay.visible && itemDelegate.haveModel
// anchors.fill: parent
// cursorShape: Qt.PointingHandCursor
// onClicked: {
// mainItem.currentIndex = index
// mainItem.conferenceSelected($modelData)
// }
// }
}
}

View file

@ -5,7 +5,6 @@ import QtQuick.Controls as Control
import Linphone
import UtilsCpp 1.0
//TODO : spacing layout
ColumnLayout {
id: mainItem
spacing: 8 * DefaultStyle.dp
@ -18,7 +17,6 @@ ColumnLayout {
Connections {
target: mainItem.conferenceInfoGui.core
onSchedulerStateChanged: {
console.log("scheduler state changed", mainItem.conferenceInfoGui.core.schedulerState)
if (mainItem.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Ready) {
mainItem.saveSucceed(isCreation)
}

View file

@ -7,8 +7,8 @@ import Linphone
Control.TabBar {
id: mainItem
spacing: 15 * DefaultStyle.dp
topPadding: 20 * DefaultStyle.dp
spacing: 32 * DefaultStyle.dp
topPadding: 36 * DefaultStyle.dp
property var model
readonly property alias cornerRadius: bottomLeftCorner.radius

View file

@ -68,17 +68,17 @@ Item {
Component{
id: activeSpeakerComponent
ActiveSpeakerLayout{
Layout.Layout.fillWidth: true
Layout.Layout.fillHeight: true
call: mainItem.call
Layout.Layout.fillWidth: true
Layout.Layout.fillHeight: true
call: mainItem.call
}
}
Component{
id: gridComponent
GridLayout{
Layout.Layout.fillWidth: true
Layout.Layout.fillHeight: true
call: mainItem.call
Layout.Layout.fillWidth: true
Layout.Layout.fillHeight: true
call: mainItem.call
}
}
}

View file

@ -55,7 +55,6 @@ ColumnLayout {
// Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Avatar {
// TODO : find friend and pass contact argument
id: detailAvatar
anchors.horizontalCenter: parent.horizontalCenter
width: 100 * DefaultStyle.dp

View file

@ -0,0 +1,30 @@
import QtQuick 2.15
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
import UtilsCpp 1.0
Item {
id: mainItem
property color panelColor: DefaultStyle.grey_100
property alias headerContent: rightPanelHeader.children
property alias content: rightPanelContent.children
Rectangle {
id: rightPanelHeader
color: DefaultStyle.grey_0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 57 * DefaultStyle.dp
}
Rectangle {
id: rightPanelContent
color: mainItem.panelColor
anchors.top: rightPanelHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}

View file

@ -18,24 +18,8 @@ Item {
property color rightPanelColor: DefaultStyle.grey_100
property alias leftPanelContent: leftPanel.children
property alias rightPanelStackView: rightPanelStackView
property alias contactEditionComp: contactEditionComp
property alias rightPanel: rightPanel
signal noItemButtonPressed()
signal contactEditionClosed()
function createContact(name, address) {
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{
}', mainItem)
friendGui.core.givenName = UtilsCpp.getGivenNameFromFullName(name)
friendGui.core.familyName = UtilsCpp.getFamilyNameFromFullName(name)
friendGui.core.defaultAddress = address
rightPanelStackView.push(contactEditionComp, {"contact": friendGui, "title": qsTr("Nouveau contact"), "saveButtonText": qsTr("Créer")})
}
function editContact(friendGui) {
rightPanelStackView.push(contactEditionComp, {"contact": friendGui, "title": qsTr("Modifier contact"), "saveButtonText": qsTr("Enregistrer")})
}
// Control.SplitView {
// id: splitView
@ -223,24 +207,8 @@ Item {
}
}
Item {
Control.StackView {
id: rightPanelStackView
anchors.fill: parent
anchors.topMargin: 39 * DefaultStyle.dp
anchors.leftMargin: 39 * DefaultStyle.dp
}
}
// We need this component here as it is used in multiple subPages (Call and Contact pages)
Component {
id: contactEditionComp
ContactEdition {
property string objectName: "contactEdition"
onCloseEdition: {
rightPanelStackView.pop(Control.StackView.Immediate)
mainItem.contactEditionClosed()
}
}
Control.StackView {
id: rightPanelStackView
}
}
}

View file

@ -22,6 +22,7 @@ AbstractMainPage {
property bool isRegistered: account ? account.core.registrationState == LinphoneEnums.RegistrationState.Ok : false
property int selectedParticipantsCount
signal startGroupCallRequested()
signal createContactRequested(string name, string address)
Connections {
enabled: confInfoGui
@ -529,7 +530,7 @@ AbstractMainPage {
}
onClicked: {
detailOptions.close()
mainItem.createContact(contactDetail.contactName, contactDetail.contactAddress)
mainItem.createContactRequested(contactDetail.contactName, contactDetail.contactAddress)
}
}
Button {

View file

@ -25,25 +25,28 @@ AbstractMainPage {
onNoItemButtonPressed: createContact("", "")
function createContact(name, address) {
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{
}', mainItem)
friendGui.core.givenName = UtilsCpp.getGivenNameFromFullName(name)
friendGui.core.familyName = UtilsCpp.getFamilyNameFromFullName(name)
friendGui.core.defaultAddress = address
rightPanelStackView.push(contactEdition, {"contact": friendGui, "title": qsTr("Nouveau contact"), "saveButtonText": qsTr("Créer")})
}
function editContact(friendGui) {
rightPanelStackView.push(contactEdition, {"contact": friendGui, "title": qsTr("Modifier contact"), "saveButtonText": qsTr("Enregistrer")})
}
// rightPanelStackView.initialItem: contactDetail
Binding {
mainItem.showDefaultItem: false
when: rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName == "contactEdition"
when: rightPanelStackView.currentItem
restoreMode: Binding.RestoreBinding
}
Connections {
target: mainItem
onContactEditionClosed: {
mainItem.forceListsUpdate()
// mainItem.rightPanelStackView.replace(contactDetail, Control.StackView.Immediate)
}
}
showDefaultItem: contactList.model.sourceModel.count === 0
function goToNewCall() {
listStackView.replace(newCallItem)
}
property MagicSearchProxy allFriends: MagicSearchProxy {
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
@ -107,6 +110,7 @@ AbstractMainPage {
id: searchBar
Layout.leftMargin: leftPanel.leftMargin
Layout.rightMargin: leftPanel.rightMargin
Layout.topMargin: 18 * DefaultStyle.dp
Layout.fillWidth: true
placeholderText: qsTr("Rechercher un contact")
}
@ -223,6 +227,12 @@ AbstractMainPage {
contactMenuVisible: true
searchBarText: searchBar.text
model: allFriends
Connections {
target: allFriends
onFriendCreated: (index) => {
contactList.currentIndex = index
}
}
onSelectedContactChanged: {
if (selectedContact) {
favoriteList.currentIndex = -1
@ -592,4 +602,15 @@ AbstractMainPage {
}
}
}
Component {
id: contactEdition
ContactEdition {
Control.StackView.onActivated: console.log("edit/create contact")
onCloseEdition: {
if (rightPanelStackView.depth <= 1) rightPanelStackView.clear()
else rightPanelStackView.pop(Control.StackView.Immediate)
}
}
}
}

View file

@ -16,14 +16,13 @@ AbstractMainPage {
property bool leftPanelEnabled: true
property ConferenceInfoGui selectedConference
property int meetingListCount
signal newConfCreated()
signal returnRequested()
signal addParticipantsValidated(list<string> selectedParticipants)
Component.onCompleted: rightPanelStackView.push(overridenRightPanel, Control.StackView.Immediate)
onSelectedConferenceChanged: {
overridenRightPanelStackView.clear()
if (selectedConference) {
if (selectedConference && selectedConference.core.haveModel) {
if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate)
}
}
@ -136,7 +135,6 @@ AbstractMainPage {
mainItem.selectedConference = conferenceList.selectedConference
}
RowLayout {
visible: leftPanelStackView.currentItem == listLayout
enabled: mainItem.leftPanelEnabled
Layout.rightMargin: 39 * DefaultStyle.dp
spacing: 0
@ -200,10 +198,6 @@ AbstractMainPage {
}
Connections {
target: mainItem
onNewConfCreated: {
// TODO : manque un connect côté c++
conferenceList.forceUpdate()
}
}
Control.ScrollBar.vertical: ScrollBar {
id: meetingsScrollbar
@ -222,8 +216,10 @@ AbstractMainPage {
Component {
id: createConf
ColumnLayout {
id: createConfLayout
property ConferenceInfoGui conferenceInfoGui
property bool isCreation
spacing: 33 * DefaultStyle.dp
RowLayout {
width: 320 * DefaultStyle.dp
@ -235,6 +231,8 @@ AbstractMainPage {
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
topPadding: 6 * DefaultStyle.dp
bottomPadding: 6 * DefaultStyle.dp
onClicked: {
meetingSetup.conferenceInfoGui.core.undo()
leftPanelStackView.pop()
@ -250,10 +248,20 @@ AbstractMainPage {
Layout.fillWidth: true
}
Button {
Layout.preferredWidth: 57 * DefaultStyle.dp
topPadding: 6 * DefaultStyle.dp
bottomPadding: 6 * DefaultStyle.dp
text: qsTr("Créer")
textSize: 13 * DefaultStyle.dp
contentItem: Text {
text: qsTr("Créer")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font {
pixelSize: 13 * DefaultStyle.dp
weight: 600 * DefaultStyle.dp
}
color: DefaultStyle.grey_0
}
onClicked: {
if (meetingSetup.conferenceInfoGui.core.subject.length === 0) {
UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("La conférence doit contenir un sujet"), false)
@ -272,8 +280,23 @@ AbstractMainPage {
conferenceInfoGui: parent.conferenceInfoGui
isCreation: parent.isCreation
Layout.rightMargin: 35 * DefaultStyle.dp
Connections {
target: meetingSetup.conferenceInfoGui ? meetingSetup.conferenceInfoGui.core : null
onConferenceSchedulerStateChanged: {
var mainWin = UtilsCpp.getMainWindow()
if (meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.AllocationPending
|| meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Updating) {
mainWin.showLoadingPopup(qsTr("Création de la conférence en cours..."))
} else {
if (meetingSetup.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Error) {
UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("La création de la conférence a échoué"), false)
}
mainWin.closeLoadingPopup()
}
createConfLayout.enabled = meetingSetup.conferenceInfoGui.core.schedulerState != LinphoneEnums.ConferenceSchedulerState.AllocationPending
}
}
onSaveSucceed: {
mainItem.newConfCreated()
leftPanelStackView.pop()
UtilsCpp.showInformationPopup(qsTr("Nouvelle réunion"), qsTr("Réunion planifiée avec succès"), true)
}
@ -364,6 +387,15 @@ AbstractMainPage {
overridenRightPanelStackView.pop()
}
}
Connections {
target: conferenceEdit.conferenceInfoGui ? conferenceEdit.conferenceInfoGui.core : null
onConferenceSchedulerStateChanged: {
var mainWin = UtilsCpp.getMainWindow()
if (conferenceEdit.conferenceInfoGui.core.schedulerState == LinphoneEnums.ConferenceSchedulerState.Error) {
UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("L'édition de la conférence a échoué"), false)
}
}
}
}
}
}
@ -382,12 +414,12 @@ AbstractMainPage {
Layout.preferredHeight: 24 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
onClicked: mainItem.returnRequested()
onClicked: container.pop()
}
ColumnLayout {
spacing: 3 * DefaultStyle.dp
Text {
text: qsTr("Appel de groupe")
text: qsTr("Ajouter des participants")
color: DefaultStyle.main1_500_main
maximumLineCount: 1
font {
@ -434,6 +466,7 @@ AbstractMainPage {
visible: mainItem.selectedConference
spacing: 25 * DefaultStyle.dp
Section {
Layout.topMargin: 58 * DefaultStyle.dp
visible: mainItem.selectedConference
content: RowLayout {
spacing: 8 * DefaultStyle.dp

View file

@ -87,4 +87,6 @@ QtObject {
property string squaresFour: "image://internal/squares-four.svg"
property string handWaving: "image://internal/hand-waving.svg"
property string screencast: "image://internal/screencast.svg"
property string videoconference: "image://internal/video-conference.svg"
property string videoconferenceSelected: "image://internal/video-conference-selected.svg"
}