Add currency selector to settings sidebar

Resolves #11
This commit is contained in:
Mononaut 2022-01-31 12:47:01 -06:00
parent 9f9ad26427
commit 89a7ef5af6
17 changed files with 379 additions and 147 deletions

View File

@ -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

View File

@ -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",

View File

@ -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"
} }
} }

View File

@ -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
} }

View File

@ -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'

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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">
?? ??

View File

@ -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
} }

View File

@ -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">

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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(' ')

View File

@ -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(),