This commit is contained in:
Gaëlle Braud 2025-07-14 18:11:06 +00:00
parent 86bbb41623
commit dce74eb958
16 changed files with 107 additions and 33 deletions

View file

@ -23,6 +23,8 @@
#include "core/chat/message/content/ChatMessageContentGui.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/setting/SettingsCore.hpp"
#include "model/core/CoreModel.hpp"
#include "model/friend/FriendModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
@ -38,7 +40,7 @@ QSharedPointer<ChatCore> ChatCore::create(const std::shared_ptr<linphone::ChatRo
}
ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObject(nullptr) {
lDebug() << "[ChatCore] new" << this;
// lDebug() << "[ChatCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime());
@ -373,6 +375,15 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection->invokeToModel(
[this, index]() { mChatModel->toggleParticipantAdminStatusAtIndex(index); });
});
mCoreModelConnection = SafeConnection<ChatCore, CoreModel>::create(me, CoreModel::getInstance());
if (!ToolModel::findFriendByAddress(mPeerAddress))
mCoreModelConnection->makeConnectToModel(&CoreModel::friendCreated,
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f); });
mCoreModelConnection->makeConnectToModel(&CoreModel::friendUpdated,
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f); });
mCoreModelConnection->makeConnectToModel(&CoreModel::friendRemoved,
[this](std::shared_ptr<linphone::Friend> f) { updateInfo(f, true); });
}
QDateTime ChatCore::getLastUpdatedTime() const {
@ -644,3 +655,47 @@ ChatCore::buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom)
QList<QSharedPointer<ParticipantCore>> ChatCore::getParticipants() const {
return mParticipants;
}
void ChatCore::updateInfo(const std::shared_ptr<linphone::Friend> &updatedFriend, bool isRemoval) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto fAddress = ToolModel::interpretUrl(mPeerAddress);
bool isThisFriend = mFriendModel && updatedFriend == mFriendModel->getFriend();
if (!isThisFriend)
for (auto f : updatedFriend->getAddresses()) {
if (f->weakEqual(fAddress)) {
isThisFriend = true;
break;
}
}
if (isThisFriend) {
if (isRemoval) {
mFriendModel = nullptr;
mFriendModelConnection = nullptr;
}
int capabilities = mChatModel->getCapabilities();
auto chatroom = mChatModel->getMonitor();
auto chatRoomAddress = chatroom->getPeerAddress()->clone();
if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
mTitle = ToolModel::getDisplayName(chatRoomAddress);
mAvatarUri = ToolModel::getDisplayName(chatRoomAddress);
emit titleChanged(mTitle);
emit avatarUriChanged();
} else {
if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
auto participants = chatroom->getParticipants();
if (participants.size() > 0) {
auto peer = participants.front();
if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone());
mAvatarUri = ToolModel::getDisplayName(peer->getAddress()->clone());
if (participants.size() == 1) {
auto peerAddress = peer->getAddress();
if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
}
}
} else if (mChatModel->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
mTitle = Utils::coreStringToAppString(chatroom->getSubject());
mAvatarUri = Utils::coreStringToAppString(chatroom->getSubject());
}
}
}
}

View file

