Defer output spend loading

This commit is contained in:
Mononaut 2022-04-28 19:45:07 -06:00
parent 3f748cd6c4
commit a8a945e77e
4 changed files with 65 additions and 12 deletions

View File

@ -8,7 +8,7 @@ import { formatCurrency } from '../utils/fx.js'
import { hlToHex, mixColor, teal, purple } from '../utils/color.js' import { hlToHex, mixColor, teal, purple } from '../utils/color.js'
import { SPKToAddress } from '../utils/encodings.js' import { SPKToAddress } from '../utils/encodings.js'
import api from '../utils/api.js' import api from '../utils/api.js'
import { searchTx, searchBlockHash, searchBlockHeight } from '../utils/search.js' import { searchTx, searchBlockHash, searchBlockHeight, fetchSpends } from '../utils/search.js'
import { fade } from 'svelte/transition' import { fade } from 'svelte/transition'
function onClose () { function onClose () {
@ -148,6 +148,23 @@ $: {
} }
} }
let spends
let loadingSpends = false
$: {
if ($detailTx && $detailTx.id) {
spends = null
loadSpends($detailTx.id)
} else {
spends = null
}
}
async function loadSpends(txid, reload = false) {
if (!reload) loadingSpends = true
spends = await fetchSpends(txid)
loadingSpends = false
}
function calcSankeyLines(inputs, outputs, fee, value, totalHeight, svgWidth, flowWeight) { function calcSankeyLines(inputs, outputs, fee, value, totalHeight, svgWidth, flowWeight) {
const total = fee + value const total = fee + value
const mergeOffset = (totalHeight - flowWeight) / 2 const mergeOffset = (totalHeight - flowWeight) / 2
@ -269,10 +286,10 @@ async function goToInput(e, input) {
loading.decrement() loading.decrement()
} }
async function goToOutput(e, output) { async function goToSpend(e, spend) {
e.preventDefault() e.preventDefault()
loading.increment() loading.increment()
await searchTx(output.spend.txid, output.spend.vin) await searchTx(spend.txid, spend.vin)
loading.decrement() loading.decrement()
} }
@ -475,7 +492,7 @@ async function goToBlock(e) {
stroke-linejoin: miter; stroke-linejoin: miter;
fill: white; fill: white;
fill-opacity: 0; fill-opacity: 0;
transition: fill-opacity 300ms; transition: fill-opacity 300ms, stroke-opacity 300ms;
} }
&.right { &.right {
@ -488,6 +505,16 @@ async function goToBlock(e) {
fill-opacity: 1; fill-opacity: 1;
} }
} }
&.loading {
.chevron .outline {
stroke-opacity: 0;
animation-name: svgpulse;
animation-delay: 500ms;
animation-duration: 2s;
animation-iteration-count: infinite;
}
}
} }
} }
@ -563,6 +590,20 @@ async function goToBlock(e) {
} }
} }
} }
@keyframes svgpulse {
0% {
fill-opacity: 0;
}
50% {
fill-opacity: 30%
}
100% {
fill-opacity: 0;
}
}
</style> </style>
<Overlay name="tx" on:close={onClose}> <Overlay name="tx" on:close={onClose}>
@ -701,8 +742,14 @@ async function goToBlock(e) {
<p class="header">{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee}+ fee{/if}</p> <p class="header">{$detailTx.outputs.length} output{$detailTx.outputs.length > 1 ? 's' : ''} {#if $detailTx.fee}+ fee{/if}</p>
{#each outputs as output} {#each outputs as output}
<div class="entry" class:clickable={output.rest} class:highlight={highlight.out != null && highlight.out === output.index} on:click={() => clickItem(output)}> <div class="entry" class:clickable={output.rest} class:highlight={highlight.out != null && highlight.out === output.index} on:click={() => clickItem(output)}>
{#if output.spend} {#if loadingSpends}
<a href="/tx/{output.spend.vin}:{output.spend.txid}" on:click={(e) => goToOutput(e, output)} class="put-link" transition:fade|local={{ duration: 200 }}> <span class="put-link loading" out:fade|local={{ duration: 500 }}>
<svg class="chevron right" height="1.2em" width="1.2em" viewBox="0 0 512 512">
<path d="M 107.628,257.54 327.095,38.078 404,114.989 261.506,257.483 404,399.978 327.086,476.89 Z" class="outline" />
</svg>
</span>
{:else if spends[output.index]}
<a href="/tx/{spends[output.index].vin}:{spends[output.index].txid}" on:click={(e) => goToSpend(e, spends[output.index])} class="put-link" in:fade|local={{ duration: 200 }}>
<svg class="chevron right" height="1.2em" width="1.2em" viewBox="0 0 512 512"> <svg class="chevron right" height="1.2em" width="1.2em" viewBox="0 0 512 512">
<path d="M 107.628,257.54 327.095,38.078 404,114.989 261.506,257.483 404,399.978 327.086,476.89 Z" class="outline" /> <path d="M 107.628,257.54 327.095,38.078 404,114.989 261.506,257.483 404,399.978 327.086,476.89 Z" class="outline" />
</svg> </svg>

View File

@ -422,8 +422,6 @@ export default class TxController {
await searchTx(selected.id) await searchTx(selected.id)
loading.decrement() loading.decrement()
} else { } else {
const spendResult = await fetchSpends(selected.id)
if (spendResult) selected = addSpends(selected, spendResult)
urlPath.set(`/tx/${selected.id}`) urlPath.set(`/tx/${selected.id}`)
detailTx.set(selected) detailTx.set(selected)
overlay.set('tx') overlay.set('tx')

View File

@ -175,7 +175,17 @@ async function fetchSpends (txid) {
}) })
if (!response) throw new Error('null response') if (!response) throw new Error('null response')
if (response.status == 200) { if (response.status == 200) {
return response.json() const result = await response.json()
return result.map(output => {
if (output) {
return {
txid: output[0],
vin: output[1],
}
} else {
return null
}
})
} else { } else {
return null return null
} }
@ -207,9 +217,7 @@ export async function searchTx (txid, input, output) {
} }
try { try {
let searchResult = await fetchTx(txid) let searchResult = await fetchTx(txid)
const spendResult = await fetchSpends(txid)
if (searchResult) { if (searchResult) {
if (spendResult) searchResult = addSpends(searchResult, spendResult)
selectedTx.set(searchResult) selectedTx.set(searchResult)
detailTx.set(searchResult) detailTx.set(searchResult)
overlay.set('tx') overlay.set('tx')

View File

@ -50,7 +50,7 @@ defmodule BitcoinStream.Router do
match "/api/tx/:hash" do match "/api/tx/:hash" do
case get_tx(hash) do case get_tx(hash) do
{:ok, tx} -> {:ok, tx} ->
put_resp_header(conn, "cache-control", "public, max-age=60, immutable") put_resp_header(conn, "cache-control", "public, max-age=300, immutable")
|> send_resp(200, tx) |> send_resp(200, tx)
_ -> _ ->
Logger.debug("Error getting tx hash"); Logger.debug("Error getting tx hash");