blob: eae0621ace7132c27edccee75320a726a3263546 [file] [log] [blame]
/**
* @module modules/byblame-page-sk
* @description <h2><code>byblame-page-sk</code></h2>
*
* Displays the current untriaged digests, grouped by blame.
*
* @attr default-corpus {string} Name of the corpus to use when not specified
* in the URL.
* @attr base-repo-url {string} Base repository URL.
*/
import { define } from 'elements-sk/define'
import { ElementSk } from '../../../infra-sk/modules/ElementSk'
import { html } from 'lit-html'
import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
import { stateReflector } from 'common-sk/modules/stateReflector';
import '../byblameentry-sk';
import '../corpus-selector-sk';
const template = (el) => html`
<corpus-selector-sk
.selectedCorpus=${el._corpus}
@corpus-selected=${(e) => el._handleCorpusChange(e)}>
</corpus-selector-sk>
<div class=entries>
${(!el._entries || el._entries.length === 0)
? (el._loaded ? 'No untriaged digests.' : 'Loading untriaged digests...')
: el._entries.map((entry) => entryTemplate(el, entry))}
</div>
`;
const entryTemplate = (el, entry) => html`
<byblameentry-sk
.byBlameEntry=${entry}
.gitLog=${el._gitLogByGroupID.get(entry.groupID)}
.corpus=${el._corpus}
.baseRepoUrl=${el._baseRepoUrl}>
</byblameentry-sk>
`;
define('byblame-page-sk', class extends ElementSk {
constructor() {
super(template);
this._corpus = '';
// Will hold ByBlameEntry objects returned by /json/byblame for the selected
// corpus.
this._entries = [];
// Maps ByBlameEntry.groupID to the corresponding gitLog object returned by
// /json/gitlog.
this._gitLogByGroupID = new Map();
this._loaded = false; // False if entries haven't been fetched yet.
// stateReflector will trigger on DomReady.
this._stateChanged = stateReflector(
/*getState*/() => {
return {
// Provide empty values.
'corpus': this._corpus,
}
}, /*setState*/(newState) => {
this._corpus = newState.corpus || this._defaultCorpus;
// Push default state to URL if absent.
if (!newState.corpus) {
this._stateChanged();
}
this._render(); // Update corpus-selector-sk if this._corpus changed.
this._fetchEntries();
});
}
connectedCallback() {
super.connectedCallback();
// Show loading indicator while we wait for results from the server.
this._render();
}
get _defaultCorpus() {
return this.getAttribute('default-corpus');
}
get _baseRepoUrl() {
return this.getAttribute('base-repo-url');
}
_handleCorpusChange(event) {
this._corpus = event.detail.corpus;
this._stateChanged();
this._fetchEntries();
}
_fetchEntries() {
// Fetching is done in two steps:
// 1. ByBlameEntry objects are fetched from /json/byblame.
// 2. A gitLog object is retrieved from /json/gitlog for each ByBlameEntry.
const query = encodeURIComponent(`source_type=${this._corpus}`);
const url = `/json/byblame?query=${query}`;
// Force only one fetch at a time. Abort any outstanding requests.
if (this._fetchController) {
this._fetchController.abort();
}
this._fetchController = new AbortController();
// The /json/byblame and /json/gitlog fetches share the same controller.
const options = {
method: 'GET',
signal: this._fetchController.signal
};
this._sendBusy();
// Step 1: Fetch ByBlameEntry objects from /json/byblame.
fetch(url, options)
.then(jsonOrThrow)
.then((json) => {
this._entries = json.data || [];
// TODO(lovisolo): Consider modifying /json/byblame to include
// commit messages in its response so we don't have to
// query the /json/gitlog endpoint.
const gitLogUrl = (entry) => {
const startHash = entry.commits[entry.commits.length - 1].hash;
const endHash = entry.commits[0].hash;
return `/json/gitlog?start=${startHash}&end=${endHash}`;
};
// Step 2: Fetch gitLog objects from /json/gitlog.
return Promise.all(
this._entries.map(
(entry) =>
fetch(gitLogUrl(entry), options)
.then(jsonOrThrow)
.then(
(gitLog) =>
this._gitLogByGroupID.set(
entry.groupID,
gitLog))));
})
.then(() => {
this._loaded = true;
this._render();
this._sendDone();
})
.catch((e) => this._sendError(e));
}
_sendBusy() {
this.dispatchEvent(new CustomEvent('begin-task', {bubbles: true}));
}
_sendDone() {
this.dispatchEvent(new CustomEvent('end-task', {bubbles: true}));
}
_sendError(e) {
this.dispatchEvent(new CustomEvent('fetch-error', {
detail: {
error: e,
loading: 'by blame page',
}, bubbles: true
}));
}
});