mirror of
https://github.com/Retropex/bitfeed.git
synced 2025-05-28 04:52:29 +02:00
parent
9f9ad26427
commit
89a7ef5af6
@ -12,7 +12,7 @@ This repo hosts the code behind [Bitfeed](https://bits.monospace.live), a live v
|
|||||||
- [Umbrel](https://getumbrel.com) (coming soon)
|
- [Umbrel](https://getumbrel.com) (coming soon)
|
||||||
- [Citadel](https://runcitadel.space/)
|
- [Citadel](https://runcitadel.space/)
|
||||||
- [Docker](https://github.com/bitfeed-project/bitfeed/blob/master/DOCKER.md)
|
- [Docker](https://github.com/bitfeed-project/bitfeed/blob/master/DOCKER.md)
|
||||||
- [Build from source](https://github.com/bitfeed-project/bitfeed/blob/master/README.md#installing-and-building-bitfeed)
|
- [Build from source](#installing-and-building-bitfeed)
|
||||||
|
|
||||||
## Installing and Building Bitfeed
|
## Installing and Building Bitfeed
|
||||||
|
|
||||||
|
17
client/package-lock.json
generated
17
client/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bitfeed-client",
|
"name": "bitfeed-client",
|
||||||
"version": "2.1.0",
|
"version": "2.1.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bitfeed-client",
|
"name": "bitfeed-client",
|
||||||
"version": "2.1.0",
|
"version": "2.1.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.16.5",
|
"@babel/core": "^7.16.5",
|
||||||
"@babel/preset-env": "^7.16.5",
|
"@babel/preset-env": "^7.16.5",
|
||||||
@ -31,7 +31,8 @@
|
|||||||
"sass": "^1.49.0",
|
"sass": "^1.49.0",
|
||||||
"sirv-cli": "^1.0.14",
|
"sirv-cli": "^1.0.14",
|
||||||
"svelte": "^3.44.3",
|
"svelte": "^3.44.3",
|
||||||
"svelte-preprocess": "^4.10.1"
|
"svelte-preprocess": "^4.10.1",
|
||||||
|
"svelte-select": "^4.4.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@ -4063,6 +4064,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte-select": {
|
||||||
|
"version": "4.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-4.4.7.tgz",
|
||||||
|
"integrity": "sha512-fIf9Z8rPI6F8naHZ9wjXT0Pv5gLyhdHAFkHFJnCfVVfELE8e82uOoF0xEVQP6Kir+b4Q5yOvNAzZ61WbSU6A0A=="
|
||||||
|
},
|
||||||
"node_modules/through2": {
|
"node_modules/through2": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||||
@ -7205,6 +7211,11 @@
|
|||||||
"strip-indent": "^3.0.0"
|
"strip-indent": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"svelte-select": {
|
||||||
|
"version": "4.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-4.4.7.tgz",
|
||||||
|
"integrity": "sha512-fIf9Z8rPI6F8naHZ9wjXT0Pv5gLyhdHAFkHFJnCfVVfELE8e82uOoF0xEVQP6Kir+b4Q5yOvNAzZ61WbSU6A0A=="
|
||||||
|
},
|
||||||
"through2": {
|
"through2": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bitfeed-client",
|
"name": "bitfeed-client",
|
||||||
"version": "2.1.2",
|
"version": "2.1.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"dev": "rollup -c -w",
|
"dev": "rollup -c -w",
|
||||||
@ -30,6 +30,7 @@
|
|||||||
"sass": "^1.49.0",
|
"sass": "^1.49.0",
|
||||||
"sirv-cli": "^1.0.14",
|
"sirv-cli": "^1.0.14",
|
||||||
"svelte": "^3.44.3",
|
"svelte": "^3.44.3",
|
||||||
"svelte-preprocess": "^4.10.1"
|
"svelte-preprocess": "^4.10.1",
|
||||||
|
"svelte-select": "^4.4.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import Icon from '../components/Icon.svelte'
|
import Icon from '../components/Icon.svelte'
|
||||||
import closeIcon from '../assets/icon/cil-x-circle.svg'
|
import closeIcon from '../assets/icon/cil-x-circle.svg'
|
||||||
import { shortBtcFormat, longBtcFormat, timeFormat, integerFormat } from '../utils/format.js'
|
import { shortBtcFormat, longBtcFormat, timeFormat, integerFormat } from '../utils/format.js'
|
||||||
import { exchangeRates, localCurrency } from '../stores.js'
|
import { exchangeRates, settings } from '../stores.js'
|
||||||
import { formatCurrency } from '../utils/fx.js'
|
import { formatCurrency } from '../utils/fx.js'
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
@ -21,10 +21,10 @@
|
|||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (block && block.value) {
|
if (block && block.value) {
|
||||||
const rate = $exchangeRates[$localCurrency]
|
const rate = $exchangeRates[$settings.currency]
|
||||||
let local
|
let local
|
||||||
if (rate && rate.last) {
|
if (rate && rate.last) {
|
||||||
local = formatCurrency($localCurrency, (block.value/100000000) * rate.last, { compact: true })
|
local = formatCurrency($settings.currency, (block.value/100000000) * rate.last, { compact: true })
|
||||||
} else {
|
} else {
|
||||||
local = null
|
local = null
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { onMount } from 'svelte'
|
|||||||
import Overlay from '../components/Overlay.svelte'
|
import Overlay from '../components/Overlay.svelte'
|
||||||
import TierCard from './sponsor/TierCard.svelte'
|
import TierCard from './sponsor/TierCard.svelte'
|
||||||
import SatoshiSlider from './sponsor/SatoshiSlider.svelte'
|
import SatoshiSlider from './sponsor/SatoshiSlider.svelte'
|
||||||
import Pill from '../components/Pill.svelte'
|
import Pill from './util/Pill.svelte'
|
||||||
import Icon from '../components/Icon.svelte'
|
import Icon from '../components/Icon.svelte'
|
||||||
import boltIcon from '../assets/icon/cil-bolt-filled.svg'
|
import boltIcon from '../assets/icon/cil-bolt-filled.svg'
|
||||||
import chainIcon from '../assets/icon/cil-link.svg'
|
import chainIcon from '../assets/icon/cil-link.svg'
|
||||||
|
@ -2,25 +2,49 @@
|
|||||||
import config from '../config.js'
|
import config from '../config.js'
|
||||||
import analytics from '../utils/analytics.js'
|
import analytics from '../utils/analytics.js'
|
||||||
import SidebarMenuItem from '../components/SidebarMenuItem.svelte'
|
import SidebarMenuItem from '../components/SidebarMenuItem.svelte'
|
||||||
import { settings, nativeAntialias, exchangeRates, localCurrency, haveMessages } from '../stores.js'
|
import { settings, nativeAntialias, exchangeRates, haveMessages } from '../stores.js'
|
||||||
|
import { currencies } from '../utils/fx.js'
|
||||||
|
|
||||||
function toggle(setting) {
|
function toggle(setting) {
|
||||||
$settings[setting] = !$settings[setting]
|
if (settingConfig[setting] != null && settingConfig[setting].valueType === 'bool') {
|
||||||
analytics.trackEvent('settings', setting, $settings[setting] ? 'on' : 'off')
|
onChange(setting, !$settings[setting])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChange(setting, value) {
|
||||||
|
$settings[setting] = value
|
||||||
|
analytics.trackEvent('settings', setting, $settings[setting])
|
||||||
|
}
|
||||||
|
|
||||||
|
const currencyOptions = Object.keys(currencies).map(code => {
|
||||||
|
return {
|
||||||
|
value: code,
|
||||||
|
label: `${currencies[code].char} ${currencies[code].name}`,
|
||||||
|
tags: [code, currencies[code].name, ...currencies[code].countries]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
let settingConfig = {
|
let settingConfig = {
|
||||||
showNetworkStatus: {
|
showNetworkStatus: {
|
||||||
label: 'Network Status'
|
label: 'Network Status',
|
||||||
|
valueType: 'bool'
|
||||||
},
|
},
|
||||||
darkMode: {
|
darkMode: {
|
||||||
label: 'Dark Mode'
|
label: 'Dark Mode',
|
||||||
|
valueType: 'bool'
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
label: 'Fiat Currency',
|
||||||
|
type: 'dropdown',
|
||||||
|
valueType: 'string',
|
||||||
|
options: currencyOptions
|
||||||
},
|
},
|
||||||
vbytes: {
|
vbytes: {
|
||||||
label: 'Size by',
|
label: 'Size by',
|
||||||
type: 'pill',
|
type: 'pill',
|
||||||
falseLabel: 'value',
|
falseLabel: 'value',
|
||||||
trueLabel: 'vbytes'
|
trueLabel: 'vbytes',
|
||||||
|
valueType: 'bool'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
$: {
|
$: {
|
||||||
@ -28,21 +52,24 @@ $: {
|
|||||||
settingConfig.fancyGraphics = false
|
settingConfig.fancyGraphics = false
|
||||||
} else {
|
} else {
|
||||||
settingConfig.fancyGraphics = {
|
settingConfig.fancyGraphics = {
|
||||||
label: 'Fancy Graphics'
|
label: 'Fancy Graphics',
|
||||||
|
valueType: 'bool'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config.messagesEnabled && $haveMessages) {
|
if (config.messagesEnabled && $haveMessages) {
|
||||||
settingConfig.showMessages = {
|
settingConfig.showMessages = {
|
||||||
label: 'Message Bar'
|
label: 'Message Bar',
|
||||||
|
valueType: 'bool'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
const rate = $exchangeRates[$localCurrency]
|
const rate = $exchangeRates[$settings.currency]
|
||||||
if (rate && rate.last) {
|
if (rate && rate.last) {
|
||||||
settingConfig.showFX = {
|
settingConfig.showFX = {
|
||||||
label: '₿ Price'
|
label: '₿ Price',
|
||||||
|
valueType: 'bool'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
settingConfig.showFX = false
|
settingConfig.showFX = false
|
||||||
@ -58,6 +85,6 @@ function getSettings(setting) {
|
|||||||
|
|
||||||
{#each Object.keys($settings) as setting (setting) }
|
{#each Object.keys($settings) as setting (setting) }
|
||||||
{#if settingConfig[setting]}
|
{#if settingConfig[setting]}
|
||||||
<SidebarMenuItem {...getSettings(setting)} active={$settings[setting]} on:click={() => { toggle(setting) }} />
|
<SidebarMenuItem {...getSettings(setting)} value={$settings[setting]} on:click={() => { toggle(setting) }} on:input={(e) => { onChange(setting, e.detail)}} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -49,54 +49,22 @@ function showBlock () {
|
|||||||
top: 20%;
|
top: 20%;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column-reverse;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<SidebarTab open={$sidebarToggle === 'settings'} on:click={() => {settings('settings')}} tooltip="Settings">
|
<!-- displayed in reverse order, to preserve proper z-index layering -->
|
||||||
<span slot="tab" title="Settings">
|
{#if blockHidden }
|
||||||
<Icon icon={cogIcon} color="var(--bold-a)" />
|
<SidebarTab on:click={() => showBlock()} tooltip="Show Latest Block">
|
||||||
</span>
|
|
||||||
<div slot="content">
|
|
||||||
<Settings />
|
|
||||||
</div>
|
|
||||||
</SidebarTab>
|
|
||||||
<SidebarTab open={$sidebarToggle === 'legend'} on:click={() => {settings('legend')}} tooltip="Key">
|
|
||||||
<span slot="tab">
|
|
||||||
<Icon icon={infoIcon} color="var(--bold-a)" />
|
|
||||||
</span>
|
|
||||||
<div slot="content">
|
|
||||||
<MempoolLegend />
|
|
||||||
</div>
|
|
||||||
</SidebarTab>
|
|
||||||
<SidebarTab open={$sidebarToggle === 'contact'} on:click={() => {settings('contact')}} tooltip="Contact">
|
|
||||||
<span slot="tab">
|
|
||||||
<Icon icon={atIcon} color="var(--bold-a)" />
|
|
||||||
</span>
|
|
||||||
<div slot="content">
|
|
||||||
<ContactTab />
|
|
||||||
</div>
|
|
||||||
</SidebarTab>
|
|
||||||
<SidebarTab on:click={() => openOverlay('about')} tooltip="About">
|
|
||||||
<span slot="tab">
|
|
||||||
<Icon icon={questionIcon} color="var(--bold-a)" />
|
|
||||||
</span>
|
|
||||||
</SidebarTab>
|
|
||||||
{#if $haveSupporters }
|
|
||||||
<SidebarTab on:click={() => openOverlay('supporters')} tooltip="Supporters">
|
|
||||||
<span slot="tab">
|
<span slot="tab">
|
||||||
<Icon icon={peopleIcon} color="var(--bold-a)" />
|
<Icon icon={gridIcon} color="var(--bold-a)" />
|
||||||
</span>
|
|
||||||
</SidebarTab>
|
|
||||||
{/if}
|
|
||||||
{#if config.donationsEnabled }
|
|
||||||
<SidebarTab on:click={() => openOverlay('donation')} tooltip="Donate">
|
|
||||||
<span slot="tab">
|
|
||||||
<Icon icon={giftIcon} color="var(--bold-a)" />
|
|
||||||
</span>
|
</span>
|
||||||
|
<div slot="content">
|
||||||
|
<MempoolLegend />
|
||||||
|
</div>
|
||||||
</SidebarTab>
|
</SidebarTab>
|
||||||
{/if}
|
{/if}
|
||||||
{#if config.dev && config.debug}
|
{#if config.dev && config.debug}
|
||||||
@ -109,14 +77,47 @@ function showBlock () {
|
|||||||
</div>
|
</div>
|
||||||
</SidebarTab>
|
</SidebarTab>
|
||||||
{/if}
|
{/if}
|
||||||
{#if blockHidden }
|
{#if config.donationsEnabled }
|
||||||
<SidebarTab on:click={() => showBlock()} tooltip="Show Latest Block">
|
<SidebarTab on:click={() => openOverlay('donation')} tooltip="Donate">
|
||||||
<span slot="tab">
|
<span slot="tab">
|
||||||
<Icon icon={gridIcon} color="var(--bold-a)" />
|
<Icon icon={giftIcon} color="var(--bold-a)" />
|
||||||
</span>
|
</span>
|
||||||
<div slot="content">
|
|
||||||
<MempoolLegend />
|
|
||||||
</div>
|
|
||||||
</SidebarTab>
|
</SidebarTab>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $haveSupporters }
|
||||||
|
<SidebarTab on:click={() => openOverlay('supporters')} tooltip="Supporters">
|
||||||
|
<span slot="tab">
|
||||||
|
<Icon icon={peopleIcon} color="var(--bold-a)" />
|
||||||
|
</span>
|
||||||
|
</SidebarTab>
|
||||||
|
{/if}
|
||||||
|
<SidebarTab on:click={() => openOverlay('about')} tooltip="About">
|
||||||
|
<span slot="tab">
|
||||||
|
<Icon icon={questionIcon} color="var(--bold-a)" />
|
||||||
|
</span>
|
||||||
|
</SidebarTab>
|
||||||
|
<SidebarTab open={$sidebarToggle === 'contact'} on:click={() => {settings('contact')}} tooltip="Contact">
|
||||||
|
<span slot="tab">
|
||||||
|
<Icon icon={atIcon} color="var(--bold-a)" />
|
||||||
|
</span>
|
||||||
|
<div slot="content">
|
||||||
|
<ContactTab />
|
||||||
|
</div>
|
||||||
|
</SidebarTab>
|
||||||
|
<SidebarTab open={$sidebarToggle === 'legend'} on:click={() => {settings('legend')}} tooltip="Key">
|
||||||
|
<span slot="tab">
|
||||||
|
<Icon icon={infoIcon} color="var(--bold-a)" />
|
||||||
|
</span>
|
||||||
|
<div slot="content">
|
||||||
|
<MempoolLegend />
|
||||||
|
</div>
|
||||||
|
</SidebarTab>
|
||||||
|
<SidebarTab open={$sidebarToggle === 'settings'} on:click={() => {settings('settings')}} tooltip="Settings">
|
||||||
|
<span slot="tab" title="Settings">
|
||||||
|
<Icon icon={cogIcon} color="var(--bold-a)" />
|
||||||
|
</span>
|
||||||
|
<div slot="content">
|
||||||
|
<Settings />
|
||||||
|
</div>
|
||||||
|
</SidebarTab>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,32 @@
|
|||||||
<script>
|
<script>
|
||||||
import Toggle from '../components/Toggle.svelte'
|
import Toggle from './util/Toggle.svelte'
|
||||||
import Pill from '../components/Pill.svelte'
|
import Pill from './util/Pill.svelte'
|
||||||
|
import Select from 'svelte-select'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let active = false
|
export let value = false
|
||||||
export let type = 'toggle'
|
export let type = 'toggle'
|
||||||
export let label = ''
|
export let label = ''
|
||||||
export let falseLabel
|
export let falseLabel
|
||||||
export let trueLabel
|
export let trueLabel
|
||||||
|
export let options
|
||||||
|
let selectedOption
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (options && value) {
|
||||||
|
selectedOption = options.find(option => option.value === value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSelectItems (label, filterText, option) {
|
||||||
|
return [label, ...option.tags].join(' | ').toLowerCase().includes(filterText.toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelect (e) {
|
||||||
|
selectedOption = e.detail
|
||||||
|
dispatch('input', selectedOption.value )
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style type="text/scss">
|
<style type="text/scss">
|
||||||
@ -31,15 +51,33 @@ export let trueLabel
|
|||||||
.label {
|
.label {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
width: 240px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--palette-a);
|
||||||
|
--inputColor: var(--palette-a);
|
||||||
|
--itemColor: var(--palette-a);
|
||||||
|
--itemHoverColor: var(--palette-a);
|
||||||
|
--borderFocusColor: var(--palette-good);
|
||||||
|
|
||||||
|
:global(.selectContainer) {
|
||||||
|
border-width: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="sidebar-menu-item" class:active={active} on:click>
|
<div class="sidebar-menu-item" class:active={value} on:click>
|
||||||
{#if type === 'pill'}
|
{#if type === 'pill'}
|
||||||
<span class="label">{ label }</span>
|
<span class="label">{ label }</span>
|
||||||
<Pill active={active} left={falseLabel} right={trueLabel} />
|
<Pill active={value} left={falseLabel} right={trueLabel} />
|
||||||
|
{:else if type === 'dropdown'}
|
||||||
|
<div class="select">
|
||||||
|
<Select items={ options } value={selectedOption} isSearchable={true} isClearable={false} itemFilter={filterSelectItems} placeholder={label} on:select={onSelect} showIndicator={true} />
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="label">{ label }</span>
|
<span class="label">{ label }</span>
|
||||||
<Toggle active={active} />
|
<Toggle active={value} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,7 @@ import { fly } from 'svelte/transition'
|
|||||||
|
|
||||||
export let open = false
|
export let open = false
|
||||||
export let tooltip = null
|
export let tooltip = null
|
||||||
|
let entered = false
|
||||||
|
|
||||||
let contentElement
|
let contentElement
|
||||||
let contentSlotElement
|
let contentSlotElement
|
||||||
@ -20,6 +21,19 @@ async function updateContentHeight (isOpen) {
|
|||||||
|
|
||||||
$: updateContentHeight(open)
|
$: updateContentHeight(open)
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (open) setTimeout(afterEnter, 400)
|
||||||
|
else beforeExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterEnter () {
|
||||||
|
entered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeExit () {
|
||||||
|
entered = false
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style type="text/scss">
|
<style type="text/scss">
|
||||||
@ -73,6 +87,12 @@ $: updateContentHeight(open)
|
|||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.sidebar-content {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +115,11 @@ $: updateContentHeight(open)
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="sidebar-tab" class:open={open} transition:fly={{ x: 30, duration: 1000 }}>
|
<div
|
||||||
|
class="sidebar-tab"
|
||||||
|
class:open={open}
|
||||||
|
class:active={entered}
|
||||||
|
>
|
||||||
<button class="tab-button" on:click title={tooltip}>
|
<button class="tab-button" on:click title={tooltip}>
|
||||||
<slot name="tab">
|
<slot name="tab">
|
||||||
??
|
??
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { longBtcFormat, integerFormat } from '../utils/format.js'
|
import { longBtcFormat, integerFormat } from '../utils/format.js'
|
||||||
import { exchangeRates, localCurrency } from '../stores.js'
|
import { exchangeRates, settings } from '../stores.js'
|
||||||
import { formatCurrency } from '../utils/fx.js'
|
import { formatCurrency } from '../utils/fx.js'
|
||||||
|
|
||||||
export let tx
|
export let tx
|
||||||
@ -19,10 +19,10 @@ let formattedLocalValue
|
|||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (tx && tx.value) {
|
if (tx && tx.value) {
|
||||||
const rate = $exchangeRates[$localCurrency]
|
const rate = $exchangeRates[$settings.currency]
|
||||||
let local
|
let local
|
||||||
if (rate && rate.last) {
|
if (rate && rate.last) {
|
||||||
formattedLocalValue = formatCurrency($localCurrency, (tx.value/100000000) * rate.last, { compact: true })
|
formattedLocalValue = formatCurrency($settings.currency, (tx.value/100000000) * rate.last, { compact: true })
|
||||||
} else {
|
} else {
|
||||||
formattedLocalValue = null
|
formattedLocalValue = null
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
import SupportersOverlay from '../components/SupportersOverlay.svelte'
|
import SupportersOverlay from '../components/SupportersOverlay.svelte'
|
||||||
import Alerts from '../components/alert/Alerts.svelte'
|
import Alerts from '../components/alert/Alerts.svelte'
|
||||||
import { integerFormat } from '../utils/format.js'
|
import { integerFormat } from '../utils/format.js'
|
||||||
import { exchangeRates, localCurrency, lastBlockId, haveSupporters } from '../stores.js'
|
import { exchangeRates, lastBlockId, haveSupporters, sidebarToggle } from '../stores.js'
|
||||||
import { formatCurrency } from '../utils/fx.js'
|
import { formatCurrency } from '../utils/fx.js'
|
||||||
import config from '../config.js'
|
import config from '../config.js'
|
||||||
|
|
||||||
@ -133,9 +133,9 @@
|
|||||||
const fxColor = 'good'
|
const fxColor = 'good'
|
||||||
let fxLabel = ''
|
let fxLabel = ''
|
||||||
$: {
|
$: {
|
||||||
const rate = $exchangeRates[$localCurrency]
|
const rate = $exchangeRates[$settings.currency]
|
||||||
if (rate && rate.last)
|
if (rate && rate.last)
|
||||||
fxLabel = formatCurrency($localCurrency, rate.last)
|
fxLabel = formatCurrency($settings.currency, rate.last)
|
||||||
}
|
}
|
||||||
|
|
||||||
const debounce = v => {
|
const debounce = v => {
|
||||||
@ -293,6 +293,7 @@
|
|||||||
.stat-counter, .fx-ticker {
|
.stat-counter, .fx-ticker {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&.bad {
|
&.bad {
|
||||||
color: var(--palette-bad);
|
color: var(--palette-bad);
|
||||||
@ -419,7 +420,7 @@
|
|||||||
<div class="status">
|
<div class="status">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{#if $settings.showFX && fxLabel }
|
{#if $settings.showFX && fxLabel }
|
||||||
<span class="fx-ticker {fxColor}">{ fxLabel }</span>
|
<span class="fx-ticker {fxColor}" on:click={() => { $sidebarToggle = 'settings'}}>{ fxLabel }</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -33,6 +33,7 @@ export let rightDisabled = false
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: .5em 1em;
|
padding: .5em 1em;
|
||||||
background: var(--palette-e);
|
background: var(--palette-e);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-left-radius: .5em;
|
border-top-left-radius: .5em;
|
@ -1,5 +1,5 @@
|
|||||||
function getInjectedEnv (key, fallback) {
|
function getInjectedEnv (key, fallback) {
|
||||||
if (window.injected && window.injected[key] != null) {
|
if (window.injected && window.injected[key] != null && window.injected[key] != "") {
|
||||||
return window.injected[key]
|
return window.injected[key]
|
||||||
}
|
}
|
||||||
return fallback
|
return fallback
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { writable, derived } from 'svelte/store'
|
import { writable, derived } from 'svelte/store'
|
||||||
import { makePollStore } from './utils/pollStore.js'
|
import { makePollStore } from './utils/pollStore.js'
|
||||||
import { symbols } from './utils/fx.js'
|
|
||||||
import LocaleCurrency from 'locale-currency'
|
import LocaleCurrency from 'locale-currency'
|
||||||
|
import { currencies } from './utils/fx.js'
|
||||||
import config from './config.js'
|
import config from './config.js'
|
||||||
|
|
||||||
function createCounter () {
|
function createCounter () {
|
||||||
@ -26,7 +26,13 @@ function createCachedDict (namespace, defaultValues) {
|
|||||||
// load from local storage
|
// load from local storage
|
||||||
Object.keys(initial).forEach(field => {
|
Object.keys(initial).forEach(field => {
|
||||||
const val = localStorage.getItem(`${namespace}-${field}`)
|
const val = localStorage.getItem(`${namespace}-${field}`)
|
||||||
if (val != null) initial[field] = JSON.parse(val)
|
if (val != null) {
|
||||||
|
try {
|
||||||
|
initial[field] = JSON.parse(val)
|
||||||
|
} catch (e) {
|
||||||
|
initial[field] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { subscribe, set, update } = writable(initial)
|
const { subscribe, set, update } = writable(initial)
|
||||||
@ -89,9 +95,13 @@ export const blockAreaSize = writable(0)
|
|||||||
|
|
||||||
export const settingsOpen = writable(false)
|
export const settingsOpen = writable(false)
|
||||||
|
|
||||||
|
let localeCurrencyCode = LocaleCurrency.getCurrency(navigator.language)
|
||||||
|
if (!currencies[localeCurrencyCode]) localeCurrencyCode = 'USD'
|
||||||
|
|
||||||
export const settings = createCachedDict('settings', {
|
export const settings = createCachedDict('settings', {
|
||||||
darkMode: true,
|
darkMode: true,
|
||||||
showNetworkStatus: true,
|
showNetworkStatus: true,
|
||||||
|
currency: localeCurrencyCode,
|
||||||
showFX: true,
|
showFX: true,
|
||||||
vbytes: false,
|
vbytes: false,
|
||||||
fancyGraphics: true,
|
fancyGraphics: true,
|
||||||
@ -111,8 +121,3 @@ export const nativeAntialias = writable(false)
|
|||||||
const newVisitor = !localStorage.getItem('seen-welcome-msg')
|
const newVisitor = !localStorage.getItem('seen-welcome-msg')
|
||||||
// export const overlay = writable(newVisitor ? 'about' : null)
|
// export const overlay = writable(newVisitor ? 'about' : null)
|
||||||
export const overlay = writable(null)
|
export const overlay = writable(null)
|
||||||
|
|
||||||
let currencyCode = LocaleCurrency.getCurrency(navigator.language)
|
|
||||||
console.log('LOCALE: ', navigator.language, currencyCode)
|
|
||||||
if (!symbols[currencyCode]) currencyCode = 'USD'
|
|
||||||
export const localCurrency = writable(currencyCode)
|
|
||||||
|
@ -1,119 +1,242 @@
|
|||||||
export const symbols = {
|
export const currencies = {
|
||||||
AUD: {
|
AUD: {
|
||||||
char: "AU$",
|
char: 'AU$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Australian Dollar',
|
||||||
|
countries: [ 'Australia' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'AUD'
|
||||||
},
|
},
|
||||||
BRL: {
|
BRL: {
|
||||||
char: "R$",
|
char: 'R$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Brazilian Real',
|
||||||
|
countries: [ 'Brazil' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'BRL'
|
||||||
},
|
},
|
||||||
CAD: {
|
CAD: {
|
||||||
char: "CA$",
|
char: 'CA$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Canadian Dollar',
|
||||||
|
countries: [ 'Canada' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'CAD'
|
||||||
},
|
},
|
||||||
CHF: {
|
CHF: {
|
||||||
char: "fr.",
|
char: 'fr.',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Swiss Franc',
|
||||||
|
countries: [ 'Switzerland' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'CHF'
|
||||||
},
|
},
|
||||||
CLP: {
|
CLP: {
|
||||||
char: "CLP$",
|
char: 'CLP$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Chilean Peso',
|
||||||
|
countries: [ 'Chile' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'CLP'
|
||||||
},
|
},
|
||||||
CNY: {
|
CNY: {
|
||||||
char: "¥",
|
char: '¥',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Chinese Yuan',
|
||||||
|
countries: [ 'China' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'CNY'
|
||||||
},
|
},
|
||||||
CZK: {
|
CZK: {
|
||||||
char: "Kč",
|
char: 'Kč',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Czech Koruna',
|
||||||
|
countries: [ 'Czechia' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'CZK'
|
||||||
},
|
},
|
||||||
DKK: {
|
DKK: {
|
||||||
char: "kr.",
|
char: 'kr.',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Danish Krone',
|
||||||
|
countries: [ 'Denmark' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'DKK'
|
||||||
},
|
},
|
||||||
EUR: {
|
EUR: {
|
||||||
char: "€",
|
char: '€',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Euro',
|
||||||
|
countries: [
|
||||||
|
'European Union', 'Austria',
|
||||||
|
'Belgium', 'Cyprus',
|
||||||
|
'Estonia', 'Finland',
|
||||||
|
'France', 'Germany',
|
||||||
|
'Greece', 'Ireland',
|
||||||
|
'Italy', 'Latvia',
|
||||||
|
'Lithuania', 'Luxembourg',
|
||||||
|
'Malta', 'Netherlands',
|
||||||
|
'Portugal', 'Slovakia',
|
||||||
|
'Slovenia', 'Spain',
|
||||||
|
'Andorra', 'Monaco',
|
||||||
|
'San Marino', 'Vatican City',
|
||||||
|
'Kosovo', 'Montenegro'
|
||||||
|
],
|
||||||
|
dp: 2,
|
||||||
|
code: 'EUR'
|
||||||
},
|
},
|
||||||
GBP: {
|
GBP: {
|
||||||
char: "£",
|
char: '£',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Pound Sterling',
|
||||||
|
countries: [ 'United Kingdom' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'GBP'
|
||||||
},
|
},
|
||||||
HKD: {
|
HKD: {
|
||||||
char: "HK$",
|
char: 'HK$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Hong Kong Dollar',
|
||||||
|
countries: [ 'Hong Kong' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'HKD'
|
||||||
},
|
},
|
||||||
HRK: {
|
HRK: {
|
||||||
char: "kn",
|
char: 'kn',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Croatian Kuna',
|
||||||
|
countries: [ 'Croatia' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'HRK'
|
||||||
},
|
},
|
||||||
HUF: {
|
HUF: {
|
||||||
char: "Ft",
|
char: 'Ft',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Hungarian Forint',
|
||||||
|
countries: [ 'Hungary' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'HUF'
|
||||||
},
|
},
|
||||||
INR: {
|
INR: {
|
||||||
char: "₹",
|
char: '₹',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Indian Rupee',
|
||||||
|
countries: [ 'India', 'Bhutan' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'INR'
|
||||||
},
|
},
|
||||||
ISK: {
|
ISK: {
|
||||||
char: "kr",
|
char: 'kr',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Icelandic Króna',
|
||||||
|
countries: [ 'Iceland' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'ISK'
|
||||||
},
|
},
|
||||||
JPY: {
|
JPY: {
|
||||||
char: "¥",
|
char: '¥',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Japanese Yen',
|
||||||
|
countries: [ 'Japan' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'JPY'
|
||||||
},
|
},
|
||||||
KRW: {
|
KRW: {
|
||||||
char: "₩",
|
char: '₩',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Korean Won',
|
||||||
|
countries: [ 'South Korea' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'KRW'
|
||||||
},
|
},
|
||||||
NZD: {
|
NZD: {
|
||||||
char: "NZ$",
|
char: 'NZ$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'New Zealand Dollar',
|
||||||
|
countries: [ 'New Zealand' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'NZD'
|
||||||
},
|
},
|
||||||
PLN: {
|
PLN: {
|
||||||
char: "zł",
|
char: 'zł',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Polish Złoty',
|
||||||
|
countries: [ 'Poland' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'PLN'
|
||||||
},
|
},
|
||||||
RON: {
|
RON: {
|
||||||
char: "L",
|
char: 'L',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Romanian Leu',
|
||||||
|
countries: [ 'Romania' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'RON'
|
||||||
},
|
},
|
||||||
RUB: {
|
RUB: {
|
||||||
char: "₽",
|
char: '₽',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Russian Ruble',
|
||||||
|
countries: [ 'Russia' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'RUB'
|
||||||
},
|
},
|
||||||
SEK: {
|
SEK: {
|
||||||
char: "kr",
|
char: 'kr',
|
||||||
pre: false
|
pre: false,
|
||||||
|
name: 'Swedish Krona',
|
||||||
|
countries: [ 'Sweden' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'SEK'
|
||||||
},
|
},
|
||||||
SGD: {
|
SGD: {
|
||||||
char: "S$",
|
char: 'S$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Singapore Dollar',
|
||||||
|
countries: [ 'Singapore' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'SGD'
|
||||||
},
|
},
|
||||||
THB: {
|
THB: {
|
||||||
char: "฿",
|
char: '฿',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Thai Baht',
|
||||||
|
countries: [ 'Thailand' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'THB'
|
||||||
},
|
},
|
||||||
TRY: {
|
TRY: {
|
||||||
char: "₺",
|
char: '₺',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'Turkish Lira',
|
||||||
|
countries: [ 'Turkey' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'TRY'
|
||||||
},
|
},
|
||||||
TWD: {
|
TWD: {
|
||||||
char: "圓",
|
char: '圓',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'New Taiwan Dollar',
|
||||||
|
countries: [ 'Taiwan' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'TWD'
|
||||||
},
|
},
|
||||||
USD: {
|
USD: {
|
||||||
char: "$",
|
char: '$',
|
||||||
pre: true
|
pre: true,
|
||||||
|
name: 'US Dollar',
|
||||||
|
countries: [ 'United States', 'USA' ],
|
||||||
|
dp: 2,
|
||||||
|
code: 'USD'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function formatCurrency (code, amount, params) {
|
export function formatCurrency (code, amount, params) {
|
||||||
// Support for Intl.NumberFormat not quite there yet for all currencies/locales
|
// Support for Intl.NumberFormat not quite there yet for all currencies/locales
|
||||||
// const format = new Intl.NumberFormat(currency.locale, {currency: currency.code, currencyDisplay: "symbol", style: "currency"})
|
// const format = new Intl.NumberFormat(currency.locale, {currency: currency.code, currencyDisplay: "symbol", style: "currency"})
|
||||||
// return format.format(amount)
|
// return format.format(amount)
|
||||||
const currency = symbols[code] || symbols.USD
|
const currency = currencies[code] || currencies.USD
|
||||||
let parts = [currency.char]
|
let parts = [currency.char]
|
||||||
if (params && params.compact) {
|
if (params && params.compact) {
|
||||||
let compacted = amount
|
let compacted = amount
|
||||||
@ -129,7 +252,7 @@ export function formatCurrency (code, amount, params) {
|
|||||||
const amountPart = amount < 1000 ? amount.toPrecision(precision) : Math.round(amount)
|
const amountPart = amount < 1000 ? amount.toPrecision(precision) : Math.round(amount)
|
||||||
parts.push(`${amountPart}${['','K','M','B','T'][suffixIndex]}`)
|
parts.push(`${amountPart}${['','K','M','B','T'][suffixIndex]}`)
|
||||||
} else {
|
} else {
|
||||||
parts.push(amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }))
|
parts.push(amount.toLocaleString(undefined, { minimumFractionDigits: currency.dp, maximumFractionDigits: currency.dp }))
|
||||||
}
|
}
|
||||||
if (!currency.pre) parts = parts.reverse()
|
if (!currency.pre) parts = parts.reverse()
|
||||||
return parts.join(' ')
|
return parts.join(' ')
|
||||||
|
@ -4,7 +4,7 @@ defmodule BitcoinStream.MixProject do
|
|||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :bitcoin_stream,
|
app: :bitcoin_stream,
|
||||||
version: "2.1.2",
|
version: "2.1.3",
|
||||||
elixir: "~> 1.10",
|
elixir: "~> 1.10",
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
deps: deps(),
|
deps: deps(),
|
||||||
|
Loading…
Reference in New Issue
Block a user