@meddleware/walrus-ui 0.1.8 → 0.1.9

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meddleware/walrus-ui",
3
- "version": "0.1.8",
4
- "description": "Standalone Vue 3 SPA for uploading blobs to Walrus decentralised storage and managing owned blobs wallet-connected, NFT-gated relay support.",
3
+ "version": "0.1.9",
4
+ "description": "Standalone Vue 3 SPA for uploading blobs to Walrus decentralised storage and managing owned blobs \u2014 wallet-connected, NFT-gated relay support.",
5
5
  "author": "Meddleware <dev@meddleware.co.uk>",
6
6
  "license": "0BSD",
7
7
  "repository": {
@@ -45,7 +45,7 @@
45
45
  "@meddleware/ui": "^0.1.7",
46
46
  "@meddleware/wallet-adapter": "^0.0.5",
47
47
  "@meddleware/walrus-client": "^0.0.5",
48
- "@meddleware/walrus-relay": "^0.1.3",
48
+ "@meddleware/walrus-relay": "^0.1.4",
49
49
  "@mysten/sui": "^2.28.0",
50
50
  "@mysten/wallet-standard": "^0.20.0",
51
51
  "@mysten/walrus": "~1.1.7",
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import { ref, watch } from 'vue'
2
+ import { onMounted, ref, watch } from 'vue'
3
3
  import type { OwnedBlob } from '@meddleware/walrus-client'
4
4
  import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url'
5
- import { getSuiClient } from '../wallet.js'
6
5
  import type { Executor } from '../wallet.js'
7
6
  import { NETWORK } from '../config.js'
7
+ import { useOwnedBlobs } from '../composables/useOwnedBlobs.js'
8
8
 
9
9
  const props = defineProps<{
10
10
  /** Connected wallet address whose owned blobs to list; `null` when no wallet is connected. */
@@ -13,10 +13,8 @@ const props = defineProps<{
13
13
  buildExecutor: () => Promise<Executor>
14
14
  }>()
15
15
 
16
- const blobs = ref<OwnedBlob[]>([])
17
- const loading = ref(false)
18
- const error = ref<string | null>(null)
19
- const currentEpoch = ref(0)
16
+ // Shared, session-persistent cache (survives tab switches and inline re-mounts).
17
+ const { blobs, currentEpoch, loading, error, load } = useOwnedBlobs()
20
18
  const extending = ref<string | null>(null)
21
19
  const extendStatus = ref<Record<string, string>>({})
22
20
 
@@ -30,26 +28,9 @@ function epochsToApproxDays(epochs: number): string {
30
28
  return `${days} days`
31
29
  }
32
30
 
33
- async function loadBlobs(): Promise<void> {
34
- if (!props.address) return
35
- loading.value = true
36
- error.value = null
37
- blobs.value = []
38
- try {
39
- const { createWalrusClient, fetchOwnedWalrusBlobs } = await import('@meddleware/walrus-client')
40
- const suiClient = getSuiClient()
41
- const walrusClient = createWalrusClient({ network: NETWORK, wasmUrl: walrusWasmUrl })
42
- const [sys, fetched] = await Promise.all([
43
- suiClient.getCurrentSystemState(),
44
- fetchOwnedWalrusBlobs(suiClient, walrusClient, props.address),
45
- ])
46
- currentEpoch.value = Number(sys.systemState.epoch)
47
- blobs.value = fetched.sort((a: OwnedBlob, b: OwnedBlob) => a.endEpoch - b.endEpoch)
48
- } catch (e) {
49
- error.value = e instanceof Error ? e.message : String(e)
50
- } finally {
51
- loading.value = false
52
- }
31
+ /** Force a fresh fetch (Refresh button + after an on-chain-affecting action). */
32
+ function refresh(): Promise<void> {
33
+ return load(props.address, { force: true })
53
34
  }
54
35
 
55
36
  async function extendBlob(blob: OwnedBlob): Promise<void> {
@@ -65,7 +46,7 @@ async function extendBlob(blob: OwnedBlob): Promise<void> {
65
46
  await executor.waitForTransaction(digest)
66
47
  extendStatus.value = { ...extendStatus.value, [blob.objectId]: `Extended ✓ (${digest.slice(0, 8)}…)` }
67
48
  // Refresh the list so the new endEpoch is visible.
68
- await loadBlobs()
49
+ await refresh()
69
50
  } catch (e) {
70
51
  extendStatus.value = {
71
52
  ...extendStatus.value,
@@ -76,14 +57,17 @@ async function extendBlob(blob: OwnedBlob): Promise<void> {
76
57
  }
77
58
  }
78
59
 
79
- watch(() => props.address, loadBlobs)
60
+ // Load on first open (fixes "nothing shows until Refresh") and whenever the address changes.
61
+ // The composable no-ops when the list is already cached for this address, so re-mounting is cheap.
62
+ onMounted(() => void load(props.address))
63
+ watch(() => props.address, (addr) => void load(addr))
80
64
  </script>
81
65
 
82
66
  <template>
83
67
  <section class="my-blobs">
84
68
  <div class="toolbar">
85
69
  <h2>My Blobs</h2>
86
- <button type="button" :disabled="!address || loading" @click="loadBlobs">
70
+ <button type="button" :disabled="!address || loading" @click="refresh">
87
71
  {{ loading ? 'Loading…' : 'Refresh' }}
88
72
  </button>
89
73
  </div>
@@ -17,6 +17,7 @@ import { WalletGuard } from '@meddleware/wallet-adapter'
17
17
  import { useWallet, getSuiClient } from '../wallet.js'
18
18
  import { NETWORK, relayHosts, accessGate, uploadRelayMaxTipMist } from '../config.js'
19
19
  import { runBlobUpload } from '../upload-flow.js'
20
+ import { useOwnedBlobs } from '../composables/useOwnedBlobs.js'
20
21
  import MyBlobs from './MyBlobs.vue'
21
22
 
22
23
  type Tab = 'upload' | 'blobs'
@@ -83,9 +84,17 @@ async function performUpload(
83
84
  })
84
85
  }
85
86
 
87
+ const ownedBlobs = useOwnedBlobs()
88
+
86
89
  function onUploaded(r: UploadResult): void {
87
90
  result.value = r
88
91
  }
92
+
93
+ // Fires after every upload attempt (success or failure) — a failed UI run may still have landed
94
+ // on-chain, so force-refresh the owned-blobs cache in the background regardless of outcome.
95
+ function onSettled(): void {
96
+ if (account.value) void ownedBlobs.load(account.value.address, { force: true })
97
+ }
89
98
  </script>
90
99
 
91
100
  <template>
@@ -127,6 +136,7 @@ function onUploaded(r: UploadResult): void {
127
136
  :access="{ gateConfigured: gateState.gateConfigured, hasAccess: gateState.hasAccess }"
128
137
  :perform-upload="performUpload"
129
138
  @uploaded="onUploaded"
139
+ @settled="onSettled"
130
140
  />
131
141
 
132
142
  <section v-if="result" class="result">
@@ -0,0 +1,54 @@
1
+ // Module-singleton cache of the connected wallet's owned Walrus blobs.
2
+ //
3
+ // State lives at module scope (not inside the composable factory) so it is shared across every
4
+ // `MyBlobs` mount in the session: switching the Upload/My-Blobs tabs — or navigating the whole
5
+ // tool view in and out inline in the dashboard — no longer loses the list or forces a refetch.
6
+ // `load()` is a no-op when the list is already loaded for the given address, unless `force` is set
7
+ // (used after an upload/extend, which can create or mutate on-chain blobs).
8
+ import { ref } from 'vue'
9
+ import type { OwnedBlob } from '@meddleware/walrus-client'
10
+ // Lightweight URL import — just the wasm asset URL (does not pull the walrus client eagerly).
11
+ import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url'
12
+ import { getSuiClient } from '../wallet.js'
13
+ import { NETWORK } from '../config.js'
14
+
15
+ const blobs = ref<OwnedBlob[]>([])
16
+ const currentEpoch = ref(0)
17
+ const loading = ref(false)
18
+ const error = ref<string | null>(null)
19
+ /** Address the current `blobs` were fetched for; `null` until a successful load. */
20
+ const loadedFor = ref<string | null>(null)
21
+
22
+ async function load(address: string | null, opts: { force?: boolean } = {}): Promise<void> {
23
+ if (!address) {
24
+ blobs.value = []
25
+ loadedFor.value = null
26
+ return
27
+ }
28
+ // Skip refetch when we already have this address's list and the last load succeeded.
29
+ if (!opts.force && loadedFor.value === address && error.value === null) return
30
+
31
+ loading.value = true
32
+ error.value = null
33
+ try {
34
+ const { createWalrusClient, fetchOwnedWalrusBlobs } = await import('@meddleware/walrus-client')
35
+ const suiClient = getSuiClient()
36
+ const walrusClient = createWalrusClient({ network: NETWORK, wasmUrl: walrusWasmUrl })
37
+ const [sys, fetched] = await Promise.all([
38
+ suiClient.getCurrentSystemState(),
39
+ fetchOwnedWalrusBlobs(suiClient, walrusClient, address),
40
+ ])
41
+ currentEpoch.value = Number(sys.systemState.epoch)
42
+ blobs.value = fetched.sort((a: OwnedBlob, b: OwnedBlob) => a.endEpoch - b.endEpoch)
43
+ loadedFor.value = address
44
+ } catch (e) {
45
+ error.value = e instanceof Error ? e.message : String(e)
46
+ } finally {
47
+ loading.value = false
48
+ }
49
+ }
50
+
51
+ /** Reactive access to the shared owned-blobs cache and its loader. */
52
+ export function useOwnedBlobs() {
53
+ return { blobs, currentEpoch, loading, error, loadedFor, load }
54
+ }