rename fr translation

set enableVideo to false if audio call #LINQT-2086

try to fix crash on macos when switching from call window to main window (#LINQT-2077 and #LINQT-2087)

set error message when not able to download attached file

fix remove chat from list
fix new chat connection after first message sent #LINQT-2090
clean code + invalidate() for Qt6.10 compatibility

fix translations
This commit is contained in:
Gaelle Braud 2025-10-27 14:09:34 +01:00
parent d4ce80f8c6
commit 2d9f568e3d
28 changed files with 1091 additions and 968 deletions

View file

@ -207,7 +207,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
&ChatModel::newEvent, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle();
lDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle();
auto event = EventLogCore::create(eventLog);
if (event->isHandled()) {
mChatModelConnection->invokeToCore([this, event]() { emit eventsInserted({event}); });
@ -220,7 +220,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
&ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) {
if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
lDebug() << "CHAT MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
QList<QSharedPointer<EventLogCore>> list;
for (auto &e : eventsLog) {
auto event = EventLogCore::create(e);
@ -491,7 +491,7 @@ ChatMessageGui *ChatCore::getLastMessage() const {
void ChatCore::setLastMessage(QSharedPointer<ChatMessageCore> lastMessage) {
if (mLastMessage != lastMessage) {
disconnect(mLastMessage.get());
if (mLastMessage) disconnect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, nullptr);
mLastMessage = lastMessage;
connect(mLastMessage.get(), &ChatMessageCore::messageStateChanged, this, &ChatCore::lastMessageChanged);
emit lastMessageChanged();
@ -544,10 +544,6 @@ std::shared_ptr<ChatModel> ChatCore::getModel() const {
return mChatModel;
}
QSharedPointer<SafeConnection<ChatCore, ChatModel>> ChatCore::getChatModelConnection() const {
return mChatModelConnection;
}
bool ChatCore::isMuted() const {
return mIsMuted;
}

View file

@ -142,7 +142,6 @@ public:
void setComposingAddress(QString composingAddress);
std::shared_ptr<ChatModel> getModel() const;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> getChatModelConnection() const;
void setParticipants(QList<QSharedPointer<ParticipantCore>> participants);
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;

View file

@ -55,11 +55,11 @@ ChatList::~ChatList() {
void ChatList::connectItem(QSharedPointer<ChatCore> chat) {
connect(chat.get(), &ChatCore::deleted, this, [this, chat] {
disconnect(chat.get());
disconnect(chat.get(), &ChatCore::unreadMessagesCountChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastUpdatedTimeChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
disconnect(chat.get(), &ChatCore::deleted, this, nullptr);
remove(chat);
// We cannot use countChanged here because it is called before mList
// really has removed the item, then emit specific signal
emit chatRemoved(chat ? new ChatGui(chat) : nullptr);
});
auto dataChange = [this, chat] {
int i = -1;
@ -94,7 +94,6 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
beginResetModel();
mList.clear();
// Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
@ -118,6 +117,7 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
disconnect(chat.get(), &ChatCore::lastMessageChanged, this, nullptr);
}
}
mList.clear();
for (auto &chat : *chats) {
connectItem(chat);
}
@ -143,18 +143,7 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
return;
}
auto chatCore = ChatCore::create(room);
mModelConnection->invokeToCore([this, chatCore] {
auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer<ChatCore> item) {
return item && chatCore && item->getModel() && chatCore->getModel() &&
item->getModel()->getMonitor() == chatCore->getModel()->getMonitor();
});
if (it == chatList.end()) {
connectItem(chatCore);
add(chatCore);
emit chatAdded();
}
});
addChatInList(chatCore);
};
mModelConnection->makeConnectToModel(&CoreModel::messageReceived,
[this, addChatToList](const std::shared_ptr<linphone::Core> &core,

View file

@ -48,7 +48,6 @@ public:
signals:
void lUpdate();
void filterChanged(QString filter);
void chatRemoved(ChatGui *chat);
void chatAdded();
void chatUpdated();

View file

@ -37,13 +37,14 @@ ChatProxy::~ChatProxy() {
void ChatProxy::setSourceModel(QAbstractItemModel *model) {
auto oldChatList = getListModel<ChatList>();
if (oldChatList) {
disconnect(oldChatList);
disconnect(this, &ChatProxy::filterTextChanged, oldChatList, nullptr);
disconnect(oldChatList, &ChatList::chatAdded, this, nullptr);
disconnect(oldChatList, &ChatList::dataChanged, this, nullptr);
}
auto newChatList = dynamic_cast<ChatList *>(model);
if (newChatList) {
connect(this, &ChatProxy::filterTextChanged, newChatList,
[this, newChatList] { emit newChatList->filterChanged(getFilterText()); });
connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved);
connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); });
connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); });
}

