Fix moving current date.

Set dummy conference info as a null core.
Load new items in case of chaning dates.
Allow gui to load until current date.
Fix video direction on screensharing.
Update SDK for tone indication on meetings.
Clean
This commit is contained in:
Julien Wadel 2024-12-09 15:44:14 +01:00
parent 656fdc8093
commit bdbab66c94
13 changed files with 326 additions and 202 deletions

View file

@ -29,6 +29,7 @@ ConferenceInfoGui::ConferenceInfoGui() {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread()); if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
} }
ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) { ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core; mCore = core;

View file

@ -86,8 +86,7 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
for (auto &item : *items) { for (auto &item : *items) {
connectItem(item); connectItem(item);
} }
resetData<ConferenceInfoCore>(*items); resetData(*items);
updateHaveCurrentDate();
delete items; delete items;
if (isInitialization) { if (isInitialization) {
emit initialized(); emit initialized();
@ -99,24 +98,35 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
// This is needed because account does not have a contact address until // This is needed because account does not have a contact address until
// it is connected, so we can't verify if it is the organizer of a deleted // it is connected, so we can't verify if it is the organizer of a deleted
// conference (which must hidden) // conference (which must hidden)
auto connectModel = [this] { mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged,
mCoreModelConnection->invokeToModel([this]() { &ConferenceInfoList::updateCurrentAccount);
if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get()); updateCurrentAccount();
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (defaultAccount) {
mCurrentAccountCore = AccountCore::create(defaultAccount);
connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this,
[this] { emit lUpdate(); });
}
});
};
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, connectModel);
connectModel();
auto addConference = [this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoCreated,
[this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); });
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoReceived,
[this](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<const linphone::ConferenceInfo> &conferenceInfo) {
lDebug() << log().arg("conference info received") << conferenceInfo->getSubject();
addConference(conferenceInfo->clone());
});
emit lUpdate(true);
}
void ConferenceInfoList::resetData(QList<QSharedPointer<ConferenceInfoCore>> data) {
beginResetModel();
mList.clear();
for (auto i : data)
mList << i.template objectCast<QObject>();
updateHaveCurrentDate(); // Set have current date before resetting models to let proxy having this info.
endResetModel();
}
void ConferenceInfoList::addConference(const std::shared_ptr<linphone::ConferenceInfo> &confInfo) {
auto list = getSharedList<ConferenceInfoCore>(); auto list = getSharedList<ConferenceInfoCore>();
auto haveConf = auto haveConf = std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer<ConferenceInfoCore> &item) {
std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer<ConferenceInfoCore> &item) {
std::shared_ptr<linphone::Address> confAddr = nullptr; std::shared_ptr<linphone::Address> confAddr = nullptr;
if (item) ToolModel::interpretUrl(item->getUri()); if (item) ToolModel::interpretUrl(item->getUri());
return confInfo->getUri()->weakEqual(confAddr); return confInfo->getUri()->weakEqual(confAddr);
@ -124,25 +134,24 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
if (haveConf == list.end()) { if (haveConf == list.end()) {
auto confInfoCore = build(confInfo); auto confInfoCore = build(confInfo);
mCoreModelConnection->invokeToCore([this, confInfoCore] { mCoreModelConnection->invokeToCore([this, confInfoCore] {
add(confInfoCore);
connectItem(confInfoCore); connectItem(confInfoCore);
add(confInfoCore);
updateHaveCurrentDate(); updateHaveCurrentDate();
emit confInfoInserted(getCount() - 1, new ConferenceInfoGui(confInfoCore)); emit confInfoInserted(confInfoCore);
}); });
} }
}; }
mCoreModelConnection->makeConnectToModel( void ConferenceInfoList::updateCurrentAccount() {
&CoreModel::conferenceInfoCreated, mCoreModelConnection->invokeToModel([this]() {
[addConference](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); }); if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get());
mCoreModelConnection->makeConnectToModel( auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
&CoreModel::conferenceInfoReceived, if (defaultAccount) {
[this, addConference](const std::shared_ptr<linphone::Core> &core, mCurrentAccountCore = AccountCore::create(defaultAccount);
const std::shared_ptr<const linphone::ConferenceInfo> &conferenceInfo) { connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this,
lDebug() << log().arg("conference info received") << conferenceInfo->getSubject(); [this] { emit lUpdate(true); });
addConference(conferenceInfo->clone()); }
}); });
emit lUpdate(true);
} }
bool ConferenceInfoList::haveCurrentDate() const { bool ConferenceInfoList::haveCurrentDate() const {
@ -179,6 +188,19 @@ int ConferenceInfoList::getCurrentDateIndex() {
return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it); return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it);
} }
QSharedPointer<ConferenceInfoCore> ConferenceInfoList::getCurrentDateConfInfo() {
auto today = QDate::currentDate();
auto confInfoList = getSharedList<ConferenceInfoCore>();
QList<QSharedPointer<ConferenceInfoCore>>::iterator it;
if (mHaveCurrentDate) {
it = std::find_if(confInfoList.begin(), confInfoList.end(),
[today](const QSharedPointer<ConferenceInfoCore> &item) {
return item && item->getDateTimeUtc().date() == today;
});
} else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr);
return it != confInfoList.end() ? *it : nullptr;
}
QSharedPointer<ConferenceInfoCore> QSharedPointer<ConferenceInfoCore>
ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo) { ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo) {
auto me = CoreModel::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress(); auto me = CoreModel::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress();
@ -196,6 +218,7 @@ ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &confe
} }
void ConferenceInfoList::connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore) { void ConferenceInfoList::connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore) {
if (confInfoCore)
connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) { connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) {
remove(confInfo); remove(confInfo);
updateHaveCurrentDate(); updateHaveCurrentDate();
@ -221,7 +244,7 @@ QVariant ConferenceInfoList::data(const QModelIndex &index, int role) const {
} }
} else { // Dummy date } else { // Dummy date
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
return QVariant::fromValue(new ConferenceInfoGui()); return QVariant::fromValue(new ConferenceInfoGui(nullptr));
} else if (role == Qt::DisplayRole + 1) { } else if (role == Qt::DisplayRole + 1) {
return Utils::toDateMonthString(QDateTime::currentDateTimeUtc()); return Utils::toDateMonthString(QDateTime::currentDateTimeUtc());
} }

