1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Network Auth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "twittertimelinemodel.h"
52
53#include <QtGui>
54#include <QtCore>
55#include <QtWidgets>
56
57TwitterTimelineModel::TwitterTimelineModel(QObject *parent) : QAbstractTableModel(parent)
58{
59 connect(sender: &twitter, signal: &Twitter::authenticated, receiver: this, slot: &TwitterTimelineModel::authenticated);
60 connect(sender: &twitter, signal: &Twitter::authenticated, receiver: this, slot: &TwitterTimelineModel::updateTimeline);
61}
62
63int TwitterTimelineModel::rowCount(const QModelIndex &parent) const
64{
65#if defined(QT_DEBUG)
66 Q_ASSERT(!parent.isValid());
67#else
68 Q_UNUSED(parent)
69#endif
70 return tweets.size();
71}
72
73QVariant TwitterTimelineModel::data(const QModelIndex &index, int role) const
74{
75 if (role != Qt::DisplayRole)
76 return QVariant();
77
78 auto it = tweets.begin();
79 std::advance(i&: it, n: index.row());
80 switch (index.column())
81 {
82 case 0:
83 return QString::number(it->id);
84 case 1:
85 return it->createdAt.toString(format: Qt::ISODateWithMs);
86 case 2:
87 return it->user;
88 case 3:
89 return it->text;
90 }
91 return QVariant();
92}
93
94int TwitterTimelineModel::columnCount(const QModelIndex &) const
95{
96 return 4;
97}
98
99QVariant TwitterTimelineModel::headerData(int section, Qt::Orientation orientation, int role) const
100{
101 if (role != Qt::DisplayRole)
102 return QVariant();
103
104 if (orientation == Qt::Horizontal) {
105 switch (section) {
106 case 0:
107 return QStringLiteral("Id");
108 case 1:
109 return QStringLiteral("Created at");
110 case 2:
111 return QStringLiteral("User");
112 case 3:
113 return QStringLiteral("Text");
114 }
115 }
116 return section;
117}
118
119void TwitterTimelineModel::authenticate(const QPair<QString, QString> &clientCredentials)
120{
121 twitter.setClientCredentials(clientCredentials);
122 twitter.grant();
123}
124
125QAbstractOAuth::Status TwitterTimelineModel::status() const
126{
127 return twitter.status();
128}
129
130void TwitterTimelineModel::updateTimeline()
131{
132 if (twitter.status() != Twitter::Status::Granted)
133 QMessageBox::warning(parent: nullptr, qApp->applicationName(), text: "Not authenticated");
134
135 QUrl url("https://api.twitter.com/1.1/statuses/home_timeline.json");
136 QVariantMap parameters;
137 if (tweets.size()) {
138 // Tweets are time-ordered, newest first. Pass the most recent
139 // ID we have to request everything more recent than it:
140 parameters.insert(akey: "since_id", avalue: QString::number(tweets.first().id));
141 // From https://dev.twitter.com/rest/reference/get/search/tweets:
142 // Returns results with an ID greater than (that is, more recent than)
143 // the specified ID. There are limits to the number of Tweets which can
144 // be accessed through the API. If the limit of Tweets has occurred
145 // since the since_id, the since_id will be forced to the oldest ID
146 // available.
147 }
148 QNetworkReply *reply = twitter.get(url, parameters);
149 connect(sender: reply, signal: &QNetworkReply::finished, receiver: this, slot: &TwitterTimelineModel::parseJson);
150}
151
152void TwitterTimelineModel::parseJson()
153{
154 QJsonParseError parseError;
155 auto reply = qobject_cast<QNetworkReply*>(object: sender());
156 Q_ASSERT(reply);
157 const auto data = reply->readAll();
158 const auto document = QJsonDocument::fromJson(json: data, error: &parseError);
159 if (parseError.error) {
160 qCritical() << "TwitterTimelineModel::parseJson. Error at:" << parseError.offset
161 << parseError.errorString();
162 return;
163 } else if (document.isObject()) {
164 // Error received :(
165 const auto object = document.object();
166 const auto errorArray = object.value(key: "errors").toArray();
167 Q_ASSERT_X(errorArray.size(), "parse", data);
168 QStringList errors;
169 for (const auto error : errorArray) {
170 Q_ASSERT(error.isObject());
171 Q_ASSERT(error.toObject().contains("message"));
172 errors.append(t: error.toObject().value(key: "message").toString());
173 }
174 QMessageBox::warning(parent: nullptr, qApp->applicationName(), text: errors.join(sep: "<br />"));
175 return;
176 }
177
178 Q_ASSERT_X(document.isArray(), "parse", data);
179 const auto array = document.array();
180 if (array.size()) {
181 beginInsertRows(parent: QModelIndex(), first: 0, last: array.size() - 1);
182 auto before = tweets.begin();
183 for (const auto &value : array) {
184 Q_ASSERT(value.isObject());
185 const auto object = value.toObject();
186 const auto createdAt = QDateTime::fromString(s: object.value(key: "created_at").toString(),
187 format: "ddd MMM dd HH:mm:ss +0000 yyyy");
188 before = tweets.insert(before, t: Tweet{
189 .id: object.value(key: "id").toVariant().toULongLong(),
190 .createdAt: createdAt,
191 .user: object.value(key: "user").toObject().value(key: "name").toString(),
192 .text: object.value(key: "text").toString()
193 });
194 std::advance(i&: before, n: 1);
195 }
196 endInsertRows();
197 }
198}
199

source code of qtnetworkauth/examples/oauth/twittertimeline/twittertimelinemodel.cpp