some work on reddit
This commit is contained in:
		
							parent
							
								
									3b064d8e34
								
							
						
					
					
						commit
						0ccedad219
					
				
					 4 changed files with 240 additions and 63 deletions
				
			
		|  | @ -3,19 +3,21 @@ | ||||||
| #ifndef REDDIT_DEFINITIONS_H | #ifndef REDDIT_DEFINITIONS_H | ||||||
| #define REDDIT_DEFINITIONS_H | #define REDDIT_DEFINITIONS_H | ||||||
| 
 | 
 | ||||||
| #define REDDIT_OAUTH_REDIRECT_URI_PORT   14499 | #define REDDIT_OAUTH_REDIRECT_URI_PORT 14499 | ||||||
| #define REDDIT_OAUTH_AUTH_URL            "https://www.reddit.com/api/v1/authorize"
 | #define REDDIT_OAUTH_AUTH_URL "https://www.reddit.com/api/v1/authorize"
 | ||||||
| #define REDDIT_OAUTH_TOKEN_URL           "https://www.reddit.com/api/v1/access_token"
 | #define REDDIT_OAUTH_TOKEN_URL "https://www.reddit.com/api/v1/access_token"
 | ||||||
| #define REDDIT_OAUTH_SCOPE               "identity" | #define REDDIT_OAUTH_SCOPE "identity mysubreddits read" | ||||||
| 
 | 
 | ||||||
| #define REDDIT_REG_API_URL         "https://www.reddit.com/prefs/apps"
 | #define REDDIT_REG_API_URL "https://www.reddit.com/prefs/apps"
 | ||||||
| 
 | 
 | ||||||
| #define REDDIT_API_GET_PROFILE       "https://oauth.reddit.com/api/v1/me"
 | #define REDDIT_API_GET_PROFILE "https://oauth.reddit.com/api/v1/me"
 | ||||||
|  | #define REDDIT_API_SUBREDDITS "https://oauth.reddit.com/subreddits/mine/subscriber?limit=%1"
 | ||||||
|  | #define REDDIT_API_HOT "https://oauth.reddit.com/r/%2/hot?limit=%1&%3"
 | ||||||
| 
 | 
 | ||||||
| #define REDDIT_DEFAULT_BATCH_SIZE  100 | #define REDDIT_DEFAULT_BATCH_SIZE 100 | ||||||
| #define REDDIT_MAX_BATCH_SIZE      999 | #define REDDIT_MAX_BATCH_SIZE 999 | ||||||
| 
 | 
 | ||||||
| #define REDDIT_CONTENT_TYPE_HTTP   "application/http" | #define REDDIT_CONTENT_TYPE_HTTP "application/http" | ||||||
| #define REDDIT_CONTENT_TYPE_JSON   "application/json" | #define REDDIT_CONTENT_TYPE_JSON "application/json" | ||||||
| 
 | 
 | ||||||
| #endif // REDDIT_DEFINITIONS_H
 | #endif // REDDIT_DEFINITIONS_H
 | ||||||
|  |  | ||||||
|  | @ -26,11 +26,14 @@ | ||||||
| #include <QThread> | #include <QThread> | ||||||
| #include <QUrl> | #include <QUrl> | ||||||
| 
 | 
 | ||||||
