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);
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
ConferenceInfoGui::ConferenceInfoGui(QSharedPointer<ConferenceInfoCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;

View file

@ -86,8 +86,7 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
for (auto &item : *items) {
connectItem(item);
}
resetData<ConferenceInfoCore>(*items);
updateHaveCurrentDate();
resetData(*items);
delete items;
if (isInitialization) {
emit initialized();
@ -99,52 +98,62 @@ void ConferenceInfoList::setSelf(QSharedPointer<ConferenceInfoList> me) {
// 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
// conference (which must hidden)
auto connectModel = [this] {
mCoreModelConnection->invokeToModel([this]() {
if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get());
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) {
auto list = getSharedList<ConferenceInfoCore>();
auto haveConf =
std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer<ConferenceInfoCore> &item) {
std::shared_ptr<linphone::Address> confAddr = nullptr;
if (item) ToolModel::interpretUrl(item->getUri());
return confInfo->getUri()->weakEqual(confAddr);
});
if (haveConf == list.end()) {
auto confInfoCore = build(confInfo);
mCoreModelConnection->invokeToCore([this, confInfoCore] {
add(confInfoCore);
connectItem(confInfoCore);
updateHaveCurrentDate();
emit confInfoInserted(getCount() - 1, new ConferenceInfoGui(confInfoCore));
});
}
};
mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged,
&ConferenceInfoList::updateCurrentAccount);
updateCurrentAccount();
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoCreated,
[addConference](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); });
[this](const std::shared_ptr<linphone::ConferenceInfo> &confInfo) { addConference(confInfo); });
mCoreModelConnection->makeConnectToModel(
&CoreModel::conferenceInfoReceived,
[this, addConference](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<const linphone::ConferenceInfo> &conferenceInfo) {
[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 haveConf = std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer<ConferenceInfoCore> &item) {
std::shared_ptr<linphone::Address> confAddr = nullptr;
if (item) ToolModel::interpretUrl(item->getUri());
return confInfo->getUri()->weakEqual(confAddr);
});
if (haveConf == list.end()) {
auto confInfoCore = build(confInfo);
mCoreModelConnection->invokeToCore([this, confInfoCore] {
connectItem(confInfoCore);
add(confInfoCore);
updateHaveCurrentDate();
emit confInfoInserted(confInfoCore);
});
}
}
void ConferenceInfoList::updateCurrentAccount() {
mCoreModelConnection->invokeToModel([this]() {
if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get());
auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
if (defaultAccount) {
mCurrentAccountCore = AccountCore::create(defaultAccount);
connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this,
[this] { emit lUpdate(true); });
}
});
}
bool ConferenceInfoList::haveCurrentDate() const {
return mHaveCurrentDate;
}
@ -179,6 +188,19 @@ int ConferenceInfoList::getCurrentDateIndex() {
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>
ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &conferenceInfo) {
auto me = CoreModel::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress();
@ -196,10 +218,11 @@ ConferenceInfoList::build(const std::shared_ptr<linphone::ConferenceInfo> &confe
}
void ConferenceInfoList::connectItem(QSharedPointer<ConferenceInfoCore> confInfoCore) {
connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) {
remove(confInfo);
updateHaveCurrentDate();
});
if (confInfoCore)
connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) {
remove(confInfo);
updateHaveCurrentDate();
});
}
QHash<int, QByteArray> ConferenceInfoList::roleNames() const {
@ -221,10 +244,10 @@ QVariant ConferenceInfoList::data(const QModelIndex &index, int role) const {
}
} else { // Dummy date
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new ConferenceInfoGui());
return QVariant::fromValue(new ConferenceInfoGui(nullptr));
} else if (role == Qt::DisplayRole + 1) {
return Utils::toDateMonthString(QDateTime::currentDateTimeUtc());
}
}
return QVariant();
}
}

View file

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

View file

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

View file

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

View file

