@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
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} ParsedStellarId
|
|
3
|
-
* @property {('unknown'|'ledger'|'transaction'|'operation')} type - Parsed id type
|
|
4
|
-
* @property {
|
|
5
|
-
* @property {
|
|
6
|
-
* @property {
|
|
7
|
-
* @property {
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Parse stellar generic id
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {ParsedStellarId}
|
|
14
|
-
*/
|
|
15
|
-
export function parseStellarGenericId(id) {
|
|
16
|
-
if (!/^\d{1,19}$/.test(id)) return {type: 'unknown'}
|
|
17
|
-
const parsed = BigInt(id)
|
|
18
|
-
const res = {
|
|
19
|
-
id,
|
|
20
|
-
type: 'unknown'
|
|
21
|
-
}
|
|
22
|
-
if (parsed < 4294967296n) {
|
|
23
|
-
res.type = 'ledger'
|
|
24
|
-
res.ledger = Number(parsed)
|
|
25
|
-
return res
|
|
26
|
-
}
|
|
27
|
-
const ledger = parsed / 4294967296n
|
|
28
|
-
const opOrder = parsed % 4096n
|
|
29
|
-
res.ledger = Number(ledger)
|
|
30
|
-
if (opOrder > 0n) {
|
|
31
|
-
res.type = 'operation'
|
|
32
|
-
res.tx = (parsed - opOrder).toString()
|
|
33
|
-
res.operationOrder = Number(opOrder)
|
|
34
|
-
} else {
|
|
35
|
-
res.type = 'transaction'
|
|
36
|
-
res.tx = parsed.toString()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return res
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ParsedStellarId
|
|
3
|
+
* @property {('unknown'|'ledger'|'transaction'|'operation')} type - Parsed id type
|
|
4
|
+
* @property {string} [id] - Parsed id
|
|
5
|
+
* @property {number} [ledger] - Ledger sequence
|
|
6
|
+
* @property {string} [tx] - Transaction id
|
|
7
|
+
* @property {number} [operationOrder] - Operation order within a transaction
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse stellar generic id
|
|
12
|
+
* @param {string} id
|
|
13
|
+
* @returns {ParsedStellarId}
|
|
14
|
+
*/
|
|
15
|
+
export function parseStellarGenericId(id) {
|
|
16
|
+
if (!/^\d{1,19}$/.test(id)) return {type: 'unknown'}
|
|
17
|
+
const parsed = BigInt(id)
|
|
18
|
+
const res = {
|
|
19
|
+
id,
|
|
20
|
+
type: 'unknown'
|
|
21
|
+
}
|
|
22
|
+
if (parsed < 4294967296n) {
|
|
23
|
+
res.type = 'ledger'
|
|
24
|
+
res.ledger = Number(parsed)
|
|
25
|
+
return res
|
|
26
|
+
}
|
|
27
|
+
const ledger = parsed / 4294967296n
|
|
28
|
+
const opOrder = parsed % 4096n
|
|
29
|
+
res.ledger = Number(ledger)
|
|
30
|
+
if (opOrder > 0n) {
|
|
31
|
+
res.type = 'operation'
|
|
32
|
+
res.tx = (parsed - opOrder).toString()
|
|
33
|
+
res.operationOrder = Number(opOrder)
|
|
34
|
+
} else {
|
|
35
|
+
res.type = 'transaction'
|
|
36
|
+
res.tx = parsed.toString()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return res
|
|
40
40
|
}
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import {Keypair} from '@stellar/stellar-base'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Convert the signature hint to the StrKey mask
|
|
5
|
-
* @param {Buffer} hint - Hint to convert
|
|
6
|
-
* @return {
|
|
7
|
-
*/
|
|
8
|
-
export function signatureHintToMask(hint) {
|
|
9
|
-
const partialPublicKey = Buffer.concat([new Buffer(28).fill(0), hint]),
|
|
10
|
-
hintKeypair = new Keypair({type: 'ed25519', publicKey: partialPublicKey}),
|
|
11
|
-
pk = hintKeypair.publicKey()
|
|
12
|
-
return pk.substring(0, 1) + '_'.repeat(46) + pk.substring(47, 52) + '_'.repeat(4)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Format the signature hint to the friendly form for UI
|
|
17
|
-
* @param {Buffer} hint - Hint to convert
|
|
18
|
-
* @return {string}
|
|
19
|
-
*/
|
|
20
|
-
export function formatSignatureHint(hint) {
|
|
21
|
-
const mask = signatureHintToMask(hint)
|
|
22
|
-
return mask.substring(0, 2) + '…' + mask.substring(46)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Check if the hint matches the specific key
|
|
27
|
-
* @param {Buffer} hint - Hint to check
|
|
28
|
-
* @param {
|
|
29
|
-
* @return {
|
|
30
|
-
*/
|
|
31
|
-
export function singatureHintMatchesKey(hint, key) {
|
|
32
|
-
return signatureHintToMask(hint).substring(47, 52) === key.substring(47, 52)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Find a key by the signature hint
|
|
37
|
-
* @param {Buffer} hint - Hint to look for
|
|
38
|
-
* @param {Array<
|
|
39
|
-
* @return {
|
|
40
|
-
*/
|
|
41
|
-
export function findKeyBySignatureHint(hint, allKeys) {
|
|
42
|
-
return allKeys.find(key => singatureHintMatchesKey(hint, key))
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Find tx signature for a given signer key
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {Signature} allSignatures
|
|
49
|
-
* @returns {Signature}
|
|
50
|
-
*/
|
|
51
|
-
export function findSignatureByKey(key, allSignatures = []) {
|
|
52
|
-
return allSignatures.find(sig => singatureHintMatchesKey(sig.hint(), key))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if the hint matches any of the provided keys
|
|
57
|
-
* @param {Buffer} signature - Tx signature
|
|
58
|
-
* @param {Array<
|
|
59
|
-
* @returns {Array<
|
|
60
|
-
*/
|
|
61
|
-
export function findKeysBySignatureHint(signature, keys) {
|
|
62
|
-
const mask = signatureHintToMask(signature.hint())
|
|
63
|
-
return keys.filter(address => mask[0] === address[0] &&
|
|
64
|
-
mask.substr(47, 5) === address.substr(47, 5))
|
|
65
|
-
}
|
|
1
|
+
import {Keypair} from '@stellar/stellar-base'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert the signature hint to the StrKey mask
|
|
5
|
+
* @param {Buffer} hint - Hint to convert
|
|
6
|
+
* @return {string}
|
|
7
|
+
*/
|
|
8
|
+
export function signatureHintToMask(hint) {
|
|
9
|
+
const partialPublicKey = Buffer.concat([new Buffer(28).fill(0), hint]),
|
|
10
|
+
hintKeypair = new Keypair({type: 'ed25519', publicKey: partialPublicKey}),
|
|
11
|
+
pk = hintKeypair.publicKey()
|
|
12
|
+
return pk.substring(0, 1) + '_'.repeat(46) + pk.substring(47, 52) + '_'.repeat(4)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Format the signature hint to the friendly form for UI
|
|
17
|
+
* @param {Buffer} hint - Hint to convert
|
|
18
|
+
* @return {string}
|
|
19
|
+
*/
|
|
20
|
+
export function formatSignatureHint(hint) {
|
|
21
|
+
const mask = signatureHintToMask(hint)
|
|
22
|
+
return mask.substring(0, 2) + '…' + mask.substring(46)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if the hint matches the specific key
|
|
27
|
+
* @param {Buffer} hint - Hint to check
|
|
28
|
+
* @param {string} key - Key to compare
|
|
29
|
+
* @return {boolean}
|
|
30
|
+
*/
|
|
31
|
+
export function singatureHintMatchesKey(hint, key) {
|
|
32
|
+
return signatureHintToMask(hint).substring(47, 52) === key.substring(47, 52)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Find a key by the signature hint
|
|
37
|
+
* @param {Buffer} hint - Hint to look for
|
|
38
|
+
* @param {Array<string>} allKeys - Array of potentially matching keys
|
|
39
|
+
* @return {string|null}
|
|
40
|
+
*/
|
|
41
|
+
export function findKeyBySignatureHint(hint, allKeys) {
|
|
42
|
+
return allKeys.find(key => singatureHintMatchesKey(hint, key))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Find tx signature for a given signer key
|
|
47
|
+
* @param {string} key
|
|
48
|
+
* @param {Signature} allSignatures
|
|
49
|
+
* @returns {Signature}
|
|
50
|
+
*/
|
|
51
|
+
export function findSignatureByKey(key, allSignatures = []) {
|
|
52
|
+
return allSignatures.find(sig => singatureHintMatchesKey(sig.hint(), key))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if the hint matches any of the provided keys
|
|
57
|
+
* @param {Buffer} signature - Tx signature
|
|
58
|
+
* @param {Array<string>} publicKeys - Array of available public keys
|
|
59
|
+
* @returns {Array<string>}
|
|
60
|
+
*/
|
|
61
|
+
export function findKeysBySignatureHint(signature, keys) {
|
|
62
|
+
const mask = signatureHintToMask(signature.hint())
|
|
63
|
+
return keys.filter(address => mask[0] === address[0] &&
|
|
64
|
+
mask.substr(47, 5) === address.substr(47, 5))
|
|
65
|
+
}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import React, {useCallback, useEffect, useState} from 'react'
|
|
2
|
-
import {render} from 'react-dom'
|
|
3
|
-
import ToastNotificationInstance from './toast-notification-instance'
|
|
4
|
-
import Notification from './toast-notification'
|
|
5
|
-
import './toast-notifications.scss'
|
|
6
|
-
|
|
7
|
-
let notificationsCounter = 0
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Initialize toast notifications, add toast notifications container to the DOM, and expose notify() global function
|
|
11
|
-
* @return {HTMLDivElement}
|
|
12
|
-
*/
|
|
13
|
-
export function createToastNotificationsContainer() {
|
|
14
|
-
const container = document.createElement('div')
|
|
15
|
-
document.body.appendChild(container)
|
|
16
|
-
render(<ToastNotificationsBlock/>, container)
|
|
17
|
-
return container
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function ToastNotificationsBlock() {
|
|
21
|
-
const [notifications, setNotifications] = useState([])
|
|
22
|
-
|
|
23
|
-
const deleteNotification = useCallback(function (id) {
|
|
24
|
-
setNotifications(prev => {
|
|
25
|
-
const pos = prev.findIndex(v => v.id === id)
|
|
26
|
-
if (pos < 0)
|
|
27
|
-
return prev
|
|
28
|
-
const res = [...prev]
|
|
29
|
-
res.splice(pos, 1)
|
|
30
|
-
return res
|
|
31
|
-
})
|
|
32
|
-
}, [])
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
//declare globally available notify() function
|
|
36
|
-
/**
|
|
37
|
-
* Show toast notification popup
|
|
38
|
-
* @param {'info'|'success'|'warning'|'error'} type - Notification type
|
|
39
|
-
* @param {
|
|
40
|
-
*/
|
|
41
|
-
window.notify = function ({type, message}) {
|
|
42
|
-
const newNotification = new ToastNotificationInstance({
|
|
43
|
-
type,
|
|
44
|
-
message,
|
|
45
|
-
id: ++notificationsCounter,
|
|
46
|
-
onDelete: deleteNotification
|
|
47
|
-
})
|
|
48
|
-
setNotifications(prevNotifications => [newNotification, ...prevNotifications])
|
|
49
|
-
}
|
|
50
|
-
//set empty callback on unload
|
|
51
|
-
return () => {
|
|
52
|
-
window.notify = function () {
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}, [])
|
|
56
|
-
|
|
57
|
-
return <div className="toast-notifications-container">
|
|
58
|
-
{notifications.map(props => <Notification key={props.id} notification={props}/>)}
|
|
59
|
-
</div>
|
|
1
|
+
import React, {useCallback, useEffect, useState} from 'react'
|
|
2
|
+
import {render} from 'react-dom'
|
|
3
|
+
import ToastNotificationInstance from './toast-notification-instance'
|
|
4
|
+
import Notification from './toast-notification'
|
|
5
|
+
import './toast-notifications.scss'
|
|
6
|
+
|
|
7
|
+
let notificationsCounter = 0
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize toast notifications, add toast notifications container to the DOM, and expose notify() global function
|
|
11
|
+
* @return {HTMLDivElement}
|
|
12
|
+
*/
|
|
13
|
+
export function createToastNotificationsContainer() {
|
|
14
|
+
const container = document.createElement('div')
|
|
15
|
+
document.body.appendChild(container)
|
|
16
|
+
render(<ToastNotificationsBlock/>, container)
|
|
17
|
+
return container
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ToastNotificationsBlock() {
|
|
21
|
+
const [notifications, setNotifications] = useState([])
|
|
22
|
+
|
|
23
|
+
const deleteNotification = useCallback(function (id) {
|
|
24
|
+
setNotifications(prev => {
|
|
25
|
+
const pos = prev.findIndex(v => v.id === id)
|
|
26
|
+
if (pos < 0)
|
|
27
|
+
return prev
|
|
28
|
+
const res = [...prev]
|
|
29
|
+
res.splice(pos, 1)
|
|
30
|
+
return res
|
|
31
|
+
})
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
//declare globally available notify() function
|
|
36
|
+
/**
|
|
37
|
+
* Show toast notification popup
|
|
38
|
+
* @param {'info'|'success'|'warning'|'error'} type - Notification type
|
|
39
|
+
* @param {string} message - Message to show
|
|
40
|
+
*/
|
|
41
|
+
window.notify = function ({type, message}) {
|
|
42
|
+
const newNotification = new ToastNotificationInstance({
|
|
43
|
+
type,
|
|
44
|
+
message,
|
|
45
|
+
id: ++notificationsCounter,
|
|
46
|
+
onDelete: deleteNotification
|
|
47
|
+
})
|
|
48
|
+
setNotifications(prevNotifications => [newNotification, ...prevNotifications])
|
|
49
|
+
}
|
|
50
|
+
//set empty callback on unload
|
|
51
|
+
return () => {
|
|
52
|
+
window.notify = function () {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, [])
|
|
56
|
+
|
|
57
|
+
return <div className="toast-notifications-container">
|
|
58
|
+
{notifications.map(props => <Notification key={props.id} notification={props}/>)}
|
|
59
|
+
</div>
|
|
60
60
|
}
|