| RedditNetworkFactory::RedditNetworkFactory(QObject* parent) : QObject(parent), | RedditNetworkFactory::RedditNetworkFactory(QObject* parent) | ||||||
|   m_service(nullptr), m_username(QString()), m_batchSize(REDDIT_DEFAULT_BATCH_SIZE), |   : QObject(parent), m_service(nullptr), m_username(QString()), m_batchSize(REDDIT_DEFAULT_BATCH_SIZE), | ||||||
|   m_downloadOnlyUnreadMessages(false), |     m_downloadOnlyUnreadMessages(false), m_oauth2(new OAuth2Service(QSL(REDDIT_OAUTH_AUTH_URL), | ||||||
|   m_oauth2(new OAuth2Service(QSL(REDDIT_OAUTH_AUTH_URL), QSL(REDDIT_OAUTH_TOKEN_URL), |                                                                     QSL(REDDIT_OAUTH_TOKEN_URL), | ||||||
|                              {}, {}, QSL(REDDIT_OAUTH_SCOPE), this)) { |                                                                     {}, | ||||||
|  |                                                                     {}, | ||||||
|  |                                                                     QSL(REDDIT_OAUTH_SCOPE), | ||||||
|  |                                                                     this)) { | ||||||
|   initializeOauth(); |   initializeOauth(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -56,23 +59,23 @@ void RedditNetworkFactory::setBatchSize(int batch_size) { | ||||||
| 
 | 
 | ||||||
| void RedditNetworkFactory::initializeOauth() { | void RedditNetworkFactory::initializeOauth() { | ||||||
|   m_oauth2->setUseHttpBasicAuthWithClientData(true); |   m_oauth2->setUseHttpBasicAuthWithClientData(true); | ||||||
|   m_oauth2->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) + |   m_oauth2->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) + QL1C(':') + QString::number(REDDIT_OAUTH_REDIRECT_URI_PORT), true); | ||||||
|                            QL1C(':') + |  | ||||||
|                            QString::number(REDDIT_OAUTH_REDIRECT_URI_PORT), |  | ||||||
|                            true); |  | ||||||
| 
 | 
 | ||||||
|   connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &RedditNetworkFactory::onTokensError); |   connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &RedditNetworkFactory::onTokensError); | ||||||
|   connect(m_oauth2, &OAuth2Service::authFailed, this, &RedditNetworkFactory::onAuthFailed); |   connect(m_oauth2, &OAuth2Service::authFailed, this, &RedditNetworkFactory::onAuthFailed); | ||||||
|   connect(m_oauth2, &OAuth2Service::tokensRetrieved, this, [this](QString access_token, QString refresh_token, int expires_in) { |   connect(m_oauth2, | ||||||
|     Q_UNUSED(expires_in) |           &OAuth2Service::tokensRetrieved, | ||||||
|     Q_UNUSED(access_token) |           this, | ||||||
|  |           [this](QString access_token, QString refresh_token, int expires_in) { | ||||||
|  |             Q_UNUSED(expires_in) | ||||||
|  |             Q_UNUSED(access_token) | ||||||
| 
 | 
 | ||||||
|     if (m_service != nullptr && !refresh_token.isEmpty()) { |             if (m_service != nullptr && !refresh_token.isEmpty()) { | ||||||
|       QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className()); |               QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className()); | ||||||
| 
 | 
 | ||||||
|       DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_service->accountId()); |               DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_service->accountId()); | ||||||
|     } |             } | ||||||
|   }); |           }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RedditNetworkFactory::downloadOnlyUnreadMessages() const { | bool RedditNetworkFactory::downloadOnlyUnreadMessages() const { | ||||||
|  | @ -114,7 +117,8 @@ QVariantHash RedditNetworkFactory::me(const QNetworkProxy& custom_proxy) { | ||||||
|                                                         false, |                                                         false, | ||||||
|                                                         {}, |                                                         {}, | ||||||
|                                                         {}, |                                                         {}, | ||||||
|                                                         custom_proxy).m_networkError; |                                                         custom_proxy) | ||||||
|  |                   .m_networkError; | ||||||
| 
 | 
 | ||||||
|   if (result != QNetworkReply::NetworkError::NoError) { |   if (result != QNetworkReply::NetworkError::NoError) { | ||||||
|     throw NetworkException(result, output); |     throw NetworkException(result, output); | ||||||
|  | @ -126,30 +130,191 @@ QVariantHash RedditNetworkFactory::me(const QNetworkProxy& custom_proxy) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | QList<Feed*> RedditNetworkFactory::subreddits(const QNetworkProxy& custom_proxy) { | ||||||
|  |   QString bearer = m_oauth2->bearer().toLocal8Bit(); | ||||||
|  | 
 | ||||||
|  |   if (bearer.isEmpty()) { | ||||||
|  |     throw ApplicationException(tr("you are not logged in")); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   QList<QPair<QByteArray, QByteArray>> headers; | ||||||
|  | 
 | ||||||
|  |   headers.append(QPair<QByteArray, QByteArray>(QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), | ||||||
|  |                                                m_oauth2->bearer().toLocal8Bit())); | ||||||
|  | 
 | ||||||
|  |   int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); | ||||||
|  |   QString after; | ||||||
|  |   QList<Feed*> subs; | ||||||
|  | 
 | ||||||
|  |   do { | ||||||
|  |     QByteArray output; | ||||||
|  |     QString final_url = QSL(REDDIT_API_SUBREDDITS).arg(QString::number(100)); | ||||||
|  | 
 | ||||||
|  |     if (!after.isEmpty()) { | ||||||
|  |       final_url += QSL("&after=%1").arg(after); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = NetworkFactory::performNetworkOperation(final_url, | ||||||
|  |                                                           timeout, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           output, | ||||||
|  |                                                           QNetworkAccessManager::Operation::GetOperation, | ||||||
|  |                                                           headers, | ||||||
|  |                                                           false, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           custom_proxy) | ||||||
|  |                     .m_networkError; | ||||||
|  | 
 | ||||||
|  |     if (result != QNetworkReply::NetworkError::NoError) { | ||||||
|  |       throw NetworkException(result, output); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       QJsonDocument doc = QJsonDocument::fromJson(output); | ||||||
|  |       QJsonObject root_doc = doc.object(); | ||||||
|  | 
 | ||||||
|  |       after = root_doc["data"].toObject()["after"].toString(); | ||||||
|  | 
 | ||||||
|  |       for (const QJsonValue& sub_val : root_doc["data"].toObject()["children"].toArray()) { | ||||||
|  |         const auto sub_obj = sub_val.toObject()["data"].toObject(); | ||||||
|  | 
 | ||||||
|  |         Feed* new_sub = new Feed(); | ||||||
|  | 
 | ||||||
|  |         new_sub->setCustomId(sub_obj["id"].toString()); | ||||||
|  |         new_sub->setTitle(sub_obj["title"].toString()); | ||||||
|  |         new_sub->setDescription(sub_obj["public_description"].toString()); | ||||||
|  | 
 | ||||||
|  |         QIcon icon; | ||||||
|  |         QString icon_url = sub_obj["community_icon"].toString(); | ||||||
|  | 
 | ||||||
|  |         if (icon_url.isEmpty()) { | ||||||
|  |           icon_url = sub_obj["icon_img"].toString(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (icon_url.contains(QL1S("?"))) { | ||||||
|  |           icon_url = icon_url.mid(0, icon_url.indexOf(QL1S("?"))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!icon_url.isEmpty() && | ||||||
|  |             NetworkFactory::downloadIcon({{icon_url, true}}, timeout, icon, headers, custom_proxy) == | ||||||
|  |               QNetworkReply::NetworkError::NoError) { | ||||||
|  |           new_sub->setIcon(icon); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         subs.append(new_sub); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   while (!after.isEmpty()); | ||||||
|  | 
 | ||||||
|  |   // posty dle jmena redditu
 | ||||||
|  |   // https://oauth.reddit.com/<SUBREDDIT>/new
 | ||||||
|  |   //
 | ||||||
|  |   // komenty pro post dle id postu
 | ||||||
|  |   // https://oauth.reddit.com/<SUBREDDIT>/comments/<ID-POSTU>
 | ||||||
|  | 
 | ||||||
|  |   return subs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QList<Message> RedditNetworkFactory::hot(const QString& sub_name, const QNetworkProxy& custom_proxy) { | ||||||
|  |   QString bearer = m_oauth2->bearer().toLocal8Bit(); | ||||||
|  | 
 | ||||||
|  |   if (bearer.isEmpty()) { | ||||||
|  |     throw ApplicationException(tr("you are not logged in")); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   QList<QPair<QByteArray, QByteArray>> headers; | ||||||
|  | 
 | ||||||
|  |   headers.append(QPair<QByteArray, QByteArray>(QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), | ||||||
|  |                                                m_oauth2->bearer().toLocal8Bit())); | ||||||
|  | 
 | ||||||
|  |   int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); | ||||||
|  |   QString after; | ||||||
|  |   QList<Message> msgs; | ||||||
|  | 
 | ||||||
|  |   do { | ||||||
|  |     QByteArray output; | ||||||
|  |     QString final_url = QSL(REDDIT_API_HOT).arg(QString::number(100), sub_name, QSL("GLOBAL")); | ||||||
|  | 
 | ||||||
|  |     if (!after.isEmpty()) { | ||||||
|  |       final_url += QSL("&after=%1").arg(after); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = NetworkFactory::performNetworkOperation(final_url, | ||||||
|  |                                                           timeout, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           output, | ||||||
|  |                                                           QNetworkAccessManager::Operation::GetOperation, | ||||||
|  |                                                           headers, | ||||||
|  |                                                           false, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           {}, | ||||||
|  |                                                           custom_proxy) | ||||||
|  |                     .m_networkError; | ||||||
|  | 
 | ||||||
|  |     if (result != QNetworkReply::NetworkError::NoError) { | ||||||
|  |       throw NetworkException(result, output); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       QJsonDocument doc = QJsonDocument::fromJson(output); | ||||||
|  |       QJsonObject root_doc = doc.object(); | ||||||
|  | 
 | ||||||
|  |       after = root_doc["data"].toObject()["after"].toString(); | ||||||
|  | 
 | ||||||
|  |       for (const QJsonValue& sub_val : root_doc["data"].toObject()["children"].toArray()) { | ||||||
|  |         const auto msg_obj = sub_val.toObject()["data"].toObject(); | ||||||
|  | 
 | ||||||
|  |         Message new_msg; | ||||||
|  | 
 | ||||||
|  |         new_msg.m_customId = msg_obj["id"].toString(); | ||||||
|  |         new_msg.m_title = msg_obj["title"].toString(); | ||||||
|  |         new_msg.m_author = msg_obj["author"].toString(); | ||||||
|  |         new_msg.m_createdFromFeed = true; | ||||||
|  |         new_msg.m_created = | ||||||
|  |           QDateTime::fromSecsSinceEpoch(msg_obj["created_utc"].toVariant().toLongLong(), Qt::TimeSpec::UTC); | ||||||
|  |         new_msg.m_url = QSL("https://reddit.com") + msg_obj["permalink"].toString(); | ||||||
|  |         new_msg.m_contents = | ||||||
|  |           msg_obj["description_html"] | ||||||
|  |             .toString(); // když prazdny, je poustnutej třeba obrazek či odkaz?, viz property "post_hint"?
 | ||||||
|  |         new_msg.m_rawContents = QJsonDocument(msg_obj).toJson(QJsonDocument::JsonFormat::Compact); | ||||||
|  | 
 | ||||||
|  |         msgs.append(new_msg); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   while (!after.isEmpty()); | ||||||
|  | 
 | ||||||
|  |   // posty dle jmena redditu
 | ||||||
|  |   // https://oauth.reddit.com/<SUBREDDIT>/new
 | ||||||
|  |   //
 | ||||||
|  |   // komenty pro post dle id postu
 | ||||||
|  |   // https://oauth.reddit.com/<SUBREDDIT>/comments/<ID-POSTU>
 | ||||||
|  | 
 | ||||||
|  |   return msgs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RedditNetworkFactory::onTokensError(const QString& error, const QString& error_description) { | void RedditNetworkFactory::onTokensError(const QString& error, const QString& error_description) { | ||||||
|   Q_UNUSED(error) |   Q_UNUSED(error) | ||||||
| 
 | 
 | ||||||
|   qApp->showGuiMessage(Notification::Event::LoginFailure, { |   qApp->showGuiMessage(Notification::Event::LoginFailure, | ||||||
|     tr("Reddit: authentication error"), |                        {tr("Reddit: authentication error"), | ||||||
|     tr("Click this to login again. Error is: '%1'").arg(error_description), |                         tr("Click this to login again. Error is: '%1'").arg(error_description), | ||||||
|     QSystemTrayIcon::MessageIcon::Critical }, |                         QSystemTrayIcon::MessageIcon::Critical}, | ||||||
|                        {}, { |                        {}, | ||||||
|     tr("Login"), |                        {tr("Login"), [this]() { | ||||||
|     [this]() { |                           m_oauth2->setAccessToken(QString()); | ||||||
|       m_oauth2->setAccessToken(QString()); |                           m_oauth2->setRefreshToken(QString()); | ||||||
|       m_oauth2->setRefreshToken(QString()); |                           m_oauth2->login(); | ||||||
|       m_oauth2->login(); |                         }}); | ||||||
|     } }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RedditNetworkFactory::onAuthFailed() { | void RedditNetworkFactory::onAuthFailed() { | ||||||
|   qApp->showGuiMessage(Notification::Event::LoginFailure, { |   qApp->showGuiMessage(Notification::Event::LoginFailure, | ||||||
|     tr("Reddit: authorization denied"), |                        {tr("Reddit: authorization denied"), | ||||||
|     tr("Click this to login again."), |                         tr("Click this to login again."), | ||||||
|     QSystemTrayIcon::MessageIcon::Critical }, |                         QSystemTrayIcon::MessageIcon::Critical}, | ||||||
|                        {}, { |                        {}, | ||||||
|     tr("Login"), |                        {tr("Login"), [this]() { | ||||||
|     [this]() { |                           m_oauth2->login(); | ||||||
|       m_oauth2->login(); |                         }}); | ||||||
|     } }); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,8 +18,10 @@ class RedditServiceRoot; | ||||||
| class OAuth2Service; | class OAuth2Service; | ||||||
| class Downloader; | class Downloader; | ||||||
| 
 | 
 | ||||||
|  | struct Subreddit {}; | ||||||
|  | 
 | ||||||
| class RedditNetworkFactory : public QObject { | class RedditNetworkFactory : public QObject { | ||||||
|   Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|   public: |   public: | ||||||
|     explicit RedditNetworkFactory(QObject* parent = nullptr); |     explicit RedditNetworkFactory(QObject* parent = nullptr); | ||||||
|  | @ -40,6 +42,8 @@ class RedditNetworkFactory : public QObject { | ||||||
| 
 | 
 | ||||||
|     // API methods.
 |     // API methods.
 | ||||||
|     QVariantHash me(const QNetworkProxy& custom_proxy); |     QVariantHash me(const QNetworkProxy& custom_proxy); | ||||||
|  |     QList<Feed*> subreddits(const QNetworkProxy& custom_proxy); | ||||||
|  |     QList<Message> hot(const QString& sub_name, const QNetworkProxy& custom_proxy); | ||||||
| 
 | 
 | ||||||
|   private slots: |   private slots: | ||||||
|     void onTokensError(const QString& error, const QString& error_description); |     void onTokensError(const QString& error, const QString& error_description); | ||||||
|  |  | ||||||
|  | @ -30,6 +30,12 @@ void RedditServiceRoot::updateTitle() { | ||||||
| RootItem* RedditServiceRoot::obtainNewTreeForSyncIn() const { | RootItem* RedditServiceRoot::obtainNewTreeForSyncIn() const { | ||||||
|   auto* root = new RootItem(); |   auto* root = new RootItem(); | ||||||
| 
 | 
 | ||||||
|  |   auto feeds = m_network->subreddits(networkProxy()); | ||||||
|  | 
 | ||||||
|  |   for (auto* feed : feeds) { | ||||||
|  |     root->appendChild(feed); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   return root; |   return root; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -58,13 +64,14 @@ void RedditServiceRoot::setCustomDatabaseData(const QVariantHash& data) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QList<Message> RedditServiceRoot::obtainNewMessages(Feed* feed, | QList<Message> RedditServiceRoot::obtainNewMessages(Feed* feed, | ||||||
|                                                     const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages, |                                                     const QHash<ServiceRoot::BagOfMessages, QStringList>& | ||||||
|  |                                                       stated_messages, | ||||||
|                                                     const QHash<QString, QStringList>& tagged_messages) { |                                                     const QHash<QString, QStringList>& tagged_messages) { | ||||||
|   Q_UNUSED(stated_messages) |   Q_UNUSED(stated_messages) | ||||||
|   Q_UNUSED(tagged_messages) |   Q_UNUSED(tagged_messages) | ||||||
|   Q_UNUSED(feed) |   Q_UNUSED(feed) | ||||||
| 
 | 
 | ||||||
|   QList<Message> messages; |   QList<Message> messages = m_network->hot(feed->title(), networkProxy()); | ||||||
| 
 | 
 | ||||||
|   return messages; |   return messages; | ||||||
| } | } | ||||||
|  | @ -100,13 +107,14 @@ void RedditServiceRoot::start(bool freshly_activated) { | ||||||
| 
 | 
 | ||||||
|   updateTitle(); |   updateTitle(); | ||||||
| 
 | 
 | ||||||
|   /*
 |   if (getSubTreeFeeds().isEmpty()) { | ||||||
|      if (getSubTreeFeeds().isEmpty()) { |     m_network->oauth()->login([this]() { | ||||||
|      syncIn(); |       syncIn(); | ||||||
|      } |     }); | ||||||
|    */ |   } | ||||||
| 
 |   else { | ||||||
|   m_network->oauth()->login(); |     m_network->oauth()->login(); | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QString RedditServiceRoot::code() const { | QString RedditServiceRoot::code() const { | ||||||
|  | @ -115,11 +123,9 @@ QString RedditServiceRoot::code() const { | ||||||
| 
 | 
 | ||||||
| QString RedditServiceRoot::additionalTooltip() const { | QString RedditServiceRoot::additionalTooltip() const { | ||||||
|   return tr("Authentication status: %1\n" |   return tr("Authentication status: %1\n" | ||||||
|             "Login tokens expiration: %2").arg(network()->oauth()->isFullyLoggedIn() |             "Login tokens expiration: %2") | ||||||
|                                                ? tr("logged-in") |     .arg(network()->oauth()->isFullyLoggedIn() ? tr("logged-in") : tr("NOT logged-in"), | ||||||
|                                                : tr("NOT logged-in"), |          network()->oauth()->tokensExpireIn().isValid() ? network()->oauth()->tokensExpireIn().toString() : QSL("-")); | ||||||
|                                                network()->oauth()->tokensExpireIn().isValid() ? |  | ||||||
|                                                network()->oauth()->tokensExpireIn().toString() : QSL("-")); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RedditServiceRoot::saveAllCachedData(bool ignore_errors) { | void RedditServiceRoot::saveAllCachedData(bool ignore_errors) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue