hide empty chat rooms flag default value to true #LINQT-1893

fix crash

call history/chats/chat messages lists loading ui
This commit is contained in:
Gaelle Braud 2025-08-18 09:55:21 +02:00
parent c9e61d1e22
commit 93418cb7c9
20 changed files with 63 additions and 26 deletions

View file

@ -57,10 +57,12 @@ void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> me) {
mModelConnection = SafeConnection<CallHistoryList, CoreModel>::create(me, CoreModel::getInstance()); mModelConnection = SafeConnection<CallHistoryList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&CallHistoryList::lUpdate, [this]() { mModelConnection->makeConnectToCore(&CallHistoryList::lUpdate, [this]() {
clearData();
emit listAboutToBeReset();
mModelConnection->invokeToModel([this]() { mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
// Avoid copy to lambdas // Avoid copy to lambdas
QList<QSharedPointer<CallHistoryCore>> *callLogs = new QList<QSharedPointer<CallHistoryCore>>(); QList<QSharedPointer<CallHistoryCore>> *callLogs = new QList<QSharedPointer<CallHistoryCore>>();
mustBeInLinphoneThread(getClassName());
std::list<std::shared_ptr<linphone::CallLog>> linphoneCallLogs; std::list<std::shared_ptr<linphone::CallLog>> linphoneCallLogs;
if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) { if (auto account = CoreModel::getInstance()->getCore()->getDefaultAccount()) {
linphoneCallLogs = account->getCallLogs(); linphoneCallLogs = account->getCallLogs();

View file

@ -62,6 +62,7 @@ signals:
void lUpdate(); void lUpdate();
void lRemoveEntriesForAddress(QString address); void lRemoveEntriesForAddress(QString address);
void lRemoveAllEntries(); void lRemoveAllEntries();
void listAboutToBeReset();
private: private:
// Check the state from CallHistoryCore: sender() must be a CallHistoryCore. // Check the state from CallHistoryCore: sender() must be a CallHistoryCore.

View file

@ -27,6 +27,7 @@ DEFINE_ABSTRACT_OBJECT(CallHistoryProxy)
CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) { CallHistoryProxy::CallHistoryProxy(QObject *parent) : LimitProxy(parent) {
mHistoryList = CallHistoryList::create(); mHistoryList = CallHistoryList::create();
connect(mHistoryList.get(), &CallHistoryList::listAboutToBeReset, this, &CallHistoryProxy::listAboutToBeReset);
setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder)); setSourceModels(new SortFilterList(mHistoryList.get(), Qt::DescendingOrder));
connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); }); connect(App::getInstance(), &App::currentDateChanged, this, [this] { emit mHistoryList->lUpdate(); });
} }
@ -56,7 +57,7 @@ bool CallHistoryProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QMo
QRegularExpression::CaseInsensitiveOption | QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption); QRegularExpression::UseUnicodePropertiesOption);
auto callLog = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRow); auto callLog = getItemAtSource<CallHistoryList, CallHistoryCore>(sourceRow);
show = callLog->mDisplayName.contains(search) || callLog->mRemoteAddress.contains(search); show = callLog && (callLog->mDisplayName.contains(search) || callLog->mRemoteAddress.contains(search));
} }
return show; return show;
} }

View file

@ -41,6 +41,9 @@ public:
Q_INVOKABLE void removeEntriesWithFilter(QString filter); Q_INVOKABLE void removeEntriesWithFilter(QString filter);
Q_INVOKABLE void reload(); Q_INVOKABLE void reload();
signals:
void listAboutToBeReset();
protected: protected:
QSharedPointer<CallHistoryList> mHistoryList; QSharedPointer<CallHistoryList> mHistoryList;

View file