View file

@ -42,9 +42,6 @@ public:
Q_INVOKABLE int findChatIndex(ChatGui *chatGui);
Q_INVOKABLE void addChatInList(ChatGui *chatGui);
signals:
void chatRemoved(ChatGui *chat);
protected:
QSharedPointer<ChatList> mList;
DECLARE_ABSTRACT_OBJECT

View file

@ -64,7 +64,6 @@ void EventLogList::disconnectItem(const QSharedPointer<EventLogCore> &item) {
if (message) {
disconnect(message.get(), &ChatMessageCore::isReadChanged, this, nullptr);
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
disconnect(message.get(), &ChatMessageCore::ephemeralDurationChanged, this, nullptr);
}
}
@ -277,6 +276,9 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
}
setIsUpdating(true);
beginResetModel();
for (auto &event : getSharedList<EventLogCore>()) {
disconnectItem(event);
}
mList.clear();
if (!mChatCore) {
endResetModel();
@ -298,13 +300,6 @@ void EventLogList::setSelf(QSharedPointer<EventLogList> me) {
events->push_back(model);
}
mCoreModelConnection->invokeToCore([this, events] {
for (auto &event : getSharedList<EventLogCore>()) {
auto message = event->getChatMessageCore();
if (message) {
disconnect(message.get(), &ChatMessageCore::ephemeralDurationChanged, this, nullptr);
disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr);
}
}
for (auto &event : *events) {
connectItem(event);
mList.append(event);

View file

@ -79,7 +79,6 @@ signals:
private:
QString mFilter;
QSharedPointer<ChatCore> mChatCore;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> mChatModelConnection;
QSharedPointer<SafeConnection<EventLogList, CoreModel>> mCoreModelConnection;
int mDisplayItemsStep = 0;
int mItemsToLoadBeforeSearchResult = 3;

View file

@ -37,7 +37,11 @@ EventLogProxy::~EventLogProxy() {
void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
auto oldEventLogList = getListModel<EventLogList>();
if (oldEventLogList) {
disconnect(oldEventLogList);
disconnect(oldEventLogList, &EventLogList::listAboutToBeReset, this, nullptr);
disconnect(oldEventLogList, &EventLogList::chatGuiChanged, this, nullptr);
disconnect(oldEventLogList, &EventLogList::displayItemsStepChanged, this, nullptr);
disconnect(oldEventLogList, &EventLogList::eventInserted, this, nullptr);
disconnect(oldEventLogList, &EventLogList::messageWithFilterFound, this, nullptr);
}
auto newEventLogList = dynamic_cast<EventLogList *>(model);
if (newEventLogList) {

View file

@ -96,7 +96,18 @@ void ChatMessageContentCore::setSelf(QSharedPointer<ChatMessageContentCore> me)
});
mChatMessageContentModelConnection->makeConnectToCore(&ChatMessageContentCore::lDownloadFile, [this]() {
mChatMessageContentModelConnection->invokeToModel([this] { mChatMessageContentModel->downloadFile(mName); });
mChatMessageContentModelConnection->invokeToModel([this] {
QString error;
bool downloaded = mChatMessageContentModel->downloadFile(mName, &error);
if (!downloaded) {
mChatMessageContentModelConnection->invokeToCore([this, &error] {
QString message = error;
//: Error downloading file %1
if (error.isEmpty()) error = tr("download_file_default_error").arg(mName);
Utils::showInformationPopup(tr("info_popup_error_titile"), message, false);
});
}
});
});
mChatMessageContentModelConnection->makeConnectToModel(
&ChatMessageContentModel::wasDownloadedChanged,

View file

@ -197,7 +197,8 @@ void ChatMessageContentList::setSelf(QSharedPointer<ChatMessageContentList> me)
mModelConnection->makeConnectToCore(&ChatMessageContentList::lUpdate, [this]() {
for (auto &content : getSharedList<ChatMessageContentCore>()) {
if (content) disconnect(content.get());
if (content) disconnect(content.get(), &ChatMessageContentCore::wasDownloadedChanged, this, nullptr);
if (content) disconnect(content.get(), &ChatMessageContentCore::thumbnailChanged, this, nullptr);
}
if (!mChatMessageCore) return;
auto contents = mChatMessageCore->getChatMessageContentList();

View file

@ -109,7 +109,7 @@ void ParticipantProxy::setShowMe(const bool &show) {
if (list->mShowMe != show) {
list->mShowMe = show;
emit showMeChanged();
invalidateFilter();
invalidate();
}
}

View file

@ -43,7 +43,7 @@ void LimitProxy::setSourceModels(SortFilterProxy *firstList) {
if (secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::onAdded);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::onRemoved);
connect(secondList, &QAbstractItemModel::modelReset, this, &LimitProxy::invalidateRowsFilter);
connect(secondList, &QAbstractItemModel::modelReset, this, &LimitProxy::invalidate);
}
connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged);
connect(firstList, &SortFilterProxy::filterTypeChanged, this, &LimitProxy::filterTypeChanged);
@ -60,8 +60,8 @@ void LimitProxy::setSourceModels(SortFilterProxy *firstList) {
/*
void LimitProxy::setSourceModels(SortFilterProxy *firstList, QAbstractItemModel *secondList) {
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidateFilter);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidateFilter);
connect(secondList, &QAbstractItemModel::rowsInserted, this, &LimitProxy::invalidate);
connect(secondList, &QAbstractItemModel::rowsRemoved, this, &LimitProxy::invalidate);
connect(firstList, &SortFilterProxy::filterTextChanged, this, &LimitProxy::filterTextChanged);
setSourceModel(firstList);
}*/
@ -119,7 +119,7 @@ void LimitProxy::setMaxDisplayItems(int maxItems) {
emit maxDisplayItemsChanged();
if (model && getDisplayCount(modelCount) != oldCount) {
invalidateFilter();
invalidate();
}
}
}
@ -178,6 +178,6 @@ void LimitProxy::onAdded() {
void LimitProxy::onRemoved() {
int count = sourceModel()->rowCount();
if (mMaxDisplayItems > 0 && mMaxDisplayItems <= count) {
invalidateFilter();
invalidate();
}
}

View file

@ -90,5 +90,6 @@ void SortFilterProxy::remove(int index, int count) {
}
void SortFilterProxy::invalidateFilter() {
QSortFilterProxyModel::invalidateFilter();
// TODO : replace by begin/endFilterChanged when ci uses Qt6.10
invalidate();
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -74,8 +74,12 @@ void ChatMessageContentModel::removeDownloadedFile(QString filePath) {
}
}
void ChatMessageContentModel::downloadFile(const QString &name) {
if (!mChatMessageModel) return;
bool ChatMessageContentModel::downloadFile(const QString &name, QString *error) {
if (!mChatMessageModel) {
//: Internal error : message object does not exist anymore !
if (error) *error = tr("download_error_object_doesnt_exist");
return false;
}
switch (mChatMessageModel->getState()) {
case linphone::ChatMessage::State::Delivered:
case linphone::ChatMessage::State::DeliveredToUser:
@ -83,10 +87,13 @@ void ChatMessageContentModel::downloadFile(const QString &name) {
case linphone::ChatMessage::State::FileTransferDone:
break;
case linphone::ChatMessage::State::FileTransferInProgress:
return;
return true;
default:
lWarning() << QStringLiteral("Wrong message state when requesting downloading, state=.")
<< LinphoneEnums::fromLinphone(mChatMessageModel->getState());
auto state = LinphoneEnums::fromLinphone(mChatMessageModel->getState());
lWarning() << QStringLiteral("Wrong message state when requesting downloading, state=") << state;
//: Wrong message state when requesting downloading, state = %1
if (error) *error = tr("download_file_error_wrong_state").arg(LinphoneEnums::toString(state));
return false;
}
bool soFarSoGood;
const QString safeFilePath = Utils::getSafeFilePath(
@ -94,21 +101,26 @@ void ChatMessageContentModel::downloadFile(const QString &name) {
if (!soFarSoGood) {
lWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(name);
return;
//: Unable to create safe file path for: %1
if (error) *error = tr("download_file_error_no_safe_file_path").arg(name);
return false;
}
mContent->setFilePath(Utils::appStringToCoreString(safeFilePath));
if (!mContent->isFileTransfer()) {
lWarning() << QStringLiteral("file transfer is not available");
Utils::showInformationPopup(
//: Error
tr("popup_error_title"),
//: This file was already downloaded and is no more on the server. Your peer have to resend it if you want
//: to get it
tr("popup_download_error_message"), false);
//: This file was already downloaded and is no more on the server. Your peer have to resend it if you want
//: to get it
if (error) *error = tr("download_file_error_file_transfer_unavailable");
return false;
} else {
if (!mChatMessageModel->getMonitor()->downloadContent(mContent))
auto downloaded = mChatMessageModel->getMonitor()->downloadContent(mContent);
if (!downloaded && error) {
lWarning() << QStringLiteral("Unable to download file of entry %1.").arg(name);
//: Unable to download file of entry %1
*error = tr("download_file_error_unable_to_download").arg(name);
}
return downloaded;
}
}

View file

@ -40,17 +40,21 @@ public:
std::shared_ptr<ChatMessageModel> chatMessageModel);
~ChatMessageContentModel();
QString getThumbnail() const;
void setThumbnail(const QString &data);
void setWasDownloaded(bool wasDownloaded);
void createThumbnail();
void removeDownloadedFile(QString filePath);
void downloadFile(const QString &name);
/**
* Returns true if download succeed, false otherwise
*/
bool downloadFile(const QString &name, QString *error = nullptr);
void cancelDownloadFile();
void openFile(const QString &name, bool wasDownloaded, bool showDirectory = false);
/**
* Returns true if file saved successfully, false otherwise
*/
bool saveAs(const QString &path);
const std::shared_ptr<linphone::Content> &getContent() const;

View file

@ -120,6 +120,9 @@ LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::Chat
QString LinphoneEnums::toString(const LinphoneEnums::ChatMessageState &data) {
switch (data) {
case LinphoneEnums::ChatMessageState::StateIdle:
//: "idle"
return QObject::tr("message_state_idle");
case LinphoneEnums::ChatMessageState::StateInProgress:
//: "delivery in progress"
return QObject::tr("message_state_in_progress");

View file

@ -269,11 +269,14 @@ VariantObject *Utils::haveAccount() {
void Utils::smartShowWindow(QQuickWindow *window) {
if (!window) return;
if (window->visibility() == QWindow::Maximized) // Avoid to change visibility mode
window->showMaximized();
else window->show();
// if (window->visibility() == QWindow::Maximized) // Avoid to change visibility mode
// window->showMaximized();
lInfo() << "[Utils] : show window" << window;
window->show();
App::getInstance()->setLastActiveWindow(window);
lInfo() << "[Utils] : raise window" << window;
window->raise(); // Raise ensure to get focus on Mac
lInfo() << "[Utils] : request activate";
window->requestActivate();
}

View file

@ -52,10 +52,10 @@ ListView {
onModelAboutToBeReset: {
loading = true
}
onChatRemoved: {
var currentChat = model.getAt(currentIndex)
onRowsRemoved: {
var index = mainItem.currentIndex
mainItem.currentIndex = -1
selectChat(currentChat)
mainItem.currentIndex = index
}
onLayoutChanged: {
selectChat(mainItem.currentChatGui)

View file

@ -21,6 +21,8 @@ Image {
id: mainItem
property ChatMessageContentGui contentGui
RectangleTest{anchors.fill: parent}
mipmap: false//SettingsModel.mipmapEnabled
autoTransform: true
fillMode: Image.PreserveAspectFit

View file

@ -317,9 +317,7 @@ ListView {
visible: itemDelegate.haveModel
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
console.log("clicked", mouse.button)
if (mouse.button === Qt.RightButton) {
console.log("open popup")
deletePopup.x = mouse.x
deletePopup.y = mouse.y
deletePopup.open()

View file

@ -100,7 +100,6 @@ FocusScope {
font {
pixelSize: Typography.h4.pixelSize
weight: Utils.getSizeWithScreenRatio(400)
capitalization: Font.Capitalize
}
}
RowLayout {
@ -310,7 +309,7 @@ FocusScope {
Control.Control {
id: participantListPopup
width: parent.width
height: Math.min(participantInfoList.height, Utils.getSizeWithScreenRatio(200))
height: visible ? Math.min(participantInfoList.height, Utils.getSizeWithScreenRatio(200)) : 0
visible: false
anchors.bottom: chatMessagesListView.bottom
anchors.left: chatMessagesListView.left

View file

@ -32,7 +32,7 @@ ColumnLayout{
property var popupId
Component{
id: contactDelegate
Contact{
Contact {
id: contactItem
Layout.preferredWidth: mainItem.childrenWidth
account: modelData

View file

@ -1215,11 +1215,11 @@ AbstractWindow {
}
}
onJoinConfRequested: uri => {
mainWindow.joinConference(uri, {
"microEnabled": microEnabled,
"localVideoEnabled": localVideoEnabled
})
}
mainWindow.joinConference(uri, {
"microEnabled": microEnabled,
"localVideoEnabled": localVideoEnabled
})
}
onCancelJoiningRequested: mainWindow.cancelJoinConference()
onCancelAfterJoinRequested: mainWindow.cancelAfterJoin()
}