Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit 8c56c63

Browse files
authored
Merge pull request #11217 from Jarsen136/issue-11148
feat: edit collection permission
2 parents 09f5a05 + 0001d9b commit 8c56c63

File tree

10 files changed

+386
-14
lines changed

10 files changed

+386
-14
lines changed

components/collection/EditModal.vue

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,87 @@
125125
</div>
126126
</div>
127127
</NeoField>
128+
129+
<NeoField
130+
v-if="isModalActive"
131+
:label="$t('mint.collection.permission.label')"
132+
>
133+
<div class="w-full flex flex-col gap-4">
134+
<div class="flex items-center justify-between">
135+
<p>
136+
{{ $t('mint.mintType') }}
137+
</p>
138+
<NeoSelect
139+
v-model="selectedMintingType"
140+
>
141+
<option
142+
v-for="menu in COLLECTION_MINTING_TYPES_OPTIONS"
143+
:key="menu.value"
144+
:value="menu.value"
145+
>
146+
{{ menu.text }}
147+
</option>
148+
</NeoSelect>
149+
</div>
150+
151+
<div>
152+
<div class="flex justify-between capitalize">
153+
<p>{{ $t(hasMintingPrice ? 'mint.collection.permission.pricePlaceholder' : 'mint.collection.permission.noPriceSet') }}</p>
154+
<NeoSwitch
155+
v-if="isHolderOfMintingTypeSelected"
156+
v-model="hasMintingPrice"
157+
position="left"
158+
/>
159+
</div>
160+
<div
161+
v-if="hasMintingPrice"
162+
class="flex focus-within:!border-border-color border border-k-shade h-12 mt-3"
163+
>
164+
<input
165+
v-model="mintingPrice"
166+
type="number"
167+
step="0.01"
168+
min="0.0001"
169+
pattern="[0-9]+([\.,][0-9]+)?"
170+
class="indent-2.5 border-none outline-none w-20 bg-background-color text-text-color w-full"
171+
:placeholder="$t('mint.collection.permission.pricePlaceholder')"
172+
>
173+
<div class="px-3 flex items-center">
174+
{{ chainSymbol }}
175+
</div>
176+
</div>
177+
<div
178+
v-if="isHolderOfMintingTypeSelected"
179+
class="mt-4"
180+
>
181+
<p class="mb-2">
182+
{{ $t('mint.collection.permission.holderOfCollection') }}
183+
</p>
184+
<CollectionSearchInput
185+
:collection-id="holderOfCollectionId"
186+
@update:collection="holderOfCollectionId = $event?.collection_id"
187+
/>
188+
</div>
189+
</div>
190+
191+
<div class="flex flex-col w-full">
192+
<div
193+
v-if="permissionSettingWarningMessage"
194+
class="flex items-center gap-2 bg-yellow-50 border border-yellow-200 rounded-md p-3 !mt-2"
195+
>
196+
<NeoIcon
197+
icon="warning"
198+
class="text-yellow-500"
199+
size="small"
200+
/>
201+
202+
<p class="text-sm text-yellow-700">
203+
{{ permissionSettingWarningMessage }}
204+
</p>
205+
</div>
206+
</div>
207+
</div>
208+
</NeoField>
128209
</form>
129210

130211
<div class="flex flex-col !mt-6">
@@ -142,9 +223,9 @@
142223
</template>
143224

