File indexing completed on 2024-05-05 04:57:28
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 "twitterpostwidget.h" 0010 0011 #include <QAction> 0012 #include <QMenu> 0013 #include <QPushButton> 0014 0015 #include <KLocalizedString> 0016 0017 #include "choqokbehaviorsettings.h" 0018 #include "choqoktools.h" 0019 #include "mediamanager.h" 0020 #include "textbrowser.h" 0021 0022 #include "twitterapiaccount.h" 0023 #include "twitterapimicroblog.h" 0024 #include "twitterapiwhoiswidget.h" 0025 0026 #include "twittersearch.h" 0027 0028 const QRegExp TwitterPostWidget::mTwitterUserRegExp(QLatin1String("([\\s\\W]|^)@([a-z0-9_]+){1,20}"), Qt::CaseInsensitive); 0029 const QRegExp TwitterPostWidget::mTwitterTagRegExp(QLatin1String("([\\s]|^)#([\\w_\\.\\-]+)"), Qt::CaseInsensitive); 0030 const QString TwitterPostWidget::mQuotedTextBase(QLatin1String("<div style=\"padding-top:5px;padding-bottom:3px;\"><table style=\"%4 border-style:groove;\" border=\"1\"><tr><td style=\"padding-top:4px;padding-bottom:4px;padding-left:3px;padding-right:3px;\"><table> <tr><td width=\"40\" style=\"padding-right:5px;\"><img src=\"img://quotedProfileImage\" width=\"40\" height=\"40\" /></td><td dir=\"%2\"><p><b><a>%3:</b> %1</p></td></tr></table></td></tr></table></div>")); 0031 const QUrl TwitterPostWidget::mQuotedAvatarResourceUrl(QLatin1String("img://quotedProfileImage")); 0032 TwitterPostWidget::TwitterPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent): TwitterApiPostWidget(account, post, parent) 0033 { 0034 } 0035 0036 TwitterPostWidget::~TwitterPostWidget() 0037 { 0038 } 0039 0040 void TwitterPostWidget::initUi() 0041 { 0042 TwitterApiPostWidget::initUi(); 0043 0044 if ( ! currentPost()->quotedPost.content.isEmpty() ) { 0045 if( !setupQuotedAvatar() ){ 0046 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl, 0047 Choqok::MediaManager::self()->defaultImage()); 0048 } 0049 0050 auto dir = getDirection(currentPost()->quotedPost.content); 0051 auto text = prepareStatus(currentPost()->quotedPost.content); 0052 QString user = QStringLiteral("<a href='user://%1'>%1</a>").arg(currentPost()->quotedPost.user.userName); 0053 QString quoteText = mQuotedTextBase.arg(text, dir, user, QLatin1String("background-color:%1;")); 0054 setExtraContents(quoteText.arg(getBackgroundColor())); 0055 updateUi(); 0056 } 0057 0058 QPushButton *btn = buttons().value(QLatin1String("btnResend")); 0059 0060 if (btn) { 0061 QMenu *menu = new QMenu(btn); 0062 QAction *resend = new QAction(i18n("Manual ReSend"), menu); 0063 connect(resend, &QAction::triggered, this, &TwitterPostWidget::slotResendPost); 0064 QAction *repeat = new QAction(i18n("Retweet"), menu); 0065 repeat->setToolTip(i18n("Retweet post using API")); 0066 connect(repeat, &QAction::triggered, this, &TwitterPostWidget::repeatPost); 0067 // If person protects their acc, we will use simple adding RT before message 0068 if (!currentPost()->author.isProtected) { 0069 menu->addAction(repeat); 0070 } 0071 menu->addAction(resend); 0072 btn->setMenu(menu); 0073 } 0074 } 0075 0076 QString TwitterPostWidget::prepareStatus(const QString &text) 0077 { 0078 QString res = TwitterApiPostWidget::prepareStatus(text); 0079 res.replace(mTwitterUserRegExp, QLatin1String("\\1@<a href='user://\\2'>\\2</a>")); 0080 res.replace(mTwitterTagRegExp, QLatin1String("\\1#<a href='tag://\\2'>\\2</a>")); 0081 return res; 0082 } 0083 0084 bool TwitterPostWidget::isRemoveAvailable() 0085 { 0086 if (currentAccount()->username().compare(currentPost()->author.userName, Qt::CaseInsensitive) == 0) { 0087 return true; 0088 } else if (currentPost()->isPrivate) { 0089 return true; 0090 } else { 0091 return false; 0092 } 0093 } 0094 0095 void TwitterPostWidget::slotReplyToAll() 0096 { 0097 QStringList nicks; 0098 nicks.append(currentPost()->author.userName); 0099 0100 QString txt = QStringLiteral("@%1 ").arg(currentPost()->author.userName); 0101 0102 int pos = 0; 0103 while ((pos = mTwitterUserRegExp.indexIn(currentPost()->content, pos)) != -1) { 0104 if (mTwitterUserRegExp.cap(2).toLower() != currentAccount()->username() && 0105 mTwitterUserRegExp.cap(2).toLower() != currentPost()->author.userName && 0106 !nicks.contains(mTwitterUserRegExp.cap(2).toLower())) { 0107 nicks.append(mTwitterUserRegExp.cap(2)); 0108 txt += QStringLiteral("@%1 ").arg(mTwitterUserRegExp.cap(2)); 0109 } 0110 pos += mTwitterUserRegExp.matchedLength(); 0111 } 0112 0113 txt.chop(1); 0114 0115 Q_EMIT reply(txt, currentPost()->postId, currentPost()->author.userName); 0116 } 0117 0118 void TwitterPostWidget::checkAnchor(const QUrl &url) 0119 { 0120 QString scheme = url.scheme(); 0121 TwitterApiMicroBlog *blog = qobject_cast<TwitterApiMicroBlog *>(currentAccount()->microblog()); 0122 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(currentAccount()); 0123 if (scheme == QLatin1String("tag")) { 0124 blog->searchBackend()->requestSearchResults(currentAccount(), 0125 QUrl::fromAce(url.host().toUtf8()), 0126 (int)TwitterSearch::ReferenceHashtag); 0127 } else if (scheme == QLatin1String("user")) { 0128 QMenu menu; 0129 QAction *info = new QAction(QIcon::fromTheme(QLatin1String("user-identity")), i18nc("Who is user", "Who is %1", url.host()), 0130 &menu); 0131 QAction *from = new QAction(QIcon::fromTheme(QLatin1String("edit-find-user")), i18nc("Posts from user", "Posts from %1", url.host()), 0132 &menu); 0133 QAction *to = new QAction(QIcon::fromTheme(QLatin1String("meeting-attending")), i18nc("Replies to user", "Replies to %1", 0134 url.host()), 0135 &menu); 0136 QAction *cont = new QAction(QIcon::fromTheme(QLatin1String("user-properties")), i18nc("Including user name", "Including %1", 0137 url.host()), 0138 &menu); 0139 QAction *openInBrowser = new QAction(QIcon::fromTheme(QLatin1String("internet-services")), 0140 i18nc("Open profile page in browser", 0141 "Open profile in browser"), &menu); 0142 from->setData(TwitterSearch::FromUser); 0143 to->setData(TwitterSearch::ToUser); 0144 cont->setData(TwitterSearch::ReferenceUser); 0145 menu.addAction(info); 0146 menu.addAction(from); 0147 menu.addAction(to); 0148 menu.addAction(cont); 0149 menu.addAction(openInBrowser); 0150 0151 //Subscribe/UnSubscribe/Block 0152 bool isSubscribe = false; 0153 QString accountUsername = currentAccount()->username().toLower(); 0154 QString postUsername = url.host().toLower(); 0155 QAction *subscribe = nullptr, *block = nullptr, *replyTo = nullptr, *dMessage = nullptr, *reportSpam = nullptr; 0156 if (accountUsername != postUsername) { 0157 menu.addSeparator(); 0158 QMenu *actionsMenu = menu.addMenu(QIcon::fromTheme(QLatin1String("applications-system")), i18n("Actions")); 0159 replyTo = new QAction(QIcon::fromTheme(QLatin1String("edit-undo")), i18nc("Write a message to user attention", "Write to %1", 0160 url.host()), actionsMenu); 0161 actionsMenu->addAction(replyTo); 0162 if (account->friendsList().contains(url.host(), 0163 Qt::CaseInsensitive)) { 0164 dMessage = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18nc("Send direct message to user", 0165 "Send private message to %1", 0166 url.host()), actionsMenu); 0167 actionsMenu->addAction(dMessage); 0168 isSubscribe = false;//It's UnSubscribe 0169 subscribe = new QAction(QIcon::fromTheme(QLatin1String("list-remove-user")), 0170 i18nc("Unfollow user", 0171 "Unfollow %1", url.host()), actionsMenu); 0172 } else { 0173 isSubscribe = true; 0174 subscribe = new QAction(QIcon::fromTheme(QLatin1String("list-add-user")), 0175 i18nc("Follow user", 0176 "Follow %1", url.host()), actionsMenu); 0177 } 0178 block = new QAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), 0179 i18nc("Block user", 0180 "Block %1", url.host()), actionsMenu); 0181 reportSpam = new QAction(QIcon::fromTheme(QLatin1String("irc-voice")), 0182 i18nc("Report user", 0183 "Report %1 as spam", url.host()), actionsMenu); 0184 0185 actionsMenu->addAction(subscribe); 0186 actionsMenu->addAction(block); 0187 actionsMenu->addAction(reportSpam); 0188 } 0189 0190 QAction *ret = menu.exec(QCursor::pos()); 0191 if (ret == nullptr) { 0192 return; 0193 } 0194 if (ret == info) { 0195 TwitterApiWhoisWidget *wd = new TwitterApiWhoisWidget(account, url.host(), *currentPost(), this); 0196 wd->show(QCursor::pos()); 0197 return; 0198 } else if (ret == subscribe) { 0199 if (isSubscribe) { 0200 blog->createFriendship(currentAccount(), url.host()); 0201 } else { 0202 blog->destroyFriendship(currentAccount(), url.host()); 0203 } 0204 return; 0205 } else if (ret == block) { 0206 blog->blockUser(currentAccount(), url.host()); 0207 return; 0208 } else if (ret == reportSpam) { 0209 blog->reportUserAsSpam(currentAccount(), url.host()); 0210 return; 0211 } else if (ret == openInBrowser) { 0212 Choqok::openUrl(currentAccount()->microblog()->profileUrl(currentAccount(), url.host())); 0213 return; 0214 } else if (ret == replyTo) { 0215 Q_EMIT reply(QStringLiteral("@%1").arg(url.host()), QString(), url.host()); 0216 return; 0217 } else if (ret == dMessage) { 0218 blog->showDirectMessageDialog(account, url.host()); 0219 return; 0220 } 0221 int type = ret->data().toInt(); 0222 blog->searchBackend()->requestSearchResults(currentAccount(), 0223 url.host(), type, QString(), Choqok::BehaviorSettings::countOfPosts()); 0224 } else { 0225 TwitterApiPostWidget::checkAnchor(url); 0226 } 0227 } 0228 0229 bool TwitterPostWidget::setupQuotedAvatar() 0230 { 0231 QPixmap pix = Choqok::MediaManager::self()->fetchImage(currentPost()->quotedPost.user.profileImageUrl, 0232 Choqok::MediaManager::Async); 0233 if (!pix.isNull()) { 0234 quotedAvatarFetched(currentPost()->quotedPost.user.profileImageUrl, pix); 0235 return true; 0236 } else { 0237 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched, 0238 this, &TwitterPostWidget::quotedAvatarFetched); 0239 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError, 0240 this, &TwitterPostWidget::quotedAvatarFetchError); 0241 return false; 0242 } 0243 } 0244 0245 void TwitterPostWidget::quotedAvatarFetched(const QUrl &remoteUrl, const QPixmap &pixmap) 0246 { 0247 if (remoteUrl == currentPost()->quotedPost.user.profileImageUrl) { 0248 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl, pixmap); 0249 disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched, 0250 this, &TwitterPostWidget::quotedAvatarFetched); 0251 disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError, 0252 this, &TwitterPostWidget::quotedAvatarFetchError); 0253 } 0254 } 0255 0256 void TwitterPostWidget::quotedAvatarFetchError(const QUrl &remoteUrl, const QString &errMsg) 0257 { 0258 Q_UNUSED(errMsg); 0259 if (remoteUrl == currentPost()->quotedPost.user.profileImageUrl) { 0260 ///Avatar fetching is failed! but will not disconnect to get the img if it fetches later! 0261 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl, 0262 QIcon::fromTheme(QLatin1String("image-missing")).pixmap(40)); 0263 } 0264 } 0265 0266 QString TwitterPostWidget::getBackgroundColor() 0267 { 0268 QString style = styleSheet(); 0269 QLatin1String str{ "background-color:rgb(" }; 0270 int idx = style.indexOf(str); 0271 if(idx != -1){ 0272 idx += str.size(); 0273 int endIdx = style.indexOf(QLatin1String(");"), idx); 0274 if( endIdx != -1 ){ 0275 QStringList rgb = style.mid(idx, endIdx-idx).split(QLatin1Char(',')); 0276 if( rgb.size() == 3 ){ 0277 return QStringLiteral("#%1%2%3").arg( rgb[0].toInt() - 20, 2, 16, QLatin1Char('0') ) 0278 .arg( rgb[1].toInt() - 20, 2, 16, QLatin1Char('0') ) 0279 .arg( rgb[2].toInt() - 20, 2, 16, QLatin1Char('0') ); 0280 } 0281 } 0282 } 0283 0284 return QLatin1String("#ffffff"); 0285 } 0286 0287 #include "moc_twitterpostwidget.cpp"