create new chat

This commit is contained in:
Gaelle Braud 2025-05-15 17:31:38 +02:00
parent 5808368b9a
commit 75e71be14d
24 changed files with 593 additions and 331 deletions

View file

@ -89,6 +89,7 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
} }
resetChatMessageList(messageList); resetChatMessageList(messageList);
mIdentifier = Utils::coreStringToAppString(chatRoom->getIdentifier()); mIdentifier = Utils::coreStringToAppString(chatRoom->getIdentifier());
mChatRoomState = LinphoneEnums::fromLinphone(chatRoom->getState());
connect(this, &ChatCore::messageListChanged, this, &ChatCore::lUpdateLastMessage); connect(this, &ChatCore::messageListChanged, this, &ChatCore::lUpdateLastMessage);
connect(this, &ChatCore::messagesInserted, this, &ChatCore::lUpdateLastMessage); connect(this, &ChatCore::messagesInserted, this, &ChatCore::lUpdateLastMessage);
connect(this, &ChatCore::messageRemoved, this, &ChatCore::lUpdateLastMessage); connect(this, &ChatCore::messageRemoved, this, &ChatCore::lUpdateLastMessage);
@ -132,6 +133,12 @@ void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
}); });
mChatModelConnection->makeConnectToModel( mChatModelConnection->makeConnectToModel(
&ChatModel::deleted, [this]() { mChatModelConnection->invokeToCore([this]() { emit deleted(); }); }); &ChatModel::deleted, [this]() { mChatModelConnection->invokeToCore([this]() { emit deleted(); }); });
mChatModelConnection->makeConnectToModel(
&ChatModel::stateChanged,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State newState) {
auto state = LinphoneEnums::fromLinphone(newState);
mChatModelConnection->invokeToCore([this, state]() { setChatRoomState(state); });
});
mChatModelConnection->makeConnectToModel(&ChatModel::chatMessageReceived, mChatModelConnection->makeConnectToModel(&ChatModel::chatMessageReceived,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom, [this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
@ -277,6 +284,17 @@ LinphoneEnums::ChatMessageState ChatCore::getLastMessageState() const {
return mLastMessage ? mLastMessage->getMessageState() : LinphoneEnums::ChatMessageState::StateIdle; return mLastMessage ? mLastMessage->getMessageState() : LinphoneEnums::ChatMessageState::StateIdle;
} }
LinphoneEnums::ChatRoomState ChatCore::getChatRoomState() const {
return mChatRoomState;
}
void ChatCore::setChatRoomState(LinphoneEnums::ChatRoomState state) {
if (mChatRoomState != state) {
mChatRoomState = state;
emit chatRoomStateChanged();
}
}
ChatMessageGui *ChatCore::getLastMessage() const { ChatMessageGui *ChatCore::getLastMessage() const {
return mLastMessage ? new ChatMessageGui(mLastMessage) : nullptr; return mLastMessage ? new ChatMessageGui(mLastMessage) : nullptr;
} }

View file

@ -43,6 +43,7 @@ public:
Q_PROPERTY(QString lastMessageText READ getLastMessageText NOTIFY lastMessageChanged) Q_PROPERTY(QString lastMessageText READ getLastMessageText NOTIFY lastMessageChanged)
Q_PROPERTY(ChatMessageGui *lastMessage READ getLastMessage NOTIFY lastMessageChanged) Q_PROPERTY(ChatMessageGui *lastMessage READ getLastMessage NOTIFY lastMessageChanged)
Q_PROPERTY(LinphoneEnums::ChatMessageState lastMessageState READ getLastMessageState NOTIFY lastMessageChanged) Q_PROPERTY(LinphoneEnums::ChatMessageState lastMessageState READ getLastMessageState NOTIFY lastMessageChanged)
Q_PROPERTY(LinphoneEnums::ChatRoomState state READ getChatRoomState NOTIFY chatRoomStateChanged)
Q_PROPERTY(int unreadMessagesCount READ getUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY Q_PROPERTY(int unreadMessagesCount READ getUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY
unreadMessagesCountChanged) unreadMessagesCountChanged)
Q_PROPERTY(QString composingName READ getComposingName WRITE setComposingName NOTIFY composingUserChanged) Q_PROPERTY(QString composingName READ getComposingName WRITE setComposingName NOTIFY composingUserChanged)
@ -70,6 +71,9 @@ public:
LinphoneEnums::ChatMessageState getLastMessageState() const; LinphoneEnums::ChatMessageState getLastMessageState() const;
LinphoneEnums::ChatRoomState getChatRoomState() const;
void setChatRoomState(LinphoneEnums::ChatRoomState state);
QSharedPointer<ChatMessageCore> getLastMessageCore() const; QSharedPointer<ChatMessageCore> getLastMessageCore() const;
void setLastMessage(QSharedPointer<ChatMessageCore> lastMessage); void setLastMessage(QSharedPointer<ChatMessageCore> lastMessage);
@ -96,11 +100,9 @@ public:
std::shared_ptr<ChatModel> getModel() const; std::shared_ptr<ChatModel> getModel() const;
Q_SIGNALS: signals:
// used to close all the notifications when one is clicked // used to close all the notifications when one is clicked
void messageOpen(); void messageOpen();
signals:
void lastUpdatedTimeChanged(QDateTime time); void lastUpdatedTimeChanged(QDateTime time);
void lastMessageChanged(); void lastMessageChanged();
void titleChanged(QString title); void titleChanged(QString title);
@ -111,6 +113,7 @@ signals:
void avatarUriChanged(); void avatarUriChanged();
void deleted(); void deleted();
void composingUserChanged(); void composingUserChanged();
void chatRoomStateChanged();
void lDeleteMessage(); void lDeleteMessage();
void lDelete(); void lDelete();
@ -134,6 +137,7 @@ private:
QString mComposingName; QString mComposingName;
QString mComposingAddress; QString mComposingAddress;
bool mIsGroupChat = false; bool mIsGroupChat = false;
LinphoneEnums::ChatRoomState mChatRoomState;
std::shared_ptr<ChatModel> mChatModel; std::shared_ptr<ChatModel> mChatModel;
QSharedPointer<ChatMessageCore> mLastMessage; QSharedPointer<ChatMessageCore> mLastMessage;
QList<QSharedPointer<ChatMessageCore>> mChatMessageList; QList<QSharedPointer<ChatMessageCore>> mChatMessageList;

View file

@ -103,28 +103,6 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
}); });
}); });
mModelConnection->makeConnectToModel(
&CoreModel::chatRoomStateChanged,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::ChatRoom> &chatRoom,
linphone::ChatRoom::State state) {
// check account, filter, then add if ok
if (chatRoom->getAccount() == core->getDefaultAccount()) {
if (state == linphone::ChatRoom::State::Created) {
auto list = getSharedList<ChatCore>();
auto found =
std::find_if(list.begin(), list.end(), [chatRoom](const QSharedPointer<ChatCore> &item) {
return (item && item->getModel()->getMonitor() == chatRoom);
});
if (found == list.end()) {
auto model = createChatCore(chatRoom);
mModelConnection->invokeToCore([this, model]() {
add(model);
emit chatAdded();
});
}
}
}
});
mModelConnection->makeConnectToModel( mModelConnection->makeConnectToModel(
&CoreModel::defaultAccountChanged, &CoreModel::defaultAccountChanged,
[this](std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) { lUpdate(); }); [this](std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) { lUpdate(); });
@ -146,6 +124,19 @@ int ChatList::findChatIndex(ChatGui *chatGui) {
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it); return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
} }
void ChatList::addChatInList(ChatGui *chatGui) {
auto chatCore = chatGui->mCore;
auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer<ChatCore> item) {
return item->getIdentifier() == chatCore->getIdentifier();
});
if (it == chatList.end()) {
connectItem(chatCore);
add(chatCore);
emit chatAdded();
}
}
QVariant ChatList::data(const QModelIndex &index, int role) const { QVariant ChatList::data(const QModelIndex &index, int role) const {
int row = index.row(); int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();

View file

@ -42,6 +42,7 @@ public:
void connectItem(QSharedPointer<ChatCore> chat); void connectItem(QSharedPointer<ChatCore> chat);
int findChatIndex(ChatGui *chat); int findChatIndex(ChatGui *chat);
void addChatInList(ChatGui *chatGui);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals: signals:

View file

@ -66,6 +66,13 @@ int ChatProxy::findChatIndex(ChatGui *chatGui) {
return -1; return -1;
} }
void ChatProxy::addChatInList(ChatGui *chatGui) {
auto chatList = getListModel<ChatList>();
if (chatList) {
chatList->addChatInList(chatGui);
}
}
bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
// auto l = getItemAtSource<ChatList, ChatCore>(sourceRow); // auto l = getItemAtSource<ChatList, ChatCore>(sourceRow);
// return l != nullptr; // return l != nullptr;

View file

@ -40,6 +40,7 @@ public:
void setSourceModel(QAbstractItemModel *sourceModel) override; void setSourceModel(QAbstractItemModel *sourceModel) override;
Q_INVOKABLE int findChatIndex(ChatGui *chatGui); Q_INVOKABLE int findChatIndex(ChatGui *chatGui);
Q_INVOKABLE void addChatInList(ChatGui *chatGui);
signals: signals:
void chatRemoved(ChatGui *chat); void chatRemoved(ChatGui *chat);

View file

@ -467,11 +467,19 @@ QString ToolModel::computeUserAgent(const std::shared_ptr<linphone::Config> &con
.remove("'"); .remove("'");
} }
bool ToolModel::isEndToEndEncryptedChatAvailable() {
auto core = CoreModel::getInstance()->getCore();
auto defaultAccount = core->getDefaultAccount();
return core->limeX3DhEnabled() && defaultAccount && !defaultAccount->getParams()->getLimeServerUrl().empty() &&
!defaultAccount->getParams()->getConferenceFactoryUri().empty();
}
std::shared_ptr<linphone::ConferenceParams> std::shared_ptr<linphone::ConferenceParams>
ToolModel::getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress) { ToolModel::getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress) {
auto core = call ? call->getCore() : CoreModel::getInstance()->getCore(); auto core = call ? call->getCore() : CoreModel::getInstance()->getCore();
auto localAddress = call ? call->getCallLog()->getLocalAddress() : nullptr; auto localAddress = call ? call->getCallLog()->getLocalAddress() : nullptr;
if (!remoteAddress && call) remoteAddress = call->getRemoteAddress()->clone(); if (!remoteAddress && call) remoteAddress = call->getRemoteAddress()->clone();
remoteAddress->clean();
auto account = findAccount(localAddress); auto account = findAccount(localAddress);
if (!account) account = core->getDefaultAccount(); if (!account) account = core->getDefaultAccount();
if (!account) qWarning() << "failed to get account, return"; if (!account) qWarning() << "failed to get account, return";
@ -481,7 +489,7 @@ ToolModel::getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_p
params->enableChat(true); params->enableChat(true);
params->enableGroup(false); params->enableGroup(false);
//: Dummy subject //: Dummy subject
params->setSubject(Utils::appStringToCoreString(QObject::tr("chat_dummy_subject"))); params->setSubject("Dummy subject");
params->setAccount(account); params->setAccount(account);
auto chatParams = params->getChatParams(); auto chatParams = params->getChatParams();
@ -498,12 +506,12 @@ ToolModel::getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_p
chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat);
params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
} else if (!accountParams->getInstantMessagingEncryptionMandatory()) { } else if (!accountParams->getInstantMessagingEncryptionMandatory()) {
if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls()) { if (isEndToEndEncryptedChatAvailable()) {
qDebug() << "Account is in interop mode but LIME is available, requesting E2E encryption"; qDebug() << "Account is in interop mode but LIME is available, requesting E2E encryption";
chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat);
params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
} else { } else {
qDebug() << "Account is in interop mode and LIME is available, disabling E2E encryption"; qDebug() << "Account is in interop mode and LIME is not available, disabling E2E encryption";
chatParams->setBackend(linphone::ChatRoom::Backend::Basic); chatParams->setBackend(linphone::ChatRoom::Backend::Basic);
params->setSecurityLevel(linphone::Conference::SecurityLevel::None); params->setSecurityLevel(linphone::Conference::SecurityLevel::None);
} }
@ -554,12 +562,14 @@ std::shared_ptr<linphone::ChatRoom> ToolModel::lookupChatForAddress(std::shared_
auto core = CoreModel::getInstance()->getCore(); auto core = CoreModel::getInstance()->getCore();
auto account = core->getDefaultAccount(); auto account = core->getDefaultAccount();
if (!account) return nullptr; if (!account) return nullptr;
auto localAddress = account->getParams()->getIdentityAddress(); auto localAddress = account->getParams()->getIdentityAddress()->clone();
localAddress->clean();
if (!localAddress || !remoteAddress) return nullptr; if (!localAddress || !remoteAddress) return nullptr;
auto params = getChatRoomParams(nullptr, remoteAddress); auto params = getChatRoomParams(nullptr, remoteAddress);
std::list<std::shared_ptr<linphone::Address>> participants; std::list<std::shared_ptr<linphone::Address>> participants;
participants.push_back(remoteAddress->clone()); remoteAddress->clean();
participants.push_back(remoteAddress);
qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant" qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant"
<< remoteAddress->asStringUriOnly(); << remoteAddress->asStringUriOnly();
@ -581,6 +591,33 @@ std::shared_ptr<linphone::ChatRoom> ToolModel::createChatForAddress(std::shared_
auto chatRoom = core->createChatRoom(params, participants); auto chatRoom = core->createChatRoom(params, participants);
return chatRoom; return chatRoom;
} }
std::shared_ptr<linphone::ChatRoom>
ToolModel::createGroupChatRoom(QString subject, std::list<std::shared_ptr<linphone::Address>> participantsAddresses) {
auto core = CoreModel::getInstance()->getCore();
auto account = core->getDefaultAccount();
auto params = core->createConferenceParams(nullptr);
params->enableChat(true);
params->enableGroup(true);
params->setSubject(Utils::appStringToCoreString(subject));
params->setAccount(account);
params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
auto chatParams = params->getChatParams();
if (!chatParams) {
qWarning() << "failed to get chat params from conference params, return";
return nullptr;
}
chatParams->setEphemeralLifetime(0);
chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat);
auto accountParams = account->getParams();
auto chatRoom = core->createChatRoom(params, participantsAddresses);
return chatRoom;
}
// Presence mapping from SDK PresenceModel/RFC 3863 <-> Linphone UI (5 statuses Online, Offline, Away, Busy, DND). // Presence mapping from SDK PresenceModel/RFC 3863 <-> Linphone UI (5 statuses Online, Offline, Away, Busy, DND).
// Online = Basic Status open with no activity // Online = Basic Status open with no activity
// Busy = Basic Status open with activity Busy and description busy // Busy = Basic Status open with activity Busy and description busy
@ -589,11 +626,10 @@ std::shared_ptr<linphone::ChatRoom> ToolModel::createChatForAddress(std::shared_
// DND = Basic Status open with activity Other and description dnd // DND = Basic Status open with activity Other and description dnd
// Note : close status on the last 2 items would be preferrable, but they currently trigger multiple tuple NOTIFY from // Note : close status on the last 2 items would be preferrable, but they currently trigger multiple tuple NOTIFY from
// flexisip presence server Note 2 : close status with no activity triggers an unsubscribe. // flexisip presence server Note 2 : close status with no activity triggers an unsubscribe.
LinphoneEnums::Presence LinphoneEnums::Presence
ToolModel::corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel) { ToolModel::corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel) {
if (!presenceModel) { if (!presenceModel) {
lWarning() << sLog().arg("presence model is null."); // lWarning() << sLog().arg("presence model is null.");
return LinphoneEnums::Presence::Undefined; return LinphoneEnums::Presence::Undefined;
} }

View file

@ -85,8 +85,11 @@ public:
getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress = nullptr); getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress = nullptr);
static std::shared_ptr<linphone::ChatRoom> lookupCurrentCallChat(std::shared_ptr<CallModel> callModel); static std::shared_ptr<linphone::ChatRoom> lookupCurrentCallChat(std::shared_ptr<CallModel> callModel);
static std::shared_ptr<linphone::ChatRoom> createCurrentCallChat(std::shared_ptr<CallModel> callModel); static std::shared_ptr<linphone::ChatRoom> createCurrentCallChat(std::shared_ptr<CallModel> callModel);
static bool isEndToEndEncryptedChatAvailable();
static std::shared_ptr<linphone::ChatRoom> lookupChatForAddress(std::shared_ptr<linphone::Address> remoteAddress); static std::shared_ptr<linphone::ChatRoom> lookupChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
static std::shared_ptr<linphone::ChatRoom> createChatForAddress(std::shared_ptr<linphone::Address> remoteAddress); static std::shared_ptr<linphone::ChatRoom> createChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
static std::shared_ptr<linphone::ChatRoom>
createGroupChatRoom(QString subject, std::list<std::shared_ptr<linphone::Address>> participantsAddresses);
static LinphoneEnums::Presence static LinphoneEnums::Presence
corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel); corePresenceModelToAppPresence(std::shared_ptr<const linphone::PresenceModel> presenceModel);

