@visualizevalue/mint-app-base 0.1.126 → 0.1.127

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.
@@ -17,7 +17,7 @@
17
17
  error: 'Retry',
18
18
  complete: 'OK',
19
19
  },
20
- }" skip-confirmation auto-close-success @complete="onComplete">
20
+ }" skip-confirmation auto-close-success toast @complete="onComplete">
21
21
  <template #start="{ start }">
22
22
  <Button @click="start">
23
23
  <Icon type="withdraw" />
@@ -20,6 +20,7 @@
20
20
  @complete="onMinted"
21
21
  skip-confirmation
22
22
  auto-close-success
23
+ toast
23
24
  >
24
25
  <template #start="{ start }">
25
26
  <Button @click="start" class="mint">
@@ -35,10 +35,12 @@
35
35
 
36
36
  <script setup>
37
37
  import { waitForTransactionReceipt, watchChainId } from '@wagmi/core'
38
+
38
39
  const checkChain = useEnsureChainIdCheck()
39
40
 
40
41
  const { $wagmi } = useNuxtApp()
41
42
  const config = useRuntimeConfig()
43
+ const txStore = useTransactionStore()
42
44
  const props = defineProps({
43
45
  text: {
44
46
  type: Object,
@@ -65,10 +67,12 @@ const props = defineProps({
65
67
  },
66
68
  skipConfirmation: Boolean,
67
69
  autoCloseSuccess: Boolean,
70
+ toast: Boolean,
68
71
  })
69
72
 
70
73
  const emit = defineEmits(['complete', 'cancel'])
71
74
 
75
+
72
76
  const open = ref(false)
73
77
 
74
78
  const switchChain = ref(false)
@@ -147,14 +151,30 @@ const initializeRequest = async (request = cachedRequest.value) => {
147
151
  requesting.value = true
148
152
  tx.value = await request()
149
153
  requesting.value = false
150
- waiting.value = true
151
- const [receiptObject] = await Promise.all([
152
- waitForTransactionReceipt($wagmi, { hash: tx.value }),
153
- ])
154
- await delay(props.delayAfter)
155
- receipt.value = receiptObject
156
- emit('complete', receiptObject)
157
- complete.value = true
154
+
155
+ if (!props.toast) {
156
+ // Blocking mode: wait for receipt in the modal (used for multi-step flows)
157
+ waiting.value = true
158
+ const [receiptObject] = await Promise.all([
159
+ waitForTransactionReceipt($wagmi, { hash: tx.value }),
160
+ ])
161
+ await delay(props.delayAfter)
162
+ receipt.value = receiptObject
163
+ emit('complete', receiptObject)
164
+ complete.value = true
165
+ } else {
166
+ // Toast mode: close modal and track in background
167
+ open.value = false
168
+ txStore.track(
169
+ tx.value,
170
+ props.text.lead?.waiting || 'Transaction submitted...',
171
+ props.text.lead?.complete || 'Transaction confirmed.',
172
+ (receiptObject) => {
173
+ receipt.value = receiptObject
174
+ emit('complete', receiptObject)
175
+ },
176
+ )
177
+ }
158
178
  } catch (e) {
159
179
  if (e?.cause?.code === 4001) {
160
180
  open.value = false
@@ -167,7 +187,7 @@ const initializeRequest = async (request = cachedRequest.value) => {
167
187
  requesting.value = false
168
188
  waiting.value = false
169
189
 
170
- if (props.autoCloseSuccess && step.value === 'complete') {
190
+ if (!props.toast && props.autoCloseSuccess && step.value === 'complete') {
171
191
  await delay(props.delayAutoclose)
172
192
  open.value = false
173
193
  await delay(300) // Animations...
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <TransitionGroup name="fade" tag="div" class="transaction-toasts">
3
+ <a
4
+ v-for="tx in txStore.pending"
5
+ :key="tx.hash"
6
+ :href="tx.txLink"
7
+ target="_blank"
8
+ class="transaction-toast"
9
+ >
10
+ <Icon
11
+ :type="tx.status === 'waiting' ? 'loader' : 'check'"
12
+ :class="{ spin: tx.status === 'waiting' }"
13
+ />
14
+ <span>{{ tx.status === 'complete' ? tx.completeText : tx.waitingText }}</span>
15
+ </a>
16
+ </TransitionGroup>
17
+ </template>
18
+
19
+ <script setup>
20
+ const txStore = useTransactionStore()
21
+ </script>
22
+
23
+ <style>
24
+ .transaction-toasts {
25
+ position: fixed;
26
+ top: var(--navbar-height);
27
+ right: var(--size-6);
28
+ z-index: 100;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--spacer-sm);
32
+ }
33
+
34
+ .transaction-toast {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: var(--spacer-sm);
38
+ padding: var(--ui-padding-y) var(--ui-padding-x);
39
+ background: var(--background);
40
+ border: var(--border);
41
+ font-size: var(--font-sm);
42
+ }
43
+ </style>
@@ -0,0 +1,51 @@
1
+ import { waitForTransactionReceipt } from '@wagmi/core'
2
+ import type { TransactionReceipt } from 'viem'
3
+
4
+ export interface TrackedTransaction {
5
+ hash: string
6
+ status: 'waiting' | 'complete' | 'error'
7
+ waitingText: string
8
+ completeText: string
9
+ txLink: string
10
+ }
11
+
12
+ export const useTransactionStore = defineStore('transactions', () => {
13
+ const { $wagmi } = useNuxtApp()
14
+ const config = useRuntimeConfig()
15
+
16
+ const pending = ref<TrackedTransaction[]>([])
17
+
18
+ const track = (
19
+ hash: string,
20
+ waitingText: string,
21
+ completeText: string,
22
+ onComplete?: (receipt: TransactionReceipt) => void,
23
+ ) => {
24
+ const entry: TrackedTransaction = reactive({
25
+ hash,
26
+ status: 'waiting',
27
+ waitingText,
28
+ completeText,
29
+ txLink: `${config.public.blockExplorer}/tx/${hash}`,
30
+ })
31
+
32
+ pending.value.push(entry)
33
+
34
+ waitForTransactionReceipt($wagmi, { hash: hash as `0x${string}` })
35
+ .then((receipt) => {
36
+ entry.status = 'complete'
37
+ onComplete?.(receipt)
38
+ setTimeout(() => remove(hash), 4000)
39
+ })
40
+ .catch(() => {
41
+ entry.status = 'error'
42
+ setTimeout(() => remove(hash), 8000)
43
+ })
44
+ }
45
+
46
+ const remove = (hash: string) => {
47
+ pending.value = pending.value.filter((t) => t.hash !== hash)
48
+ }
49
+
50
+ return { pending, track, remove }
51
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visualizevalue/mint-app-base",
3
- "version": "0.1.126",
3
+ "version": "0.1.127",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "dependencies": {
@@ -0,0 +1,13 @@
1
+ import { createVNode, render } from 'vue'
2
+ import TransactionToasts from '~/components/TransactionToasts.vue'
3
+
4
+ export default defineNuxtPlugin((nuxtApp) => {
5
+ nuxtApp.hook('app:mounted', () => {
6
+ const container = document.createElement('div')
7
+ document.body.appendChild(container)
8
+
9
+ const vnode = createVNode(TransactionToasts)
10
+ vnode.appContext = nuxtApp.vueApp._context
11
+ render(vnode, container)
12
+ })
13
+ })