Adding Related changes to new change summary
This is rewriting part of gr-related-changes-list to lit-element
with new change summary features like collapsable sections.
Change-Id: I4faf365f9949fa2e8248856e46fcf5f6b9298e0d
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index 3c37ec7..4798430 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -548,6 +548,7 @@
<gr-related-changes-list-experimental
change="[[_change]]"
id="relatedChangesExperimental"
+ patch-num="[[_computeLatestPatchNum(_allPatchSets)]]"
></gr-related-changes-list-experimental>
</template>
<template is="dom-if" if="[[!_isNewChangeSummaryUiEnabled]]">
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
index 5134b72..77bdf48 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
@@ -23,12 +23,16 @@
SubmittedTogetherInfo,
ChangeInfo,
RelatedChangeAndCommitInfo,
+ RelatedChangesInfo,
+ PatchSetNum,
+ CommitId,
} from '../../../types/common';
import {appContext} from '../../../services/app-context';
import {ParsedChangeInfo} from '../../../types/types';
import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {pluralize} from '../../../utils/string-util';
import {ChangeStatus} from '../../../constants/constants';
+import {getRevisionKey} from '../../../utils/change-util';
function isChangeInfo(
x: ChangeInfo | RelatedChangeAndCommitInfo | ParsedChangeInfo
@@ -44,12 +48,18 @@
@property()
change?: ParsedChangeInfo;
+ @property({type: String})
+ patchNum?: PatchSetNum;
+
@property()
_submittedTogether?: SubmittedTogetherInfo = {
changes: [],
non_visible_changes: 0,
};
+ @property()
+ _relatedResponse?: RelatedChangesInfo = {changes: []};
+
private readonly restApiService = appContext.restApiService;
static get styles() {
@@ -81,16 +91,57 @@
}
render() {
+ const relatedChanges = this._relatedResponse?.changes ?? [];
+ let showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
+ relatedChanges.length,
+ relatedChanges.findIndex(relatedChange =>
+ this._changesEqual(relatedChange, this.change)
+ )
+ );
+ const connectedRevisions = this._computeConnectedRevisions(
+ this.change,
+ this.patchNum,
+ relatedChanges
+ );
+ const relatedChangeSection = html` <section
+ class="relatedChanges"
+ ?hidden=${!relatedChanges.length}
+ >
+ <h4 class="title">Relation chain</h4>
+ <gr-related-collapse .length=${relatedChanges.length}>
+ ${relatedChanges.map(
+ (change, index) =>
+ html`<gr-related-change
+ class="${classMap({
+ ['show-when-collapsed']: showWhenCollapsedPredicate(index),
+ })}"
+ .isCurrentChange="${this._changesEqual(change, this.change)}"
+ .change="${change}"
+ .connectedRevisions="${connectedRevisions}"
+ .href="${change?._change_number
+ ? GerritNav.getUrlForChangeById(
+ change._change_number,
+ change.project,
+ change._revision_number as PatchSetNum
+ )
+ : ''}"
+ .showChangeStatus=${true}
+ >${change.commit.subject}</gr-related-change
+ >`
+ )}
+ </gr-related-collapse>
+ </section>`;
+
const submittedTogetherChanges = this._submittedTogether?.changes ?? [];
const countNonVisibleChanges =
this._submittedTogether?.non_visible_changes ?? 0;
- const showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
+ showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
submittedTogetherChanges.length,
submittedTogetherChanges.findIndex(relatedChange =>
this._changesEqual(relatedChange, this.change)
)
);
- return html` <section
+ const submittedTogetherSection = html`<section
id="submittedTogether"
?hidden=${!submittedTogetherChanges?.length &&
!this._submittedTogether?.non_visible_changes}
@@ -98,20 +149,29 @@
<h4 class="title">Submitted together</h4>
<gr-related-collapse .length=${submittedTogetherChanges.length}>
${submittedTogetherChanges.map(
- (relatedChange, index) =>
+ (change, index) =>
html`<gr-related-change
class="${classMap({
['show-when-collapsed']: showWhenCollapsedPredicate(index),
})}"
- .currentChange="${this._changesEqual(relatedChange, this.change)}"
- .change="${relatedChange}"
- ></gr-related-change>`
+ .currentChange="${this._changesEqual(change, this.change)}"
+ .change="${change}"
+ .href="${GerritNav.getUrlForChangeById(
+ change._number,
+ change.project
+ )}"
+ .showSubmittableCheck=${true}
+ >${change.project}: ${change.branch}:
+ ${change.subject}</gr-related-change
+ >`
)}
</gr-related-collapse>
<div class="note" ?hidden=${!countNonVisibleChanges}>
(+ ${pluralize(countNonVisibleChanges, 'non-visible change')})
</div>
</section>`;
+
+ return html`${relatedChangeSection}${submittedTogetherSection}`;
}
showWhenCollapsedPredicateFactory(length: number, highlightIndex: number) {
@@ -128,11 +188,24 @@
reload() {
if (!this.change) return Promise.reject(new Error('change missing'));
- return this.restApiService
- .getChangesSubmittedTogether(this.change._number)
- .then(response => {
- this._submittedTogether = response;
- });
+ if (!this.patchNum) return Promise.reject(new Error('patchNum missing'));
+ const promises: Array<Promise<void>> = [
+ this.restApiService
+ .getRelatedChanges(this.change._number, this.patchNum)
+ .then(response => {
+ if (!response) {
+ throw new Error('getRelatedChanges returned undefined response');
+ }
+ this._relatedResponse = response;
+ }),
+ this.restApiService
+ .getChangesSubmittedTogether(this.change._number)
+ .then(response => {
+ this._submittedTogether = response;
+ }),
+ ];
+
+ return Promise.all(promises);
}
/**
@@ -165,6 +238,46 @@
}
return change._change_number;
}
+
+ /*
+ * A list of commit ids connected to change to understand if other change
+ * is direct or indirect ancestor / descendant.
+ */
+ _computeConnectedRevisions(
+ change?: ParsedChangeInfo,
+ patchNum?: PatchSetNum,
+ relatedChanges?: RelatedChangeAndCommitInfo[]
+ ) {
+ if (!patchNum || !relatedChanges || !change) {
+ return [];
+ }
+
+ const connected: CommitId[] = [];
+ const changeRevision = getRevisionKey(change, patchNum);
+ const commits = relatedChanges.map(c => c.commit);
+ let pos = commits.length - 1;
+
+ while (pos >= 0) {
+ const commit: CommitId = commits[pos].commit;
+ connected.push(commit);
+ // TODO(TS): Ensure that both (commit and changeRevision) are string and use === instead
+ // eslint-disable-next-line eqeqeq
+ if (commit == changeRevision) {
+ break;
+ }
+ pos--;
+ }
+ while (pos >= 0) {
+ for (let i = 0; i < commits[pos].parents.length; i++) {
+ if (connected.includes(commits[pos].parents[i].commit)) {
+ connected.push(commits[pos].commit);
+ break;
+ }
+ }
+ --pos;
+ }
+ return connected;
+ }
}
@customElement('gr-related-collapse')
@@ -232,10 +345,26 @@
@customElement('gr-related-change')
export class GrRelatedChange extends GrLitElement {
@property()
- change?: ChangeInfo;
+ change?: ChangeInfo | RelatedChangeAndCommitInfo;
@property()
- currentChange = false;
+ href?: string;
+
+ @property()
+ isCurrentChange = false;
+
+ @property()
+ showSubmittableCheck = false;
+
+ @property()
+ showChangeStatus = false;
+
+ /*
+ * Needed for calculation if change is direct or indirect ancestor/descendant
+ * to current change.
+ */
+ @property()
+ connectedRevisions?: CommitId[];
static get styles() {
return [
@@ -258,6 +387,29 @@
color: var(--deemphasized-text-color);
text-decoration: line-through;
}
+ .status {
+ color: var(--deemphasized-text-color);
+ font-weight: var(--font-weight-bold);
+ margin-left: var(--spacing-xs);
+ }
+ .notCurrent {
+ color: #e65100;
+ }
+ .indirectAncestor {
+ color: #33691e;
+ }
+ .submittableCheck {
+ padding-left: var(--spacing-s);
+ color: var(--positive-green-text-color);
+ display: none;
+ }
+ .submittableCheck.submittable {
+ display: inline;
+ }
+ .hidden,
+ .mobile {
+ display: none;
+ }
.submittableCheck {
padding-left: var(--spacing-s);
color: var(--positive-green-text-color);
@@ -281,30 +433,30 @@
role="img"
class="arrowToCurrentChange"
aria-label="Arrow marking current change"
- ?hidden=${!this.currentChange}
+ ?hidden=${!this.isCurrentChange}
>➔</span
>
<div class="changeContainer">
- <a
- href="${GerritNav.getUrlForChangeById(
- change._number,
- change.project
- )}"
- class="${linkClass}"
- >${change.project}: ${change.branch}: ${change.subject}</a
- >
- <span
- tabindex="-1"
- title="Submittable"
- class="submittableCheck ${linkClass}"
- role="img"
- aria-label="Submittable"
- >✓</span
- >
+ <a href="${this.href}" class="${linkClass}"><slot></slot></a>
+ ${this.showSubmittableCheck
+ ? html`<span
+ tabindex="-1"
+ title="Submittable"
+ class="submittableCheck ${linkClass}"
+ role="img"
+ aria-label="Submittable"
+ >✓</span
+ >`
+ : ''}
+ ${this.showChangeStatus && !isChangeInfo(change)
+ ? html`<span class="${this._computeChangeStatusClass(change)}">
+ (${this._computeChangeStatus(change)})
+ </span>`
+ : ''}
</div> `;
}
- _computeLinkClass(change: ChangeInfo) {
+ _computeLinkClass(change: ChangeInfo | RelatedChangeAndCommitInfo) {
const statuses = [];
if (change.status === ChangeStatus.ABANDONED) {
statuses.push('strikethrough');
@@ -314,6 +466,44 @@
}
return statuses.join(' ');
}
+
+ _computeChangeStatusClass(change: RelatedChangeAndCommitInfo) {
+ const classes = ['status'];
+ if (change._revision_number !== change._current_revision_number) {
+ classes.push('notCurrent');
+ } else if (this._isIndirectAncestor(change)) {
+ classes.push('indirectAncestor');
+ } else if (change.submittable) {
+ classes.push('submittable');
+ } else if (change.status === ChangeStatus.NEW) {
+ classes.push('hidden');
+ }
+ return classes.join(' ');
+ }
+
+ _computeChangeStatus(change: RelatedChangeAndCommitInfo) {
+ switch (change.status) {
+ case ChangeStatus.MERGED:
+ return 'Merged';
+ case ChangeStatus.ABANDONED:
+ return 'Abandoned';
+ }
+ if (change._revision_number !== change._current_revision_number) {
+ return 'Not current';
+ } else if (this._isIndirectAncestor(change)) {
+ return 'Indirect ancestor';
+ } else if (change.submittable) {
+ return 'Submittable';
+ }
+ return '';
+ }
+
+ _isIndirectAncestor(change: RelatedChangeAndCommitInfo) {
+ return (
+ this.connectedRevisions &&
+ !this.connectedRevisions.includes(change.commit.commit)
+ );
+ }
}
declare global {