From 1e5ec027bbd824296d2a0f110e95b8ea8ddc33cf Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Tue, 8 Jul 2025 15:32:00 +0200 Subject: [PATCH] forward message --- Linphone/core/chat/ChatCore.cpp | 24 +- Linphone/core/chat/ChatProxy.cpp | 1 + Linphone/data/languages/de.ts | 287 +++++------- Linphone/data/languages/en.ts | 185 +++++--- Linphone/data/languages/fr_FR.ts | 183 +++++--- Linphone/model/chat/ChatModel.cpp | 5 + Linphone/model/chat/ChatModel.hpp | 1 + Linphone/tool/Utils.cpp | 42 +- Linphone/tool/Utils.hpp | 2 + .../Control/Display/Chat/ChatListView.qml | 2 + .../view/Control/Display/Chat/ChatMessage.qml | 36 ++ .../Display/Chat/ChatMessagesListView.qml | 4 +- .../Display/Contact/ContactListItem.qml | 427 +++++++++--------- .../Display/Contact/ContactListView.qml | 8 +- .../view/Page/Form/Chat/SelectedChatView.qml | 155 +++++-- .../Layout/Chat/MessageImdnStatusInfos.qml | 2 +- .../Page/Layout/Chat/MessageInfosLayout.qml | 2 + Linphone/view/Page/Main/Chat/ChatPage.qml | 5 +- 18 files changed, 805 insertions(+), 566 deletions(-) diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 75aaa530c..7aacb0ade 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -196,19 +196,17 @@ void ChatCore::setSelf(QSharedPointer me) { }); // Events (excluding messages) - mChatModelConnection->makeConnectToModel(&ChatModel::newEvent, - [this](const std::shared_ptr &chatRoom, - const std::shared_ptr &eventLog) { - if (mChatModel->getMonitor() != chatRoom) return; - qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle(); - auto event = EventLogCore::create(eventLog); - if (event->isHandled()) { - mChatModelConnection->invokeToCore([this, event]() { - appendEventLogToEventLogList(event); - emit lUpdateLastUpdatedTime(); - }); - } - }); + mChatModelConnection->makeConnectToModel( + &ChatModel::newEvent, [this](const std::shared_ptr &chatRoom, + const std::shared_ptr &eventLog) { + if (mChatModel->getMonitor() != chatRoom) return; + qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle(); + auto event = EventLogCore::create(eventLog); + if (event->isHandled()) { + mChatModelConnection->invokeToCore([this, event]() { appendEventLogToEventLogList(event); }); + } + mChatModelConnection->invokeToCore([this, event]() { emit lUpdateLastUpdatedTime(); }); + }); // Chat messages mChatModelConnection->makeConnectToModel( diff --git a/Linphone/core/chat/ChatProxy.cpp b/Linphone/core/chat/ChatProxy.cpp index 12c16279d..47d683415 100644 --- a/Linphone/core/chat/ChatProxy.cpp +++ b/Linphone/core/chat/ChatProxy.cpp @@ -45,6 +45,7 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) { [this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved); connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); + connect(newChatList, &ChatList::dataChanged, this, [this] { invalidate(); }); } auto firstList = new SortFilterList(model, Qt::AscendingOrder); firstList->setDynamicSortFilter(true); diff --git a/Linphone/data/languages/de.ts b/Linphone/data/languages/de.ts index e5624b5d7..4680d6ff2 100644 --- a/Linphone/data/languages/de.ts +++ b/Linphone/data/languages/de.ts @@ -691,7 +691,7 @@ media_encryption_dtls DTLS - + DTLS @@ -703,7 +703,7 @@ media_encryption_srtp SRTP - + SRTP @@ -788,26 +788,6 @@ CallHistoryLayout - - contact_presence_status_online - "En ligne" - Online - - - contact_presence_status_busy - "Occupé" - Beschäftigt - - - contact_presence_status_do_not_disturb - "Ne pas déranger" - Nicht stören - - - contact_presence_status_offline - "Hors ligne" - Offline - meeting_info_join_title @@ -1054,16 +1034,6 @@ "Lancer" Starten - - history_group_call_start_dialog_subject_hint - "Nom du groupe" - Gruppenname - - - required - "Requis" - Erforderlich - @@ -1758,47 +1728,47 @@ ChatListView - + chat_message_is_writing_info %1 is writing… - + chat_message_draft_sending_text - + chat_room_delete "Delete" - + chat_room_mute - + chat_room_unmute "Mute" - + chat_room_mark_as_read "Mark as read" - + chat_list_delete_chat_popup_title Delete the conversation ? - + chat_list_delete_chat_popup_message This conversation and all its messages will be deleted. Do You want to continue ? @@ -1807,67 +1777,79 @@ ChatMessage - + chat_message_copy_selection "Copy selection" - + chat_message_copy "Copy" - + chat_message_copied_to_clipboard_title Copied - + chat_message_copied_to_clipboard_toast "to clipboard" - + chat_message_remote_replied %1 replied - + + chat_message_forward + Forward + + + + chat_message_remote_replied_to %1 replied to %2 - + + chat_message_forwarded + Forwarded + + + + chat_message_user_replied_to You replied to %1 - + chat_message_user_replied You replied - + chat_message_reception_info "Reception info" - + chat_message_reply Reply - + chat_message_delete "Delete" @@ -1963,13 +1945,13 @@ Error - + info_toast_deleted_title Deleted - + info_toast_deleted_message The message has been deleted @@ -2022,20 +2004,20 @@ Error ChatMessagesListView - + chat_message_list_encrypted_header_title End to end encrypted chat - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_is_writing_info %1 is writing… @@ -2098,37 +2080,37 @@ Error - + chat_action_start_new_chat "New chat" - + chat_start_group_chat_title "Nouveau groupe" - + chat_action_start_group_chat "Créer" - - + + information_popup_error_title - + group_chat_error_must_have_name "Un nom doit être donné au groupe - + group_call_error_not_connected "Vous n'etes pas connecté" Sie sind nicht verbunden @@ -2205,26 +2187,6 @@ Error Contact - - drawer_menu_account_connection_status_connected - "Connecté" - Verbunden - - - drawer_menu_account_connection_status_cleared - "Désactivé" - Deaktiviert - - - drawer_menu_account_connection_status_refreshing - "Connexion…" - Verbinden… - - - drawer_menu_account_connection_status_failed - "Erreur" - Fehler - information_popup_error_title @@ -2330,53 +2292,53 @@ Error ContactListItem - + contact_details_remove_from_favourites "Enlever des favoris" Aus Favoriten entfernen - + contact_details_add_to_favourites "Ajouter aux favoris" Zu Favoriten hinzufügen - + Partager Teilen - + information_popup_error_title Fehler - + information_popup_vcard_creation_error La création du fichier vcard a échoué VCard-Erstellung fehlgeschlagen - + information_popup_vcard_creation_title VCard créée VCard erstellt - + information_popup_vcard_creation_success "VCard du contact enregistrée dans %1" VCard in %1 gespeichert - + contact_sharing_email_title Partage de contact Kontakt teilen - + contact_details_delete "Supprimer" Löschen @@ -2538,26 +2500,6 @@ Error "Appel vidéo" Videoanruf - - contact_presence_status_online - "En ligne" - Online - - - contact_presence_status_busy - "Occupé" - Beschäftigt - - - contact_presence_status_do_not_disturb - "Ne pas déranger" - Nicht stören - - - contact_presence_status_offline - "Hors ligne" - Offline - contact_details_numbers_and_addresses_title @@ -3159,11 +3101,6 @@ Error Medias & documents Medien & Dokumente - - group_infos_shared_media - Shared medias - Geteilte Medien - group_infos_shared_docs @@ -4006,10 +3943,6 @@ Error meeting_schedule_add_participants_apply Apply - - add - Hinzufügen - group_call_participant_selected @@ -4145,11 +4078,6 @@ Error "Appels en cours" Laufender Anruf - - search_bar_look_for_contact_text - "Rechercher un contact" - Kontakt suchen - call_start_group_call_title @@ -4477,22 +4405,10 @@ Error QObject - - DTLS - DTLS - - - None - Nichts - - - SRTP - SRTP - media_encryption_dtls - + DTLS @@ -4502,7 +4418,7 @@ Error media_encryption_srtp - + SRTP @@ -4949,28 +4865,40 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - + chat_view_group_call_toast_message Start a group call ? - + reply_to_label Reply to %1 - + shared_medias_title Shared medias - + shared_documents_title Shared documents + + + forward_to_title + Transfer to... + + + + + conversations_title + Conversations + + SettingsPage @@ -5109,7 +5037,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. Utils - + nMinute @@ -5117,7 +5045,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nHour @@ -5125,8 +5053,8 @@ Pour les activer dans un projet commercial, merci de nous contacter. - - + + nDay @@ -5134,7 +5062,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nWeek @@ -5142,7 +5070,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nSeconds @@ -5273,42 +5201,73 @@ Failed to create 1-1 conversation with %1 ! Offline - - chatMessage_error - Cannot reply to invalid message - - - - + recorder_error Error with the recorder - + + chat_error + + + chat_message_forward_error + Cannot forward an invalid message + + - - - + + + + + info_popup_error_title Error - + info_popup_forward_message_error + Could not forward message : %1 + + + + + info_popup_send_forward_message_error_message + Failed to create forward message + + + + + chat_message_reply_error + Cannot reply to invalid message + + + + + info_popup_reply_message_error + Could not send reply message : %1 + + + + + info_popup_send_reply_message_error_message + Failed to create reply message + + + + info_popup_send_voice_message_error_message Could not send voice message : %1 - - + info_popup_send_voice_message_sending_error_message Failed to create message from record diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 4e0544922..9f96e5135 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -1720,47 +1720,47 @@ ChatListView - + chat_message_is_writing_info %1 is writing… %1 is writing… - + chat_message_draft_sending_text Draft : %1 - + chat_room_delete "Delete" Delete - + chat_room_mute Mute - + chat_room_unmute "Mute" Unmute - + chat_room_mark_as_read "Mark as read" Mark as read - + chat_list_delete_chat_popup_title Delete the conversation ? Delete the conversation ? - + chat_list_delete_chat_popup_message This conversation and all its messages will be deleted. Do You want to continue ? This conversation and all its messages will be deleted. Do You want to continue ? @@ -1769,67 +1769,79 @@ ChatMessage - + chat_message_copy_selection "Copy selection" Copy selection - + chat_message_copy "Copy" Copy - + chat_message_copied_to_clipboard_title Copied Copied - + chat_message_copied_to_clipboard_toast "to clipboard" in clipboard - + chat_message_remote_replied %1 replied %1 replied to - + + chat_message_forward + Forward + Forward + + + chat_message_remote_replied_to %1 replied to %2 %1 replied to %2 - + + chat_message_forwarded + Forwarded + Forwarded + + + chat_message_user_replied_to You replied to %1 You replied to %1 - + chat_message_user_replied You replied You replied - + chat_message_reception_info "Reception info" Reception info - + chat_message_reply Reply Reply - + chat_message_delete "Delete" Delete @@ -1925,13 +1937,13 @@ Error Reactions - + info_toast_deleted_title Deleted Deleted - + info_toast_deleted_message The message has been deleted The message has been deleted @@ -1984,13 +1996,13 @@ Error ChatMessagesListView - + chat_message_list_encrypted_header_title End to end encrypted chat End to end encrypted chat - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. @@ -1998,7 +2010,7 @@ Error Only your correspondent can decrypt them. - + chat_message_is_writing_info %1 is writing… %1 is writing… @@ -2061,37 +2073,37 @@ Only your correspondent can decrypt them. No conversation in history - + chat_action_start_new_chat "New chat" New conversation - + chat_start_group_chat_title "Nouveau groupe" New group - + chat_action_start_group_chat "Créer" Create - - + + information_popup_error_title Error - + group_chat_error_must_have_name "Un nom doit être donné au groupe A name must be set for the group - + group_call_error_not_connected "Vous n'etes pas connecté" You are not connected @@ -2273,53 +2285,53 @@ Only your correspondent can decrypt them. ContactListItem - + contact_details_remove_from_favourites "Enlever des favoris" Remove from favorites - + contact_details_add_to_favourites "Ajouter aux favoris" Add to favorites - + Partager Share - + information_popup_error_title Error - + information_popup_vcard_creation_error La création du fichier vcard a échoué VCard creation failed - + information_popup_vcard_creation_title VCard créée VCard created - + information_popup_vcard_creation_success "VCard du contact enregistrée dans %1" VCard has been saved in %1 - + contact_sharing_email_title Partage de contact Share contact - + contact_details_delete "Supprimer" Delete @@ -4843,28 +4855,40 @@ To enable them in a commercial project, please contact us. SelectedChatView - + chat_view_group_call_toast_message Start a group call ? - + reply_to_label Reply to %1 Reply to %1 - + shared_medias_title Shared medias Shared medias - + shared_documents_title Shared documents Shared documents + + + forward_to_title + Transfer to... + Transfer to… + + + + conversations_title + Conversations + Conversations + SettingsPage @@ -5003,7 +5027,7 @@ To enable them in a commercial project, please contact us. Utils - + nSeconds %1 second @@ -5011,15 +5035,51 @@ To enable them in a commercial project, please contact us. - + nMinute %1 minute %1 minutes + + + chat_message_forward_error + Cannot forward an invalid message + Cannot forward an invalid message + + + + info_popup_forward_message_error + Could not forward message : %1 + Could not forward message : %1 + + + + info_popup_send_forward_message_error_message + Failed to create forward message + Failed to create forward message + + + + chat_message_reply_error + Cannot reply to invalid message + Cannot reply to invalid message + + + + info_popup_reply_message_error + Could not send reply message : %1 + Could not send reply message : %1 + + + + info_popup_send_reply_message_error_message + Failed to create reply message + Failed to create reply message + - + nHour %1 hour @@ -5027,8 +5087,8 @@ To enable them in a commercial project, please contact us. - - + + nDay %1 day @@ -5036,7 +5096,7 @@ To enable them in a commercial project, please contact us. - + nWeek %1 week @@ -5167,42 +5227,37 @@ Failed to create 1-1 conversation with %1 ! Failed to create 1-1 conversation with %1 ! - - chatMessage_error - Cannot reply to invalid message - Cannot reply to invalid message - - - + recorder_error Error with the recorder Error with the recorder - + + chat_error Error in the chat - - - + + + + + info_popup_error_title Error Error - - + info_popup_send_voice_message_error_message Could not send voice message : %1 Could not send voice message : %1 - - + info_popup_send_voice_message_sending_error_message Failed to create message from record Failed to create message from record diff --git a/Linphone/data/languages/fr_FR.ts b/Linphone/data/languages/fr_FR.ts index 7cbd6cc09..69cf47f32 100644 --- a/Linphone/data/languages/fr_FR.ts +++ b/Linphone/data/languages/fr_FR.ts @@ -1720,47 +1720,47 @@ ChatListView - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… - + chat_message_draft_sending_text Brouillon : %1 - + chat_room_delete "Delete" Supprimer - + chat_room_mute Mettre en sourdine - + chat_room_unmute "Mute" Enlever la sourdine - + chat_room_mark_as_read "Mark as read" Marquer comme lu - + chat_list_delete_chat_popup_title Delete the conversation ? Supprimer la conversation ? - + chat_list_delete_chat_popup_message This conversation and all its messages will be deleted. Do You want to continue ? La conversation et tous ses messages seront supprimés. Souhaitez-vous continuer ? @@ -1769,67 +1769,79 @@ ChatMessage - + chat_message_copy_selection "Copy selection" Copier la sélection - + chat_message_copy "Copy" Copier - + chat_message_copied_to_clipboard_title Copied Copié - + chat_message_copied_to_clipboard_toast "to clipboard" dans le presse-papiers - + chat_message_remote_replied %1 replied %1 a répondu - + + chat_message_forward + Forward + Transférer + + + chat_message_remote_replied_to %1 replied to %2 %1 a répondu à %2 - + + chat_message_forwarded + Forwarded + Transféré + + + chat_message_user_replied_to You replied to %1 Vous avez répondu à %1 - + chat_message_user_replied You replied Vous avez répondu - + chat_message_reception_info "Reception info" Info de réception - + chat_message_reply Reply Répondre - + chat_message_delete "Delete" Supprimer @@ -1925,13 +1937,13 @@ Error Réactions - + info_toast_deleted_title Deleted Supprimé - + info_toast_deleted_message The message has been deleted Le message a été supprimé @@ -1984,13 +1996,13 @@ Error ChatMessagesListView - + chat_message_list_encrypted_header_title End to end encrypted chat Conversation chiffrée de bout en bout - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. @@ -1998,7 +2010,7 @@ Error en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… @@ -2061,37 +2073,37 @@ en bout. Seul votre correspondant peut les déchiffrer. Aucune conversation dans votre historique - + chat_action_start_new_chat "New chat" Nouvelle conversation - + chat_start_group_chat_title "Nouveau groupe" Nouveau groupe - + chat_action_start_group_chat "Créer" Créer - - + + information_popup_error_title Erreur - + group_chat_error_must_have_name "Un nom doit être donné au groupe Un nom doit être donné au groupe - + group_call_error_not_connected "Vous n'etes pas connecté" Vous n'êtes pas connecté @@ -2273,53 +2285,53 @@ en bout. Seul votre correspondant peut les déchiffrer. ContactListItem - + contact_details_remove_from_favourites "Enlever des favoris" Enlever des favoris - + contact_details_add_to_favourites "Ajouter aux favoris" Ajouter aux favoris - + Partager Partager - + information_popup_error_title Erreur - + information_popup_vcard_creation_error La création du fichier vcard a échoué La création du fichier vcard a échoué - + information_popup_vcard_creation_title VCard créée VCard créée - + information_popup_vcard_creation_success "VCard du contact enregistrée dans %1" VCard du contact enregistrée dans %1 - + contact_sharing_email_title Partage de contact Partage de contact - + contact_details_delete "Supprimer" Supprimer @@ -4843,28 +4855,40 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - + chat_view_group_call_toast_message Démarrer un appel de groupe ? - + reply_to_label Reply to %1 Réponse à %1 - + shared_medias_title Shared medias Médias partagés - + shared_documents_title Shared documents Documents partagés + + + forward_to_title + Transfer to... + Transférer à… + + + + conversations_title + Conversations + Conversations + SettingsPage @@ -5003,7 +5027,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. Utils - + nMinute %1 minute @@ -5011,7 +5035,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nHour %1 heure @@ -5019,8 +5043,8 @@ Pour les activer dans un projet commercial, merci de nous contacter. - - + + nDay %1 jour @@ -5028,7 +5052,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nWeek %1 semaine @@ -5036,7 +5060,7 @@ Pour les activer dans un projet commercial, merci de nous contacter. - + nSeconds %1 seconde @@ -5167,42 +5191,73 @@ Failed to create 1-1 conversation with %1 ! Erreur lors de la création de la conversation avec %1 - - chatMessage_error - Cannot reply to invalid message - Impossible de répondre : message invalide - - - + recorder_error Error with the recorder Erreur avec l'enregistreur - + + chat_error Erreur dans le chat + + + chat_message_forward_error + Cannot forward an invalid message + Transfert impossible: message invalide + - - - + + + + + info_popup_error_title Error Erreur - + info_popup_forward_message_error + Could not forward message : %1 + Impossible de transférer le message : %1 + + + + info_popup_send_forward_message_error_message + Failed to create forward message + Le transfert du message a échoué + + + + chat_message_reply_error + Cannot reply to invalid message + Impossible de répondre: message invalide + + + + info_popup_reply_message_error + Could not send reply message : %1 + Impossible d'envoyer la réponse: %1 + + + + info_popup_send_reply_message_error_message + Failed to create reply message + La création du message a échoué + + + info_popup_send_voice_message_error_message Could not send voice message : %1 Impossible d'envoyer le message vocal : %1 - - + info_popup_send_voice_message_sending_error_message Failed to create message from record Impossible de créer le message vocal diff --git a/Linphone/model/chat/ChatModel.cpp b/Linphone/model/chat/ChatModel.cpp index 9042c9c7c..815cf132c 100644 --- a/Linphone/model/chat/ChatModel.cpp +++ b/Linphone/model/chat/ChatModel.cpp @@ -148,6 +148,11 @@ ChatModel::createReplyMessage(const std::shared_ptr &mess return mMonitor->createReplyMessage(message); } +std::shared_ptr +ChatModel::createForwardMessage(const std::shared_ptr &message) { + return mMonitor->createForwardMessage(message); +} + std::shared_ptr ChatModel::createTextMessageFromText(QString text) { return mMonitor->createMessageFromUtf8(Utils::appStringToCoreString(text)); } diff --git a/Linphone/model/chat/ChatModel.hpp b/Linphone/model/chat/ChatModel.hpp index 8b07db1f1..47988722b 100644 --- a/Linphone/model/chat/ChatModel.hpp +++ b/Linphone/model/chat/ChatModel.hpp @@ -54,6 +54,7 @@ public: createVoiceRecordingMessage(const std::shared_ptr &recorder); std::shared_ptr createReplyMessage(const std::shared_ptr &message); + std::shared_ptr createForwardMessage(const std::shared_ptr &message); std::shared_ptr createTextMessageFromText(QString text); std::shared_ptr createMessage(QString text, diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 29c3973e7..759168807 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -2028,18 +2028,50 @@ QString Utils::getSafeFilePath(const QString &filePath, bool *soFarSoGood) { return QString(""); } +void Utils::forwardMessageTo(ChatMessageGui *message, ChatGui *chatGui) { + auto chatModel = chatGui && chatGui->mCore ? chatGui->mCore->getModel() : nullptr; + auto chatMessageModel = message && message->mCore ? message->mCore->getModel() : nullptr; + if (!chatModel || !chatMessageModel) { + //: Cannot forward an invalid message + QString error = !chatMessageModel ? tr("chat_message_forward_error") + //: Error creating or opening the chat + : tr("chat_error"); + //: Error + showInformationPopup(tr("info_popup_error_title"), + //: Could not forward message : %1 + tr("info_popup_forward_message_error").arg(error)); + return; + } + App::postModelAsync([chatModel, chatMessageModel] { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + auto chat = chatModel->getMonitor(); + auto messageToForward = chatMessageModel->getMonitor(); + auto linMessage = chatModel->createForwardMessage(messageToForward); + if (linMessage) { + linMessage->send(); + } else { + App::postCoreAsync([] { + //: Error + showInformationPopup(tr("info_popup_error_title"), + //: Failed to create forward message + tr("info_popup_send_forward_message_error_message")); + }); + } + }); +} + void Utils::sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files) { auto chatModel = chatGui && chatGui->mCore ? chatGui->mCore->getModel() : nullptr; auto chatMessageModel = message && message->mCore ? message->mCore->getModel() : nullptr; if (!chatModel || !chatMessageModel) { //: Cannot reply to invalid message - QString error = !chatMessageModel ? tr("chatMessage_error") + QString error = !chatMessageModel ? tr("chat_message_reply_error") //: Error in the chat : tr("chat_error"); //: Error showInformationPopup(tr("info_popup_error_title"), - //: Could not send voice message : %1 - tr("info_popup_send_voice_message_error_message").arg(error)); + //: Could not send reply message : %1 + tr("info_popup_reply_message_error").arg(error)); return; } QList> filesContent; @@ -2065,8 +2097,8 @@ void Utils::sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString App::postCoreAsync([] { //: Error showInformationPopup(tr("info_popup_error_title"), - //: Failed to create message from record - tr("info_popup_send_voice_message_sending_error_message")); + //: Failed to create reply message + tr("info_popup_send_reply_message_error_message")); }); } }); diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 2e5c6e4e8..01c3949dd 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -180,6 +180,8 @@ public: Q_INVOKABLE static VariantObject *createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui); Q_INVOKABLE static void sendReplyMessage(ChatMessageGui *message, ChatGui *chatGui, QString text, QVariantList files); + Q_INVOKABLE static void forwardMessageTo(ChatMessageGui *message, ChatGui *chatGui); + Q_INVOKABLE static void sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui); Q_INVOKABLE static QString getEphemeralFormatedTime(int selectedTime); diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index 1b837588b..3f098486b 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -22,6 +22,7 @@ ListView { signal resultsReceived() signal markAllAsRead() + signal chatClicked(ChatGui chat) onResultsReceived: { loading = false @@ -411,6 +412,7 @@ ListView { if (pressedButtons & Qt.RightButton) { chatroomPopup.open() } else { + mainItem.chatClicked(modelData) mainItem.currentIndex = model.index mainItem.forceActiveFocus() } diff --git a/Linphone/view/Control/Display/Chat/ChatMessage.qml b/Linphone/view/Control/Display/Chat/ChatMessage.qml index 423cfeb18..a625bf433 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessage.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessage.qml @@ -21,6 +21,7 @@ Control.Control { property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false property bool isFromChatGroup: chatMessage? chatMessage.core.isFromChatGroup : false property bool isReply: chatMessage? chatMessage.core.isReply : false + property bool isForward: chatMessage? chatMessage.core.isForward : false property string replyText: chatMessage? chatMessage.core.replyText : false property var msgState: chatMessage ? chatMessage.core.messageState : LinphoneEnums.ChatMessageState.StateIdle hoverEnabled: true @@ -34,6 +35,7 @@ Control.Control { signal showReactionsForMessageRequested() signal showImdnStatusForMessageRequested() signal replyToMessageRequested() + signal forwardMessageRequested() background: Item { anchors.fill: parent @@ -62,6 +64,28 @@ Control.Control { weight: Typography.p4.weight } } + RowLayout { + id: forwardLayout + spacing: Math.round(8 * DefaultStyle.dp) + visible: mainItem.isForward + Layout.leftMargin: mainItem.isFromChatGroup ? Math.round(9 * DefaultStyle.dp) + avatar.width : 0 + Layout.alignment: mainItem.isRemoteMessage ? Qt.AlignLeft: Qt.AlignRight + EffectImage { + imageSource: AppIcons.forward + colorizationColor: DefaultStyle.main2_500main + Layout.preferredWidth: Math.round(12 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(12 * DefaultStyle.dp) + } + Text { + //: Forwarded + text: qsTr("chat_message_forwarded") + color: DefaultStyle.main2_600 + font { + pixelSize: Typography.p4.pixelSize + weight: Typography.p4.weight + } + } + } RowLayout { id: replyLayout visible: mainItem.isReply @@ -377,6 +401,18 @@ Control.Control { optionsMenu.close() } } + IconLabelButton { + inverseLayout: true + //: Forward + text: qsTr("chat_message_forward") + icon.source: AppIcons.forward + Layout.fillWidth: true + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + onClicked: { + mainItem.forwardMessageRequested() + optionsMenu.close() + } + } Rectangle { Layout.fillWidth: true Layout.preferredHeight: Math.min(1, Math.round(1 * DefaultStyle.dp)) diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index d0a3afe51..32fd6b1b1 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -18,6 +18,7 @@ ListView { signal showReactionsForMessageRequested(ChatMessageGui chatMessage) signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) signal replyToMessageRequested(ChatMessageGui chatMessage) + signal forwardMessageRequested(ChatMessageGui chatMessage) Component.onCompleted: { Qt.callLater(function() { @@ -64,7 +65,7 @@ ListView { header: Item { visible: mainItem.chat && mainItem.chat.core.isEncrypted - height: visible ? headerMessage.height + Math.round(50 * DefaultStyle.dp) : 0 + height: visible ? headerMessage.height + Math.round(50 * DefaultStyle.dp) : Math.round(30 * DefaultStyle.dp) width: headerMessage.width anchors.horizontalCenter: parent.horizontalCenter Control.Control { @@ -150,6 +151,7 @@ ListView { onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(modelData) onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(modelData) onReplyToMessageRequested: mainItem.replyToMessageRequested(modelData) + onForwardMessageRequested: mainItem.forwardMessageRequested(modelData) } } diff --git a/Linphone/view/Control/Display/Contact/ContactListItem.qml b/Linphone/view/Control/Display/Contact/ContactListItem.qml index 4019edda5..b2ddd8321 100644 --- a/Linphone/view/Control/Display/Contact/ContactListItem.qml +++ b/Linphone/view/Control/Display/Contact/ContactListItem.qml @@ -40,225 +40,224 @@ FocusScope { signal contactDeletionRequested(FriendGui contact) signal containsMouseChanged(bool containsMouse) - Text { - id: initial - anchors.left: parent.left - visible: mainItem.showInitials - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: Math.round(15 * DefaultStyle.dp) - verticalAlignment: Text.AlignVCenter - width: Math.round(20 * DefaultStyle.dp) - opacity: previousInitial != mainItem.initial ? 1 : 0 - text: mainItem.initial || "" - color: DefaultStyle.main2_400 - font { - pixelSize: Math.round(20 * DefaultStyle.dp) - weight: Math.round(500 * DefaultStyle.dp) - capitalization: Font.AllUppercase - } - } - RowLayout { - id: contactDelegate - anchors.left: initial.visible ? initial.right : parent.left - anchors.right: parent.right - anchors.rightMargin: mainItem.itemsRightMargin - anchors.top: parent.top - anchors.bottom: parent.bottom - spacing: Math.round(16 * DefaultStyle.dp) - z: 1 - Avatar { - Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) - Layout.leftMargin: Math.round(5 * DefaultStyle.dp) - contact: searchResultItem - shadowEnabled: false - } - ColumnLayout { - spacing: 0 - Text { - text: UtilsCpp.boldTextPart(mainItem.displayName, - mainItem.highlightText) - font { - pixelSize: mainItem.showDefaultAddress ? Typography.h4.pixelSize : Typography.p1.pixelSize - capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase - weight: mainItem.showDefaultAddress ? Typography.h4.weight : Typography.p1.weight - } - maximumLineCount: 1 - Layout.fillWidth: true + MouseArea { + Text { + id: initial + anchors.left: parent.left + visible: mainItem.showInitials + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: Math.round(15 * DefaultStyle.dp) + verticalAlignment: Text.AlignVCenter + width: Math.round(20 * DefaultStyle.dp) + opacity: previousInitial != mainItem.initial ? 1 : 0 + text: mainItem.initial || "" + color: DefaultStyle.main2_400 + font { + pixelSize: Math.round(20 * DefaultStyle.dp) + weight: Math.round(500 * DefaultStyle.dp) + capitalization: Font.AllUppercase } - Text { - Layout.topMargin: Math.round(2 * DefaultStyle.dp) - Layout.fillWidth: true - visible: mainItem.showDefaultAddress - property string address: SettingsCpp.onlyDisplaySipUriUsername - ? UtilsCpp.getUsername(mainItem.addressFromFilter) - : mainItem.addressFromFilter - text: UtilsCpp.boldTextPart(address, mainItem.highlightText) - maximumLineCount: 1 - elide: Text.ElideRight - font { - weight: Math.round(300 * DefaultStyle.dp) - pixelSize: Math.round(12 * DefaultStyle.dp) - } - } - } - Item { - Layout.fillWidth: true } RowLayout { - id: actionsRow - z: 1 - visible: mainItem.showActions || actionButtons.visible || mainItem.showContactMenu || mainItem.multiSelectionEnabled - spacing: visible ? Math.round(16 * DefaultStyle.dp) : 0 - enabled: visible - Layout.rightMargin: Math.round(5 * DefaultStyle.dp) - EffectImage { - id: isSelectedCheck - visible: mainItem.multiSelectionEnabled - && (mainItem.selectedContacts.indexOf(mainItem.addressFromFilter) != -1) - Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) - imageSource: AppIcons.check - colorizationColor: DefaultStyle.main1_500_main + id: contactDelegate + anchors.left: initial.visible ? initial.right : parent.left + anchors.right: parent.right + anchors.rightMargin: mainItem.itemsRightMargin + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: Math.round(16 * DefaultStyle.dp) + z: contactArea.z + 1 + Avatar { + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + Layout.leftMargin: Math.round(5 * DefaultStyle.dp) + contact: searchResultItem + shadowEnabled: false + } + ColumnLayout { + spacing: 0 + Text { + text: UtilsCpp.boldTextPart(mainItem.displayName, + mainItem.highlightText) + font { + pixelSize: mainItem.showDefaultAddress ? Typography.h4.pixelSize : Typography.p1.pixelSize + capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase + weight: mainItem.showDefaultAddress ? Typography.h4.weight : Typography.p1.weight + } + maximumLineCount: 1 + Layout.fillWidth: true + } + Text { + Layout.topMargin: Math.round(2 * DefaultStyle.dp) + Layout.fillWidth: true + visible: mainItem.showDefaultAddress + property string address: SettingsCpp.onlyDisplaySipUriUsername + ? UtilsCpp.getUsername(mainItem.addressFromFilter) + : mainItem.addressFromFilter + text: UtilsCpp.boldTextPart(address, mainItem.highlightText) + maximumLineCount: 1 + elide: Text.ElideRight + font { + weight: Math.round(300 * DefaultStyle.dp) + pixelSize: Math.round(12 * DefaultStyle.dp) + } + } + } + Item { + Layout.fillWidth: true } RowLayout { - id: actionButtons - visible: mainItem.showActions - spacing: visible ? Math.round(10 * DefaultStyle.dp) : 0 - IconButton { - id: callButton - Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) - icon.width: Math.round(24 * DefaultStyle.dp) - icon.height: Math.round(24 * DefaultStyle.dp) - icon.source: AppIcons.phone - focus: visible - radius: Math.round(40 * DefaultStyle.dp) - style: ButtonStyle.grey - onClicked: UtilsCpp.createCall(mainItem.addressFromFilter) - KeyNavigation.left: chatButton - KeyNavigation.right: videoCallButton - } - IconButton { - id: videoCallButton - visible: SettingsCpp.videoEnabled - Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) - icon.width: Math.round(24 * DefaultStyle.dp) - icon.height: Math.round(24 * DefaultStyle.dp) - icon.source: AppIcons.videoCamera - focus: visible && !callButton.visible - radius: Math.round(40 * DefaultStyle.dp) - style: ButtonStyle.grey - onClicked: UtilsCpp.createCall(mainItem.addressFromFilter, {"localVideoEnabled": true}) - KeyNavigation.left: callButton - KeyNavigation.right: chatButton - } - IconButton { - id: chatButton - visible: actionButtons.visible - && !SettingsCpp.disableChatFeature - Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) - icon.width: Math.round(24 * DefaultStyle.dp) - icon.height: Math.round(24 * DefaultStyle.dp) - icon.source: AppIcons.chatTeardropText - focus: visible && !callButton.visible - && !videoCallButton.visible - radius: Math.round(40 * DefaultStyle.dp) - style: ButtonStyle.grey - KeyNavigation.left: videoCallButton - KeyNavigation.right: callButton - onClicked: { - console.debug("[ContactListItem.qml] Open conversation") - mainWindow.displayChatPage(mainItem.addressFromFilter) - } - } - } - PopupButton { - id: friendPopup - z: 1 - popup.x: 0 - popup.padding: Math.round(10 * DefaultStyle.dp) - visible: mainItem.showContactMenu && (contactArea.containsMouse || mainItem.isLastHovered || hovered || popup.opened) + id: actionsRow + z: contactArea.z + 1 + visible: mainItem.showActions || actionButtons.visible || mainItem.showContactMenu || mainItem.multiSelectionEnabled + spacing: visible ? Math.round(16 * DefaultStyle.dp) : 0 enabled: visible + Layout.rightMargin: Math.round(5 * DefaultStyle.dp) + EffectImage { + id: isSelectedCheck + visible: mainItem.multiSelectionEnabled + && (mainItem.selectedContacts.indexOf(mainItem.addressFromFilter) != -1) + Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) + imageSource: AppIcons.check + colorizationColor: DefaultStyle.main1_500_main + } + RowLayout { + id: actionButtons + visible: mainItem.showActions + spacing: visible ? Math.round(10 * DefaultStyle.dp) : 0 + IconButton { + id: callButton + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + icon.width: Math.round(24 * DefaultStyle.dp) + icon.height: Math.round(24 * DefaultStyle.dp) + icon.source: AppIcons.phone + focus: visible + radius: Math.round(40 * DefaultStyle.dp) + style: ButtonStyle.grey + onClicked: UtilsCpp.createCall(mainItem.addressFromFilter) + KeyNavigation.left: chatButton + KeyNavigation.right: videoCallButton + } + IconButton { + id: videoCallButton + visible: SettingsCpp.videoEnabled + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + icon.width: Math.round(24 * DefaultStyle.dp) + icon.height: Math.round(24 * DefaultStyle.dp) + icon.source: AppIcons.videoCamera + focus: visible && !callButton.visible + radius: Math.round(40 * DefaultStyle.dp) + style: ButtonStyle.grey + onClicked: UtilsCpp.createCall(mainItem.addressFromFilter, {"localVideoEnabled": true}) + KeyNavigation.left: callButton + KeyNavigation.right: chatButton + } + IconButton { + id: chatButton + visible: actionButtons.visible + && !SettingsCpp.disableChatFeature + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + icon.width: Math.round(24 * DefaultStyle.dp) + icon.height: Math.round(24 * DefaultStyle.dp) + icon.source: AppIcons.chatTeardropText + focus: visible && !callButton.visible + && !videoCallButton.visible + radius: Math.round(40 * DefaultStyle.dp) + style: ButtonStyle.grey + KeyNavigation.left: videoCallButton + KeyNavigation.right: callButton + onClicked: { + console.debug("[ContactListItem.qml] Open conversation") + mainWindow.displayChatPage(mainItem.addressFromFilter) + } + } + } + PopupButton { + id: friendPopup + z: contactArea.z + 1 + popup.x: 0 + popup.padding: Math.round(10 * DefaultStyle.dp) + visible: mainItem.showContactMenu && (contactArea.containsMouse || mainItem.isLastHovered || hovered || popup.opened) + enabled: visible - popup.contentItem: ColumnLayout { - IconLabelButton { - Layout.fillWidth: true - visible: searchResultItem.core.isStored - && !searchResultItem.core.readOnly - //: "Enlever des favoris" - text: searchResultItem.core.starred ? qsTr("contact_details_remove_from_favourites") - //: "Ajouter aux favoris" - : qsTr("contact_details_add_to_favourites") - icon.source: searchResultItem.core.starred ? AppIcons.heartFill : AppIcons.heart - spacing: Math.round(10 * DefaultStyle.dp) - textColor: DefaultStyle.main2_500main - hoveredImageColor: searchResultItem.core.starred ? DefaultStyle.main1_700 : DefaultStyle.danger_700 - contentImageColor: searchResultItem.core.starred ? DefaultStyle.danger_500main : DefaultStyle.main2_600 - onClicked: { - searchResultItem.core.lSetStarred( - !searchResultItem.core.starred) - friendPopup.close() + popup.contentItem: ColumnLayout { + IconLabelButton { + Layout.fillWidth: true + visible: searchResultItem.core.isStored + && !searchResultItem.core.readOnly + //: "Enlever des favoris" + text: searchResultItem.core.starred ? qsTr("contact_details_remove_from_favourites") + //: "Ajouter aux favoris" + : qsTr("contact_details_add_to_favourites") + icon.source: searchResultItem.core.starred ? AppIcons.heartFill : AppIcons.heart + spacing: Math.round(10 * DefaultStyle.dp) + textColor: DefaultStyle.main2_500main + hoveredImageColor: searchResultItem.core.starred ? DefaultStyle.main1_700 : DefaultStyle.danger_700 + contentImageColor: searchResultItem.core.starred ? DefaultStyle.danger_500main : DefaultStyle.main2_600 + onClicked: { + searchResultItem.core.lSetStarred( + !searchResultItem.core.starred) + friendPopup.close() + } + style: ButtonStyle.noBackground } - style: ButtonStyle.noBackground - } - IconLabelButton { - text: qsTr("Partager") - Layout.fillWidth: true - icon.source: AppIcons.shareNetwork - spacing: Math.round(10 * DefaultStyle.dp) - textColor: DefaultStyle.main2_500main - onClicked: { - var vcard = searchResultItem.core.getVCard() - var username = searchResultItem.core.givenName - + searchResultItem.core.familyName - var filepath = UtilsCpp.createVCardFile( - username, vcard) - if (filepath == "") - UtilsCpp.showInformationPopup( - qsTr("information_popup_error_title"), - //: La création du fichier vcard a échoué - qsTr("information_popup_vcard_creation_error"), - false) - else - //: VCard créée - mainWindow.showInformationPopup(qsTr("information_popup_vcard_creation_title"), - //: "VCard du contact enregistrée dans %1" - qsTr("information_popup_vcard_creation_success").arg(filepath)) - //: Partage de contact - UtilsCpp.shareByEmail(qsTr("contact_sharing_email_title"),vcard, filepath) + IconLabelButton { + text: qsTr("Partager") + Layout.fillWidth: true + icon.source: AppIcons.shareNetwork + spacing: Math.round(10 * DefaultStyle.dp) + textColor: DefaultStyle.main2_500main + onClicked: { + var vcard = searchResultItem.core.getVCard() + var username = searchResultItem.core.givenName + + searchResultItem.core.familyName + var filepath = UtilsCpp.createVCardFile( + username, vcard) + if (filepath == "") + UtilsCpp.showInformationPopup( + qsTr("information_popup_error_title"), + //: La création du fichier vcard a échoué + qsTr("information_popup_vcard_creation_error"), + false) + else + //: VCard créée + mainWindow.showInformationPopup(qsTr("information_popup_vcard_creation_title"), + //: "VCard du contact enregistrée dans %1" + qsTr("information_popup_vcard_creation_success").arg(filepath)) + //: Partage de contact + UtilsCpp.shareByEmail(qsTr("contact_sharing_email_title"),vcard, filepath) + } + style: ButtonStyle.noBackground } - style: ButtonStyle.noBackground - } - IconLabelButton { - //: "Supprimer" - text: qsTr("contact_details_delete") - icon.source: AppIcons.trashCan - spacing: Math.round(10 * DefaultStyle.dp) - visible: !searchResultItem.core.readOnly - Layout.fillWidth: true - onClicked: { - mainItem.contactDeletionRequested( - searchResultItem) - friendPopup.close() + IconLabelButton { + //: "Supprimer" + text: qsTr("contact_details_delete") + icon.source: AppIcons.trashCan + spacing: Math.round(10 * DefaultStyle.dp) + visible: !searchResultItem.core.readOnly + Layout.fillWidth: true + onClicked: { + mainItem.contactDeletionRequested( + searchResultItem) + friendPopup.close() + } + style: ButtonStyle.noBackgroundRed } - style: ButtonStyle.noBackgroundRed } } } } - } - MouseArea { id: contactArea enabled: mainItem.selectionEnabled - anchors.fill: contactDelegate + anchors.fill: parent //height: mainItem.height hoverEnabled: true acceptedButtons: Qt.AllButtons - z: -1 focus: !actionButtons.visible onContainsMouseChanged: { mainItem.containsMouseChanged(containsMouse) @@ -271,21 +270,21 @@ FocusScope { visible: mainItem.isLastHovered || mainItem.isSelected || friendPopup.hovered } Keys.onPressed: event => { - if (event.key == Qt.Key_Space - || event.key == Qt.Key_Enter - || event.key == Qt.Key_Return) { - contactArea.clicked(undefined) - event.accepted = true - } - } + if (event.key == Qt.Key_Space + || event.key == Qt.Key_Enter + || event.key == Qt.Key_Return) { + contactArea.clicked(undefined) + event.accepted = true + } + } onClicked: mouse => { - forceActiveFocus() - if (mouse && mouse.button == Qt.RightButton - && mainItem.showContactMenu) { - if (friendPopup) friendPopup.open() - } else { - mainItem.clicked(mouse) - } - } + forceActiveFocus() + if (mouse && mouse.button == Qt.RightButton + && mainItem.showContactMenu) { + if (friendPopup) friendPopup.open() + } else { + mainItem.clicked(mouse) + } + } } } diff --git a/Linphone/view/Control/Display/Contact/ContactListView.qml b/Linphone/view/Control/Display/Contact/ContactListView.qml index da6650cdf..f9ae92c46 100644 --- a/Linphone/view/Control/Display/Contact/ContactListView.qml +++ b/Linphone/view/Control/Display/Contact/ContactListView.qml @@ -170,15 +170,11 @@ ListView { Item { Layout.fillWidth: true } - Button { + RoundButton { id: headerExpandButton style: ButtonStyle.noBackground icon.source: mainItem.expanded ? AppIcons.upArrow : AppIcons.downArrow - Layout.fillHeight: true - Layout.preferredWidth: height - Layout.rightMargin: Math.round(23 * DefaultStyle.dp) - icon.width: Math.round(24 * DefaultStyle.dp) - icon.height: Math.round(24 * DefaultStyle.dp) + Layout.rightMargin: mainItem.itemsRightMargin focus: true onClicked: mainItem.expanded = !mainItem.expanded } diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index edcfded16..dbce5bd00 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -20,6 +20,7 @@ RowLayout { property alias callHeaderContent: splitPanel.headerContent property bool replyingToMessage: false spacing: 0 + enum PanelType { MessageReactions, SharedFiles, Medias, ImdnStatus, ForwardToList, ManageParticipants, EphemeralSettings, None} signal oneOneCall(bool video) signal groupCall() @@ -154,18 +155,23 @@ RowLayout { Control.ScrollBar.vertical: scrollbar onShowReactionsForMessageRequested: (chatMessage) => { mainItem.chatMessage = chatMessage - contentLoader.showingMessageReactions = true + contentLoader.panelType = SelectedChatView.PanelType.MessageReactions detailsPanel.visible = true } onShowImdnStatusForMessageRequested: (chatMessage) => { mainItem.chatMessage = chatMessage - contentLoader.showingImdnStatus = true + contentLoader.panelType = SelectedChatView.PanelType.ImdnStatus detailsPanel.visible = true } onReplyToMessageRequested: (chatMessage) => { mainItem.chatMessage = chatMessage mainItem.replyingToMessage = true } + onForwardMessageRequested: (chatMessage) => { + mainItem.chatMessage = chatMessage + contentLoader.panelType = SelectedChatView.PanelType.ForwardToList + detailsPanel.visible = true + } Popup { id: emojiPickerPopup @@ -422,8 +428,7 @@ RowLayout { Layout.fillHeight: true Layout.preferredWidth: Math.round(387 * DefaultStyle.dp) onVisibleChanged: if(!visible) { - contentLoader.showingMessageReactions = false - contentLoader.showingImdnStatus = false + contentLoader.panelType = SelectedChatView.PanelType.None } background: Rectangle { @@ -433,27 +438,25 @@ RowLayout { contentItem: Loader { id: contentLoader - property bool showingMessageReactions: false - property bool showingSharedFiles: false - property bool showingMedias: false - property bool showingImdnStatus: false - property bool showingManageParticipants: false - property bool showingEphemeralSettings: false - anchors.top: parent.top + property int panelType: SelectedChatView.PanelType.None + // anchors.top: parent.top + anchors.fill: parent anchors.topMargin: Math.round(39 * DefaultStyle.dp) - sourceComponent: showingEphemeralSettings + sourceComponent: panelType === SelectedChatView.PanelType.EphemeralSettings ? ephemeralSettingsComponent - : showingMessageReactions + : panelType === SelectedChatView.PanelType.MessageReactions ? messageReactionsComponent - : showingImdnStatus + : panelType === SelectedChatView.PanelType.ImdnStatus ? messageImdnStatusComponent - : showingSharedFiles + : panelType === SelectedChatView.PanelType.SharedFiles || panelType === SelectedChatView.PanelType.Medias ? sharedFilesComponent - : showingManageParticipants - ? manageParticipantsComponent - : mainItem.chat.core.isGroupChat - ? groupInfoComponent - : oneToOneInfoComponent + : panelType === SelectedChatView.PanelType.ForwardToList + ? forwardToListsComponent + : panelType === SelectedChatView.PanelType.ManageParticipants + ? manageParticipantsComponent + : mainItem.chat.core.isGroupChat + ? groupInfoComponent + : oneToOneInfoComponent active: detailsPanel.visible onLoaded: { if (contentLoader.item && contentLoader.item.parentView) { @@ -466,10 +469,9 @@ RowLayout { id: oneToOneInfoComponent OneOneConversationInfos { chatGui: mainItem.chat - onEphemeralSettingsRequested: contentLoader.showingEphemeralSettings = true + onEphemeralSettingsRequested: contentLoader.panelType = SelectedChatView.PanelType.EphemeralSettings onShowSharedFilesRequested: (showMedias) => { - contentLoader.showingSharedFiles = true - contentLoader.showingMedias = showMedias + contentLoader.panelType = showMedias ? SelectedChatView.PanelType.SharedFiles : SelectedChatView.PanelType.Medias } } } @@ -478,12 +480,11 @@ RowLayout { id: groupInfoComponent GroupConversationInfos { chatGui: mainItem.chat - onManageParticipantsRequested: contentLoader.showingManageParticipants = true + onManageParticipantsRequested: contentLoader.panelType = SelectedChatView.PanelType.ManageParticipants onShowSharedFilesRequested: (showMedias) => { - contentLoader.showingSharedFiles = true - contentLoader.showingMedias = showMedias + contentLoader.panelType = showMedias ? SelectedChatView.PanelType.SharedFiles : SelectedChatView.PanelType.Medias } - onEphemeralSettingsRequested: contentLoader.showingEphemeralSettings = true + onEphemeralSettingsRequested: contentLoader.panelType = SelectedChatView.PanelType.EphemeralSettings } } @@ -512,15 +513,15 @@ RowLayout { id: sharedFilesComponent MessageSharedFilesInfos { chatGui: mainItem.chat - title: contentLoader.showingMedias + title: contentLoader.panelType === SelectedChatView.PanelType.Medias //: Shared medias ? qsTr("shared_medias_title") //: Shared documents : qsTr("shared_documents_title") - filter: contentLoader.showingMedias ? ChatMessageFileProxy.FilterContentType.Medias : ChatMessageFileProxy.FilterContentType.Documents + filter: contentLoader.panelType === SelectedChatView.PanelType.Medias ? ChatMessageFileProxy.FilterContentType.Medias : ChatMessageFileProxy.FilterContentType.Documents onGoBackRequested: { // detailsPanel.visible = false - contentLoader.showingSharedFiles = false + contentLoader.panelType = SelectedChatView.PanelType.SharedFiles } } } @@ -529,7 +530,7 @@ RowLayout { id: manageParticipantsComponent ManageParticipants { chatGui: mainItem.chat - onDone: contentLoader.showingManageParticipants = false + onDone: contentLoader.panelType = SelectedChatView.PanelType.None } } @@ -537,8 +538,98 @@ RowLayout { id: ephemeralSettingsComponent EphemeralSettings { chatGui: mainItem.chat - onDone: contentLoader.showingEphemeralSettings = false + onDone: contentLoader.panelType = SelectedChatView.PanelType.None } } + + Component { + id: forwardToListsComponent + MessageInfosLayout { + //: Transfer to... + title: qsTr("forward_to_title") + // width: detailsPanel.width + // RectangleTest{anchors.fill: parent} + onGoBackRequested: { + detailsPanel.visible = false + mainItem.chatMessage = null + } + content: ColumnLayout { + spacing: Math.round(31 * DefaultStyle.dp) + SearchBar { + id: forwardSearchBar + Layout.fillWidth: true + } + Flickable { + Layout.fillWidth: true + Layout.fillHeight: true + contentWidth: parent.width + // width: parent.width + // Control.ScrollBar.vertical: ScrollBar { + // id: scrollbar + // topPadding: Math.round(24 * DefaultStyle.dp) // Avoid to be on top of collapse button + // active: true + // interactive: true + // visible: parent.contentHeight > parent.height + // policy: Control.ScrollBar.AsNeeded + // } + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: Math.round(8 * DefaultStyle.dp) + // width: parent.width //- scrollbar.width - Math.round(5 * DefaultStyle.dp) + RowLayout { + Text { + //: Conversations + text: qsTr("conversations_title") + font { + pixelSize: Typography.h4.pixelSize + weight: Typography.h4.weight + } + } + Item{Layout.fillWidth: true} + RoundButton { + id: expandChatButton + style: ButtonStyle.noBackground + checkable: true + checked: true + icon.source: checked ? AppIcons.upArrow : AppIcons.downArrow + KeyNavigation.down: contentControl + } + } + ChatListView { + visible: expandChatButton.checked + searchBar: forwardSearchBar + Layout.fillWidth: true + Layout.preferredHeight: contentHeight + onChatClicked: (chat) => { + UtilsCpp.forwardMessageTo(mainItem.chatMessage, chat) + mainItem.chat = chat + detailsPanel.visible = false + } + } + AllContactListView { + visible: expandContactButton.checked + Layout.fillWidth: true + itemsRightMargin: 0 + showActions: false + showContactMenu: false + showFavorites: false + searchBarText: forwardSearchBar.text + Layout.preferredHeight: contentHeight + onContactSelected: contact => selectedFriend = contact + property FriendGui selectedFriend + property var chatForSelectedAddressObj: selectedFriend ? UtilsCpp.getChatForAddress(selectedFriend.core.defaultAddress) : null + property ChatGui chatForAddress: chatForSelectedAddressObj ? chatForSelectedAddressObj.value : null + onChatForAddressChanged: if(chatForAddress) { + UtilsCpp.forwardMessageTo(mainItem.chatMessage, chatForAddress) + mainItem.chat = chatForAddress + detailsPanel.visible = false + } + } + } + } + } + } + } } } diff --git a/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml b/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml index 8e90bad85..f30f56b09 100644 --- a/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml +++ b/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml @@ -12,7 +12,7 @@ MessageInfosLayout { tabbarModel: chatMessageGui ? chatMessageGui.core.imdnStatusListAsString : [] listModel: ImdnStatusProxy { imdnStatusList: chatMessageGui ? chatMessageGui.core.imdnStatusList : [] - filter: chatMessageGui ? chatMessageGui.core.imdnStatusAsSingletons[mainItem.tabbar.currentIndex].state : "" + filter: chatMessageGui && chatMessageGui.core.imdnStatusAsSingletons[mainItem.tabbar.currentIndex]?.state || "" } listView.delegate: Item { diff --git a/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml b/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml index da533ed83..d15c5cfc6 100644 --- a/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml +++ b/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml @@ -48,6 +48,7 @@ ColumnLayout { Layout.rightMargin: Math.round(16 * DefaultStyle.dp) TabBar { id: tabbar + visible: mainItem.tabbarModel !== undefined Layout.fillWidth: true model: mainItem.tabbarModel pixelSize: Typography.h3m.pixelSize @@ -56,6 +57,7 @@ ColumnLayout { ListView { id: listView + visible: mainItem.listModel !== undefined Layout.fillWidth: true Layout.fillHeight: true spacing: Math.round(11 * DefaultStyle.dp) diff --git a/Linphone/view/Page/Main/Chat/ChatPage.qml b/Linphone/view/Page/Main/Chat/ChatPage.qml index 380a494b8..9b56f3c45 100644 --- a/Linphone/view/Page/Main/Chat/ChatPage.qml +++ b/Linphone/view/Page/Main/Chat/ChatPage.qml @@ -196,7 +196,9 @@ AbstractMainPage { Connections { target: mainItem - onSelectedChatGuiChanged: chatListView.selectChat(mainItem.selectedChatGui) + onSelectedChatGuiChanged: { + chatListView.selectChat(mainItem.selectedChatGui) + } } } } @@ -334,6 +336,7 @@ AbstractMainPage { SelectedChatView { anchors.fill: parent chat: mainItem.selectedChatGui || null + onChatChanged: if (mainItem.selectedChatGui !== chat) mainItem.selectedChatGui = chat } } }