1#include <PrCommitsList.h>
2
3#include <GitServerCache.h>
4#include <GitHubRestApi.h>
5#include <GitLabRestApi.h>
6#include <CircularPixmap.h>
7#include <ButtonLink.hpp>
8#include <AvatarHelper.h>
9
10#include <QApplication>
11#include <QClipboard>
12#include <QToolTip>
13#include <QCursor>
14#include <QLabel>
15#include <QScrollArea>
16#include <QStandardPaths>
17#include <QVBoxLayout>
18#include <QNetworkReply>
19#include <QDir>
20#include <QFile>
21
22PrCommitsList::PrCommitsList(const QSharedPointer<GitServerCache> &gitServerCache, QWidget *parent)
23 : QFrame(parent)
24 , mGitServerCache(gitServerCache)
25 , mManager(new QNetworkAccessManager())
26{
27 setObjectName("IssuesViewFrame");
28}
29
30PrCommitsList::~PrCommitsList()
31{
32 delete mManager;
33}
34
35void PrCommitsList::loadData(int number)
36{
37 connect(mGitServerCache.get(), &GitServerCache::prUpdated, this, &PrCommitsList::onCommitsReceived,
38 Qt::UniqueConnection);
39
40 mPrNumber = number;
41
42 const auto pr = mGitServerCache->getPullRequest(number);
43
44 mGitServerCache->getApi()->requestCommitsFromPR(pr.number);
45}
46
47void PrCommitsList::onCommitsReceived(const GitServer::PullRequest &pr)
48{
49 disconnect(mGitServerCache.get(), &GitServerCache::prUpdated, this, &PrCommitsList::onCommitsReceived);
50
51 if (mPrNumber != pr.number)
52 return;
53
54 delete mScroll;
55
56 mPrNumber = pr.number;
57
58 const auto commitsLayout = new QVBoxLayout();
59 commitsLayout->setContentsMargins(20, 20, 20, 20);
60 commitsLayout->setAlignment(Qt::AlignTop);
61 commitsLayout->setSpacing(30);
62
63 const auto mIssuesFrame = new QFrame();
64 mIssuesFrame->setObjectName("IssuesViewFrame");
65 mIssuesFrame->setLayout(commitsLayout);
66
67 mScroll = new QScrollArea();
68 mScroll->setWidgetResizable(true);
69 mScroll->setWidget(mIssuesFrame);
70
71 delete layout();
72
73 const auto aLayout = new QVBoxLayout(this);
74 aLayout->setContentsMargins(QMargins());
75 aLayout->setSpacing(0);
76 aLayout->addWidget(mScroll);
77
78 for (auto &commit : pr.commits)
79 {
80 const auto bubble = createBubbleForComment(commit);
81 commitsLayout->addWidget(bubble);
82 }
83
84 commitsLayout->addStretch();
85}
86
87QFrame *PrCommitsList::createBubbleForComment(const GitServer::Commit &commit)
88{
89 const auto days = commit.authorCommittedTimestamp.daysTo(QDateTime::currentDateTime());
90 const auto whenText = days <= 30
91 ? days != 0 ? tr(" %1 days ago").arg(days) : tr(" today")
92 : tr(" on %1").arg(commit.authorCommittedTimestamp.date().toString(QLocale().dateFormat(QLocale::ShortFormat)));
93
94 const auto creator = new QLabel(tr("Committed by <b>%1</b> %2").arg(commit.author.name, whenText));
95
96 auto commitMsg = commit.message.split("\n\n").constFirst().trimmed();
97
98 if (commitMsg.count() >= 47)
99 commitMsg = commitMsg.left(47).append("...");
100
101 const auto link = new ButtonLink(QString("<b>%1</b>").arg(commitMsg));
102 connect(link, &ButtonLink::clicked, this, [this, sha = commit.sha]() { emit openDiff(sha); });
103
104 const auto frame = new QFrame();
105 frame->setObjectName("IssueIntro");
106
107 const auto layout = new QGridLayout(frame);
108 layout->setAlignment(Qt::AlignVertical_Mask);
109 layout->setContentsMargins(10, 10, 10, 10);
110 layout->setHorizontalSpacing(10);
111 layout->setVerticalSpacing(5);
112 layout->addWidget(createAvatar(commit.author.name, commit.author.avatar), 0, 0, 2, 1);
113 layout->addWidget(link, 0, 1);
114 layout->addWidget(creator, 1, 1);
115 layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed), 1, 2);
116
117 const auto shaLink = new ButtonLink(commit.sha);
118 connect(shaLink, &ButtonLink::clicked, this, [this, sha = commit.sha]() {
119 QApplication::clipboard()->setText(sha);
120 QToolTip::showText(QCursor::pos(), tr("Copied!"), this);
121 });
122
123 layout->addWidget(shaLink, 0, 3, 3, 1);
124
125 if (commit.author.name != commit.commiter.name)
126 layout->addWidget(createAvatar(commit.commiter.name, commit.commiter.avatar));
127
128 return frame;
129}
130