Mobile & caching fixes

This commit is contained in:
Mononaut 2022-04-29 08:41:41 -06:00
parent a8a945e77e
commit d386f01586
10 changed files with 160 additions and 85 deletions

View File

@ -31,7 +31,7 @@
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/global.css?v=2.3.0'>
<link rel='stylesheet' href='/build/bundle.css'>
<script src="/env.js"></script>
<script defer src="/build/bundle.js"></script>

View File

@ -6,7 +6,7 @@
import Icon from '../components/Icon.svelte'
import closeIcon from '../assets/icon/cil-x-circle.svg'
import { shortBtcFormat, longBtcFormat, dateFormat, numberFormat } from '../utils/format.js'
import { exchangeRates, settings, blocksEnabled, latestBlockHeight, blockTransitionDirection, loading } from '../stores.js'
import { exchangeRates, settings, blocksEnabled, latestBlockHeight, blockTransitionDirection, loading, freezeResize, pageWidth, pageHeight } from '../stores.js'
import { formatCurrency } from '../utils/fx.js'
import { searchBlockHeight } from '../utils/search.js'
@ -20,6 +20,18 @@
let restoring = false
let formattedBlockValue = ''
let compactView
let landscape
let tinyView
$: {
if ($pageWidth && $pageHeight && !$freezeResize) {
const aspectRatio = ($pageWidth / $pageHeight)
landscape = aspectRatio >= 1
compactView = (aspectRatio < 1) && ($pageHeight < 760)
tinyView = (aspectRatio < 1) && ($pageHeight <= 400)
}
}
$: {
if (block && block.value) {
const rate = $exchangeRates[$settings.currency]
@ -188,7 +200,7 @@
display: none;
}
@media (max-aspect-ratio: 1/1) and (max-height: 760px) {
&.compact {
.compact {
display: block;
}
@ -196,14 +208,6 @@
display: none;
}
}
@media (aspect-ratio: 1/1) and (max-height: 760px) {
.compact {
display: none;
}
.full-size {
display: block;
}
}
.data-row {
display: flex;
@ -278,65 +282,61 @@
}
}
@media (min-aspect-ratio: 1/1) {
.block-info {
bottom: unset;
left: unset;
top: 0;
right: 100%;
padding-right: .5rem;
.block-info.landscape {
bottom: unset;
left: unset;
top: 0;
right: 100%;
padding-right: .5rem;
min-width: 0;
transform: translateX(0);
min-width: 0;
transform: translateX(0);
.data-row {
.data-row {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
&.spacer {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
&.spacer {
display: flex;
}
}
.data-field {
white-space: wrap;
margin-left: 0;
margin-right: 5px;
&.title-field {
margin-bottom: .5em;
}
&.close-button {
display: none;
}
}
}
.standalone.close-button {
display: block;
position: absolute;
bottom: 100%;
left: 100%;
margin: 5px;
.data-field {
white-space: wrap;
margin-left: 0;
margin-right: 5px;
&.title-field {
margin-bottom: .5em;
}
&.close-button {
display: none;
}
}
}
@media (min-aspect-ratio: 1/1) and (max-height: 400px) {
.standalone.close-button {
top: 0;
bottom: unset;
margin-top: 0;
}
.standalone.landscape.close-button {
display: block;
position: absolute;
bottom: 100%;
left: 100%;
margin: 5px;
}
.standalone.tinyscreen.close-button {
top: 0;
bottom: unset;
margin-top: 0;
}
</style>
{#key transitionDirection}
{#each ((block != null && visible && $blocksEnabled) ? [block] : []) as block (block.id)}
<div class="block-info-container" out:fly|local={flyOut} in:fly|local={flyIn}>
<div class="block-info">
<div class="block-info" class:compact={compactView} class:landscape={landscape}>
<!-- <span class="data-field">Hash: { block.id }</span> -->
<div class="full-size">
<div class="data-row">
@ -363,7 +363,7 @@
</div>
<div class="compact">
<div class="data-row">
<span class="data-field title-field" title="{block.miner_sig}"><b>Latest Block: </b>{ numberFormat.format(block.height) }</span>
<span class="data-field title-field" title="{block.miner_sig}"><b>{#if block.height == $latestBlockHeight}Latest {/if}Block: </b>{ numberFormat.format(block.height) }</span>
<button class="data-field close-button" on:click={hideBlock}><Icon icon={closeIcon} color="var(--palette-x)" /></button>
</div>
<div class="data-row">
@ -371,7 +371,7 @@
<span class="data-field">{ formattedBlockValue }</span>
</div>
<div class="data-row">
<span class="data-field">{ formatCount(block.txnCount) } transactions</span>
<span class="data-field">{ formatCount(block.txnCount) } transaction{block.txnCount == 1 ? '' : 's'}</span>
{#if block.fees != null}
<span class="data-field">{ formatFee(block.avgFeerate) } sats/vb</span>
{:else}
@ -395,7 +395,7 @@
</svg>
</a>
{/if}
<button class="close-button standalone" on:click={hideBlock}>
<button class="close-button standalone" class:landscape={landscape} class:tinyscreen={tinyView} on:click={hideBlock}>
<Icon icon={closeIcon} color="var(--palette-x)" />
</button>
</div>

View File

@ -10,7 +10,7 @@ import TxIcon from '../assets/icon/cil-arrow-circle-right.svg'
import BlockIcon from '../assets/icon/grid-icon.svg'
import { fly } from 'svelte/transition'
import { matchQuery, searchTx, searchBlockHeight, searchBlockHash } from '../utils/search.js'
import { selectedTx, detailTx, overlay, loading } from '../stores.js'
import { selectedTx, detailTx, overlay, loading, freezeResize } from '../stores.js'
const queryIcons = {
txid: TxIcon,
@ -53,6 +53,8 @@ function handleSearchError (err) {
async function searchSubmit (e) {
e.preventDefault()
if (document.activeElement) document.activeElement.blur()
if (matchedQuery && matchedQuery.query !== 'address') {
loading.increment()
let searchErr
@ -87,6 +89,17 @@ async function searchSubmit (e) {
return false
}
let freezeTimeout
function focusIn(e) {
if (freezeTimeout) clearTimeout(freezeTimeout)
$freezeResize = true
}
async function focusOut(e) {
freezeTimeout = setTimeout(() => {
$freezeResize = false
}, 500)
}
</script>
<style type="text/scss">
@ -219,7 +232,7 @@ async function searchSubmit (e) {
<div class="input-wrapper" transition:fly={{ y: -25 }}>
<form class="search-form" action="" on:submit={searchSubmit}>
<input class="search-input" type="text" bind:value={query} placeholder="txid, block id or block height">
<input class="search-input" type="text" bind:value={query} placeholder="txid, block id or block height" on:focusin={focusIn} on:focusout={focusOut}>
<div class="clear-button" class:disabled={query == null || query === ''} on:click={clearInput} title="Clear">
<Icon icon={CrossIcon}/>
</div>

View File

@ -9,7 +9,7 @@ import CrossIcon from '../assets/icon/cil-x.svg'
import AddressIcon from '../assets/icon/cil-wallet.svg'
import TxIcon from '../assets/icon/cil-arrow-circle-right.svg'
import { matchQuery } from '../utils/search.js'
import { highlight, newHighlightQuery, highlightingFull } from '../stores.js'
import { highlight, newHighlightQuery, highlightingFull, freezeResize } from '../stores.js'
import { hlToHex, highlightA, highlightB, highlightC, highlightD, highlightE } from '../utils/color.js'
const highlightColors = [highlightA, highlightB, highlightC, highlightD, highlightE]
@ -145,9 +145,21 @@ async function remove (index) {
function searchSubmit (e) {
e.preventDefault()
if (document.activeElement) document.activeElement.blur()
add()
return false
}
let freezeTimeout
function focusIn(e) {
if (freezeTimeout) clearTimeout(freezeTimeout)
$freezeResize = true
}
async function focusOut(e) {
freezeTimeout = setTimeout(() => {
$freezeResize = false
}, 500)
}
</script>
<style type="text/scss">
@ -244,7 +256,7 @@ function searchSubmit (e) {
<div class="input-wrapper" class:full={$highlightingFull} style="--input-color: {queryColorHex};">
{#if !$highlightingFull }
<form class="search-form" action="" on:submit={searchSubmit}>
<input class="search-input" type="text" bind:value={query} placeholder="Enter an address or txid...">
<input class="search-input" type="text" bind:value={query} placeholder="Enter an address or txid..." on:focusin={focusIn} on:focusOut={focusOut}>
<button type="submit" class="search-submit" />
</form>
{:else}

View File

@ -20,7 +20,7 @@ import MempoolLegend from '../components/MempoolLegend.svelte'
import ContactTab from '../components/ContactTab.svelte'
import SearchTab from '../components/SearchTab.svelte'
import { sidebarToggle, overlay, currentBlock, blockVisible, haveSupporters } from '../stores.js'
import { sidebarToggle, overlay, currentBlock, blockVisible, haveSupporters, freezeResize } from '../stores.js'
let searchTabComponent
@ -58,12 +58,14 @@ function showBlock () {
align-items: flex-start;
@media (max-width: 480px) and (max-height: 480px) {
display: none;
&:not(.frozen) {
display: none;
}
}
}
</style>
<div class="sidebar">
<div class="sidebar" class:frozen={$freezeResize}>
<!-- displayed in reverse order, to preserve proper z-index layering -->
{#if blockHidden }
<SidebarTab on:click={() => showBlock()} tooltip="Show Latest Block">

View File

@ -1,8 +1,10 @@
<script>
import { tick } from 'svelte'
import Toggle from './util/Toggle.svelte'
import Pill from './util/Pill.svelte'
import Select from 'svelte-select'
import { createEventDispatcher } from 'svelte'
import { freezeResize } from '../stores.js'
const dispatch = createEventDispatcher()
export let value = false
@ -26,6 +28,18 @@ function filterSelectItems (label, filterText, option) {
function onSelect (e) {
selectedOption = e.detail
dispatch('input', selectedOption.value )
if (document.activeElement) document.activeElement.blur()
}
let freezeTimeout
function focusIn(e) {
if (freezeTimeout) clearTimeout(freezeTimeout)
$freezeResize = true
}
async function focusOut(e) {
freezeTimeout = setTimeout(() => {
$freezeResize = false
}, 500)
}
</script>
@ -73,7 +87,7 @@ function onSelect (e) {
<span class="label">{ label }</span>
<Pill active={value} left={falseLabel} right={trueLabel} />
{:else if type === 'dropdown'}
<div class="select">
<div class="select" on:focusin={focusIn} on:focusOut={focusOut}>
<Select items={ options } value={selectedOption} isSearchable={true} isClearable={false} itemFilter={filterSelectItems} placeholder={label} on:select={onSelect} showIndicator={true} />
</div>
{:else}

View File

@ -4,7 +4,7 @@
import fragShaderSrc from '../shaders/tx.frag'
import TxSprite from '../models/TxSprite.js'
import { color, hcl } from 'd3-color'
import { darkMode, frameRate, avgFrameRate, nativeAntialias, settings, devSettings } from '../stores.js'
import { darkMode, frameRate, avgFrameRate, nativeAntialias, settings, devSettings, freezeResize } from '../stores.js'
import config from '../config.js'
let canvas
@ -32,6 +32,11 @@
export let controller
export let running = false
let sizeFrozen
freezeResize.subscribe(val => {
sizeFrozen = val
})
// Shader attributes
// each attribute (except index) contains [x: startValue, y: endValue, z: startTime, w: rate]
// shader interpolates between start and end values at the given rate, from the given time
@ -72,9 +77,12 @@
resizeCanvas()
}
let resizeTimer
function resizeCanvas () {
if (resizeTimer) clearTimeout(resizeTimer)
resizeTimer = null
// var rect = canvas.parentNode.getBoundingClientRect()
if (canvas) {
if (canvas && !sizeFrozen) {
displayWidth = window.innerWidth
displayHeight = window.innerHeight
if (simulateAntialiasing) {
@ -86,7 +94,7 @@
}
if (gl) gl.viewport(0, 0, canvas.width, canvas.height)
} else {
setTimeout(resizeCanvas, 500)
resizeTimer = setTimeout(resizeCanvas, 500)
}
}

View File

@ -6,7 +6,7 @@
import { settings, overlay, serverConnected, serverDelay, txCount, mempoolCount,
mempoolScreenHeight, frameRate, avgFrameRate, blockVisible, tinyScreen,
compactScreen, currentBlock, latestBlockHeight, selectedTx, blockAreaSize,
devEvents, devSettings, pageWidth, pageHeight, loading } from '../stores.js'
devEvents, devSettings, pageWidth, pageHeight, loading, freezeResize } from '../stores.js'
import BlockInfo from '../components/BlockInfo.svelte'
import SearchBar from '../components/SearchBar.svelte'
import TxInfo from '../components/TxInfo.svelte'
@ -53,6 +53,19 @@
}
}
let canvasWidth = '100%'
let canvasHeight = '100%'
$: {
if ($freezeResize) {
canvasWidth = `${window.innerWidth}px`
canvasHeight = `${window.innerHeight}px`
} else {
canvasWidth = '100%'
canvasHeight = '100%'
resize()
}
}
onMount(() => {
txController = new TxController({ width, height })
@ -86,7 +99,7 @@
function resize () {
$pageWidth = window.innerWidth
$pageHeight = window.innerHeight
if (width !== window.innerWidth - 20 || height !== window.innerHeight - 20) {
if ((width !== window.innerWidth - 20 || height !== window.innerHeight - 20) && !$freezeResize) {
// don't force resize unless the viewport has actually changed
width = window.innerWidth - 20
height = window.innerHeight - 20
@ -483,7 +496,7 @@
<svelte:window on:resize={resize} on:load={resize} on:click={pointerLeave} />
<!-- <svelte:window on:resize={resize} on:click={pointerMove} /> -->
<div class="tx-area" class:light-mode={!$settings.darkMode}>
<div class="tx-area" class:light-mode={!$settings.darkMode} style="width: {canvasWidth}; height: {canvasHeight}">
<div class="canvas-wrapper" on:pointerleave={pointerLeave} on:pointermove={pointerMove} on:click={onClick}>
<TxRender controller={txController} />
@ -539,13 +552,15 @@
<SearchBar />
</div>
{/if}
<div class="alert-bar-wrapper">
{#if config.messagesEnabled && $settings.showMessages && !$tinyScreen }
<Alerts />
{:else}
<div class="spacer"></div>
{/if}
</div>
{#if !$tinyScreen}
<div class="alert-bar-wrapper">
{#if config.messagesEnabled && $settings.showMessages}
<Alerts />
{:else}
<div class="spacer"></div>
{/if}
</div>
{/if}
</div>
<Sidebar />

View File

@ -152,13 +152,24 @@ export const highlightingFull = writable(false)
export const pageWidth = writable(window.innerWidth)
export const pageHeight = writable(window.innerHeight)
export const freezeResize = writable(false)
export const tinyScreen = derived([pageWidth, pageHeight], ([$pageWidth, $pageHeight]) => {
const aspectRatio = $pageWidth / $pageHeight
return (aspectRatio >= 1 && pageWidth < 480) || (aspectRatio <= 1 && $pageHeight < 480)
let lastTinyScreen
export const tinyScreen = derived([pageWidth, pageHeight, freezeResize], ([$pageWidth, $pageHeight, $freezeResize]) => {
if ($freezeResize) return lastTinyScreen
else {
const aspectRatio = $pageWidth / $pageHeight
lastTinyScreen = (aspectRatio >= 1 && $pageWidth < 480) || (aspectRatio <= 1 && $pageHeight < 480)
return lastTinyScreen
}
})
export const compactScreen = derived([pageWidth, pageHeight], ([$pageWidth, $pageHeight]) => {
return ($pageWidth <= 640 && $pageHeight <= 550)
let lastCompactScreen
export const compactScreen = derived([pageWidth, pageHeight, freezeResize], ([$pageWidth, $pageHeight, $freezeResize]) => {
if ($freezeResize) return lastTinyScreen
else {
lastCompactScreen = ($pageWidth <= 640 && $pageHeight <= 550)
return lastCompactScreen
}
})
export const blocksEnabled = derived([settings], ([$settings]) => {

View File

@ -34,7 +34,7 @@ defmodule BitcoinStream.Router do
match "/api/block/:hash" do
case get_block(hash) do
{:ok, block, true} ->
put_resp_header(conn, "cache-control", "public, max-age=120, immutable")
put_resp_header(conn, "cache-control", "public, max-age=1200, immutable")
|> send_resp(200, block)
{:ok, block, false} ->