contacts list

This commit is contained in:
Gaelle Braud 2024-01-03 09:54:17 +01:00
parent 97c01e66f8
commit 6849b8d378
29 changed files with 933 additions and 376 deletions

View file

@ -42,6 +42,7 @@
#include "core/camera/CameraGui.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/friend/FriendGui.hpp"
#include "core/friend/FriendInitialProxy.hpp"
#include "core/logger/QtLogger.hpp"
#include "core/login/LoginPage.hpp"
#include "core/notifier/Notifier.hpp"
@ -163,6 +164,7 @@ void App::initCppInterfaces() {
qmlRegisterType<FriendGui>(Constants::MainQmlUri, 1, 0, "FriendGui");
qmlRegisterUncreatableType<FriendCore>(Constants::MainQmlUri, 1, 0, "FriendCore", QLatin1String("Uncreatable"));
qmlRegisterType<MagicSearchProxy>(Constants::MainQmlUri, 1, 0, "MagicSearchProxy");
qmlRegisterType<FriendInitialProxy>(Constants::MainQmlUri, 1, 0, "FriendInitialProxy");
qmlRegisterType<CameraGui>(Constants::MainQmlUri, 1, 0, "CameraGui");
LinphoneEnums::registerMetaTypes();
}

View file

@ -16,6 +16,7 @@ list(APPEND _LINPHONEAPP_SOURCES
core/camera/CameraDummy.cpp
core/friend/FriendCore.cpp
core/friend/FriendGui.cpp
core/friend/FriendInitialProxy.cpp
core/logger/QtLogger.cpp
core/login/LoginPage.cpp
core/notifier/Notifier.cpp

View file

@ -55,7 +55,7 @@ CallHistoryCore::CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callL
}
CallHistoryCore::~CallHistoryCore() {
qDebug() << "[CallHistoryCore] delete" << this;
// qDebug() << "[CallHistoryCore] delete" << this;
mustBeInMainThread("~" + getClassName());
}

View file

