File indexing completed on 2023-11-26 08:16:45

0001 /*
0002  * Copyright (C) 2013 David Edmundson <kde@davidedmundson.co.uk>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0017  */
0018 
0019 #include "text-channel-watcher-proxy-model.h"
0020 
0021 #include <TelepathyQt/TextChannel>
0022 #include <TelepathyQt/ChannelClassSpecList>
0023 #include <TelepathyQt/MethodInvocationContext>
0024 #include <TelepathyQt/Types>
0025 #include <TelepathyQt/ReceivedMessage>
0026 
0027 #include <KTp/types.h>
0028 #include <KTp/contact.h>
0029 #include <KTp/message.h>
0030 
0031 
0032 inline Tp::ChannelClassSpecList channelClasses() {
0033     return Tp::ChannelClassSpecList() << Tp::ChannelClassSpec::textChat();
0034 }
0035 
0036 class ChannelWatcher : public QObject, public Tp::RefCounted
0037 {
0038     Q_OBJECT
0039 public:
0040     ChannelWatcher(const QPersistentModelIndex &index, const Tp::TextChannelPtr &channel, QObject *parent=nullptr);
0041     int unreadMessageCount() const;
0042     QString lastMessage() const;
0043     KTp::Message::MessageDirection lastMessageDirection() const;
0044     QPersistentModelIndex modelIndex() const;
0045 Q_SIGNALS:
0046     void messagesChanged();
0047     void invalidated();
0048 private Q_SLOTS:
0049     void onMessageReceived(const Tp::ReceivedMessage &message);
0050     void onMessageSent(const Tp::Message &message);
0051 private:
0052     QPersistentModelIndex m_index;
0053     Tp::TextChannelPtr m_channel;
0054     QString m_lastMessage;
0055     KTp::Message::MessageDirection m_lastMessageDirection;
0056 };
0057 
0058 typedef Tp::SharedPtr<ChannelWatcher> ChannelWatcherPtr;
0059 
0060 ChannelWatcher::ChannelWatcher(const QPersistentModelIndex &index, const Tp::TextChannelPtr &channel, QObject *parent):
0061     QObject(parent),
0062     m_index(index),
0063     m_channel(channel),
0064     m_lastMessageDirection(KTp::Message::LocalToRemote)
0065 {
0066     connect(channel.data(), SIGNAL(pendingMessageRemoved(Tp::ReceivedMessage)), SIGNAL(messagesChanged()));
0067     connect(channel.data(), SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)), SIGNAL(invalidated()));
0068 
0069     connect(channel.data(), SIGNAL(messageReceived(Tp::ReceivedMessage)), SLOT(onMessageReceived(Tp::ReceivedMessage)));
0070     connect(channel.data(), SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)), SLOT(onMessageSent(Tp::Message)));
0071 
0072     //trigger an update to the contact straight away
0073     QTimer::singleShot(0, this, SIGNAL(messagesChanged()));
0074 }
0075 
0076 QPersistentModelIndex ChannelWatcher::modelIndex() const
0077 {
0078     return m_index;
0079 }
0080 
0081 int ChannelWatcher::unreadMessageCount() const
0082 {
0083     return m_channel->messageQueue().size();
0084 }
0085 
0086 QString ChannelWatcher::lastMessage() const
0087 {
0088     return m_lastMessage;
0089 }
0090 
0091 KTp::Message::MessageDirection ChannelWatcher::lastMessageDirection() const
0092 {
0093     return m_lastMessageDirection;
0094 }
0095 
0096 void ChannelWatcher::onMessageReceived(const Tp::ReceivedMessage &message)
0097 {
0098     if (!message.isDeliveryReport()) {
0099         m_lastMessage = message.text();
0100         m_lastMessageDirection = KTp::Message::RemoteToLocal;
0101         Q_EMIT messagesChanged();
0102     }
0103 }
0104 
0105 void ChannelWatcher::onMessageSent(const Tp::Message &message)
0106 {
0107     m_lastMessage = message.text();
0108     m_lastMessageDirection = KTp::Message::LocalToRemote;
0109     Q_EMIT messagesChanged();
0110 }
0111 
0112 namespace KTp {
0113 
0114 class TextChannelWatcherProxyModel::Private {
0115 public:
0116     QHash<KTp::ContactPtr, ChannelWatcherPtr> currentChannels;
0117 };
0118 
0119 } //namespace
0120 
0121 
0122 
0123 
0124 KTp::TextChannelWatcherProxyModel::TextChannelWatcherProxyModel(QObject *parent) :
0125     QIdentityProxyModel(parent),
0126     Tp::AbstractClientObserver(channelClasses(), true),
0127     d(new TextChannelWatcherProxyModel::Private)
0128 {
0129 }
0130 
0131 KTp::TextChannelWatcherProxyModel::~TextChannelWatcherProxyModel()
0132 {
0133     delete d;
0134 }
0135 
0136 void KTp::TextChannelWatcherProxyModel::observeChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList<Tp::ChannelPtr> &channels, const Tp::ChannelDispatchOperationPtr &dispatchOperation, const QList<Tp::ChannelRequestPtr> &requestsSatisfied, const Tp::AbstractClientObserver::ObserverInfo &observerInfo)
0137 {
0138     Q_UNUSED(context)
0139     Q_UNUSED(account)
0140     Q_UNUSED(connection)
0141     Q_UNUSED(dispatchOperation)
0142     Q_UNUSED(requestsSatisfied)
0143     Q_UNUSED(observerInfo)
0144 
0145     if (!sourceModel()) {
0146         return;
0147     }
0148 
0149     Q_FOREACH(const Tp::ChannelPtr & channel, channels) {
0150         Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel);
0151         if (textChannel) {
0152             KTp::ContactPtr targetContact = KTp::ContactPtr::qObjectCast(textChannel->targetContact());
0153 
0154             //skip group chats and situations where we don't have a single contact to mark messages for
0155             if (targetContact.isNull()) {
0156                 continue;
0157             }
0158 
0159             //if it's not in our source model, ignore the channel
0160             QModelIndexList matchedContacts = sourceModel()->match(QModelIndex(sourceModel()->index(0,0)), KTp::IdRole, QVariant::fromValue(targetContact->id()));
0161             if (matchedContacts.size() !=1) {
0162                 continue;
0163             }
0164 
0165             QPersistentModelIndex contactIndex(matchedContacts[0]);
0166 
0167             ChannelWatcherPtr watcher = ChannelWatcherPtr(new ChannelWatcher(contactIndex, textChannel));
0168             d->currentChannels[targetContact] = watcher;
0169 
0170             connect(watcher.data(), SIGNAL(messagesChanged()), SLOT(onChannelMessagesChanged()));
0171         }
0172     }
0173 }
0174 
0175 QVariant KTp::TextChannelWatcherProxyModel::data(const QModelIndex &proxyIndex, int role) const
0176 {
0177     // if we're processing a person and either of those two roles,
0178     // we propagate the data from sub contacts
0179     if (proxyIndex.model()->rowCount(proxyIndex) > 0
0180         && (role == KTp::ContactHasTextChannelRole || role == KTp::ContactUnreadMessageCountRole)) {
0181         QVariant personData;
0182 
0183         for (int i = 0; i < proxyIndex.model()->rowCount(proxyIndex); i++) {
0184             QVariant data = proxyIndex.child(i, 0).data(role);
0185             if (!data.isNull()) {
0186                 personData = data;
0187                 break;
0188             }
0189         }
0190 
0191         return personData;
0192     }
0193 
0194     const QModelIndex sourceIndex = mapToSource(proxyIndex);
0195 
0196     if (role == KTp::ContactHasTextChannelRole) {
0197         KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>();
0198         if (contact) {
0199             if (d->currentChannels.contains(contact)) {
0200                 return true;
0201             }
0202         }
0203         return QVariant();
0204     }
0205 
0206     if (role == KTp::ContactUnreadMessageCountRole) {
0207         KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>();
0208         if (contact) {
0209             if (d->currentChannels.contains(contact)) {
0210                 return d->currentChannels[contact]->unreadMessageCount();
0211             }
0212         }
0213         return QVariant();
0214     }
0215     if (role == KTp::ContactLastMessageRole) {
0216         KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>();
0217         if (contact) {
0218             if (d->currentChannels.contains(contact)) {
0219                 return d->currentChannels[contact]->lastMessage();
0220             }
0221         }
0222         return QString();
0223     }
0224     if (role == KTp::ContactLastMessageDirectionRole) {
0225         KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>();
0226         if (contact) {
0227             if (d->currentChannels.contains(contact)) {
0228                 return d->currentChannels[contact]->lastMessageDirection();
0229             }
0230         }
0231         return KTp::Message::LocalToRemote;
0232     }
0233 
0234     return sourceIndex.data(role);
0235 }
0236 
0237 void KTp::TextChannelWatcherProxyModel::onChannelMessagesChanged()
0238 {
0239     ChannelWatcher* watcher = qobject_cast<ChannelWatcher*>(sender());
0240     Q_ASSERT(watcher);
0241     const QModelIndex &index = mapFromSource(watcher->modelIndex());
0242     dataChanged(index, index);
0243 }
0244 
0245 void KTp::TextChannelWatcherProxyModel::onChannelInvalidated()
0246 {
0247     ChannelWatcher* watcher = qobject_cast<ChannelWatcher*>(sender());
0248     Q_ASSERT(watcher);
0249     const QModelIndex &index = mapFromSource(watcher->modelIndex());
0250     const KTp::ContactPtr &contact = index.data(KTp::ContactRole).value<KTp::ContactPtr>();
0251 
0252     d->currentChannels.remove(contact);
0253     dataChanged(index, index);
0254 }
0255 
0256 #include "text-channel-watcher-proxy-model.moc"
0257 #include "moc_text-channel-watcher-proxy-model.cpp"