View file

@ -98,6 +98,7 @@ public:
Q_PROPERTY(QString ContactUrl MEMBER ContactUrl CONSTANT) Q_PROPERTY(QString ContactUrl MEMBER ContactUrl CONSTANT)
Q_PROPERTY(QString TranslationUrl MEMBER TranslationUrl CONSTANT) Q_PROPERTY(QString TranslationUrl MEMBER TranslationUrl CONSTANT)
Q_PROPERTY(QString DefaultFont MEMBER DefaultFont CONSTANT) Q_PROPERTY(QString DefaultFont MEMBER DefaultFont CONSTANT)
Q_PROPERTY(QString DefaultLocale MEMBER DefaultLocale CONSTANT)
Q_PROPERTY(int maxMosaicParticipants MEMBER MaxMosaicParticipants CONSTANT) Q_PROPERTY(int maxMosaicParticipants MEMBER MaxMosaicParticipants CONSTANT)
Q_PROPERTY(QStringList reactionsList READ getReactionsList CONSTANT) Q_PROPERTY(QStringList reactionsList READ getReactionsList CONSTANT)

View file

@ -1538,7 +1538,7 @@ VariantObject *Utils::getCurrentCallChat(CallGui *call) {
data->mConnection->invokeToCore([] { data->mConnection->invokeToCore([] {
//: Error //: Error
showInformationPopup(tr("information_popup_error_title"), showInformationPopup(tr("information_popup_error_title"),
//: Failed to create 1-1 conversation with %1 ! //: Failed to create 1-1 conversation with %1 !
tr("information_popup_chatroom_creation_error_message"), false, tr("information_popup_chatroom_creation_error_message"), false,
getCallsWindow()); getCallsWindow());
}); });
@ -1556,6 +1556,7 @@ VariantObject *Utils::getChatForAddress(QString address) {
data->makeRequest([address, data]() { data->makeRequest([address, data]() {
auto linAddr = ToolModel::interpretUrl(address); auto linAddr = ToolModel::interpretUrl(address);
if (!linAddr) return QVariant(); if (!linAddr) return QVariant();
linAddr->clean();
auto linphoneChatRoom = ToolModel::lookupChatForAddress(linAddr); auto linphoneChatRoom = ToolModel::lookupChatForAddress(linAddr);
if (linphoneChatRoom) { if (linphoneChatRoom) {
auto chatCore = ChatCore::create(linphoneChatRoom); auto chatCore = ChatCore::create(linphoneChatRoom);
@ -1584,6 +1585,28 @@ VariantObject *Utils::getChatForAddress(QString address) {
return data; return data;
} }
VariantObject *Utils::createGroupChat(QString subject, QStringList participantAddresses) {
VariantObject *data = new VariantObject("lookupCurrentCallChat");
if (!data) return nullptr;
data->makeRequest([subject, participantAddresses, data]() {
std::list<std::shared_ptr<linphone::Address>> addresses;
for (auto &addr : participantAddresses) {
auto linAddr = ToolModel::interpretUrl(addr);
if (linAddr) addresses.push_back(linAddr);
else lWarning() << "Could not interpret address" << addr;
}
auto linphoneChatRoom = ToolModel::createGroupChatRoom(subject, addresses);
if (linphoneChatRoom) {
auto chatCore = ChatCore::create(linphoneChatRoom);
return QVariant::fromValue(new ChatGui(chatCore));
} else {
return QVariant();
}
});
data->requestValue();
return data;
}
void Utils::openChat(ChatGui *chat) { void Utils::openChat(ChatGui *chat) {
auto mainWindow = getMainWindow(); auto mainWindow = getMainWindow();
smartShowWindow(mainWindow); smartShowWindow(mainWindow);

View file

@ -148,6 +148,7 @@ public:
Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call); Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call);
Q_INVOKABLE static VariantObject *getChatForAddress(QString address); Q_INVOKABLE static VariantObject *getChatForAddress(QString address);
Q_INVOKABLE static VariantObject *createGroupChat(QString subject, QStringList participantAddresses);
Q_INVOKABLE static void openChat(ChatGui *chat); Q_INVOKABLE static void openChat(ChatGui *chat);
Q_INVOKABLE static bool isEmptyMessage(QString message); Q_INVOKABLE static bool isEmptyMessage(QString message);
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString &text, Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString &text,

View file

@ -22,6 +22,8 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Button/Settings/SwitchSetting.qml view/Control/Button/Settings/SwitchSetting.qml
view/Control/Container/Carousel.qml view/Control/Container/Carousel.qml
view/Control/Container/CreationFormLayout.qml
view/Control/Container/GroupCreationFormLayout.qml
view/Control/Container/DetailLayout.qml view/Control/Container/DetailLayout.qml
view/Control/Container/FormItemLayout.qml view/Control/Container/FormItemLayout.qml
view/Control/Container/ScrollBar.qml view/Control/Container/ScrollBar.qml
@ -107,6 +109,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Tool/Prototype/PhoneNumberPrototype.qml view/Control/Tool/Prototype/PhoneNumberPrototype.qml
view/Page/Form/Call/NewCallForm.qml view/Page/Form/Call/NewCallForm.qml
view/Page/Form/Chat/NewChatForm.qml
view/Page/Form/Chat/SelectedChatView.qml view/Page/Form/Chat/SelectedChatView.qml
view/Page/Form/Contact/ContactDescription.qml view/Page/Form/Contact/ContactDescription.qml
view/Page/Form/Contact/ContactEdition.qml view/Page/Form/Contact/ContactEdition.qml

View file

@ -0,0 +1,109 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import QtQuick.Effects
import Linphone
import UtilsCpp
import SettingsCpp
FocusScope {
id: mainItem
property color searchBarColor: DefaultStyle.grey_100
property color searchBarBorderColor: "transparent"
property alias searchBar: searchBar
property string startGroupButtonText
property NumericPadPopup numPadPopup
signal groupCreationRequested()
signal contactClicked(FriendGui contact)
clip: true
property alias topContent: topLayout.data
property bool topLayoutVisible: topLayout.children.length > 0
ColumnLayout {
anchors.fill: parent
spacing: Math.round(22 * DefaultStyle.dp)
ColumnLayout {
id: topLayout
visible: mainItem.topLayoutVisible
spacing: Math.round(18 * DefaultStyle.dp)
}
ColumnLayout {
onVisibleChanged: if (!visible && mainItem.numPadPopup) mainItem.numPadPopup.close()
spacing: Math.round(38 * DefaultStyle.dp)
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.rightMargin: Math.round(39 * DefaultStyle.dp)
focus: true
color: mainItem.searchBarColor
borderColor: mainItem.searchBarBorderColor
//: "Rechercher un contact"
placeholderText: qsTr("search_bar_look_for_contact_text")
numericPadPopup: mainItem.numPadPopup
KeyNavigation.down: grouCreationButton
}
ColumnLayout {
id: content
spacing: Math.round(32 * DefaultStyle.dp)
Button {
id: grouCreationButton
Layout.preferredWidth: Math.round(320 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(44 * DefaultStyle.dp)
padding: 0
KeyNavigation.up: searchBar
KeyNavigation.down: contactList
onClicked: mainItem.groupCreationRequested()
background: Rectangle {
anchors.fill: parent
radius: Math.round(50 * DefaultStyle.dp)
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.main2_100}
GradientStop { position: 1.0; color: DefaultStyle.grey_0}
}
}
contentItem: RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
Image {
source: AppIcons.groupCall
Layout.preferredWidth: Math.round(44 * DefaultStyle.dp)
sourceSize.width: Math.round(44 * DefaultStyle.dp)
fillMode: Image.PreserveAspectFit
}
Text {
text: mainItem.startGroupButtonText
color: DefaultStyle.grey_1000
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
Item {
Layout.fillWidth: true
}
EffectImage {
imageSource: AppIcons.rightArrow
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
colorizationColor: DefaultStyle.main2_500main
}
}
}
AllContactListView{
id: contactList
Layout.fillWidth: true
Layout.fillHeight: true
showContactMenu: false
searchBarText: searchBar.text
onContactSelected: (contact) => {
mainItem.contactClicked(contact)
}
}
}
}
}
}

View file

@ -0,0 +1,101 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import QtQuick.Effects
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
FocusScope {
id: mainItem
property alias addParticipantsLayout: addParticipantsLayout
property alias groupName: groupName
property string formTitle
property string createGroupButtonText
property int selectedParticipantsCount
signal returnRequested()
signal groupCreationRequested()
ColumnLayout {
spacing: 0
anchors.fill: parent
RowLayout {
spacing: Math.round(10 * DefaultStyle.dp)
Button {
id: backGroupCallButton
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.leftArrow
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
KeyNavigation.down: groupName
KeyNavigation.right: groupCallButton
KeyNavigation.left: groupCallButton
onClicked: {
mainItem.returnRequested()
}
}
Text {
text: mainItem.formTitle
color: DefaultStyle.main1_500_main
maximumLineCount: 1
font {
pixelSize: Math.round(18 * DefaultStyle.dp)
weight: Typography.h4.weight
}
Layout.fillWidth: true
}
SmallButton {
id: groupCallButton
enabled: mainItem.selectedParticipantsCount.length != 0
Layout.rightMargin: Math.round(21 * DefaultStyle.dp)
text: mainItem.createGroupButtonText
style: ButtonStyle.main
KeyNavigation.down: addParticipantsLayout
KeyNavigation.left: backGroupCallButton
KeyNavigation.right: backGroupCallButton
onClicked: {
mainItem.groupCreationRequested()
}
}
}
RowLayout {
spacing: 0
Layout.topMargin: Math.round(18 * DefaultStyle.dp)
Layout.rightMargin: Math.round(38 * DefaultStyle.dp)
Text {
font.pixelSize: Typography.p2.pixelSize
font.weight: Typography.p2.weight
//: "Nom du groupe"
text: qsTr("history_group_call_start_dialog_subject_hint")
}
Item {
Layout.fillWidth: true
}
Text {
font.pixelSize: Math.round(12 * DefaultStyle.dp)
font.weight: Math.round(300 * DefaultStyle.dp)
//: "Requis"
text: qsTr("required")
}
}
TextField {
id: groupName
Layout.fillWidth: true
Layout.rightMargin: Math.round(38 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(49 * DefaultStyle.dp)
focus: true
KeyNavigation.down: addParticipantsLayout //participantList.count > 0 ? participantList : searchbar
}
AddParticipantsForm {
id: addParticipantsLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: Math.round(15 * DefaultStyle.dp)
onSelectedParticipantsCountChanged: mainItem.selectedParticipantsCount = selectedParticipantsCount
focus: true
}
}
}

View file

@ -17,6 +17,9 @@ ListView {
property string searchText: searchBar?.text property string searchText: searchBar?.text
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp) property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
property ChatGui currentChatGui
onCurrentIndexChanged: currentChatGui = model.getAt(currentIndex) || null
signal resultsReceived signal resultsReceived
onResultsReceived: { onResultsReceived: {
@ -43,6 +46,10 @@ ListView {
mainItem.currentIndex = -1 mainItem.currentIndex = -1
mainItem.currentIndex = indexToSelect mainItem.currentIndex = indexToSelect
} }
onLayoutChanged: {
var chatToSelect = getAt(mainItem.currentIndex)
selectChat(mainItem.currentChatGui)
}
} }
// flickDeceleration: 10000 // flickDeceleration: 10000
spacing: Math.round(10 * DefaultStyle.dp) spacing: Math.round(10 * DefaultStyle.dp)
@ -50,6 +57,14 @@ ListView {
function selectChat(chatGui) { function selectChat(chatGui) {
var index = chatProxy.findChatIndex(chatGui) var index = chatProxy.findChatIndex(chatGui)
mainItem.currentIndex = index mainItem.currentIndex = index
// if the chat exists, it may not be displayed
// in list if hide_empty_chatrooms is set. Thus, we need
// to force adding it in the list so it is displayed
if (index === -1 && chatGui) {
chatProxy.addChatInList(chatGui)
var index = chatProxy.findChatIndex(chatGui)
mainItem.currentIndex = index
}
} }
Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items
@ -67,9 +82,6 @@ ListView {
chatProxy.displayMore() chatProxy.displayMore()
} }
} }
onCountChanged: {
if (count > 0 && currentIndex < 0) currentIndex = 0
}
//---------------------------------------------------------------- //----------------------------------------------------------------
function moveToCurrentItem() { function moveToCurrentItem() {

View file

@ -4,7 +4,7 @@ import QtQuick.Controls.Basic as Control
import Linphone import Linphone
import UtilsCpp 1.0 import UtilsCpp 1.0
import ConstantsCpp 1.0 import ConstantsCpp
import SettingsCpp import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle

View file

@ -25,7 +25,7 @@ Notification {
property string message: notificationData ? notificationData.message : "" property string message: notificationData ? notificationData.message : ""
Connections { Connections {
enabled: chat enabled: chat
target: chat.core target: chat ? chat.core : null
function onMessageOpen() { function onMessageOpen() {
close() close()
} }

View file

@ -7,131 +7,43 @@ import Linphone
import UtilsCpp import UtilsCpp
import SettingsCpp import SettingsCpp
FocusScope { CreationFormLayout {
id: mainItem id: mainItem
property bool groupCallVisible property bool groupCallVisible
property bool displayCurrentCalls: false property bool displayCurrentCalls: false
property color searchBarColor: DefaultStyle.grey_100
property color searchBarBorderColor: "transparent"
property alias searchBar: searchBar
property NumericPadPopup numPadPopup
signal callButtonPressed(string address)
signal groupCallCreationRequested()
signal transferCallToAnotherRequested(CallGui dest) signal transferCallToAnotherRequested(CallGui dest)
signal contactClicked(FriendGui contact)
clip: true
ColumnLayout { //: Appel de groupe
anchors.fill: parent startGroupButtonText: qsTr("call_start_group_call_title")
spacing: Math.round(22 * DefaultStyle.dp)
ColumnLayout {
spacing: Math.round(18 * DefaultStyle.dp)
visible: mainItem.displayCurrentCalls && callList.count > 0
Text {
//: "Appels en cours"
text: qsTr("call_transfer_active_calls_label")
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: callListBackground.height
Layout.maximumHeight: mainItem.height/2
contentHeight: callListBackground.height
contentWidth: width
RoundedPane {
id: callListBackground
anchors.left: parent.left
anchors.right: parent.right
contentItem: CallListView {
id: callList
isTransferList: true
onTransferCallToAnotherRequested: (dest) => {
mainItem.transferCallToAnotherRequested(dest)
}
}
}
}
}
ColumnLayout { topLayoutVisible: mainItem.displayCurrentCalls && callList.count > 0
onVisibleChanged: if (!visible) mainItem.numPadPopup.close() topContent: [
spacing: Math.round(38 * DefaultStyle.dp) Text {
SearchBar { //: "Appels en cours"
id: searchBar text: qsTr("call_transfer_active_calls_label")
Layout.alignment: Qt.AlignTop font {
Layout.fillWidth: true pixelSize: Typography.h4.pixelSize
Layout.rightMargin: Math.round(39 * DefaultStyle.dp) weight: Typography.h4.weight
focus: true
color: mainItem.searchBarColor
borderColor: mainItem.searchBarBorderColor
//: "Rechercher un contact"
placeholderText: qsTr("search_bar_look_for_contact_text")
numericPadPopup: mainItem.numPadPopup
KeyNavigation.down: grouCallButton
} }
ColumnLayout { },
id: content Flickable {
spacing: Math.round(32 * DefaultStyle.dp) Layout.fillWidth: true
Button { Layout.preferredHeight: callListBackground.height
id: grouCallButton Layout.maximumHeight: mainItem.height/2
visible: mainItem.groupCallVisible && !SettingsCpp.disableMeetingsFeature contentHeight: callListBackground.height
Layout.preferredWidth: Math.round(320 * DefaultStyle.dp) contentWidth: width
Layout.preferredHeight: Math.round(44 * DefaultStyle.dp) RoundedPane {
padding: 0 id: callListBackground
KeyNavigation.up: searchBar anchors.left: parent.left
KeyNavigation.down: contactList anchors.right: parent.right
onClicked: mainItem.groupCallCreationRequested() contentItem: CallListView {
background: Rectangle { id: callList
anchors.fill: parent isTransferList: true
radius: Math.round(50 * DefaultStyle.dp) onTransferCallToAnotherRequested: (dest) => {
gradient: Gradient { mainItem.transferCallToAnotherRequested(dest)
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: DefaultStyle.main2_100}
GradientStop { position: 1.0; color: DefaultStyle.grey_0}
}
}
contentItem: RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
Image {
source: AppIcons.groupCall
Layout.preferredWidth: Math.round(44 * DefaultStyle.dp)
sourceSize.width: Math.round(44 * DefaultStyle.dp)
fillMode: Image.PreserveAspectFit
}
Text {
text: qsTr("call_start_group_call_title")
color: DefaultStyle.grey_1000
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
Item {
Layout.fillWidth: true
}
EffectImage {
imageSource: AppIcons.rightArrow
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
colorizationColor: DefaultStyle.main2_500main
}
}
}
AllContactListView{
id: contactList
Layout.fillWidth: true
Layout.fillHeight: true
showContactMenu: false
searchBarText: searchBar.text
onContactSelected: (contact) => {
mainItem.contactClicked(contact)
} }
} }
} }
} }
} ]
} }

View file

@ -0,0 +1,15 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import QtQuick.Effects
import Linphone
import UtilsCpp
import SettingsCpp
CreationFormLayout {
id: mainItem
//: Nouveau groupe
startGroupButtonText: qsTr("chat_start_group_chat_title")
}

View file

@ -0,0 +1,15 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import QtQuick.Effects
import Linphone
import UtilsCpp
import SettingsCpp
CreationFormLayout {
id: mainItem
//: Nouveau groupe
startGroupButtonText: qsTr("chat_start_group_chat_title")
}

View file

@ -25,52 +25,94 @@ FocusScope{
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
spacing: Math.round(15 * DefaultStyle.dp) spacing: Math.round(15 * DefaultStyle.dp)
ListView { GridView {
id: participantList id: participantList
Layout.fillWidth: true Layout.fillWidth: true
visible: contentHeight > 0 visible: contentHeight > 0
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
Layout.maximumHeight: mainItem.height / 3 Layout.maximumHeight: mainItem.height / 3
width: mainItem.width width: mainItem.width
cellWidth: Math.round((50 + 18) * DefaultStyle.dp)
cellHeight: Math.round(80 * DefaultStyle.dp)
// columnCount: Math.floor(width/cellWidth)
model: mainItem.selectedParticipants model: mainItem.selectedParticipants
clip: true clip: true
// columnSpacing: Math.round(18 * DefaultStyle.dp)
// rowSpacing: Math.round(9 * DefaultStyle.dp)
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if(currentIndex <=0 && event.key == Qt.Key_Up){ if(currentIndex <=0 && event.key == Qt.Key_Up){
nextItemInFocusChain(false).forceActiveFocus() nextItemInFocusChain(false).forceActiveFocus()
} }
} }
header: Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
visible: count > 0
//: "%n participant(s) sélectionné(s)"
text: qsTr("add_participant_selected_count", '', count).arg(count)
maximumLineCount: 1
color: DefaultStyle.grey_1000
font {
pixelSize: Math.round(12 * DefaultStyle.dp)
weight: Math.round(300 * DefaultStyle.dp)
}
}
delegate: FocusScope { delegate: FocusScope {
height: Math.round(56 * DefaultStyle.dp) ColumnLayout {
width: participantList.width - scrollbar.implicitWidth - Math.round(28 * DefaultStyle.dp)
RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: Math.round(10 * DefaultStyle.dp) spacing: Math.round(4 * DefaultStyle.dp)
Avatar { width: Math.round(50 * DefaultStyle.dp)
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) Item {
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) Layout.alignment: Qt.AlignHCenter
_address: modelData Layout.preferredWidth: Math.round(50 * DefaultStyle.dp)
shadowEnabled: false Layout.preferredHeight: Math.round(50 * DefaultStyle.dp)
Avatar {
anchors.fill: parent
_address: modelData
shadowEnabled: false
}
Button {
Layout.preferredWidth: Math.round(17 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(17 * DefaultStyle.dp)
icon.width: Math.round(12 * DefaultStyle.dp)
icon.height: Math.round(12 * DefaultStyle.dp)
icon.source: AppIcons.closeX
anchors.top: parent.top
anchors.right: parent.right
background: Item {
Rectangle {
id: backgroundRect
color: DefaultStyle.grey_0
anchors.fill: parent
radius: Math.round(50 * DefaultStyle.dp)
}
MultiEffect {
anchors.fill: backgroundRect
source: backgroundRect
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 0.1
shadowOpacity: 0.5
}
}
onClicked: contactList.removeSelectedContactByAddress(modelData)
}
} }
Text { Text {
Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: width
width: Math.round(50 * DefaultStyle.dp)
maximumLineCount: 1 maximumLineCount: 1
clip: true
property var nameObj: UtilsCpp.getDisplayName(modelData) property var nameObj: UtilsCpp.getDisplayName(modelData)
text: nameObj ? nameObj.value : "" text: nameObj ? nameObj.value : ""
font.pixelSize: Math.round(14 * DefaultStyle.dp) color: DefaultStyle.main2_700
font.capitalization: Font.Capitalize wrapMode: Text.WrapAnywhere
} font {
Item { pixelSize: Typography.p3.pixelSize
Layout.fillWidth: true weight: Typography.p3.weight
} capitalization: Font.Capitalize
Button { }
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.closeX
icon.width: Math.round(24 * DefaultStyle.dp)
icon.height: Math.round(24 * DefaultStyle.dp)
focus: true
onClicked: contactList.removeSelectedContactByAddress(modelData)
} }
} }
} }

View file

@ -35,7 +35,6 @@ AbstractMainPage {
property bool isRegistered: account ? account.core?.registrationState property bool isRegistered: account ? account.core?.registrationState
== LinphoneEnums.RegistrationState.Ok : false == LinphoneEnums.RegistrationState.Ok : false
property int selectedParticipantsCount property int selectedParticipantsCount
signal startGroupCallRequested
signal createCallFromSearchBarRequested signal createCallFromSearchBarRequested
signal createContactRequested(string name, string address) signal createContactRequested(string name, string address)
signal openNumPadRequest signal openNumPadRequest
@ -370,7 +369,7 @@ AbstractMainPage {
onContactClicked: contact => { onContactClicked: contact => {
mainWindow.startCallWithContact(contact, false, callContactsList) mainWindow.startCallWithContact(contact, false, callContactsList)
} }
onGroupCallCreationRequested: { onGroupCreationRequested: {
console.log("groupe call requetsed") console.log("groupe call requetsed")
listStackView.push(groupCallItem) listStackView.push(groupCallItem)
} }
@ -387,129 +386,30 @@ AbstractMainPage {
Component { Component {
id: groupCallItem id: groupCallItem
FocusScope { GroupCreationFormLayout {
objectName: "groupCallItem" objectName: "groupCallItem"
//: "Appel de groupe"
formTitle: qsTr("call_start_group_call_title")
//: "Lancer"
createGroupButtonText: qsTr("call_action_start_group_call")
Control.StackView.onActivated: { Control.StackView.onActivated: {
addParticipantsLayout.forceActiveFocus() addParticipantsLayout.forceActiveFocus()
} }
ColumnLayout { onReturnRequested: {
spacing: 0 listStackView.pop()
anchors.fill: parent listStackView.currentItem?.forceActiveFocus()
RowLayout { }
spacing: Math.round(10 * DefaultStyle.dp) onGroupCreationRequested: {
visible: !SettingsCpp.disableMeetingsFeature if (groupName.text.length === 0) {
Button { UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
id: backGroupCallButton //: "Un nom doit être donné à l'appel de groupe
style: ButtonStyle.noBackgroundOrange qsTr("group_call_error_must_have_name"), false)
icon.source: AppIcons.leftArrow } else if (!mainItem.isRegistered) {
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) //: "Vous n'etes pas connecté"
KeyNavigation.down: listStackView qsTr("group_call_error_not_connected"), false)
KeyNavigation.right: groupCallButton } else {
KeyNavigation.left: groupCallButton UtilsCpp.createGroupCall(groupName.text, addParticipantsLayout.selectedParticipants)
onClicked: {
listStackView.pop()
listStackView.currentItem?.forceActiveFocus()
}
}
ColumnLayout {
spacing: Math.round(3 * DefaultStyle.dp)
Text {
//: "Appel de groupe"
text: qsTr("call_start_group_call_title")
color: DefaultStyle.main1_500_main
maximumLineCount: 1
font {
pixelSize: Math.round(18 * DefaultStyle.dp)
weight: Typography.h4.weight
}
Layout.fillWidth: true
}
Text {
//: "%n participant(s) sélectionné(s)"
text: qsTr("group_call_participant_selected", '', mainItem.selectedParticipantsCount).arg(mainItem.selectedParticipantsCount)
color: DefaultStyle.main2_500main
maximumLineCount: 1
font {
pixelSize: Math.round(12 * DefaultStyle.dp)
weight: Math.round(300 * DefaultStyle.dp)
}
Layout.fillWidth: true
}
}
SmallButton {
id: groupCallButton
enabled: mainItem.selectedParticipantsCount.length != 0
Layout.rightMargin: Math.round(21 * DefaultStyle.dp)
//: "Lancer"
text: qsTr("call_action_start_group_call")
style: ButtonStyle.main
KeyNavigation.down: listStackView
KeyNavigation.left: backGroupCallButton
KeyNavigation.right: backGroupCallButton
onClicked: {
mainItem.startGroupCallRequested()
}
}
}
RowLayout {
spacing: 0
Layout.topMargin: Math.round(18 * DefaultStyle.dp)
Layout.rightMargin: Math.round(38 * DefaultStyle.dp)
Text {
font.pixelSize: Typography.p2.pixelSize
font.weight: Typography.p2.weight
//: "Nom du groupe"
text: qsTr("history_group_call_start_dialog_subject_hint")
}
Item {
Layout.fillWidth: true
}
Text {
font.pixelSize: Math.round(12 * DefaultStyle.dp)
font.weight: Math.round(300 * DefaultStyle.dp)
//: "Requis"
text: qsTr("required")
}
}
TextField {
id: groupCallName
Layout.fillWidth: true
Layout.rightMargin: Math.round(38 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(49 * DefaultStyle.dp)
focus: true
KeyNavigation.down: addParticipantsLayout //participantList.count > 0 ? participantList : searchbar
}
AddParticipantsForm {
id: addParticipantsLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: Math.round(15 * DefaultStyle.dp)
onSelectedParticipantsCountChanged: mainItem.selectedParticipantsCount = selectedParticipantsCount
focus: true
Connections {
target: mainItem
function onStartGroupCallRequested() {
if (groupCallName.text.length === 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Un nom doit être donné à l'appel de groupe
qsTr("group_call_error_must_have_name"), false)
} else if (!mainItem.isRegistered) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Vous n'etes pas connecté"
qsTr("group_call_error_not_connected"), false)
} else {
// mainItem.confInfoGui = Qt.createQmlObject(
// "import Linphone
// ConferenceInfoGui{}", mainItem)
// mainItem.confInfoGui.core.subject = groupCallName.text
// mainItem.confInfoGui.core.isScheduled = false
// mainItem.confInfoGui.core.addParticipants(addParticipantsLayout.selectedParticipants)
// mainItem.confInfoGui.core.save()
UtilsCpp.createGroupCall(groupCallName.text, addParticipantsLayout.selectedParticipants)
}
}
}
} }
} }
} }

View file

@ -16,24 +16,41 @@ AbstractMainPage {
emptyListText: qsTr("chat_empty_title") emptyListText: qsTr("chat_empty_title")
newItemIconSource: AppIcons.plusCircle newItemIconSource: AppIcons.plusCircle
property var selectedChatGui property AccountProxy accounts: AccountProxy {
id: accountProxy
sourceModel: AppCpp.accounts
}
property AccountGui account: accountProxy.defaultAccount
property var state: account && account.core?.registrationState || 0
property bool isRegistered: account ? account.core?.registrationState
== LinphoneEnums.RegistrationState.Ok : false
property var selectedChatGui
property string remoteAddress property string remoteAddress
onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress) onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress)
property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress) property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress)
property ChatGui remoteChat: remoteChatObj && remoteChatObj.value ? remoteChatObj.value : null property var remoteChat: remoteChatObj ? remoteChatObj.value : null
onRemoteChatChanged: if (remoteChat) selectedChatGui = remoteChat onRemoteChatChanged: {
selectedChatGui = remoteChat
}
onSelectedChatGuiChanged: { onSelectedChatGuiChanged: {
if (selectedChatGui) if (selectedChatGui) {
if (!listStackView.currentItem || listStackView.currentItem.objectName !== "chatListItem") {
listStackView.popToIndex(0)
if (listStackView.depth === 0 || listStackView.currentItem.objectName !== "chatListItem") listStackView.push(chatListItem)
}
rightPanelStackView.replace(currentChatComp, rightPanelStackView.replace(currentChatComp,
Control.StackView.Immediate) Control.StackView.Immediate)
else }
else {
rightPanelStackView.replace(emptySelection, rightPanelStackView.replace(emptySelection,
Control.StackView.Immediate) Control.StackView.Immediate)
}
} }
rightPanelStackView.initialItem: emptySelection rightPanelStackView.initialItem: emptySelection
rightPanelStackView.visible: listStackView.currentItem && listStackView.currentItem.objectName === "chatListItem"
onNoItemButtonPressed: goToNewChat() onNoItemButtonPressed: goToNewChat()
@ -206,36 +223,79 @@ AbstractMainPage {
//: "New chat" //: "New chat"
text: qsTr("chat_action_start_new_chat") text: qsTr("chat_action_start_new_chat")
color: DefaultStyle.main2_700 color: DefaultStyle.main2_700
font.pixelSize: Typography.h2.pixelSize font.pixelSize: Typography.h2m.pixelSize
font.weight: Typography.h2.weight font.weight: Typography.h2m.weight
} }
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
// NewCallForm { NewChatForm {
// id: callContactsList id: newChatForm
// Layout.topMargin: Math.round(18 * DefaultStyle.dp) Layout.fillWidth: true
// Layout.fillWidth: true Layout.fillHeight: true
// Layout.fillHeight: true Layout.topMargin: Math.round(18 * DefaultStyle.dp)
// focus: true onGroupCreationRequested: {
// numPadPopup: numericPadPopupItem console.log("groupe call requetsed")
// groupCallVisible: true listStackView.push(groupChatItem)
// searchBarColor: DefaultStyle.grey_100 }
// onContactClicked: contact => { onContactClicked: (contact) => {
// mainWindow.startCallWithContact(contact, false, callContactsList) if (contact) {
// } mainItem.remoteAddress = ""
// onGroupCallCreationRequested: { mainItem.remoteAddress = contact.core.defaultAddress
// console.log("groupe call requetsed") }
// listStackView.push(groupCallItem) }
// } }
// Connections { }
// target: mainItem }
// function onCreateCallFromSearchBarRequested() { }
// UtilsCpp.createCall(callContactsList.searchBar.text)
// } Component {
// } id: groupChatItem
// } GroupCreationFormLayout {
id: chatCreationLayout
objectName: "groupChatItem"
//: "Nouveau groupe"
formTitle: qsTr("chat_start_group_chat_title")
//: "Créer"
createGroupButtonText: qsTr("chat_action_start_group_chat")
property var groupChatObj
property var groupChat: groupChatObj ? groupChatObj.value : null
onGroupChatChanged: if (groupChat && groupChat.core.state === LinphoneEnums.ChatRoomState.Created) {
mainItem.selectedChatGui = groupChat
}
Connections {
enabled: groupChat || false
target: groupChat?.core || null
function onChatRoomStateChanged() {
if (chatCreationLayout.groupChat.core.state === LinphoneEnums.ChatRoomState.Created) {
mainItem.selectedChatGui = chatCreationLayout.groupChat
}
}
}
Control.StackView.onActivated: {
addParticipantsLayout.forceActiveFocus()
}
onReturnRequested: {
listStackView.pop()
listStackView.currentItem?.forceActiveFocus()
}
onGroupCreationRequested: {
if (groupName.text.length === 0) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Un nom doit être donné au groupe
qsTr("group_chat_error_must_have_name"), false)
} else if (!mainItem.isRegistered) {
UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"),
//: "Vous n'etes pas connecté"
qsTr("group_call_error_not_connected"), false)
} else {
console.log("create group chat")
chatCreationLayout.groupChatObj = UtilsCpp.createGroupChat(chatCreationLayout.groupName.text, addParticipantsLayout.selectedParticipants)
}
} }
} }
} }

View file

@ -16,6 +16,13 @@ QtObject {
pixelSize: Math.round(22 * DefaultStyle.dp), pixelSize: Math.round(22 * DefaultStyle.dp),
weight: Math.min(Math.round(800 * DefaultStyle.dp), 1000) weight: Math.min(Math.round(800 * DefaultStyle.dp), 1000)
}) })
// Title/H2M - Large bloc title
property font h2m: Qt.font( {
family: DefaultStyle.defaultFont,
pixelSize: Math.round(20 * DefaultStyle.dp),
weight: Math.min(Math.round(800 * DefaultStyle.dp), 1000)
})
// Title/H2 - Large bloc title // Title/H2 - Large bloc title
property font h2: Qt.font( { property font h2: Qt.font( {