diff --git a/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md index f5739abf13..b3e6d8ca00 100644 --- a/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md @@ -33,7 +33,7 @@ #### Community participation -- [ ] [Are you at KodaDot Discord?](https://discord.gg/35hzy2dXXh) +- [ ] [Are you at KodaDot Ecosystem Telegram?](https://t.me/kodadot_eco) ## Screenshot 📸 diff --git a/ACTIVITY.md b/ACTIVITY.md index eba3684f78..5bc28656fc 100644 --- a/ACTIVITY.md +++ b/ACTIVITY.md @@ -52,36 +52,46 @@ flowchart TD [![Top contributors](https://images.repography.com/23713223/kodadot/nft-gallery/top-contributors/1nMiB_aZjymZHZUDQ6R3hWGHqWUWahnU6VdRYYv2InU/ygTelP2NVzMzr-XPmCeXq2GzAIHSFlcUsZDXKY3Qrl4_table.svg)](https://github.com/kodadot/nft-gallery/graphs/contributors) -### RepoTracker +RepoTracker +--- [Peak on recent activity over KodaDot](https://repo-tracker.com/r/gh/kodadot/nft-gallery) -![image](https://user-images.githubusercontent.com/5887929/232563230-85fcda10-2cd8-46a2-b4aa-a0f1261a0660.png) +![image](https://github.com/kodadot/nft-gallery/assets/5887929/7c8db5f6-3c8b-40c1-a6e8-fa5e0155227c) + +![image](https://github.com/kodadot/nft-gallery/assets/5887929/75a99f4c-54c0-4848-955a-828ba82ab401) -![image](https://user-images.githubusercontent.com/5887929/232563299-39e84aa1-19c8-4031-9488-6813f74f65f3.png) +![image](https://github.com/kodadot/nft-gallery/assets/5887929/1611f8f3-21aa-4f52-a9a8-f4166d8619ab) -### Axiom - Repobeats +![image](https://github.com/kodadot/nft-gallery/assets/5887929/1e7a6ffa-f938-4c08-97cf-98e89eee1e13) + +Axiom - Repobeats +--- ![Alt](https://repobeats.axiom.co/api/embed/0fb5819705db8bf2be040d140b66f04aaf529a30.svg "Repobeats analytics image") -### Crowd.dev - AnalyzeMyRepo +Crowd.dev - AnalyzeMyRepo +--- [Verbose version from AnalyzeMyRepo](https://analyzemyrepo.com/analyze/kodadot/nft-gallery) -## We're constantly growing! +We're constantly growing +--- [![Contributors Over Time](https://contributor-overtime-api.git-contributor.com/contributors-svg?chart=contributorOverTime&repo=kodadot/nft-gallery)](https://git-contributor.com?chart=contributorOverTime&repo=kodadot/nft-gallery) [![Monthly Active Contributors](https://contributor-overtime-api.git-contributor.com/contributors-svg?chart=contributorMonthlyActivity&repo=kodadot/nft-gallery)](https://git-contributor.com?chart=contributorMonthlyActivity&repo=kodadot/nft-gallery) -## Recent Contributors ✨ +Recent Contributors ✨ +--- Check out KodaDot's [monthly statistics](https://github.com/kodadot/nft-gallery/pulse/monthly) and each contributors [activity]((https://github.com/kodadot/nft-gallery/graphs/contributors)) -## Brief code structure +Brief code structure +--- ![Visualization of this repo](.github/diagram.svg) [Interactive visualization](https://octo-repo-visualization.vercel.app/?repo=kodadot%2Fnft-gallery) @@ -90,7 +100,8 @@ Check out KodaDot's [monthly statistics](https://github.com/kodadot/nft-gallery/ [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/kodadot/nft-gallery.svg)](http://isitmaintained.com/project/kodadot/nft-gallery "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/kodadot/nft-gallery.svg)](http://isitmaintained.com/project/kodadot/nft-gallery "Percentage of issues still open") --> -## Open Bounties sorted by bounty size +Open Bounties sorted by bounty size +--- [![Rewards](https://flat.badgen.net/github/label-issues/kodadot/nft-gallery/$/open?scale=2)](https://github.com/kodadot/nft-gallery/issues?q=is%3Aopen+is%3Aissue+label%3A%24) [![Rewards](https://flat.badgen.net/github/label-issues/kodadot/nft-gallery/$$/open?scale=2)](https://github.com/kodadot/nft-gallery/issues?q=is%3Aopen+is%3Aissue+label%3A%24%24+) @@ -98,12 +109,14 @@ Check out KodaDot's [monthly statistics](https://github.com/kodadot/nft-gallery/ [![Rewards](https://flat.badgen.net/github/label-issues/kodadot/nft-gallery/$$$$/open?scale=2)](https://github.com/kodadot/nft-gallery/issues?q=is%3Aopen+is%3Aissue+label%3A%24%24%24%24+) [![Rewards](https://flat.badgen.net/github/label-issues/kodadot/nft-gallery/$$$$$/open?scale=2)](https://github.com/kodadot/nft-gallery/issues?q=is%3Aopen+is%3Aissue+label%3A%24%24%24%24%24+) -## Star history +Star history +--- [![Star History Chart](https://api.star-history.com/svg?repos=kodadot/nft-gallery&type=Date)](https://star-history.com/#kodadot/nft-gallery&Date) -### New Issues and pull requests -- https://www.repotrends.com/kodadot/nft-gallery +New Issues and pull requests in KodaDot +-- +- [repotrends.com/kodadot/nft-gallery](https://www.repotrends.com/kodadot/nft-gallery) ![image](https://user-images.githubusercontent.com/5887929/232562963-f7f42c34-89ea-4783-8dad-a426dd91c262.png) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 443f80b0d2..cf144c25c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ If everything goes well, chances that you will be **rewarded are high**. We might give retro-active reward, where the bounty label wasn't present, **if we like your contribution.** -**For better coordination, please join our [Development channel (#coordination) on Discord](https://discord.gg/4CeHXamhqB)** +**For better coordination, please join our [Development channel (#coordination) on [KodaDot Ecosystem Telegram](https://t.me/kodadot_eco)** ## Getting started diff --git a/HIRING.md b/HIRING.md index 786f3ee9e5..31d07da287 100644 --- a/HIRING.md +++ b/HIRING.md @@ -38,7 +38,7 @@ You can [check recent issues by labels **p1** or **p2**](https://github.com/koda Once you've crossed **10-20 merged pull requests**, which were successfully merged to the upstream `main` branch, you will be **eligible to join our closer team**. -Feel free to reach out `yangwao` on our Discord. +Feel free to reach out `yangwao` on our [KodaDot Ecosystem Telegram](https://t.me/kodadot_eco). We are happy to give you **one or two long-term tasks** to see how you can tackle more challenging and complex issues. ### Reasoning @@ -57,4 +57,4 @@ Landing job at KodaDot could result getting payroll on range between **40k-160k$ #### Questions? -Head's up to our [Discord - channel #jobs](https://dsc.gg/kodadot) +Head's up to our [KodaDot Ecosystem Telegram](https://t.me/kodadot_eco) ~~[Discord - channel #jobs](https://dsc.gg/kodadot)~~ diff --git a/QA.md b/QA.md index 17bf7a66fa..05fefb386d 100644 --- a/QA.md +++ b/QA.md @@ -75,7 +75,7 @@ KodaDot offers paid interviews, valuing your time and contributions. You will be ## Next stage -After completing 10-20 merged pull requests, you may be eligible to join our core team. Contact yangwao on Discord to discuss the next steps and potential long-term tasks. +After completing 10-20 merged pull requests, you may be eligible to join our core team. Contact yangwao on [KodaDot Ecosystem Telegram](https://t.me/kodadot_eco) to discuss the next steps and potential long-term tasks. Our hiring process prioritizes cultural fit and teamwork. We are interested in character traits, problem-solving abilities, and your willingness to help others with their PRs. This approach ensures a strong, long-term team dynamic as our codebase grows in complexity. diff --git a/README.md b/README.md index d06cb6b794..cc32b8b199 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,6 @@ You'll find our goals for KodaDot upcoming future! # Stay Tuned for Updates! - Follow us on [Twitter](https://twitter.com/KodaDot), [Youtube](https://www.youtube.com/channel/UCEULduld5NrqOL49k1KVjoA), [SubStack](https://kodadot.substack.com/) and [Medium](https://blog.kodadot.xyz), [Instagram](https://instagram.com/kodadot.xyz), -- Join our [Telegram KodaDot Ecosystem](https://t.me/kodadot_eco), [Discord](https://discord.gg/u6ymnbz4PR) +- Join our [Telegram KodaDot Ecosystem](https://t.me/kodadot_eco), ~~[Discord](https://discord.gg/u6ymnbz4PR)~~ - [Pick your T-shirt in KodaDot Swag Shop](https://shop.kodadot.xyz), use voucher `readme100` to get 100% OFF, first 10 redemptions only. diff --git a/components/balance/MultipleBalances.vue b/components/balance/MultipleBalances.vue index 570b5e1a82..08eaae6795 100644 --- a/components/balance/MultipleBalances.vue +++ b/components/balance/MultipleBalances.vue @@ -31,10 +31,10 @@
- {{ token.details?.balance }} + {{ formatNumber(token.details?.balance) }}
- ${{ delimiter(token.details?.usd || '0') }} + ${{ formatNumber(token.details?.usd || '0') }}
@@ -48,7 +48,9 @@

