Start displaying the list of changes (PRs only so far)

This commit is contained in:
Yuri Sizov
2023-03-22 22:21:03 +01:00
parent 0398b54d58
commit 9c95bf4d7b
5 changed files with 467 additions and 10 deletions

View File

@@ -0,0 +1,196 @@
import { LitElement, html, css, customElement, property } from 'lit-element';
import PullRequestItem from "./PullRequestItem";
@customElement('gr-changes-list')
export default class ChangesList extends LitElement {
static get styles() {
return css`
/** Colors and variables **/
:host {
--changes-background-color: #e5edf8;
--changes-toolbar-color: #9bbaed;
--changes-toolbar-accent-color: #5a6f90;
}
@media (prefers-color-scheme: dark) {
:host {
--changes-background-color: #191d23;
--changes-toolbar-color: #222c3d;
--changes-toolbar-accent-color: #566783;
}
}
/** Component styling **/
:host {
flex-grow: 1;
}
:host .version-changes-empty {
color: var(--g-font-color);
display: inline-block;
font-size: 20px;
line-height: 24px;
margin-top: 6px;
margin-bottom: 12px;
padding: 14px 12px;
word-break: break-word;
}
:host .version-changes {
background-color: var(--changes-background-color);
border-radius: 0 4px 4px 0;
padding: 8px 12px;
max-width: 760px;
}
:host .version-changes-toolbar {
background: var(--changes-toolbar-color);
border-radius: 4px;
display: flex;
flex-direction: row;
gap: 16px;
padding: 10px 14px;
margin-bottom: 6px;
}
:host .changes-count {
font-size: 15px;
}
:host .changes-count strong {
font-size: 18px;
}
:host .changes-count-label {
color: var(--dimmed-font-color);
}
@media only screen and (max-width: 900px) {
:host .version-changes {
padding: 8px;
max-width: 95%;
margin: 0px auto;
}
:host .changes-count {
font-size: 17px;
text-align: center;
width: 100%;
}
:host .changes-count strong {
font-size: 20px;
}
}
`;
}
@property({ type: Object }) version = {};
@property({ type: Array }) log = [];
@property({ type: Object }) authors = {};
@property({ type: Object }) commits = {};
@property({ type: Object }) pulls = {};
@property({ type: String }) selectedVersion = "";
@property({ type: String }) selectedRelease = "";
@property({ type: Boolean, reflect: true }) loading = false;
render(){
if (this.selectedVersion === "") {
return html``;
}
if (this.loading) {
return html`
<span class="version-changes-empty">Loading changes...</span>
`
}
let filtered_commits = [];
let filtered_pulls = [];
let commit_log = this.version.commit_log;
if (this.selectedRelease !== "") {
for (let release of this.version.releases) {
if (release.name === this.selectedRelease) {
commit_log = release.commit_log;
break;
}
}
}
commit_log.forEach((commitHash) => {
if (typeof this.commits[commitHash] === "undefined") {
return; // This is not good.
}
let commit = this.commits[commitHash];
filtered_commits.push(commit);
if (commit.is_cherrypick && typeof this.commits[commit.cherrypick_hash] !== "undefined") {
commit = this.commits[commit.cherrypick_hash];
}
if (commit.pull !== "" && typeof this.pulls[commit.pull] !== "undefined") {
const pull = this.pulls[commit.pull];
if (filtered_pulls.indexOf(pull) < 0) {
filtered_pulls.push(this.pulls[commit.pull]);
}
}
});
return html`
<div class="version-changes">
<div class="version-changes-toolbar">
<div class="changes-count">
<strong>${filtered_commits.length}</strong>
<span class="changes-count-label"> ${(filtered_commits.length === 1 ? "commit": "commits")}</span>
</div>
<div class="changes-count">
<strong>${filtered_pulls.length}</strong>
<span class="changes-count-label"> ${(filtered_pulls.length === 1 ? "PR": "PRs")}</span>
</div>
</div>
${(filtered_pulls.length === 0 ? html`
<span class="version-changes-empty">This version contains no new changes.</span>
` : null)}
${filtered_pulls.map((item) => {
let authorIds = [];
item.commits.forEach((commitHash) => {
if (typeof this.commits[commitHash] === "undefined") {
return;
}
const commit = this.commits[commitHash];
commit.authored_by.forEach((authoredBy) => {
if (authorIds.indexOf(authoredBy) < 0) {
authorIds.push(authoredBy);
}
})
});
if (authorIds.indexOf(item.authored_by) < 0) {
authorIds.push(item.authored_by);
}
let authors = [];
authorIds.forEach((authoredBy) => {
if (typeof this.authors[authoredBy] !== "undefined") {
authors.push(this.authors[authoredBy]);
}
});
return html`
<gr-pull-request
.id="${item.public_id}"
.title="${item.title}"
.authors="${authors}"
.url="${item.url}"
.created_at="${item.created_at}"
.updated_at="${item.updated_at}"
.labels="${item.labels}"
/>
`;
})}
</div>
`;
}
}