View file

@ -39,13 +39,17 @@ public:
~ConferenceInfoList(); ~ConferenceInfoList();
void setSelf(QSharedPointer<ConferenceInfoList> me); void setSelf(QSharedPointer<ConferenceInfoList> me);
void resetData(QList<QSharedPointer<ConferenceInfoCore>> data);
void addConference(const std::shared_ptr<linphone::ConferenceInfo> &confInfo);
void updateCurrentAccount();
bool haveCurrentDate() const; bool haveCurrentDate() const;
void setHaveCurrentDate(bool have); void setHaveCurrentDate(bool have);
void updateHaveCurrentDate(); void updateHaveCurrentDate();
int getCurrentDateIndex(); int getCurrentDateIndex();
QSharedPointer<ConferenceInfoCore> getCurrentDateConfInfo();
QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo); QSharedPointer<ConferenceInfoCore> build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo);
void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore); void connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore);
@ -60,7 +64,7 @@ signals:
void addCurrentDateChanged(); void addCurrentDateChanged();
void haveCurrentDateChanged(); void haveCurrentDateChanged();
void currentDateIndexChanged(int index); void currentDateIndexChanged(int index);
void confInfoInserted(int index, ConferenceInfoGui *data); void confInfoInserted(QSharedPointer<ConferenceInfoCore> data);
private: private:
QSharedPointer<SafeConnection<ConferenceInfoList, CoreModel>> mCoreModelConnection; QSharedPointer<SafeConnection<ConferenceInfoList, CoreModel>> mCoreModelConnection;

View file

