@stellar-expert/ui-framework 1.16.8 → 1.19.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.
Files changed (85) hide show
  1. package/README.md +1168 -3
  2. package/account/account-address.js +127 -127
  3. package/account/available-balance.js +22 -22
  4. package/account/identicon.js +90 -90
  5. package/account/signer-key.js +65 -64
  6. package/api/explorer-api-hooks.js +209 -202
  7. package/api/explorer-api-paginated-list-hooks.js +441 -440
  8. package/api/explorer-batch-info-loader.js +111 -85
  9. package/api/explorer-tx-api.js +28 -28
  10. package/api/ledger-stream.js +15 -0
  11. package/asset/amount.js +56 -56
  12. package/asset/asset-icon.js +41 -41
  13. package/asset/asset-issuer.js +21 -21
  14. package/asset/asset-link.js +107 -107
  15. package/asset/asset-list-hooks.js +59 -59
  16. package/asset/asset-meta-hooks.js +88 -88
  17. package/asset/asset-selector.js +72 -71
  18. package/claimable-balance/claimable-balance-claimants.js +23 -18
  19. package/contract/contract-api.js +15 -0
  20. package/contract/invocation-info-view.js +10 -2
  21. package/contract/sc-val.js +131 -107
  22. package/controls/button-group.js +25 -19
  23. package/controls/button.js +93 -78
  24. package/controls/code-block.js +42 -34
  25. package/controls/dropdown.js +318 -287
  26. package/controls/external-link.js +10 -4
  27. package/controls/info-tooltip.js +23 -16
  28. package/controls/slider.js +29 -19
  29. package/controls/tabs.js +94 -94
  30. package/controls/tooltip.js +244 -240
  31. package/controls/update-highlighter.js +32 -27
  32. package/date/date-selector.js +56 -54
  33. package/date/elapsed-time.js +28 -21
  34. package/dex/price-dynamic.js +53 -44
  35. package/directory/directory-hooks.js +109 -97
  36. package/effect/effect-description.js +346 -344
  37. package/errors/error-boundary.js +110 -97
  38. package/filter/editors/account-filter-view.js +20 -0
  39. package/filter/editors/asset-filter-view.js +9 -0
  40. package/filter/editors/timestamp-filter-view.js +34 -0
  41. package/filter/editors/type-filter-view.js +148 -0
  42. package/filter/filter-editors.js +102 -0
  43. package/filter/filter-view.js +218 -0
  44. package/filter/filter.scss +57 -0
  45. package/horizon/horizon-account-helpers.js +104 -104
  46. package/horizon/horizon-ledger-helpers.js +35 -35
  47. package/horizon/horizon-trades-helper.js +88 -88
  48. package/horizon/horizon-transaction-helpers.js +36 -36
  49. package/index.d.ts +1271 -0
  50. package/index.js +95 -93
  51. package/interaction/accordion.js +43 -35
  52. package/interaction/autofocus.js +13 -9
  53. package/interaction/block-select.js +64 -53
  54. package/interaction/copy-to-clipboard.js +25 -18
  55. package/interaction/inline-progress.js +20 -15
  56. package/interaction/qr-code.js +34 -34
  57. package/interaction/responsive.js +20 -20
  58. package/interaction/spoiler.js +52 -39
  59. package/interaction/theme-selector.js +13 -10
  60. package/ledger/ledger-entry-href-formatter.js +27 -26
  61. package/ledger/ledger-entry-link.js +93 -58
  62. package/ledger/ledger-info-parser.js +46 -18
  63. package/meta/page-meta-tags.js +243 -238
  64. package/module/dynamic-module.js +47 -47
  65. package/package.json +71 -40
  66. package/state/on-screen-hooks.js +22 -22
  67. package/state/page-visibility-helpers.js +29 -16
  68. package/state/page-visibility-hooks.js +13 -11
  69. package/state/screen-orientation-hooks.js +19 -15
  70. package/state/state-hooks.js +76 -76
  71. package/state/stellar-network-hooks.js +56 -44
  72. package/state/theme.js +29 -28
  73. package/stellar/key-type.js +92 -91
  74. package/stellar/ledger-generic-id.js +39 -39
  75. package/stellar/signature-hint-utils.js +65 -65
  76. package/toast/toast-notifications-block.js +59 -59
  77. package/tx/op-description-view.js +945 -942
  78. package/tx/op-icon.js +98 -98
  79. package/tx/parser/op-balance-changes.js +66 -66
  80. package/tx/parser/op-descriptor.js +51 -48
  81. package/tx/parser/tx-details-parser.js +81 -81
  82. package/tx/parser/tx-matcher.js +371 -371
  83. package/tx/parser/type-filter-matcher.js +126 -126
  84. package/tx/tx-list-hooks.js +32 -18
  85. package/tx/tx-operations-list.js +117 -116
