in src/components/Signatures/index.ts [29:130]
export default function Signatures(props: {
pings: Ping[],
selectedSignature?: (info: SignatureInfo | undefined) => void,
}) {
const selectSignature = (sig: SignatureInfo | undefined) => {
SignatureInfo.setSelected(sig);
settings.signature = sig?.signature;
if (props.selectedSignature) {
props.selectedSignature(sig);
}
}
const processed = createMemo(() => {
const pingData = allPings();
const allSignatures = pingData.signature;
const allClients = pingData.clientid;
const countClients = (pings: Ping[]): number => new Set(pings.map(p => allClients.values[p])).size;
const bySignature = props.pings.reduce((map, ping) => {
const signature = allSignatures.values[ping];
if (signature !== 0) {
if (!map.has(signature)) {
map.set(signature, new SignatureInfo(allSignatures.strings[signature]));
}
map.get(signature)!.pings.push(ping);
}
return map;
}, new Map<StringIndex, SignatureInfo>());
const signatures = bySignature.values().toArray();
for (const sig of signatures) {
sig.clientCount = countClients(sig.pings);
}
const [totalPings, totalClients] = signatures.reduce(
([p, c], data) => [p + data.pingCount, c + data.clientCount],
[0, 0]
);
// Load settings
{
const signature = untrack(() => settings.signature);
if (signature) {
const sig = signatures.find(sig => sig.signature == signature);
if (sig) {
selectSignature(sig);
}
}
}
return { signatures, totalPings, totalClients };
});
// Clear the selected signature whenever the pings change.
let firstRun = true;
createEffect(on(() => props.pings, () => {
if (firstRun) {
firstRun = false;
return;
}
selectSignature(undefined);
}));
const sortedSignatures = createMemo(() => {
const { signatures, totalPings, totalClients } = processed();
const sortVal = settings.sort == "clients" ? (s: SignatureInfo) => s.clientCount : (s: SignatureInfo) => s.pingCount;
const percTotal = settings.sort == "clients" ? totalClients : totalPings;
for (const sig of signatures) {
sig.percentage = sortVal(sig) * 100 / percTotal;
}
return signatures.sort((a, b) => b.percentage - a.percentage);
},
undefined,
// We always return the same value (the signatures array), but modify
// it internally as an optimization, so we set `equals: false` to
// always signal a change.
{ equals: false }
);
const header = () => {
const { signatures, totalPings, totalClients } = processed();
return html`<h2>
${signatures.length} signatures, ${totalClients} clients, ${totalPings} crash pings
</h2>`;
};
const selectOn = (which: SortBy) => () => {
return { "selected": settings.sort == which };
};
const renderSignature = (sig: SignatureInfo, idx: number) => {
const url = `https://crash-stats.mozilla.org/search/?signature=~${encodeURIComponent(sig.signature)}`;
return html`
<div role="row" onClick=${(_: Event) => selectSignature(sig)} class="listrow" classList=${() => sig.selectedClassList}>
<div role="cell" class="rank">${idx + 1}</div>
<div role="cell" class="percent">${sig.percentage.toFixed(2)}%</div>
<div role="cell" class="signature"><tt>${sig.signature}</tt></div>
<div role="cell" class="copy"><span role="button" tabindex="0" title="Copy signature to clipboard" onClick=${(_: Event) => copyText(sig.signature)}><span aria-hidden="true" class="icon fas fa-copy copyicon"></span></span></div>