@ -81,6 +81,8 @@ void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
void ChatList::setSelf(QSharedPointer<ChatList> me) { void ChatList::setSelf(QSharedPointer<ChatList> me) {
mModelConnection = SafeConnection<ChatList, CoreModel>::create(me, CoreModel::getInstance()); mModelConnection = SafeConnection<ChatList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&ChatList::lUpdate, [this]() { mModelConnection->makeConnectToCore(&ChatList::lUpdate, [this]() {
clearData();
emit listAboutToBeReset();
mModelConnection->invokeToModel([this]() { mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
// Avoid copy to lambdas // Avoid copy to lambdas
@ -126,13 +128,13 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
qWarning() << log().arg("Receiver account has no address, return"); qWarning() << log().arg("Receiver account has no address, return");
return; return;
} }
auto senderAddress = message->getFromAddress();
auto defaultAddress = core->getDefaultAccount()->getContactAddress(); auto defaultAddress = core->getDefaultAccount()->getContactAddress();
if (!defaultAddress->weakEqual(receiverAddress)) { if (defaultAddress && !defaultAddress->weakEqual(receiverAddress)) {
qDebug() << log().arg("Receiver account is not the default one, do not add chat to list"); qDebug() << log().arg("Receiver account is not the default one, do not add chat to list");
return; return;
} }
if (defaultAddress->weakEqual(senderAddress)) { auto senderAddress = message->getFromAddress();
if (defaultAddress && defaultAddress->weakEqual(senderAddress)) {
qDebug() << log().arg("Sender account is the default one, do not add chat to list"); qDebug() << log().arg("Sender account is the default one, do not add chat to list");
return; return;
} }

View file

@ -51,6 +51,7 @@ signals:
void chatRemoved(ChatGui *chat); void chatRemoved(ChatGui *chat);
void chatAdded(); void chatAdded();
void chatUpdated(); void chatUpdated();
void listAboutToBeReset();
private: private:
QString mFilter; QString mFilter;

View file

@ -44,6 +44,7 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) {
connect(this, &ChatProxy::filterTextChanged, newChatList, connect(this, &ChatProxy::filterTextChanged, newChatList,
[this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); [this, newChatList] { emit newChatList->filterChanged(getFilterText()); });
connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved); connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved);
connect(newChatList, &ChatList::listAboutToBeReset, this, &ChatProxy::listAboutToBeReset);
connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); });
connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); }); connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); });
} }

View file

@ -44,6 +44,7 @@ public:
signals: signals:
void chatRemoved(ChatGui *chat); void chatRemoved(ChatGui *chat);
void listAboutToBeReset();
protected: protected:
QSharedPointer<ChatList> mList; QSharedPointer<ChatList> mList;

View file