@ -111,7 +111,7 @@ int MagicSearchProxy::loadUntil(const QString &address) {
if (listIndex == -1) return -1;
listIndex =
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 -1;

View file

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

View file

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

View file

@ -100,6 +100,41 @@ Flickable{
contactsList.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:{
favoritesList.highlightedContact = highlightedContact
@ -126,39 +161,6 @@ Flickable{
if( (contactsProxy.haveMore && contactList.expanded ) || mainItem.hideSuggestions) contactsProxy.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{
NumberAnimation {
duration: 500

View file

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Effects
import QtQuick.Controls.Basic
import Linphone
import QtQml
@ -8,36 +9,121 @@ import UtilsCpp
ListView {
id: mainItem
visible: count > 0
clip: true
property string searchBarText
property bool hoverEnabled: true
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
highlightFollowsCurrentItem: true
highlightMoveVelocity: 1500
onCountChanged: {
selectedConference = model && currentIndex != -1 && currentIndex < model.count ? model.getAt(currentIndex) : null
highlightFollowsCurrentItem: false
function selectIndex(index){
mainItem.currentIndex = index
}
onCurrentIndexChanged: {
selectedConference = model.getAt(currentIndex) || null
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()
}
}
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 {
id: confInfoProxy
filterText: searchBarText
filterType: ConferenceInfoProxy.None
initialDisplayItems: mainItem.height / (63 * DefaultStyle.dp) + 5
displayItemsStep: initialDisplayItems/2
onConferenceInfoCreated: (index) => {
mainItem.currentIndex = index
function selectData(confInfoGui){
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 {
@ -56,30 +142,31 @@ ListView {
}
property: '$sectionMonth'
}
delegate: FocusScope {
id: itemDelegate
height: 63 * DefaultStyle.dp
height: 63 * DefaultStyle.dp + (!isFirst && dateDay.visible ? topOffset : 0)
width: mainItem.width
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 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 int topOffset: (dateDay.visible && !isFirst? 8 * DefaultStyle.dp : 0)
property var endDateTime: $modelData ? $modelData.core.endDateTime : UtilsCpp.getCurrentDateTime()
property var haveModel: !!$modelData && $modelData != undefined && $modelData.core.haveModel || false
property bool isCanceled: $modelData?.core.state === LinphoneEnums.ConferenceInfoState.Cancelled || false
Component.onCompleted: if (!isFirst && dateDay.visible) {
height = (63+topOffset)*DefaultStyle.dp
delegateIn.anchors.topMargin = topOffset
}
property var endDateTime: itemGui.core ? itemGui.core.endDateTime : UtilsCpp.getCurrentDateTime()
property bool haveModel: itemGui.core ? itemGui.core.haveModel : false
property bool isCanceled: itemGui.core ? itemGui.core.state === LinphoneEnums.ConferenceInfoState.Cancelled : false
property bool isSelected: itemGui.core == mainItem.selectedConference?.core
RowLayout{
id: delegateIn
anchors.fill: parent
anchors.topMargin: !itemDelegate.isFirst && dateDay.visible ? itemDelegate.topOffset : 0
spacing: 0
Item{
Layout.preferredWidth: 32 * DefaultStyle.dp
@ -141,7 +228,7 @@ ListView {
anchors.rightMargin: 5 // margin to avoid clipping shadows at right
radius: 10 * DefaultStyle.dp
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 {
anchors.fill: parent
anchors.left: parent.left
@ -156,7 +243,7 @@ ListView {
Layout.preferredHeight: 24 * DefaultStyle.dp
}
Text {
text: $modelData? $modelData.core.subject : ""
text: itemGui.core? itemGui.core.subject : ""
Layout.fillWidth: true
maximumLineCount: 1
font {
@ -203,8 +290,7 @@ ListView {
cursorShape: Qt.PointingHandCursor
visible: itemDelegate.haveModel
onClicked: {
mainItem.currentIndex = index
itemDelegate.forceActiveFocus()
mainItem.selectIndex(index)
}
}
}

View file

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

View file

@ -8,30 +8,18 @@ import UtilsCpp
// TODO : spacing
AbstractMainPage {
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 int meetingListCount
signal returnRequested()
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
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) {
var isCreation = !confInfoGui
@ -49,6 +37,33 @@ AbstractMainPage {
item.forceActiveFocus()
}
}
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 {
id: cancelAndDeleteConfDialog
@ -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 {
id: overridenRightPanel
Control.StackView {
@ -188,8 +194,13 @@ AbstractMainPage {
Layout.fillWidth: true
Layout.fillHeight: true
onCountChanged: mainItem.meetingListCount = count
searchBarText: searchBar.text
onCountChanged: mainItem.meetingListCount = count
onSelectedConferenceChanged: {
mainItem.selectedConference = selectedConference
}
Keys.onPressed: (event) => {
if(event.key == Qt.Key_Escape){
searchBar.forceActiveFocus()
@ -199,18 +210,6 @@ AbstractMainPage {
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 {
Layout.fillWidth: true
text: mainItem.selectedConference ? mainItem.selectedConference.core.subject : ""
text: mainItem.selectedConference ? mainItem.selectedConference.core?.subject : ""
maximumLineCount: 1
font {
pixelSize: 20 * DefaultStyle.dp
@ -591,7 +590,7 @@ AbstractMainPage {
}
Button {
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
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
@ -622,8 +621,8 @@ AbstractMainPage {
textColor: DefaultStyle.danger_500main
contentImageColor: DefaultStyle.danger_500main
inversedColors: true
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core.organizerAddress)
property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference.core.state !== LinphoneEnums.ConferenceInfoState.Cancelled
property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress)
property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference?.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled
icon.source: AppIcons.trashCan
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
@ -670,7 +669,7 @@ AbstractMainPage {
id: linkButton
Layout.fillWidth: true
font.bold: shadowEnabled
text: mainItem.selectedConference ? mainItem.selectedConference.core.uri : ""
text: mainItem.selectedConference ? mainItem.selectedConference.core?.uri : ""
textSize: 14 * DefaultStyle.dp
textWeight: 400 * DefaultStyle.dp
underline: true
@ -719,10 +718,10 @@ AbstractMainPage {
}
Text {
text: mainItem.selectedConference
? UtilsCpp.toDateString(mainItem.selectedConference.core.dateTimeUtc)
+ " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core.dateTimeUtc)
? UtilsCpp.toDateString(mainItem.selectedConference.core?.dateTimeUtc)
+ " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core?.dateTimeUtc)
+ " - "
+ UtilsCpp.toDateHourString(mainItem.selectedConference.core.endDateTime)
+ UtilsCpp.toDateHourString(mainItem.selectedConference.core?.endDateTime)
: ''
font {
pixelSize: 14 * DefaultStyle.dp
@ -738,7 +737,7 @@ AbstractMainPage {
source: AppIcons.globe
}
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 {
pixelSize: 14 * DefaultStyle.dp
capitalization: Font.Capitalize
@ -748,7 +747,7 @@ AbstractMainPage {
}
}
Section {
visible: mainItem.selectedConference && mainItem.selectedConference.core.description.length != 0
visible: mainItem.selectedConference && mainItem.selectedConference.core?.description.length != 0
content: RowLayout {
spacing: 8 * DefaultStyle.dp
EffectImage {
@ -758,7 +757,7 @@ AbstractMainPage {
colorizationColor: DefaultStyle.main2_600
}
Text {
text: mainItem.selectedConference ? mainItem.selectedConference.core.description : ""
text: mainItem.selectedConference ? mainItem.selectedConference.core?.description : ""
Layout.fillWidth: true
font {
pixelSize: 14 * DefaultStyle.dp
@ -779,10 +778,10 @@ AbstractMainPage {
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
_address: mainItem.selectedConference ? mainItem.selectedConference.core.organizerAddress : ""
_address: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerAddress : ""
}
Text {
text: mainItem.selectedConference ? mainItem.selectedConference.core.organizerName : ""
text: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerName : ""
font {
pixelSize: 14 * DefaultStyle.dp
capitalization: Font.Capitalize
@ -807,7 +806,7 @@ AbstractMainPage {
id: participantList
Layout.preferredHeight: Math.min(184 * DefaultStyle.dp, contentHeight)
Layout.fillWidth: true
model: mainItem.selectedConference ? mainItem.selectedConference.core.participants : []
model: mainItem.selectedConference ? mainItem.selectedConference.core?.participants : []
clip: true
delegate: RowLayout {
height: 56 * DefaultStyle.dp
@ -828,7 +827,7 @@ AbstractMainPage {
}
Text {
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
font {
pixelSize: 12 * DefaultStyle.dp
@ -841,7 +840,7 @@ AbstractMainPage {
}
Button {
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
text: qsTr("Rejoindre la réunion")
topPadding: 11 * DefaultStyle.dp

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