View File

@@ -0,0 +1,226 @@
import { LitElement, html, css, customElement, property } from 'lit-element';
@customElement('gr-pull-request')
export default class PullRequestItem extends LitElement {
static get styles() {
return css`
/** Colors and variables **/
:host {
--pr-border-color: #fcfcfa;
--star-font-color: #ffcc31;
--ghost-font-color: #738b99;
}
@media (prefers-color-scheme: dark) {
:host {
--pr-border-color: #0d1117;
--star-font-color: #e0c537;
--ghost-font-color: #495d68;
}
}
/** Component styling **/
:host {
border-bottom: 3px solid var(--pr-border-color);
display: block;
padding: 14px 12px 20px 12px;
}
:host a {
color: var(--link-font-color);
text-decoration: none;
}
:host a:hover {
color: var(--link-font-color-hover);
}
:host .pr-title {
display: inline-block;
font-size: 20px;
margin-top: 6px;
margin-bottom: 12px;
}
:host .pr-title-name {
color: var(--g-font-color);
line-height: 24px;
word-break: break-word;
}
:host .pr-container--draft .pr-title {
filter: saturate(0.4);
}
:host .pr-container--draft .pr-title-name {
opacity: 0.7;
}
:host .pr-meta {
color: var(--dimmed-font-color);
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 16px;
font-size: 13px;
}
:host .pr-labels {
display: flex;
flex-flow: row wrap;
padding: 4px 0;
}
:host .pr-label {
padding-right: 8px;
}
:host .pr-label-dot {
border-radius: 4px;
box-shadow: rgb(0 0 0 / 28%) 0 0 3px 0;
display: inline-block;
width: 8px;
height: 8px;
}
:host .pr-label-name {
padding-left: 3px;
}
:host .pr-people {
display: flex;
flex-direction: column;
gap: 2px;
text-align: right;
}
:host .pr-author {
}
:host .pr-author-value {
display: inline-flex;
flex-direction: row;
gap: 6px;
align-items: center;
vertical-align: bottom;
padding-left: 6px;
}
:host .pr-author-value--hot:before {
content: "★";
color: var(--star-font-color);
}
:host .pr-author-value--ghost {
color: var(--ghost-font-color);
font-weight: 600;
}
:host .pr-author-avatar {
background-size: cover;
border-radius: 2px;
display: inline-block;
width: 16px;
height: 16px;
}
@media only screen and (max-width: 900px) {
:host {
padding: 14px 0 20px 0;
}
:host .pr-meta {
flex-wrap: wrap;
}
:host .pr-labels {
width: 100%;
justify-content: space-between;
}
}
`;
}
@property({ type: String }) id = '';
@property({ type: String }) title = '';
@property({ type: Array }) authors = [];
@property({ type: String, reflect: true }) url = '';
@property({ type: String }) created_at = '';
@property({ type: String }) updated_at = '';
@property({ type: Array }) labels = [];
render(){
// Some labels aren't useful in this context; hide them.
let filteredLabels = [];
this.labels.forEach((item) => {
if (item.name.startsWith("cherrypick:")) {
return;
}
filteredLabels.push(item);
});
return html`
<div class="pr-container ${(this.draft ? "pr-container--draft" : "")}">
<div class="pr-title">
<span class="pr-title-name">${this.title}</span>
</div>
<div class="pr-meta">
<div class="pr-labels">
${filteredLabels.map((item) => {
return html`
<span
class="pr-label"
>
<span
class="pr-label-dot"
style="background-color: ${item.color}"
></span>
<span
class="pr-label-name"
>
${item.name}
</span>
</span>
`;
})}
</div>
<div class="pr-people">
<div>
<span>submitted as </span>
<a
href="${this.url}"
target="_blank"
title="Open PR #${this.id} on GitHub"
>
GH-${this.id}
</a>
</div>
<div class="pr-author">
<span>by </span>
${this.authors.map((author) => {
const authorClassList = [ "pr-author-value" ];
if (author.pull_count > 12) {
authorClassList.push("pr-author-value--hot");
}
if (author.id === "") {
authorClassList.push("pr-author-value--ghost");
}
return html`
<a
class="${authorClassList.join(" ")}"
href="https://github.com/godotengine/godot/pulls/${author.user}"
target="_blank"
title="Open all PRs by ${author.user}"
>
<span
class="pr-author-avatar"
style="background-image: url('${author.avatar}')"
>
</span>
${author.user}
</a>
`;
})}
</div>
</div>
</div>
</div>
`;
}
}