{{ $i18n.t('spotlight.total') }}: - ${{ delimiter(identityStore.getTotalUsd) }} + ${{ formatNumber(identityStore.getTotalUsd) }}

@@ -60,8 +62,7 @@ import { decodeAddress, encodeAddress } from '@polkadot/util-crypto' import { CHAINS, ENDPOINT_MAP } from '@kodadot1/static' import { NeoSkeleton } from '@kodadot1/brick' import { balanceOf } from '@kodadot1/sub-api' - -import format from '@/utils/format/balance' +import format, { formatNumber } from '@/utils/format/balance' import { useFiatStore } from '@/stores/fiat' import { calculateExactUsdFromToken } from '@/utils/calculation' import { getAssetIdByAccount } from '@/utils/api/bsx/query' @@ -115,16 +116,6 @@ const currentNetwork = computed(() => isTestnet.value ? 'test-network' : 'main-network' ) -function delimiter(amount: string | number) { - const formatAmount = typeof amount === 'number' ? amount.toString() : amount - const number = parseFloat(formatAmount.replace(/,/g, '')) - - return number.toLocaleString('en-US', { - minimumFractionDigits: 0, - maximumFractionDigits: 2, - }) -} - const fiatStore = useFiatStore() function calculateUsd(amount: string, token = 'KSM') { if (!amount) { @@ -174,8 +165,7 @@ async function getBalance(chainName: string, token = 'KSM', tokenId = 0) { selectedTokenId = await getAssetIdByAccount(api, prefixAddress) } - const balance = delimiter(currentBalance) - const usd = calculateUsd(balance, token) + const usd = calculateUsd(currentBalance, token) identityStore.setMultiBalances({ address: defaultAddress, @@ -183,7 +173,7 @@ async function getBalance(chainName: string, token = 'KSM', tokenId = 0) { [chainName]: { [token.toLowerCase()]: { address: prefixAddress, - balance, + balance: currentBalance, nativeBalance, usd, selected: selectedTokenId === String(tokenId), diff --git a/components/base/types.ts b/components/base/types.ts index 9f9d8d6df6..34dc8244e4 100644 --- a/components/base/types.ts +++ b/components/base/types.ts @@ -41,3 +41,11 @@ export interface CarouselNFT extends ItemResources { collectionName?: string chain?: Prefix } + +export interface BaseNFTMeta { + id: string + image?: string + animationUrl?: string + name?: string + description?: string +} diff --git a/components/blog/BlogPost.vue b/components/blog/BlogPost.vue index cd98a58959..854a26ae28 100644 --- a/components/blog/BlogPost.vue +++ b/components/blog/BlogPost.vue @@ -1,36 +1,81 @@ - diff --git a/components/gallery/GalleryItemTabsPanel/GalleryItemActivityTable.vue b/components/gallery/GalleryItemTabsPanel/GalleryItemActivityTable.vue index 791e4b0375..aa7a957dac 100644 --- a/components/gallery/GalleryItemTabsPanel/GalleryItemActivityTable.vue +++ b/components/gallery/GalleryItemTabsPanel/GalleryItemActivityTable.vue @@ -21,7 +21,10 @@ field="meta" :label="`${$t(`tabs.tabActivity.price`)} (${chainSymbol})`">

- {{ formatPrice(props.row.meta) }} + {{ formatPrice(props.row.meta)[0] }} + + (${{ formatPrice(props.row.meta)[1] }})

@@ -100,8 +103,12 @@ import { NeoTooltip, } from '@kodadot1/brick' import { formatToNow } from '@/utils/format/time' -import formatBalance from '@/utils/format/balance' +import formatBalance, { + formatNumber, + withoutDigitSeparator, +} from '@/utils/format/balance' import { parseDate } from '@/utils/datetime' +import { getApproximatePriceOf } from '@/utils/coingecko' import type { Interaction } from '@/components/rmrk/service/scheme' import useSubscriptionGraphql from '@/composables/useSubscriptionGraphql' @@ -113,6 +120,11 @@ const dprops = defineProps<{ const { decimals, chainSymbol } = useChain() const { urlPrefix } = usePrefix() +const tokenPrice = ref(0) + +onMounted(async () => { + tokenPrice.value = await getApproximatePriceOf(chainSymbol.value) +}) const interaction = urlPrefix.value === 'ksm' @@ -159,7 +171,11 @@ watchEffect(() => { }) const formatPrice = (price) => { - return formatBalance(price, decimals.value, false) + const tokenAmount = formatBalance(price, decimals.value, false) + const flatPrice = `${formatNumber( + Number(withoutDigitSeparator(tokenAmount)) * tokenPrice.value + )}` + return [formatNumber(tokenAmount), flatPrice] } diff --git a/components/shared/gallery/GalleryItemCardList.vue b/components/shared/gallery/GalleryItemCardList.vue deleted file mode 100644 index 7ead6ea856..0000000000 --- a/components/shared/gallery/GalleryItemCardList.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/components/shared/gallery/NftCard.vue b/components/shared/gallery/NftCard.vue deleted file mode 100644 index 4b1788074a..0000000000 --- a/components/shared/gallery/NftCard.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/components/teleport/Teleport.vue b/components/teleport/Teleport.vue index acb4332844..bdd0842bd5 100644 --- a/components/teleport/Teleport.vue +++ b/components/teleport/Teleport.vue @@ -2,11 +2,11 @@
-

+

{{ $i18n.t('teleport.page') }} -

+

-

{{ $i18n.t('teleport.from') }}

+

{{ $i18n.t('teleport.from') }}

-

{{ $i18n.t('teleport.to') }}

+

{{ $i18n.t('teleport.to') }}

import { web3Enable } from '@polkadot/extension-dapp' import '@polkadot/api-augment' -import { toDefaultAddress } from '@/utils/account' +import { getss58AddressByPrefix, toDefaultAddress } from '@/utils/account' import { getAddress } from '@/utils/extension' import { Chain, @@ -101,7 +101,6 @@ import { getChainEndpointByPrefix } from '@/utils/chain' import { txCb } from '@/utils/transactionExecutor' import TeleportTabs from './TeleportTabs.vue' import { NeoButton } from '@kodadot1/brick' -import { getss58AddressByPrefix } from '@/utils/account' import { blockExplorerOf } from '@/utils/config/chain.config' import { simpleDivision } from '@/utils/balance' import { useFiatStore } from '@/stores/fiat' diff --git a/components/transfer/Transfer.vue b/components/transfer/Transfer.vue index 9c743c020d..218b16bddd 100644 --- a/components/transfer/Transfer.vue +++ b/components/transfer/Transfer.vue @@ -7,7 +7,13 @@ 'theme-background-color k-shadow border py-8 px-6': !isMobile, }, ]"> - +

@@ -243,7 +249,6 @@ import { } from '@/utils/format/balance' import { getNumberSumOfObjectField } from '@/utils/math' import { useFiatStore } from '@/stores/fiat' -import { useIdentityStore } from '@/stores/identity' import Avatar from '@/components/shared/Avatar.vue' import Identity from '@/components/identity/IdentityIndex.vue' import { getMovedItemToFront } from '@/utils/objects' @@ -260,6 +265,9 @@ import { } from '@kodadot1/brick' import TransferTokenTabs, { TransferTokenTab } from './TransferTokenTabs.vue' import { TokenDetails } from '@/composables/useToken' +import AddressInput from '@/components/shared/AddressInput.vue' +import TransactionLoader from '@/components/shared/TransactionLoader.vue' +import { ApiPromise } from '@polkadot/api' const Money = defineAsyncComponent( () => import('@/components/shared/format/Money.vue') ) @@ -269,34 +277,45 @@ const router = useRouter() const { $consola, $i18n } = useNuxtApp() const { unit, decimals } = useChain() const { apiInstance } = useApi() -const { urlPrefix, setUrlPrefix } = usePrefix() +const { urlPrefix } = usePrefix() const { isLogIn, accountId } = useAuth() -const identityStore = useIdentityStore() +const { getBalance } = useBalance() const { fetchFiatPrice, getCurrentTokenValue } = useFiatStore() const { initTransactionLoader, isLoading, resolveStatus, status } = useTransactionStatus() const { toast } = useToast() const isTransferModalVisible = ref(false) +const isLoaderModalVisible = ref(false) + +watch(isLoading, (newValue, oldValue) => { + // trigger modal only when loading change from false => true + // we want to keep modal open when loading changes true => false + if (newValue && !oldValue) { + isLoaderModalVisible.value = isLoading.value + } +}) export type TargetAddress = { - address?: string + address: string usd?: number | string token?: number | string } const isMobile = computed(() => useWindowSize().width.value <= 1024) +const balance = computed(() => getBalance(unit.value) || 0) const transactionValue = ref('') const sendSameAmount = ref(false) const displayUnit = ref<'token' | 'usd'>('token') const { getTokenIconBySymbol } = useIcon() -const { tokens, getPrefixByToken, availableTokens } = useToken() + +const { tokens } = useToken() const selectedTabFirst = ref(true) const tokenIcon = computed(() => getTokenIconBySymbol(unit.value)) const tokenTabs = ref([]) -const targetAddresses = ref([{}]) +const targetAddresses = ref([{ address: '' }]) const hasValidTarget = computed(() => targetAddresses.value.some((item) => isAddress(item.address) && item.token) @@ -308,8 +327,6 @@ const displayTotalValue = computed(() => : [`${totalTokenAmount.value} ${unit.value}`, `$${totalUsdValue.value}`] ) -const balance = computed(() => identityStore.getAuthBalance) - const disabled = computed( () => !isLogIn.value || @@ -319,20 +336,16 @@ const disabled = computed( const handleTokenSelect = (newToken: string) => { selectedTabFirst.value = false - const token = tokens.value.find((t) => t.symbol === newToken) - if (token) { - const chain = getPrefixByToken(token.symbol) - - if (!chain) { - $consola.error( - `[ERR: INVALID TOKEN] Chain for token ${token.symbol} is not valid` - ) - return - } + const token = tokens.value.find((t) => t.symbol === newToken) - setUrlPrefix(chain) + if (!token) { + return } + + routerReplace({ + params: { prefix: token.defaultChain }, + }) } const generateTokenTabs = ( @@ -478,11 +491,7 @@ const unifyAddressAmount = (target: TargetAddress) => { const updateTargetAdressesOnTokenSwitch = () => { targetAddresses.value.forEach((targetAddress) => { - if (displayUnit.value === 'usd') { - onUsdFieldChange(targetAddress) - } else { - onAmountFieldChange(targetAddress) - } + onUsdFieldChange(targetAddress) }) } @@ -503,39 +512,71 @@ const handleOpenConfirmModal = () => { } } +const getAmountToTransfer = (amount: number, decimals: number) => + String(calculateBalance(Number(amount), decimals)) + +interface TransferParams { + api: ApiPromise + decimals: number +} + +const getMultipleAddressesTransferParams = ({ + api, + decimals, +}: TransferParams) => { + const arg = [ + targetAddresses.value.map((target) => { + const amountToTransfer = getAmountToTransfer( + target.token as number, + decimals + ) + + return api.tx.balances.transfer( + target.address as string, + amountToTransfer + ) + }), + ] + + return [api.tx.utility.batch, arg] +} + +const getSingleAddressTransferParams = ({ api, decimals }: TransferParams) => { + const target = targetAddresses.value[0] + + const amountToTransfer = getAmountToTransfer(target.token as number, decimals) + + return [ + api.tx.balances.transfer, + [target.address as string, amountToTransfer], + ] +} + const submit = async ( event: any, usedNodeUrls: string[] = [] ): Promise => { - showNotification(`${route.query.target ? 'Sent for Sign' : 'Dispatched'}`) isTransferModalVisible.value = false initTransactionLoader() try { const api = await apiInstance.value const numOfTargetAddresses = targetAddresses.value.length - const cb = - numOfTargetAddresses > 1 ? api.tx.utility.batch : api.tx.balances.transfer - const arg = - numOfTargetAddresses > 1 - ? [ - targetAddresses.value.map((target) => { - const amountToTransfer = String( - calculateBalance(Number(target.token), decimals.value) - ) - return api.tx.balances.transfer( - target.address as string, - amountToTransfer - ) - }), - ] - : [ - targetAddresses.value[0].address as string, - calculateBalance( - Number(targetAddresses.value[0].token), - decimals.value - ), - ] + const multipleAddresses = numOfTargetAddresses > 1 + + let cb, arg + + if (multipleAddresses) { + ;[cb, arg] = getMultipleAddressesTransferParams({ + api, + decimals: decimals.value as number, + }) + } else { + ;[cb, arg] = getSingleAddressTransferParams({ + api, + decimals: decimals.value as number, + }) + } const tx = await exec( accountId.value, @@ -543,20 +584,20 @@ const submit = async ( cb, arg, txCb( - async (blockHash) => { + () => { transactionValue.value = execResultValue(tx) - const header = await api.rpc.chain.getHeader(blockHash) - const blockNumber = header.number.toString() - showNotification( - `[${unit.value}] Transfered ${totalTokenAmount.value} ${unit.value} in block ${blockNumber}`, - notificationTypes.success - ) + targetAddresses.value = [{ address: '' }] - targetAddresses.value = [{}] - if (route.query && !route.query.donation) { - router.push(route.path) - } + // not sure what is the purpose of this + // but it causes the explorer url in Transaction Loader to become wrong + // after the transaction is finalized + // also causes: + //https://github.com/kodadot/nft-gallery/issues/6944 + + // if (route.query && !route.query.donation) { + // router.push(route.path) + // } isLoading.value = false }, @@ -572,6 +613,7 @@ const submit = async ( if (e.message === 'Cancelled') { showNotification(e.message, notificationTypes.warn) isLoading.value = false + isLoaderModalVisible.value = false return } @@ -625,48 +667,26 @@ const addAddress = () => { }) } -const syncQueryToken = () => { - const { query } = route - - const token = query.token?.toString() - - if (!token || !availableTokens.includes(token)) { - return - } - - const chain = getPrefixByToken(token) - - if (!chain) { - return - } - - setUrlPrefix(chain) -} - -watch( - route, - () => { - syncQueryToken() - }, - { immediate: true, deep: true } -) - onMounted(() => { fetchFiatPrice().then(checkQueryParams) }) +const routerReplace = ({ params = {}, query = {} }) => { + router + .replace({ + params: params, + query: { + ...route.query, + ...query, + }, + }) + .catch(() => null) // null to further not throw navigation errors +} + watchDebounced( () => targetAddresses.value[0].usd, (usdamount) => { - router - .replace({ - query: { - ...route.query, - usdamount: (usdamount || 0).toString(), - token: unit.value, - }, - }) - .catch(() => null) // null to further not throw navigation errors + routerReplace({ query: { usdamount: (usdamount || 0).toString() } }) }, { debounce: 300 } ) diff --git a/composables/massmint/parsers/parseTxt.ts b/composables/massmint/parsers/parseTxt.ts index a28a8e0a98..88e61a9b99 100644 --- a/composables/massmint/parsers/parseTxt.ts +++ b/composables/massmint/parsers/parseTxt.ts @@ -56,26 +56,46 @@ function parsePriceAndCurrency(fieldValue: string): { return { price: undefined, currency: undefined } } +const updateEntry = (entry, line) => { + const parsedField = parseField(line) + + if (!parsedField) { + return + } + const { fieldName, fieldValue } = parsedField + if (fieldName === 'price') { + const { price, currency } = parsePriceAndCurrency(fieldValue) + entry.price = price + entry.currency = currency + } else { + entry[fieldName] = fieldValue + } +} + function processBlock(block: string): Partial { const lines = block.split(/\r?\n/) const entry: Partial = {} - for (const line of lines) { - const parsedField = parseField(line) - - if (parsedField) { - const { fieldName, fieldValue } = parsedField - if (fieldName === 'price') { - const { price, currency } = parsePriceAndCurrency(fieldValue) - entry.price = price - entry.currency = currency - } else { - entry[fieldName] = fieldValue - } + lines.forEach((line) => updateEntry(entry, line)) + return entry +} + +const updateEntries = (entries, block) => { + const entry = processBlock(block) + const { $consola } = useNuxtApp() + + if (entry.file) { + entries[entry.file] = { + file: entry.file, + name: entry.name, + description: entry.description, + price: entry.price, + currency: entry.currency, + valid: isValidEntry(entry), } + } else { + $consola.error('Unable to extract file name from invalid block') } - - return entry } export function parseTxt( @@ -87,26 +107,11 @@ export function parseTxt( const entries: Record = {} - for (const block of blocks) { - const entry = processBlock(block) - - if (entry.file) { - entries[entry.file] = { - file: entry.file, - name: entry.name || undefined, - description: entry.description || undefined, - price: entry.price || undefined, - currency: entry.currency || undefined, - valid: isValidEntry(entry), - } - } else { - $consola.error('Unable to extract file name from invalid block') - } - } + blocks.forEach((block) => updateEntries(entries, block)) if (Object.keys(entries).length === 0) { $consola.error('Invalid TXT file structure') - return undefined + return } return entries diff --git a/composables/transaction/mintToken/transactionMintBasilisk.ts b/composables/transaction/mintToken/transactionMintBasilisk.ts index 23d3fb748c..006915f1b1 100644 --- a/composables/transaction/mintToken/transactionMintBasilisk.ts +++ b/composables/transaction/mintToken/transactionMintBasilisk.ts @@ -1,12 +1,8 @@ -import type { - ActionMintToken, - MintTokenParams, - MintedCollection, - TokenToMint, -} from '../types' +import type { ActionMintToken, MintedCollection, TokenToMint } from '../types' import { isRoyaltyValid } from '@/utils/royalty' import { constructMeta } from './constructMeta' import { BaseMintedCollection } from '@/components/base/types' +import { expandCopies, transactionFactory } from './utils' const prepareTokenMintArgs = async ( token: TokenToMint, @@ -42,8 +38,9 @@ const prepareTokenMintArgs = async ( const getArgs = async (item: ActionMintToken, api) => { const { $consola } = useNuxtApp() - const tokens = Array.isArray(item.token) ? item.token : [item.token] - + const tokens = expandCopies( + Array.isArray(item.token) ? item.token : [item.token] + ) const arg = ( await Promise.all( tokens.map((token, i) => { @@ -60,34 +57,4 @@ const getArgs = async (item: ActionMintToken, api) => { return [arg] } -export async function execMintBasilisk({ - item, - api, - executeTransaction, - isLoading, - status, -}: MintTokenParams) { - const { $i18n } = useNuxtApp() - isLoading.value = true - status.value = 'loader.ipfs' - const args = await getArgs(item, api) - - const nameInNotifications = Array.isArray(item.token) - ? item.token.map((t) => t.name).join(', ') - : item.token.name - - executeTransaction({ - cb: api.tx.utility.batchAll, - arg: args, - successMessage: - item.successMessage || - ((blockNumber) => - $i18n.t('mint.mintNFTSuccess', { - name: nameInNotifications, - block: blockNumber, - })), - errorMessage: - item.errorMessage || - $i18n.t('mint.errorCreateNewNft', { name: nameInNotifications }), - }) -} +export const execMintBasilisk = transactionFactory(getArgs) diff --git a/composables/transaction/mintToken/transactionMintRmrk.ts b/composables/transaction/mintToken/transactionMintRmrk.ts index e097193f94..219b9799e5 100644 --- a/composables/transaction/mintToken/transactionMintRmrk.ts +++ b/composables/transaction/mintToken/transactionMintRmrk.ts @@ -26,7 +26,7 @@ import { } from '../types' import { constructMeta } from './constructMeta' import { isRoyaltyValid } from '@/utils/royalty' -import { calculateFees, copiesToMint } from './utils' +import { calculateFees, copiesToMint, getNameInNotifications } from './utils' const getOnChainProperties = ({ tags, royalty, hasRoyalty }: TokenToMint) => { let onChainProperties = convertAttributesToProperties(tags) @@ -151,9 +151,7 @@ export async function execMintRmrk({ status.value = 'loader.ipfs' const { args, createdNFTs } = await getArgs(item, api) - const nameInNotifications = Array.isArray(item.token) - ? item.token.map((t) => t.name).join(', ') - : item.token.name + const nameInNotifications = getNameInNotifications(item) const isSingle = args.length === 1 const cb = isSingle ? api.tx.system.remark : api.tx.utility.batchAll diff --git a/composables/transaction/mintToken/transactionMintStatemine.ts b/composables/transaction/mintToken/transactionMintStatemine.ts index 4bf171cb17..5dec084a50 100644 --- a/composables/transaction/mintToken/transactionMintStatemine.ts +++ b/composables/transaction/mintToken/transactionMintStatemine.ts @@ -1,12 +1,8 @@ import { BaseMintedCollection } from '@/components/base/types' -import type { - ActionMintToken, - MintTokenParams, - MintedCollection, -} from '../types' +import type { ActionMintToken, MintedCollection } from '../types' import { TokenToMint } from '../types' import { constructMeta } from './constructMeta' -import { calculateFees, copiesToMint } from './utils' +import { calculateFees, expandCopies, transactionFactory } from './utils' import { canSupport } from '@/utils/support' type id = { id: number } @@ -27,24 +23,6 @@ export const assignIds = (tokens: T[]): (T & id)[] => { }) } -export const expandCopies = (tokens: T[]): T[] => { - return tokens.flatMap((token) => { - const copies = copiesToMint(token) - if (copies === 1) { - return token - } - - return Array(copies) - .fill(null) - .map((_, index) => { - return { - ...token, - name: token.postfix ? `${token.name} #${index + 1}` : token.name, - } - }) - }) -} - export const prepareTokenMintArgs = async (token: TokenToMint & id, api) => { const { id: collectionId } = token.selectedCollection as BaseMintedCollection const { price, id: nextId } = token @@ -117,35 +95,4 @@ const getArgs = async (item: ActionMintToken, api) => { return [[...arg.flat(), ...supportInteraction]] } -export async function execMintStatemine({ - item, - api, - executeTransaction, - isLoading, - status, -}: MintTokenParams) { - const { $i18n } = useNuxtApp() - - isLoading.value = true - status.value = 'loader.ipfs' - const args = await getArgs(item, api) - - const nameInNotifications = Array.isArray(item.token) - ? item.token.map((t) => t.name).join(', ') - : item.token.name - - executeTransaction({ - cb: api.tx.utility.batchAll, - arg: args, - successMessage: - item.successMessage || - ((blockNumber) => - $i18n.t('mint.mintNFTSuccess', { - name: nameInNotifications, - block: blockNumber, - })), - errorMessage: - item.errorMessage || - $i18n.t('mint.errorCreateNewNft', { name: nameInNotifications }), - }) -} +export const execMintStatemine = transactionFactory(getArgs) diff --git a/composables/transaction/mintToken/utils.ts b/composables/transaction/mintToken/utils.ts index c45dc0ef05..b2d613d1b7 100644 --- a/composables/transaction/mintToken/utils.ts +++ b/composables/transaction/mintToken/utils.ts @@ -1,5 +1,11 @@ import { usePreferencesStore } from '@/stores/preferences' -import { Max, MintedCollection, TokenToMint } from '../types' +import { + ActionMintToken, + Max, + MintTokenParams, + MintedCollection, + TokenToMint, +} from '../types' export const copiesToMint = (token: T): number => { const { copies, selectedCollection } = token @@ -22,3 +28,55 @@ export const calculateFees = () => { return { enabledFees, feeMultiplier } } + +export const getNameInNotifications = (item: ActionMintToken) => { + return Array.isArray(item.token) + ? item.token.map((t) => t.name).join(', ') + : item.token.name +} + +export const transactionFactory = (getArgs) => { + return async (mintTokenParams: MintTokenParams) => { + const { item, api, executeTransaction, isLoading, status } = mintTokenParams + const { $i18n } = useNuxtApp() + + isLoading.value = true + status.value = 'loader.ipfs' + const args = await getArgs(item, api) + + const nameInNotifications = getNameInNotifications(item) + + executeTransaction({ + cb: api.tx.utility.batchAll, + arg: args, + successMessage: + item.successMessage || + ((blockNumber) => + $i18n.t('mint.mintNFTSuccess', { + name: nameInNotifications, + block: blockNumber, + })), + errorMessage: + item.errorMessage || + $i18n.t('mint.errorCreateNewNft', { name: nameInNotifications }), + }) + } +} + +export const expandCopies = (tokens: T[]): T[] => { + return tokens.flatMap((token) => { + const copies = copiesToMint(token) + if (copies === 1) { + return token + } + + return Array(copies) + .fill(null) + .map((_, index) => { + return { + ...token, + name: token.postfix ? `${token.name} #${index + 1}` : token.name, + } + }) + }) +} diff --git a/composables/transaction/transactionList.ts b/composables/transaction/transactionList.ts index 3b867c54ac..3d2d2adf4f 100644 --- a/composables/transaction/transactionList.ts +++ b/composables/transaction/transactionList.ts @@ -51,78 +51,84 @@ const createKusamaInteraction = (item: ActionList) => { .filter((interaction): interaction is string => interaction !== undefined) } -export function execListTx(item: ActionList, api, executeTransaction) { - const isSingle = !Array.isArray(item.token) +const execKsm = (isSingle: boolean, item: ActionList, api) => { + const interaction = createKusamaInteraction(item) + if (!interaction) { + return + } + const args = isSingle + ? interaction + : (interaction as string[]).map((interaction) => + api.tx.system.remark(interaction) + ) + const cb = isSingle ? api.tx.system.remark : api.tx.utility.batchAll + return { + cb, + arg: [args], + } +} - if (item.urlPrefix === 'rmrk' || item.urlPrefix === 'ksm') { - const interaction = createKusamaInteraction(item) - if (!interaction) { - return +const execBsx = (isSingle: boolean, item: ActionList, api) => { + if (isSingle) { + const token = item.token as TokenToList + return { + cb: getApiCall(api, item.urlPrefix, Interaction.LIST), + arg: bsxParamResolver(token.nftId, Interaction.LIST, token.price), } - const args = isSingle - ? interaction - : (interaction as string[]).map((interaction) => - api.tx.system.remark(interaction) - ) - const cb = isSingle ? api.tx.system.remark : api.tx.utility.batchAll - executeTransaction({ - cb, + } else { + const tokens = item.token as TokenToList[] + const cb = getApiCall(api, item.urlPrefix, Interaction.LIST) + const args = tokens.map((token) => + cb(...bsxParamResolver(token.nftId, Interaction.LIST, token.price)) + ) + return { + cb: api.tx.utility.batchAll, arg: [args], - successMessage: item.successMessage, - errorMessage: item.errorMessage, - }) + } + } +} +const execAhkOrAhp = (isSingle: boolean, item: ActionList, api) => { + const getParams = (token: TokenToList) => { + const legacy = isLegacy(token.nftId) + const paramResolver = assetHubParamResolver(legacy) + return { legacy, paramResolver } } - if (item.urlPrefix === 'snek' || item.urlPrefix === 'bsx') { - if (isSingle) { - const token = item.token as TokenToList - executeTransaction({ - cb: getApiCall(api, item.urlPrefix, Interaction.LIST), - arg: bsxParamResolver(token.nftId, Interaction.LIST, token.price), - successMessage: item.successMessage, - errorMessage: item.errorMessage, - }) - } else { - const tokens = item.token as TokenToList[] - const cb = getApiCall(api, item.urlPrefix, Interaction.LIST) - const args = tokens.map((token) => - cb(...bsxParamResolver(token.nftId, Interaction.LIST, token.price)) - ) - executeTransaction({ - cb: api.tx.utility.batchAll, - arg: [args], - successMessage: item.successMessage, - errorMessage: item.errorMessage, - }) + if (isSingle) { + const token = item.token as TokenToList + const { legacy, paramResolver } = getParams(token) + return { + cb: getApiCall(api, item.urlPrefix, Interaction.LIST, legacy), + arg: paramResolver(token.nftId, Interaction.LIST, token.price), } + } else { + const tokens = item.token as TokenToList[] + const args = tokens.map((token) => { + const { legacy, paramResolver } = getParams(token) + const cb = getApiCall(api, item.urlPrefix, Interaction.LIST, legacy) + return cb(...paramResolver(token.nftId, Interaction.LIST, token.price)) + }) + + return { cb: api.tx.utility.batchAll, arg: [args] } } +} - if (item.urlPrefix === 'ahk' || item.urlPrefix === 'ahp') { - if (isSingle) { - const token = item.token as TokenToList - const legacy = isLegacy(token.nftId) - const paramResolver = assetHubParamResolver(legacy) - executeTransaction({ - cb: getApiCall(api, item.urlPrefix, Interaction.LIST, legacy), - arg: paramResolver(token.nftId, Interaction.LIST, token.price), - successMessage: item.successMessage, - errorMessage: item.errorMessage, - }) - } else { - const tokens = item.token as TokenToList[] - const args = tokens.map((token) => { - const legacy = isLegacy(token.nftId) - const cb = getApiCall(api, item.urlPrefix, Interaction.LIST, legacy) - const paramResolver = assetHubParamResolver(legacy) - return cb(...paramResolver(token.nftId, Interaction.LIST, token.price)) - }) - - executeTransaction({ - cb: api.tx.utility.batchAll, - arg: [args], - successMessage: item.successMessage, - errorMessage: item.errorMessage, - }) - } +export function execListTx(item: ActionList, api, executeTransaction) { + const isSingle = !Array.isArray(item.token) + + const fnMap = { + rmrk: execKsm, + ksm: execKsm, + snek: execBsx, + bsx: execBsx, + ahk: execAhkOrAhp, + ahp: execAhkOrAhp, + } + + const { cb, arg } = + fnMap[item.urlPrefix]?.call(null, isSingle, item, api) || {} + if (cb && arg) { + const { successMessage, errorMessage } = item + executeTransaction({ cb, arg, successMessage, errorMessage }) } } diff --git a/composables/transaction/transactionOfferAccept.ts b/composables/transaction/transactionOfferAccept.ts index b1a3ee44fe..8126078ee8 100644 --- a/composables/transaction/transactionOfferAccept.ts +++ b/composables/transaction/transactionOfferAccept.ts @@ -1,23 +1,3 @@ -import { warningMessage } from '@/utils/notification' -import { tokenIdToRoute } from '@/components/unique/utils' +import { transactionOfferFactory } from './utils' -import type { ActionWithdrawOffer } from './types' - -export function execAcceptOfferTx( - params: ActionWithdrawOffer, - api, - executeTransaction -) { - try { - const { id, item } = tokenIdToRoute(params.nftId) - const args = [id, item, params.maker] - const cb = api.tx.marketplace.acceptOffer - - executeTransaction({ - cb, - arg: args, - }) - } catch (error) { - warningMessage(error) - } -} +export const execAcceptOfferTx = transactionOfferFactory('acceptOffer') diff --git a/composables/transaction/transactionOfferWithdraw.ts b/composables/transaction/transactionOfferWithdraw.ts index 8bb11efc45..761c594306 100644 --- a/composables/transaction/transactionOfferWithdraw.ts +++ b/composables/transaction/transactionOfferWithdraw.ts @@ -1,23 +1,3 @@ -import { warningMessage } from '@/utils/notification' -import { tokenIdToRoute } from '@/components/unique/utils' +import { transactionOfferFactory } from './utils' -import type { ActionWithdrawOffer } from './types' - -export function execWithdrawOfferTx( - params: ActionWithdrawOffer, - api, - executeTransaction -) { - try { - const { id, item } = tokenIdToRoute(params.nftId) - const args = [id, item, params.maker] - const cb = api.tx.marketplace.withdrawOffer - - executeTransaction({ - cb, - arg: args, - }) - } catch (error) { - warningMessage(error) - } -} +export const execWithdrawOfferTx = transactionOfferFactory('withdrawOffer') diff --git a/composables/transaction/utils.ts b/composables/transaction/utils.ts new file mode 100644 index 0000000000..168512c3a0 --- /dev/null +++ b/composables/transaction/utils.ts @@ -0,0 +1,20 @@ +import { warningMessage } from '@/utils/notification' +import { tokenIdToRoute } from '@/components/unique/utils' + +import type { ActionWithdrawOffer } from './types' + +export function transactionOfferFactory(key: 'acceptOffer' | 'withdrawOffer') { + return function (params: ActionWithdrawOffer, api, executeTransaction) { + try { + const { id, item } = tokenIdToRoute(params.nftId) + const args = [id, item, params.maker] + const cb = api.tx.marketplace[key] + executeTransaction({ + cb, + arg: args, + }) + } catch (error) { + warningMessage(error) + } + } +} diff --git a/composables/useBalance.ts b/composables/useBalance.ts index 9c2672369d..81a182f43b 100644 --- a/composables/useBalance.ts +++ b/composables/useBalance.ts @@ -4,26 +4,33 @@ import { getKusamaAssetId } from '@/utils/api/bsx/query' export default function () { const { urlPrefix } = usePrefix() const identityStore = useIdentityStore() - const balance = computed(() => { + + const getBalance = (token: string) => { + token = token.toLocaleLowerCase() switch (urlPrefix.value) { case 'rmrk': case 'ksm': case 'ahk': case 'ahp': + case 'dot': return identityStore.getAuthBalance case 'bsx': - return identityStore.multiBalances.chains.basilisk?.ksm?.nativeBalance + return identityStore.multiBalances.chains.basilisk?.[token] + ?.nativeBalance case 'snek': - return identityStore.multiBalances.chains['basilisk-testnet']?.ksm + return identityStore.multiBalances.chains['basilisk-testnet']?.[token] ?.nativeBalance default: return identityStore.getTokenBalanceOf( getKusamaAssetId(urlPrefix.value) ) } - }) + } + + const balance = computed(() => getBalance('KSM')) return { balance, + getBalance, } } diff --git a/composables/useBlockTime.ts b/composables/useBlockTime.ts new file mode 100644 index 0000000000..c922ccf4a5 --- /dev/null +++ b/composables/useBlockTime.ts @@ -0,0 +1,21 @@ +import { PartialConfig } from '@/utils/config/types' + +export default function () { + const { urlPrefix } = usePrefix() + + const paraChainBlockTime = 12 //seconds + const relayChainBlockTime = 6 //seconds + + const chainBlockTimes: PartialConfig = { + ksm: relayChainBlockTime, + dot: relayChainBlockTime, + rmrk: relayChainBlockTime, + } + + const blocktime = computed( + () => chainBlockTimes[urlPrefix.value] ?? paraChainBlockTime + ) + return { + blocktime, + } +} diff --git a/composables/useNft.ts b/composables/useNft.ts index 2660b7c4e9..1d737ca966 100644 --- a/composables/useNft.ts +++ b/composables/useNft.ts @@ -1,5 +1,6 @@ import type { NFT, NFTMetadata } from '@/components/rmrk/service/scheme' import { sanitizeIpfsUrl } from '@/utils/ipfs' +import type { BaseNFTMeta } from '@/components/base/types' import { processSingleMetadata } from '@/utils/cachingStrategy' import { getMimeType } from '@/utils/gallery/media' import unionBy from 'lodash/unionBy' @@ -22,7 +23,7 @@ export type ItemResources = { } export type NFTWithMetadata = NFT & - NFTMetadata & { meta: NFTMetadata } & ItemResources + NFTMetadata & { meta: BaseNFTMeta } & ItemResources function getGeneralMetadata(nft: NFTWithMetadata) { return { @@ -30,7 +31,7 @@ function getGeneralMetadata(nft: NFTWithMetadata) { name: nft.name || nft.meta.name || nft.id, description: nft.description || nft.meta.description || '', image: sanitizeIpfsUrl(nft.meta.image), - animation_url: sanitizeIpfsUrl(nft.meta.animation_url || ''), + animationUrl: sanitizeIpfsUrl(nft.meta.animation_url || ''), type: nft.meta.type || '', } } @@ -53,7 +54,7 @@ async function getProcessMetadata(nft: NFTWithMetadata) { nft.metadata )) as NFTWithMetadata const image = sanitizeIpfsUrl(metadata.image || metadata.mediaUri || '') - const animation_url = sanitizeIpfsUrl(metadata.animation_url || '') + const animationUrl = sanitizeIpfsUrl(metadata.animation_url || '') const getAttributes = () => { const hasMetadataAttributes = metadata.attributes && metadata.attributes.length > 0 @@ -73,7 +74,7 @@ async function getProcessMetadata(nft: NFTWithMetadata) { name: nft.name || metadata.name || nft.id, description: nft.description || metadata.description || '', image, - animation_url, + animationUrl, type: metadata.type || '', attributes: getAttributes(), } diff --git a/composables/useToken.ts b/composables/useToken.ts index 11ddb07958..1cdb554a8f 100644 --- a/composables/useToken.ts +++ b/composables/useToken.ts @@ -1,74 +1,40 @@ import { useFiatStore } from '@/stores/fiat' -import { type ChainProperties, type Prefix } from '@kodadot1/static' -import { chainPropListOf } from '@/utils/config/chain.config' -import { groupByNestedProperty } from '@/utils/objects' -import { availablePrefixes } from '@/utils/chain' +import { type Prefix } from '@kodadot1/static' +import { useIdentityStore } from '@/stores/identity' +import { defultTokenChain } from '@/utils/config/chain.config' export interface TokenDetails { symbol: string value: number | string | null icon: string - chains: Prefix[] + defaultChain: Prefix } +const getAssetToken = (asset) => asset?.token || 'KSM' +const getUniqueArrayItems = (items: string[]) => [...new Set(items)] + export default function useToken() { const { getCurrentTokenValue } = useFiatStore() const { getTokenIconBySymbol } = useIcon() + const { multiBalanceAssets } = useIdentityStore() - const availableChains = availablePrefixes().map( - (item) => item.value as Prefix - ) - - const availableTokens = ['BSX', 'DOT', 'KSM'] - - const chainsProperties = computed(() => { - return availableChains.reduce( - (reducer, chain: Prefix) => ({ - ...reducer, - [chain]: chainPropListOf(chain), - }), - {} - ) as { [k in Prefix]: ChainProperties }[] - }) - - const groupedTokensByChains = computed(() => - groupByNestedProperty(chainsProperties.value, 'tokenSymbol') + const availableAssets = computed(() => multiBalanceAssets) + const availableTokensAcrossAllChains = computed(() => + getUniqueArrayItems(Object.values(availableAssets.value).map(getAssetToken)) ) - const getTokenChain = (token: string): Prefix[] => - groupedTokensByChains.value[token] || [] - const tokens = computed(() => { - const filteredTokens = Object.keys(groupedTokensByChains.value).filter( - (token) => availableTokens.includes(token) - ) - - return filteredTokens.map((tokenSymbol) => { + return availableTokensAcrossAllChains.value.map((tokenSymbol) => { return { symbol: tokenSymbol as string, value: getCurrentTokenValue(tokenSymbol), icon: getTokenIconBySymbol(tokenSymbol), - chains: getTokenChain(tokenSymbol), + defaultChain: defultTokenChain[tokenSymbol], } }) }) - const getPrefixByToken = (token: string): Prefix | null => { - switch (token.toLowerCase()) { - case 'bsx': - return 'bsx' - case 'dot': - return 'dot' - case 'ksm': - return 'ksm' - default: - return null - } - } - return { tokens, - availableTokens, - getPrefixByToken, } } diff --git a/composables/useTransactionStatus.ts b/composables/useTransactionStatus.ts index d11216c946..4235583685 100644 --- a/composables/useTransactionStatus.ts +++ b/composables/useTransactionStatus.ts @@ -1,39 +1,55 @@ import { ExtrinsicStatus } from '@polkadot/types/interfaces' +export enum TransactionStatus { + Broadcast = 'loader.broadcast', + Casting = 'loader.casting', + Sign = 'loader.sign', + Block = 'loader.block', + Finalized = 'loader.finalized', + Unknown = '', + IPFS = 'loader.ipfs', +} + function useTransactionStatus() { - const status = ref('') + const status = ref(TransactionStatus.Unknown) const isLoading = ref(false) const resolveStatus = ( extrinsicStatus: ExtrinsicStatus, omitFinalized?: boolean ): void => { + if (extrinsicStatus.isBroadcast) { + status.value = TransactionStatus.Broadcast + return + } if (extrinsicStatus.isReady) { - status.value = 'loader.casting' + status.value = TransactionStatus.Casting return } if (extrinsicStatus.isInBlock) { - status.value = 'loader.block' + status.value = TransactionStatus.Block return } if (extrinsicStatus.isFinalized) { - status.value = omitFinalized ? '' : 'loader.finalized' + status.value = omitFinalized + ? TransactionStatus.Unknown + : TransactionStatus.Finalized return } - status.value = '' + status.value = TransactionStatus.Unknown } const initTransactionLoader = (): void => { isLoading.value = true - status.value = 'loader.sign' + status.value = TransactionStatus.Unknown } const stopLoader = (): void => { isLoading.value = false - status.value = '' + status.value = TransactionStatus.Unknown } return { status, diff --git a/content/blog/first-time.md b/content/blog/first-time.md index e555b293a8..59c1682421 100644 --- a/content/blog/first-time.md +++ b/content/blog/first-time.md @@ -69,6 +69,38 @@ docker-compose up Voila! KodaDot will be available at [localhost:9090](http://localhost:9090). KodaDot supports Hot Module Replacement on docker; any changes made will take effect immediately. +## Submitting a pull request + +To enhance your workflow with our repository, we suggest making use of the [GitHub CLI](https://cli.github.com/) to initiate a pull request. This is particularly beneficial since we offer a selection of predefined templates for your convenience. + +To get started ensure that you are authenticated on the GitHub CLI and the remote repository has been set. You can find a quickstart guide [here](https://docs.github.com/en/github-cli/github-cli/quickstart). + +1. To submit a new pull request use the command in your terminal: + +```bash +gh pr create +``` + +2. You'll be prompted to select a branch. You can select your working branch or skip the step if you have already pushed your branch. + +3. Next you'll be prompted for a title and to choose a template. Please use one of the predefined templates that is relevant to your pull request. You will also be given an option to edit it as required. + +```bash +Creating pull request for username:example-branch into main in kodadot/nft-gallery + +? Title example-branch-title +? Choose a template [Use arrows to move, type to filter] +> PULL_REQUEST_TEMPLATE.md + QUICK_AND_DESIGN_PULL_REQUEST_TEMPLATE.md + QUICK_AND_QA_PULL_REQUEST_TEMPLATE.md + QUICK_PULL_REQUEST_TEMPLATE.md + Open a blank pull request +``` + +4. Once you have edited the template, submit your pull request. 🚀 + +Alternatively, if you cannot use GitHub CLI, you can find the templates [here](https://github.com/kodadot/nft-gallery/tree/main/.github/PULL_REQUEST_TEMPLATE) and create a pull request on github. + ## Dev hacks (FAQ) 🦇 **0. How can I resolve conflict on pnpm-lock.yaml?** @@ -188,7 +220,6 @@ Current Indexers we have/use: - SubSquid - Basilisk: [snek](https://github.com/kodadot/snek) - Kusama: [rubick](https://github.com/kodadot/rubick) - - Basilisk: [snek](https://github.com/kodadot/snek) - AssetHub: [stick](https://github.com/kodadot/stick) - MoonRiver: [click](https://github.com/kodadot/click) diff --git a/libs/ui/src/components/NeoModal/NeoModal.vue b/libs/ui/src/components/NeoModal/NeoModal.vue index c64677d5c8..9ecbd0d4a4 100644 --- a/libs/ui/src/components/NeoModal/NeoModal.vue +++ b/libs/ui/src/components/NeoModal/NeoModal.vue @@ -7,6 +7,7 @@ :can-cancel="canCancel" :full-screen="fullScreen" :content-class="[...contentClassName, noShadow ? 'no-shadow' : '']" + :mobile-breakpoint="mobileBreakpoint" :root-class="rootClass" :style="{ '--max-height': maxHeight, @@ -29,6 +30,7 @@ const props = withDefaults( rootClass?: string noShadow?: boolean maxHeight?: string | number + mobileBreakpoint?: string }>(), { destroyOnHide: true, @@ -38,6 +40,7 @@ const props = withDefaults( rootClass: '', noShadow: false, maxHeight: '80vh', + mobileBreakpoint: '768px', } ) diff --git a/libs/ui/src/components/NeoSteps/NeoSteps.scss b/libs/ui/src/components/NeoSteps/NeoSteps.scss index a1355e7915..01c384e585 100644 --- a/libs/ui/src/components/NeoSteps/NeoSteps.scss +++ b/libs/ui/src/components/NeoSteps/NeoSteps.scss @@ -5,14 +5,14 @@ @import '@oruga-ui/oruga/src/scss/utilities/helpers.scss'; $steps-details-background-color: hsla(0, 0%, 0%, 0); -$steps-details-padding: .5em !default; -$steps-font-size: .85rem; -$steps-active-color: #6188E7; -$steps-previous-color: #6188E7; +$steps-details-padding: 0.5em !default; +$steps-font-size: var(--step-size, 0.85rem); +$steps-active-color: #6188e7; +$steps-previous-color: #6188e7; $steps-divider-height: 2px; $steps-marker-background: #fff; -$steps-marker-border: .2em solid $grey-light !default; +$steps-marker-border: 0.2em solid $grey-light !default; $steps-marker-color: $primary-invert !default; $steps-marker-font-weight: 700 !default; @@ -24,3 +24,81 @@ $steps-link-color: hsl(0, 0%, 29%) !default; $steps-content-padding: 1rem !default; @import '@oruga-ui/oruga/src/scss/components/steps'; + +.o-steps { + // Base styles - Themed + &__marker { + @include ktheme() { + background: theme('background-color'); + } + } + + .o-icon { + @include ktheme() { + color: theme('background-color'); + } + } + + &__divider { + @include ktheme() { + background: linear-gradient( + to left, + theme('k-shade') 50%, + $steps-active-color 50% + ); + background-size: 200% 100%; + background-position: right bottom; + } + } + + // oruga mixes the step size and the titlle font size together ($steps-font-size) + // we need to override the title font size to make it bigger (12px / 0.75rem + &__title { + font-size: 0.75rem !important; + + @include ktheme() { + color: theme('k-grey'); + } + } + + // Navigation Items + &__nav-item { + // force active background color on last nav-item when active + &--last { + &.o-steps__nav-item-active { + .o-steps__marker { + background-color: $steps-active-color; + } + } + } + + // Current nav-item styles + &-active { + .o-steps__title { + color: $steps-previous-color; + } + // move divider to right side (color it active) + // this is required becuase of the theme override above of the divider background + .o-steps__divider { + background-position: left bottom; + } + } + + // Previous nav-item styles + &-previous { + .o-steps__marker { + border-color: $steps-previous-color; + background-color: $steps-previous-color; + } + .o-steps__title { + color: $steps-previous-color; + } + + // move divider to right side (color it active) + // this is required becuase of the theme override above of the divider background + .o-steps__divider { + background-position: left bottom; + } + } + } +} diff --git a/libs/ui/src/components/NeoSteps/NeoSteps.vue b/libs/ui/src/components/NeoSteps/NeoSteps.vue index 23f58d28a8..c55d232ae1 100644 --- a/libs/ui/src/components/NeoSteps/NeoSteps.vue +++ b/libs/ui/src/components/NeoSteps/NeoSteps.vue @@ -1,9 +1,43 @@ -