@visualizevalue/mint-app-base 0.1.43 → 0.1.45

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.
@@ -2,7 +2,7 @@
2
2
  /* GRAYS */
3
3
  --white: white;
4
4
  --gray: gray;
5
- --black: rgb(0, 0, 0);
5
+ --black: black;
6
6
 
7
7
  /* COLORS */
8
8
  --primary: blue;
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <rect width="64" height="64" fill="var(--black)"/>
4
+ </svg>
5
+ </template>
@@ -2,7 +2,8 @@
2
2
  <article class="token-detail">
3
3
  <div class="artifact">
4
4
  <div>
5
- <Image :src="token.artifact" :alt="token.name" />
5
+ <Image v-if="token.artifact" :src="token.artifact" :alt="token.name" />
6
+ <ImageVoid v-else />
6
7
  </div>
7
8
  </div>
8
9
 
@@ -3,34 +3,35 @@
3
3
  <slot :mints="mints" :loading="loading">
4
4
  <h1>Mint Timeline</h1>
5
5
 
6
- <div v-if="currentBlock" class="token-mint-timeline-items">
7
- <TokenMintTimelineItem
8
- v-for="mint of mints"
9
- :mint="mint"
10
- :key="mint.tx"
11
- :block="currentBlock"
12
- />
13
- <TokenMintTimelineItem v-if="backfillComplete">
14
- <Account :address="collection.owner" class="account" />
15
-
16
- <span class="amount">1<span>×</span></span>
17
- <span class="price">Artist Mint</span>
18
-
19
- <span class="time-ago"><BlocksTimeAgo v-if="currentBlock" :blocks="currentBlock - mintedAtBlock" /></span>
20
-
21
- <span class="links">
22
- <NuxtLink :to="`${config.public.blockExplorer}/nft/${token.collection}/${token.tokenId}`" target="_blank">
23
- <Icon type="link" />
24
- </NuxtLink>
25
- </span>
26
- </TokenMintTimelineItem>
27
- </div>
6
+ <template v-if="currentBlock">
7
+ <TokenMintTimelineVirtualScroller
8
+ :mints="mints"
9
+ :block="currentBlock"
10
+ >
11
+ <template #after>
12
+ <TokenMintTimelineItem v-if="backfillComplete">
13
+ <Account :address="collection.owner" class="account" />
14
+
15
+ <span class="amount">1<span>×</span></span>
16
+ <span class="price">Artist Mint</span>
17
+
18
+ <span class="time-ago"><BlocksTimeAgo v-if="currentBlock" :blocks="currentBlock - mintedAtBlock" /></span>
19
+
20
+ <span class="links">
21
+ <NuxtLink :to="`${config.public.blockExplorer}/nft/${token.collection}/${token.tokenId}`" target="_blank">
22
+ <Icon type="link" />
23
+ </NuxtLink>
24
+ </span>
25
+ </TokenMintTimelineItem>
26
+ </template>
27
+ </TokenMintTimelineVirtualScroller>
28
+ </template>
28
29
 
29
30
  <div v-if="! backfillComplete" v-show="! loading" ref="loadMore" class="load-more">
30
31
  <Button @click="backfill">Load more</Button>
31
32
  </div>
32
33
 
33
- <Loading v-if="loading || ! currentBlock" txt="Mint History..." />
34
+ <Loading v-if="loading || ! currentBlock" txt="Loading Mint History..." />
34
35
  </slot>
35
36
  </section>
36
37
  </template>
@@ -72,16 +73,27 @@ watch(currentBlock, () => {
72
73
  state.fetchTokenMints(token)
73
74
  })
74
75
 
76
+ const loadMore = ref()
77
+ const loadMoreVisible = useElementVisibility(loadMore)
75
78
  const backfill = async () => {
76
79
  loading.value = true
77
- await state.backfillTokenMints(token)
80
+
81
+ try {
82
+ await state.backfillTokenMints(token)
83
+
84
+ if (loadMoreVisible.value) {
85
+ await delay(300)
86
+ await backfill()
87
+ }
88
+ } catch (e) {
89
+ console.error(`Issue during backfill`, e)
90
+ }
91
+
78
92
  loading.value = false
79
93
  }
