@lukso/transaction-view-core 0.5.0-dev.f500300 → 0.5.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 (44) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/dist/index.cjs +220 -131
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +10 -1
  5. package/dist/index.d.ts +10 -1
  6. package/dist/index.js +252 -158
  7. package/dist/index.js.map +1 -1
  8. package/dist/shared/index.cjs +64 -31
  9. package/dist/shared/index.cjs.map +1 -1
  10. package/dist/shared/index.d.cts +20 -8
  11. package/dist/shared/index.d.ts +20 -8
  12. package/dist/shared/index.js +76 -43
  13. package/dist/shared/index.js.map +1 -1
  14. package/dist/styles/index.cjs +1 -1
  15. package/dist/styles/index.cjs.map +1 -1
  16. package/dist/styles/index.js +1 -1
  17. package/dist/styles/index.js.map +1 -1
  18. package/package.json +4 -5
  19. package/src/components/asset-card.ts +80 -63
  20. package/src/components/contract-card.ts +67 -2
  21. package/src/components/info-panel.ts +21 -1
  22. package/src/components/permissions-block.ts +5 -7
  23. package/src/components/transaction-playback.ts +4 -4
  24. package/src/shared/address-display.ts +6 -3
  25. package/src/shared/address-view.ts +18 -2
  26. package/src/shared/custom-select.ts +1 -1
  27. package/src/shared/function-name-view.ts +1 -1
  28. package/src/shared/gas-provider-view.ts +7 -4
  29. package/src/shared/icon-view.ts +9 -7
  30. package/src/shared/image-list.ts +4 -3
  31. package/src/shared/load-web-components.ts +2 -3
  32. package/src/shared/notification-view.ts +3 -3
  33. package/src/shared/progress-view.ts +10 -1
  34. package/src/shared/scroll-to-bottom-button.ts +1 -1
  35. package/src/shared/signal-controller.ts +1 -3
  36. package/src/shared/transaction-view-controller.ts +1 -1
  37. package/src/shared/url-view.ts +3 -3
  38. package/src/shared/value-view.ts +30 -10
  39. package/src/styles/styles.css +1 -1
  40. package/src/vue-entry.ts +1 -0
  41. package/tsup.config.ts +3 -3
  42. package/.turbo/turbo-typecheck.log +0 -4
  43. package/src/utils.test.ts +0 -47
  44. package/src/utils.ts +0 -82
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukso/transaction-view-core",
3
- "version": "0.5.0-dev.f500300",
3
+ "version": "0.5.0",
4
4
  "description": "Core components and hooks for LUKSO transaction display",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -35,8 +35,8 @@
35
35
  "@preact/signals-core": "^1.12.1",
36
36
  "ethereum-blockies-base64": "^1.0.2",
37
37
  "lit": "^3.3.1",
38
- "@lukso/transaction-decoder": "1.3.0-dev.f500300",
39
- "@lukso/transaction-view-headless": "0.4.0-dev.f500300"
38
+ "@lukso/transaction-decoder": "1.3.0",
39
+ "@lukso/transaction-view-headless": "0.4.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@tailwindcss/typography": "^0.5.19",
@@ -52,7 +52,6 @@
52
52
  "build": "tsup",
53
53
  "dev": "tsup --watch",
54
54
  "dev-alt": "tsup --watch",
55
- "clean": "rm -rf dist",
56
- "typecheck": "tsc --noEmit"
55
+ "clean": "rm -rf dist"
57
56
  }
58
57
  }
@@ -11,12 +11,26 @@ import '../shared/image-list.js'
11
11
  import '@lukso/web-components'
12
12
  import { Task } from '@lit/task'
13
13
  import {
14
- type ResolvedAddress,
14
+ ResolvedAddress,
15
15
  useAddressResolver,
16
16
  } from '@lukso/transaction-view-headless'
17
17
  import { safeCustomElement } from '../shared/safe-custom-element.js'
18
18
  import { CoreLitElement } from '../styles'
19
- import { formatUnits, parseBigInt } from '../utils.js'
19
+
20
+ // Value formatting utilities
21
+ function formatUnits(value: bigint, decimals = 18): string {
22
+ const divisor = 10n ** BigInt(decimals)
23
+ const quotient = value / divisor
24
+ const remainder = value % divisor
25
+
26
+ if (remainder === 0n) {
27
+ return quotient.toString()
28
+ }
29
+
30
+ const remainderStr = remainder.toString().padStart(decimals, '0')
31
+ const trimmed = remainderStr.replace(/0+$/, '')
32
+ return `${quotient}.${trimmed}`
33
+ }
20
34
 