144225
<script setup lang="ts">
145-
import { NeoButton, NeoField, NeoInput, NeoModal, NeoSwitch } from '@kodadot1/brick'
226+
import { NeoButton, NeoField, NeoInput, NeoModal, NeoSwitch, NeoSelect, NeoIcon } from '@kodadot1/brick'
146227
import ModalBody from '@/components/shared/modals/ModalBody.vue'
147-
import type { UpdateCollection } from '@/composables/transaction/types'
228+
import { type UpdateCollection, type CollectionMintSetting, CollectionMintSettingType } from '@/composables/transaction/types'
148229
149230
export type CollectionEditMetadata = {
150231
name: string
@@ -153,16 +234,21 @@ export type CollectionEditMetadata = {
153234
imageType: string
154235
banner?: string
155236
max: number | null
237+
mintingSettings: CollectionMintSetting
156238
}
157239
240+
const COLLECTION_MINTING_TYPES_OPTIONS = ([CollectionMintSettingType.Issuer, CollectionMintSettingType.Public, CollectionMintSettingType.HolderOf]).map(type => ({ value: type, text: type }))
241+
158242
const emit = defineEmits(['submit'])
159243
const props = defineProps<{
160244
modelValue: boolean
161245
collection: CollectionEditMetadata
162246
min?: number
163247
}>()
164248
249+
const { $i18n } = useNuxtApp()
165250
const isModalActive = useVModel(props, 'modelValue')
251+
const { chainSymbol, decimals, withDecimals } = useChain()
166252
167253
const name = ref<string>()
168254
const description = ref<string>()
@@ -171,13 +257,22 @@ const banner = ref<File>()
171257
const imageUrl = ref<string>()
172258
const bannerUrl = ref<string>()
173259
const unlimited = ref(true)
260+
const hasMintingPrice = ref(false)
261+
const mintingPrice = ref<number | null>(null)
174262
263+
const selectedMintingType = ref<CollectionMintSettingType | null>(null)
175264
const min = computed(() => props.min || 1)
176265
const max = ref<number | null>(null)
177266
178267
const nameChanged = computed(() => props.collection.name !== name.value)
179268
const hasImageChanged = computed(() => (!imageUrl.value && Boolean(props.collection.image)) || Boolean(image.value))
180269
const originalLogoImageUrl = computed(() => sanitizeIpfsUrl(props.collection.image))
270+
const mintTypeChanged = computed(() => selectedMintingType.value !== props.collection.mintingSettings.mintType)
271+
const mintPriceChanged = computed(() => mintingPrice.value !== originalMintPrice.value)
272+
const originalMintPrice = computed(() => props.collection.mintingSettings.price ? Number(props.collection.mintingSettings.price) / (10 ** decimals.value) : null)
273+
const originalHolderOfCollectionId = computed(() => props.collection.mintingSettings.holderOf)
274+
const holderOfCollectionId = ref<string | undefined>(originalHolderOfCollectionId.value)
275+
const isHolderOfMintingTypeSelected = computed(() => selectedMintingType.value === CollectionMintSettingType.HolderOf)
181276
182277
const disabled = computed(() => {
183278
const hasImage = imageUrl.value
@@ -186,10 +281,17 @@ const disabled = computed(() => {
186281
const descriptionChanged = props.collection.description !== description.value
187282
const hasBannerChanged = (!bannerUrl.value && Boolean(props.collection.banner)) || Boolean(banner.value)
188283
const hasMaxChanged = max.value !== props.collection.max
284+
const holderOfCollectionIdChanged = holderOfCollectionId.value !== originalHolderOfCollectionId.value
285+
const invalidPublicCollection = selectedMintingType.value === 'Public' && !mintingPrice.value
286+
287+
const invalidHolderOfCollection = isHolderOfMintingTypeSelected.value && !holderOfCollectionId.value
189288
190-
return !hasImage || !isNameFilled || (!nameChanged.value && !descriptionChanged && !hasImageChanged.value && !hasBannerChanged && !hasMaxChanged)
289+
return !hasImage || !isNameFilled || invalidHolderOfCollection || invalidPublicCollection
290+
|| (!nameChanged.value && !descriptionChanged && !hasImageChanged.value && !hasBannerChanged && !hasMaxChanged && !mintTypeChanged.value && !mintPriceChanged.value && !holderOfCollectionIdChanged)
191291
})
192292
293+
const permissionSettingWarningMessage = computed(() => selectedMintingType.value && permissionSettingCheckingMap[selectedMintingType.value]?.())
294+
193295
const initLogoImage = () => {
194296
imageUrl.value = originalLogoImageUrl.value
195297
image.value = undefined
@@ -203,6 +305,11 @@ const editCollection = async () => {
203305
imageType: props.collection.imageType,
204306
banner: bannerUrl.value ? banner.value || props.collection.banner : undefined,
205307
max: max.value,
308+
mintingSettings: {
309+
mintType: selectedMintingType.value,
310+
price: hasMintingPrice.value ? String(withDecimals(mintingPrice.value || 0)) : null,
311+
holderOf: holderOfCollectionId.value || originalHolderOfCollectionId.value,
312+
},
206313
} as UpdateCollection)
207314
}
208315
@@ -215,6 +322,55 @@ watch(isModalActive, (value) => {
215322
initLogoImage()
216323
unlimited.value = !props.collection.max
217324
max.value = props.collection.max
325+
326+
// permission
327+
selectedMintingType.value = props.collection.mintingSettings.mintType
328+
hasMintingPrice.value = Boolean(props.collection.mintingSettings.price)
329+
mintingPrice.value = originalMintPrice.value || null
330+
holderOfCollectionId.value = originalHolderOfCollectionId.value
331+
}
332+
}, {
333+
immediate: true,
334+
})
335+
const mintTypeChangeHandlerMap: Record<CollectionMintSettingType, () => void> = {
336+
[CollectionMintSettingType.Issuer]: () => {
337+
hasMintingPrice.value = false
338+
mintingPrice.value = null
339+
},
340+
[CollectionMintSettingType.Public]: () => {
341+
hasMintingPrice.value = true
342+
mintingPrice.value = null
343+
},
344+
[CollectionMintSettingType.HolderOf]: () => {
345+
hasMintingPrice.value = false
346+
mintingPrice.value = null
347+
holderOfCollectionId.value = undefined
348+
},
349+
}
350+
351+
const permissionSettingCheckingMap: Record<CollectionMintSettingType, () => string | undefined> = {
352+
[CollectionMintSettingType.Issuer]: () => {
353+
if (mintingPrice.value) {
354+
return $i18n.t('mint.collection.permission.issuerWarning')
355+
}
356+
},
357+
[CollectionMintSettingType.Public]: () => {
358+
if (!mintingPrice.value || mintingPrice.value <= 0) {
359+
return $i18n.t('mint.collection.permission.publicWarning')
360+
}
361+
return $i18n.t('mint.collection.permission.publicWithPriceWarning')
362+
},
363+
[CollectionMintSettingType.HolderOf]: () => {
364+
if (!holderOfCollectionId.value) {
365+
return $i18n.t('mint.collection.permission.holderOfIdWarning')
366+
}
367+
return $i18n.t('mint.collection.permission.holderOfWarning')
368+
},
369+
}
370+
371+
watch(selectedMintingType, (type, oldType) => {
372+
if (oldType && type && mintTypeChangeHandlerMap[type]) {
373+
mintTypeChangeHandlerMap[type]()
218374
}
219375
})
220376

components/collection/HeroButtonEditCollection.vue

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<template>
22
<NeoDropdownItem
3+
:disabled="!collectionMetadata"
34
@click="isModalActive = true"
45
>
56
{{ $t('moreActions.editCollection') }}
@@ -24,7 +25,8 @@
2425
<script setup lang="ts">
2526
import { NeoDropdownItem } from '@kodadot1/brick'
2627
import { type CollectionEditMetadata } from '@/components/collection/EditModal.vue'
27-
import { Collections, type UpdateCollection } from '@/composables/transaction/types'
28+
import { CollectionMintSettingType, Collections, type UpdateCollection, type CollectionMintSetting } from '@/composables/transaction/types'
29+
import { getCollectionMintSettings } from '@/composables/transaction/mintCollection/utils'
2830
2931
const props = defineProps<{
3032
collection: any
@@ -34,47 +36,76 @@ const { transaction, status, isLoading } = useTransaction()
3436
const { $i18n } = useNuxtApp()
3537
const { urlPrefix } = usePrefix()
3638
const route = useRoute()
37-
39+
const collectionId = route.params.id.toString()
40+
const collectionPermissionSettings = ref<CollectionMintSetting>()
3841
const isModalActive = ref(false)
42+
const editedCollection = ref<UpdateCollection>()
3943
4044
const collectionMetadata = computed(() =>
41-
props.collection
45+
props.collection && collectionPermissionSettings.value
4246
? {
4347
name: props.collection.meta.name,
4448
description: props.collection.meta.description,
4549
image: props.collection.meta.image,
4650
imageType: props.collection.meta.type,
4751
banner: props.collection.meta.banner || undefined,
4852
max: props.collection.max,
53+
mintingSettings: collectionPermissionSettings.value!,
4954
} as CollectionEditMetadata
5055
: null)
5156
5257
const updateMetadata = (a: UpdateCollection, b: UpdateCollection) => {
5358
const getMetadataKey = (m: UpdateCollection) => {
54-
const { max, ...rest } = m
59+
const { max, mintingSettings, ...rest } = m
5560
return JSON.stringify(rest)
5661
}
5762
5863
return getMetadataKey(a) !== getMetadataKey(b)
5964
}
6065
61-
const editCollection = async (collection: UpdateCollection) => {
66+
const shouldUpdatePermission = (a: CollectionMintSetting, b: CollectionMintSetting) => {
67+
return a.price !== b.price || a.mintType !== b.mintType || a.holderOf !== b.holderOf
68+
}
69+
70+
const editCollection = async (updatedCollection?: UpdateCollection) => {
6271
isModalActive.value = false
6372
73+
const collection = updatedCollection || editedCollection.value!
74+
75+
// retry action
76+
if (updatedCollection) {
77+
editedCollection.value = updatedCollection
78+
}
79+
6480
if (!collectionMetadata.value) {
6581
return
6682
}
6783
6884
await transaction({
6985
interaction: Collections.UPDATE_COLLECTION,
70-
collectionId: route.params.id.toString(),
86+
collectionId: collectionId,
7187
collection,
7288
update: {
7389
metadata: updateMetadata(collection, collectionMetadata.value),
7490
max: collection.max !== collectionMetadata.value.max,
91+
permission: shouldUpdatePermission(collection.mintingSettings, collectionPermissionSettings.value!),
7592
},
7693
urlPrefix: urlPrefix.value,
7794
successMessage: $i18n.t('edit.collection.success'),
7895
})
7996
}
97+
98+
watch(computed(() => collectionId), async () => {
99+
const mintSettings = await getCollectionMintSettings(collectionId)
100+
101+
mintSettings.price = mintSettings.price?.replaceAll(',', '')
102+
103+
if (typeof mintSettings.mintType !== 'string') {
104+
mintSettings.holderOf = (mintSettings.mintType as any as { HolderOf: string }).HolderOf
105+
mintSettings.mintType = CollectionMintSettingType.HolderOf
106+
}
107+
collectionPermissionSettings.value = mintSettings
108+
}, {
109+
immediate: true,
110+
})
80111
</script>

0 commit comments

Comments
 (0)