Skip to content

Commit

Permalink
Merge pull request #11217 from Jarsen136/issue-11148
Browse files Browse the repository at this point in the history
feat: edit collection permission
  • Loading branch information
Jarsen136 authored Jan 23, 2025
2 parents 09f5a05 + 0001d9b commit 8c56c63
Show file tree
Hide file tree
Showing 10 changed files with 386 additions and 14 deletions.
162 changes: 159 additions & 3 deletions components/collection/EditModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,87 @@
</div>
</div>
</NeoField>

<NeoField
v-if="isModalActive"
:label="$t('mint.collection.permission.label')"
>
<div class="w-full flex flex-col gap-4">
<div class="flex items-center justify-between">
<p>
{{ $t('mint.mintType') }}
</p>
<NeoSelect
v-model="selectedMintingType"
>
<option
v-for="menu in COLLECTION_MINTING_TYPES_OPTIONS"
:key="menu.value"
:value="menu.value"
>
{{ menu.text }}
</option>
</NeoSelect>
</div>

<div>
<div class="flex justify-between capitalize">
<p>{{ $t(hasMintingPrice ? 'mint.collection.permission.pricePlaceholder' : 'mint.collection.permission.noPriceSet') }}</p>
<NeoSwitch
v-if="isHolderOfMintingTypeSelected"
v-model="hasMintingPrice"
position="left"
/>
</div>
<div
v-if="hasMintingPrice"
class="flex focus-within:!border-border-color border border-k-shade h-12 mt-3"
>
<input
v-model="mintingPrice"
type="number"
step="0.01"
min="0.0001"
pattern="[0-9]+([\.,][0-9]+)?"
class="indent-2.5 border-none outline-none w-20 bg-background-color text-text-color w-full"
:placeholder="$t('mint.collection.permission.pricePlaceholder')"
>
<div class="px-3 flex items-center">
{{ chainSymbol }}
</div>
</div>
<div
v-if="isHolderOfMintingTypeSelected"
class="mt-4"
>
<p class="mb-2">
{{ $t('mint.collection.permission.holderOfCollection') }}
</p>
<CollectionSearchInput
:collection-id="holderOfCollectionId"
@update:collection="holderOfCollectionId = $event?.collection_id"
/>
</div>
</div>

<div class="flex flex-col w-full">
<div
v-if="permissionSettingWarningMessage"
class="flex items-center gap-2 bg-yellow-50 border border-yellow-200 rounded-md p-3 !mt-2"
>
<NeoIcon
icon="warning"
class="text-yellow-500"
size="small"
/>

<p class="text-sm text-yellow-700">
{{ permissionSettingWarningMessage }}
</p>
</div>
</div>
</div>
</NeoField>
</form>

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