21
35
  /**
22
36
  * Asset card component for displaying token/NFT information
@@ -44,6 +58,9 @@ export class AssetCard extends CoreLitElement {
44
58
  @property({ type: String, attribute: 'function-icon' })
45
59
  functionIcon: string = 'code-outline'
46
60
 
61
+ @property({ type: String })
62
+ size: 'small' | 'medium' | 'large' = 'medium'
63
+
47
64
  @property({ type: Boolean, attribute: 'show-info-button' })
48
65
  showInfoButton = true
49
66
 
@@ -56,6 +73,13 @@ export class AssetCard extends CoreLitElement {
56
73
  @state()
57
74
  private _resolvedData?: ResolvedAddress
58
75
 
76
+ @state()
77
+ private _imageError = false
78
+
79
+ private _handleImageError() {
80
+ this._imageError = true
81
+ }
82
+
59
83
  private _handleInfoClick() {
60
84
  this.displayDetails = !this.displayDetails
61
85
  }
@@ -79,6 +103,19 @@ export class AssetCard extends CoreLitElement {
79
103
  return addressOrComposite.split('-')[0]
80
104
  }
81
105
 
106
+ private _extractTokenId(addressOrComposite: string): string | undefined {
107
+ const parts = addressOrComposite.split('-')
108
+ return parts.length > 1 ? parts[1] : undefined
109
+ }
110
+
111
+ private _copyToClipboard(text: string) {
112
+ if (navigator.clipboard) {
113
+ navigator.clipboard.writeText(text)
114
+ // Could dispatch a 'copied' event here for toast notifications
115
+ this.dispatchEvent(new CustomEvent('copied', { detail: { text } }))
116
+ }
117
+ }
118
+
82
119
  private _renderAddressSection() {
83
120
  const data = this._resolutionTask.value
84
121
  if (!data) return ''
@@ -194,7 +231,9 @@ export class AssetCard extends CoreLitElement {
194
231
  const displayTokenId = formattedTokenId ? `${formattedTokenId}` : ''
195
232
  if (displayTokenId) return displayTokenId
196
233
  // If its a collection, do not show tokenId
197
- return this.tokenId ? `${parseInt(this.tokenId, 16)}` : undefined
234
+ return this.tokenId && !this._resolvedData?.isCollection
235
+ ? `${parseInt(this.tokenId, 16)}`
236
+ : undefined
198
237
  }
199
238
 
200
239
  private _renderTokenId() {
@@ -259,7 +298,6 @@ export class AssetCard extends CoreLitElement {
259
298
  private _renderImages() {
260
299
  const images = (this._resolvedData as any)?.images || []
261
300
  if (images.length === 0) return ''
262
- if (!this.address) return ''
263
301
 
264
302
  return html`
265
303
  <image-list
@@ -274,7 +312,6 @@ export class AssetCard extends CoreLitElement {
274
312
  private _renderAssets() {
275
313
  const assets = (this._resolvedData as any)?.assets || []
276
314
  if (assets.length === 0) return ''
277
- if (!this.address) return ''
278
315
 
279
316
  return html`
280
317
  <div class="section">
@@ -292,11 +329,11 @@ export class AssetCard extends CoreLitElement {
292
329
  return html`
293
330
  <div class="flex justify-between items-center my-4">
294
331
  <function-name-view
295
- label=${this.functionName || 'Execute'}
296
- size="small"
297
- icon=${this.functionIcon ? this.functionIcon : 'code-outline'}
332
+ label=${this.functionName}
333
+ size="small"
334
+ icon=${this.functionIcon ? this.functionIcon : 'code-outline'}
298
335
  ></function-name-view>
299
- <function-name-view label=${this._resolvedData?.standard ?? 'Unknown'} size="small"></function-name-view>
336
+ <function-name-view label=${this._resolvedData?.standard} size="small"></function-name-view>
300
337
  </div>
301
338
  `
302
339
  }
@@ -317,15 +354,6 @@ export class AssetCard extends CoreLitElement {
317
354
  `
318
355
  }
319
356
 
320
- private _formattedAmount() {
321
- const data = this._resolutionTask.value
322
- const amt = this.amount
323
- if (amt === undefined || amt === null || amt === '') return undefined
324
- const bigintAmount = parseBigInt(amt as any)
325
- if (bigintAmount === undefined) return undefined
326
- return formatUnits(bigintAmount, data?.decimals)
327
- }
328
-
329
357
  private renderFormattedTokenIdBadge() {
330
358
  if (!this.displayedTokenId) return ''
331
359
  return html`
@@ -337,65 +365,54 @@ export class AssetCard extends CoreLitElement {
337
365
  return html`
338
366
  <div >
339
367
  ${this._renderStandards()}
340
- <div class="mt-6 flex-col">
341
- <div class="flex items-center justify-center gap-4">
342
- <div class="flex justify-end w-[48%]">
343
- <div class="border relative border-neutral-90 rounded-xl">
344
- ${this.renderFormattedTokenIdBadge()}
345
- <div class="p-2 relative">
346
- ${when(
347
- this.address,
348
- () => html`
349
- <icon-view
350
- address=${this.address ?? ''}
351
- size="2x-large"
352
- .showName=${false}
353
- ></icon-view>
354
- `
355
- )}
356
- </div>
357
- <div style="height: 1px;" class="bg-neutral-90 w-full"></div>
358
- <div class="px-2">
359
- ${this._renderAddressSection()}
360
- </div>
368
+ <div class="mt-6 flex-col">
369
+ <div class="flex items-center justify-center gap-4">
370
+ <div class="flex justify-end w-[48%]">
371
+ <div class="border relative border-neutral-90 rounded-xl">
372
+ ${this.renderFormattedTokenIdBadge()}
373
+ <div class="p-2 relative">
374
+ <icon-view
375
+ address="${this.address}"
376
+ size="2x-large"
377
+ .showName=${false}
378
+ ></icon-view>
379
+ </div>
380
+ <div style="height: 1px;" class="bg-neutral-90 w-full"></div>
381
+ <div class="px-2">
382
+ ${this._renderAddressSection()}
361
383
  </div>
362
384
  </div>
385
+ </div>
363
386
 
364
- <div class="flex flex-col gap-1 w-[51%]">
365
- <div class="text-[14px] font-bold text-neutral-20">
366
- ${this._resolvedData?.lsp4TokenName || 'UNKNOWN'}
367
- </div>
368
- <div class="text-[17px] font-semibold text-neutral-20">
369
- ${when(
370
- this.amount,
371
- () => html`
372
- <span class="">${this._formattedAmount() ?? ''}</span>
373
- <span class="ml-1 text-neutral-60 max-w-[60px] whitespace-nowrap overflow-hidden text-ellipsis text-[14px] font-semibold">${
374
- this._resolvedData?.lsp4TokenSymbol || ''
375
- }</span>
376
- `,
377
- () => html`${this._resolvedData?.lsp4TokenSymbol || ''}`
378
- )}
379
- </div>
380
-
387
+ <div class="flex flex-col gap-1 w-[51%]">
388
+ <div class="text-[14px] font-bold text-neutral-20">
389
+ ${this._resolvedData?.lsp4TokenName || 'UNKNOWN'}
390
+ </div>
391
+ <div class="text-[17px] font-semibold text-neutral-20">
381
392
  ${when(
382
393
  this.amount,
383
394
  () => html`
384
- <span class="">${formatUnits(BigInt(this.amount || 0), this._resolvedData?.decimals)}</span>
395
+ <span class="">${formatUnits(BigInt(this.amount!), this._resolvedData?.decimals)}</span>
385
396
  <span class="ml-1 text-neutral-60 max-w-[60px] whitespace-nowrap overflow-hidden text-ellipsis text-[14px] font-semibold">${
386
397
  this._resolvedData?.lsp4TokenSymbol || ''
387
398
  }</span>
388
399
  `,
389
400
  () => html`${this._resolvedData?.lsp4TokenSymbol || ''}`
390
401
  )}
391
- <div class="mt-4">
392
- ${when(this.showCreator, () => this._renderCreatorSection())}
393
- </div>
402
+ </div>
403
+
404
+ ${when(
405
+ this.displayedTokenId,
406
+ () =>
407
+ html`<div class="text-[14px] font-medium text-neutral-60"><span class="font-bold">#${this.displayedTokenId}</span></div>`
408
+ )}
409
+ <div class="mt-4">
410
+ ${when(this.showCreator, () => this._renderCreatorSection())}
394
411
  </div>
395
412
  </div>
396
- <div class="border-neutral-90 pt-2">
397
- ${this._renderTokenDetails()}
398
- </div>
413
+ </div>
414
+ <div class="border-neutral-90 pt-2">
415
+ ${this._renderTokenDetails()}
399
416
  </div>
400
417
  </div>
401
418
  `
@@ -1,5 +1,10 @@
1
- import type { ResolvedAddress } from '@lukso/transaction-view-headless'
2
- import { css, html, LitElement } from 'lit'
1
+ import { Task } from '@lit/task'
2
+ import {
3
+ type ResolvedAddress,
4
+ useAddressResolver,
5
+ useImageLoader,
6
+ } from '@lukso/transaction-view-headless'
7
+ import { css, html, LitElement, nothing } from 'lit'
3
8
  import { property, state } from 'lit/decorators.js'
4
9
  import { safeCustomElement } from '../shared/safe-custom-element'
5
10
  import '../shared/load-web-components'
@@ -110,6 +115,66 @@ export class ContractCard extends LitElement {
110
115
  @state() private _profileImageUrl?: string
111
116
  @state() private _backgroundImageUrl?: string
112
117
 
118
+ private _resolutionTask = new Task(this, {
119
+ task: async ([address, resolved]) => {
120
+ if (resolved) {
121
+ this._resolvedData = resolved
122
+ return resolved
123
+ }
124
+
125
+ const { resolveAddress } = useAddressResolver()
126
+ const baseAddress = this._extractBaseAddress(address as string)
127
+ const data = await resolveAddress(baseAddress)
128
+ this._resolvedData = data
129
+ return data
130
+ },
131
+ args: () => [this.address, this.resolved] as const,
132
+ })
133
+
134
+ private _profileImageTask = new Task(this, {
135
+ task: async ([resolved]) => {
136
+ const { loadProfileImage } = useImageLoader()
137
+ const data = resolved || this._resolvedData
138
+
139
+ if (!data) return null
140
+
141
+ const image = await loadProfileImage(data, {
142
+ width: 256,
143
+ height: 256,
144
+ dpr: window.devicePixelRatio || 1,
145
+ })
146
+
147
+ if (image?.src) {
148
+ this._profileImageUrl = image.src
149
+ }
150
+
151
+ return image
152
+ },
153
+ args: () => [this._resolvedData] as const,
154
+ })
155
+
156
+ private _backgroundImageTask = new Task(this, {
157
+ task: async ([resolved]) => {
158
+ const { loadBackgroundImage } = useImageLoader()
159
+ const data = resolved || this._resolvedData
160
+
161
+ if (!data) return null
162
+
163
+ const image = await loadBackgroundImage(data, {
164
+ width: 800,
165
+ height: 200,
166
+ dpr: window.devicePixelRatio || 1,
167
+ })
168
+
169
+ if (image?.src) {
170
+ this._backgroundImageUrl = image.src
171
+ }
172
+
173
+ return image
174
+ },
175
+ args: () => [this._resolvedData] as const,
176
+ })
177
+
113
178
  private _extractBaseAddress(addressOrComposite: string): string {
114
179
  return addressOrComposite.split('-')[0]
115
180
  }
@@ -1,4 +1,8 @@
1
- import type { ResolvedAddress } from '@lukso/transaction-view-headless'
1
+ import { Task } from '@lit/task'
2
+ import {
3
+ type ResolvedAddress,
4
+ useAddressResolver,
5
+ } from '@lukso/transaction-view-headless'
2
6
  import { css, html, LitElement } from 'lit'
3
7
  import { property, state } from 'lit/decorators.js'
4
8
  import { classMap } from 'lit/directives/class-map.js'
@@ -30,9 +34,25 @@ export class InfoPanel extends LitElement {
30
34
  @property({ type: String })
31
35
  type: 'transfer' | 'mint' | 'burn' | 'approve' = 'transfer'
32
36
 
37
+ @state()
38
+ private _isAnimating = false
39
+
33
40
  @state()
34
41
  private _resolvedData?: ResolvedAddress
35
42
 
43
+ private _resolutionTask = new Task(this, {
44
+ task: async ([address]) => {
45
+ if (!address) return null
46
+
47
+ const { resolveAddress } = useAddressResolver()
48
+ // Resolve the full address (including tokenId if present) to get token-specific data
49
+ const data = await resolveAddress(address as string)
50
+ this._resolvedData = data
51
+ return data
52
+ },
53
+ args: () => [this.address] as const,
54
+ })
55
+
36
56
  private _extractBaseAddress(addressOrComposite: string): string {
37
57
  return addressOrComposite.split('-')[0]
38
58
  }
@@ -79,15 +79,13 @@ export class PermissionsBlock extends CoreLitElement {
79
79
  }
80
80
 
81
81
  private renderPermission(p: PermissionLevels) {
82
- const nameBase = (p.icon || p.permissionName || '')
83
- .toLowerCase()
84
- .replace(/_/g, '-')
82
+ const nameBase = (p.icon || p.permissionName || '').toLowerCase()
85
83
  const activeIcon = `${nameBase}-activated`
86
84
  const inactiveIcon = `${nameBase}-inactive`
87
85
  return html`
88
86
  <div class="mt-4 grid grid-cols-[40px,1fr,20px] gap-x-2 items-start">
89
87
  <div class="relative w-10 h-10">
90
- <lukso-icon
88
+ <svg-icon
91
89
  class="active absolute bottom-0 opacity-0 z-[99] transition-opacity duration-200 ${
92
90
  p.isActive ? 'opacity-100' : ''
93
91
  }"
@@ -95,14 +93,14 @@ export class PermissionsBlock extends CoreLitElement {
95
93
  width="40"
96
94
  height="40"
97
95
  color=${this.color}
98
- ></lukso-icon>
99
- <lukso-icon
96
+ ></svg-icon>
97
+ <svg-icon
100
98
  class="inactive absolute bottom-0 ${p.isActive ? 'opacity-0' : 'opacity-100'}"
101
99
  name=${inactiveIcon}
102
100
  width="40"
103
101
  height="40"
104
102
  color=${this.color}
105
- ></lukso-icon>
103
+ ></svg-icon>
106
104
  </div>
107
105
 
108
106
  <div>
@@ -231,7 +231,7 @@ export class TransactionPlayback extends LitElement {
231
231
  }
232
232
  }
233
233
 
234
- protected updated(_changedProperties: PropertyValues): void {
234
+ protected updated(changedProperties: PropertyValues): void {
235
235
  // Mount component after render
236
236
  if (this.loadedComponent && this.componentContainerRef.value) {
237
237
  // Clear existing content
@@ -364,14 +364,14 @@ export class TransactionPlayback extends LitElement {
364
364
  () => html`
365
365
  <div class="debug-item">
366
366
  <span class="debug-label">Child Index:</span>
367
- <span class="debug-value">${this.currentResult?.indexInfo.childIndex}</span>
367
+ <span class="debug-value">${this.currentResult!.indexInfo.childIndex}</span>
368
368
  </div>
369
369
  `
370
370
  )}
371
371
  <div class="debug-item">
372
372
  <span class="debug-label">Available Indices:</span>
373
373
  <span class="debug-value">
374
- [${this.playbackState?.batchInfo.availableIndices.join(', ')}]
374
+ [${this.playbackState!.batchInfo.availableIndices.join(', ')}]
375
375
  </span>
376
376
  </div>
377
377
  ${when(
@@ -379,7 +379,7 @@ export class TransactionPlayback extends LitElement {
379
379
  () => html`
380
380
  <div class="debug-item">
381
381
  <span class="debug-label">Matched View:</span>
382
- <span class="debug-value">${this.currentResult?.match?.view.id}</span>
382
+ <span class="debug-value">${this.currentResult!.match!.view.id}</span>
383
383
  </div>
384
384
  `
385
385
  )}
@@ -4,7 +4,7 @@ import {
4
4
  useImageLoader,
5
5
  } from '@lukso/transaction-view-headless'
6
6
  import { css, html, LitElement } from 'lit'
7
- import { property } from 'lit/decorators.js'
7
+ import { customElement, property, state } from 'lit/decorators.js'
8
8
  import { safeCustomElement } from './safe-custom-element'
9
9
 
10
10
  // LUKSO web components are loaded dynamically via plugin
@@ -96,14 +96,17 @@ export class AddressDisplay extends LitElement {
96
96
  return this.value || this.address || ''
97
97
  }
98
98
 
99
+ // Internal state
100
+ @state() private imageData?: { src: string }
101
+ @state() private isLoadingImage = false
102
+
99
103
  // Composables
100
104
  private imageLoader = useImageLoader()
101
105
  private addressResolver = useAddressResolver()
102
- isLoadingImage = false
103
106
 
104
107
  // Task for loading profile image
105
108
  private imageTask = new Task(this, {
106
- task: async ([resolved]) => {
109
+ task: async ([resolved], { signal }) => {
107
110
  if (!resolved) return null
108
111
 
109
112
  this.isLoadingImage = true
@@ -90,7 +90,7 @@ export class AddressView extends CoreLitElement {
90
90
 
91
91
  // Task for resolving address data
92
92
  private addressTask = new Task(this, {
93
- task: async ([address]) => {
93
+ task: async ([address], { signal }) => {
94
94
  if (!address) return null
95
95
 
96
96
  this.isLoadingAddress = true
@@ -99,7 +99,7 @@ export class AddressView extends CoreLitElement {
99
99
  const resolved = await this.addressResolver.resolveAddress(address)
100
100
  this.requestUpdate() // Force re-render after resolution
101
101
  return resolved
102
- } catch (_error) {
102
+ } catch (error) {
103
103
  return null
104
104
  } finally {
105
105
  this.isLoadingAddress = false
@@ -129,6 +129,22 @@ export class AddressView extends CoreLitElement {
129
129
  return this.label || computedName || 'Anonymous'
130
130
  }
131
131
 
132
+ /**
133
+ * Check if this is a contract address (non-profile)
134
+ */
135
+ private get isContract(): boolean {
136
+ const resolved = this.addressTask.value
137
+ return resolved ? resolved.__gqltype !== 'Profile' : false
138
+ }
139
+
140
+ /**
141
+ * Check if this should show the address hash (profiles show hash, contracts don't)
142
+ */
143
+ private get shouldShowAddressHash(): boolean {
144
+ const resolved = this.addressTask.value
145
+ return resolved ? resolved.__gqltype === 'Profile' : true // Default to true for fallback
146
+ }
147
+
132
148
  /**
133
149
  * Get prefix for the address display
134
150
  */
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit'
2
- import { property } from 'lit/decorators.js'
2
+ import { customElement, property, query, state } from 'lit/decorators.js'
3
3
  import { safeCustomElement } from './safe-custom-element'