@@ -1,203 +1,210 @@
1
- import {useEffect, useState} from 'react'
2
- import isEqual from 'react-fast-compare'
3
- import {stringifyQuery} from '@stellar-expert/navigation'
4
- import {addVisibilityChangeListener, isDocumentVisible} from '../state/page-visibility-helpers'
5
- import {useStellarNetwork} from '../state/stellar-network-hooks'
6
- import {fetchExplorerApi} from './explorer-api-call'
7
- import apiCache from './api-cache'
8
-
9
- export class ExplorerApiResult {
10
- constructor(apiEndpoint, data, ts) {
11
- this.apiEndpoint = apiEndpoint
12
- this.data = data
13
- this.fetchedAt = ts
14
- }
15
-
16
- /**
17
- * Relative API URL
18
- * @type {String}
19
- */
20
- apiEndpoint
21
-
22
- /**
23
- * API response data
24
- * @type {Object}
25
- */
26
- data = null
27
-
28
- /**
29
- * Response error if any
30
- * @type {String}
31
- */
32
- error
33
-
34
- /**
35
- * @type {Number}
36
- */
37
- status = 200
38
-
39
- /**
40
- * Response timestamp
41
- * @type {Number}
42
- */
43
- fetchedAt = 0
44
-
45
- /**
46
- * Response result
47
- * @return {Boolean}
48
- */
49
- get loaded() {
50
- return !!this.data || !!this.error
51
- }
52
-
53
- /**
54
- * @private
55
- */
56
- update(data) {
57
- this.data = data
58
- }
59
- }
60
-
61
- function buildApiResult(apiEndpoint, data, ts) {
62
- return new ExplorerApiResult(apiEndpoint, data, ts)
63
- }
64
-
65
- function buildApiError(apiEndpoint, error, status) {
66
- const res = new ExplorerApiResult(apiEndpoint, null, Math.round(new Date().getTime() / 1000))
67
- res.error = error
68
- res.status = status
69
- return res
70
- }
71
-
72
- function setupAutoRefresh(refreshInterval, fetchData) {
73
- if (!refreshInterval) return () => null
74
-
75
- let autoRefreshTimer,
76
- refreshedAt = 0
77
-
78
- function scheduleAutoRefresh() {
79
- autoRefreshTimer = setInterval(() => {
80
- //refresh only when the tab is active
81
- if (isDocumentVisible()) {
82
- const now = new Date().getTime()
83
- if (refreshedAt + (refreshInterval - 1) * 1000 < now) {
84
- refreshedAt = now
85
- fetchData()
86
- }
87
- } else {
88
- stopAutoRefresh()
89
- }
90
- }, refreshInterval * 1000)
91
- }
92
-
93
- function stopAutoRefresh() {
94
- clearInterval(autoRefreshTimer)
95
- autoRefreshTimer = undefined
96
- }
97
-
98
- const stopVisibilityTracking = addVisibilityChangeListener(visible => {
99
- if (visible) {
100
- scheduleAutoRefresh()
101
- } else {
102
- stopAutoRefresh()
103
- }
104
- })
105
- if (isDocumentVisible()) {
106
- scheduleAutoRefresh()
107
- }
108
- //return finalizer
109
- return function () {
110
- refreshInterval = 0
111
- stopVisibilityTracking()
112
- stopAutoRefresh()
113
- }
114
- }
115
-
116
- const currentRequests = {}
117
-
118
- export function fetchData(url, ttl, processResult) {
119
- //try to retrieve data from the browser cache
120
- const fromCache = apiCache.get(url)
121
- //check if the cache is fresh enough
122
- if (fromCache && !fromCache.isExpired) {
123
- //if the cached data is up to date - just proceed with it
124
- return Promise.resolve({data: fromCache.data, ts: fromCache.ts})
125
- }
126
- const existingRequest = currentRequests[url]
127
- if (existingRequest) return existingRequest
128
- //fetch from the server only if there is no data or it is expired
129
- return currentRequests[url] = fetchExplorerApi(url)
130
- .then(data => {
131
- if (typeof processResult === 'function') {
132
- data = processResult(data)
133
- }
134
- //in case of error set small ttl in order to try re-fetching in 4 seconds
135
- const {ts} = apiCache.set(url, data, data.error ? 4 : ttl)
136
- delete currentRequests[url]
137
- return {data, ts}
138
- })
139
- }
140
-
141
- /**
142
- *
143
- * @param {String|APIEndpointParams} apiEndpoint - Server API endpoint to use as a data source.
144
- * @param {Number} [refreshInterval] - Auto-refresh interval in seconds for dynamic data.
145
- * @param {Number} [ttl] - Cache time-to-live in seconds.
146
- * @param {Function} [processResult] - Callback to process a fetch result.
147
- * @param {Boolean} [allowStaleDataTransition] - Allow stale data to be returned when the url changed and the new data has not been loaded yet.
148
- * @return {ExplorerApiResult}
149
- */
150
- export function useExplorerApi(apiEndpoint, {refreshInterval, ttl = 60, processResult, allowStaleDataTransition = false} = {}) {
151
- const network = useStellarNetwork()
152
- const endpointWithQuery = `${network}/${apiEndpoint}`
153
- const [apiResponseData, updateApiResponseData] = useState(buildApiResult(endpointWithQuery))
154
- useEffect(() => {
155
- let componentUnmounted = false
156
- updateApiResponseData(buildApiResult(endpointWithQuery))
157
-
158
- if (!apiEndpoint) {
159
- updateApiResponseData(buildApiError(endpointWithQuery, 'Not found', 404))
160
- return
161
- }
162
-
163
- if (typeof apiEndpoint !== 'string') {
164
- apiEndpoint = apiEndpoint.path + stringifyQuery(apiEndpoint.query)
165
- }
166
-
167
- function load() {
168
- fetchData(endpointWithQuery, ttl, processResult)
169
- .then(({data, ts}) => {
170
- if (componentUnmounted) return
171
-
172
- const newData = buildApiResult(endpointWithQuery, data, ts)
173
-
174
- if (!isEqual(newData, apiResponseData)) {
175
- updateApiResponseData(newData)
176
- }
177
- })
178
- }
179
-
180
- if (!componentUnmounted) {
181
- load()
182
- }
183
-
184
- //set up auto-refresh
185
- const stopAutoRefresh = setupAutoRefresh(refreshInterval, load)
186
-
187
- return () => {
188
- //finalize
189
- componentUnmounted = true
190
- stopAutoRefresh()
191
- }
192
- }, [apiEndpoint])
193
- if (apiResponseData.apiEndpoint !== endpointWithQuery && !allowStaleDataTransition)
194
- return buildApiResult(endpointWithQuery) //return empty result on URL transition if allowStaleDataTransition not set
195
-
196
- return apiResponseData
197
- }
198
-
199
- /**
200
- * @typedef {Object} APIEndpointParams
201
- * @property {String} path
202
- * @property {Object} query
1
+ import {useEffect, useState} from 'react'
2
+ import isEqual from 'react-fast-compare'
3
+ import {stringifyQuery} from '@stellar-expert/navigation'
4
+ import {addVisibilityChangeListener, isDocumentVisible} from '../state/page-visibility-helpers'
5
+ import {useStellarNetwork} from '../state/stellar-network-hooks'
6
+ import {fetchExplorerApi} from './explorer-api-call'
7
+ import apiCache from './api-cache'
8
+
9
+ export class ExplorerApiResult {
10
+ constructor(apiEndpoint, data, ts) {
11
+ this.apiEndpoint = apiEndpoint
12
+ this.data = data
13
+ this.fetchedAt = ts
14
+ }
15
+
16
+ /**
17
+ * Relative API URL
18
+ * @type {string}
19
+ */
20
+ apiEndpoint
21
+
22
+ /**
23
+ * API response data
24
+ * @type {Object}
25
+ */
26
+ data = null
27
+
28
+ /**
29
+ * Response error if any
30
+ * @type {string}
31
+ */
32
+ error
33
+
34
+ /**
35
+ * @type {number}
36
+ */
37
+ status = 200
38
+
39
+ /**
40
+ * Response timestamp
41
+ * @type {number}
42
+ */
43
+ fetchedAt = 0
44
+
45
+ /**
46
+ * Response result
47
+ * @return {boolean}
48
+ */
49
+ get loaded() {
50
+ return !!this.data || !!this.error
51
+ }
52
+
53
+ /**
54
+ * @private
55
+ */
56
+ update(data) {
57
+ this.data = data
58
+ }
59
+ }
60
+
61
+ function buildApiResult(apiEndpoint, data, ts) {
62
+ return new ExplorerApiResult(apiEndpoint, data, ts)
63
+ }
64
+
65
+ function buildApiError(apiEndpoint, error, status) {
66
+ const res = new ExplorerApiResult(apiEndpoint, null, Math.round(new Date().getTime() / 1000))
67
+ res.error = error
68
+ res.status = status
69
+ return res
70
+ }
71
+
72
+ function setupAutoRefresh(refreshInterval, fetchData) {
73
+ if (!refreshInterval) return () => null
74
+
75
+ let autoRefreshTimer,
76
+ refreshedAt = 0
77
+
78
+ function scheduleAutoRefresh() {
79
+ autoRefreshTimer = setInterval(() => {
80
+ //refresh only when the tab is active
81
+ if (isDocumentVisible()) {
82
+ const now = new Date().getTime()
83
+ if (refreshedAt + (refreshInterval - 1) * 1000 < now) {
84
+ refreshedAt = now
85
+ fetchData()
86
+ }
87
+ } else {
88
+ stopAutoRefresh()
89
+ }
90
+ }, refreshInterval * 1000)
91
+ }
92
+
93
+ function stopAutoRefresh() {
94
+ clearInterval(autoRefreshTimer)
95
+ autoRefreshTimer = undefined
96
+ }
97
+
98
+ const stopVisibilityTracking = addVisibilityChangeListener(visible => {
99
+ if (visible) {
100
+ scheduleAutoRefresh()
101
+ } else {
102
+ stopAutoRefresh()
103
+ }
104
+ })
105
+ if (isDocumentVisible()) {
106
+ scheduleAutoRefresh()
107
+ }
108
+ //return finalizer
109
+ return function () {
110
+ refreshInterval = 0
111
+ stopVisibilityTracking()
112
+ stopAutoRefresh()
113
+ }
114
+ }
115
+
116
+ const currentRequests = {}
117
+
118
+ /**
119
+ * Fetch data from the Explorer API with caching support
120
+ * @param {string} url - API endpoint URL (with network prefix)
121
+ * @param {number} [ttl] - Cache time-to-live in seconds
122
+ * @param {function(*): *} [processResult] - Optional callback to transform the response data
123
+ * @return {Promise<{data: *, ts: number}>}
124
+ */
125
+ export function fetchData(url, ttl, processResult) {
126
+ //try to retrieve data from the browser cache
127
+ const fromCache = apiCache.get(url)
128
+ //check if the cache is fresh enough
129
+ if (fromCache && !fromCache.isExpired) {
130
+ //if the cached data is up to date - just proceed with it
131
+ return Promise.resolve({data: fromCache.data, ts: fromCache.ts})
132
+ }
133
+ const existingRequest = currentRequests[url]
134
+ if (existingRequest) return existingRequest
135
+ //fetch from the server only if there is no data or it is expired
136
+ return currentRequests[url] = fetchExplorerApi(url)
137
+ .then(data => {
138
+ if (typeof processResult === 'function') {
139
+ data = processResult(data)
140
+ }
141
+ //in case of error set small ttl in order to try re-fetching in 4 seconds
142
+ const {ts} = apiCache.set(url, data, data.error ? 4 : ttl)
143
+ delete currentRequests[url]
144
+ return {data, ts}
145
+ })
146
+ }
147
+
148
+ /**
149
+ *
150
+ * @param {string|APIEndpointParams} apiEndpoint - Server API endpoint to use as a data source.
151
+ * @param {number} [refreshInterval] - Auto-refresh interval in seconds for dynamic data.
152
+ * @param {number} [ttl] - Cache time-to-live in seconds.
153
+ * @param {function} [processResult] - Callback to process a fetch result.
154
+ * @param {boolean} [allowStaleDataTransition] - Allow stale data to be returned when the url changed and the new data has not been loaded yet.
155
+ * @return {ExplorerApiResult}
156
+ */
157
+ export function useExplorerApi(apiEndpoint, {refreshInterval, ttl = 60, processResult, allowStaleDataTransition = false} = {}) {
158
+ const network = useStellarNetwork()
159
+ const endpointWithQuery = `${network}/${apiEndpoint}`
160
+ const [apiResponseData, updateApiResponseData] = useState(buildApiResult(endpointWithQuery))
161
+ useEffect(() => {
162
+ let componentUnmounted = false
163
+ updateApiResponseData(buildApiResult(endpointWithQuery))
164
+
165
+ if (!apiEndpoint) {
166
+ updateApiResponseData(buildApiError(endpointWithQuery, 'Not found', 404))
167
+ return
168
+ }
169
+
170
+ if (typeof apiEndpoint !== 'string') {
171
+ apiEndpoint = apiEndpoint.path + stringifyQuery(apiEndpoint.query)
172
+ }
173
+
174
+ function load() {
175
+ fetchData(endpointWithQuery, ttl, processResult)
176
+ .then(({data, ts}) => {
177
+ if (componentUnmounted) return
178
+
179
+ const newData = buildApiResult(endpointWithQuery, data, ts)
180
+
181
+ if (!isEqual(newData, apiResponseData)) {
182
+ updateApiResponseData(newData)
183
+ }
184
+ })
185
+ }
186
+
187
+ if (!componentUnmounted) {
188
+ load()
189
+ }
190
+
191
+ //set up auto-refresh
192
+ const stopAutoRefresh = setupAutoRefresh(refreshInterval, load)
193
+
194
+ return () => {
195
+ //finalize
196
+ componentUnmounted = true
197
+ stopAutoRefresh()
198
+ }
199
+ }, [apiEndpoint])
200
+ if (apiResponseData.apiEndpoint !== endpointWithQuery && !allowStaleDataTransition)
201
+ return buildApiResult(endpointWithQuery) //return empty result on URL transition if allowStaleDataTransition not set
202
+
203
+ return apiResponseData
204
+ }
205
+
206
+ /**
207
+ * @typedef {Object} APIEndpointParams
208
+ * @property {string} path
209
+ * @property {{}} query
203
210
  */