File indexing completed on 2024-04-28 04:55:39

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "twitterapiwhoiswidget.h"
0010 
0011 #include <QApplication>
0012 #include <QDesktopWidget>
0013 #include <QIcon>
0014 #include <QJsonDocument>
0015 #include <QPoint>
0016 #include <QPointer>
0017 #include <QStatusBar>
0018 #include <QTextBrowser>
0019 #include <QUrl>
0020 #include <QVBoxLayout>
0021 
0022 #include <KAnimatedButton>
0023 #include <KIO/Job>
0024 #include <KLocalizedString>
0025 
0026 #include "choqokappearancesettings.h"
0027 #include "choqoktools.h"
0028 #include "choqoktypes.h"
0029 #include "mediamanager.h"
0030 #include "microblog.h"
0031 #include "notifymanager.h"
0032 #include "twitterapiaccount.h"
0033 #include "twitterapidebug.h"
0034 #include "twitterapimicroblog.h"
0035 
0036 class TwitterApiWhoisWidget::Private
0037 {
0038 public:
0039     Private(TwitterApiAccount *account, const QString &userN)
0040         : currentAccount(account), waitFrame(nullptr), job(nullptr), username(userN)
0041     {
0042         mBlog = qobject_cast<TwitterApiMicroBlog *>(account->microblog());
0043     }
0044     QTextBrowser *wid;
0045     TwitterApiAccount *currentAccount;
0046     TwitterApiMicroBlog *mBlog;
0047     QFrame *waitFrame;
0048     QPointer<KJob> job;
0049     Choqok::Post currentPost;
0050     QString username;
0051     QString errorMessage;
0052 
0053     QString followersCount;
0054     QString friendsCount;
0055     QString statusesCount;
0056     QString timeZone;
0057     QString imgActions;
0058 //     bool isFollowing;
0059 };
0060 
0061 TwitterApiWhoisWidget::TwitterApiWhoisWidget(TwitterApiAccount *theAccount, const QString &username,
0062         const Choqok::Post &post, QWidget *parent)
0063     : QFrame(parent), d(new Private(theAccount, username))
0064 {
0065     qCDebug(CHOQOK);
0066     setAttribute(Qt::WA_DeleteOnClose);
0067     d->currentPost = post;
0068     loadUserInfo(theAccount, username);
0069 
0070     d->wid = new QTextBrowser(this);
0071     setFrameShape(StyledPanel);
0072     setFrameShadow(Sunken);
0073 
0074     d->wid->setFrameShape(QFrame::NoFrame);
0075     QVBoxLayout *layout = new QVBoxLayout(this);
0076     layout->setContentsMargins(0, 0, 0, 0);
0077     layout->addWidget(d->wid);
0078     this->setLayout(layout);
0079     this->setWindowFlags(Qt::Popup);// | Qt::FramelessWindowHint | Qt::Ta);
0080     d->wid->setOpenLinks(false);
0081     connect(d->wid, &QTextBrowser::anchorClicked, this, &TwitterApiWhoisWidget::checkAnchor);
0082     setupUi();
0083     setActionImages();
0084 }
0085 
0086 TwitterApiWhoisWidget::~TwitterApiWhoisWidget()
0087 {
0088     qCDebug(CHOQOK);
0089     delete d;
0090 }
0091 
0092 void TwitterApiWhoisWidget::loadUserInfo(TwitterApiAccount *theAccount, const QString &username)
0093 {
0094     qCDebug(CHOQOK);
0095     QString urlStr;
0096     QString user = username;
0097     if (user.contains(QLatin1Char('@'))) {
0098         QStringList lst = user.split(QLatin1Char('@'));
0099         if (lst.count() == 2) { //USER@HOST
0100             QString host = lst[1];
0101             urlStr = QStringLiteral("https://%1/api").arg(host);
0102             user = lst[0];
0103         }
0104     } else if (d->currentPost.source == QLatin1String("ostatus") && !d->currentPost.author.homePageUrl.isEmpty()) {
0105         urlStr = d->currentPost.author.homePageUrl.toDisplayString();
0106         if (urlStr.endsWith(user)) {
0107             int len = urlStr.length();
0108             int userLen = user.length();
0109             urlStr.remove(len - userLen, userLen);
0110             qCDebug(CHOQOK) << urlStr;
0111         }
0112         urlStr.append(QLatin1String("api"));
0113     } else {
0114         urlStr = theAccount->apiUrl().url();
0115     }
0116     QUrl url(urlStr);
0117 
0118     url = url.adjusted(QUrl::StripTrailingSlash);
0119     url.setPath(url.path() + QStringLiteral("/users/show/%1.json").arg(user));
0120 
0121 //     qCDebug(CHOQOK) << url;
0122 
0123     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo);
0124     if (d->currentPost.source != QLatin1String("ostatus")) {
0125         job->addMetaData(QStringLiteral("customHTTPHeader"),
0126                          QStringLiteral("Authorization: ") +
0127                          QLatin1String(d->mBlog->authorizationHeader(theAccount, url, QNetworkAccessManager::GetOperation)));
0128     }
0129     d->job = job;
0130     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiWhoisWidget::userInfoReceived);
0131     job->start();
0132 }
0133 
0134 void TwitterApiWhoisWidget::userInfoReceived(KJob *job)
0135 {
0136     qCDebug(CHOQOK);
0137     if (job->error()) {
0138         qCCritical(CHOQOK) << "Job Error:" << job->errorString();
0139         if (Choqok::UI::Global::mainWindow()->statusBar()) {
0140             Choqok::UI::Global::mainWindow()->statusBar()->showMessage(job->errorString());
0141         }
0142         slotCancel();
0143         return;
0144     }
0145     KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job);
0146 //     qCDebug(CHOQOK)<<stj->data();
0147     const QJsonDocument json = QJsonDocument::fromJson(stj->data());
0148     if (json.isNull()) {
0149         qCDebug(CHOQOK) << "JSON parsing failed! Data is:\n\t" << stj->data();
0150         d->errorMessage = i18n("Cannot load user information.");
0151         updateHtml();
0152         showForm();
0153         return;
0154     }
0155 
0156     const QVariantMap map = json.toVariant().toMap();
0157 
0158     QString timeStr;
0159     Choqok::Post post;
0160     d->errorMessage = map[QLatin1String("error")].toString();
0161     if (d->errorMessage.isEmpty()) {  //No Error
0162         post.author.realName = map[QLatin1String("name")].toString();
0163         post.author.userName = map[QLatin1String("screen_name")].toString();
0164         post.author.location = map[QLatin1String("location")].toString();
0165         post.author.description = map[QLatin1String("description")].toString();
0166         post.author.profileImageUrl = map[QLatin1String("profile_image_url")].toUrl();
0167         post.author.homePageUrl = map[QLatin1String("url")].toUrl();
0168         d->timeZone = map[QLatin1String("time_zone")].toString();
0169         d->followersCount = map[QLatin1String("followers_count")].toString();
0170         d->friendsCount = map[QLatin1String("friends_count")].toString();
0171         d->statusesCount = map[QLatin1String("statuses_count")].toString();
0172         QVariantMap var = map[QLatin1String("status")].toMap();
0173         post.content = var[QLatin1String("text")].toString();
0174         post.creationDateTime = d->mBlog->dateFromString(var[QLatin1String("created_at")].toString());
0175         post.isFavorited = var[QLatin1String("favorited")].toBool();
0176         post.postId = var[QLatin1String("id")].toString();
0177         post.replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString();
0178         post.replyToUser.userId = var[QLatin1String("in_reply_to_user_id")].toString();
0179         post.replyToUser.userName = var[QLatin1String("in_reply_to_screen_name")].toString();
0180         post.source = var[QLatin1String("source")].toString();
0181         d->currentPost = post;
0182     }
0183 
0184     updateHtml();
0185     showForm();
0186 
0187     QPixmap userAvatar = Choqok::MediaManager::self()->fetchImage(post.author.profileImageUrl,
0188                          Choqok::MediaManager::Async);
0189 
0190     if (!userAvatar.isNull()) {
0191         d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("img://profileImage")),
0192                                         userAvatar);
0193     } else {
0194         connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
0195                 this, &TwitterApiWhoisWidget::avatarFetched);
0196         connect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError,
0197                 this, &TwitterApiWhoisWidget::avatarFetchError);
0198     }
0199 }
0200 
0201 void TwitterApiWhoisWidget::avatarFetched(const QUrl &remoteUrl, const QPixmap &pixmap)
0202 {
0203     qCDebug(CHOQOK);
0204     if (remoteUrl == d->currentPost.author.profileImageUrl) {
0205         const QUrl url(QLatin1String("img://profileImage"));
0206         d->wid->document()->addResource(QTextDocument::ImageResource, url, pixmap);
0207         updateHtml();
0208         disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
0209                    this, &TwitterApiWhoisWidget::avatarFetched);
0210         disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError,
0211                    this, &TwitterApiWhoisWidget::avatarFetchError);
0212     }
0213 }
0214 
0215 void TwitterApiWhoisWidget::avatarFetchError(const QUrl &remoteUrl, const QString &errMsg)
0216 {
0217     qCDebug(CHOQOK);
0218     Q_UNUSED(errMsg);
0219     if (remoteUrl == d->currentPost.author.profileImageUrl) {
0220         ///Avatar fetching is failed! but will not disconnect to get the img if it fetches later!
0221         const QUrl url(QLatin1String("img://profileImage"));
0222         d->wid->document()->addResource(QTextDocument::ImageResource, url, QIcon::fromTheme(QLatin1String("image-missing")).pixmap(48));
0223         updateHtml();
0224     }
0225 }
0226 
0227 void TwitterApiWhoisWidget::updateHtml()
0228 {
0229     qCDebug(CHOQOK);
0230     QString html;
0231     if (d->errorMessage.isEmpty()) {
0232         QString url = d->currentPost.author.homePageUrl.isEmpty() ? QString()
0233                       : QStringLiteral("<a title='%1' href='%1'>%1</a>").arg(d->currentPost.author.homePageUrl.toDisplayString());
0234 
0235         QString mainTable = QString(QLatin1String("<table width='100%'><tr>\
0236         <td width=49><img width=48 height=48 src='img://profileImage'/>\
0237         <center><table width='100%' cellpadding='3'><tr>%1</tr></table></center></td>\
0238         <td><table width='100%'><tr><td><font size=5><b>%2</b></font></td>\
0239         <td><a href='choqok://close'><img src='icon://close' title='") + i18n("Close") + QLatin1String("' align='right' /></a></td></tr></table><br/>\
0240         <b>@%3</b>&nbsp;<font size=3>%4</font><font size=2>%5</font><br/>\
0241         <i>%6</i><br/>\
0242         <font size=3>%7</font></td></tr></table>"))
0243                             .arg(d->imgActions)
0244                             .arg(d->currentPost.author.realName.toHtmlEscaped())
0245                             .arg(d->currentPost.author.userName).arg(d->currentPost.author.location.toHtmlEscaped())
0246                             .arg(!d->timeZone.isEmpty() ? QLatin1Char('(') + d->timeZone + QLatin1Char(')') : QString())
0247                             .arg(d->currentPost.author.description)
0248                             .arg(url);
0249 
0250         QString countTable = QString(QLatin1String("<table><tr>\
0251         <td><b>%1</b><br/>") + i18nc("User posts", "Posts") + QLatin1String("</td>\
0252         <td><b>%2</b><br/>") + i18nc("User friends", "Friends") + QLatin1String("</td>\
0253         <td><b>%3</b><br/>") + i18nc("User followers" , "Followers") + QLatin1String("</td>\
0254         </tr></table><br/>"))
0255                              .arg(d->statusesCount)
0256                              .arg(d->friendsCount)
0257                              .arg(d->followersCount);
0258 
0259         html = mainTable + countTable;
0260 
0261         if (!d->currentPost.content.isEmpty()) {
0262             html.append(i18n("<table><tr><b>Last Status:</b> %1</tr></table>", d->currentPost.content));
0263         }
0264 
0265     } else {
0266         html = i18n("<h3>%1</h3>", d->errorMessage);
0267     }
0268     d->wid->setHtml(html);
0269 }
0270 
0271 void TwitterApiWhoisWidget::showForm()
0272 {
0273     qCDebug(CHOQOK);
0274     QPoint pos = d->waitFrame->pos();
0275     d->waitFrame->deleteLater();
0276     d->wid->resize(320, 200);
0277     d->wid->document()->setTextWidth(width() - 2);
0278     d->wid->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0279     int h = d->wid->document()->size().toSize().height() + 10;
0280     d->wid->setMinimumHeight(h);
0281     d->wid->setMaximumHeight(h);
0282     this->resize(320, h + 4);
0283     int desktopHeight = QApplication::desktop()->height();
0284     int desktopWidth = QApplication::desktop()->width();
0285     if ((pos.x() + this->width()) > desktopWidth) {
0286         pos.setX(desktopWidth - width());
0287     }
0288     if ((pos.y() + this->height()) > desktopHeight) {
0289         pos.setY(desktopHeight - height());
0290     }
0291     move(pos);
0292     QWidget::show();
0293 }
0294 
0295 void TwitterApiWhoisWidget::show(QPoint pos)
0296 {
0297     qCDebug(CHOQOK);
0298     d->waitFrame = new QFrame(this);
0299     d->waitFrame->setFrameShape(NoFrame);
0300     d->waitFrame->setWindowFlags(Qt::Popup);
0301     KAnimatedButton *waitButton = new KAnimatedButton;
0302     waitButton->setToolTip(i18n("Please wait..."));
0303     connect(waitButton, &KAnimatedButton::clicked, this, &TwitterApiWhoisWidget::slotCancel);
0304     waitButton->setAnimationPath(QLatin1String("process-working-kde"));
0305     waitButton->start();
0306 
0307     QVBoxLayout *ly = new QVBoxLayout(d->waitFrame);
0308     ly->setSpacing(0);
0309     ly->setContentsMargins(0, 0, 0, 0);
0310     ly->addWidget(waitButton);
0311 
0312     d->waitFrame->move(pos - QPoint(15, 15));
0313     d->waitFrame->show();
0314 }
0315 
0316 void TwitterApiWhoisWidget::checkAnchor(const QUrl url)
0317 {
0318     qCDebug(CHOQOK);
0319     if (url.scheme() == QLatin1String("choqok")) {
0320         if (url.host() == QLatin1String("close")) {
0321             this->close();
0322         } else if (url.host() == QLatin1String("subscribe")) {
0323             d->mBlog->createFriendship(d->currentAccount, d->username);
0324             connect(d->mBlog, &TwitterApiMicroBlog::friendshipCreated,
0325                     this, &TwitterApiWhoisWidget::slotFriendshipCreated);
0326         } else if (url.host() == QLatin1String("unsubscribe")) {
0327             d->mBlog->destroyFriendship(d->currentAccount, d->username);
0328             connect(d->mBlog, &TwitterApiMicroBlog::friendshipDestroyed,
0329                     this, &TwitterApiWhoisWidget::slotFriendshipDestroyed);
0330         } else if (url.host() == QLatin1String("block")) {
0331             d->mBlog->blockUser(d->currentAccount, d->username);
0332 //             connect(d->mBlog, SIGNAL(userBlocked(Choqok::Account*,QString)), SLOT(slotUserBlocked(Choqok::Account*,QString)));
0333         }
0334     } else {
0335         Choqok::openUrl(url);
0336         close();
0337     }
0338 }
0339 
0340 void TwitterApiWhoisWidget::setupUi()
0341 {
0342     qCDebug(CHOQOK);
0343     d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://close")),
0344                                     QIcon::fromTheme(QLatin1String("dialog-close")).pixmap(16));
0345 
0346     QString style = QLatin1String("color: %1; background-color: %2");
0347     if (Choqok::AppearanceSettings::isCustomUi()) {
0348         setStyleSheet(style.arg(Choqok::AppearanceSettings::readForeColor().name())
0349                       .arg(Choqok::AppearanceSettings::readBackColor().name()));
0350     } else {
0351         QPalette p = window()->palette();
0352         setStyleSheet(style.arg(p.color(QPalette::WindowText).name()).arg(p.color(QPalette::Window).name()));
0353     }
0354 }
0355 
0356 void TwitterApiWhoisWidget::slotCancel()
0357 {
0358     qCDebug(CHOQOK);
0359     if (d->waitFrame) {
0360         d->waitFrame->deleteLater();
0361     }
0362     if (d->job) {
0363         d->job->kill();
0364     }
0365     this->close();
0366 }
0367 
0368 void TwitterApiWhoisWidget::setActionImages()
0369 {
0370     d->imgActions.clear();
0371     if (d->username.compare(d->currentAccount->username(), Qt::CaseInsensitive) != 0) {
0372         if (d->currentAccount->friendsList().contains(d->username, Qt::CaseInsensitive)) {
0373             d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://unsubscribe")),
0374                                             QIcon::fromTheme(QLatin1String("list-remove-user")).pixmap(16));
0375             d->imgActions += QLatin1String("<a href='choqok://unsubscribe'><img src='icon://unsubscribe' title='") +
0376                              i18n("Unsubscribe") + QLatin1String("'></a> ");
0377         } else {
0378             d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://subscribe")),
0379                                             QIcon::fromTheme(QLatin1String("list-add-user")).pixmap(16));
0380             d->imgActions += QLatin1String("<a href='choqok://subscribe'><img src='icon://subscribe' title='") +
0381                              i18n("Subscribe") + QLatin1String("'></a> ");
0382         }
0383 
0384         d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://block")),
0385                                         QIcon::fromTheme(QLatin1String("dialog-cancel")).pixmap(16));
0386         d->imgActions += QLatin1String("<a href='choqok://block'><img src='icon://block' title='") + i18n("Block") + QLatin1String("'></a>");
0387     }
0388 }
0389 
0390 void TwitterApiWhoisWidget::slotFriendshipCreated(Choqok::Account *theAccount, const QString &username)
0391 {
0392     if (theAccount == d->currentAccount && username == d->username) {
0393         setActionImages();
0394         updateHtml();
0395     }
0396 }
0397 
0398 void TwitterApiWhoisWidget::slotFriendshipDestroyed(Choqok::Account *theAccount, const QString &username)
0399 {
0400     if (theAccount == d->currentAccount && username == d->username) {
0401         setActionImages();
0402         updateHtml();
0403     }
0404 }
0405 
0406 // void TwitterApiWhoisWidget::slotUserBlocked(Choqok::Account* theAccount, const QString &username)
0407 // {
0408 //     if(theAccount == d->currentAccount && username == d->username){
0409 //         Choqok::NotifyManager::success( i18n("Your posts are blocked for %1.", username) );
0410 //     }
0411 // }
0412 
0413 #include "moc_twitterapiwhoiswidget.cpp"