@ -150,6 +150,8 @@ void EventLogList::findChatMessageWithFilter(QString filter,
void EventLogList::setSelf(QSharedPointer<EventLogList> me) { void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
connect(this, &EventLogList::lUpdate, this, [this]() { connect(this, &EventLogList::lUpdate, this, [this]() {
resetData();
emit listAboutToBeReset();
for (auto &event : getSharedList<EventLogCore>()) { for (auto &event : getSharedList<EventLogCore>()) {
auto message = event->getChatMessageCore(); auto message = event->getChatMessageCore();
if (message) disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr); if (message) disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);

View file

@ -64,6 +64,7 @@ signals:
void eventChanged(); void eventChanged();
void eventInserted(int index, EventLogGui *message); void eventInserted(int index, EventLogGui *message);
void messageWithFilterFound(int index); void messageWithFilterFound(int index);
void listAboutToBeReset();
private: private:
QString mFilter; QString mFilter;

View file

@ -42,6 +42,7 @@ void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
auto newEventLogList = dynamic_cast<EventLogList *>(model); auto newEventLogList = dynamic_cast<EventLogList *>(model);
if (newEventLogList) { if (newEventLogList) {
connect(newEventLogList, &EventLogList::eventChanged, this, &EventLogProxy::eventChanged); connect(newEventLogList, &EventLogList::eventChanged, this, &EventLogProxy::eventChanged);
connect(newEventLogList, &EventLogList::listAboutToBeReset, this, &EventLogProxy::listAboutToBeReset);
connect(newEventLogList, &EventLogList::eventInserted, this, connect(newEventLogList, &EventLogList::eventInserted, this,
[this, newEventLogList](int index, EventLogGui *event) { [this, newEventLogList](int index, EventLogGui *event) {
invalidate(); invalidate();

View file

@ -55,6 +55,7 @@ signals:
void eventChanged(); void eventChanged();
void eventInserted(int index, EventLogGui *message); void eventInserted(int index, EventLogGui *message);
void indexWithFilterFound(int index); void indexWithFilterFound(int index);
void listAboutToBeReset();
protected: protected:
QSharedPointer<EventLogList> mList; QSharedPointer<EventLogList> mList;

View file

@ -26,6 +26,7 @@ aggregate_imdn=1
enable_basic_to_client_group_chat_room_migration=0 enable_basic_to_client_group_chat_room_migration=0
enable_simple_group_chat_message_state=0 enable_simple_group_chat_message_state=0
notify_each_friend_individually_when_presence_received=0 notify_each_friend_individually_when_presence_received=0
hide_empty_chat_rooms=1
[net] [net]
force_ice_disablement=0 force_ice_disablement=0

View file

@ -29,6 +29,7 @@ ListView {
Component.onCompleted: { Component.onCompleted: {
loading = true loading = true
} }
onListAboutToBeReset: loading = true
filterText: mainItem.searchText filterText: mainItem.searchText
onFilterTextChanged: maxDisplayItems = initialDisplayItems onFilterTextChanged: maxDisplayItems = initialDisplayItems
initialDisplayItems: Math.max( initialDisplayItems: Math.max(
@ -43,16 +44,14 @@ ListView {
spacing: Math.round(10 * DefaultStyle.dp) spacing: Math.round(10 * DefaultStyle.dp)
Keys.onPressed: event => { Keys.onPressed: event => {
if (event.key == Qt.Key_Escape) { if (event.key == Qt.Key_Escape) {
console.log("Back") console.log("Back")
searchBar.forceActiveFocus() searchBar.forceActiveFocus()
event.accepted = true event.accepted = true
} }
} }
Component.onCompleted: cacheBuffer = Math.max( Component.onCompleted: cacheBuffer = Math.max(mainItem.height,0) //contentHeight>0 ? contentHeight : 0// cache all items
contentHeight,
0) //contentHeight>0 ? contentHeight : 0// cache all items
// remove binding loop // remove binding loop
onContentHeightChanged: Qt.callLater(function () { onContentHeightChanged: Qt.callLater(function () {
if (mainItem) if (mainItem)

View file

@ -44,6 +44,9 @@ ListView {
onModelReset: { onModelReset: {
mainItem.resultsReceived() mainItem.resultsReceived()
} }
onListAboutToBeReset: {
loading = true
}
onChatRemoved: { onChatRemoved: {
var currentChat = model.getAt(currentIndex) var currentChat = model.getAt(currentIndex)
mainItem.currentIndex = -1 mainItem.currentIndex = -1
@ -58,7 +61,6 @@ ListView {
function selectChat(chatGui) { function selectChat(chatGui) {
var index = chatProxy.findChatIndex(chatGui) var index = chatProxy.findChatIndex(chatGui)
console.log("current index", mainItem.currentIndex, "new", index)
mainItem.currentIndex = index mainItem.currentIndex = index
// if the chat exists, it may not be displayed // if the chat exists, it may not be displayed
// in list if hide_empty_chatrooms is set. Thus, we need // in list if hide_empty_chatrooms is set. Thus, we need

View file

@ -14,6 +14,9 @@ ListView {
property color backgroundColor property color backgroundColor
property bool lastItemVisible: false property bool lastItemVisible: false
property int lastIndexFoundWithFilter: -1 property int lastIndexFoundWithFilter: -1
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
property bool loading: false
verticalLayoutDirection: ListView.BottomToTop verticalLayoutDirection: ListView.BottomToTop
signal showReactionsForMessageRequested(ChatMessageGui chatMessage) signal showReactionsForMessageRequested(ChatMessageGui chatMessage)
signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage)
@ -87,7 +90,10 @@ ListView {
if (!mainItem.visible) return if (!mainItem.visible) return
if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.Beginning) if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.Beginning)
} }
Component.onCompleted: loading = true
onListAboutToBeReset: loading = true
onModelReset: Qt.callLater(function() { onModelReset: Qt.callLater(function() {
loading = false
var index = eventLogProxy.findFirstUnreadIndex() var index = eventLogProxy.findFirstUnreadIndex()
positionViewAtIndex(index, ListView.Beginning) positionViewAtIndex(index, ListView.Beginning)
eventLogProxy.markIndexAsRead(index) eventLogProxy.markIndexAsRead(index)
@ -204,6 +210,16 @@ ListView {
} }
} }
} }
BusyIndicator {
anchors.horizontalCenter: mainItem.horizontalCenter
visible: mainItem.loading
height: visible ? mainItem.busyIndicatorSize : 0
width: mainItem.busyIndicatorSize
indicatorHeight: mainItem.busyIndicatorSize
indicatorWidth: mainItem.busyIndicatorSize
indicatorColor: DefaultStyle.main1_500_main
}
delegate: DelegateChooser { delegate: DelegateChooser {
role: "eventType" role: "eventType"

View file

@ -248,7 +248,6 @@ FocusScope {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Math.round(18 * DefaultStyle.dp) anchors.leftMargin: Math.round(18 * DefaultStyle.dp)
anchors.rightMargin: Math.round(18 * DefaultStyle.dp) anchors.rightMargin: Math.round(18 * DefaultStyle.dp)
onActiveFocusChanged: console.log("chat messages focus", activeFocus)
Control.ScrollBar.vertical: scrollbar Control.ScrollBar.vertical: scrollbar
onShowReactionsForMessageRequested: (chatMessage) => { onShowReactionsForMessageRequested: (chatMessage) => {
mainItem.chatMessage = chatMessage mainItem.chatMessage = chatMessage

View file

@ -84,9 +84,8 @@ Item {
AccountProxy { AccountProxy {
id: accountProxy id: accountProxy
sourceModel: AppCpp.accounts sourceModel: AppCpp.accounts
onDefaultAccountChanged: if (tabbar.currentIndex === 0 onDefaultAccountChanged: if (tabbar.currentIndex === 0 && defaultAccount)
&& defaultAccount) defaultAccount.core?.lResetMissedCalls()
defaultAccount.core?.lResetMissedCalls()
} }
CallProxy { CallProxy {
@ -130,7 +129,6 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
Layout.preferredWidth: Math.round(82 * DefaultStyle.dp) Layout.preferredWidth: Math.round(82 * DefaultStyle.dp)
defaultAccount: accountProxy.defaultAccount defaultAccount: accountProxy.defaultAccount
currentIndex: 0
Binding on currentIndex { Binding on currentIndex {
when: mainItem.contextualMenuOpenedComponent != undefined when: mainItem.contextualMenuOpenedComponent != undefined
value: -1 value: -1

View file

@ -21,8 +21,8 @@ AbstractMainPage {
signal goToCallForwardSettings signal goToCallForwardSettings
onVisibleChanged: if (!visible) { onVisibleChanged: if (!visible) {
goToCallHistory() goToCallHistory()
} }
//Group call properties //Group call properties
property ConferenceInfoGui confInfoGui property ConferenceInfoGui confInfoGui
@ -109,8 +109,8 @@ AbstractMainPage {
initialItem: historyListItem initialItem: historyListItem
focus: true focus: true
onActiveFocusChanged: if (activeFocus) { onActiveFocusChanged: if (activeFocus) {
currentItem.forceActiveFocus() currentItem.forceActiveFocus()
} }
} }
Item { Item {
@ -264,7 +264,7 @@ AbstractMainPage {
background: Item {} background: Item {}
contentItem: ColumnLayout { contentItem: ColumnLayout {
Text { Text {
visible: historyListView.count === 0 visible: historyListView.count === 0 && !historyListView.loading
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(137 * DefaultStyle.dp) Layout.topMargin: Math.round(137 * DefaultStyle.dp)
//: "Aucun résultat" //: "Aucun résultat"
@ -290,6 +290,12 @@ AbstractMainPage {
historyListView.model.reload() historyListView.model.reload()
} }
} }
Binding {
target: mainItem
property: "showDefaultItem"
when: historyListView.loading
value: false
}
onCurrentIndexChanged: { onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt( mainItem.selectedRowHistoryGui = model.getAt(
currentIndex) currentIndex)

View file

@ -188,7 +188,6 @@ AbstractMainPage {
Control.ScrollBar.vertical: scrollbar Control.ScrollBar.vertical: scrollbar
onChatClicked: (chat) => { onChatClicked: (chat) => {
console.log("chat clicked")
mainItem.selectedChatGui = chat mainItem.selectedChatGui = chat
} }