blob: 58d73f5938c8809c4c0dc7dda3fc2a27252d78a8 [file] [log] [blame]
/**
* @module modules/byblame-page-sk
* @description <h2><code>byblame-page-sk</code></h2>
*
* Displays the current untriaged digests, grouped by the commits that may have caused them
* (i.e. the blamelist or blame, for short).
*/
import { define } from 'elements-sk/define';
import { html } from 'lit-html';
import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
import { stateReflector } from 'common-sk/modules/stateReflector';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import '../byblameentry-sk';
import '../corpus-selector-sk';
import { sendBeginTask, sendEndTask, sendFetchError } from '../common';
import { defaultCorpus } from '../settings';
import { ByBlameEntry, ByBlameResponse, GUICorpusStatus, StatusResponse } from '../rpc_types';
const corpusRendererFn = (corpus: GUICorpusStatus): string => {
if (corpus.untriagedCount) {
return `${corpus.name} (${corpus.untriagedCount})`;
}
return corpus.name;
};
export class ByBlamePageSk extends ElementSk {
private static template = (ele: ByBlamePageSk) => html`
<div class=top-container>
<corpus-selector-sk
.corpora=${ele.corpora}
.selectedCorpus=${ele.corpora.find((c) => c.name === ele.corpus)}
.corpusRendererFn=${corpusRendererFn}
@corpus-selected=${ele.handleCorpusChange}>
</corpus-selector-sk>
</div>
<div class=entries>
${(!ele.entries || ele.entries.length === 0)
? ByBlamePageSk.noEntries(ele)
: ele.entries.map((entry) => ByBlamePageSk.entryTemplate(ele, entry))}
</div>
`;
private static entryTemplate = (ele: ByBlamePageSk, entry: ByBlameEntry) => html`
<byblameentry-sk
.byBlameEntry=${entry}
.corpus=${ele.corpus}>
</byblameentry-sk>
`;
private static noEntries = (ele: ByBlamePageSk) => {
if (!ele.loaded) {
return 'Loading untriaged digests...';
}
return `No untriaged digests for corpus ${ele.corpus}.`;
};
private corpora: GUICorpusStatus[] = [];
private corpus = '';
private entries: ByBlameEntry[] = [];
private loaded = false;
private readonly stateChanged: () => void;
private fetchController: AbortController | null = null;
constructor() {
super(ByBlamePageSk.template);
// stateReflector will trigger on DomReady.
this.stateChanged = stateReflector(
/* getState */ () => ({
// Provide empty values.
corpus: this.corpus,
}),
/* setState */ (newState) => {
// The stateReflector's lingering popstate event handler will continue
// to call this function on e.g. browser back button clicks long after
// this custom element is detached from the DOM.
if (!this._connected) {
return;
}
this.corpus = newState.corpus as string || defaultCorpus();
this._render(); // Update corpus selector immediately.
this.fetch();
},
);
}
connectedCallback() {
super.connectedCallback();
// Show loading indicator while we wait for results from the server.
this._render();
}
private handleCorpusChange(event: CustomEvent<GUICorpusStatus>) {
this.corpus = event.detail.name;
this.stateChanged();
this.fetch();
}
private fetch() {
// Force only one fetch at a time. Abort any outstanding requests.
if (this.fetchController) {
this.fetchController.abort();
}
this.fetchController = new AbortController();
const options = {
method: 'GET',
signal: this.fetchController.signal,
};
const query = encodeURIComponent(`source_type=${this.corpus}`);
const byBlameURL = `/json/v1/byblame?query=${query}`;
sendBeginTask(this);
fetch(byBlameURL, options)
.then(jsonOrThrow)
.then((res: ByBlameResponse) => {
this.entries = res.data || [];
this.loaded = true;
this._render();
sendEndTask(this);
})
.catch((e) => sendFetchError(this, e, 'byblamepage_entries'));
sendBeginTask(this);
fetch('/json/v1/trstatus', options)
.then(jsonOrThrow)
.then((res: StatusResponse) => {
this.corpora = res.corpStatus;
this._render();
sendEndTask(this);
})
.catch((e) => sendFetchError(this, e, 'byblamepage_trstatus'));
}
}
define('byblame-page-sk', ByBlamePageSk);