@ -32,27 +32,20 @@ ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) {
connect( connect(
mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this, mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this,
[this] { [this] {
setCurrentDateIndex(getCurrentDateIndex());
auto sortModel = dynamic_cast<SortFilterList *>(sourceModel()); auto sortModel = dynamic_cast<SortFilterList *>(sourceModel());
sortModel->invalidate(); sortModel->invalidate(); // New date => sort and filter change.
loadUntil(nullptr);
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
connect(
App::getInstance(), &App::currentDateChanged, this, [this] { setCurrentDateIndex(getCurrentDateIndex()); },
Qt::QueuedConnection);
connect( connect(
mList.get(), &ConferenceInfoList::confInfoInserted, this, mList.get(), &ConferenceInfoList::confInfoInserted, this,
[this](int index, ConferenceInfoGui *data) { [this](QSharedPointer<ConferenceInfoCore> data) {
auto sortModel = dynamic_cast<SortFilterList *>(sourceModel()); auto sortModel = dynamic_cast<SortFilterList *>(sourceModel());
if (sortModel) { sortModel->invalidate(); // New conf => sort change. Filter can change if on current date.
auto proxyIndex = sortModel->mapFromSource(mList->index(index, 0)).row(); emit conferenceInfoCreated(new ConferenceInfoGui(data));
if (proxyIndex >= getMaxDisplayItems()) setMaxDisplayItems(proxyIndex + 1);
emit conferenceInfoCreated(proxyIndex);
}
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
connect(mList.get(), &ConferenceInfoList::initialized, this, connect(mList.get(), &ConferenceInfoList::initialized, this, &ConferenceInfoProxy::initialized);
[this] { setCurrentDateIndex(getCurrentDateIndex()); });
} }
ConferenceInfoProxy::~ConferenceInfoProxy() { ConferenceInfoProxy::~ConferenceInfoProxy() {
@ -90,19 +83,35 @@ bool ConferenceInfoProxy::SortFilterList::filterAcceptsRow(int sourceRow, const
} }
} }
int ConferenceInfoProxy::getCurrentDateIndex() const { void ConferenceInfoProxy::clear() {
auto sortModel = dynamic_cast<SortFilterList *>(sourceModel()); mList->clearData();
auto modelIndex = mList->getCurrentDateIndex();
auto proxyIndex = sortModel->mapFromSource(mList->index(modelIndex, 0)).row();
return proxyIndex;
} }
void ConferenceInfoProxy::setCurrentDateIndex(int index) { int ConferenceInfoProxy::loadUntil(ConferenceInfoGui *confInfo) {
if (mCurrentDateIndex != index) { return loadUntil(confInfo ? confInfo->mCore : nullptr);
if (index >= mMaxDisplayItems) setMaxDisplayItems(index + 1);
mCurrentDateIndex = index;
emit currentDateIndexChanged(index);
} }
int ConferenceInfoProxy::loadUntil(QSharedPointer<ConferenceInfoCore> data) {
auto confInfoList = getListModel<ConferenceInfoList>();
if (confInfoList) {
int listIndex = -1;
// Get list index.
if (!data) listIndex = confInfoList->getCurrentDateIndex();
else confInfoList->get(data.get(), &listIndex);
if (listIndex == -1) return -1;
// Get the index inside sorted/filtered list.
auto listModelIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(confInfoList->index(listIndex, 0));
// Load enough items into LimitProxy.
if (mMaxDisplayItems < listModelIndex.row()) setMaxDisplayItems(listModelIndex.row() + mDisplayItemsStep);
// Get the new index inside sorted/filtered list.
listModelIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(confInfoList->index(listIndex, 0));
// Get the index inside LimitProxy.
listIndex = mapFromSource(listModelIndex).row();
return listIndex;
}
return -1;
} }
bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft,

View file

@ -25,12 +25,13 @@
#include "tool/AbstractObject.hpp" #include "tool/AbstractObject.hpp"
class ConferenceInfoList; class ConferenceInfoList;
class ConferenceInfoGui;
class ConferenceInfoCore;
class ConferenceInfoProxy : public LimitProxy, public AbstractObject { class ConferenceInfoProxy : public LimitProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool haveCurrentDate READ haveCurrentDate NOTIFY haveCurrentDateChanged) Q_PROPERTY(bool haveCurrentDate READ haveCurrentDate NOTIFY haveCurrentDateChanged)
// Q_PROPERTY(int currentDateIndex READ getCurrentDateIndex NOTIFY currentDateIndexChanged)
public: public:
enum ConferenceInfoFiltering { None = 0, Future = 1 }; enum ConferenceInfoFiltering { None = 0, Future = 1 };
@ -43,17 +44,18 @@ public:
bool haveCurrentDate() const; bool haveCurrentDate() const;
Q_INVOKABLE int getCurrentDateIndex() const; Q_INVOKABLE void clear();
Q_INVOKABLE void setCurrentDateIndex(int index); Q_INVOKABLE int loadUntil(ConferenceInfoGui *confInfo);
int loadUntil(QSharedPointer<ConferenceInfoCore> data);
signals: signals:
void initialized();
void haveCurrentDateChanged(); void haveCurrentDateChanged();
void conferenceInfoCreated(int index); void conferenceInfoCreated(ConferenceInfoGui *confInfo);
void currentDateIndexChanged(int index);
private: private:
QSharedPointer<ConferenceInfoList> mList; QSharedPointer<ConferenceInfoList> mList;
int mCurrentDateIndex = -1; ConferenceInfoCore *mCurrentConfInfo = nullptr;
// int mCurrentDateIndex = -1;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -111,7 +111,7 @@ int MagicSearchProxy::loadUntil(const QString &address) {
if (listIndex == -1) return -1; if (listIndex == -1) return -1;
listIndex = listIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row(); dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row();
if (mMaxDisplayItems < listIndex) setMaxDisplayItems(listIndex + 1); if (mMaxDisplayItems < listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex; return listIndex;
} }
return -1; return -1;

View file

@ -143,11 +143,11 @@ void ConferenceModel::toggleScreenSharing() {
if (enable) { if (enable) {
params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker); params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker);
params->enableVideo(true); params->enableVideo(true);
params->enableCamera(false);
auto videoDirection = params->getVideoDirection(); auto videoDirection = params->getVideoDirection();
if (videoDirection != linphone::MediaDirection::SendOnly && params->setVideoDirection(videoDirection == linphone::MediaDirection::RecvOnly ||
videoDirection != linphone::MediaDirection::SendRecv) videoDirection == linphone::MediaDirection::SendRecv
params->setVideoDirection(linphone::MediaDirection::SendOnly); ? linphone::MediaDirection::SendRecv
: linphone::MediaDirection::SendOnly);
} }
if (params->isValid()) mMonitor->getCall()->update(params); if (params->isValid()) mMonitor->getCall()->update(params);
else lCritical() << log().arg("Cannot toggle screen sharing because parameters are invalid"); else lCritical() << log().arg("Cannot toggle screen sharing because parameters are invalid");

View file

@ -26,7 +26,6 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
columns: mainItem.useVerticalLayout ? 1 : children.length columns: mainItem.useVerticalLayout ? 1 : children.length
rows: 1 rows: 1
onColumnsChanged: console.log("columns changed", columns, rows)
columnSpacing: 49 * DefaultStyle.dp columnSpacing: 49 * DefaultStyle.dp
rowSpacing: 27 * DefaultStyle.dp rowSpacing: 27 * DefaultStyle.dp

View file

@ -101,6 +101,41 @@ Flickable{
suggestionsList.currentIndex = -1 suggestionsList.currentIndex = -1
} }
function findNextList(item, count, direction){
if(count == 3) return null
var nextItem
switch(item){
case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break;
case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break;
case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break;
default: return null
}
if( nextItem.model.count > 0) return nextItem
else return findNextList(nextItem, count+1, direction)
}
function updatePosition(list){
var item = list.itemAtIndex(list.currentIndex)
var centerItemPos = 0
if( item && list.expanded){
// For debugging just in case
//var listPosition = item.mapToItem(favoriteList, item.x, item.y)
//var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y)
//console.log("item pos: " +item.x + " / " +item.y)
//console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y)
//console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY)
//console.log("main pos: " +mainItem.x + " / " +mainItem.y)
//console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY)
//console.log("list pos: " +listPosition.x + " / " +listPosition.y)
//console.log("new pos: " +newPosition.x + " / " +newPosition.y)
//console.log("header pos: " +headerItem.x + " / " +headerItem.y)
//console.log("Moving to " + (headerItem.y+item.y))
centerItemPos = item.y + list.y + list.headerHeight +item.height/2
}
var centerPos = centerItemPos - height/2
mainItem.contentY = Math.max(0, Math.min(centerPos, height, contentHeight-height))
}
onHighlightedContactChanged:{ onHighlightedContactChanged:{
favoritesList.highlightedContact = highlightedContact favoritesList.highlightedContact = highlightedContact
contactsList.highlightedContact = highlightedContact contactsList.highlightedContact = highlightedContact
@ -126,39 +161,6 @@ Flickable{
if( (contactsProxy.haveMore && contactList.expanded ) || mainItem.hideSuggestions) contactsProxy.displayMore() if( (contactsProxy.haveMore && contactList.expanded ) || mainItem.hideSuggestions) contactsProxy.displayMore()
else suggestionsProxy.displayMore() else suggestionsProxy.displayMore()
} }
function findNextList(item, count, direction){
if(count == 3) return null
var nextItem
switch(item){
case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break;
case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break;
case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break;
default: return null
}
if( nextItem.model.count > 0) return nextItem
else return findNextList(nextItem, count+1, direction)
}
function updatePosition(list){
var item = list.itemAtIndex(list.currentIndex)
var centerItemPos = 0
if( item && list.expanded){
// For debugging just in case
//var listPosition = item.mapToItem(favoriteList, item.x, item.y)
//var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y)
//console.log("item pos: " +item.x + " / " +item.y)
//console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y)
//console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY)
//console.log("main pos: " +mainItem.x + " / " +mainItem.y)
//console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY)
//console.log("list pos: " +listPosition.x + " / " +listPosition.y)
//console.log("new pos: " +newPosition.x + " / " +newPosition.y)
//console.log("header pos: " +headerItem.x + " / " +headerItem.y)
//console.log("Moving to " + (headerItem.y+item.y))
centerItemPos = item.y + list.y + list.headerHeight +item.height/2
}
var centerPos = centerItemPos - height/2
mainItem.contentY = Math.max(0, Math.min(centerPos, height, contentHeight-height))
}
Behavior on contentY{ Behavior on contentY{
NumberAnimation { NumberAnimation {
duration: 500 duration: 500

View file

@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Controls.Basic
import Linphone import Linphone
import QtQml import QtQml
@ -8,36 +9,121 @@ import UtilsCpp
ListView { ListView {
id: mainItem id: mainItem
visible: count > 0
clip: true
property string searchBarText property string searchBarText
property bool hoverEnabled: true property bool hoverEnabled: true
property var delegateButtons property var delegateButtons
property ConferenceInfoGui selectedConference: model && currentIndex != -1 ? model.getAt(currentIndex) : null property ConferenceInfoGui selectedConference
property bool _moveToIndex: false
visible: count > 0
clip: true
cacheBuffer: height/2
spacing: 8 * DefaultStyle.dp spacing: 8 * DefaultStyle.dp
highlightFollowsCurrentItem: true highlightFollowsCurrentItem: false
highlightMoveVelocity: 1500
onCountChanged: { function selectIndex(index){
selectedConference = model && currentIndex != -1 && currentIndex < model.count ? model.getAt(currentIndex) : null mainItem.currentIndex = index
}
function resetSelections(){
mainItem.selectedConference = null
mainItem.currentIndex = -1
}
// Issues Notes:
// positionViewAtIndex:
// - if currentItem was in cache, it will not go to it (ex: contentY=63, currentItem.y=3143)
// - Animation don't work
function moveToCurrentItem(){
var centerItemPos = 0
if( currentItem){
centerItemPos = currentItem.y + currentItem.height/2
}
var centerPos = centerItemPos - height/2
moveBehaviorTimer.startAnimation()
mainItem.contentY = Math.max(0, Math.min(centerPos, contentHeight-height))
}
onCurrentItemChanged: {
moveToCurrentItem()
if(currentItem) {
mainItem.selectedConference = currentItem.itemGui
currentItem.forceActiveFocus()
}
}
// When cache is updating, contentHeight changes. Update position if we are moving the view.
onContentHeightChanged:{
if(moveBehavior.enabled){
moveToCurrentItem()
} }
onCurrentIndexChanged: {
selectedConference = model.getAt(currentIndex) || null
} }
onAtYEndChanged: if(atYEnd) confInfoProxy.displayMore() onAtYEndChanged: if(atYEnd) confInfoProxy.displayMore()
Timer{
id: moveBehaviorTimer
interval: 501
onTriggered: moveBehavior.enabled = false
function startAnimation(){
moveBehavior.enabled = true
moveBehaviorTimer.restart()
}
}
Behavior on contentY{
id: moveBehavior
enabled: false
NumberAnimation {
duration: 500
easing.type: Easing.OutExpo
onFinished: {// Not call if on Behavior. Callback just in case.
moveBehavior.enabled = false
}
}
}
Keys.onPressed: (event)=> {
if(event.key == Qt.Key_Up) {
if(currentIndex > 0 ) {
selectIndex(mainItem.currentIndex-1)
event.accepted = true
} else {
selectIndex(model.count - 1)
event.accepted = true
}
}else if(event.key == Qt.Key_Down){
if(currentIndex < model.count - 1) {
selectIndex(currentIndex+1)
event.accepted = true
} else {
selectIndex(0)
event.accepted = true
}
}
}
model: ConferenceInfoProxy { model: ConferenceInfoProxy {
id: confInfoProxy id: confInfoProxy
filterText: searchBarText filterText: searchBarText
filterType: ConferenceInfoProxy.None filterType: ConferenceInfoProxy.None
initialDisplayItems: mainItem.height / (63 * DefaultStyle.dp) + 5 initialDisplayItems: mainItem.height / (63 * DefaultStyle.dp) + 5
displayItemsStep: initialDisplayItems/2 displayItemsStep: initialDisplayItems/2
onConferenceInfoCreated: (index) => { function selectData(confInfoGui){
mainItem.currentIndex = index mainItem.currentIndex = loadUntil(confInfoGui)
} }
onCurrentDateIndexChanged: (index) => mainItem.currentIndex = index onConferenceInfoCreated: (confInfoGui) => {
selectData(confInfoGui)
}
onInitialized: {
// Move to currentDate
selectData(null)
}
}
ScrollBar.vertical: ScrollBar {
id: scrollbar
rightPadding: 8 * DefaultStyle.dp
active: true
interactive: true
policy: mainItem.contentHeight > mainItem.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
} }
section { section {
@ -59,27 +145,28 @@ ListView {
delegate: FocusScope { delegate: FocusScope {
id: itemDelegate id: itemDelegate
height: 63 * DefaultStyle.dp height: 63 * DefaultStyle.dp + (!isFirst && dateDay.visible ? topOffset : 0)
width: mainItem.width width: mainItem.width
enabled: !isCanceled && haveModel enabled: !isCanceled && haveModel
property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null
property var dateTime: !!$modelData && $modelData.core.haveModel ? $modelData.core.dateTime : UtilsCpp.getCurrentDateTime() property var itemGui: $modelData
// Do not use itemAtIndex because of caching items. Using getAt ensure to have a GUI
property var previousConfInfoGui : mainItem.model.getAt(index-1)
property var dateTime: itemGui.core ? itemGui.core.dateTimeUtc : UtilsCpp.getCurrentDateTime()
property string day : UtilsCpp.toDateDayNameString(dateTime) property string day : UtilsCpp.toDateDayNameString(dateTime)
property string dateString: UtilsCpp.toDateString(dateTime) property string dateString: UtilsCpp.toDateString(dateTime)
property string previousDateString: previousItem ? UtilsCpp.toDateString(previousItem.core ? previousItem.core.dateTimeUtc : UtilsCpp.getCurrentDateTimeUtc()) : '' property string previousDateString: previousConfInfoGui ? UtilsCpp.toDateString(previousConfInfoGui.core ? previousConfInfoGui.core.dateTimeUtc : UtilsCpp.getCurrentDateTimeUtc()) : ''
property bool isFirst : ListView.previousSection !== ListView.section property bool isFirst : ListView.previousSection !== ListView.section
property int topOffset: (dateDay.visible && !isFirst? 8 * DefaultStyle.dp : 0) property int topOffset: (dateDay.visible && !isFirst? 8 * DefaultStyle.dp : 0)
property var endDateTime: $modelData ? $modelData.core.endDateTime : UtilsCpp.getCurrentDateTime() property var endDateTime: itemGui.core ? itemGui.core.endDateTime : UtilsCpp.getCurrentDateTime()
property var haveModel: !!$modelData && $modelData != undefined && $modelData.core.haveModel || false property bool haveModel: itemGui.core ? itemGui.core.haveModel : false
property bool isCanceled: $modelData?.core.state === LinphoneEnums.ConferenceInfoState.Cancelled || false property bool isCanceled: itemGui.core ? itemGui.core.state === LinphoneEnums.ConferenceInfoState.Cancelled : false
Component.onCompleted: if (!isFirst && dateDay.visible) { property bool isSelected: itemGui.core == mainItem.selectedConference?.core
height = (63+topOffset)*DefaultStyle.dp
delegateIn.anchors.topMargin = topOffset
}
RowLayout{ RowLayout{
id: delegateIn id: delegateIn
anchors.fill: parent anchors.fill: parent
anchors.topMargin: !itemDelegate.isFirst && dateDay.visible ? itemDelegate.topOffset : 0
spacing: 0 spacing: 0
Item{ Item{
Layout.preferredWidth: 32 * DefaultStyle.dp Layout.preferredWidth: 32 * DefaultStyle.dp
@ -141,7 +228,7 @@ ListView {
anchors.rightMargin: 5 // margin to avoid clipping shadows at right anchors.rightMargin: 5 // margin to avoid clipping shadows at right
radius: 10 * DefaultStyle.dp radius: 10 * DefaultStyle.dp
visible: itemDelegate.haveModel || itemDelegate.activeFocus visible: itemDelegate.haveModel || itemDelegate.activeFocus
color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.grey_0 color: itemDelegate.isSelected ? DefaultStyle.main2_200 : DefaultStyle.grey_0 // mainItem.currentIndex === index
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.left: parent.left anchors.left: parent.left
@ -156,7 +243,7 @@ ListView {
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
} }
Text { Text {
text: $modelData? $modelData.core.subject : "" text: itemGui.core? itemGui.core.subject : ""
Layout.fillWidth: true Layout.fillWidth: true
maximumLineCount: 1 maximumLineCount: 1
font { font {
@ -203,8 +290,7 @@ ListView {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
visible: itemDelegate.haveModel visible: itemDelegate.haveModel
onClicked: { onClicked: {
mainItem.currentIndex = index mainItem.selectIndex(index)
itemDelegate.forceActiveFocus()
} }
} }
} }

View file

@ -31,7 +31,6 @@ AbstractMainPage {
if (rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName === "contactDetail") rightPanelStackView.clear() if (rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName === "contactDetail") rightPanelStackView.clear()
} }
} }
signal forceListsUpdate()
onNoItemButtonPressed: createContact("", "") onNoItemButtonPressed: createContact("", "")

View file

@ -8,30 +8,18 @@ import UtilsCpp
// TODO : spacing // TODO : spacing
AbstractMainPage { AbstractMainPage {
id: mainItem id: mainItem
noItemButtonText: qsTr("Créer une réunion")
emptyListText: qsTr("Aucune réunion")
newItemIconSource: AppIcons.plusCircle
rightPanelColor: selectedConference ? DefaultStyle.grey_0 : DefaultStyle.grey_100
property ConferenceInfoGui selectedConference property ConferenceInfoGui selectedConference
property int meetingListCount property int meetingListCount
signal returnRequested() signal returnRequested()
signal addParticipantsValidated(list<string> selectedParticipants) signal addParticipantsValidated(list<string> selectedParticipants)
Component.onCompleted: rightPanelStackView.push(overridenRightPanel, Control.StackView.Immediate)
noItemButtonText: qsTr("Créer une réunion")
emptyListText: qsTr("Aucune réunion")
newItemIconSource: AppIcons.plusCircle
rightPanelColor: selectedConference ? DefaultStyle.grey_0 : DefaultStyle.grey_100
showDefaultItem: leftPanelStackView.currentItem?.objectName === "listLayout" && meetingListCount === 0 showDefaultItem: leftPanelStackView.currentItem?.objectName === "listLayout" && meetingListCount === 0
onVisibleChanged: if (!visible) {
leftPanelStackView.clear()
leftPanelStackView.push(leftPanelStackView.initialItem)
}
onSelectedConferenceChanged: {
overridenRightPanelStackView.clear()
if (selectedConference && selectedConference.core.haveModel) {
if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate)
}
}
onNoItemButtonPressed: editConference()
function editConference(confInfoGui = null) { function editConference(confInfoGui = null) {
var isCreation = !confInfoGui var isCreation = !confInfoGui
@ -50,6 +38,33 @@ AbstractMainPage {
} }
} }
onVisibleChanged: if (!visible) {
leftPanelStackView.clear()
leftPanelStackView.push(leftPanelStackView.initialItem)
}
onSelectedConferenceChanged: {
overridenRightPanelStackView.clear()
if (selectedConference && selectedConference.core && selectedConference.core.haveModel) {
if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate)
}
}
onNoItemButtonPressed: editConference()
Component.onCompleted: rightPanelStackView.push(overridenRightPanel, Control.StackView.Immediate)
leftPanelContent: Control.StackView {
id: leftPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 45 * DefaultStyle.dp
initialItem: listLayout
clip: true
}
Dialog { Dialog {
id: cancelAndDeleteConfDialog id: cancelAndDeleteConfDialog
property bool cancel: false property bool cancel: false
@ -96,15 +111,6 @@ AbstractMainPage {
] ]
} }
leftPanelContent: Control.StackView {
id: leftPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 45 * DefaultStyle.dp
initialItem: listLayout
clip: true
}
Item { Item {
id: overridenRightPanel id: overridenRightPanel
Control.StackView { Control.StackView {
@ -188,8 +194,13 @@ AbstractMainPage {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
onCountChanged: mainItem.meetingListCount = count
searchBarText: searchBar.text searchBarText: searchBar.text
onCountChanged: mainItem.meetingListCount = count
onSelectedConferenceChanged: {
mainItem.selectedConference = selectedConference
}
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if(event.key == Qt.Key_Escape){ if(event.key == Qt.Key_Escape){
searchBar.forceActiveFocus() searchBar.forceActiveFocus()
@ -199,18 +210,6 @@ AbstractMainPage {
event.accepted = true event.accepted = true
} }
} }
onSelectedConferenceChanged: {
mainItem.selectedConference = selectedConference
}
Control.ScrollBar.vertical: ScrollBar {
id: meetingsScrollbar
anchors.right: parent.right
anchors.rightMargin: 8 * DefaultStyle.dp
active: true
interactive: true
policy: Control.ScrollBar.AsNeeded
}
} }
} }
} }
@ -579,7 +578,7 @@ AbstractMainPage {
} }
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: mainItem.selectedConference ? mainItem.selectedConference.core.subject : "" text: mainItem.selectedConference ? mainItem.selectedConference.core?.subject : ""
maximumLineCount: 1 maximumLineCount: 1
font { font {
pixelSize: 20 * DefaultStyle.dp pixelSize: 20 * DefaultStyle.dp
@ -591,7 +590,7 @@ AbstractMainPage {
} }
Button { Button {
id: editButton id: editButton
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core.organizerAddress) property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress)
visible: mainItem.selectedConference && isMeObj && isMeObj.value || false visible: mainItem.selectedConference && isMeObj && isMeObj.value || false
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
@ -622,8 +621,8 @@ AbstractMainPage {
textColor: DefaultStyle.danger_500main textColor: DefaultStyle.danger_500main
contentImageColor: DefaultStyle.danger_500main contentImageColor: DefaultStyle.danger_500main
inversedColors: true inversedColors: true
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core.organizerAddress) property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress)
property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference.core.state !== LinphoneEnums.ConferenceInfoState.Cancelled property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference?.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
icon.source: AppIcons.trashCan icon.source: AppIcons.trashCan
icon.width: 24 * DefaultStyle.dp icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp icon.height: 24 * DefaultStyle.dp
@ -670,7 +669,7 @@ AbstractMainPage {
id: linkButton id: linkButton
Layout.fillWidth: true Layout.fillWidth: true
font.bold: shadowEnabled font.bold: shadowEnabled
text: mainItem.selectedConference ? mainItem.selectedConference.core.uri : "" text: mainItem.selectedConference ? mainItem.selectedConference.core?.uri : ""
textSize: 14 * DefaultStyle.dp textSize: 14 * DefaultStyle.dp
textWeight: 400 * DefaultStyle.dp textWeight: 400 * DefaultStyle.dp
underline: true underline: true
@ -719,10 +718,10 @@ AbstractMainPage {
} }
Text { Text {
text: mainItem.selectedConference text: mainItem.selectedConference
? UtilsCpp.toDateString(mainItem.selectedConference.core.dateTimeUtc) ? UtilsCpp.toDateString(mainItem.selectedConference.core?.dateTimeUtc)
+ " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core.dateTimeUtc) + " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core?.dateTimeUtc)
+ " - " + " - "
+ UtilsCpp.toDateHourString(mainItem.selectedConference.core.endDateTime) + UtilsCpp.toDateHourString(mainItem.selectedConference.core?.endDateTime)
: '' : ''
font { font {
pixelSize: 14 * DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
@ -738,7 +737,7 @@ AbstractMainPage {
source: AppIcons.globe source: AppIcons.globe
} }
Text { Text {
text: qsTr("Time zone: ") + (mainItem.selectedConference ? (mainItem.selectedConference.core.timeZoneModel.displayName + ", " + mainItem.selectedConference.core.timeZoneModel.countryName) : "") text: qsTr("Time zone: ") + (mainItem.selectedConference ? (mainItem.selectedConference.core?.timeZoneModel.displayName + ", " + mainItem.selectedConference.core.timeZoneModel.countryName) : "")
font { font {
pixelSize: 14 * DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
capitalization: Font.Capitalize capitalization: Font.Capitalize
@ -748,7 +747,7 @@ AbstractMainPage {
} }
} }
Section { Section {
visible: mainItem.selectedConference && mainItem.selectedConference.core.description.length != 0 visible: mainItem.selectedConference && mainItem.selectedConference.core?.description.length != 0
content: RowLayout { content: RowLayout {
spacing: 8 * DefaultStyle.dp spacing: 8 * DefaultStyle.dp
EffectImage { EffectImage {
@ -758,7 +757,7 @@ AbstractMainPage {
colorizationColor: DefaultStyle.main2_600 colorizationColor: DefaultStyle.main2_600
} }
Text { Text {
text: mainItem.selectedConference ? mainItem.selectedConference.core.description : "" text: mainItem.selectedConference ? mainItem.selectedConference.core?.description : ""
Layout.fillWidth: true Layout.fillWidth: true
font { font {
pixelSize: 14 * DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
@ -779,10 +778,10 @@ AbstractMainPage {
Avatar { Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp Layout.preferredHeight: 45 * DefaultStyle.dp
_address: mainItem.selectedConference ? mainItem.selectedConference.core.organizerAddress : "" _address: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerAddress : ""
} }
Text { Text {
text: mainItem.selectedConference ? mainItem.selectedConference.core.organizerName : "" text: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerName : ""
font { font {
pixelSize: 14 * DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
capitalization: Font.Capitalize capitalization: Font.Capitalize
@ -807,7 +806,7 @@ AbstractMainPage {
id: participantList id: participantList
Layout.preferredHeight: Math.min(184 * DefaultStyle.dp, contentHeight) Layout.preferredHeight: Math.min(184 * DefaultStyle.dp, contentHeight)
Layout.fillWidth: true Layout.fillWidth: true
model: mainItem.selectedConference ? mainItem.selectedConference.core.participants : [] model: mainItem.selectedConference ? mainItem.selectedConference.core?.participants : []
clip: true clip: true
delegate: RowLayout { delegate: RowLayout {
height: 56 * DefaultStyle.dp height: 56 * DefaultStyle.dp
@ -828,7 +827,7 @@ AbstractMainPage {
} }
Text { Text {
text: qsTr("Organizer") text: qsTr("Organizer")
visible: mainItem.selectedConference && mainItem.selectedConference.core.organizerAddress === modelData.address visible: mainItem.selectedConference && mainItem.selectedConference.core?.organizerAddress === modelData.address
color: DefaultStyle.main2_400 color: DefaultStyle.main2_400
font { font {
pixelSize: 12 * DefaultStyle.dp pixelSize: 12 * DefaultStyle.dp
@ -841,7 +840,7 @@ AbstractMainPage {
} }
Button { Button {
id: joinButton id: joinButton
visible: mainItem.selectedConference && mainItem.selectedConference.core.state !== LinphoneEnums.ConferenceInfoState.Cancelled visible: mainItem.selectedConference && mainItem.selectedConference.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Rejoindre la réunion") text: qsTr("Rejoindre la réunion")
topPadding: 11 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp

@ -1 +1 @@
Subproject commit c8d895cb162e0d6782d339e2ea3950ba2b8ca3da Subproject commit 3e3edec2889317585d5267d764885b2c25806aeb