@ -20,6 +20,8 @@
#include "FriendCore.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
@ -42,7 +44,11 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObje
mPresenceTimestamp = mFriendModel->getPresenceTimestamp();
mPictureUri = Utils::coreStringToAppString(contact->getPhoto());
auto address = contact->getAddress();
mAddress = address ? Utils::coreStringToAppString(contact->getAddress()->asString()) : "NoAddress";
mAddress = address ? Utils::coreStringToAppString(contact->getAddress()->asStringUriOnly()) : "NoAddress";
auto name = contact->getName();
mName =
name.empty() ? Utils::getDisplayName(mAddress)->getValue().toString() : Utils::coreStringToAppString(name);
mStarred = contact->getStarred();
mIsSaved = true;
} else mIsSaved = false;
}
@ -76,11 +82,20 @@ void FriendCore::setSelf(QSharedPointer<FriendCore> me) {
mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](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::objectNameChanged,
[this](const QString &objectName) { qDebug() << "object name changed" << objectName; });
// From GUI
mFriendModelConnection->makeConnectToCore(&FriendCore::lSetPictureUri, [this](QString uri) {
mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); });
});
mFriendModelConnection->makeConnectToCore(&FriendCore::lSetStarred, [this](bool starred) {
mFriendModelConnection->invokeToModel([this, starred]() { mFriendModel->setStarred(starred); });
});
} else { // Create
mCoreModelConnection = QSharedPointer<SafeConnection<FriendCore, CoreModel>>(
@ -107,6 +122,16 @@ void FriendCore::setName(QString data) {
}
}
bool FriendCore::getStarred() const {
return mStarred;
}
void FriendCore::onStarredChanged(bool starred) {
mStarred = starred;
save();
emit starredChanged();
}
QString FriendCore::getAddress() const {
return mAddress;
}
@ -192,17 +217,26 @@ void FriendCore::remove() {
void FriendCore::save() { // Save Values to model
FriendCore *thisCopy = new FriendCore(*this); // Pointer to avoid multiple copies in lambdas
if (mFriendModel) { // Update
auto linphoneAddr = ToolModel::interpretUrl(mAddress);
if (mFriendModel) {
mFriendModelConnection->invokeToModel([this, thisCopy]() { // Copy values to avoid concurrency
auto core = CoreModel::getInstance()->getCore();
auto contact = mFriendModel->getFriend();
thisCopy->writeInto(contact);
thisCopy->deleteLater();
mFriendModelConnection->invokeToCore([this]() { saved(); });
});
} else { // Creation
mCoreModelConnection->invokeToModel([this, thisCopy]() {
} else {
mCoreModelConnection->invokeToModel([this, thisCopy, linphoneAddr]() {
auto core = CoreModel::getInstance()->getCore();
auto contact = core->findFriend(linphoneAddr);
auto friendExists = contact != nullptr;
if (contact != nullptr) {
thisCopy->writeInto(contact);
thisCopy->deleteLater();
if (mFriendModelConnection) mFriendModelConnection->invokeToCore([this] { saved(); });
else mCoreModelConnection->invokeToCore([this] { saved(); });
} else {
auto contact = core->createFriend();
thisCopy->writeInto(contact);
thisCopy->deleteLater();
@ -217,8 +251,30 @@ void FriendCore::save() { // Save Value
if (created) setSelf(mCoreModelConnection->mCore);
setIsSaved(created);
});
}
});
}
// if (mFriendModel) { // Update
// } else { // Creation
// mCoreModelConnection->invokeToModel([this, thisCopy]() {
// auto core = CoreModel::getInstance()->getCore();
// auto contact = core->createFriend();
// thisCopy->writeInto(contact);
// thisCopy->deleteLater();
// bool created = (core->getDefaultFriendList()->addFriend(contact) == linphone::FriendList::Status::OK);
// if (created) {
// mFriendModel = Utils::makeQObject_ptr<FriendModel>(contact);
// mFriendModel->setSelf(mFriendModel);
// 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

View file

@ -46,6 +46,7 @@ class FriendCore : public QObject, public AbstractObject {
consolidatedPresenceChanged)
Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE lSetPictureUri NOTIFY pictureUriChanged)
Q_PROPERTY(bool starred READ getStarred WRITE lSetStarred NOTIFY starredChanged)
public:
// Should be call from model Thread. Will be automatically in App thread after initialization
@ -60,6 +61,9 @@ public:
QString getName() const;
void setName(QString data);
bool getStarred() const;
void onStarredChanged(bool starred);
QString getAddress() const;
void setAddress(QString address);
@ -84,6 +88,7 @@ public:
signals:
void contactUpdated();
void nameChanged(QString name);
void starredChanged();
void addressChanged(QString address);
void consolidatedPresenceChanged(LinphoneEnums::ConsolidatedPresence level);
void presenceTimestampChanged(QDateTime presenceTimestamp);
@ -95,6 +100,7 @@ signals:
void removed(FriendCore *contact);
void lSetPictureUri(QString pictureUri);
void lSetStarred(bool starred);
protected:
void writeInto(std::shared_ptr<linphone::Friend> contact) const;
@ -103,6 +109,7 @@ protected:
LinphoneEnums::ConsolidatedPresence mConsolidatedPresence = LinphoneEnums::ConsolidatedPresence::Offline;
QDateTime mPresenceTimestamp;
QString mName;
bool mStarred;
QString mAddress;
QString mPictureUri;
bool mIsSaved;

View file

@ -36,6 +36,9 @@ FriendGui::~FriendGui() {
mustBeInMainThread("~" + getClassName());
}
void FriendGui::createContact(const QString &address) {
}
FriendCore *FriendGui::getCore() const {
return mCore.get();
}

View file

@ -35,6 +35,7 @@ public:
FriendGui(QSharedPointer<FriendCore> core);
~FriendGui();
FriendCore *getCore() const;
Q_INVOKABLE void createContact(const QString &address);
QSharedPointer<FriendCore> mCore;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -0,0 +1,76 @@
/*
* 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 "FriendInitialProxy.hpp"
#include "FriendCore.hpp"
#include "FriendGui.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(FriendInitialProxy)
FriendInitialProxy::FriendInitialProxy(QObject *parent) : SortFilterProxy(parent) {
}
FriendInitialProxy::~FriendInitialProxy() {
setSourceModel(nullptr);
}
QString FriendInitialProxy::getFilterText() const {
return mFilterText;
}
void FriendInitialProxy::setFilterText(const QString &filter) {
if (mFilterText != filter) {
mFilterText = filter;
invalidate();
emit filterTextChanged();
}
}
// void FriendInitialProxy::setSourceModel(QAbstractItemModel *sourceModel) {
// mSource = qSharedPointerCast<MagicSearchProxy>(QSharedPointer<QAbstractItemModel>(sourceModel));
// }
// QAbstractItemModel *FriendInitialProxy::sourceModel() const {
// return mSource.get();
// }
bool FriendInitialProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
bool show = (mFilterText.isEmpty() || mFilterText == "*");
if (!show) {
QRegularExpression search(mFilterText, QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto friendData = sourceModel()->data(sourceModel()->index(sourceRow, 0, sourceParent)).value<FriendGui *>();
auto name = friendData->getCore()->getName();
show = friendData->getCore()->getName().indexOf(search) == 0;
}
return show;
}
// bool FriendInitialProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
// // auto l = getItemAt<MagicSearchProxy, FriendCore>(left.row());
// // auto r = getItemAt<MagicSearchProxy, FriendCore>(right.row());
// // return l->getName() < r->getName();
// }
QVariant FriendInitialProxy::data(const QModelIndex &index, int role) const {
return sourceModel()->data(mapToSource(index));
}

View file

@ -0,0 +1,63 @@
/*
* 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/>.
*/
#ifndef FRIEND_INITIAL_PROXY_H_
#define FRIEND_INITIAL_PROXY_H_
#include "../proxy/SortFilterProxy.hpp"
#include "core/search/MagicSearchList.hpp"
#include "core/search/MagicSearchProxy.hpp"
#include "tool/AbstractObject.hpp"
/**
* A proxy to filter the friends list with the first letter of the names
**/
// =============================================================================
class FriendInitialProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
// Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
public:
FriendInitialProxy(QObject *parent = Q_NULLPTR);
~FriendInitialProxy();
QString getFilterText() const;
void setFilterText(const QString &filter);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void filterTextChanged();
void sourceModelChanged();
protected:
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
// virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
QString mFilterText;
QSharedPointer<MagicSearchProxy> mSource;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -133,6 +133,8 @@ void MagicSearchList::setAggregationFlag(LinphoneEnums::MagicSearchAggregation f
QVariant MagicSearchList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) return QVariant::fromValue(new FriendGui(mList[row].objectCast<FriendCore>()));
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new FriendGui(mList[row].objectCast<FriendCore>()));
}
return QVariant();
}

View file

@ -15,6 +15,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/caret-down.svg"
"data/image/caret-left.svg"
"data/image/caret-right.svg"
"data/image/caret-up.svg"
"data/image/verif_page_image.svg"
"data/image/check.svg"
"data/image/dialer.svg"
@ -61,6 +62,8 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/trash-simple.svg"
"data/image/copy.svg"
"data/image/empty.svg"
"data/image/heart.svg"
"data/image/heart-fill.svg"
data/shaders/roundEffect.vert.qsb
data/shaders/roundEffect.frag.qsb

View file

@ -33,8 +33,8 @@ CallHistoryModel::CallHistoryModel(const std::shared_ptr<linphone::CallLog> &cal
}
CallHistoryModel::~CallHistoryModel() {
qDebug() << "[CallHistoryModel] delete" << this;
mustBeInLinphoneThread("~" + getClassName());
// qDebug() << "[CallHistoryModel] delete" << this;
// mustBeInLinphoneThread("~" + getClassName());
}
void CallHistoryModel::removeCallHistory() {

View file

@ -49,6 +49,23 @@ QDateTime FriendModel::getPresenceTimestamp() const {
} else return QDateTime();
}
QString FriendModel::getAddress() const {
return Utils::coreStringToAppString(mMonitor->getAddress()->asStringUriOnly());
}
QString FriendModel::getName() const {
return Utils::coreStringToAppString(mMonitor->getName());
}
bool FriendModel::getStarred() const {
return mMonitor->getStarred();
}
void FriendModel::setStarred(bool starred) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->setStarred(starred);
emit starredChanged(starred);
}
void FriendModel::onPresenceReceived(const std::shared_ptr<linphone::Friend> &contact) {
emit presenceReceived(LinphoneEnums::fromLinphone(contact->getConsolidatedPresence()), getPresenceTimestamp());
}