<script setup lang="ts">
import { NeoButton, NeoField, NeoInput, NeoModal, NeoSwitch } from '@kodadot1/brick'
import { NeoButton, NeoField, NeoInput, NeoModal, NeoSwitch, NeoSelect, NeoIcon } from '@kodadot1/brick'
import ModalBody from '@/components/shared/modals/ModalBody.vue'
import type { UpdateCollection } from '@/composables/transaction/types'
import { type UpdateCollection, type CollectionMintSetting, CollectionMintSettingType } from '@/composables/transaction/types'
export type CollectionEditMetadata = {
name: string
Expand All @@ -153,16 +234,21 @@ export type CollectionEditMetadata = {
imageType: string
banner?: string
max: number | null
mintingSettings: CollectionMintSetting
}
const COLLECTION_MINTING_TYPES_OPTIONS = ([CollectionMintSettingType.Issuer, CollectionMintSettingType.Public, CollectionMintSettingType.HolderOf]).map(type => ({ value: type, text: type }))
const emit = defineEmits(['submit'])
const props = defineProps<{
modelValue: boolean
collection: CollectionEditMetadata
min?: number
}>()
const { $i18n } = useNuxtApp()
const isModalActive = useVModel(props, 'modelValue')
const { chainSymbol, decimals, withDecimals } = useChain()
const name = ref<string>()
const description = ref<string>()
Expand All @@ -171,13 +257,22 @@ const banner = ref<File>()
const imageUrl = ref<string>()
const bannerUrl = ref<string>()
const unlimited = ref(true)
const hasMintingPrice = ref(false)
const mintingPrice = ref<number | null>(null)
const selectedMintingType = ref<CollectionMintSettingType | null>(null)
const min = computed(() => props.min || 1)
const max = ref<number | null>(null)
const nameChanged = computed(() => props.collection.name !== name.value)
const hasImageChanged = computed(() => (!imageUrl.value && Boolean(props.collection.image)) || Boolean(image.value))
const originalLogoImageUrl = computed(() => sanitizeIpfsUrl(props.collection.image))
const mintTypeChanged = computed(() => selectedMintingType.value !== props.collection.mintingSettings.mintType)
const mintPriceChanged = computed(() => mintingPrice.value !== originalMintPrice.value)
const originalMintPrice = computed(() => props.collection.mintingSettings.price ? Number(props.collection.mintingSettings.price) / (10 ** decimals.value) : null)
const originalHolderOfCollectionId = computed(() => props.collection.mintingSettings.holderOf)
const holderOfCollectionId = ref<string | undefined>(originalHolderOfCollectionId.value)
const isHolderOfMintingTypeSelected = computed(() => selectedMintingType.value === CollectionMintSettingType.HolderOf)
const disabled = computed(() => {
const hasImage = imageUrl.value
Expand All @@ -186,10 +281,17 @@ const disabled = computed(() => {
const descriptionChanged = props.collection.description !== description.value
const hasBannerChanged = (!bannerUrl.value && Boolean(props.collection.banner)) || Boolean(banner.value)
const hasMaxChanged = max.value !== props.collection.max
const holderOfCollectionIdChanged = holderOfCollectionId.value !== originalHolderOfCollectionId.value
const invalidPublicCollection = selectedMintingType.value === 'Public' && !mintingPrice.value
const invalidHolderOfCollection = isHolderOfMintingTypeSelected.value && !holderOfCollectionId.value
return !hasImage || !isNameFilled || (!nameChanged.value && !descriptionChanged && !hasImageChanged.value && !hasBannerChanged && !hasMaxChanged)
return !hasImage || !isNameFilled || invalidHolderOfCollection || invalidPublicCollection
|| (!nameChanged.value && !descriptionChanged && !hasImageChanged.value && !hasBannerChanged && !hasMaxChanged && !mintTypeChanged.value && !mintPriceChanged.value && !holderOfCollectionIdChanged)
})
const permissionSettingWarningMessage = computed(() => selectedMintingType.value && permissionSettingCheckingMap[selectedMintingType.value]?.())
const initLogoImage = () => {
imageUrl.value = originalLogoImageUrl.value
image.value = undefined
Expand All @@ -203,6 +305,11 @@ const editCollection = async () => {
imageType: props.collection.imageType,
banner: bannerUrl.value ? banner.value || props.collection.banner : undefined,
max: max.value,
mintingSettings: {
mintType: selectedMintingType.value,
price: hasMintingPrice.value ? String(withDecimals(mintingPrice.value || 0)) : null,
holderOf: holderOfCollectionId.value || originalHolderOfCollectionId.value,
},
} as UpdateCollection)
}
Expand All @@ -215,6 +322,55 @@ watch(isModalActive, (value) => {
initLogoImage()
unlimited.value = !props.collection.max
max.value = props.collection.max
// permission
selectedMintingType.value = props.collection.mintingSettings.mintType
hasMintingPrice.value = Boolean(props.collection.mintingSettings.price)
mintingPrice.value = originalMintPrice.value || null
holderOfCollectionId.value = originalHolderOfCollectionId.value
}
}, {
immediate: true,
})
const mintTypeChangeHandlerMap: Record<CollectionMintSettingType, () => void> = {
[CollectionMintSettingType.Issuer]: () => {
hasMintingPrice.value = false
mintingPrice.value = null
},
[CollectionMintSettingType.Public]: () => {
hasMintingPrice.value = true
mintingPrice.value = null
},
[CollectionMintSettingType.HolderOf]: () => {
hasMintingPrice.value = false
mintingPrice.value = null
holderOfCollectionId.value = undefined
},
}
const permissionSettingCheckingMap: Record<CollectionMintSettingType, () => string | undefined> = {
[CollectionMintSettingType.Issuer]: () => {
if (mintingPrice.value) {
return $i18n.t('mint.collection.permission.issuerWarning')
}
},
[CollectionMintSettingType.Public]: () => {
if (!mintingPrice.value || mintingPrice.value <= 0) {
return $i18n.t('mint.collection.permission.publicWarning')
}
return $i18n.t('mint.collection.permission.publicWithPriceWarning')
},
[CollectionMintSettingType.HolderOf]: () => {
if (!holderOfCollectionId.value) {
return $i18n.t('mint.collection.permission.holderOfIdWarning')
}
return $i18n.t('mint.collection.permission.holderOfWarning')
},
}
watch(selectedMintingType, (type, oldType) => {
if (oldType && type && mintTypeChangeHandlerMap[type]) {
mintTypeChangeHandlerMap[type]()
}
})
Expand Down
43 changes: 37 additions & 6 deletions components/collection/HeroButtonEditCollection.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<NeoDropdownItem
:disabled="!collectionMetadata"
@click="isModalActive = true"
>
{{ $t('moreActions.editCollection') }}
Expand All @@ -24,7 +25,8 @@
<script setup lang="ts">
import { NeoDropdownItem } from '@kodadot1/brick'
import { type CollectionEditMetadata } from '@/components/collection/EditModal.vue'
import { Collections, type UpdateCollection } from '@/composables/transaction/types'
import { CollectionMintSettingType, Collections, type UpdateCollection, type CollectionMintSetting } from '@/composables/transaction/types'
import { getCollectionMintSettings } from '@/composables/transaction/mintCollection/utils'
const props = defineProps<{
collection: any
Expand All @@ -34,47 +36,76 @@ const { transaction, status, isLoading } = useTransaction()
const { $i18n } = useNuxtApp()
const { urlPrefix } = usePrefix()
const route = useRoute()
const collectionId = route.params.id.toString()
const collectionPermissionSettings = ref<CollectionMintSetting>()
const isModalActive = ref(false)
const editedCollection = ref<UpdateCollection>()
const collectionMetadata = computed(() =>
props.collection
props.collection && collectionPermissionSettings.value
? {
name: props.collection.meta.name,
description: props.collection.meta.description,
image: props.collection.meta.image,
imageType: props.collection.meta.type,
banner: props.collection.meta.banner || undefined,
max: props.collection.max,
mintingSettings: collectionPermissionSettings.value!,
} as CollectionEditMetadata
: null)
const updateMetadata = (a: UpdateCollection, b: UpdateCollection) => {
const getMetadataKey = (m: UpdateCollection) => {
const { max, ...rest } = m
const { max, mintingSettings, ...rest } = m
return JSON.stringify(rest)
}
return getMetadataKey(a) !== getMetadataKey(b)
}
const editCollection = async (collection: UpdateCollection) => {
const shouldUpdatePermission = (a: CollectionMintSetting, b: CollectionMintSetting) => {
return a.price !== b.price || a.mintType !== b.mintType || a.holderOf !== b.holderOf
}
const editCollection = async (updatedCollection?: UpdateCollection) => {
isModalActive.value = false
const collection = updatedCollection || editedCollection.value!
// retry action
if (updatedCollection) {
editedCollection.value = updatedCollection
}
if (!collectionMetadata.value) {
return
}
await transaction({
interaction: Collections.UPDATE_COLLECTION,
collectionId: route.params.id.toString(),
collectionId: collectionId,
collection,
update: {
metadata: updateMetadata(collection, collectionMetadata.value),
max: collection.max !== collectionMetadata.value.max,
permission: shouldUpdatePermission(collection.mintingSettings, collectionPermissionSettings.value!),
},
urlPrefix: urlPrefix.value,
successMessage: $i18n.t('edit.collection.success'),
})
}
watch(computed(() => collectionId), async () => {
const mintSettings = await getCollectionMintSettings(collectionId)
mintSettings.price = mintSettings.price?.replaceAll(',', '')
if (typeof mintSettings.mintType !== 'string') {
mintSettings.holderOf = (mintSettings.mintType as any as { HolderOf: string }).HolderOf
mintSettings.mintType = CollectionMintSettingType.HolderOf
}
collectionPermissionSettings.value = mintSettings
}, {
immediate: true,
})
</script>
Loading

0 comments on commit 8c56c63

Please sign in to comment.