@ -33,6 +33,7 @@
#include <linphone++/linphone.hh>
class EventLogCore;
class FriendModel;
class ChatCore : public QObject, public AbstractObject {
Q_OBJECT
@ -140,6 +141,8 @@ public:
QVariantList getParticipantsGui() const;
QStringList getParticipantsAddresses() const;
void updateInfo(const std::shared_ptr<linphone::Friend> &updatedFriend, bool isRemoval = false);
signals:
// used to close all the notifications when one is clicked
void messageOpen();
@ -208,7 +211,10 @@ private:
std::shared_ptr<ChatModel> mChatModel;
QSharedPointer<ChatMessageCore> mLastMessage;
QList<QSharedPointer<EventLogCore>> mEventLogList;
std::shared_ptr<FriendModel> mFriendModel;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> mChatModelConnection;
QSharedPointer<SafeConnection<ChatCore, CoreModel>> mCoreModelConnection;
QSharedPointer<SafeConnection<ChatCore, FriendModel>> mFriendModelConnection;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -212,8 +212,6 @@ bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap d
void Notifier::showNotification(QObject *notification, int timeout) {
// Display notification.
QMetaObject::invokeMethod(notification, NotificationShowMethodName, Qt::DirectConnection);
QTimer *timer = new QTimer(notification);
timer->setInterval(timeout);
timer->setSingleShot(true);

View file

@ -32,7 +32,7 @@ DEFINE_ABSTRACT_OBJECT(ChatModel)
ChatModel::ChatModel(const std::shared_ptr<linphone::ChatRoom> &chatroom, QObject *parent)
: ::Listener<linphone::ChatRoom, linphone::ChatRoomListener>(chatroom, parent) {
lDebug() << "[ChatModel] new" << this << " / SDKModel=" << chatroom.get();
// lDebug() << "[ChatModel] new" << this << " / SDKModel=" << chatroom.get();
mustBeInLinphoneThread(getClassName());
auto coreModel = CoreModel::getInstance();
if (coreModel)
@ -88,6 +88,14 @@ QString ChatModel::getPeerAddress() const {
return Utils::coreStringToAppString(mMonitor->getPeerAddress()->asStringUriOnly());
}
int ChatModel::getCapabilities() const {
return mMonitor->getCapabilities();
}
bool ChatModel::hasCapability(int capability) const {
return mMonitor->hasCapability(capability);
}
std::shared_ptr<linphone::ChatMessage> ChatModel::getLastChatMessage() {
return mMonitor->getLastMessageInHistory();
}

View file

@ -42,6 +42,8 @@ public:
QString getTitle();
QString getPeerAddress() const;
std::shared_ptr<linphone::ChatMessage> getLastChatMessage();
int getCapabilities() const;
bool hasCapability(int capability) const;
int getUnreadMessagesCount() const;
void markAsRead();
std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const;

View file

@ -254,7 +254,7 @@ VariantObject *Utils::haveAccount() {
void Utils::smartShowWindow(QQuickWindow *window) {
if (!window) return;
if (window->visibility() == QWindow::Maximized) // Avoid to change visibility mode
window->showNormal();
window->showMaximized();
else window->show();
App::getInstance()->setLastActiveWindow(window);
window->raise(); // Raise ensure to get focus on Mac

View file

@ -44,7 +44,7 @@ Rectangle {
Text {
font: Typography.p3
color: DefaultStyle.main2_500main
text: mainItem.friendCore.presenceNote
text: mainItem.friendCore?.presenceNote || ""
wrapMode: Text.Wrap
Layout.fillWidth: true
}

View file

@ -175,7 +175,7 @@ ListView {
id: historyAvatar
property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.peerAddress)
contact: contactObj?.value || null
displayNameVal: contact ? "" : modelData.core.avatarUri
displayNameVal: contact ? undefined : modelData.core.avatarUri
secured: modelData.core.isEncrypted
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)

View file

@ -148,7 +148,7 @@ Control.Control {
bottomPadding: Math.round(19 * DefaultStyle.dp)
leftPadding: Math.round(18 * DefaultStyle.dp)
rightPadding: Math.round(18 * DefaultStyle.dp)
width: Math.min(implicitWidth, mainItem.maxWidth - avatar.implicitWidth)
Layout.preferredWidth: Math.min(implicitWidth, mainItem.maxWidth - avatar.implicitWidth)
background: Rectangle {
anchors.fill: parent
color: DefaultStyle.grey_200

View file

@ -63,6 +63,7 @@ ListView {
onCountChanged: if (atYEnd) {
positionViewAtEnd()
}
onChatChanged: lastItemVisible = false
Button {
visible: !mainItem.lastItemVisible
@ -95,6 +96,10 @@ ListView {
if (!mainItem.visible) return
mainItem.positionViewAtIndex(index, ListView.End)
}
onModelReset: Qt.callLater(function() {
var index = eventLogProxy.findFirstUnreadIndex()
positionViewAtIndex(index, ListView.End)
})
}
header: Item {
@ -262,7 +267,7 @@ ListView {
footerPositioning: ListView.OverlayFooter
footer: Control.Control {
visible: composeLayout.composingName !== ""
visible: composeLayout.composingName !== "" && composeLayout.composingName !== undefined
width: mainItem.width
z: mainItem.z + 2
topPadding: Math.round(5 * DefaultStyle.dp)
@ -273,11 +278,11 @@ ListView {
}
contentItem: RowLayout {
id: composeLayout
property string composingName: mainItem.chat.core.composingName
property var composingName: mainItem.chat?.core.composingName
Avatar {
Layout.preferredWidth: Math.round(20 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(20 * DefaultStyle.dp)
_address: mainItem.chat.core.composingAddress
_address: mainItem.chat?.core.composingAddress
}
Text {
Layout.fillWidth: true

View file

@ -17,7 +17,7 @@ Loader{
property CallGui call: null
property bool isConference: false
property bool shadowEnabled: true
property string _address: account
property var _address: account
? account.core?.identityAddress || ""
: call
? call.core.remoteAddress
@ -26,7 +26,7 @@ Loader{
: ''
readonly property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(_address) : _address
property var displayNameObj: UtilsCpp.getDisplayName(_address)
property string displayNameVal: account && account.core.displayName
property var displayNameVal: account && account.core.displayName
? account.core.displayName
: contact && contact.core.fullName
? contact.core.fullName
@ -152,7 +152,7 @@ Loader{
}
EffectImage {
id: initialImg
visible: initialItem.initials == ''
visible: initialItem.initials === ""
width: stackView.width/2
height: width
colorizationColor: DefaultStyle.main2_600

View file

@ -37,10 +37,10 @@ Window {
// ---------------------------------------------------------------------------
objectName: '__internalWindow'
property bool isFrameLess : false;
property bool showAsTool : false
// Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool ensure to have the Window on Top and fullscreen independant
flags: Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint;
// flags: Qt.WindowDoesNotAcceptFocus | Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint;
flags: Qt.WindowDoesNotAcceptFocus | Qt.FramelessWindowHint
opacity: 1.0
height: _content[0] != null ? _content[0].height : 0
width: _content[0] != null ? _content[0].width : 0
@ -48,6 +48,7 @@ Window {
Item {
id: content
anchors.fill:parent
focus: false
property var $parent: mainItem
}

View file

@ -81,7 +81,7 @@ RowLayout {
Avatar {
property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat?.core.peerAddress) : null
contact: contactObj?.value || null
displayNameVal: contact ? "" : mainItem.chat.core.avatarUri
displayNameVal: contact ? "" : mainItem.chat?.core.avatarUri
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
}
@ -97,7 +97,7 @@ RowLayout {
}
}
EffectImage {
visible: mainItem.chat?.core.muted
visible: mainItem.chat?.core.muted || false
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 20 * DefaultStyle.dp
@ -122,7 +122,7 @@ RowLayout {
RoundButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.videoCamera
visible: !mainItem.chat.core.isGroupChat
visible: !mainItem.chat?.core.isGroupChat || false
onPressed: mainItem.oneOneCall(true)
}
RoundButton {
@ -443,8 +443,8 @@ RowLayout {
}
ChatDroppableTextArea {
id: messageSender
Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : height
Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
Control.SplitView.preferredHeight: mainItem.chat?.core.isReadOnly ? 0 : height
Control.SplitView.minimumHeight: mainItem.chat?.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
chat: mainItem.chat
onChatChanged: {
if (chat) messageSender.text = mainItem.chat.core.sendingText
@ -513,7 +513,7 @@ RowLayout {
? forwardToListsComponent
: panelType === SelectedChatView.PanelType.ManageParticipants
? manageParticipantsComponent
: mainItem.chat.core.isGroupChat
: mainItem.chat?.core.isGroupChat
? groupInfoComponent
: oneToOneInfoComponent
active: detailsPanel.visible

View file

@ -192,6 +192,7 @@ FocusScope {
id: rightPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
visible: false
}
}
}

View file

@ -25,7 +25,7 @@ AbstractMainPage {
property bool isRegistered: account ? account.core?.registrationState
== LinphoneEnums.RegistrationState.Ok : false
property var selectedChatGui
property var selectedChatGui: null
property string remoteAddress
onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress)
property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress)
@ -40,17 +40,14 @@ AbstractMainPage {
listStackView.popToIndex(0)
if (listStackView.depth === 0 || listStackView.currentItem.objectName !== "chatListItem") listStackView.push(chatListItem)
}
rightPanelStackView.replace(currentChatComp,
Control.StackView.Immediate)
}
else {
rightPanelStackView.replace(emptySelection,
Control.StackView.Immediate)
}
}
rightPanelStackView.initialItem: emptySelection
rightPanelStackView.visible: listStackView.currentItem && listStackView.currentItem.objectName === "chatListItem"
rightPanelStackView.initialItem: currentChatComp
// Only play on the visible property of the right panel as there is only one item pushed
// and the sending TextArea must be instantiated only once, otherwise it looses focus
// when the chat list order is updated
rightPanelStackView.visible: false//listStackView.currentItem && listStackView.currentItem.objectName === "chatListItem" && selectedChatGui !== null
onNoItemButtonPressed: goToNewChat()
@ -334,10 +331,11 @@ AbstractMainPage {
id: currentChatComp
FocusScope {
SelectedChatView {
visible: chat != undefined && chat != null
anchors.fill: parent
chat: mainItem.selectedChatGui || null
onChatChanged: if (mainItem.selectedChatGui !== chat) mainItem.selectedChatGui = chat
}
}
}
}
}

View file

@ -28,7 +28,7 @@ AbstractWindow {
property var accountProxy
// TODO : use this to make the border transparent
// flags: Qt.Window | Qt.FramelessWindowHint | Qt.WindowTitleHint
flags: Qt.Window | Qt.WindowTitleHint
// menuBar: Rectangle {
// width: parent.width
// height: Math.round(40 * DefaultStyle.dp)