View file

@ -39,12 +39,17 @@ public:
~FriendModel();
QDateTime getPresenceTimestamp() const;
QString getAddress() const;
QString getName() const;
bool getStarred() const;
std::shared_ptr<linphone::Friend> getFriend() const;
void setPictureUri(QString uri);
void setStarred(bool starred);
signals:
void pictureUriChanged(QString uri);
void starredChanged(bool starred);
private:
DECLARE_ABSTRACT_OBJECT

View file

@ -59,6 +59,7 @@ QString ToolModel::getDisplayName(const std::shared_ptr<const linphone::Address>
// auto sipAddressEntry = getSipAddressEntry(qtAddress, cleanAddress);
// displayName = sipAddressEntry->displayNames.get();
}
displayName.replace('.', ' ');
return displayName;
}

View file

@ -170,6 +170,13 @@ LinphoneEnums::MagicSearchAggregation LinphoneEnums::fromLinphone(const linphone
return static_cast<LinphoneEnums::MagicSearchAggregation>(data);
}
linphone::MagicSearch::Source LinphoneEnums::toLinphone(const LinphoneEnums::MagicSearchSource &data) {
return static_cast<linphone::MagicSearch::Source>(data);
}
LinphoneEnums::MagicSearchSource LinphoneEnums::fromLinphone(const linphone::MagicSearch::Source &data) {
return static_cast<LinphoneEnums::MagicSearchSource>(data);
}
linphone::LogLevel LinphoneEnums::toLinphone(const QtMsgType &data) {
switch (data) {
case QtDebugMsg:

View file

@ -218,6 +218,24 @@ Q_ENUM_NS(MagicSearchAggregation);
linphone::MagicSearch::Aggregation toLinphone(const LinphoneEnums::MagicSearchAggregation &data);
LinphoneEnums::MagicSearchAggregation fromLinphone(const linphone::MagicSearch::Aggregation &data);
enum class MagicSearchSource {
None = int(linphone::MagicSearch::Source::None),
Friends = int(linphone::MagicSearch::Source::Friends),
CallLogs = int(linphone::MagicSearch::Source::CallLogs),
LdapServers = int(linphone::MagicSearch::Source::LdapServers),
ChatRooms = int(linphone::MagicSearch::Source::ChatRooms),
Request = int(linphone::MagicSearch::Source::Request),
FavoriteFriends = int(linphone::MagicSearch::Source::FavoriteFriends),
ConferencesInfo = int(linphone::MagicSearch::Source::ConferencesInfo),
All = int(linphone::MagicSearch::Source::All)
};
Q_ENUM_NS(MagicSearchSource);
// Q_DECLARE_FLAGS(MagicSearchSources, MagicSearchSource)
// Q_DECLARE_OPERATORS_FOR_FLAGS(MagicSearchSources)
linphone::MagicSearch::Source toLinphone(const LinphoneEnums::MagicSearchSource &data);
LinphoneEnums::MagicSearchSource fromLinphone(const linphone::MagicSearch::Source &data);
linphone::LogLevel toLinphone(const QtMsgType &data);
QtMsgType fromLinphone(const linphone::LogLevel &data);

View file

@ -454,7 +454,7 @@ Window {
}
Component {
id: contactsListPanel
ContactsList {
CallContactsLists {
sideMargin: 10 * DefaultStyle.dp
topMargin: 15 * DefaultStyle.dp
groupCallVisible: false

View file

@ -7,7 +7,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/Account/Accounts.qml
view/Item/Call/ContactsList.qml
view/Item/Call/CallContactsLists.qml
view/Item/Call/OngoingCallRightPanel.qml
view/Item/Notification/Notification.qml
@ -25,6 +25,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/Carousel.qml
view/Item/CheckBox.qml
view/Item/ComboBox.qml
view/Item/ContactsList.qml
view/Item/DesktopPopup.qml
view/Item/DigitInput.qml
view/Item/EffectImage.qml

View file

@ -0,0 +1,307 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2 as Control
import QtQuick.Effects
import Linphone
import UtilsCpp 1.0
Item {
id: mainItem
property int sideMargin: 25 * DefaultStyle.dp
property int topMargin: 5 * DefaultStyle.dp
property bool groupCallVisible
property color searchBarColor: DefaultStyle.grey_100
property color searchBarBorderColor: "transparent"
signal callButtonPressed(string address)
clip: true
Popup {
id: startCallPopup
property FriendGui contact
onContactChanged: {
}
underlineColor: DefaultStyle.main1_500_main
anchors.centerIn: parent
width: parent.width
modal: true
leftPadding: 15 * DefaultStyle.dp
rightPadding: 15 * DefaultStyle.dp
topPadding: 20 * DefaultStyle.dp
bottomPadding: 25 * DefaultStyle.dp
contentItem: ColumnLayout {
RowLayout {
Text {
text: qsTr("Select channel")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
}
Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
background: Item{}
contentItem: Image {
anchors.fill: parent
source: AppIcons.closeX
}
onClicked: startCallPopup.close()
}
}
Repeater {
id: adresses
model: [{label: "SIP", address: startCallPopup.contact ? startCallPopup.contact.core.address : ""},
{label: "Work", address: "06000000000"},
{label: "Personal", address: "060000000"}
] //account.adresses
Button {
id: channel
// required property int index
leftPadding: 0
rightPadding: 0
// topPadding: 0
bottomPadding: 0
Layout.fillWidth: true
background: Item{}
contentItem: ColumnLayout {
RowLayout {
ColumnLayout {
Text {
Layout.leftMargin: 5 * DefaultStyle.dp
Layout.rightMargin: 5 * DefaultStyle.dp
text: modelData.label
font {
pixelSize: 14 * DefaultStyle.dp
weight: 700 * DefaultStyle.dp
}
}
Text {
Layout.leftMargin: 5 * DefaultStyle.dp
Layout.rightMargin: 5 * DefaultStyle.dp
text: modelData.address
font {
pixelSize: 13 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
Item {
Layout.fillWidth: true
}
}
Rectangle {
visible: index < adresses.count - 1
Layout.fillWidth: true
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
}
onClicked: mainItem.callButtonPressed(modelData.address)
}
}
}
}
Control.ScrollBar {
id: contactsScrollbar
active: true
interactive: true
policy: Control.ScrollBar.AlwaysOn
// Layout.fillWidth: true
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
// x: mainItem.x + mainItem.width - width
// anchors.left: control.right
}
Control.Control {
id: listLayout
anchors.fill: parent
anchors.topMargin: mainItem.topMargin
background: Item {
anchors.fill: parent
}
contentItem: ColumnLayout {
anchors.fill: parent
spacing: 10 * DefaultStyle.dp
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.maximumWidth: mainItem.width
Layout.leftMargin: mainItem.sideMargin
Layout.rightMargin: mainItem.sideMargin
color: mainItem.searchBarColor
borderColor: mainItem.searchBarBorderColor
placeholderText: qsTr("Rechercher un contact")
numericPad: numPad
}
Control.ScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: mainItem.sideMargin
Layout.topMargin: 25 * DefaultStyle.dp
rightPadding: mainItem.sideMargin
contentWidth: width - mainItem.sideMargin
contentHeight: content.height
clip: true
Control.ScrollBar.vertical: contactsScrollbar
ColumnLayout {
id: content
width: parent.width
spacing: 25 * DefaultStyle.dp
Button {
visible: mainItem.groupCallVisible
Layout.fillWidth: true
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
background: Rectangle {
color: DefaultStyle.groupCallButtonColor
anchors.fill: parent
radius: 50 * DefaultStyle.dp
}
contentItem: RowLayout {
Image {
source: AppIcons.groupCall
Layout.preferredWidth: 35 * DefaultStyle.dp
sourceSize.width: 35 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: "Appel de groupe"
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
}
Image {
source: AppIcons.rightArrow
}
}
}
RowLayout {
visible: searchBar.text.length > 0
Layout.maximumWidth: parent.width
Layout.fillWidth: true
Text {
text: searchBar.text
maximumLineCount: 1
elide: Text.ElideRight
}
Item {
Layout.fillWidth: true
}
Control.Button {
implicitWidth: 30 * DefaultStyle.dp
implicitHeight: 30 * DefaultStyle.dp
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.phone
width: 20 * DefaultStyle.dp
sourceSize.width: 20 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: {
mainItem.callButtonPressed(searchBar.text)
}
}
}
ColumnLayout {
Text {
text: qsTr("All contacts")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Repeater {
model: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\\d"]
RowLayout {
visible: contactList.count > 0
spacing: 8 * DefaultStyle.dp
Layout.fillWidth: true
Text {
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.alignment: Qt.AlignTop
Layout.topMargin: 15 * DefaultStyle.dp // Align center with the first row
text: modelData == "\\d" ? " " : modelData
color: DefaultStyle.main2_400
font {
pixelSize: 20 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
}
}
ContactsList{
Layout.fillWidth: true
id: contactList
initialProxyModel: modelData
searchBarText: searchBar.text
// contactMenuVisible: false
}
}
}
}
ColumnLayout {
Text {
text: qsTr("Suggestions")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
ContactsList{
contactMenuVisible: false
Layout.fillHeight: true
model: FriendInitialProxy {
filterText: ""
property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags
sourceModel: MagicSearchProxy {
id: search
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends
}
}
}
}
Item {
Layout.fillHeight: true
}
}
}
}
}
Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: numPad.implicitHeight
NumericPad {
id: numPad
width: parent.width
onLaunchCall: {
var callVarObject = UtilsCpp.createCall(searchBar.text + "@sip.linphone.org")
// TODO : auto completion instead of sip linphone
}
}
}
}

View file

@ -1,165 +0,0 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2 as Control
import QtQuick.Effects
import Linphone
Item {
id: mainItem
property int sideMargin: 25 * DefaultStyle.dp
property int topMargin: 5 * DefaultStyle.dp
property bool groupCallVisible
property color searchBarColor: DefaultStyle.grey_100
property color searchBarBorderColor: "transparent"
signal callButtonPressed(string address)
clip: true
Control.Control {
id: listLayout
anchors.fill: parent
anchors.leftMargin: mainItem.sideMargin
anchors.rightMargin: mainItem.sideMargin
anchors.topMargin: mainItem.topMargin
background: Item {
anchors.fill: parent
}
contentItem: ColumnLayout {
anchors.fill: parent
spacing: 10 * DefaultStyle.dp
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.maximumWidth: mainItem.width
color: mainItem.searchBarColor
borderColor: mainItem.searchBarBorderColor
placeholderText: qsTr("Rechercher un contact")
numericPad: numPad
}
Button {
visible: mainItem.groupCallVisible
Layout.fillWidth: true
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
background: Rectangle {
color: DefaultStyle.groupCallButtonColor
anchors.fill: parent
radius: 50 * DefaultStyle.dp
}
contentItem: RowLayout {
Image {
source: AppIcons.groupCall
Layout.preferredWidth: 35 * DefaultStyle.dp
sourceSize.width: 35 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: "Appel de groupe"
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
}
Item {
Layout.fillWidth: true
}
Image {
source: AppIcons.rightArrow
}
}
}
RowLayout {
visible: searchBar.text.length > 0 // && contactList.count === 0 (pas trouvé dans la liste)
Layout.maximumWidth: parent.width
Layout.fillWidth: true
Text {
text: searchBar.text
maximumLineCount: 1
elide: Text.ElideRight
}
Item {
Layout.fillWidth: true
}
Control.Button {
implicitWidth: 30 * DefaultStyle.dp
implicitHeight: 30 * DefaultStyle.dp
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.phone
width: 20 * DefaultStyle.dp
sourceSize.width: 20 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: {
mainItem.callButtonPressed(searchBar.text)
}
}
}
ListView {
id: contactList
Layout.fillWidth: true
Layout.fillHeight: true
// call history
model: 30
delegate: Item {
required property int index
width:contactList.width
height: 30 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
Image {
source: AppIcons.info
}
ColumnLayout {
Text {
text: "John Doe"
}
// RowLayout {
// Image {
// source: AppIcons.incomingCall
// }
// Text {
// text: "info sur l'appel"
// }
// }
}
Item {
Layout.fillWidth: true
}
}
MouseArea {
hoverEnabled: true
Rectangle {
anchors.fill: parent
opacity: 0.1
radius: 15 * DefaultStyle.dp
color: DefaultStyle.main2_500main
visible: parent.containsMouse
}
onClicked: contactList.currentIndex = parent.index
}
}
}
}
}
Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: numPad.implicitHeight
NumericPad {
id: numPad
width: parent.width
onLaunchCall: {
var callVarObject = UtilsCpp.createCall(searchBar.text + "@sip.linphone.org")
// TODO : auto completion instead of sip linphone
}
}
}
}

View file

@ -19,6 +19,8 @@ StackView{
? account.core.identityAddress
: call
? call.core.peerAddress
: contact
? contact.core.address
: ''
property var displayNameObj: UtilsCpp.getDisplayName(address)
property bool haveAvatar: (account && account.core.pictureUri )

View file

@ -0,0 +1,129 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Linphone
import UtilsCpp 1.0
ListView {
id: mainItem
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
height: contentHeight
visible: count > 0
property string initialProxyModel
property bool favorite: true
// Component.onCompleted: model.sourceModel.sourceFlags = magicSearchSourceFlags
property string searchBarText
property bool contactMenuVisible: true
model: FriendInitialProxy {
filterText: initialProxyModel
property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags
sourceModel: MagicSearchProxy {
id: search
searchText: searchBarText.length === 0 ? "*" : searchBarText
}
}
delegate: Item {
width: mainItem.width
height: 56 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
z: 1
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
contact: modelData
}
Text {
text: UtilsCpp.getDisplayName(modelData.core.address).value
font.pixelSize: 14 * DefaultStyle.dp
font.capitalization: Font.Capitalize
}
Item {
Layout.fillWidth: true
}
PopupButton {
id: friendPopup
hoverEnabled: true
visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened)
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
popup.contentItem: ColumnLayout {
Button {
background: Item{}
contentItem: RowLayout {
Image {
source: modelData.core.starred ? AppIcons.heartFill : AppIcons.heart
fillMode: Image.PreserveAspectFit
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
Text {
text: modelData.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori")
color: DefaultStyle.main2_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
onClicked: {
modelData.core.lSetStarred(!modelData.core.starred)
friendPopup.close()
}
}
Button {
background: Item{}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.trashCan
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
Text {
text: qsTr("Supprimmer")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
onClicked: {
modelData.core.remove()
friendPopup.close()
}
}
}
}
}
MouseArea {
id: contactArea
hoverEnabled: true
anchors.fill: parent
Rectangle {
anchors.fill: contactArea
opacity: 0.1
color: DefaultStyle.main2_500main
visible: contactArea.containsMouse || friendPopup.hovered
}
onClicked: {
startCallPopup.contact = modelData
startCallPopup.open()
// mainItem.callButtonPressed(modelData.core.address)
}
}
}
}

View file

@ -13,7 +13,7 @@ Button {
rightPadding: 0
topPadding: 0
bottomPadding: 0
function closePopup() {
function close() {
popup.close()
}
background: Rectangle {

View file

@ -27,10 +27,10 @@ Item {
implicitWidth: 8 * DefaultStyle.dp
color: Control.SplitHandle.hovered ? DefaultStyle.grey_200 : DefaultStyle.grey_100
}
ColumnLayout {
id: leftPanel
Control.SplitView.preferredWidth: 350 * DefaultStyle.dp
Control.SplitView.minimumWidth: 350 * DefaultStyle.dp
}
Rectangle {
id: rightPanel

View file

@ -70,7 +70,7 @@ AbstractMainPage {
colorizationColor: DefaultStyle.danger_500main
}
Text {
text: qsTr("Supprimmer lhistorique")
text: qsTr("Supprimer lhistorique")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
@ -80,7 +80,7 @@ AbstractMainPage {
}
onClicked: {
historyListView.model.removeAllEntries()
removeHistory.closePopup()
removeHistory.close()
}
}
}
@ -101,7 +101,16 @@ AbstractMainPage {
}
}
}
ColumnLayout {
SearchBar {
id: searchBar
Layout.fillWidth: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
placeholderText: qsTr("Rechercher un appel")
}
RowLayout {
Layout.topMargin: 30 * DefaultStyle.dp
Control.Control {
id: listLayout
Layout.fillWidth: true
@ -114,12 +123,6 @@ AbstractMainPage {
}
ColumnLayout {
anchors.fill: parent
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
placeholderText: qsTr("Rechercher un appel")
}
ColumnLayout {
Text {
text: qsTr("Aucun appel")
@ -129,21 +132,17 @@ AbstractMainPage {
}
visible: historyListView.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
}
ListView {
id: historyListView
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 30 * DefaultStyle.dp
model: CallHistoryProxy{
filterText: searchBar.text
}
currentIndex: -1
onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt(currentIndex)
}
spacing: 10 * DefaultStyle.dp
highlightMoveDuration: 10
highlightMoveVelocity: -1
@ -263,7 +262,9 @@ AbstractMainPage {
}
}
}
onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt(currentIndex)
}
onCountChanged: {
mainItem.showDefaultItem = historyListView.count === 0 && historyListView.visible
}
@ -288,11 +289,13 @@ AbstractMainPage {
Control.ScrollBar {
id: scrollbar
active: true
policy: Control.ScrollBar.AlwaysOn
Layout.fillHeight: true
}
}
}
}
}
Component {
id: newCallItem
ColumnLayout {
@ -324,10 +327,15 @@ AbstractMainPage {
Layout.fillWidth: true
}
}
ContactsList {
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: parent.width
// Layout.maximumWidth: parent.width
CallContactsLists {
Layout.fillWidth: true
Layout.fillHeight: true
// Layout.leftMargin: listStackView.sideMargin
// Layout.rightMargin: listStackView.sideMargin
groupCallVisible: true
searchBarColor: DefaultStyle.grey_100
@ -341,6 +349,7 @@ AbstractMainPage {
}
}
}
}
rightPanelContent: RowLayout {
Layout.fillWidth: true
@ -366,7 +375,7 @@ AbstractMainPage {
anchors.centerIn: parent
width: 100 * DefaultStyle.dp
height: 100 * DefaultStyle.dp
address: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
address: mainItem.selectedRowHistoryGui && mainItem.selectedRowHistoryGui.core.remoteAddress || ""
}
PopupButton {
id: detailOptions
@ -381,7 +390,14 @@ AbstractMainPage {
text: qsTr("Ajouter aux contacts")
iconSource: AppIcons.plusCircle
}
onClicked: console.debug("[CallPage.qml] TODO : add to contact")
onClicked: {
// console.debug("[CallPage.qml] TODO : add to contact")
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{}', detailAvatar)
friendGui.core.name = contactName.text
friendGui.core.address = contactAddress.text
friendGui.core.save()
}
}
Button {
background: Item {}
@ -413,8 +429,8 @@ AbstractMainPage {
}
onClicked: {
detailListView.model.removeEntriesWithFilter()
detailListView.currentIndex = -1 // reset index for ui
detailOptions.closePopup()
// detailListView.currentIndex = -1 // reset index for ui
detailOptions.close()
}
}
}
@ -424,6 +440,7 @@ AbstractMainPage {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Text {
id: contactName
property var remoteAddress: mainItem.selectedRowHistoryGui ? UtilsCpp.getDisplayName(mainItem.selectedRowHistoryGui.core.remoteAddress) : undefined
Layout.alignment: Qt.AlignHCenter
text: remoteAddress ? remoteAddress.value : ""
@ -434,6 +451,7 @@ AbstractMainPage {
}
}
Text {
id: contactAddress
text: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
horizontalAlignment: Text.AlignHCenter
font {
@ -554,7 +572,6 @@ AbstractMainPage {
sourceSize.height: 6.67 * DefaultStyle.dp
}
Text {
Component.onCompleted: console.log("status", modelData.core.status)
text: modelData.core.status === LinphoneEnums.CallStatus.Missed
? qsTr("Appel manqué")
: modelData.core.isOutgoing

View file

@ -32,7 +32,7 @@ Window{
contact.core.save()
}
}
Text{
Text {
text: 'IsSaved:'+contact.core.isSaved
}
}
@ -40,6 +40,7 @@ Window{
id: friends
Layout.fillHeight: true
Layout.fillWidth: true
onCountChanged: console.log("count changed", count)
model: MagicSearchProxy{
id: search

View file

@ -10,6 +10,7 @@ QtObject {
property string downArrow: "image://internal/caret-down.svg"
property string returnArrow: "image://internal/caret-left.svg"
property string rightArrow: "image://internal/caret-right.svg"
property string upArrow: "image://internal/caret-up.svg"
property string info: "image://internal/info.svg"
property string loginImage: "image://internal/login_image.svg"
property string belledonne: "image://internal/belledonne.svg"
@ -61,4 +62,6 @@ QtObject {
property string trashCan: "image://internal/trash-simple.svg"
property string copy: "image://internal/copy.svg"
property string empty: "image://internal/empty.svg"
property string heart: "image://internal/heart.svg"
property string heartFill: "image://internal/heart-fill.svg"
}