4
4
  import './load-web-components'
5
5
  import makeBlockie from 'ethereum-blockies-base64'
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit'
2
- import { property } from 'lit/decorators.js'
2
+ import { customElement, property } from 'lit/decorators.js'
3
3
  import { safeCustomElement } from './safe-custom-element'
4
4
  import './load-web-components'
5
5
  import { CoreLitElement } from '../styles'
@@ -1,5 +1,8 @@
1
+ import { Task } from '@lit/task'
2
+ import { useAddressResolver } from '@lukso/transaction-view-headless'
3
+ import makeBlockie from 'ethereum-blockies-base64'
1
4
  import { html } from 'lit'
2
- import { property } from 'lit/decorators.js'
5
+ import { customElement, property, state } from 'lit/decorators.js'
3
6
  import { safeCustomElement } from './safe-custom-element'
4
7
  import './load-web-components'
5
8
  import { CoreLitElement } from '../styles'
@@ -145,7 +148,7 @@ export class GasProviderView extends CoreLitElement {
145
148
 
146
149
  private displayRelayerResetDate() {
147
150
  const date = new Date(this.relayerResetDate)
148
- return Number.isNaN(date.getTime()) ? 'N/A' : date.toLocaleDateString()
151
+ return isNaN(date.getTime()) ? 'N/A' : date.toLocaleDateString()
149
152
  }
150
153
 
151
154
  override render() {
@@ -175,12 +178,12 @@ export class GasProviderView extends CoreLitElement {
175
178
  ? this.controllerAddress
176
179
  : (() => {
177
180
  const r = this.allRelayers.find(
178
- (x) => x.id === this.selectedGasProvider?.id
181
+ (x) => x.id === this.selectedGasProvider!.id
179
182
  )
180
183
  return (
181
184
  r?.website ||
182
185
  r?.apiUrl ||
183
- `relayer:${this.selectedGasProvider?.id}`
186
+ `relayer:${this.selectedGasProvider!.id}`
184
187
  )
185
188
  })()
186
189
  : undefined
@@ -1,11 +1,10 @@
1
1
  import { Task } from '@lit/task'
2
2
  import {
3
- type ImageResult,
4
3
  useAddressResolver,
5
4
  useImageLoader,
6
5
  } from '@lukso/transaction-view-headless'
7
6
  import { html } from 'lit'
8
- import { property } from 'lit/decorators.js'
7
+ import { customElement, property, state } from 'lit/decorators.js'
9
8
  import { CoreLitElement } from '../styles'
10
9
  import { safeCustomElement } from './safe-custom-element'
11
10
 
@@ -116,15 +115,18 @@ export class IconView extends CoreLitElement {
116
115
  */
117
116
  @property({ type: Number, attribute: 'image-index' }) imageIndex?: number
118
117
 
118
+ // Internal state
119
+ @state() private imageData?: { src: string }
120
+ @state() private isLoadingImage = false
121
+ @state() private isLoadingAddress = false
122
+
119
123
  // Composables
120
124
  private imageLoader = useImageLoader()
121
125
  private addressResolver = useAddressResolver()
122
- isLoadingAddress = false
123
- isLoadingImage = false
124
126
 
125
127
  // Task for resolving address data
126
128
  private addressTask = new Task(this, {
127
- task: async ([address]) => {
129
+ task: async ([address], { signal }) => {
128
130
  if (!address) return null
129
131
 
130
132
  this.isLoadingAddress = true
@@ -145,12 +147,12 @@ export class IconView extends CoreLitElement {
145
147
 
146
148
  // Task for loading profile image
147
149
  private imageTask = new Task(this, {
148
- task: async ([resolved]) => {
150
+ task: async ([resolved], { signal }) => {
149
151
  if (!resolved) return null
150
152
 
151
153
  this.isLoadingImage = true
152
154
  try {
153
- let image: ImageResult | null = null
155
+ let image
154
156
 
155
157
  // If imageHint is specified, load directly from that property
156
158
  if (this.imageHint && (resolved as any)[this.imageHint]) {
@@ -1,7 +1,7 @@
1
1
  import { Task } from '@lit/task'
2
2
  import { useAddressResolver } from '@lukso/transaction-view-headless'
3
3
  import { html } from 'lit'
4
- import { property, state } from 'lit/decorators.js'
4
+ import { customElement, property, state } from 'lit/decorators.js'
5
5
  import { repeat } from 'lit/directives/repeat.js'
6
6
  import { safeCustomElement } from './safe-custom-element'
7
7
  import './icon-view.js'
@@ -72,7 +72,7 @@ export class ImageList extends CoreLitElement {
72
72
 
73
73
  // Task for resolving address data
74
74
  private addressTask = new Task(this, {
75
- task: async ([address]) => {
75
+ task: async ([address], { signal }) => {
76
76
  if (!address) return null
77
77
 
78
78
  this.isLoading = true
@@ -140,7 +140,7 @@ export class ImageList extends CoreLitElement {
140
140
  * Render a single image item
141
141
  */
142
142
  private renderImageItem(item: any, index: number) {
143
- const _itemIndex = item.index !== undefined ? item.index : index
143
+ const itemIndex = item.index !== undefined ? item.index : index
144
144
  const isImage = this.isImageFile(item)
145
145
 
146
146
  if (isImage) {
@@ -237,6 +237,7 @@ export class ImageList extends CoreLitElement {
237
237
  </div>
238
238
  `
239
239
  }
240
+
240
241
  const grid = html`
241
242
  <div class="grid grid-cols-[repeat(auto-fill,minmax(${this.getImageMinSizePx()}px,300px))] gap-3 p-2">
242
243
  ${repeat(
@@ -34,12 +34,11 @@ const originalDefine =
34
34
 
35
35
  // Wrap customElements.define to catch duplicate registration errors
36
36
  if (typeof customElements !== 'undefined' && originalDefine) {
37
- customElements.define = (
37
+ customElements.define = function (
38
38
  name: string,
39
- // biome-ignore lint/suspicious/noShadowRestrictedNames: This is the web-components api.
40
39
  constructor: CustomElementConstructor,
41
40
  options?: ElementDefinitionOptions
42
- ) => {
41
+ ) {
43
42
  // Check if already defined
44
43
  if (!customElements.get(name)) {
45
44
  originalDefine(name, constructor, options)