@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.
- package/.turbo/turbo-build.log +19 -19
- package/dist/index.cjs +220 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +252 -158
- package/dist/index.js.map +1 -1
- package/dist/shared/index.cjs +64 -31
- package/dist/shared/index.cjs.map +1 -1
- package/dist/shared/index.d.cts +20 -8
- package/dist/shared/index.d.ts +20 -8
- package/dist/shared/index.js +76 -43
- package/dist/shared/index.js.map +1 -1
- package/dist/styles/index.cjs +1 -1
- package/dist/styles/index.cjs.map +1 -1
- package/dist/styles/index.js +1 -1
- package/dist/styles/index.js.map +1 -1
- package/package.json +4 -5
- package/src/components/asset-card.ts +80 -63
- package/src/components/contract-card.ts +67 -2
- package/src/components/info-panel.ts +21 -1
- package/src/components/permissions-block.ts +5 -7
- package/src/components/transaction-playback.ts +4 -4
- package/src/shared/address-display.ts +6 -3
- package/src/shared/address-view.ts +18 -2
- package/src/shared/custom-select.ts +1 -1
- package/src/shared/function-name-view.ts +1 -1
- package/src/shared/gas-provider-view.ts +7 -4
- package/src/shared/icon-view.ts +9 -7
- package/src/shared/image-list.ts +4 -3
- package/src/shared/load-web-components.ts +2 -3
- package/src/shared/notification-view.ts +3 -3
- package/src/shared/progress-view.ts +10 -1
- package/src/shared/scroll-to-bottom-button.ts +1 -1
- package/src/shared/signal-controller.ts +1 -3
- package/src/shared/transaction-view-controller.ts +1 -1
- package/src/shared/url-view.ts +3 -3
- package/src/shared/value-view.ts +30 -10
- package/src/styles/styles.css +1 -1
- package/src/vue-entry.ts +1 -0
- package/tsup.config.ts +3 -3
- package/.turbo/turbo-typecheck.log +0 -4
- package/src/utils.test.ts +0 -47
- 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
|
|
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
|
|
39
|
-
"@lukso/transaction-view-headless": "0.4.0
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
|
2
|
-
import {
|
|
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
|
|
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
|
-
<
|
|
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
|
-
></
|
|
99
|
-
<
|
|
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
|
-
></
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
181
|
+
(x) => x.id === this.selectedGasProvider!.id
|
|
179
182
|
)
|
|
180
183
|
return (
|
|
181
184
|
r?.website ||
|
|
182
185
|
r?.apiUrl ||
|
|
183
|
-
`relayer:${this.selectedGasProvider
|
|
186
|
+
`relayer:${this.selectedGasProvider!.id}`
|
|
184
187
|
)
|
|
185
188
|
})()
|
|
186
189
|
: undefined
|
package/src/shared/icon-view.ts
CHANGED
|
@@ -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
|
|
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]) {
|
package/src/shared/image-list.ts
CHANGED
|
@@ -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
|
|
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)
|