View File

@@ -174,7 +174,10 @@ export default class VersionItem extends LitElement {
${this.name}
</span>
<span class="${countClassList.join(" ")}">
<span
class="${countClassList.join(" ")}"
title="${this.loading ? "" : `${this.pull_count} changes since last release.`}"
>
${this.loading ? "" : this.pull_count}
</span>

View File

@@ -162,7 +162,7 @@ export default class VersionList extends LitElement {
<gr-version-item
.name="${item.name}"
.type="${"main"}"
.pull_count="${item.pull_count}"
.pull_count="${item.commit_log.length}"
?active="${this.selectedVersion === item.name}"
?expanded="${this.toggledVersions.includes(item.name)}"
?loading="${this.loadingVersions.includes(item.name)}"
@@ -178,7 +178,7 @@ export default class VersionList extends LitElement {
<gr-version-item
.name="${release.name}"
.type="${"sub"}"
.pull_count="${release.pull_count}"
.pull_count="${release.commit_log.length}"
?active="${this.selectedVersion === item.name && this.selectedRelease === release.name}"
@click="${this._onItemClicked.bind(this, "sub", item.name, release.name)}"
@iconclick="${this._onItemIconClicked.bind(this, "sub", item.name, release.name)}"

View File

@@ -6,6 +6,7 @@ import IndexHeader from "./components/IndexHeader";
import IndexDescription from "./components/IndexDescription";
import VersionList from "./components/versions/VersionList";
import ChangesList from "./components/changes/ChangesList";
@customElement('entry-component')
export default class EntryComponent extends LitElement {
@@ -86,9 +87,9 @@ export default class EntryComponent extends LitElement {
this._versions = data;
this._versions.forEach((version) => {
version.pull_count = 0;
version.commit_log = [];
version.releases.forEach((release) => {
release.pull_count = 0;
release.commit_log = [];
});
});
} else {
@@ -108,20 +109,21 @@ export default class EntryComponent extends LitElement {
this._loadingVersions.push(version.name);
const versionData = await greports.api.getVersionData(this._selectedRepository, version.name);
versionData.config = version;
this._versionData[version.name] = versionData;
// Calculate number of changes for the version, and each if its releases.
const commitLog = versionData.log;
const [...commitLog] = versionData.log;
commitLog.reverse();
version.pull_count = commitLog.length;
version.commit_log = commitLog;
version.releases.forEach((release) => {
release.pull_count = 0;
release.commit_log = [];
let counting = false;
commitLog.forEach((commitHash, index) => {
if (counting) {
release.pull_count += 1;
release.commit_log.push(commitHash);
}
// We need to check indices for some refs, because they are not written
@@ -131,7 +133,7 @@ export default class EntryComponent extends LitElement {
if (release.from_ref === version.from_ref && index === 0) {
counting = true;
// HACK: Exclude the lower end by default, but include for the first range.
release.pull_count += 1;
release.commit_log.push(commitHash);
}
else if (commitHash === release.from_ref) {
counting = true;
@@ -166,6 +168,22 @@ export default class EntryComponent extends LitElement {
const [...versions] = this._versions;
const [...loadingVersions] = this._loadingVersions;
let version = {};
let commitLog = [];
let authors = {};
let commits = {};
let pulls = {};
if (this._selectedVersion !== "" && typeof this._versionData[this._selectedVersion] !== "undefined") {
const versionData = this._versionData[this._selectedVersion];
version = versionData.config;
commitLog = versionData.log;
authors = versionData.authors;
commits = versionData.commits;
pulls = versionData.pulls;
}
return html`
<page-content>
<shared-nav></shared-nav>
@@ -183,6 +201,20 @@ export default class EntryComponent extends LitElement {
.selectedRelease="${this._selectedRelease}"
@versionclick="${this._onVersionClicked}"
></gr-version-list>
${(this._selectedVersion !== "" ? html`
<gr-changes-list
.version=${version}
.log="${commitLog}"
.authors="${authors}"
.commits="${commits}"
.pulls="${pulls}"
.selectedVersion="${this._selectedVersion}"
.selectedRelease="${this._selectedRelease}"
?loading="${loadingVersions.indexOf(this._selectedVersion) >= 0}"
></gr-changes-list>
` : null)}
</div>
`)}
</page-content>