Feature/group chat infos

This commit is contained in:
Christophe Deschamps 2025-06-12 14:19:02 +00:00
parent 4ef65219ad
commit ff78a5abf1
15 changed files with 732 additions and 136 deletions

View file

@ -67,6 +67,7 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
mTitle = Utils::coreStringToAppString(chatRoom->getSubject());
mAvatarUri = Utils::coreStringToAppString(chatRoom->getSubject());
mIsGroupChat = true;
mMeAdmin = chatRoom->getMe() && chatRoom->getMe()->isAdmin();
}
}
mUnreadMessagesCount = chatRoom->getUnreadMessagesCount();
@ -102,6 +103,7 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
connect(this, &ChatCore::eventRemoved, this, &ChatCore::lUpdateLastMessage);
mEphemeralEnabled = chatRoom->ephemeralEnabled();
mIsMuted = chatRoom->getMuted();
mParticipants = buildParticipants(chatRoom);
}
ChatCore::~ChatCore() {
@ -155,18 +157,23 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
});
});
mChatModelConnection->makeConnectToModel(&ChatModel::chatMessageReceived, // TODO onNewEvent?
// Events (excluding messages)
mChatModelConnection->makeConnectToModel(&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();
auto event = EventLogCore::create(eventLog);
mChatModelConnection->invokeToCore([this, event]() {
appendEventLogToEventLogList(event);
emit lUpdateUnreadCount();
emit lUpdateLastUpdatedTime();
});
if (event->isHandled()) {
mChatModelConnection->invokeToCore([this, event]() {
appendEventLogToEventLogList(event);
emit lUpdateUnreadCount();
emit lUpdateLastUpdatedTime();
});
}
});
// Chat messages
mChatModelConnection->makeConnectToModel(
&ChatModel::chatMessagesReceived, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventsLog) {
@ -196,7 +203,7 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
auto lastMessageModel = mLastMessage ? mLastMessage->getModel() : nullptr;
mChatModelConnection->invokeToModel([this, lastMessageModel]() {
auto linphoneMessage = mChatModel->getLastChatMessage();
if (!lastMessageModel || lastMessageModel->getMonitor() != linphoneMessage) {
if (linphoneMessage && (!lastMessageModel || lastMessageModel->getMonitor() != linphoneMessage)) {
auto chatMessageCore = ChatMessageCore::create(linphoneMessage);
mChatModelConnection->invokeToCore([this, chatMessageCore]() { setLastMessage(chatMessageCore); });
}
@ -269,6 +276,47 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
}
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lSetSubject, [this](QString subject) {
mChatModelConnection->invokeToModel([this, subject]() { mChatModel->setSubject(subject); });
});
mChatModelConnection->makeConnectToModel(
&ChatModel::subjectChanged, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
QString subject = Utils::coreStringToAppString(chatRoom->getSubject());
mChatModelConnection->invokeToCore([this, subject]() { setTitle(subject); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::participantAdded,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() {
mParticipants = participants;
emit participantsChanged();
});
});
mChatModelConnection->makeConnectToModel(&ChatModel::participantRemoved,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() {
mParticipants = participants;
emit participantsChanged();
});
});
mChatModelConnection->makeConnectToModel(&ChatModel::participantAdminStatusChanged,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
auto participants = buildParticipants(chatRoom);
mChatModelConnection->invokeToCore([this, participants]() {
mParticipants = participants;
emit participantsChanged();
});
});
mChatModelConnection->makeConnectToCore(&ChatCore::lRemoveParticipantAtIndex, [this](int index) {
mChatModelConnection->invokeToModel([this, index]() { mChatModel->removeParticipantAtIndex(index); });
});
}
QDateTime ChatCore::getLastUpdatedTime() const {
@ -463,3 +511,34 @@ bool ChatCore::isMuted() const {
bool ChatCore::isEphemeralEnabled() const {
return mEphemeralEnabled;
}
void ChatCore::setMeAdmin(bool admin) {
if (mMeAdmin != admin) {
mMeAdmin = admin;
emit meAdminChanged();
}
}
bool ChatCore::getMeAdmin() const {
return mMeAdmin;
}
QVariantList ChatCore::getParticipantsGui() const {
QVariantList result;
for (auto participantCore : mParticipants) {
auto participantGui = new ParticipantGui(participantCore);
result.append(QVariant::fromValue(participantGui));
}
return result;
}
QList<QSharedPointer<ParticipantCore>>
ChatCore::buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QList<QSharedPointer<ParticipantCore>> result;
for (auto participant : chatRoom->getParticipants()) {
auto participantCore = ParticipantCore::create(participant);
result.append(participantCore);
}
return result;
}

View file

@ -22,6 +22,7 @@
#define CHAT_CORE_H_
#include "core/chat/message/EventLogGui.hpp"
#include "core/participant/ParticipantCore.hpp"
#include "message/ChatMessageGui.hpp"
#include "model/chat/ChatModel.hpp"
#include "model/search/MagicSearchModel.hpp"
@ -57,6 +58,8 @@ public:
Q_PROPERTY(QString sendingText READ getSendingText WRITE setSendingText NOTIFY sendingTextChanged)
Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE lEnableEphemeral NOTIFY ephemeralEnabledChanged)
Q_PROPERTY(bool muted READ isMuted WRITE lSetMuted NOTIFY mutedChanged)
Q_PROPERTY(bool meAdmin READ getMeAdmin WRITE setMeAdmin NOTIFY meAdminChanged)
Q_PROPERTY(QVariantList participants READ getParticipantsGui NOTIFY participantsChanged)
// Should be call from model Thread. Will be automatically in App thread after initialization
static QSharedPointer<ChatCore> create(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
@ -103,6 +106,9 @@ public:
QString getChatRoomAddress() const;
QString getPeerAddress() const;
bool getMeAdmin() const;
void setMeAdmin(bool admin);
QList<QSharedPointer<EventLogCore>> getEventLogList() const;
void resetEventLogList(QList<QSharedPointer<EventLogCore>> list);
void appendEventLogToEventLogList(QSharedPointer<EventLogCore> event);
@ -120,6 +126,9 @@ public:
std::shared_ptr<ChatModel> getModel() const;
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;
QVariantList getParticipantsGui() const;
signals:
// used to close all the notifications when one is clicked
void messageOpen();
@ -138,6 +147,8 @@ signals:
void sendingTextChanged(QString text);
void mutedChanged();
void ephemeralEnabledChanged();
void meAdminChanged();
void participantsChanged();
void lDeleteMessage();
void lDelete();
@ -152,6 +163,8 @@ signals:
void lLeave();
void lSetMuted(bool muted);
void lEnableEphemeral(bool enable);
void lSetSubject(QString subject);
void lRemoveParticipantAtIndex(int index);
private:
QString id;
@ -170,6 +183,8 @@ private:
bool mIsReadOnly = false;
bool mEphemeralEnabled = false;
bool mIsMuted = false;
bool mMeAdmin = false;
QList<QSharedPointer<ParticipantCore>> mParticipants;
LinphoneEnums::ChatRoomState mChatRoomState;
std::shared_ptr<ChatModel> mChatModel;
QSharedPointer<ChatMessageCore> mLastMessage;

View file

@ -56,6 +56,9 @@ public:
std::string getEventLogId();
QSharedPointer<ChatMessageCore> getChatMessageCore();
QSharedPointer<CallHistoryCore> getCallHistoryCore();
bool isHandled() const {
return mHandled;
}
private:
DECLARE_ABSTRACT_OBJECT

View file

@ -50,8 +50,7 @@ ParticipantCore::ParticipantCore(const std::shared_ptr<linphone::Participant> &p
mIsMe = ToolModel::isMe(mSipAddress);
mCreationTime = QDateTime::fromSecsSinceEpoch(participant->getCreationTime());
mDisplayName = Utils::coreStringToAppString(participant->getAddress()->getDisplayName());
if (mDisplayName.isEmpty())
mDisplayName = Utils::coreStringToAppString(participant->getAddress()->getUsername());
if (mDisplayName.isEmpty()) mDisplayName = ToolModel::getDisplayName(participant->getAddress()->clone());
for (auto &device : participant->getDevices()) {
auto name = Utils::coreStringToAppString(device->getName());
auto address = Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());

View file

@ -6245,6 +6245,11 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>Meeting</extracomment>
<translation>Meeting</translation>
</message>
<message>
<source>group_infos_participants</source>
<extracomment>Participants</extracomment>
<translation>Participants (%1)</translation>
</message>
<message>
<source>group_infos_media_docs</source>
<extracomment>Medias &amp; documents</extracomment>
@ -6300,6 +6305,64 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>All the messages will be removed from the chat room. Do you want to continue ?</extracomment>
<translation>Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren?</translation>
</message>
<message>
<source>group_infos_group_call_toast_message</source>
<extracomment>&quot;Start a group call ?&quot;</extracomment>
<translation>Start a group call ?</translation>
</message>
</context>
<context>
<name>GroupChatInfoParticipants</name>
<message>
<source>group_infos_participant_is_admin</source>
<extracomment>Admin</extracomment>
<translation>Admin</translation>
</message>
<message>
<source>group_infos_add_participants_title</source>
<extracomment>Add Participants</extracomment>
<translation>Add Participants</translation>
</message>
<message>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation>Kontakt anzeigen</translation>
</message>
<message>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation>Zu Kontakten hinzufügen</translation>
</message>
<message>
<source>group_infos_give_admin_rights</source>
<extracomment>&quot;Give admin rights&quot;</extracomment>
<translation>Give admin rights</translation>
</message>
<message>
<source>group_infos_remove_admin_rights</source>
<extracomment>&quot;Remove admin rights&quot;</extracomment>
<translation>Remove admin rights</translation>
</message>
<message>
<source>group_infos_copy_sip_address</source>
<extracomment>&quot;Copy SIP Address&quot;</extracomment>
<translation>Copy SIP Address</translation>
</message>
<message>
<source>group_infos_remove_participant</source>
<extracomment>&quot;Remove participant&quot;</extracomment>
<translation>Remove participant</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_title</source>
<extracomment>&quot;Remove participant ?&quot;</extracomment>
<translation>Remove participant ?</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_message</source>
<extracomment>&quot;Participant will be removed from chat room.&quot;</extracomment>
<translation>Participant will be removed from chat room.</translation>
</message>
</context>
</TS>

View file

@ -6224,7 +6224,7 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<message>
<source>one_one_infos_open_contact</source>
<extracomment>Open contact</extracomment>
<translation>Open contact</translation>
<translation>Show contact</translation>
</message>
<message>
<source>one_one_infos_create_contact</source>
@ -6249,6 +6249,11 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>Meeting</extracomment>
<translation>Meeting</translation>
</message>
<message>
<source>group_infos_participants</source>
<extracomment>Participants</extracomment>
<translation>Participants (%1)</translation>
</message>
<message>
<source>group_infos_media_docs</source>
<extracomment>Medias &amp; documents</extracomment>
@ -6309,5 +6314,64 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>All the messages will be removed from the chat room. Do you want to continue ?</extracomment>
<translation>All the messages will be removed from the chat room. Do you want to continue ?</translation>
</message>
<message>
<source>group_infos_group_call_toast_message</source>
<extracomment>&quot;Start a group call ?&quot;</extracomment>
<translation>Start a group call ?</translation>
</message>
</context>
<context>
<name>GroupChatInfoParticipants</name>
<message>
<source>group_infos_add_participants_title</source>
<extracomment>Add Participants</extracomment>
<translation>Add Participants</translation>
</message>
<message>
<source>group_infos_participant_is_admin</source>
<extracomment>Admin</extracomment>
<translation>Admin</translation>
</message>
<message>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation>Show contact</translation>
</message>
<message>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation>Add to contacts</translation>
</message>
<message>
<source>group_infos_give_admin_rights</source>
<extracomment>&quot;Give admin rights&quot;</extracomment>
<translation>Give admin rights</translation>
</message>
<message>
<source>group_infos_remove_admin_rights</source>
<extracomment>&quot;Remove admin rights&quot;</extracomment>
<translation>Remove admin rights</translation>
</message>
<message>
<source>group_infos_copy_sip_address</source>
<extracomment>&quot;Copy SIP Address&quot;</extracomment>
<translation>Copy SIP Address</translation>
</message>
<message>
<source>group_infos_remove_participant</source>
<extracomment>&quot;Remove participant&quot;</extracomment>
<translation>Remove participant</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_title</source>
<extracomment>&quot;Remove participant ?&quot;</extracomment>
<translation>Remove participant ?</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_message</source>
<extracomment>&quot;Participant will be removed from chat room.&quot;</extracomment>
<translation>Participant will be removed from chat room.</translation>
</message>
</context>
</TS>

View file

@ -6146,6 +6146,11 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>Meeting</extracomment>
<translation>Réunion</translation>
</message>
<message>
<source>group_infos_participants</source>
<extracomment>Participants</extracomment>
<translation>Participants (%1)</translation>
</message>
<message>
<source>group_infos_media_docs</source>
<extracomment>Medias &amp; documents</extracomment>
@ -6206,5 +6211,65 @@ Failed to create 1-1 conversation with %1 !</extracomment>
<extracomment>All the messages will be removed from the chat room. Do you want to continue ?</extracomment>
<translation>Vous ne recevrez ni pourrez envoyer des messages dans cette conversation, quitter ?</translation>
</message>
<message>
<source>group_infos_group_call_toast_message</source>
<extracomment>&quot;Start a group call ?&quot;</extracomment>
<translation>Démarrer un appel de groupe ?</translation>
</message>
</context>
<context>
<name>GroupChatInfoParticipants</name>
<message>
<source>group_infos_add_participants_title</source>
<extracomment>Add Participants</extracomment>
<translation>Ajouter des Participants</translation>
</message>
<message>
<source>group_infos_participant_is_admin</source>
<extracomment>Admin</extracomment>
<translation>Admin</translation>
</message>
<message>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation>Voir le contact</translation>
</message>
<message>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation>Ajouter aux contacts</translation>
</message>
<message>
<source>group_infos_give_admin_rights</source>
<extracomment>&quot;Give admin rights&quot;</extracomment>
<translation>Donner les droits admins</translation>
</message>
<message>
<source>group_infos_remove_admin_rights</source>
<extracomment>&quot;Remove admin rights&quot;</extracomment>
<translation>Retirer les droits admins</translation>
</message>
<message>
<source>group_infos_copy_sip_address</source>
<extracomment>&quot;Copy SIP Address&quot;</extracomment>
<translation>Copier ladresse SIP</translation>
</message>
<message>
<source>group_infos_remove_participant</source>
<extracomment>&quot;Remove participant&quot;</extracomment>
<translation>Retirer le participant</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_title</source>
<extracomment>&quot;Remove participant ?&quot;</extracomment>
<translation>Retirer le participant ?</translation>
</message>
<message>
<source>group_infos_remove_participants_toast_message</source>
<extracomment>&quot;Participant will be removed from chat room.&quot;</extracomment>
<translation>La participant sere retiré de la conversation</translation>
</message>
</context>
</TS>

View file

@ -150,6 +150,15 @@ linphone::ChatRoom::State ChatModel::getState() const {
return mMonitor->getState();
}
void ChatModel::setSubject(QString subject) const {
return mMonitor->setSubject(Utils::appStringToCoreString(subject));
}
void ChatModel::removeParticipantAtIndex(int index) const {
auto participant = *std::next(mMonitor->getParticipants().begin(), index);
mMonitor->removeParticipant(participant);
}
//---------------------------------------------------------------//
void ChatModel::onIsComposingReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,

View file

@ -56,6 +56,8 @@ public:
linphone::ChatRoom::State getState() const;
void setMuted(bool muted);
void enableEphemeral(bool enable);
void setSubject(QString subject) const;
void removeParticipantAtIndex(int index) const;
signals:
void historyDeleted();

View file

@ -2004,4 +2004,4 @@ void Utils::setGlobalCursor(Qt::CursorShape cursor) {
void Utils::restoreGlobalCursor() {
App::getInstance()->restoreOverrideCursor();
}
}

View file

@ -154,6 +154,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Page/Layout/Chat/GroupConversationInfos.qml
view/Page/Layout/Chat/OneOneConversationInfos.qml
view/Page/Layout/Chat/ChatInfoActionsGroup.qml
view/Page/Layout/Chat/GroupChatInfoParticipants.qml
view/Page/Main/AbstractMainPage.qml
view/Page/Main/Account/AccountListView.qml

View file

@ -267,28 +267,21 @@ RowLayout {
id: contentLoader
anchors.top: parent.top
anchors.topMargin: Math.round(39 * DefaultStyle.dp)
active: true
property var chat: mainItem.chat
sourceComponent: chat && chat.core.isGroupChat ? groupInfoComponent : oneToOneInfoComponent
onLoaded: {
if (item && item.hasOwnProperty("chat")) {
item.chat = chat
}
}
sourceComponent: mainItem.chat.core.isGroupChat ? groupInfoComponent : oneToOneInfoComponent
active: detailsPanel.visible
}
Component {
id: oneToOneInfoComponent
OneOneConversationInfos {
chat: contentLoader.chat
chatGui: mainItem.chat
}
}
Component {
id: groupInfoComponent
GroupConversationInfos {
chat: contentLoader.chat
chatGui: mainItem.chat
}
}
}

View file

@ -0,0 +1,206 @@
import QtCore
import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Dialogs
import QtQuick.Effects
import QtQuick.Layouts
import Linphone
import UtilsCpp
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ColumnLayout {
id: mainItem
property var title: String
property var participants
property var chatCore
signal addParticipantRequested()
function isGroupEditable() {
return chatCore && chatCore.meAdmin && !chatCore.isReadOnly
}
Text {
font: Typography.h4
color: DefaultStyle.main2_600
text: title
Layout.topMargin: Math.round(5 * DefaultStyle.dp)
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: Math.round(9 * DefaultStyle.dp)
color: DefaultStyle.grey_100
radius: Math.round(15 * DefaultStyle.dp)
height: contentColumn.implicitHeight
ColumnLayout {
id: contentColumn
anchors.fill: parent
spacing: Math.round(16 * DefaultStyle.dp)
Item {
visible: participants.length > 0
Layout.preferredHeight: Math.round(1 * DefaultStyle.dp)
}
Repeater {
model: participants
delegate: RowLayout {
width: parent.width
Layout.leftMargin: Math.round(17 * DefaultStyle.dp)
Layout.rightMargin: Math.round(10 * DefaultStyle.dp)
spacing: Math.round(10 * DefaultStyle.dp)
property var participantGui: modelData
property var participantCore: participantGui.core
property var contactObj: UtilsCpp.findFriendByAddress(participantCore.sipAddress)
property var contact: contactObj?.value || null
Avatar {
contact: contactObj?.value || null
displayNameVal: contact ? "" : participantCore.displayName
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
}
ColumnLayout {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(56 * DefaultStyle.dp)
ColumnLayout {
spacing: Math.round(2 * DefaultStyle.dp)
Layout.alignment: Qt.AlignVCenter
Text {
text: participantCore.displayName
font: Typography.p1
color: DefaultStyle.main2_700
}
Text {
visible: participantCore.isAdmin
text: qsTr("group_infos_participant_is_admin")
font: Typography.p3
color: DefaultStyle.main2_500main
}
}
}
Item {
Layout.fillWidth: true
}
PopupButton {
id: detailOptions
popup.x: width
popup.contentItem: FocusScope {
implicitHeight: detailsButtons.implicitHeight
implicitWidth: detailsButtons.implicitWidth
Keys.onPressed: event => {
if (event.key == Qt.Key_Left
|| event.key == Qt.Key_Escape) {
detailOptions.popup.close()
event.accepted = true
}
}
ColumnLayout {
id: detailsButtons
anchors.fill: parent
IconLabelButton {
Layout.fillWidth: true
//: "Show contact"
text: contact && contact.core && contact.core.isAppFriend ? qsTr("menu_see_existing_contact") :
//: "Add to contacts"
qsTr("menu_add_address_to_contacts")
icon.source: (contact && contact.core && contact.core.isAppFriend)
? AppIcons.adressBook
: AppIcons.plusCircle
icon.width: Math.round(32 * DefaultStyle.dp)
icon.height: Math.round(32 * DefaultStyle.dp)
onClicked: {
detailOptions.close()
if (contact && contact.core.isAppFriend)
UtilsCpp.getMainWindow().displayContactPage(participantCore.sipAddress)
else
UtilsCpp.getMainWindow().displayCreateContactPage("",participantCore.sipAddress)
}
}
IconLabelButton {
visible: mainItem.isGroupEditable()
Layout.fillWidth: true
text: participantCore.isAdmin ? qsTr("group_infos_remove_admin_rights") : qsTr("group_infos_give_admin_rights")
icon.source: AppIcons.profile
icon.width: Math.round(32 * DefaultStyle.dp)
icon.height: Math.round(32 * DefaultStyle.dp)
onClicked: {
detailOptions.close()
participantCore.isAdmin = !participantCore.isAdmin
}
}
IconLabelButton {
visible: !contact || (contact.core && !contact.core.isAppFriend)
Layout.fillWidth: true
text: qsTr("group_infos_copy_sip_address")
icon.source: AppIcons.copy
icon.width: Math.round(32 * DefaultStyle.dp)
icon.height: Math.round(32 * DefaultStyle.dp)
onClicked: {
detailOptions.close()
UtilsCpp.copyToClipboard(participantCore.sipAddress)
}
}
Rectangle {
visible: mainItem.isGroupEditable()
color: DefaultStyle.main2_200
Layout.fillWidth: true
height: Math.round(1 * DefaultStyle.dp)
width: parent.width - Math.round(30 * DefaultStyle.dp)
Layout.leftMargin: Math.round(17 * DefaultStyle.dp)
}
IconLabelButton {
visible: mainItem.isGroupEditable()
Layout.fillWidth: true
text: qsTr("group_infos_remove_participant")
icon.source: AppIcons.trashCan
icon.width: Math.round(32 * DefaultStyle.dp)
icon.height: Math.round(32 * DefaultStyle.dp)
style: ButtonStyle.hoveredBackgroundRed
onClicked: {
detailOptions.close()
UtilsCpp.getMainWindow().showConfirmationLambdaPopup(qsTr("group_infos_remove_participants_toast_title"),
qsTr("group_infos_remove_participants_toast_message"),
"",
function(confirmed) {
if (confirmed) {
mainItem.chatCore.lRemoveParticipantAtIndex(index)
}
})
}
}
}
}
}
}
}
MediumButton {
id: addParticipant
visible: mainItem.isGroupEditable()
height: Math.round(40 * DefaultStyle.dp)
icon.source: AppIcons.plusCircle
icon.width: Math.round(16 * DefaultStyle.dp)
icon.height: Math.round(16 * DefaultStyle.dp)
//: "Ajouter des participants"
text: qsTr("group_infos_add_participants_title")
style: ButtonStyle.secondary
onClicked: mainItem.addParticipantRequested()
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Math.round(17 * DefaultStyle.dp)
}
Item {
visible: !addParticipant.visible
Layout.bottomMargin: Math.round(7 * DefaultStyle.dp)
}
}
}
}

View file

@ -1,6 +1,6 @@
import QtCore
import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Controls 2.15
import QtQuick.Dialogs
import QtQuick.Effects
import QtQuick.Layouts
@ -11,28 +11,93 @@ import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ColumnLayout {
id: mainItem
property var chat
property ChatGui chatGui
property var chatCore: chatGui.core
spacing: 0
Avatar {
Layout.alignment: Qt.AlignHCenter
displayNameVal: mainItem.chat.core.avatarUri
displayNameVal: mainItem.chatCore.avatarUri
Layout.preferredWidth: Math.round(100 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(100 * DefaultStyle.dp)
}
Text {
font: Typography.p1
color: DefaultStyle.main2_700
text: mainItem.chat?.core.title || ""
RowLayout {
id: titleMainItem
property bool isEditingSubject: false
property bool canEditSubject: mainItem.chatCore.meAdmin && mainItem.chatCore.isGroupChat
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(11 * DefaultStyle.dp)
function saveSubject() {
mainItem.chatCore.lSetSubject(title.text)
}
Item {
visible: titleMainItem.canEditSubject
Layout.preferredWidth: Math.round(18 * DefaultStyle.dp)
}
Rectangle {
color: "transparent"
border.color: titleMainItem.isEditingSubject ? DefaultStyle.main1_500_main : "transparent"
border.width: 1
radius: Math.round(4 * DefaultStyle.dp)
Layout.preferredWidth: Math.round(150 * DefaultStyle.dp)
Layout.preferredHeight: title.contentHeight
anchors.margins: Math.round(2 * DefaultStyle.dp)
TextEdit {
id: title
anchors.fill: parent
anchors.margins: 6
font: Typography.p1
color: DefaultStyle.main2_700
text: mainItem.chatCore.title || ""
enabled: titleMainItem.isEditingSubject
wrapMode: TextEdit.Wrap
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Keys.onReturnPressed: {
if (titleMainItem.isEditingSubject)
titleMainItem.saveSubject()
titleMainItem.isEditingSubject = !titleMainItem.isEditingSubject
}
}
}
Item {
visible: titleMainItem.canEditSubject
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: Math.round(18 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(18 * DefaultStyle.dp)
EffectImage {
anchors.fill: parent
fillMode: Image.PreserveAspectFit
imageSource: titleMainItem.isEditingSubject ? AppIcons.check : AppIcons.pencil
colorizationColor: titleMainItem.isEditingSubject ? DefaultStyle.main1_500_main : DefaultStyle.main2_500main
}
MouseArea {
id: editMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (titleMainItem.isEditingSubject)
titleMainItem.saveSubject()
titleMainItem.isEditingSubject = !titleMainItem.isEditingSubject
}
cursorShape: Qt.PointingHandCursor
}
}
}
Text {
font: Typography.p3
color: DefaultStyle.main2_700
text: mainItem.chat?.core.peerAddress
text: mainItem.chatCore.peerAddress
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(5 * DefaultStyle.dp)
}
@ -46,11 +111,11 @@ ColumnLayout {
height: Math.round(56 * DefaultStyle.dp)
button.icon.width: Math.round(24 * DefaultStyle.dp)
button.icon.height: Math.round(24 * DefaultStyle.dp)
button.icon.source: chat.core.muted ? AppIcons.bell : AppIcons.bellSlash
button.icon.source: chatCore.muted ? AppIcons.bell : AppIcons.bellSlash
//: "Sourdine"
label: qsTr("group_infos_mute")
button.onClicked: {
chat.core.muted = !chat.core.muted
chatCore.muted = !chatCore.muted
}
}
LabelButton {
@ -62,7 +127,21 @@ ColumnLayout {
//: "Appel"
label: qsTr("group_infos_call")
button.onClicked: {
//TODO
mainWindow.showConfirmationLambdaPopup(qsTr("group_infos_call"),
qsTr("group_infos_group_call_toast_message"),
"",
function(confirmed) {
if (confirmed) {
const sourceList = mainItem.chatCore.participants
let addresses = [];
for (let i = 0; i < sourceList.length; ++i) {
const participantGui = sourceList[i]
const participantCore = participantGui.core
addresses.push(participantCore.sipAddress)
}
UtilsCpp.createGroupCall(mainItem.chatCore.title, addresses)
}
})
}
}
LabelButton {
@ -78,94 +157,112 @@ ColumnLayout {
}
}
}
ChatInfoActionsGroup {
Layout.leftMargin: Math.round(15 * DefaultStyle.dp)
Layout.rightMargin: Math.round(12 * DefaultStyle.dp)
Layout.topMargin: Math.round(30 * DefaultStyle.dp)
title: qsTr("group_infos_media_docs")
entries: [
{
icon: AppIcons.photo,
visible: true,
text: qsTr("group_infos_media_docs"),
color: DefaultStyle.main2_600,
showRightArrow: true,
action: function() {
console.log("group_infos_shared_media")
}
},
{
icon: AppIcons.pdf,
visible: true,
text: qsTr("group_infos_shared_docs"),
color: DefaultStyle.main2_600,
showRightArrow: true,
action: function() {
console.log("Opening shared documents")
}
}
]
}
ChatInfoActionsGroup {
Layout.leftMargin: Math.round(15 * DefaultStyle.dp)
Layout.rightMargin: Math.round(12 * DefaultStyle.dp)
Layout.topMargin: Math.round(17 * DefaultStyle.dp)
title: qsTr("group_infos_other_actions")
entries: [
{
icon: AppIcons.clockCountDown,
visible: true,
text: mainItem.chat.core.ephemeralEnabled ? qsTr("group_infos_disable_ephemerals") : qsTr("group_infos_enable_ephemerals"),
color: DefaultStyle.main2_600,
showRightArrow: false,
action: function() {
mainItem.chat.core.ephemeralEnabled = !mainItem.chat.core.ephemeralEnabled
}
},
{
icon: AppIcons.signOut,
visible: !mainItem.chat.core.isReadOnly,
text: qsTr("group_infos_leave_room"),
color: DefaultStyle.main2_600,
showRightArrow: false,
action: function() {
//: Leave Chat Room ?
mainWindow.showConfirmationLambdaPopup(qsTr("group_infos_leave_room_toast_title"),
//: All the messages will be removed from the chat room. Do you want to continue ?
qsTr("group_infos_leave_room_toast_message"),
"",
function(confirmed) {
if (confirmed) {
mainItem.chat.core.lLeave()
}
})
}
},
{
icon: AppIcons.trashCan,
visible: true,
text: qsTr("group_infos_delete_history"),
color: DefaultStyle.danger_500main,
showRightArrow: false,
action: function() {
//: Delete history ?
mainWindow.showConfirmationLambdaPopup(qsTr("group_infos_delete_history_toast_title"),
//: All the messages will be removed from the chat room. Do you want to continue ?
qsTr("group_infos_delete_history_toast_message"),
"",
function(confirmed) {
if (confirmed) {
mainItem.chat.core.lDeleteHistory()
}
})
}
}
]
}
Item {
ScrollView {
id: scrollView
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: Math.round(30 * DefaultStyle.dp)
clip: true
Layout.leftMargin: Math.round(15 * DefaultStyle.dp)
Layout.rightMargin: Math.round(15 * DefaultStyle.dp)
ColumnLayout {
spacing: 0
width: scrollView.width
GroupChatInfoParticipants {
Layout.fillWidth: true
title: qsTr("group_infos_participants").arg(mainItem.chatCore.participants.length)
participants: mainItem.chatCore.participants
chatCore: mainItem.chatCore
}
ChatInfoActionsGroup {
Layout.topMargin: Math.round(30 * DefaultStyle.dp)
title: qsTr("group_infos_media_docs")
entries: [
{
icon: AppIcons.photo,
visible: true,
text: qsTr("group_infos_media_docs"),
color: DefaultStyle.main2_600,
showRightArrow: true,
action: function() {
console.log("group_infos_shared_media")
}
},
{
icon: AppIcons.pdf,
visible: true,
text: qsTr("group_infos_shared_docs"),
color: DefaultStyle.main2_600,
showRightArrow: true,
action: function() {
console.log("Opening shared documents")
}
}
]
}
ChatInfoActionsGroup {
Layout.topMargin: Math.round(17 * DefaultStyle.dp)
title: qsTr("group_infos_other_actions")
entries: [
{
icon: AppIcons.clockCountDown,
visible: !mainItem.chatCore.isReadOnly,
text: mainItem.chatCore.ephemeralEnabled ? qsTr("group_infos_disable_ephemerals") : qsTr("group_infos_enable_ephemerals"),
color: DefaultStyle.main2_600,
showRightArrow: false,
action: function() {
mainItem.chatCore.ephemeralEnabled = !mainItem.chatCore.ephemeralEnabled
}
},
{
icon: AppIcons.signOut,
visible: !mainItem.chatCore.isReadOnly,
text: qsTr("group_infos_leave_room"),
color: DefaultStyle.main2_600,
showRightArrow: false,
action: function() {
//: Leave Chat Room ?
mainWindow.showConfirmationLambdaPopup(qsTr("group_infos_leave_room_toast_title"),
//: All the messages will be removed from the chat room. Do you want to continue ?
qsTr("group_infos_leave_room_toast_message"),
"",
function(confirmed) {
if (confirmed) {
mainItem.chatCore.lLeave()
}
})
}
},
{
icon: AppIcons.trashCan,
visible: true,
text: qsTr("group_infos_delete_history"),
color: DefaultStyle.danger_500main,
showRightArrow: false,
action: function() {
//: Delete history ?
mainWindow.showConfirmationLambdaPopup(qsTr("group_infos_delete_history_toast_title"),
//: All the messages will be removed from the chat room. Do you want to continue ?
qsTr("group_infos_delete_history_toast_message"),
"",
function(confirmed) {
if (confirmed) {
mainItem.chatCore.lDeleteHistory()
}
})
}
}
]
}
}
}
Item {
Layout.preferredHeight: Math.round(50 * DefaultStyle.dp)
}
}

View file

@ -11,14 +11,15 @@ import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ColumnLayout {
id: mainItem
property var chat
property var contactObj: chat ? UtilsCpp.findFriendByAddress(chat?.core.peerAddress) : null
property ChatGui chatGui
property var chatCore: chatGui.core
property var contactObj: chat ? UtilsCpp.findFriendByAddress(mainItem.chatCore.peerAddress) : null
spacing: 0
Avatar {
Layout.alignment: Qt.AlignHCenter
contact: contactObj?.value || null
displayNameVal: contact ? "" : mainItem.chat.core.avatarUri
displayNameVal: contact ? "" : mainItem.chatCore.avatarUri
Layout.preferredWidth: Math.round(100 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(100 * DefaultStyle.dp)
}
@ -26,7 +27,7 @@ ColumnLayout {
Text {
font: Typography.p1
color: DefaultStyle.main2_700
text: mainItem.chat?.core.title || ""
text: mainItem.chatCore.title || ""
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(11 * DefaultStyle.dp)
}
@ -34,20 +35,20 @@ ColumnLayout {
Text {
font: Typography.p3
color: DefaultStyle.main2_700
text: mainItem.chat?.core.peerAddress
text: mainItem.chatCore.peerAddress
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(5 * DefaultStyle.dp)
}
Text {
visible: contactObj?.value
font: Typography.p3
color: contactObj?.value.core.presenceColor
text: contactObj?.value.core.presenceStatus
color: contactObj?.value != null ? contactObj?.value.core.presenceColor : "transparent"
text: contactObj?.value != null ? contactObj?.value.core.presenceStatus : ""
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(5 * DefaultStyle.dp)
}
RowLayout {
spacing: Math.round(55 * DefaultStyle.dp)
Layout.alignment: Qt.AlignHCenter
@ -69,11 +70,11 @@ ColumnLayout {
height: Math.round(56 * DefaultStyle.dp)
button.icon.width: Math.round(24 * DefaultStyle.dp)
button.icon.height: Math.round(24 * DefaultStyle.dp)
button.icon.source: chat.core.muted ? AppIcons.bell : AppIcons.bellSlash
button.icon.source: mainItem.chatCore.muted ? AppIcons.bell : AppIcons.bellSlash
//: "Sourdine"
label: qsTr("one_one_infos_mute")
button.onClicked: {
chat.core.muted = !chat.core.muted
mainItem.chatCore.muted = !mainItem.chatCore.muted
}
}
LabelButton {
@ -126,7 +127,7 @@ ColumnLayout {
title: qsTr("one_one_infos_other_actions")
entries: [
{
icon: AppIcons.adressBook,
icon: contactObj.value ? AppIcons.adressBook : AppIcons.plusCircle,
visible: true,
text: contactObj.value ? qsTr("one_one_infos_open_contact") : qsTr("one_one_infos_create_contact"),
color: DefaultStyle.main2_600,
@ -136,18 +137,17 @@ ColumnLayout {
if (contactObj.value)
mainWindow.displayContactPage(contactObj.value.core.defaultAddress)
else
mainWindow.displayCreateContactPage("",chat.core.peerAddress)
//mainItem.createContactRequested(contactDetail.contactName, chat.core.peerAddress)
mainWindow.displayCreateContactPage("",mainItem.chatCore.peerAddress)
}
},
{
icon: AppIcons.clockCountDown,
visible: true,
text: mainItem.chat.core.ephemeralEnabled ? qsTr("one_one_infos_disable_ephemerals") : qsTr("one_one_infos_enable_ephemerals"),
text: mainItem.chatCore.ephemeralEnabled ? qsTr("one_one_infos_disable_ephemerals") : qsTr("one_one_infos_enable_ephemerals"),
color: DefaultStyle.main2_600,
showRightArrow: false,
action: function() {
mainItem.chat.core.ephemeralEnabled = !mainItem.chat.core.ephemeralEnabled
mainItem.chatCore.ephemeralEnabled = !mainItem.chatCore.ephemeralEnabled
}
},
{
@ -164,7 +164,7 @@ ColumnLayout {
"",
function(confirmed) {
if (confirmed) {
mainItem.chat.core.lDeleteHistory()
mainItem.chatCore.lDeleteHistory()
}
})
}