80
-
81
- const loadMore = ref()
82
- const loadMoreVisible = useElementVisibility(loadMore)
83
94
  watch(loadMoreVisible, () => {
84
- if (! loadMoreVisible.value) return
95
+ // Skip if we have enough mints for the viewport or we're already loading
96
+ if (! loadMoreVisible.value || loading.value) return
85
97
 
86
98
  backfill()
87
99
  })
@@ -92,6 +104,11 @@ watch(loadMoreVisible, () => {
92
104
  padding-top: var(--spacer-lg);
93
105
  padding-bottom: var(--spacer-lg);
94
106
  container-type: inline-size;
107
+
108
+ :deep(.token-mint-timeline-items) {
109
+ display: grid;
110
+ gap: var(--spacer);
111
+ }
95
112
  }
96
113
 
97
114
  h1 {
@@ -102,10 +119,6 @@ h1 {
102
119
  margin: 0 0 var(--spacer);
103
120
  }
104
121
 
105
- .token-mint-timeline-items {
106
- display: grid;
107
- gap: var(--spacer);
108
- }
109
122
 
110
123
  .load-more {
111
124
  .button {
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div class="token-mint-timeline-item">
3
3
  <slot :mint="mint" :formatted-price="formattedPrice">
4
- <NuxtLink :to="{ name: 'profile-address', params: { address: mint.address } }">
5
- <Account :address="mint.address" class="account" />
4
+ <NuxtLink :to="{ name: 'profile-address', params: { address: mint.address } }" class="account">
5
+ <Account :address="mint.address" />
6
6
  </NuxtLink>
7
7
 
8
8
  <span class="amount">{{ mint.amount.toString() }}<span>×</span></span>
@@ -49,7 +49,7 @@ const formattedPrice = computed(() => props.mint && customFormatEther(props.mint
49
49
  span {
50
50
  white-space: nowrap;
51
51
 
52
- &:not(.account) {
52
+ &:not(.account):not(.account *) {
53
53
  color: var(--muted);
54
54
  font-size: var(--font-sm);
55
55
  }
@@ -57,6 +57,8 @@ const formattedPrice = computed(() => props.mint && customFormatEther(props.mint
57
57
 
58
58
  a,
59
59
  button {
60
+ color: var(--color);
61
+
60
62
  &:--highlight {
61
63
  color: var(--color);
62
64
  }
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div ref="wrapper">
3
+ <RecycleScroller
4
+ :items="mints"
5
+ :item-size="itemSize"
6
+ key-field="tx"
7
+ list-class="token-mint-timeline-items"
8
+ page-mode
9
+ >
10
+ <template #before>
11
+ <slot name="before" />
12
+ </template>
13
+
14
+ <template #default="{ item: mint }">
15
+ <TokenMintTimelineItem
16
+ :mint="mint"
17
+ :key="mint.tx"
18
+ :block="block"
19
+ />
20
+ </template>
21
+
22
+ <template #after>
23
+ <slot name="after" />
24
+ </template>
25
+ </RecycleScroller>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { useElementSize } from '@vueuse/core'
31
+ import { RecycleScroller } from 'vue-virtual-scroller'
32
+ import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
33
+
34
+ defineProps({
35
+ mints: Array,
36
+ block: BigInt,
37
+ })
38
+
39
+ const wrapper = ref()
40
+ const REM = 16
41
+ const { width: wrapperWidth } = useElementSize(wrapper)
42
+ const itemSize = computed(() => 24 * REM > wrapperWidth.value ? 60 : 40)
43
+ </script>
44
+
@@ -32,7 +32,8 @@
32
32
  <p v-if="mintOpen" class="closes-in">Closes in {{ blocksRemaining }} {{ pluralize('block', Number(blocksRemaining))}}</p>
33
33
  <p v-else class="closed-at">Closed at block {{ token.untilBlock }}</p>
34
34
  </header>
35
- <Image :src="token.artifact" :alt="token.name" />
35
+ <Image v-if="token.artifact" :src="token.artifact" :alt="token.name" />
36
+ <ImageVoid v-else />
36
37
  <CardLink :to="{
37
38
  name: 'id-collection-tokenId',
38
39
  params: { id: collection.owner, collection: token.collection, tokenId: `${token.tokenId}` }
@@ -4,7 +4,7 @@ import { parseAbiItem, type PublicClient } from 'viem'
4
4
  import type { MintEvent } from '~/utils/types'
5
5
 
6
6
  export const CURRENT_STATE_VERSION = 4
7
- export const MAX_BLOCK_RANGE = 2500n
7
+ export const MAX_BLOCK_RANGE = 1000n
8
8
  export const MINT_BLOCKS = BLOCKS_PER_DAY
9
9
 
10
10
  export const useOnchainStore = () => {
@@ -320,7 +320,7 @@ export const useOnchainStore = () => {
320
320
  },
321
321
 
322
322
  async fetchTokenMints (token: Token) {
323
- const client = getPublicClient($wagmi) as PublicClient
323
+ const client = getPublicClient($wagmi, { chainId }) as PublicClient
324
324
  const currentBlock = await client.getBlockNumber()
325
325
  const mintedAtBlock = token.untilBlock - MINT_BLOCKS
326
326
  const storedToken = this.collections[token.collection].tokens[token.tokenId.toString()]
@@ -365,7 +365,7 @@ export const useOnchainStore = () => {
365
365
 
366
366
  // We want to fetch until our max range (5000), or until when the token minted
367
367
  const fromBlock = toBlock - MAX_BLOCK_RANGE > mintedAtBlock ? toBlock - MAX_BLOCK_RANGE : mintedAtBlock
368
- console.log(`Backfilling token mints blocks ${fromBlock}-${toBlock}`)
368
+ console.info(`Backfilling token mints blocks ${fromBlock}-${toBlock}`)
369
369
 
370
370
  // Finally, we update our database
371
371
  this.addTokenMints(token, await this.loadMintEvents(token, fromBlock, toBlock), 'append')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visualizevalue/mint-app-base",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "dependencies": {
@@ -21,8 +21,8 @@
21
21
  "postcss-preset-env": "^9.5.13",
22
22
  "viem": "2.x",
23
23
  "vite": "^5.4.3",
24
- "vue-virtual-scroller": "^2.0.0-beta.8",
25
- "@visualizevalue/mint-utils": "^0.0.2"
24
+ "vue-virtual-scroller": "2.0.0-beta.8",
25
+ "@visualizevalue/mint-utils": "^0.0.3"
26
26
  },
27
27
  "devDependencies": {
28
28
  "nuxt": "^3.13.2",
package/utils/time.ts CHANGED
@@ -1,12 +1,10 @@
1
- export const BLOCKS_PER_CACHE = 5n * 30n // 30 minutes (5 blocks each)
2
- export const BLOCKS_PER_HOUR = 300n // 5n * 60n * 24n (5 blocks/min * 60 min * 24 hours)
3
- export const BLOCKS_PER_DAY = 7200n // 300n * 24n (300 blocks per hour * 24 hours)
1
+ import { BLOCKS_PER_CACHE, BLOCKS_PER_HOUR, BLOCKS_PER_DAY } from '@visualizevalue/mint-utils/time'
2
+ import { blocksToSeconds, delay, nowInSeconds } from '@visualizevalue/mint-utils'
4
3
 
5
- export const delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
6
-
7
- export const blocksToSeconds = (blocks: bigint): number => Number(blocks * 12n)
8
-
9
- export const nowInSeconds = (): number => Math.floor(Date.now() / 1000)
4
+ export {
5
+ BLOCKS_PER_CACHE, BLOCKS_PER_HOUR, BLOCKS_PER_DAY,
6
+ blocksToSeconds, delay, nowInSeconds,
7
+ }
10
8
 
11
9
  const now = ref(nowInSeconds())
12
10
  let nowInterval: NodeJS.Timeout