@stellar-expert/ui-framework 1.16.7 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +35 -0
- package/README.md +1125 -3
- package/account/account-address.js +127 -127
- package/account/available-balance.js +22 -22
- package/account/identicon.js +90 -90
- package/account/signer-key.js +65 -64
- package/api/explorer-api-hooks.js +209 -202
- package/api/explorer-api-paginated-list-hooks.js +441 -440
- package/api/explorer-batch-info-loader.js +111 -85
- package/api/explorer-tx-api.js +28 -28
- package/api/ledger-stream.js +15 -0
- package/asset/amount.js +56 -56
- package/asset/asset-icon.js +41 -41
- package/asset/asset-issuer.js +21 -21
- package/asset/asset-link.js +107 -107
- package/asset/asset-list-hooks.js +59 -59
- package/asset/asset-meta-hooks.js +88 -88
- package/asset/asset-selector.js +72 -71
- package/basic-styles/base.scss +50 -50
- package/basic-styles/grid.scss +1 -1
- package/basic-styles/table.scss +1 -1
- package/claimable-balance/claimable-balance-claimants.js +5 -0
- package/contract/contract-api.js +15 -0
- package/contract/invocation-info-view.js +10 -2
- package/contract/sc-val.js +24 -0
- package/controls/button-group.js +25 -19
- package/controls/button-group.scss +46 -46
- package/controls/button.js +93 -78
- package/controls/button.scss +173 -173
- package/controls/code-block.js +42 -34
- package/controls/code-block.scss +71 -71
- package/controls/dropdown.js +318 -287
- package/controls/dropdown.scss +159 -159
- package/controls/external-link.js +10 -4
- package/controls/info-tooltip.js +23 -16
- package/controls/slider.js +29 -19
- package/controls/tabs.js +94 -94
- package/controls/tabs.scss +70 -70
- package/controls/tooltip.js +244 -240
- package/controls/tooltip.scss +116 -116
- package/controls/update-highlighter.js +32 -27
- package/controls/update-highlighter.scss +7 -7
- package/date/date-selector.js +56 -54
- package/date/elapsed-time.js +28 -21
- package/dex/price-dynamic.js +53 -44
- package/dex/price-dynamic.scss +33 -33
- package/directory/directory-hooks.js +109 -97
- package/effect/effect-description.js +5 -3
- package/errors/error-boundary.js +110 -97
- package/horizon/horizon-account-helpers.js +104 -104
- package/horizon/horizon-ledger-helpers.js +35 -35
- package/horizon/horizon-trades-helper.js +88 -88
- package/horizon/horizon-transaction-helpers.js +36 -36
- package/index.d.ts +1241 -0
- package/interaction/accordion.js +43 -35
- package/interaction/autofocus.js +13 -9
- package/interaction/block-select.js +64 -53
- package/interaction/block-select.scss +21 -21
- package/interaction/copy-to-clipboard.js +25 -18
- package/interaction/inline-progress.js +20 -15
- package/interaction/qr-code.js +34 -34
- package/interaction/responsive.js +20 -20
- package/interaction/spoiler.js +52 -39
- package/interaction/spoiler.scss +6 -6
- package/interaction/theme-selector.js +13 -10
- package/ledger/ledger-entry-href-formatter.js +4 -3
- package/ledger/ledger-entry-link.js +93 -58
- package/ledger/ledger-info-parser.js +28 -0
- package/meta/page-meta-tags.js +243 -238
- package/module/dynamic-module.js +47 -47
- package/package.json +3 -2
- package/state/on-screen-hooks.js +22 -22
- package/state/page-visibility-helpers.js +29 -16
- package/state/page-visibility-hooks.js +13 -11
- package/state/screen-orientation-hooks.js +19 -15
- package/state/state-hooks.js +76 -76
- package/state/stellar-network-hooks.js +56 -44
- package/state/theme.js +29 -28
- package/stellar/key-type.js +92 -91
- package/stellar/ledger-generic-id.js +39 -39
- package/stellar/signature-hint-utils.js +65 -65
- package/toast/toast-notifications-block.js +59 -59
- package/toast/toast-notifications.scss +1 -1
- package/tx/op-description-view.js +84 -81
- package/tx/op-icon.js +98 -98
- package/tx/parser/op-balance-changes.js +66 -66
- package/tx/parser/op-descriptor.js +51 -48
- package/tx/parser/tx-details-parser.js +81 -81
- package/tx/parser/tx-matcher.js +371 -371
- package/tx/parser/type-filter-matcher.js +126 -126
- package/tx/tx-list-hooks.js +32 -18
- package/tx/tx-operations-list.js +117 -116
package/asset/asset-link.js
CHANGED
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import cn from 'classnames'
|
|
3
|
-
import {shortenString} from '@stellar-expert/formatter'
|
|
4
|
-
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
5
|
-
import {useStellarNetwork} from '../state/stellar-network-hooks'
|
|
6
|
-
import {formatExplorerLink} from '../ledger/ledger-entry-href-formatter'
|
|
7
|
-
import {AccountAddress} from '../account/account-address'
|
|
8
|
-
import {useAssetMeta} from './asset-meta-hooks'
|
|
9
|
-
import {AssetIcon} from './asset-icon'
|
|
10
|
-
import {AssetIssuer} from './asset-issuer'
|
|
11
|
-
import './asset-link.scss'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Explorer asset link
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {{}} [style] - Optional CSS style
|
|
21
|
-
* @param {*} [children] - Optional inner link text
|
|
22
|
-
* @constructor
|
|
23
|
-
*/
|
|
24
|
-
export const AssetLink = React.memo(function AssetLink({
|
|
25
|
-
asset,
|
|
26
|
-
link,
|
|
27
|
-
issuer,
|
|
28
|
-
icon,
|
|
29
|
-
className,
|
|
30
|
-
style,
|
|
31
|
-
children: innerText
|
|
32
|
-
}) {
|
|
33
|
-
if (!(asset instanceof AssetDescriptor)) {
|
|
34
|
-
asset = AssetDescriptor.parse(asset)
|
|
35
|
-
}
|
|
36
|
-
useStellarNetwork()
|
|
37
|
-
const meta = useAssetMeta(asset)
|
|
38
|
-
|
|
39
|
-
if (!asset)
|
|
40
|
-
return null
|
|
41
|
-
|
|
42
|
-
let children = innerText
|
|
43
|
-
if (!innerText) {
|
|
44
|
-
if (asset.poolId) {
|
|
45
|
-
if (meta) {
|
|
46
|
-
const [assetA, assetB] = meta.assets.map(a => AssetDescriptor.parse(a.asset))
|
|
47
|
-
children = <span title={'Liquidity pool ' + asset.poolId} className="nowrap">
|
|
48
|
-
<span>
|
|
49
|
-
<AssetWarningStatus meta={assetA}/>
|
|
50
|
-
{icon !== false && <AssetIcon asset={assetA}/>}
|
|
51
|
-
{assetA.code}
|
|
52
|
-
{issuer === true && <AssetIssuer asset={assetA}/>}
|
|
53
|
-
</span>
|
|
54
|
-
<span style={{fontSize: '0.7em'}}> <i className="icon icon-plus dimmed"/> </span>
|
|
55
|
-
<span>
|
|
56
|
-
<AssetWarningStatus meta={assetB}/>
|
|
57
|
-
{icon !== false && <AssetIcon asset={assetB}/>}
|
|
58
|
-
{assetB.code}
|
|
59
|
-
{issuer === true && <AssetIssuer asset={assetB}/>}
|
|
60
|
-
</span>
|
|
61
|
-
</span>
|
|
62
|
-
} else {
|
|
63
|
-
children = <>{shortenString(asset.poolId)}</>
|
|
64
|
-
}
|
|
65
|
-
} else if (asset.isContract) {
|
|
66
|
-
children = <>
|
|
67
|
-
<AssetWarningStatus meta={meta}/>
|
|
68
|
-
{icon !== false && <AssetIcon asset={asset}/>}
|
|
69
|
-
{!!meta?.code && <span title={meta.tokenName}>{meta.code} </span>}
|
|
70
|
-
{(issuer !== false || !meta?.code) &&
|
|
71
|
-
<AccountAddress account={asset.contract} chars={8} link={false} icon={false}/>}
|
|
72
|
-
</>
|
|
73
|
-
} else {
|
|
74
|
-
children = <>
|
|
75
|
-
<AssetWarningStatus meta={meta}/>
|
|
76
|
-
{icon !== false && <AssetIcon asset={asset}/>}
|
|
77
|
-
{!!asset.code && asset.code}
|
|
78
|
-
{issuer !== false && <AssetIssuer asset={asset}/>}
|
|
79
|
-
</>
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const props = {
|
|
84
|
-
'aria-label': asset.toString(),
|
|
85
|
-
className: cn('asset-link', className),
|
|
86
|
-
style,
|
|
87
|
-
children
|
|
88
|
-
}
|
|
89
|
-
if (link === false)
|
|
90
|
-
return <span {...props}/>
|
|
91
|
-
if (typeof link === 'string') {
|
|
92
|
-
props.href = link
|
|
93
|
-
} else {
|
|
94
|
-
props.href = asset.poolId ?
|
|
95
|
-
formatExplorerLink('liquidity-pool', asset.poolId) :
|
|
96
|
-
formatExplorerLink('asset', asset.toString())
|
|
97
|
-
if (window.origin !== explorerFrontendOrigin) {
|
|
98
|
-
props.target = '_blank'
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return <a {...props}/>
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
function AssetWarningStatus({meta}) {
|
|
105
|
-
if (!meta?.unsafe)
|
|
106
|
-
return null
|
|
107
|
-
return <i className="icon icon-warning color-warning" title="Warning: asset reported as unsafe"/>
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import cn from 'classnames'
|
|
3
|
+
import {shortenString} from '@stellar-expert/formatter'
|
|
4
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
5
|
+
import {useStellarNetwork} from '../state/stellar-network-hooks'
|
|
6
|
+
import {formatExplorerLink} from '../ledger/ledger-entry-href-formatter'
|
|
7
|
+
import {AccountAddress} from '../account/account-address'
|
|
8
|
+
import {useAssetMeta} from './asset-meta-hooks'
|
|
9
|
+
import {AssetIcon} from './asset-icon'
|
|
10
|
+
import {AssetIssuer} from './asset-issuer'
|
|
11
|
+
import './asset-link.scss'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Explorer asset link
|
|
15
|
+
* @param {string|AssetDescriptor|Asset} asset - Asset name/descriptor
|
|
16
|
+
* @param {boolean|string} [link] - Reference link
|
|
17
|
+
* @param {boolean} [issuer] - Whether to show asset issuer
|
|
18
|
+
* @param {boolean} [icon] - Whether to show asset icon
|
|
19
|
+
* @param {string} [className] - Optional CSS class name
|
|
20
|
+
* @param {{}} [style] - Optional CSS style
|
|
21
|
+
* @param {*} [children] - Optional inner link text
|
|
22
|
+
* @constructor
|
|
23
|
+
*/
|
|
24
|
+
export const AssetLink = React.memo(function AssetLink({
|
|
25
|
+
asset,
|
|
26
|
+
link,
|
|
27
|
+
issuer,
|
|
28
|
+
icon,
|
|
29
|
+
className,
|
|
30
|
+
style,
|
|
31
|
+
children: innerText
|
|
32
|
+
}) {
|
|
33
|
+
if (!(asset instanceof AssetDescriptor)) {
|
|
34
|
+
asset = AssetDescriptor.parse(asset)
|
|
35
|
+
}
|
|
36
|
+
useStellarNetwork()
|
|
37
|
+
const meta = useAssetMeta(asset)
|
|
38
|
+
|
|
39
|
+
if (!asset)
|
|
40
|
+
return null
|
|
41
|
+
|
|
42
|
+
let children = innerText
|
|
43
|
+
if (!innerText) {
|
|
44
|
+
if (asset.poolId) {
|
|
45
|
+
if (meta) {
|
|
46
|
+
const [assetA, assetB] = meta.assets.map(a => AssetDescriptor.parse(a.asset))
|
|
47
|
+
children = <span title={'Liquidity pool ' + asset.poolId} className="nowrap">
|
|
48
|
+
<span>
|
|
49
|
+
<AssetWarningStatus meta={assetA}/>
|
|
50
|
+
{icon !== false && <AssetIcon asset={assetA}/>}
|
|
51
|
+
{assetA.code}
|
|
52
|
+
{issuer === true && <AssetIssuer asset={assetA}/>}
|
|
53
|
+
</span>
|
|
54
|
+
<span style={{fontSize: '0.7em'}}> <i className="icon icon-plus dimmed"/> </span>
|
|
55
|
+
<span>
|
|
56
|
+
<AssetWarningStatus meta={assetB}/>
|
|
57
|
+
{icon !== false && <AssetIcon asset={assetB}/>}
|
|
58
|
+
{assetB.code}
|
|
59
|
+
{issuer === true && <AssetIssuer asset={assetB}/>}
|
|
60
|
+
</span>
|
|
61
|
+
</span>
|
|
62
|
+
} else {
|
|
63
|
+
children = <>{shortenString(asset.poolId)}</>
|
|
64
|
+
}
|
|
65
|
+
} else if (asset.isContract) {
|
|
66
|
+
children = <>
|
|
67
|
+
<AssetWarningStatus meta={meta}/>
|
|
68
|
+
{icon !== false && <AssetIcon asset={asset}/>}
|
|
69
|
+
{!!meta?.code && <span title={meta.tokenName}>{meta.code} </span>}
|
|
70
|
+
{(issuer !== false || !meta?.code) &&
|
|
71
|
+
<AccountAddress account={asset.contract} chars={8} link={false} icon={false}/>}
|
|
72
|
+
</>
|
|
73
|
+
} else {
|
|
74
|
+
children = <>
|
|
75
|
+
<AssetWarningStatus meta={meta}/>
|
|
76
|
+
{icon !== false && <AssetIcon asset={asset}/>}
|
|
77
|
+
{!!asset.code && asset.code}
|
|
78
|
+
{issuer !== false && <AssetIssuer asset={asset}/>}
|
|
79
|
+
</>
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const props = {
|
|
84
|
+
'aria-label': asset.toString(),
|
|
85
|
+
className: cn('asset-link', className),
|
|
86
|
+
style,
|
|
87
|
+
children
|
|
88
|
+
}
|
|
89
|
+
if (link === false)
|
|
90
|
+
return <span {...props}/>
|
|
91
|
+
if (typeof link === 'string') {
|
|
92
|
+
props.href = link
|
|
93
|
+
} else {
|
|
94
|
+
props.href = asset.poolId ?
|
|
95
|
+
formatExplorerLink('liquidity-pool', asset.poolId) :
|
|
96
|
+
formatExplorerLink('asset', asset.toString())
|
|
97
|
+
if (window.origin !== explorerFrontendOrigin) {
|
|
98
|
+
props.target = '_blank'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return <a {...props}/>
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
function AssetWarningStatus({meta}) {
|
|
105
|
+
if (!meta?.unsafe)
|
|
106
|
+
return null
|
|
107
|
+
return <i className="icon icon-warning color-warning" title="Warning: asset reported as unsafe"/>
|
|
108
108
|
}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import React, {useState} from 'react'
|
|
2
|
-
import {throttle} from 'throttle-debounce'
|
|
3
|
-
import equal from 'react-fast-compare'
|
|
4
|
-
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
5
|
-
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
-
import {useDeepEffect} from '../state/state-hooks'
|
|
7
|
-
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
-
|
|
9
|
-
const defaults = {
|
|
10
|
-
limit: 20,
|
|
11
|
-
order: 'desc',
|
|
12
|
-
sort: 'rating'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getCurrentCursor(data) {
|
|
16
|
-
if (!data || !data.length) return undefined
|
|
17
|
-
return data[data.length - 1].paging_token
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
* @param {Object} params
|
|
23
|
-
* @return {{assets: Array<Object>, loadPage:
|
|
24
|
-
*/
|
|
25
|
-
export function useAssetList(params) {
|
|
26
|
-
const [loading, setLoading] = useState(false),
|
|
27
|
-
[assets, setAssets] = useState({data: [], params}),
|
|
28
|
-
loadPage = throttle(1000, function () {
|
|
29
|
-
if (loading) return
|
|
30
|
-
const requestParams = {
|
|
31
|
-
...defaults, ...params
|
|
32
|
-
}
|
|
33
|
-
if (equal(params, assets.params)) {
|
|
34
|
-
requestParams.cursor = getCurrentCursor(assets.data)
|
|
35
|
-
}
|
|
36
|
-
const endpoint = getCurrentStellarNetwork() + '/asset' + stringifyQuery(requestParams)
|
|
37
|
-
setLoading(true)
|
|
38
|
-
fetchExplorerApi(endpoint)
|
|
39
|
-
.then(res => {
|
|
40
|
-
const {records} = res._embedded
|
|
41
|
-
setAssets(existing => {
|
|
42
|
-
let data = records
|
|
43
|
-
if (equal(params, existing.params)) {
|
|
44
|
-
data = [...existing.data, ...records]
|
|
45
|
-
}
|
|
46
|
-
return {data, params}
|
|
47
|
-
})
|
|
48
|
-
setLoading(false)
|
|
49
|
-
})
|
|
50
|
-
.catch(e => console.error(e))
|
|
51
|
-
.finally(() => {
|
|
52
|
-
setLoading(false)
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
useDeepEffect(() => {
|
|
56
|
-
loadPage()
|
|
57
|
-
}, [params])
|
|
58
|
-
|
|
59
|
-
return {assets: assets.data, loadPage, loading}
|
|
1
|
+
import React, {useState} from 'react'
|
|
2
|
+
import {throttle} from 'throttle-debounce'
|
|
3
|
+
import equal from 'react-fast-compare'
|
|
4
|
+
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
5
|
+
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
+
import {useDeepEffect} from '../state/state-hooks'
|
|
7
|
+
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
+
|
|
9
|
+
const defaults = {
|
|
10
|
+
limit: 20,
|
|
11
|
+
order: 'desc',
|
|
12
|
+
sort: 'rating'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCurrentCursor(data) {
|
|
16
|
+
if (!data || !data.length) return undefined
|
|
17
|
+
return data[data.length - 1].paging_token
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} params
|
|
23
|
+
* @return {{assets: Array<Object>, loadPage: function, loading: boolean}}
|
|
24
|
+
*/
|
|
25
|
+
export function useAssetList(params) {
|
|
26
|
+
const [loading, setLoading] = useState(false),
|
|
27
|
+
[assets, setAssets] = useState({data: [], params}),
|
|
28
|
+
loadPage = throttle(1000, function () {
|
|
29
|
+
if (loading) return
|
|
30
|
+
const requestParams = {
|
|
31
|
+
...defaults, ...params
|
|
32
|
+
}
|
|
33
|
+
if (equal(params, assets.params)) {
|
|
34
|
+
requestParams.cursor = getCurrentCursor(assets.data)
|
|
35
|
+
}
|
|
36
|
+
const endpoint = getCurrentStellarNetwork() + '/asset' + stringifyQuery(requestParams)
|
|
37
|
+
setLoading(true)
|
|
38
|
+
fetchExplorerApi(endpoint)
|
|
39
|
+
.then(res => {
|
|
40
|
+
const {records} = res._embedded
|
|
41
|
+
setAssets(existing => {
|
|
42
|
+
let data = records
|
|
43
|
+
if (equal(params, existing.params)) {
|
|
44
|
+
data = [...existing.data, ...records]
|
|
45
|
+
}
|
|
46
|
+
return {data, params}
|
|
47
|
+
})
|
|
48
|
+
setLoading(false)
|
|
49
|
+
})
|
|
50
|
+
.catch(e => console.error(e))
|
|
51
|
+
.finally(() => {
|
|
52
|
+
setLoading(false)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
useDeepEffect(() => {
|
|
56
|
+
loadPage()
|
|
57
|
+
}, [params])
|
|
58
|
+
|
|
59
|
+
return {assets: assets.data, loadPage, loading}
|
|
60
60
|
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import {useEffect, useState} from 'react'
|
|
2
|
-
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
3
|
-
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
4
|
-
import {InMemoryClientCache} from '@stellar-expert/client-cache'
|
|
5
|
-
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
-
import {ExplorerBatchInfoLoader} from '../api/explorer-batch-info-loader'
|
|
7
|
-
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef AssetBasicTomlInfo
|
|
11
|
-
* @property {
|
|
12
|
-
* @property {
|
|
13
|
-
* @property {
|
|
14
|
-
* @property {
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef AssetMeta
|
|
19
|
-
* @property {
|
|
20
|
-
* @property {
|
|
21
|
-
* @property {AssetBasicTomlInfo} toml_info
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const cache = new InMemoryClientCache()
|
|
25
|
-
|
|
26
|
-
const loader = new ExplorerBatchInfoLoader(batch => {
|
|
27
|
-
return fetchExplorerApi(getCurrentStellarNetwork() + '/asset/meta' + stringifyQuery({
|
|
28
|
-
asset: batch,
|
|
29
|
-
origin: window.location.origin
|
|
30
|
-
}))
|
|
31
|
-
}, entry => {
|
|
32
|
-
cache.set(entry.name, entry)
|
|
33
|
-
return {
|
|
34
|
-
key: entry.name,
|
|
35
|
-
info: entry
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
function retrieveFromCache(asset) {
|
|
40
|
-
//try to load from the shared cache
|
|
41
|
-
const cachedEntry = cache.get(asset)
|
|
42
|
-
if (cachedEntry && !cachedEntry.isStale) {
|
|
43
|
-
if (!cachedEntry.isExpired)
|
|
44
|
-
return cachedEntry.data//everything is up to date - no need to re-fetch
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function normalizeAssetName(asset) {
|
|
49
|
-
if (!asset)
|
|
50
|
-
return null
|
|
51
|
-
if (typeof asset === 'string' && asset.length === 56)
|
|
52
|
-
return asset //contract id
|
|
53
|
-
return AssetDescriptor.parse(asset).toFQAN()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
*
|
|
58
|
-
* @param {AssetDescriptor|
|
|
59
|
-
* @return {AssetMeta}
|
|
60
|
-
*/
|
|
61
|
-
export function useAssetMeta(asset) {
|
|
62
|
-
try {
|
|
63
|
-
asset = normalizeAssetName(asset)
|
|
64
|
-
} catch (e) {
|
|
65
|
-
asset = null
|
|
66
|
-
}
|
|
67
|
-
const [assetInfo, setAssetInfo] = useState(retrieveFromCache(asset))
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
if (!asset)
|
|
70
|
-
return
|
|
71
|
-
const cached = retrieveFromCache(asset)
|
|
72
|
-
setAssetInfo(cached)
|
|
73
|
-
if (cached)
|
|
74
|
-
return
|
|
75
|
-
let unloaded = false
|
|
76
|
-
//load from the server
|
|
77
|
-
loader.loadEntry(asset)
|
|
78
|
-
.then(a => {
|
|
79
|
-
if (unloaded)
|
|
80
|
-
return
|
|
81
|
-
setAssetInfo(a)
|
|
82
|
-
})
|
|
83
|
-
return () => {
|
|
84
|
-
unloaded = true
|
|
85
|
-
}
|
|
86
|
-
}, [asset])
|
|
87
|
-
|
|
88
|
-
return assetInfo
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
3
|
+
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
4
|
+
import {InMemoryClientCache} from '@stellar-expert/client-cache'
|
|
5
|
+
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
+
import {ExplorerBatchInfoLoader} from '../api/explorer-batch-info-loader'
|
|
7
|
+
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef AssetBasicTomlInfo
|
|
11
|
+
* @property {string} name
|
|
12
|
+
* @property {string} orgName
|
|
13
|
+
* @property {string} image
|
|
14
|
+
* @property {number} decimals
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef AssetMeta
|
|
19
|
+
* @property {string} name
|
|
20
|
+
* @property {string} domain
|
|
21
|
+
* @property {AssetBasicTomlInfo} toml_info
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const cache = new InMemoryClientCache()
|
|
25
|
+
|
|
26
|
+
const loader = new ExplorerBatchInfoLoader(batch => {
|
|
27
|
+
return fetchExplorerApi(getCurrentStellarNetwork() + '/asset/meta' + stringifyQuery({
|
|
28
|
+
asset: batch,
|
|
29
|
+
origin: window.location.origin
|
|
30
|
+
}))
|
|
31
|
+
}, entry => {
|
|
32
|
+
cache.set(entry.name, entry)
|
|
33
|
+
return {
|
|
34
|
+
key: entry.name,
|
|
35
|
+
info: entry
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function retrieveFromCache(asset) {
|
|
40
|
+
//try to load from the shared cache
|
|
41
|
+
const cachedEntry = cache.get(asset)
|
|
42
|
+
if (cachedEntry && !cachedEntry.isStale) {
|
|
43
|
+
if (!cachedEntry.isExpired)
|
|
44
|
+
return cachedEntry.data//everything is up to date - no need to re-fetch
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeAssetName(asset) {
|
|
49
|
+
if (!asset)
|
|
50
|
+
return null
|
|
51
|
+
if (typeof asset === 'string' && asset.length === 56)
|
|
52
|
+
return asset //contract id
|
|
53
|
+
return AssetDescriptor.parse(asset).toFQAN()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {AssetDescriptor|string} asset
|
|
59
|
+
* @return {AssetMeta}
|
|
60
|
+
*/
|
|
61
|
+
export function useAssetMeta(asset) {
|
|
62
|
+
try {
|
|
63
|
+
asset = normalizeAssetName(asset)
|
|
64
|
+
} catch (e) {
|
|
65
|
+
asset = null
|
|
66
|
+
}
|
|
67
|
+
const [assetInfo, setAssetInfo] = useState(retrieveFromCache(asset))
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!asset)
|
|
70
|
+
return
|
|
71
|
+
const cached = retrieveFromCache(asset)
|
|
72
|
+
setAssetInfo(cached)
|
|
73
|
+
if (cached)
|
|
74
|
+
return
|
|
75
|
+
let unloaded = false
|
|
76
|
+
//load from the server
|
|
77
|
+
loader.loadEntry(asset)
|
|
78
|
+
.then(a => {
|
|
79
|
+
if (unloaded)
|
|
80
|
+
return
|
|
81
|
+
setAssetInfo(a)
|
|
82
|
+
})
|
|
83
|
+
return () => {
|
|
84
|
+
unloaded = true
|
|
85
|
+
}
|
|
86
|
+
}, [asset])
|
|
87
|
+
|
|
88
|
+
return assetInfo
|
|
89
89
|
}
|