From 821d1e17da01532f7ceda87db7963382c19bf3d1 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 7 Jan 2022 11:52:51 +0100 Subject: [PATCH] Unread chat message event. Shortcut to go at the end of chat. Avoid MarkAsRead when not at end of chat. Fix composers. --- .../assets/images/move_to_bottom_custom.svg | 84 +++++++++++++++++++ linphone-app/assets/languages/da.ts | 8 ++ linphone-app/assets/languages/de.ts | 8 ++ linphone-app/assets/languages/en.ts | 8 ++ linphone-app/assets/languages/es.ts | 8 ++ linphone-app/assets/languages/fr_FR.ts | 8 ++ linphone-app/assets/languages/hu.ts | 7 ++ linphone-app/assets/languages/it.ts | 8 ++ linphone-app/assets/languages/ja.ts | 7 ++ linphone-app/assets/languages/lt.ts | 9 ++ linphone-app/assets/languages/pt_BR.ts | 8 ++ linphone-app/assets/languages/ru.ts | 9 ++ linphone-app/assets/languages/sv.ts | 8 ++ linphone-app/assets/languages/tr.ts | 7 ++ linphone-app/assets/languages/uk.ts | 9 ++ linphone-app/assets/languages/zh_CN.ts | 7 ++ linphone-app/resources.qrc | 1 + .../chat-events/ChatNoticeModel.cpp | 22 ++++- .../chat-events/ChatNoticeModel.hpp | 9 +- .../components/chat-room/ChatRoomModel.cpp | 47 +++++++++-- .../components/chat-room/ChatRoomModel.hpp | 9 +- .../chat-room/ChatRoomProxyModel.cpp | 15 +++- .../chat-room/ChatRoomProxyModel.hpp | 6 ++ .../Common/View/ScrollableListView.qml | 71 ++++++++++------ .../ui/modules/Linphone/Chat/Chat.qml | 37 +++++++- .../ui/modules/Linphone/Chat/Notice.qml | 5 ++ .../Linphone/Styles/Chat/ChatStyle.qml | 12 +++ linphone-sdk | 2 +- 28 files changed, 397 insertions(+), 42 deletions(-) create mode 100644 linphone-app/assets/images/move_to_bottom_custom.svg diff --git a/linphone-app/assets/images/move_to_bottom_custom.svg b/linphone-app/assets/images/move_to_bottom_custom.svg new file mode 100644 index 000000000..32d352312 --- /dev/null +++ b/linphone-app/assets/images/move_to_bottom_custom.svg @@ -0,0 +1,84 @@ + + + + + + image/svg+xml + + drop_down_list + + + + + + drop_down_list + Created with Sketch. + + + + + + + + + + + + diff --git a/linphone-app/assets/languages/da.ts b/linphone-app/assets/languages/da.ts index c04160dde..cdc2ee0c9 100644 --- a/linphone-app/assets/languages/da.ts +++ b/linphone-app/assets/languages/da.ts @@ -1563,6 +1563,14 @@ Klik her: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/de.ts b/linphone-app/assets/languages/de.ts index 784ed26f6..6790d8942 100644 --- a/linphone-app/assets/languages/de.ts +++ b/linphone-app/assets/languages/de.ts @@ -1563,6 +1563,14 @@ Klicken Sie hier: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Kurzlebige Nachrichten wurden aktualisiert: %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts index de2ee4bd5..2e60808ce 100644 --- a/linphone-app/assets/languages/en.ts +++ b/linphone-app/assets/languages/en.ts @@ -1563,6 +1563,14 @@ Click here: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Ephemeral messages have been updated: %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + %1 unread message + %1 unread messages + + Notifier diff --git a/linphone-app/assets/languages/es.ts b/linphone-app/assets/languages/es.ts index 6dd96dde1..6f145b0cb 100644 --- a/linphone-app/assets/languages/es.ts +++ b/linphone-app/assets/languages/es.ts @@ -1563,6 +1563,14 @@ Haga clic aquí: <a href="%1">%1 </a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/fr_FR.ts b/linphone-app/assets/languages/fr_FR.ts index adfb3dde6..42bca6f82 100644 --- a/linphone-app/assets/languages/fr_FR.ts +++ b/linphone-app/assets/languages/fr_FR.ts @@ -1563,6 +1563,14 @@ Cliquez ici : <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Délai d’expiration des messages : %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/hu.ts b/linphone-app/assets/languages/hu.ts index 047d83ad4..c28508b65 100644 --- a/linphone-app/assets/languages/hu.ts +++ b/linphone-app/assets/languages/hu.ts @@ -1553,6 +1553,13 @@ Kattintson ide: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Mulandó üzenetek frissítve: %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + Notifier diff --git a/linphone-app/assets/languages/it.ts b/linphone-app/assets/languages/it.ts index a34f22946..344253319 100644 --- a/linphone-app/assets/languages/it.ts +++ b/linphone-app/assets/languages/it.ts @@ -1563,6 +1563,14 @@ Clicca: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/ja.ts b/linphone-app/assets/languages/ja.ts index cec7c868b..9a4df0ec8 100644 --- a/linphone-app/assets/languages/ja.ts +++ b/linphone-app/assets/languages/ja.ts @@ -1553,6 +1553,13 @@ 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + Notifier diff --git a/linphone-app/assets/languages/lt.ts b/linphone-app/assets/languages/lt.ts index 98b968793..2963d2ee3 100644 --- a/linphone-app/assets/languages/lt.ts +++ b/linphone-app/assets/languages/lt.ts @@ -1573,6 +1573,15 @@ Spustelėkite čia: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + + Notifier diff --git a/linphone-app/assets/languages/pt_BR.ts b/linphone-app/assets/languages/pt_BR.ts index 4461c601a..31c5964ce 100644 --- a/linphone-app/assets/languages/pt_BR.ts +++ b/linphone-app/assets/languages/pt_BR.ts @@ -1563,6 +1563,14 @@ Clique aqui: <a href="%1">%1 </a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Mensagens efêmeras foram atualizadas: %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/ru.ts b/linphone-app/assets/languages/ru.ts index 349205e6a..e998b93a0 100644 --- a/linphone-app/assets/languages/ru.ts +++ b/linphone-app/assets/languages/ru.ts @@ -1573,6 +1573,15 @@ 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + + Notifier diff --git a/linphone-app/assets/languages/sv.ts b/linphone-app/assets/languages/sv.ts index 83d293da7..df01c1dcf 100644 --- a/linphone-app/assets/languages/sv.ts +++ b/linphone-app/assets/languages/sv.ts @@ -1563,6 +1563,14 @@ Klicka här: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + Notifier diff --git a/linphone-app/assets/languages/tr.ts b/linphone-app/assets/languages/tr.ts index 6d0082047..56647e3a6 100644 --- a/linphone-app/assets/languages/tr.ts +++ b/linphone-app/assets/languages/tr.ts @@ -1553,6 +1553,13 @@ Buraya tıklayın: <a href="%1">%1</a> 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + Notifier diff --git a/linphone-app/assets/languages/uk.ts b/linphone-app/assets/languages/uk.ts index 021151aa2..6aeaae896 100644 --- a/linphone-app/assets/languages/uk.ts +++ b/linphone-app/assets/languages/uk.ts @@ -1573,6 +1573,15 @@ 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + + + Notifier diff --git a/linphone-app/assets/languages/zh_CN.ts b/linphone-app/assets/languages/zh_CN.ts index b45bb1cec..420871429 100644 --- a/linphone-app/assets/languages/zh_CN.ts +++ b/linphone-app/assets/languages/zh_CN.ts @@ -1553,6 +1553,13 @@ 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time 临时消息已被更新: %1 + + unreadMessageNotice + '%1 unread messages' : Little message to show on an event where unread messages begin. + + + + Notifier diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index b2abbde85..40c729121 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -98,6 +98,7 @@ assets/images/micro_on_custom.svg assets/images/missed_incoming_call_custom.svg assets/images/missed_outgoing_call_custom.svg + assets/images/move_to_bottom_custom.svg assets/images/new_call_custom.svg assets/images/new_chat_group_custom.svg assets/images/new_conference_custom.svg diff --git a/linphone-app/src/components/chat-events/ChatNoticeModel.cpp b/linphone-app/src/components/chat-events/ChatNoticeModel.cpp index 428300b1d..d8767282d 100644 --- a/linphone-app/src/components/chat-events/ChatNoticeModel.cpp +++ b/linphone-app/src/components/chat-events/ChatNoticeModel.cpp @@ -34,6 +34,14 @@ ChatNoticeModel::ChatNoticeModel ( std::shared_ptr eventLog, mTimestamp = QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000); } +ChatNoticeModel::ChatNoticeModel ( NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::NoticeEntry, parent) { + App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE + mEventLogType = LinphoneEnums::EventLogType::EventLogTypeNone; + setStatus(noticeType); + setName(txt); + mTimestamp = timestamp; +} + ChatNoticeModel::~ChatNoticeModel(){ } @@ -46,6 +54,15 @@ std::shared_ptr ChatNoticeModel::create(std::shared_ptr ChatNoticeModel::create(NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent){ + auto model = std::make_shared(noticeType, timestamp, txt, parent); + if(model ){ + model->mSelf = model; + return model; + }else + return nullptr; +} + std::shared_ptr ChatNoticeModel::getEventLog(){ return mEventLog; } @@ -53,6 +70,8 @@ std::shared_ptr ChatNoticeModel::getEventLog(){ //--------------------------------------------------------------------------------------------- bool ChatNoticeModel::update(){ bool handledEvent = true; + if(!mEventLog) + return false; auto participantAddress = mEventLog->getParticipantAddress(); switch(mEventLog->getType()){ @@ -158,5 +177,6 @@ void ChatNoticeModel::setEventLogType(const LinphoneEnums::EventLogType& data){ } void ChatNoticeModel::deleteEvent(){ - mEventLog->deleteFromDatabase(); + if(mEventLog) + mEventLog->deleteFromDatabase(); } \ No newline at end of file diff --git a/linphone-app/src/components/chat-events/ChatNoticeModel.hpp b/linphone-app/src/components/chat-events/ChatNoticeModel.hpp index 6fe8a4e97..0b1e171e8 100644 --- a/linphone-app/src/components/chat-events/ChatNoticeModel.hpp +++ b/linphone-app/src/components/chat-events/ChatNoticeModel.hpp @@ -32,16 +32,19 @@ class ChatNoticeModel : public ChatEvent { public: enum NoticeType { - NoticeMessage, - NoticeError + NoticeMessage, // This is a Linphone message + NoticeError, // This is a Linphone error + NoticeUnreadMessages }; Q_ENUM(NoticeType); static std::shared_ptr create(std::shared_ptr eventLog, QObject * parent = nullptr);// Call it instead constructor + static std::shared_ptr create(NoticeType noticeType, const QDateTime& timestamp,const QString& txt, QObject * parent = nullptr); ChatNoticeModel (std::shared_ptr eventLog, QObject * parent = nullptr); + ChatNoticeModel (NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent = nullptr); virtual ~ChatNoticeModel(); - Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT) + Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT)// NoticeEntry Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT) Q_PROPERTY(QString name MEMBER mName WRITE setName NOTIFY nameChanged) Q_PROPERTY(NoticeType status MEMBER mStatus WRITE setStatus NOTIFY statusChanged) diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 825aafd8e..59d03f1bc 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -205,6 +205,7 @@ ChatRoomModel::ChatRoomModel (std::shared_ptr chatRoom, QObj QElapsedTimer timer; timer.start(); CoreHandlers *coreHandlers = mCoreHandlers.get(); + QObject::connect(this, &ChatRoomModel::messageSent, this, &ChatRoomModel::resetMessageCount); //QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &ChatRoomModel::handleCallCreated); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); @@ -482,6 +483,10 @@ bool ChatRoomModel::haveEncryption() const{ return mChatRoom && mChatRoom->getCurrentParams()->getEncryptionBackend() != linphone::ChatRoomEncryptionBackend::None; } +bool ChatRoomModel::markAsReadEnabled() const{ + return mMarkAsReadEnabled; +} + bool ChatRoomModel::isSecure() const{ return mChatRoom && (mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted || mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Safe); @@ -514,11 +519,11 @@ bool ChatRoomModel::isCurrentProxy() const{ bool ChatRoomModel::canHandleParticipants() const{ return mChatRoom->canHandleParticipants(); } -/* + bool ChatRoomModel::getIsRemoteComposing () const { - return mIsRemoteComposing; + return mComposers.size() > 0; } -*/ + std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; @@ -603,6 +608,13 @@ void ChatRoomModel::setEphemeralLifetime(long lifetime){ } } +void ChatRoomModel::enableMarkAsRead(const bool& enable){ + if( mMarkAsReadEnabled != enable){ + mMarkAsReadEnabled = enable; + emit markAsReadEnabledChanged(); + } +} + void ChatRoomModel::setReply(ChatMessageModel * model){ if(model != mReplyModel.get()){ if( model && model->getChatMessage() ) @@ -738,10 +750,16 @@ void ChatRoomModel::compose () { } void ChatRoomModel::resetMessageCount () { - if(mChatRoom && !mDeleteChatRoom){ + if(mChatRoom && !mDeleteChatRoom && markAsReadEnabled()){ if (mChatRoom->getUnreadMessagesCount() > 0){ mChatRoom->markAsRead();// Marking as read is only for messages. Not for calls. } + if(!mUnreadMessageNotice.first) + mUnreadMessageNotice.first = true; + else if( mUnreadMessageNotice.second){ + removeEntry(mUnreadMessageNotice.second.get()); + mUnreadMessageNotice.second = nullptr; + } setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); setMissedCallsCount(0); emit messageCountReset(); @@ -831,9 +849,14 @@ void ChatRoomModel::initEntries(){ // On call : reinitialize all entries. This allow to free up memory QList > entries; QList prepareEntries; + QDateTime lastUnreadMessage = QDateTime::currentDateTime(); // Get chat messages - for (auto &message : mChatRoom->getHistory(mLastEntriesStep)) + for (auto &message : mChatRoom->getHistory(mLastEntriesStep)) { prepareEntries << EntrySorterHelper(message->getTime() ,MessageEntry, message); + if( !message->isRead()) { + lastUnreadMessage = min(lastUnreadMessage, QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000)); + } + } // Get events for(auto &eventLog : mChatRoom->getHistoryEvents(mLastEntriesStep)) prepareEntries << EntrySorterHelper(eventLog->getCreationTime() , NoticeEntry, eventLog); @@ -852,6 +875,12 @@ void ChatRoomModel::initEntries(){ } EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this); + if( mChatRoom->getUnreadMessagesCount() > 0) { + mUnreadMessageNotice.first = false; + mUnreadMessageNotice.second = ChatNoticeModel::create(ChatNoticeModel::NoticeType::NoticeUnreadMessages, lastUnreadMessage, QString::number(mChatRoom->getUnreadMessagesCount())); + entries.push_front(mUnreadMessageNotice.second); + } + mIsInitialized = true; if(entries.size() >0){ beginResetModel(); @@ -914,7 +943,7 @@ int ChatRoomModel::loadMoreEntries(){ bool haveEntry = false; while(!haveEntry && itEntries != mEntries.end()){ auto entry = dynamic_cast(itEntries->get()); - haveEntry = (entry && entry->getEventLog() == eventLog); + haveEntry = (entry && entry->getEventLog() && entry->getEventLog() == eventLog); ++itEntries; } if(!haveEntry) @@ -1057,6 +1086,12 @@ void ChatRoomModel::insertNotices (const QList &call, linphone::Call::State state) { diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp index 2e19836f8..77bab8021 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -134,7 +134,7 @@ public: Q_PROPERTY(bool isMeAdmin READ isMeAdmin NOTIFY isMeAdminChanged) Q_PROPERTY(bool canHandleParticipants READ canHandleParticipants CONSTANT) - //Q_PROPERTY(bool isComposing MEMBER mIsRemoteComposing NOTIFY isRemoteComposingChanged) + Q_PROPERTY(bool isComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged) Q_PROPERTY(QList composers READ getComposers NOTIFY isRemoteComposingChanged) Q_PROPERTY(bool hasBeenLeft READ hasBeenLeft NOTIFY hasBeenLeftChanged) @@ -148,6 +148,7 @@ public: Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime WRITE setEphemeralLifetime NOTIFY ephemeralLifetimeChanged) Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE setEphemeralEnabled NOTIFY ephemeralEnabledChanged) Q_PROPERTY(bool canBeEphemeral READ canBeEphemeral NOTIFY canBeEphemeralChanged) + Q_PROPERTY(bool markAsReadEnabled READ markAsReadEnabled WRITE enableMarkAsRead NOTIFY markAsReadEnabledChanged) Q_PROPERTY(ParticipantListModel* participants READ getParticipants CONSTANT) @@ -187,6 +188,7 @@ public: long getEphemeralLifetime() const; bool canBeEphemeral(); bool haveEncryption() const; + bool markAsReadEnabled() const; Q_INVOKABLE bool isSecure() const; int getSecurityLevel() const; bool isGroupEnabled() const; @@ -211,6 +213,7 @@ public: void addMissedCallsCount(std::shared_ptr call); void setEphemeralEnabled(bool enabled); void setEphemeralLifetime(long lifetime); + void enableMarkAsRead(const bool& enable); void setReply(ChatMessageModel * model); ChatMessageModel * getReply()const; @@ -237,6 +240,7 @@ public: bool mDeleteChatRoom = false; // Use as workaround because of core->deleteChatRoom() that call destructor without takking account of count ref : call it in ChatRoomModel destructor int mLastEntriesStep = 50; // Retrieve a part of the history to avoid too much processing + bool mMarkAsReadEnabled = true; void insertCall (const std::shared_ptr &callLog); @@ -312,6 +316,7 @@ signals: void ephemeralEnabledChanged(); void ephemeralLifetimeChanged(); void canBeEphemeralChanged(); + void markAsReadEnabledChanged(); void chatRoomDeleted();// Must be connected with DirectConnection mode void replyChanged(); @@ -337,7 +342,7 @@ private: //void handleMessageReceived (const std::shared_ptr &message); //bool mIsRemoteComposing = false; - + QPair > mUnreadMessageNotice; QList > mEntries; std::shared_ptr mParticipantListModel; std::shared_ptr mCoreHandlers; diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index 504488ae0..f20894b87 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -76,6 +76,7 @@ private: ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(new ChatRoomModelFilter(this)); + mMarkAsReadEnabled = true; //mIsSecure = false; App *app = App::getInstance(); @@ -240,6 +241,14 @@ void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { //reload(); } +bool ChatRoomProxyModel::markAsReadEnabled() const{ + return mChatRoomModel->markAsReadEnabled(); +} + +void ChatRoomProxyModel::enableMarkAsRead(const bool& enable){ + mChatRoomModel->enableMarkAsRead(enable); +} + QList ChatRoomProxyModel::getComposers() const{ return (mChatRoomModel?mChatRoomModel->getComposers():QList()); } @@ -266,6 +275,7 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) { QObject::disconnect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); + QObject::disconnect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); } @@ -276,7 +286,8 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) { ChatRoomModel *ChatRoomModel = mChatRoomModel.get(); QObject::connect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); QObject::connect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); - QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); + QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); + QObject::connect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); } static_cast(sourceModel())->setSourceModel(mChatRoomModel.get()); @@ -324,7 +335,7 @@ static inline QWindow *getParentWindow (QObject *object) { } void ChatRoomProxyModel::handleIsActiveChanged (QWindow *window) { - if (mChatRoomModel && window->isActive() && getParentWindow(this) == window) { + if (markAsReadEnabled() && mChatRoomModel && window->isActive() && getParentWindow(this) == window) { auto timeline = CoreManager::getInstance()->getTimelineListModel()->getTimeline(mChatRoomModel->getChatRoom(), false); if(timeline && timeline->mSelected){ mChatRoomModel->resetMessageCount(); diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp index 41d1d8817..7f8ba3020 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp @@ -47,6 +47,7 @@ class ChatRoomProxyModel : public QSortFilterProxyModel { Q_PROPERTY(QString cachedText READ getCachedText) Q_PROPERTY(QString filterText MEMBER mFilterText WRITE setFilterText NOTIFY filterTextChanged) + Q_PROPERTY(bool markAsReadEnabled READ markAsReadEnabled WRITE enableMarkAsRead NOTIFY markAsReadEnabledChanged)// Focus is at end of the list. Used to reset message count if not at end public: ChatRoomProxyModel (QObject *parent = Q_NULLPTR); @@ -78,6 +79,7 @@ signals: void fullPeerAddressChanged (const QString &fullPeerAddress); void fullLocalAddressChanged (const QString &fullLocalAddress); bool isRemoteComposingChanged (); + void markAsReadEnabledChanged(); //bool isSecureChanged(bool secure); void chatRoomModelChanged(); @@ -104,6 +106,9 @@ private: QString getFullLocalAddress () const; void setFullLocalAddress (const QString &localAddress); + bool markAsReadEnabled() const; + void enableMarkAsRead(const bool& enable); + //bool isSecure () const; //void setIsSecure (const int &secure); @@ -129,6 +134,7 @@ private: QString mFullPeerAddress; QString mFullLocalAddress; static QString gCachedText; + bool mMarkAsReadEnabled; QString mFilterText; diff --git a/linphone-app/ui/modules/Common/View/ScrollableListView.qml b/linphone-app/ui/modules/Common/View/ScrollableListView.qml index 208a9291e..8e7918697 100644 --- a/linphone-app/ui/modules/Common/View/ScrollableListView.qml +++ b/linphone-app/ui/modules/Common/View/ScrollableListView.qml @@ -6,28 +6,51 @@ import Common 1.0 // ============================================================================= ListView { - id: view - - // --------------------------------------------------------------------------- - - ScrollBar.vertical: ForceScrollBar { - id: vScrollBar - - onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() - // ScrollBar.AsNeeded doesn't work. Do it ourself. - policy: (view.contentHeight > view.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) - } - // --------------------------------------------------------------------------- - - boundsBehavior: Flickable.StopAtBounds - clip: true - contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) - spacing: 0 - synchronousDrag: true - cacheBuffer: height - // --------------------------------------------------------------------------- - - // TODO: Find a solution at this bug => - // https://bugreports.qt.io/browse/QTBUG-31573 - // https://bugreports.qt.io/browse/QTBUG-49989 + id: view + + function getVisibleIndex(checkMax) { + var center_x = view.x + view.width / 2 + var index = -1 + var yCheck = 0 + var direction = checkMax ? -1 : 1 + var yStart = view.y + view.contentY + (checkMax ? view.height : 0) + var yStep = 5 + while(index<0 && yCheck < view.height){ + index = indexAt( center_x, yStart + yCheck * direction) + yCheck += yStep + } + return index + } + function getVisibleIndexRange() { + return [getVisibleIndex(0), getVisibleIndex(1)] + } + function isIndexVisible(index){ + return getVisibleIndex(0) <= index && index <= getVisibleIndex(1) + } + function isIndexAfter(index){ + return getVisibleIndex(1) < index + } + + // --------------------------------------------------------------------------- + + ScrollBar.vertical: ForceScrollBar { + id: vScrollBar + + onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() + // ScrollBar.AsNeeded doesn't work. Do it ourself. + policy: (view.contentHeight > view.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) + } + // --------------------------------------------------------------------------- + + boundsBehavior: Flickable.StopAtBounds + clip: true + contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) + spacing: 0 + synchronousDrag: true + cacheBuffer: height + // --------------------------------------------------------------------------- + + // TODO: Find a solution at this bug => + // https://bugreports.qt.io/browse/QTBUG-31573 + // https://bugreports.qt.io/browse/QTBUG-49989 } diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index d3a2fe6c5..1010450a4 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -296,13 +296,19 @@ Rectangle { } } footer: Item{ + implicitHeight: composersItem.implicitHeight + width: parent.width Text { - property var composers : container.proxyModel.composers + id: composersItem + property var composers : container.proxyModel.chatRoomModel.composers + onComposersChanged: console.log(composers) + onVisibleChanged: console.log(visible) color: ChatStyle.composingText.color font.pointSize: ChatStyle.composingText.pointSize height: visible ? undefined : 0 leftPadding: ChatStyle.composingText.leftPadding - visible: composers.length > 0 && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) + visible: composers.length > 0 && ( (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled) + || (proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) ) wrapMode: Text.Wrap //: '%1 is typing...' indicate that someone is composing in chat text:(composers.length==0?'': qsTr('chatTyping','',composers.length).arg(container.proxyModel.getDisplayNameComposers())) @@ -315,7 +321,7 @@ Rectangle { } Rectangle{ id: messageBlock - height: 32 + height: opacity > 0 ? 32 : 0 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom @@ -375,6 +381,31 @@ Rectangle { } ] } + + ActionButton{ + anchors.bottom: messageBlock.top + anchors.bottomMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 40 + visible: chat.isIndexAfter(chat.count-1) + onVisibleChanged: container.proxyModel.markAsReadEnabled = !visible + + isCustom: true + backgroundRadius: width/2 + colorSet: ChatStyle.gotToBottom + onClicked: { + chat.bindToEnd = true + } + MessageCounter{ + anchors.left: parent.right + anchors.bottom: parent.top + anchors.bottomMargin: -5 + anchors.leftMargin: -5 + count: container.proxyModel.chatRoomModel.unreadMessagesCount + } + } + + } // ------------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Linphone/Chat/Notice.qml b/linphone-app/ui/modules/Linphone/Chat/Notice.qml index 49f9d3ccb..a54230410 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Notice.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Notice.qml @@ -15,6 +15,11 @@ RowLayout{ id: mainLayout property string _type: { var status = $chatEntry.eventLogType + var type = $chatEntry.status + + if(type == ChatNoticeModel.NoticeUnreadMessages) + //: '%1 unread messages' : Little message to show on an event where unread messages begin. + return qsTr('unreadMessageNotice', '', $chatEntry.name) if (status == LinphoneEnums.EventLogTypeConferenceCreated) { //: 'You have joined the group' : Little message to show on the event when the user join the chat group. diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml index c0a53d115..92eae750b 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml @@ -26,6 +26,18 @@ QtObject { } } + property QtObject gotToBottom: QtObject{ + property string name: 'goToBottom' + property string icon: 'move_to_bottom_custom' + property int iconSize: 30 + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color + } + property QtObject sendArea: QtObject { property int height: 80 diff --git a/linphone-sdk b/linphone-sdk index 3d422c64a..5800f6e48 160000 --- a/linphone-sdk +++ b/linphone-sdk @@ -1 +1 @@ -Subproject commit 3d422c64a3a10289fe6dda31ccb0af17b41cfffa +Subproject commit 5800f6e489b250c5ffccaa90c8771a10a9edad59