diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 1ee7ee518..13e79aaf1 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -60,6 +60,7 @@ #include "core/chat/message/EventLogProxy.hpp" #include "core/chat/message/content/ChatMessageContentGui.hpp" #include "core/chat/message/content/ChatMessageContentProxy.hpp" +#include "core/chat/message/imdn/ImdnStatusProxy.hpp" #include "core/conference/ConferenceGui.hpp" #include "core/conference/ConferenceInfoGui.hpp" #include "core/conference/ConferenceInfoProxy.hpp" @@ -687,6 +688,7 @@ void App::initCppInterfaces() { qmlRegisterType(Constants::MainQmlUri, 1, 0, "FPSCounter"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "EmojiModel"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "EmojiProxy"); + qmlRegisterType(Constants::MainQmlUri, 1, 0, "ImdnStatusProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "SoundPlayerGui"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "RecorderGui"); diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index faa9368eb..ac0074ee9 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -33,6 +33,8 @@ list(APPEND _LINPHONEAPP_SOURCES core/chat/message/content/ChatMessageContentGui.cpp core/chat/message/content/ChatMessageContentList.cpp core/chat/message/content/ChatMessageContentProxy.cpp + core/chat/message/imdn/ImdnStatusList.cpp + core/chat/message/imdn/ImdnStatusProxy.cpp core/emoji/EmojiList.cpp core/emoji/EmojiModel.cpp core/emoji/EmojiProxy.cpp diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index 6325b2a6f..85e5b9bf5 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -27,6 +27,40 @@ DEFINE_ABSTRACT_OBJECT(ChatMessageCore) /***********************************************************************/ +ImdnStatus ImdnStatus::operator=(ImdnStatus r) { + mAddress = r.mAddress; + mState = r.mState; + mLastUpdatedTime = r.mLastUpdatedTime; + return *this; +} + +bool ImdnStatus::operator==(const ImdnStatus &r) const { + return r.mState == mState && r.mAddress == mAddress && r.mLastUpdatedTime == mLastUpdatedTime; +} + +bool ImdnStatus::operator!=(ImdnStatus r) { + return r.mState != mState || r.mAddress != mAddress || r.mLastUpdatedTime != mLastUpdatedTime; +} + +ImdnStatus ImdnStatus::createMessageImdnStatusVariant(const QString &address, + const LinphoneEnums::ChatMessageState &state, + QDateTime lastUpdatedTime) { + ImdnStatus s; + s.mState = state; + s.mAddress = address; + s.mLastUpdatedTime = lastUpdatedTime; + return s; +} + +QVariant createImdnStatusSingletonVariant(const LinphoneEnums::ChatMessageState &state, int count = 1) { + QVariantMap map; + map.insert("state", QVariant::fromValue(state)); + map.insert("count", count); + return map; +} + +/***********************************************************************/ + Reaction Reaction::operator=(Reaction r) { mAddress = r.mAddress; mBody = r.mBody; @@ -132,6 +166,7 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c mIsForward = chatmessage->isForward(); mIsReply = chatmessage->isReply(); + mImdnStatusList = computeDeliveryStatus(chatmessage); } ChatMessageCore::~ChatMessageCore() { @@ -205,8 +240,12 @@ void ChatMessageCore::setSelf(QSharedPointer me) { &ChatMessageModel::msgStateChanged, [this](const std::shared_ptr &message, linphone::ChatMessage::State state) { if (mChatMessageModel->getMonitor() != message) return; + auto imdnStatusList = computeDeliveryStatus(message); auto msgState = LinphoneEnums::fromLinphone(state); - mChatMessageModelConnection->invokeToCore([this, msgState] { setMessageState(msgState); }); + mChatMessageModelConnection->invokeToCore([this, msgState, imdnStatusList] { + setImdnStatusList(imdnStatusList); + setMessageState(msgState); + }); }); mChatMessageModelConnection->makeConnectToModel( &ChatMessageModel::fileTransferProgressIndication, @@ -265,6 +304,40 @@ void ChatMessageCore::setSelf(QSharedPointer me) { [this](const std::shared_ptr &message) {}); } +QList ChatMessageCore::computeDeliveryStatus(const std::shared_ptr &message) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + QList imdnStatusList; + auto createImdnStatus = [this, &imdnStatusList](std::shared_ptr participant, + linphone::ChatMessage::State state) { + auto address = participant->getParticipant() ? participant->getParticipant()->getAddress()->clone() : nullptr; + auto lastUpdated = QDateTime::fromSecsSinceEpoch(participant->getStateChangeTime()); + if (address) { + address->clean(); + auto addrString = Utils::coreStringToAppString(address->asStringUriOnly()); + auto imdn = + ImdnStatus::createMessageImdnStatusVariant(addrString, LinphoneEnums::fromLinphone(state), lastUpdated); + imdnStatusList.append(imdn); + } + }; + // Read + for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::Displayed)) { + createImdnStatus(participant, linphone::ChatMessage::State::Displayed); + } + // Received + for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::DeliveredToUser)) { + createImdnStatus(participant, linphone::ChatMessage::State::DeliveredToUser); + } + // Sent + for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::Delivered)) { + createImdnStatus(participant, linphone::ChatMessage::State::Delivered); + } + // Error + for (auto &participant : message->getParticipantsByImdnState(linphone::ChatMessage::State::NotDelivered)) { + createImdnStatus(participant, linphone::ChatMessage::State::NotDelivered); + } + return imdnStatusList; +} + QDateTime ChatMessageCore::getTimestamp() const { return mTimestamp; } @@ -449,6 +522,49 @@ void ChatMessageCore::setMessageState(LinphoneEnums::ChatMessageState state) { } } +QList ChatMessageCore::getImdnStatusList() const { + return mImdnStatusList; +} + +void ChatMessageCore::setImdnStatusList(QList status) { + mImdnStatusList = status; + emit imdnStatusListChanged(); +} + +QStringList ChatMessageCore::getImdnStatusListLabels() const { + QStringList statusList; + int count = 0; + auto imdnSingletons = getImdnStatusAsSingletons(); + for (auto &status : imdnSingletons) { + auto map = status.toMap(); + auto val = map["state"].value(); + auto count = map["count"].toInt(); + statusList.append(QString("%1 %2").arg(LinphoneEnums::toString(val)).arg(count)); + } + return statusList; +} + +QList ChatMessageCore::getImdnStatusAsSingletons() const { + QList statusSingletons; + for (auto &stat : mImdnStatusList) { + auto it = std::find_if(statusSingletons.begin(), statusSingletons.end(), [state = stat.mState](QVariant data) { + auto dataState = data.toMap()["state"].value(); + return state == dataState; + }); + if (it == statusSingletons.end()) statusSingletons.push_back(createImdnStatusSingletonVariant(stat.mState, 1)); + else { + auto map = it->toMap(); + auto count = map["count"].toInt(); + ++count; + map.remove("count"); + map.insert("count", count); + statusSingletons.erase(it); + statusSingletons.push_back(map); + } + } + return statusSingletons; +} + std::shared_ptr ChatMessageCore::getModel() const { return mChatMessageModel; } diff --git a/Linphone/core/chat/message/ChatMessageCore.hpp b/Linphone/core/chat/message/ChatMessageCore.hpp index 96db71a93..b2c6c1875 100644 --- a/Linphone/core/chat/message/ChatMessageCore.hpp +++ b/Linphone/core/chat/message/ChatMessageCore.hpp @@ -34,6 +34,26 @@ #include +struct ImdnStatus { + Q_GADGET + + Q_PROPERTY(QString address MEMBER mAddress) + Q_PROPERTY(LinphoneEnums::ChatMessageState state MEMBER mState) + Q_PROPERTY(QDateTime lastUpdatedTime MEMBER mLastUpdatedTime) + +public: + QString mAddress; + LinphoneEnums::ChatMessageState mState; + QDateTime mLastUpdatedTime; + + ImdnStatus operator=(ImdnStatus r); + bool operator==(const ImdnStatus &r) const; + bool operator!=(ImdnStatus r); + static ImdnStatus createMessageImdnStatusVariant(const QString &address, + const LinphoneEnums::ChatMessageState &state, + QDateTime mLastUpdatedTime); +}; + struct Reaction { Q_GADGET @@ -71,6 +91,9 @@ class ChatMessageCore : public QObject, public AbstractObject { Q_PROPERTY(bool isFromChatGroup READ isFromChatGroup CONSTANT) Q_PROPERTY(bool isRead READ isRead WRITE setIsRead NOTIFY isReadChanged) Q_PROPERTY(QString ownReaction READ getOwnReaction WRITE setOwnReaction NOTIFY messageReactionChanged) + Q_PROPERTY(QStringList imdnStatusListAsString READ getImdnStatusListLabels NOTIFY imdnStatusListChanged) + Q_PROPERTY(QList imdnStatusList READ getImdnStatusList NOTIFY imdnStatusListChanged) + Q_PROPERTY(QList imdnStatusAsSingletons READ getImdnStatusAsSingletons NOTIFY imdnStatusListChanged) Q_PROPERTY(QList reactions READ getReactions WRITE setReactions NOTIFY messageReactionChanged) Q_PROPERTY(QList reactionsSingleton READ getReactionsSingleton NOTIFY singletonReactionMapChanged) Q_PROPERTY( @@ -87,6 +110,8 @@ public: ~ChatMessageCore(); void setSelf(QSharedPointer me); + QList computeDeliveryStatus(const std::shared_ptr &message); + QDateTime getTimestamp() const; void setTimestamp(QDateTime timestamp); @@ -121,6 +146,10 @@ public: LinphoneEnums::ChatMessageState getMessageState() const; void setMessageState(LinphoneEnums::ChatMessageState state); + QList getImdnStatusList() const; + void setImdnStatusList(QList status); + QList getImdnStatusAsSingletons() const; + QStringList getImdnStatusListLabels() const; std::shared_ptr getModel() const; Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const; @@ -132,6 +161,7 @@ signals: void isReadChanged(bool read); void isRemoteMessageChanged(bool isRemote); void messageStateChanged(); + void imdnStatusListChanged(); void messageReactionChanged(); void singletonReactionMapChanged(); @@ -156,6 +186,7 @@ private: QString mMessageId; QString mOwnReaction; QList mReactions; + QList mImdnStatusList; QList mReactionsSingletonMap; QDateTime mTimestamp; bool mIsRemoteMessage = false; diff --git a/Linphone/core/chat/message/imdn/ImdnStatusList.cpp b/Linphone/core/chat/message/imdn/ImdnStatusList.cpp new file mode 100644 index 000000000..3eeac8556 --- /dev/null +++ b/Linphone/core/chat/message/imdn/ImdnStatusList.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ImdnStatusList.hpp" +#include "core/App.hpp" + +#include + +#include + +// ============================================================================= + +DEFINE_ABSTRACT_OBJECT(ImdnStatusList) + +QSharedPointer ImdnStatusList::create() { + auto model = QSharedPointer(new ImdnStatusList(), &QObject::deleteLater); + model->moveToThread(App::getInstance()->thread()); + return model; +} + +ImdnStatusList::ImdnStatusList(QObject *parent) : AbstractListProxy(parent) { + mustBeInMainThread(getClassName()); + App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); +} + +ImdnStatusList::~ImdnStatusList() { + mustBeInMainThread("~" + getClassName()); +} + +QList ImdnStatusList::getImdnStatusList() { + return mList; +} + +void ImdnStatusList::setImdnStatusList(QList imdnStatusList) { + resetData(imdnStatusList); +} + +QVariant ImdnStatusList::data(const QModelIndex &index, int role) const { + int row = index.row(); + if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); + if (role == Qt::DisplayRole) return QVariant::fromValue(mList.at(row)); + return QVariant(); +} \ No newline at end of file diff --git a/Linphone/core/chat/message/imdn/ImdnStatusList.hpp b/Linphone/core/chat/message/imdn/ImdnStatusList.hpp new file mode 100644 index 000000000..2a7b31405 --- /dev/null +++ b/Linphone/core/chat/message/imdn/ImdnStatusList.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef IMDN_STATUS_LIST_H_ +#define IMDN_STATUS_LIST_H_ + +#include "core/chat/message/ChatMessageCore.hpp" +#include "core/proxy/AbstractListProxy.hpp" +#include "tool/AbstractObject.hpp" +#include "tool/thread/SafeConnection.hpp" +#include + +// ============================================================================= + +class ImdnStatusList : public AbstractListProxy, public AbstractObject { + Q_OBJECT +public: + static QSharedPointer create(); + ImdnStatusList(QObject *parent = Q_NULLPTR); + ~ImdnStatusList(); + + QList getImdnStatusList(); + void setImdnStatusList(QList imdnStatusList); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +signals: + void imdnStatusListChanged(); + +private: + QList mImdnStatuss; + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/core/chat/message/imdn/ImdnStatusProxy.cpp b/Linphone/core/chat/message/imdn/ImdnStatusProxy.cpp new file mode 100644 index 000000000..47e624bd2 --- /dev/null +++ b/Linphone/core/chat/message/imdn/ImdnStatusProxy.cpp @@ -0,0 +1,65 @@ + +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ImdnStatusProxy.hpp" +#include "ImdnStatusList.hpp" +#include "core/App.hpp" +// #include "core/chat/message/ChatMessageGui.hpp" + +DEFINE_ABSTRACT_OBJECT(ImdnStatusProxy) + +ImdnStatusProxy::ImdnStatusProxy(QObject *parent) : LimitProxy(parent) { + mList = ImdnStatusList::create(); + setSourceModel(mList.get()); + connect(mList.get(), &ImdnStatusList::modelReset, this, &ImdnStatusProxy::imdnStatusListChanged); + connect(this, &ImdnStatusProxy::filterChanged, this, [this] { invalidate(); }); +} + +ImdnStatusProxy::~ImdnStatusProxy() { +} + +QList ImdnStatusProxy::getImdnStatusList() { + return mList->getImdnStatusList(); +} + +void ImdnStatusProxy::setImdnStatusList(QList statusList) { + mList->setImdnStatusList(statusList); +} + +LinphoneEnums::ChatMessageState ImdnStatusProxy::getFilter() const { + return mFilter; +} + +void ImdnStatusProxy::setFilter(LinphoneEnums::ChatMessageState filter) { + if (mFilter != filter) { + mFilter = filter; + emit filterChanged(); + } +} + +bool ImdnStatusProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { + auto imdn = mList->getAt(sourceRow); + return imdn.mState == mFilter; +} + +bool ImdnStatusProxy::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { + return true; +} diff --git a/Linphone/core/chat/message/imdn/ImdnStatusProxy.hpp b/Linphone/core/chat/message/imdn/ImdnStatusProxy.hpp new file mode 100644 index 000000000..27219c11a --- /dev/null +++ b/Linphone/core/chat/message/imdn/ImdnStatusProxy.hpp @@ -0,0 +1,62 @@ + +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef IMDN_STATUS_PROXY_H_ +#define IMDN_STATUS_PROXY_H_ + +#include "core/chat/message/ChatMessageCore.hpp" +#include "core/proxy/LimitProxy.hpp" +#include "tool/AbstractObject.hpp" + +// ============================================================================= + +class ImdnStatusList; + +class ImdnStatusProxy : public LimitProxy, public AbstractObject { + Q_OBJECT + Q_PROPERTY( + QList imdnStatusList READ getImdnStatusList WRITE setImdnStatusList NOTIFY imdnStatusListChanged) + Q_PROPERTY(LinphoneEnums::ChatMessageState filter READ getFilter WRITE setFilter NOTIFY filterChanged) + +public: + ImdnStatusProxy(QObject *parent = Q_NULLPTR); + ~ImdnStatusProxy(); + + QList getImdnStatusList(); + void setImdnStatusList(QList imdnStatusList); + + LinphoneEnums::ChatMessageState getFilter() const; + void setFilter(LinphoneEnums::ChatMessageState filter); + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override; + +signals: + void imdnStatusListChanged(); + void filterChanged(); + +protected: + LinphoneEnums::ChatMessageState mFilter; + QSharedPointer mList; + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/core/emoji/EmojiList.hpp b/Linphone/core/emoji/EmojiList.hpp index 8a2ad9de7..1ed5e6dea 100644 --- a/Linphone/core/emoji/EmojiList.hpp +++ b/Linphone/core/emoji/EmojiList.hpp @@ -41,9 +41,6 @@ public: virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; -signals: - void reactionsChanged(); - private: QList mReactions; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/core/emoji/EmojiProxy.cpp b/Linphone/core/emoji/EmojiProxy.cpp index cd904e1e2..1123faca2 100644 --- a/Linphone/core/emoji/EmojiProxy.cpp +++ b/Linphone/core/emoji/EmojiProxy.cpp @@ -29,7 +29,7 @@ DEFINE_ABSTRACT_OBJECT(EmojiProxy) EmojiProxy::EmojiProxy(QObject *parent) : LimitProxy(parent) { mList = EmojiList::create(); setSourceModel(mList.get()); - connect(mList.get(), &EmojiList::reactionsChanged, this, &EmojiProxy::reactionsChanged); + connect(mList.get(), &EmojiList::modelReset, this, &EmojiProxy::reactionsChanged); connect(this, &EmojiProxy::filterChanged, this, [this] { invalidate(); }); } diff --git a/Linphone/data/languages/de.ts b/Linphone/data/languages/de.ts index 51642f759..f52e39627 100644 --- a/Linphone/data/languages/de.ts +++ b/Linphone/data/languages/de.ts @@ -13,13 +13,13 @@ AbstractWindow - + contact_dialog_pick_phone_number_or_sip_address_title "Choisissez un numéro ou adresse SIP" Telefonnummer oder SIP-Adresse wählen - + fps_counter %1 FPS @@ -523,7 +523,7 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Möchten Sie die Remote-Konfiguration von dieser Adresse herunterladen und anwenden? @@ -1758,18 +1758,18 @@ ChatListView - + chat_message_is_writing_info %1 is writing… - + chat_message_draft_sending_text - + chat_room_delete "Supprimer" @@ -1787,13 +1787,13 @@ - + 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 ? @@ -1802,31 +1802,37 @@ 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_see_status + "See message status" + + + + chat_message_delete "Delete" @@ -1916,19 +1922,19 @@ Error ChatMessageCore - + all_reactions_label "Reactions": all reactions for one message label - + info_toast_deleted_title Deleted - + info_toast_deleted_message The message has been deleted @@ -1981,20 +1987,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… @@ -3089,7 +3095,7 @@ Error Apply - + group_infos_call "Appel" Anrufen @@ -3107,19 +3113,19 @@ Error Unmute - + group_infos_meeting "Réunion" Meeting - + group_infos_participants Participants (%1) - - + + group_infos_media_docs Medien & Dokumente @@ -3129,55 +3135,55 @@ Error Geteilte Medien - + group_infos_shared_docs Geteilte Dokumente - + group_infos_other_actions Weitere Aktionen - + group_infos_enable_ephemerals Flüchtige Nachrichten aktivieren - + group_infos_disable_ephemerals Flüchtige Nachrichten deaktivieren - + group_infos_leave_room - + group_infos_delete_history Verlauf löschen - + group_infos_delete_history_toast_title Delete history ? Verlauf löschen? - + group_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? - + group_infos_leave_room_toast_title Leave Chat Room ? Chatraum verlassen? - + group_infos_leave_room_toast_message All the messages will be removed from the chat room. Do you want to continue ? Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? @@ -3551,102 +3557,102 @@ Error MainLayout - + bottom_navigation_calls_label "Appels" Anrufe - + bottom_navigation_contacts_label "Contacts" Kontakte - + bottom_navigation_conversations_label "Conversations" Konversationen - + bottom_navigation_meetings_label "Réunions" Besprechungen - + searchbar_placeholder_text "Rechercher un contact, appeler %1" Kontakt suchen, %1 anrufen - + searchbar_placeholder_text_chat_feature_enabled "ou envoyer un message …" oder eine Nachricht senden … - - + + contact_presence_status_disable_do_not_disturb "Désactiver ne pas déranger" Nicht stören deaktivieren - + information_popup_error_title Fehler - + no_voicemail_uri_error_message "L'URI de messagerie vocale n'est pas définie." Die Voicemail-URI ist nicht definiert. - + drawer_menu_manage_account Mon compte Mein Konto - + contact_presence_status_enable_do_not_disturb "Activer ne pas déranger" Nicht stören aktivieren - + settings_title Einstellungen - + recordings_title "Enregistrements" Aufnahmen - + help_title "Aide" Hilfe - + help_quit_title "Quitter l'application" App beenden - + quit_app_question "Quitter %1 ?" %1 beenden? - + drawer_menu_add_account "Ajouter un compte" Konto hinzufügen @@ -3655,78 +3661,78 @@ Error MainWindow - + information_popup_connexion_succeed_title "Connexion réussie" Verbindung erfolgreich - + information_popup_connexion_succeed_message "Vous êtes connecté en mode %1" Sie sind im %1-Modus verbunden - + interoperable interopérable interoperabel - + call_transfer_successful_toast_title "Appel transféré" Anruf weitergeleitet - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Ihr Gesprächspartner wurde an den ausgewählten Kontakt weitergeleitet - + information_popup_success_title Gespeichert - + information_popup_changes_saved "Les changements ont été sauvegardés" Änderungen wurden gespeichert - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Bitte das Captcha auf der Webseite bestätigen - + assistant_register_error_title "Erreur lors de la création" Fehler bei der Erstellung - + assistant_register_success_title "Compte créé" Konto erstellt - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" Das Konto wurde erstellt. Sie können sich jetzt anmelden. - + assistant_register_error_code "Erreur dans le code de validation" Fehler im Bestätigungscode - + information_popup_error_title Fehler @@ -3800,152 +3806,152 @@ Error Keine Besprechungen - + meeting_schedule_cancel_dialog_message "Souhaitez-vous annuler et supprimer cette réunion ?" Möchten Sie diese Besprechung absagen und löschen? - + meeting_schedule_delete_dialog_message Souhaitez-vous supprimer cette réunion ? Möchten Sie diese Besprechung löschen? - + meeting_schedule_cancel_and_delete_action "Annuler et supprimer" Absagen und löschen - + meeting_schedule_delete_only_action "Supprimer seulement" Nur löschen - + meeting_schedule_delete_action "Supprimer" Löschen - + back_action Retour Zurück - + meetings_list_title Réunions Besprechungen - + meetings_search_hint "Rechercher une réunion" Besprechung suchen - + list_filter_no_result_found "Aucun résultat…" Kein Ergebnis… - + meetings_empty_list "Aucune réunion" Keine Besprechungen - - + + meeting_schedule_title "Nouvelle réunion" Neue Besprechung - + create Erstellen - - - - - - + + + + + + information_popup_error_title Fehler - - + + meeting_schedule_mandatory_field_not_filled_toast Veuillez saisir un titre et sélectionner au moins un participant Bitte Titel bestimmen und mindestens einen Teilnehmer auswählen - - + + meeting_schedule_duration_error_toast "La fin de la conférence doit être plus récente que son début" Das Ende der Besprechung muss nach dem Beginn liegen - - + + meeting_schedule_creation_in_progress "Création de la réunion en cours …" Besprechung wird erstellt… - + meeting_info_created_toast "Réunion planifiée avec succès" Besprechung erfolgreich erstellt - + meeting_failed_to_schedule_toast "Échec de création de la réunion !" Besprechung konnte nicht erstellt werden! - + save Speichern - - + + saved "Enregistré" Gespeichert - + meeting_info_updated_toast "Réunion mise à jour" Besprechung geändert - + meeting_schedule_edit_in_progress "Modification de la réunion en cours…" Bersprechung wird geändert… - + meeting_failed_to_edit_toast "Échec de la modification de la réunion !" Besprechung konnte nicht geändert werden! - + meeting_schedule_add_participants_title "Ajouter des participants" Teilnehmer hinzufügen @@ -3957,12 +3963,12 @@ Error Apply - + add Hinzufügen - + group_call_participant_selected "%n participant(s) sélectionné(s)" @@ -3971,31 +3977,31 @@ Error - + meeting_info_delete "Supprimer la réunion" Besprechung löschen - + meeting_address_copied_to_clipboard_toast "Adresse de la réunion copiée" Besprechungs-URI kopiert - + meeting_schedule_timezone_title "Fuseau horaire" Zeitzone - + meeting_info_organizer_label "Organisateur" Organisator - + meeting_info_join_title "Rejoindre la réunion" Besprechung beitreten @@ -4023,15 +4029,30 @@ Error - MessageReactionsInfos + MessageImdnStatusInfos - - message_details_status title + + message_details_status_title Message status - + + click_to_delete_reaction_info + Click to delete + + + + + MessageReactionsInfos + + + message_details_reactions_title + Reactions + + + + click_to_delete_reaction_info Click to delete @@ -4454,31 +4475,91 @@ Error Post-quantum ZRTP - + + message_state_in_progress + "delivery in progress" + + + + + message_state_delivered + sent + + + + + message_state_not_delivered + error + + + + + message_state_file_transfer_error + cannot get file from server + + + + + message_state_file_transfer_done + file transfer has been completed successfully + + + + + message_state_delivered_to_user + received + + + + + message_state_displayed + read + + + + + message_state_file_transfer__in_progress + file transfer in progress + + + + + message_state_pending_delivery + pending delivery + + + + + message_state_file_transfer_cancelling + file transfer canceled + + + + incoming "Entrant" Eingehend - + outgoing "Sortant" Ausgehend - + conference_layout_active_speaker "Participant actif" Aktiver Sprecher - + conference_layout_grid "Mosaïque" Raster - + conference_layout_audio_only "Audio uniquement" Nur Audio diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 0a3108736..92972c053 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -13,13 +13,13 @@ AbstractWindow - + contact_dialog_pick_phone_number_or_sip_address_title "Choisissez un numéro ou adresse SIP" Choose a SIP number or address - + fps_counter %1 FPS @@ -523,7 +523,7 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Do you want to download and apply remote provisioning from this address ? @@ -1720,18 +1720,18 @@ ChatListView - + chat_message_is_writing_info %1 is writing… %1 is writing… - + chat_message_draft_sending_text Draft : %1 - + chat_room_delete "Supprimer" Delete @@ -1749,13 +1749,13 @@ Unmute - + 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 ? @@ -1764,31 +1764,37 @@ 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_see_status + "See message status" + See message status + + + chat_message_delete "Delete" Delete @@ -1878,19 +1884,19 @@ Error ChatMessageCore - + all_reactions_label "Reactions": all reactions for one message label Reactions - + info_toast_deleted_title Deleted Deleted - + info_toast_deleted_message The message has been deleted The message has been deleted @@ -1943,13 +1949,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. @@ -1957,7 +1963,7 @@ Error Only your correspondent can decrypt them. - + chat_message_is_writing_info %1 is writing… %1 is writing… @@ -3012,7 +3018,7 @@ Only your correspondent can decrypt them. Apply - + group_infos_call "Appel" Call @@ -3030,77 +3036,72 @@ Only your correspondent can decrypt them. Unmute - + group_infos_meeting "Réunion" Meeting - + group_infos_participants Participants (%1) - - + + group_infos_media_docs Medias & documents - group_infos_shared_media - Shared medias - Shared medias - - - + group_infos_shared_docs Shared documents - + group_infos_other_actions Other actions - + group_infos_enable_ephemerals Enable ephemeral messages - + group_infos_disable_ephemerals Disable ephemeral messages - + group_infos_delete_history Delete history - + group_infos_delete_history_toast_title Delete history ? Delete history ? - + group_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? All the messages will be removed from the chat room. Do you want to continue ? - + group_infos_leave_room Leave Chat Room - + group_infos_leave_room_toast_title Leave Chat Room ? Leave Chat Room ? - + group_infos_leave_room_toast_message All the messages will be removed from the chat room. Do you want to continue ? All the messages will be removed from the chat room. Do you want to continue ? @@ -3469,102 +3470,102 @@ Only your correspondent can decrypt them. MainLayout - + bottom_navigation_calls_label "Appels" Calls - + bottom_navigation_contacts_label "Contacts" Contacts - + bottom_navigation_conversations_label "Conversations" Conversations - + bottom_navigation_meetings_label "Réunions" Meetings - + searchbar_placeholder_text "Rechercher un contact, appeler %1" Find contact, call %1 - + searchbar_placeholder_text_chat_feature_enabled "ou envoyer un message …" or send message … - - + + contact_presence_status_disable_do_not_disturb "Désactiver ne pas déranger" Disable do not disturb - + information_popup_error_title Error - + no_voicemail_uri_error_message "L'URI de messagerie vocale n'est pas définie." The voicemail URI is not defined. - + drawer_menu_manage_account Mon compte My account - + contact_presence_status_enable_do_not_disturb "Activer ne pas déranger" Enable do not disturb - + settings_title Settings - + recordings_title "Enregistrements" Records - + help_title "Aide" Help - + help_quit_title "Quitter l'application" Quit the app - + quit_app_question "Quitter %1 ?" Quit %1 ? - + drawer_menu_add_account "Ajouter un compte" Add an account @@ -3573,78 +3574,78 @@ Only your correspondent can decrypt them. MainWindow - + information_popup_connexion_succeed_title "Connexion réussie" Connection succeed - + information_popup_connexion_succeed_message "Vous êtes connecté en mode %1" You are logged in %1 mode - + interoperable interopérable interoperable - + call_transfer_successful_toast_title "Appel transféré" Call forwarded - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Your correspondent has been transferred to the selected contact - + information_popup_success_title Saved - + information_popup_changes_saved "Les changements ont été sauvegardés" Changes have been saved - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Please validate the captcha on the web page - + assistant_register_error_title "Erreur lors de la création" Error while creating - + assistant_register_success_title "Compte créé" Account created - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" The account has been created. You can now log in. - + assistant_register_error_code "Erreur dans le code de validation" Error in validation code - + information_popup_error_title Error @@ -3718,152 +3719,152 @@ Only your correspondent can decrypt them. No meeting - + meeting_schedule_cancel_dialog_message "Souhaitez-vous annuler et supprimer cette réunion ?" Would you like to cancel and delete this meeting? - + meeting_schedule_delete_dialog_message Souhaitez-vous supprimer cette réunion ? Would you like to delete this meeting? - + meeting_schedule_cancel_and_delete_action "Annuler et supprimer" Cancel and delete - + meeting_schedule_delete_only_action "Supprimer seulement" Delete only - + meeting_schedule_delete_action "Supprimer" Delete - + back_action Retour Back - + meetings_list_title Réunions Meetings - + meetings_search_hint "Rechercher une réunion" Find meeting - + list_filter_no_result_found "Aucun résultat…" No result… - + meetings_empty_list "Aucune réunion" No meeting - - + + meeting_schedule_title "Nouvelle réunion" New meeting - + create Create - - - - - - + + + + + + information_popup_error_title Error - - + + meeting_schedule_mandatory_field_not_filled_toast Veuillez saisir un titre et sélectionner au moins un participant Please fill the title and select at least one participant - - + + meeting_schedule_duration_error_toast "La fin de la conférence doit être plus récente que son début" The end of the conference must be more recent than its beginning - - + + meeting_schedule_creation_in_progress "Création de la réunion en cours …" Creation in progress… - + meeting_info_created_toast "Réunion planifiée avec succès" Meeting successfully created - + meeting_failed_to_schedule_toast "Échec de création de la réunion !" Failed to create meeting! - + save Save - - + + saved "Enregistré" Saved - + meeting_info_updated_toast "Réunion mise à jour" Meeting updated - + meeting_schedule_edit_in_progress "Modification de la réunion en cours…" Meeting update in progress… - + meeting_failed_to_edit_toast "Échec de la modification de la réunion !" Failed to update meeting ! - + meeting_schedule_add_participants_title "Ajouter des participants" Add participants @@ -3875,12 +3876,12 @@ Only your correspondent can decrypt them. Apply - + add Add - + group_call_participant_selected "%n participant(s) sélectionné(s)" @@ -3889,31 +3890,31 @@ Only your correspondent can decrypt them. - + meeting_info_delete "Supprimer la réunion" Delete meeting - + meeting_address_copied_to_clipboard_toast "Adresse de la réunion copiée" Meeting URI copied - + meeting_schedule_timezone_title "Fuseau horaire" Timezone - + meeting_info_organizer_label "Organisateur" Organizer - + meeting_info_join_title "Rejoindre la réunion" Join meeting @@ -3941,15 +3942,30 @@ Only your correspondent can decrypt them. - MessageReactionsInfos + MessageImdnStatusInfos - - message_details_status title + + message_details_status_title Message status Message status - + + click_to_delete_reaction_info + Click to delete + Click to delete + + + + MessageReactionsInfos + + + message_details_reactions_title + Reactions + Reactions + + + click_to_delete_reaction_info Click to delete Click to delete @@ -4355,31 +4371,91 @@ Only your correspondent can decrypt them. Post quantum ZRTP - + + message_state_in_progress + "delivery in progress" + delivery in progress + + + + message_state_delivered + sent + sent + + + + message_state_not_delivered + error + error + + + + message_state_file_transfer_error + cannot get file from server + cannot get file from server + + + + message_state_file_transfer_done + file transfer has been completed successfully + file transfer has been completed successfully + + + + message_state_delivered_to_user + received + received + + + + message_state_displayed + read + read + + + + message_state_file_transfer__in_progress + file transfer in progress + file transfer in progress + + + + message_state_pending_delivery + pending delivery + pending delivery + + + + message_state_file_transfer_cancelling + file transfer canceled + file transfer canceled + + + incoming "Entrant" Incoming - + outgoing "Sortant" Outgoing - + conference_layout_active_speaker "Participant actif" Active speaker - + conference_layout_grid "Mosaïque" Grid - + conference_layout_audio_only "Audio uniquement" Audio only diff --git a/Linphone/data/languages/fr_FR.ts b/Linphone/data/languages/fr_FR.ts index 883986fad..15cf8df15 100644 --- a/Linphone/data/languages/fr_FR.ts +++ b/Linphone/data/languages/fr_FR.ts @@ -13,13 +13,13 @@ AbstractWindow - + contact_dialog_pick_phone_number_or_sip_address_title "Choisissez un numéro ou adresse SIP" Choisissez un numéro ou adresse SIP - + fps_counter %1 FPS @@ -523,7 +523,7 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? @@ -1720,18 +1720,18 @@ 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 "Supprimer" Supprimer @@ -1749,13 +1749,13 @@ Enlever la sourdine - + 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 ? @@ -1764,31 +1764,37 @@ 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_see_status + "See message status" + Voir les statuts du message + + + chat_message_delete "Delete" Supprimer @@ -1878,19 +1884,19 @@ Error ChatMessageCore - + all_reactions_label "Reactions": all reactions for one message label Réactions - + info_toast_deleted_title Deleted Supprimé - + info_toast_deleted_message The message has been deleted Le message a été supprimé @@ -1943,13 +1949,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. @@ -1957,7 +1963,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… @@ -3012,7 +3018,7 @@ en bout. Seul votre correspondant peut les déchiffrer. Appliquer - + group_infos_call "Appel" Appel @@ -3030,77 +3036,72 @@ en bout. Seul votre correspondant peut les déchiffrer. Réactiver les notifications - + group_infos_meeting "Réunion" Réunion - + group_infos_participants Participants (%1) - - + + group_infos_media_docs Medias & documents - group_infos_shared_media - Shared medias - Médias partagés - - - + group_infos_shared_docs Documents partagés - + group_infos_other_actions Autres actions - + group_infos_enable_ephemerals Activer les messages éphémères - + group_infos_disable_ephemerals Désactiver les messages éphémères - + group_infos_delete_history Supprimer l'historique - + group_infos_delete_history_toast_title Delete history ? Supprimer l'historique ? - + group_infos_delete_history_toast_message All the messages will be removed from the chat room. Do you want to continue ? Tous les messages seront supprimés. Souhaitez-vous continuer ? - + group_infos_leave_room Quitter la conversation - + group_infos_leave_room_toast_title Leave Chat Room ? Quitter la conversation ? - + group_infos_leave_room_toast_message All the messages will be removed from the chat room. Do you want to continue ? Vous ne recevrez ni pourrez envoyer des messages dans cette conversation, quitter ? @@ -3469,102 +3470,102 @@ en bout. Seul votre correspondant peut les déchiffrer. MainLayout - + bottom_navigation_calls_label "Appels" Appels - + bottom_navigation_contacts_label "Contacts" Contacts - + bottom_navigation_conversations_label "Conversations" Conversations - + bottom_navigation_meetings_label "Réunions" Réunions - + searchbar_placeholder_text "Rechercher un contact, appeler %1" Rechercher un contact, appeler %1 - + searchbar_placeholder_text_chat_feature_enabled "ou envoyer un message …" ou envoyer un message … - - + + contact_presence_status_disable_do_not_disturb "Désactiver ne pas déranger" Désactiver ne pas déranger - + information_popup_error_title Erreur - + no_voicemail_uri_error_message "L'URI de messagerie vocale n'est pas définie." L'URI de messagerie vocale n'est pas définie. - + drawer_menu_manage_account Mon compte Mon compte - + contact_presence_status_enable_do_not_disturb "Activer ne pas déranger" Activer ne pas déranger - + settings_title Paramètres - + recordings_title "Enregistrements" Enregistrements - + help_title "Aide" Aide - + help_quit_title "Quitter l'application" Quitter l'application - + quit_app_question "Quitter %1 ?" Quitter %1 ? - + drawer_menu_add_account "Ajouter un compte" Ajouter un compte @@ -3573,78 +3574,78 @@ en bout. Seul votre correspondant peut les déchiffrer. MainWindow - + information_popup_connexion_succeed_title "Connexion réussie" Connexion réussie - + information_popup_connexion_succeed_message "Vous êtes connecté en mode %1" Vous êtes connecté en mode %1 - + interoperable interopérable interopérable - + call_transfer_successful_toast_title "Appel transféré" Appel transféré - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Votre correspondant a été transféré au contact sélectionné - + information_popup_success_title Enregistré - + information_popup_changes_saved "Les changements ont été sauvegardés" Les changements ont été sauvegardés - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Veuillez valider le captcha sur la page web - + assistant_register_error_title "Erreur lors de la création" Erreur lors de la création - + assistant_register_success_title "Compte créé" Compte créé - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" Le compte a été créé. Vous pouvez maintenant vous connecter - + assistant_register_error_code "Erreur dans le code de validation" Erreur dans le code de validation - + information_popup_error_title Erreur @@ -3718,152 +3719,152 @@ en bout. Seul votre correspondant peut les déchiffrer. Aucune réunion - + meeting_schedule_cancel_dialog_message "Souhaitez-vous annuler et supprimer cette réunion ?" Souhaitez-vous annuler et supprimer cette réunion ? - + meeting_schedule_delete_dialog_message Souhaitez-vous supprimer cette réunion ? Souhaitez-vous supprimer cette réunion ? - + meeting_schedule_cancel_and_delete_action "Annuler et supprimer" Annuler et supprimer - + meeting_schedule_delete_only_action "Supprimer seulement" Supprimer seulement - + meeting_schedule_delete_action "Supprimer" Supprimer - + back_action Retour Retour - + meetings_list_title Réunions Réunions - + meetings_search_hint "Rechercher une réunion" Rechercher une réunion - + list_filter_no_result_found "Aucun résultat…" Aucun résultat… - + meetings_empty_list "Aucune réunion" Aucune réunion - - + + meeting_schedule_title "Nouvelle réunion" Nouvelle réunion - + create Créer - - - - - - + + + + + + information_popup_error_title Erreur - - + + meeting_schedule_mandatory_field_not_filled_toast Veuillez saisir un titre et sélectionner au moins un participant Veuillez saisir un titre et sélectionner au moins un participant - - + + meeting_schedule_duration_error_toast "La fin de la conférence doit être plus récente que son début" La fin de la conférence doit être plus récente que son début - - + + meeting_schedule_creation_in_progress "Création de la réunion en cours …" Création de la réunion en cours… - + meeting_info_created_toast "Réunion planifiée avec succès" Réunion planifiée avec succès - + meeting_failed_to_schedule_toast "Échec de création de la réunion !" Échec de création de la réunion ! - + save Enregistrer - - + + saved "Enregistré" Enregistré - + meeting_info_updated_toast "Réunion mise à jour" Réunion mise à jour - + meeting_schedule_edit_in_progress "Modification de la réunion en cours…" Modification de la réunion en cours… - + meeting_failed_to_edit_toast "Échec de la modification de la réunion !" Échec de la modification de la réunion ! - + meeting_schedule_add_participants_title "Ajouter des participants" Ajouter des participants @@ -3875,12 +3876,12 @@ en bout. Seul votre correspondant peut les déchiffrer. Appliquer - + add Ajouter - + group_call_participant_selected "%n participant(s) sélectionné(s)" @@ -3889,31 +3890,31 @@ en bout. Seul votre correspondant peut les déchiffrer. - + meeting_info_delete "Supprimer la réunion" Supprimer la réunion - + meeting_address_copied_to_clipboard_toast "Adresse de la réunion copiée" Adresse de la réunion copiée - + meeting_schedule_timezone_title "Fuseau horaire" Fuseau horaire - + meeting_info_organizer_label "Organisateur" Organisateur - + meeting_info_join_title "Rejoindre la réunion" Rejoindre la réunion @@ -3941,15 +3942,30 @@ en bout. Seul votre correspondant peut les déchiffrer. - MessageReactionsInfos + MessageImdnStatusInfos - - message_details_status title + + message_details_status_title Message status Statut du message - + + click_to_delete_reaction_info + Click to delete + Appuyez pour supprimer + + + + MessageReactionsInfos + + + message_details_reactions_title + Reactions + Réactions + + + click_to_delete_reaction_info Click to delete Appuyez pour supprimer @@ -4355,31 +4371,91 @@ en bout. Seul votre correspondant peut les déchiffrer. ZRTP - Post quantique - + + message_state_in_progress + "delivery in progress" + envoi en cours + + + + message_state_delivered + sent + envoyé + + + + message_state_not_delivered + error + en erreur + + + + message_state_file_transfer_error + cannot get file from server + impossible de récupérer le fichier depuis le serveur + + + + message_state_file_transfer_done + file transfer has been completed successfully + fichier transféré avec succès + + + + message_state_delivered_to_user + received + reçu + + + + message_state_displayed + read + lu + + + + message_state_file_transfer__in_progress + file transfer in progress + transfert du fichier en cours + + + + message_state_pending_delivery + pending delivery + envoi en attente + + + + message_state_file_transfer_cancelling + file transfer canceled + transfert du fichier annulé + + + incoming "Entrant" Entrant - + outgoing "Sortant" Sortant - + conference_layout_active_speaker "Participant actif" Intervenant actif - + conference_layout_grid "Mosaïque" Mosaïque - + conference_layout_audio_only "Audio uniquement" Audio uniquement diff --git a/Linphone/tool/LinphoneEnums.cpp b/Linphone/tool/LinphoneEnums.cpp index 8bebda391..179df9f1a 100644 --- a/Linphone/tool/LinphoneEnums.cpp +++ b/Linphone/tool/LinphoneEnums.cpp @@ -118,6 +118,41 @@ LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::Chat return static_cast(data); } +QString LinphoneEnums::toString(const LinphoneEnums::ChatMessageState &data) { + switch (data) { + case LinphoneEnums::ChatMessageState::StateInProgress: + //: "delivery in progress" + return QObject::tr("message_state_in_progress"); + case LinphoneEnums::ChatMessageState::StateDelivered: + //: sent + return QObject::tr("message_state_delivered"); + case LinphoneEnums::ChatMessageState::StateNotDelivered: + //: error + return QObject::tr("message_state_not_delivered"); + case LinphoneEnums::ChatMessageState::StateFileTransferError: + //: cannot get file from server + return QObject::tr("message_state_file_transfer_error"); + case LinphoneEnums::ChatMessageState::StateFileTransferDone: + //: file transfer has been completed successfully + return QObject::tr("message_state_file_transfer_done"); + case LinphoneEnums::ChatMessageState::StateDeliveredToUser: + //: received + return QObject::tr("message_state_delivered_to_user"); + case LinphoneEnums::ChatMessageState::StateDisplayed: + //: read + return QObject::tr("message_state_displayed"); + case LinphoneEnums::ChatMessageState::StateFileTransferInProgress: + //: file transfer in progress + return QObject::tr("message_state_file_transfer__in_progress"); + case LinphoneEnums::ChatMessageState::StatePendingDelivery: + //: pending delivery + return QObject::tr("message_state_pending_delivery"); + case LinphoneEnums::ChatMessageState::StateFileTransferCancelling: + //: file transfer canceled + return QObject::tr("message_state_file_transfer_cancelling"); + } +} + linphone::ChatRoom::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatRoomState &data) { return static_cast(data); } diff --git a/Linphone/tool/LinphoneEnums.hpp b/Linphone/tool/LinphoneEnums.hpp index 8e1293401..d13deccca 100644 --- a/Linphone/tool/LinphoneEnums.hpp +++ b/Linphone/tool/LinphoneEnums.hpp @@ -97,12 +97,15 @@ enum class ChatMessageState { StateFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), StateDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), StateDisplayed = int(linphone::ChatMessage::State::Displayed), - StateFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress) + StateFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress), + StatePendingDelivery = int(linphone::ChatMessage::State::PendingDelivery), + StateFileTransferCancelling = int(linphone::ChatMessage::State::FileTransferCancelling) }; Q_ENUM_NS(ChatMessageState) linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState &data); LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State &data); +QString toString(const LinphoneEnums::ChatMessageState &data); enum class ChatRoomState { None = int(linphone::ChatRoom::State::None), diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 873cf8de7..a77799429 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -329,7 +329,8 @@ QString Utils::formatElapsedTime(int seconds, bool dotsSeparator) { else return (h == 0 ? "" : hours + "h ") + (m == 0 ? "" : min + "min ") + sec + "s"; } -QString Utils::formatDate(const QDateTime &date, bool includeTime, bool includeDateIfToday, QString format) { +QString Utils::formatDate(QDateTime date, bool includeTime, bool includeDateIfToday, QString format) { + date = getOffsettedUTC(date); QString dateDay; //: "Aujourd'hui" if (date.date() == QDate::currentDate()) dateDay = tr("today"); diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 4aa589157..cecd2e17f 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -90,7 +90,7 @@ public: Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path Q_INVOKABLE static QString formatElapsedTime(int seconds, bool dotsSeparator = true); // Return the elapsed time formated - Q_INVOKABLE static QString formatDate(const QDateTime &date, + Q_INVOKABLE static QString formatDate(QDateTime date, bool includeTime = true, bool includeDateIfToday = true, QString format = ""); // Return the date formated diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 40a8f3107..be6bfa06f 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -154,6 +154,8 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Page/Layout/Settings/NetworkSettingsLayout.qml view/Page/Layout/Settings/AdvancedSettingsLayout.qml view/Page/Layout/Chat/GroupConversationInfos.qml + view/Page/Layout/Chat/MessageImdnStatusInfos.qml + view/Page/Layout/Chat/MessageInfosLayout.qml view/Page/Layout/Chat/MessageReactionsInfos.qml view/Page/Layout/Chat/OneOneConversationInfos.qml view/Page/Layout/Chat/ChatInfoActionsGroup.qml diff --git a/Linphone/view/Control/Container/Contact/ContactLayout.qml b/Linphone/view/Control/Container/Contact/ContactLayout.qml index 1507a7f49..abd596367 100644 --- a/Linphone/view/Control/Container/Contact/ContactLayout.qml +++ b/Linphone/view/Control/Container/Contact/ContactLayout.qml @@ -69,8 +69,8 @@ ColumnLayout { } } PresenceNoteLayout { - visible: contact.core.presenceNote.length > 0 && mainItem.useVerticalLayout - friendCore: contact.core + visible: contact?.core.presenceNote.length > 0 && mainItem.useVerticalLayout + friendCore: contact?.core || null Layout.preferredWidth: 412 * DefaultStyle.dp Layout.preferredHeight: 85 * DefaultStyle.dp } @@ -92,10 +92,10 @@ ColumnLayout { Layout.fillWidth:true Layout.preferredHeight: 79 * DefaultStyle.dp color: 'transparent' - visible: contact.core.presenceNote.length > 0 && !mainItem.useVerticalLayout + visible: contact && contact.core.presenceNote.length > 0 && !mainItem.useVerticalLayout PresenceNoteLayout { anchors.centerIn: parent - friendCore: contact.core + friendCore: contact?.core || null width: 412 * DefaultStyle.dp height: 85 * DefaultStyle.dp } diff --git a/Linphone/view/Control/Container/TabBar.qml b/Linphone/view/Control/Container/TabBar.qml index 1ae264c85..c2e56c7f7 100644 --- a/Linphone/view/Control/Container/TabBar.qml +++ b/Linphone/view/Control/Container/TabBar.qml @@ -57,15 +57,20 @@ Control.TabBar { delay: 1000 text: modelData } + MouseArea{ + anchors.fill: parent + cursorShape: tabButton.hovered ? Qt.PointingHandCursor: Qt.ArrowCursor + acceptedButtons: Qt.NoButton + } background: Item { anchors.fill: parent + visible: mainItem.currentIndex === index || tabButton.hovered Rectangle { id: tabBackground - visible: mainItem.currentIndex === index height: Math.round(5 * DefaultStyle.dp) - color: DefaultStyle.main1_500_main + color: mainItem.currentIndex === index ? DefaultStyle.main1_500_main : DefaultStyle.main2_400 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right @@ -86,9 +91,12 @@ Control.TabBar { contentItem: Text { id: tabText width: Math.min(implicitWidth, mainItem.width / mainItem.model.length) - font.weight: mainItem.textWeight + font { + pixelSize: mainItem.pixelSize + weight: mainItem.textWeight + capitalization: Font.Capitalize + } color: mainItem.currentIndex === index ? DefaultStyle.main2_600 : DefaultStyle.main2_400 - font.pixelSize: mainItem.pixelSize elide: Text.ElideRight maximumLineCount: 1 text: modelData diff --git a/Linphone/view/Control/Display/Chat/ChatMessage.qml b/Linphone/view/Control/Display/Chat/ChatMessage.qml index b080c21f2..8e849ec7e 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessage.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessage.qml @@ -29,6 +29,7 @@ Control.Control { signal messageDeletionRequested() signal isFileHoveringChanged(bool isFileHovering) signal showReactionsForMessageRequested() + signal showImdnStatusForMessageRequested() background: Item { anchors.fill: parent @@ -241,14 +242,32 @@ Control.Control { icon.source: AppIcons.copy // spacing: Math.round(10 * DefaultStyle.dp) Layout.fillWidth: true - Layout.preferredHeight: 45 * DefaultStyle.dp + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) onClicked: { var success = UtilsCpp.copyToClipboard(chatBubbleContent.selectedText != "" ? chatBubbleContent.selectedText : mainItem.chatMessage.core.text) //: Copied if (success) UtilsCpp.showInformationPopup(qsTr("chat_message_copied_to_clipboard_title"), //: "to clipboard" qsTr("chat_message_copied_to_clipboard_toast")) - } + optionsMenu.close() + } + } + IconLabelButton { + inverseLayout: true + //: "See message status" + text: qsTr("chat_message_see_status") + icon.source: AppIcons.chatTeardropText + Layout.fillWidth: true + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + onClicked: { + mainItem.showImdnStatusForMessageRequested() + optionsMenu.close() + } + } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(1, Math.round(1 * DefaultStyle.dp)) + color: DefaultStyle.main2_400 } IconLabelButton { inverseLayout: true @@ -257,18 +276,13 @@ Control.Control { icon.source: AppIcons.trashCan // spacing: Math.round(10 * DefaultStyle.dp) Layout.fillWidth: true - Layout.preferredHeight: 45 * DefaultStyle.dp + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) onClicked: { mainItem.messageDeletionRequested() optionsMenu.close() } style: ButtonStyle.hoveredBackgroundRed } - // Rectangle { - // Layout.fillWidth: true - // Layout.preferredHeight: Math.round(1 * DefaultStyle.dp) - // color: DefaultStyle.main2_200 - // } } } PopupButton { diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index fd0f8ed0c..3cc031c4c 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -15,6 +15,7 @@ ListView { property ChatGui chat property color backgroundColor signal showReactionsForMessageRequested(ChatMessageGui chatMessage) + signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) Component.onCompleted: { var index = eventLogProxy.findFirstUnreadIndex() @@ -135,6 +136,7 @@ ListView { onMessageDeletionRequested: modelData.core.lDelete() onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(modelData) + onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(modelData) } } diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 5828c9074..cbc1c1af5 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -156,6 +156,11 @@ RowLayout { contentLoader.showingMessageReactions = true detailsPanel.visible = true } + onShowImdnStatusForMessageRequested: (chatMessage) => { + mainItem.chatMessage = chatMessage + contentLoader.showingImdnStatus = true + detailsPanel.visible = true + } Popup { id: emojiPickerPopup @@ -322,7 +327,10 @@ RowLayout { visible: false Layout.fillHeight: true Layout.preferredWidth: Math.round(387 * DefaultStyle.dp) - onVisibleChanged: if(!visible) contentLoader.showingMessageReactions = false + onVisibleChanged: if(!visible) { + contentLoader.showingMessageReactions = false + contentLoader.showingImdnStatus = false + } background: Rectangle { color: DefaultStyle.grey_0 @@ -332,13 +340,16 @@ RowLayout { contentItem: Loader { id: contentLoader property bool showingMessageReactions: false + property bool showingImdnStatus: false anchors.top: parent.top anchors.topMargin: Math.round(39 * DefaultStyle.dp) sourceComponent: showingMessageReactions ? messageReactionsComponent - : mainItem.chat.core.isGroupChat - ? groupInfoComponent - : oneToOneInfoComponent + : showingImdnStatus + ? messageImdnStatusComponent + : mainItem.chat.core.isGroupChat + ? groupInfoComponent + : oneToOneInfoComponent active: detailsPanel.visible onLoaded: { if (contentLoader.item) { @@ -371,5 +382,15 @@ RowLayout { } } } + Component { + id: messageImdnStatusComponent + MessageImdnStatusInfos { + chatMessageGui: mainItem.chatMessage + onGoBackRequested: { + detailsPanel.visible = false + mainItem.chatMessage = null + } + } + } } } \ No newline at end of file diff --git a/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml b/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml new file mode 100644 index 000000000..8e90bad85 --- /dev/null +++ b/Linphone/view/Page/Layout/Chat/MessageImdnStatusInfos.qml @@ -0,0 +1,74 @@ +import QtCore +import QtQuick +import QtQuick.Layouts +import Linphone +import UtilsCpp + +MessageInfosLayout { + id: mainItem + spacing: Math.round(25 * DefaultStyle.dp) + //: Message status + title: qsTr("message_details_status_title") + tabbarModel: chatMessageGui ? chatMessageGui.core.imdnStatusListAsString : [] + listModel: ImdnStatusProxy { + imdnStatusList: chatMessageGui ? chatMessageGui.core.imdnStatusList : [] + filter: chatMessageGui ? chatMessageGui.core.imdnStatusAsSingletons[mainItem.tabbar.currentIndex].state : "" + } + + listView.delegate: Item { + id: listDelegate + width: listView.width + height: delegateIn.implicitHeight + property var contactObj: modelData ? UtilsCpp.findFriendByAddress(modelData.address) : null + property FriendGui contact: contactObj && contactObj.value || null + property var nameObj: modelData ? UtilsCpp.getDisplayName(modelData.address) : null + property string updateTime: UtilsCpp.isCurrentDay(modelData.lastUpdatedTime) + ? UtilsCpp.toTimeString(modelData.lastUpdatedTime, "hh:mm") + : UtilsCpp.formatDate(modelData.lastUpdatedTime, true) + RowLayout { + id: delegateIn + anchors.fill: parent + spacing: Math.round(16 * DefaultStyle.dp) + Avatar { + Layout.alignment: Qt.AlignHCenter + contact: listDelegate.contact + displayNameVal: contact + ? "" + : nameObj + ? nameObj.value + : "" + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + } + ColumnLayout { + spacing: 0 + Text { + text: nameObj?.value || "" + font { + pixelSize: Typography.p1.pixelSize + weight: Typography.p1.weight + } + } + Text { + visible: listDelegate.contact + horizontalAlignment: Text.AlignLeft + Layout.fillWidth: true + text: listDelegate.contact ? listDelegate.contact.core.presenceStatus : "" + color: listDelegate.contact ? listDelegate.contact.core.presenceColor : 'transparent' + font { + pixelSize: Typography.p3.pixelSize + weight: Typography.p3.weight + } + } + } + Item{Layout.fillWidth: true} + Text { + text: listDelegate.updateTime + font { + pixelSize: Typography.p3.pixelSize + weight: Typography.p3.weight + } + } + } + } +} diff --git a/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml b/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml new file mode 100644 index 000000000..a00020b5f --- /dev/null +++ b/Linphone/view/Page/Layout/Chat/MessageInfosLayout.qml @@ -0,0 +1,63 @@ +import QtCore +import QtQuick +import QtQuick.Controls.Basic as Control +import QtQuick.Dialogs +import QtQuick.Effects +import QtQuick.Layouts +import Linphone +import UtilsCpp +import SettingsCpp +import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle + +ColumnLayout { + id: mainItem + + property string title + property ChatMessageGui chatMessageGui + property var tabbarModel + property var listModel + property var listDelegate + property alias tabbar: tabbar + property alias listView: listView + property var parentView + + spacing: Math.round(25 * DefaultStyle.dp) + + signal goBackRequested() + + RowLayout { + BigButton { + icon.source: AppIcons.leftArrow + style: ButtonStyle.noBackground + onClicked: mainItem.goBackRequested() + } + Text { + text: mainItem.title + font { + pixelSize: Typography.h4.pixelSize + weight: Typography.h4.weight + } + } + } + + ColumnLayout { + spacing: Math.round(21 * DefaultStyle.dp) + Layout.leftMargin: Math.round(16 * DefaultStyle.dp) + Layout.rightMargin: Math.round(16 * DefaultStyle.dp) + TabBar { + id: tabbar + Layout.fillWidth: true + model: mainItem.tabbarModel + pixelSize: Typography.h3m.pixelSize + textWeight: Typography.h3m.weight + } + + ListView { + id: listView + Layout.fillWidth: true + Layout.fillHeight: true + spacing: Math.round(11 * DefaultStyle.dp) + model: mainItem.listModel + } + } +} diff --git a/Linphone/view/Page/Layout/Chat/MessageReactionsInfos.qml b/Linphone/view/Page/Layout/Chat/MessageReactionsInfos.qml index 6c87f2eef..fd5786171 100644 --- a/Linphone/view/Page/Layout/Chat/MessageReactionsInfos.qml +++ b/Linphone/view/Page/Layout/Chat/MessageReactionsInfos.qml @@ -1,118 +1,77 @@ import QtCore import QtQuick -import QtQuick.Controls.Basic as Control -import QtQuick.Dialogs -import QtQuick.Effects import QtQuick.Layouts import Linphone import UtilsCpp -import SettingsCpp -import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle -ColumnLayout { +MessageInfosLayout { id: mainItem - property ChatMessageGui chatMessageGui - property var parentView spacing: Math.round(25 * DefaultStyle.dp) - signal goBackRequested() - - RowLayout { - BigButton { - icon.source: AppIcons.leftArrow - style: ButtonStyle.noBackground - onClicked: mainItem.goBackRequested() - } - Text { - //: Message status - text: qsTr("message_details_status title") - font { - pixelSize: Typography.h4.pixelSize - weight: Typography.h4.weight - } - } + //: Reactions + title: qsTr("message_details_reactions_title") + tabbarModel: chatMessageGui ? chatMessageGui.core.reactionsSingletonAsStrings : [] + listModel: EmojiProxy { + reactions: chatMessageGui ? chatMessageGui.core.reactions : [] + // First index of reactionsSingletonAsStrings list is all reactions combined which does not appear + // in reactionsSingleton list + filter: tabbar.currentIndex >=1 && chatMessageGui && chatMessageGui.core.reactionsSingleton[tabbar.currentIndex-1].body || "" } - - ColumnLayout { - spacing: Math.round(11 * DefaultStyle.dp) - Layout.leftMargin: Math.round(16 * DefaultStyle.dp) - Layout.rightMargin: Math.round(16 * DefaultStyle.dp) - TabBar { - id: tabbar - Layout.fillWidth: true - model: mainItem.chatMessageGui ? mainItem.chatMessageGui.core.reactionsSingletonAsStrings : [] - pixelSize: Typography.h3m.pixelSize - textWeight: Typography.h3m.weight + listView.delegate: Item { + width: listView.width + height: delegateIn.implicitHeight + property var contactObj: modelData ? UtilsCpp.findFriendByAddress(modelData.address) : null + property var nameObj: modelData ? UtilsCpp.getDisplayName(modelData.address) : null + property var isMeObj: modelData ? UtilsCpp.isMe(modelData.address) : null + MouseArea { + anchors.fill: parent + enabled: isMeObj && isMeObj.value + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + hoverEnabled: true + onClicked: mainItem.chatMessageGui.core.lRemoveReaction() } - - ListView { - id: reactionsList - Layout.fillWidth: true - Layout.fillHeight: true - spacing: Math.round(11 * DefaultStyle.dp) - model: EmojiProxy { - reactions: mainItem.chatMessageGui ? mainItem.chatMessageGui.core.reactions : [] - // First index of reactionsSingletonAsStrings list is all reactions combined which does not appear - // in reactionsSingleton list - filter: tabbar.currentIndex >=1 && mainItem.chatMessageGui && mainItem.chatMessageGui.core.reactionsSingleton[tabbar.currentIndex-1].body || "" + RowLayout { + id: delegateIn + anchors.fill: parent + spacing: Math.round(16 * DefaultStyle.dp) + Avatar { + Layout.alignment: Qt.AlignHCenter + contact: contactObj?.value || null + displayNameVal: contact + ? "" + : nameObj + ? nameObj.value + : "" + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) } - delegate: Item { - width: reactionsList.width - height: delegateIn.implicitHeight - property var contactObj: modelData ? UtilsCpp.findFriendByAddress(modelData.address) : null - property var nameObj: modelData ? UtilsCpp.getDisplayName(modelData.address) : null - property var isMeObj: modelData ? UtilsCpp.isMe(modelData.address) : null - MouseArea { - anchors.fill: parent - enabled: isMeObj && isMeObj.value - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - hoverEnabled: true - onClicked: mainItem.chatMessageGui.core.lRemoveReaction() + ColumnLayout { + Text { + text: nameObj?.value || "" + font { + pixelSize: Typography.p1.pixelSize + weight: Typography.p1.weight + } } - RowLayout { - id: delegateIn - anchors.fill: parent - spacing: Math.round(16 * DefaultStyle.dp) - Avatar { - Layout.alignment: Qt.AlignHCenter - contact: contactObj?.value || null - displayNameVal: contact - ? "" - : nameObj - ? nameObj.value - : "" - Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) - Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) - } - ColumnLayout { - Text { - text: nameObj?.value || "" - font { - pixelSize: Typography.p1.pixelSize - weight: Typography.p1.weight - } - } - Text { - visible: isMeObj && isMeObj.value - //: Click to delete - text: qsTr("click_to_delete_reaction_info") - color: DefaultStyle.main2_400 - font { - pixelSize: Typography.p3.pixelSize - weight: Typography.p3.weight - } - } - } - Item{Layout.fillWidth: true} - Text { - text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData.body) - font { - pixelSize: Typography.h3.pixelSize - weight: Typography.p3.weight - } + Text { + visible: isMeObj && isMeObj.value + //: Click to delete + text: qsTr("click_to_delete_reaction_info") + color: DefaultStyle.main2_400 + font { + pixelSize: Typography.p3.pixelSize + weight: Typography.p3.weight } } } + Item{Layout.fillWidth: true} + Text { + text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData.body) + font { + pixelSize: Typography.h3.pixelSize + weight: Typography.p3.weight + } + } } } }