1 /*
2     Copyright (C) 2016  Martin Klapetek <mklapetek@kde.org>
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Lesser General Public License for more details.
13 
14     You should have received a copy of the GNU Lesser General Public
15     License along with this library; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 #ifndef MAINLOGMODEL_H
20 #define MAINLOGMODEL_H
21 
22 #include <QObject>
23 #include <QAbstractListModel>
24 #include <QSqlQuery>
25 
26 #include <TelepathyQt/AbstractClientObserver>
27 #include <TelepathyQt/AbstractClientHandler>
28 #include <TelepathyQt/ChannelDispatchOperation>
29 
30 #include <KTp/persistent-contact.h>
31 #include <KTp/types.h>
32 
33 class Conversation;
34 class MainLogModel; // Cause of ObserverProxy
35 
36 class LogItem {
37 public:
38     QDateTime messageDateTime;
39     QString message;
40     QString accountObjectPath;
41     QString targetContact;
42     Conversation *conversation;
43 };
44 
45 /**
46  * The reason for this class is that an Observer and a Handler cannot
47  * be registered under the same client name if the Observer is not to
48  * be autostarted and only monitor things once the app is executed.
49  *
50  * So this is a tiny proxy class that gets registered as SpaceBarObserverProxy
51  * and forwards all observerChannels calls to the model which then merges
52  * them with the existing conversations
53  */
54 class ObserverProxy : public QObject, public Tp::AbstractClientObserver
55 {
56     Q_OBJECT
57 
58 public:
59     ObserverProxy(MainLogModel *model);
60 
61     void observeChannels(const Tp::MethodInvocationContextPtr<> &context,
62                          const Tp::AccountPtr &account,
63                          const Tp::ConnectionPtr &connection,
64                          const QList<Tp::ChannelPtr> &channels,
65                          const Tp::ChannelDispatchOperationPtr &dispatchOperation,
66                          const QList<Tp::ChannelRequestPtr> &requestsSatisfied,
67                          const Tp::AbstractClientObserver::ObserverInfo &observerInfo) override;
68 
69 private:
70     MainLogModel *m_model;
71 };
72 
73 //-----------------------------------------------------------------------------
74 
75 class MainLogModel : public QAbstractListModel, public Tp::AbstractClientHandler
76 {
77     Q_OBJECT
78 
79 public:
80     enum Role {
81         ContactDisplayNameRole = Qt::DisplayRole,
82         ChatPictureRole = Qt::DecorationRole,
83         ContactIdRole = Qt::UserRole,
84         PersonUriRole,
85         AccountIdRole,
86         LastMessageDateRole,
87         LastMessageTextRole,
88         ConversationRole,
89         HasUnreadMessagesRole,
90         UnreadMessagesCountRole,
91 
92         UserRole = Qt::UserRole + 0x1000 ///< in case it's needed to extend, use this one to start from
93     };
94     Q_ENUMS(Role)
95 
96     MainLogModel(QObject *parent = nullptr);
97     ~MainLogModel() override;
98 
99     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
100     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
101     QHash<int, QByteArray> roleNames() const override;
102 
103     Q_INVOKABLE bool canChat(const QString &accountId) const;
104     Q_INVOKABLE void setAccountManager(const Tp::AccountManagerPtr &accountManager);
105     Q_INVOKABLE QVariant data(int index, QByteArray role) const;
106     Q_INVOKABLE QObject* observerProxy() const;
107 
108     void handleChannels(const Tp::MethodInvocationContextPtr<> &context,
109                         const Tp::AccountPtr &account,
110                         const Tp::ConnectionPtr &connection,
111                         const QList<Tp::ChannelPtr> &channels,
112                         const QList<Tp::ChannelRequestPtr> &channelRequests,
113                         const QDateTime &userActionTime,
114                         const HandlerInfo &handlerInfo) override;
115 
116     bool bypassApproval() const override;
117 
118 Q_SIGNALS:
119     void newRequestedChannel(const QModelIndex &index);
120 
121 public Q_SLOTS:
122     void startChat(const QString &personUri);
123     void startChat(const QString &accountId, const QString &contactId);
124 
125 private Q_SLOTS:
126     void handleChannel(const Tp::AccountPtr &account, const Tp::TextChannelPtr &channel);
127     void onConversationChanged();
128     void onAccountManagerReady();
129 
130 private:
131     void setupSignals(Conversation *conversation) const;
132     void processQueryResults(QSqlQuery query);
133 
134     QHash<QString, Conversation*> m_conversations; // This is a hash with keys "accountId + contactId"
135     QList<LogItem> m_logItems;
136     QSqlQuery m_query;
137     QSqlDatabase m_db;
138     Tp::AccountManagerPtr m_accountManager;
139     ObserverProxy *m_observerProxy;
140 
141     // This is true when mission control autostarted the app
142     // on an incoming channel; the model will emit newRequestedChannel()
143     // for the first incoming channel even though it was not requested
144     // This is useful to switch the application directly to the new
145     // message
146     bool m_openIncomingChannel;
147 
148     friend class ObserverProxy;
149 };
150 
151 Q_DECLARE_METATYPE(Tp::ChannelDispatchOperationPtr)
152 
153 #endif
154