@stellar-expert/ui-framework 1.14.14 → 1.14.16

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.
@@ -1,7 +1,7 @@
1
1
  import {useRef} from 'react'
2
2
  import isEqual from 'react-fast-compare'
3
3
  import {parseQuery, stringifyQuery, navigation} from '@stellar-expert/navigation'
4
- import {getCurrentStellarNetwork, useStellarNetwork} from '../state/stellar-network-hooks'
4
+ import {useStellarNetwork} from '../state/stellar-network-hooks'
5
5
  import {useDependantState} from '../state/state-hooks'
6
6
  import {fetchExplorerApi} from './explorer-api-call'
7
7
  import apiCache from './api-cache'
@@ -0,0 +1,101 @@
1
+ import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
2
+ import {fetchExplorerApi} from './explorer-api-call'
3
+
4
+ class LedgerStream {
5
+ constructor() {
6
+ this.listeners = new Set()
7
+ }
8
+
9
+ /**
10
+ * @type {boolean}
11
+ * @private
12
+ */
13
+ listening = false
14
+ /**
15
+ * @type {Set<function>}
16
+ * @private
17
+ */
18
+ listeners
19
+
20
+ /**
21
+ * @param {function} listener
22
+ */
23
+ on(listener) {
24
+ this.listeners.add(listener)
25
+ if (!this.listening) {
26
+ this.listening = true
27
+ setTimeout(() => this.waitForLedger(), 100)
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @param {function} listener
33
+ */
34
+ off(listener) {
35
+ this.listeners.delete(listener)
36
+ if (!this.listeners.size) {
37
+ this.listening = false
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @param {number} ledger
43
+ * @private
44
+ */
45
+ notify(ledger) {
46
+ for (const listener of this.listeners) {
47
+ listener(ledger)
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @return {Promise}
53
+ * @private
54
+ */
55
+ async waitForLedger() {
56
+ const url = `${explorerApiOrigin}/explorer/${getCurrentStellarNetwork()}/ledger/stream`
57
+ try {
58
+ const resp = await fetch(url, {keepalive: true, cache: 'no-cache'})
59
+ if (!resp.ok) {
60
+ let errorExt
61
+ try {
62
+ errorExt = await resp.json()
63
+ } catch (parsingError) {
64
+ errorExt = {}
65
+ }
66
+ const err = new Error(errorExt?.error || resp.statusText || 'Failed to fetch data from the server')
67
+ err.status = resp.status
68
+ err.ext = errorExt
69
+ throw err
70
+ }
71
+ const {ledger} = await resp.json()
72
+ this.notify(ledger)
73
+ if (this.listening) {
74
+ setTimeout(() => this.waitForLedger(), 1000)
75
+ }
76
+ } catch (e) {
77
+ console.error(e)
78
+ if (e instanceof Error) {
79
+ e = {
80
+ error: e.message,
81
+ status: e.status || 500,
82
+ ext: e.ext
83
+ }
84
+ }
85
+ if (e.ext && e.ext.status) {
86
+ e.status = e.ext.status
87
+ }
88
+ return e
89
+ }
90
+ }
91
+
92
+ async getLast() {
93
+ return fetchExplorerApi('ledger/last')
94
+ }
95
+
96
+ async getLastSequence() {
97
+ return (await this.getLast()).sequence
98
+ }
99
+ }
100
+
101
+ export const ledgerStream = new LedgerStream()
package/index.js CHANGED
@@ -19,6 +19,7 @@ export * from './api/explorer-api-hooks'
19
19
  export * from './api/explorer-api-paginated-list-hooks'
20
20
  export * from './api/explorer-tx-api'
21
21
  export * from './api/explorer-batch-info-loader'
22
+ export * from './api/ledger-stream'
22
23
  //Horizon API binding and utils
23
24
  export * from './stellar/ledger-generic-id'
24
25
  export * from './horizon/horizon-client-helpers'
@@ -59,6 +60,7 @@ export * from './date/date-selector'
59
60
  //ledger-entries-related components
60
61
  export * from './ledger/ledger-entry-link'
61
62
  export * from './ledger/ledger-entry-href-formatter'
63
+ export * from './ledger/ledger-info-parser'
62
64
  //account-related components
63
65
  export * from './account/identicon'
64
66
  export * from './account/account-address'
@@ -0,0 +1,17 @@
1
+ import {xdr} from '@stellar/stellar-base'
2
+
3
+ export function retrieveLedgerInfo(data) {
4
+ const parsed = xdr.LedgerHeader.fromXDR(data.xdr, 'base64')
5
+ return {
6
+ sequence: data.sequence,
7
+ ts: data.ts,
8
+ protocol: data.protocol,
9
+ operations: data.operations || 0,
10
+ txSuccess: data.txSuccess,
11
+ txFailed: data.txFailed,
12
+ xlm: parsed.totalCoins().toBigInt(),
13
+ feePool: parsed.feePool().toBigInt(),
14
+ baseFee: parsed.baseFee(),
15
+ baseReserve: parsed.baseReserve()
16
+ }
17
+ }
@@ -1,23 +1,32 @@
1
1
  import {useEffect} from 'react'
2
2
  import isEqual from 'react-fast-compare'
3
3
 
4
- const {origin} = window.location
5
- const domain = origin.replace(/https?:\/\//, '')
6
-
7
4
  const metaProps = {
8
5
  serviceTitle: '',
9
6
  description: '',
10
- facebookImage: '',
11
- twitterImage: '',
12
- twitterUsername: ''
7
+ image: '',
8
+ imageEndpoint: '',
9
+ origin: '',
10
+ domain: ''
13
11
  }
14
12
 
13
+ setOrigin(window.location.origin)
14
+
15
15
  /**
16
16
  * Initialize default meta properties for the application
17
- * @param {{serviceTitle: String, description: String, facebookImage: String, twitterImage: String, twitterUsername: String}} appMetaProps
17
+ * @param {{serviceTitle: String, description: String, origin: String, [image]: String, [imageEndpoint]: String}} appMetaProps
18
18
  */
19
19
  export function initMeta(appMetaProps) {
20
- Object.assign(metaProps, appMetaProps)
20
+ const {origin, ...props} = appMetaProps
21
+ Object.assign(metaProps, props)
22
+ if (origin) {
23
+ setOrigin(origin)
24
+ }
25
+ }
26
+
27
+ function setOrigin(origin) {
28
+ metaProps.origin = origin
29
+ metaProps.domain = origin.replace(/https?:\/\//, '')
21
30
  }
22
31
 
23
32
  function formatPageTitle(title) {
@@ -47,48 +56,35 @@ function generateDescriptionMeta({description}) {
47
56
  }
48
57
  }
49
58
 
50
- function generateTwitterMeta({description, title, twitterImage}) {
59
+ function generateOpenGraphMeta({description, title, image}, canonicalUrl) {
60
+ //imageEndpoint
51
61
  return {
52
- locator: 'name',
62
+ locator: 'property',
53
63
  tags: [
54
- {name: 'twitter:card', content: 'summary_large_image'},
55
- {name: 'twitter:site', content: metaProps.twitterUsername},
56
- {name: 'twitter:title', content: formatPageTitle(title)},
57
- {name: 'twitter:description', content: description || metaProps.description},
58
- {name: 'twitter:image:src', content: twitterImage || metaProps.twitterImage}
64
+ {name: 'og:title', content: formatPageTitle(title)},
65
+ {name: 'og:url', content: canonicalUrl},
66
+ {name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
67
+ {name: 'og:description', content: description || metaProps.description},
68
+ {name: 'og:type', content: 'website'},
69
+ {name: 'og:image:width', content: 1200},
70
+ {name: 'og:image:height', content: 630},
71
+ {name: 'og:image', content: formatPageImage(image, canonicalUrl)}
59
72
  ]
60
73
  }
61
74
  }
62
75
 
63
- function generateOpenGraphMeta({description, title, facebookImage}, canonicalUrl) {
64
- let tags = [
65
- {name: 'og:title', content: formatPageTitle(title)},
66
- {name: 'og:url', content: canonicalUrl},
67
- {name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
68
- {name: 'og:description', content: description || metaProps.description},
69
- {name: 'og:type', content: 'website'},
70
- {name: 'og:image:width', content: 1200},
71
- {name: 'og:image:height', content: 630},
72
- {name: 'og:image', content: facebookImage || metaProps.facebookImage}
73
- ]
74
- return {
75
- locator: 'property',
76
- tags
77
- }
78
- }
79
-
80
76
  function generateItemPropSchema({description, title, image}) {
81
77
  return {
82
78
  locator: 'itemprop',
83
79
  tags: [
84
80
  {name: 'name', content: title},
85
81
  {name: 'description', content: description || metaProps.description},
86
- {name: 'image', content: image || metaProps.facebookImage}
82
+ {name: 'image', content: image || metaProps.image}
87
83
  ]
88
84
  }
89
85
  }
90
86
 
91
- function generateLdJsonSchema({title}) {
87
+ function generateLdJsonSchema({title, description, image}, canonicalUrl) {
92
88
  return {
93
89
  tag: 'script',
94
90
  locator: 'type',
@@ -97,10 +93,15 @@ function generateLdJsonSchema({title}) {
97
93
  name: 'application/ld+json',
98
94
  content: JSON.stringify({
99
95
  '@context': 'http://schema.org',
100
- '@type': 'WebSite',
101
- 'name': domain,
102
- 'alternateName': title,
103
- 'url': origin
96
+ '@type': 'WebPage',
97
+ name: formatPageTitle(title),
98
+ description: description || metaProps.description,
99
+ url: canonicalUrl,
100
+ thumbnailUrl: formatPageImage(image, canonicalUrl),
101
+ publisher: {
102
+ '@type': 'ProfilePage',
103
+ name: metaProps.serviceTitle
104
+ }
104
105
  })
105
106
  }
106
107
  ]
@@ -145,23 +146,37 @@ function removeTag(selector) {
145
146
  }
146
147
  }
147
148
 
149
+ function formatCanonicalUrl() {
150
+ return metaProps.origin + window.location.pathname// + location.search
151
+ }
152
+
153
+ function formatPageImage(image, canonicalUrl) {
154
+ if (image)
155
+ return image
156
+ if (metaProps.imageEndpoint)
157
+ return metaProps.imageEndpoint + canonicalUrl.replace(metaProps.origin, '')
158
+ return metaProps.image
159
+ }
160
+
148
161
  let pageMeta = {}
149
162
 
150
- const tagReplacerPipeline = [generateCanonicalLink, generateDescriptionMeta, generateOpenGraphMeta, generateTwitterMeta, generateLdJsonSchema] // generateItemPropSchema
163
+ const tagReplacerPipeline = [
164
+ generateCanonicalLink,
165
+ generateDescriptionMeta,
166
+ generateOpenGraphMeta,
167
+ generateLdJsonSchema
168
+ ] // generateItemPropSchema
151
169
 
152
170
  /**
153
171
  * Update page metadata tags
154
172
  * @param {PageMeta} meta - Page metadata
155
173
  */
156
- export function setPageMetadata(meta) {
174
+ function setPageMetadata(meta) {
157
175
  if (isEqual(pageMeta, meta))
158
176
  return
159
- const canonicalUrl = origin + location.pathname// + location.search
177
+ //generate canonical URL
178
+ const canonicalUrl = formatCanonicalUrl()
160
179
  document.title = formatPageTitle(meta.title)
161
- if (meta.image) {
162
- meta.facebookImage = meta.image
163
- meta.twitterImage = meta.image
164
- }
165
180
  for (const replacer of tagReplacerPipeline) {
166
181
  replaceMetaTags(replacer(meta, canonicalUrl))
167
182
  }
@@ -175,12 +190,11 @@ export function setPageMetadata(meta) {
175
190
  * Reset page metadata tags to their default values
176
191
  * @param {PageMeta} meta - Page metadata
177
192
  */
178
- export function resetPageMetadata(meta) {
193
+ function resetPageMetadata(meta) {
179
194
  setPageMetadata({
180
195
  title: metaProps.serviceTitle,
181
196
  description: metaProps.description,
182
- twitterImage: metaProps.twitterImage,
183
- facebookImage: metaProps.facebookImage
197
+ image: metaProps.image
184
198
  })
185
199
  //TODO: add logic to cleanup custom page meta tags on page unload
186
200
  }
@@ -188,22 +202,32 @@ export function resetPageMetadata(meta) {
188
202
  /**
189
203
  * React hook for setting page metadata
190
204
  * @param {PageMeta} meta - Page metadata
191
- * @param {*[]} dependencies
192
205
  */
193
- export function usePageMetadata(meta, dependencies = []) {
206
+ export function usePageMetadata(meta) {
194
207
  useEffect(() => {
195
208
  setPageMetadata(meta)
196
209
  return () => resetPageMetadata(meta)
197
- }, [JSON.stringify(meta), ...dependencies])
210
+ }, [JSON.stringify(meta), [formatCanonicalUrl()]])
211
+ }
212
+
213
+ export function setPageNoIndex(noIndex) {
214
+ if (!noIndex) {
215
+ removeTag('meta[name=robots]')
216
+ } else {
217
+ replaceMetaTags({
218
+ locator: 'name',
219
+ tags: [
220
+ {name: 'robots', content: 'noindex,nofollow'}
221
+ ]
222
+ })
223
+ }
198
224
  }
199
225
 
200
226
  /**
201
227
  * @typedef {Object} PageMeta
202
228
  * @property {String} title - Page title
203
229
  * @property {String} description - Contents description
204
- * @property {String} [twitterImage] - Twitter image url
205
- * @property {String} [facebookImage] - Facebook image url
206
- * @property {String} [image] - Sets the image for both the Twitter image and the Facebook image
230
+ * @property {String} [image] - Page image url
207
231
  * @property {MetaTagReplacement} [customMeta] - Custom metadata tags
208
232
  */
209
233
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-expert/ui-framework",
3
- "version": "1.14.14",
3
+ "version": "1.14.16",
4
4
  "description": "StellarExpert shared UI components library",
5
5
  "main": "index.js",
6
6
  "module": "./index.js",