diff --git a/app.vue b/app.vue index 695dc095..70e5174e 100644 --- a/app.vue +++ b/app.vue @@ -1,7 +1,4 @@ diff --git a/components/shared/ChartOnEntityPage.vue b/components/shared/ChartOnEntityPage.vue index 255e59f8..1d397891 100644 --- a/components/shared/ChartOnEntityPage.vue +++ b/components/shared/ChartOnEntityPage.vue @@ -221,9 +221,9 @@ onBeforeUnmount(() => {
diff --git a/components/ui/Input.vue b/components/ui/Input.vue index 14bf09fb..9042a57f 100644 --- a/components/ui/Input.vue +++ b/components/ui/Input.vue @@ -163,6 +163,10 @@ const handlePaste = (e) => { height: 28px; } +.base.mini { + height: 26px; +} + .base:hover { box-shadow: inset 0 0 0 1px var(--op-10); } diff --git a/components/ui/Modal.vue b/components/ui/Modal.vue index 91a0c0b2..d97a06b7 100644 --- a/components/ui/Modal.vue +++ b/components/ui/Modal.vue @@ -124,6 +124,16 @@ const handleClose = (e) => { const onKeydown = (e) => { if (e.code === "Escape") handleClose() } + +const route = useRoute() +watch( + () => route.fullPath, + () => { + if (props.show) { + handleClose() + } + } +) @@ -137,14 +184,24 @@ watch( } .header { - height: 40px; + height: 46px; border-radius: 8px 8px 4px 4px; background: var(--card-background); - padding: 0 12px; + padding: 0 16px; } +.footer { + height: 46px; + + border-radius: 4px 4px 8px 8px; + background: var(--card-background); + + padding: 0 16px; +} + + @media (max-width: 500px) { .wrapper { padding: 32px 12px; diff --git a/pages/ibc/chain/[id].vue b/pages/ibc/chain/[id].vue index 132a685f..555c8208 100644 --- a/pages/ibc/chain/[id].vue +++ b/pages/ibc/chain/[id].vue @@ -9,13 +9,12 @@ import { IbcChainName } from "@/services/constants/ibc" import { fetchIbcChainsStats } from "@/services/api/stats" const route = useRoute() -const router = useRouter() const { data } = await useAsyncData(`ibc-chains`, () => fetchIbcChainsStats({ limit: 100 })) const chain = ref(data.value.find((c) => c.chain === route.params.id)) if (!chain.value) { - router.push("/") + throw createError({ statusCode: 404, statusMessage: `IBC chain ${route.params.id} not found` }) } useHead({ diff --git a/pages/ibc/chains.vue b/pages/ibc/chains.vue index 1e3781fa..d3c8c00f 100644 --- a/pages/ibc/chains.vue +++ b/pages/ibc/chains.vue @@ -5,6 +5,12 @@ import { fetchIbcChainsStats } from "@/services/api/stats" /** Components */ import ChainsTable from "@/components/modules/ibc/ChainsTable.vue" +/** UI */ +import Button from "@/components/ui/Button.vue" + +/** Utils */ +import { comma } from "@/services/utils" + useHead({ title: `Celestia IBC Chains - Celenium`, link: [ @@ -54,12 +60,17 @@ useHead({ }) const allChains = ref([]) -const showedChains = computed(() => allChains.value.slice((page.value - 1) * 10, page.value * 10)) +const showedChains = computed(() => allChains.value.slice((page.value - 1) * itemsPerPage, page.value * itemsPerPage)) const isLoading = ref(true) /** Pagination */ const page = ref(1) +const itemsPerPage = 20 +const isNextPageDisabled = computed(() => { + return !showedChains.value.length || showedChains.value.length !== itemsPerPage +}) const handleNext = () => { + if (isNextPageDisabled.value) return page.value += 1 } const handlePrev = () => { @@ -90,23 +101,55 @@ await getChains() :class="$style.breadcrumbs" /> - + IBC Chains + + + + + + + + + + + + + + + + + + + + @@ -121,12 +164,21 @@ await getChains() } .header { - height: 40px; + height: 46px; border-radius: 8px 8px 4px 4px; background: var(--card-background); - padding: 0 12px; + padding: 0 16px; +} + +.footer { + height: 46px; + + border-radius: 4px 4px 8px 8px; + background: var(--card-background); + + padding: 0 16px; } @media (max-width: 500px) { diff --git a/pages/ibc/transfers.vue b/pages/ibc/transfers.vue index c7fd119d..5ecd0e79 100644 --- a/pages/ibc/transfers.vue +++ b/pages/ibc/transfers.vue @@ -5,6 +5,12 @@ import TransfersTable from "@/components/modules/ibc/TransfersTable.vue" /** API */ import { fetchIbcTransfers } from "@/services/api/ibc" +/** UI */ +import Button from "@/components/ui/Button.vue" + +/** Utils */ +import { comma } from "@/services/utils" + useHead({ title: `Celestia IBC Transfers - Celenium`, link: [ @@ -58,7 +64,12 @@ const isLoading = ref(true) /** Pagination */ const page = ref(1) +const limit = 20 +const isNextPageDisabled = computed(() => { + return !transfers.value.length || transfers.value.length !== limit +}) const handleNext = () => { + if (isNextPageDisabled.value) return page.value += 1 } const handlePrev = () => { @@ -71,8 +82,8 @@ const getTransfers = async () => { const { data } = await useAsyncData(`ibc-transfers-${page.value}`, () => fetchIbcTransfers({ - offset: (page.value - 1) * 10, - limit: 10, + offset: (page.value - 1) * limit, + limit: limit, }), ) transfers.value = data.value @@ -87,12 +98,16 @@ await getTransfers() watch( () => page.value, async () => { + isLoading.value = true + const data = await fetchIbcTransfers({ - offset: (page.value - 1) * 10, - limit: 10, + offset: (page.value - 1) * limit, + limit: limit, }) transfers.value = data transfers.value.sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()) + + isLoading.value = false }, ) @@ -114,17 +129,49 @@ watch( IBC Transfers + + + + + + + + + + + + + + + + + + + + @@ -139,12 +186,21 @@ watch( } .header { - height: 40px; + height: 46px; border-radius: 8px 8px 4px 4px; background: var(--card-background); - padding: 0 12px; + padding: 0 16px; +} + +.footer { + height: 46px; + + border-radius: 4px 4px 8px 8px; + background: var(--card-background); + + padding: 0 16px; } @media (max-width: 500px) { diff --git a/pages/namespace/[id].vue b/pages/namespace/[id].vue index 2e9aac86..e5bae6cf 100644 --- a/pages/namespace/[id].vue +++ b/pages/namespace/[id].vue @@ -4,7 +4,7 @@ import NamespaceOverview from "@/components/modules/namespace/NamespaceOverview. import NamespaceCharts from "@/components/modules/namespace/NamespaceCharts.vue" /** Services */ -import { getNamespaceID } from "@/services/utils" +import { getNamespaceID, isValidId } from "@/services/utils" /** API */ import { fetchNamespaceByID } from "@/services/api/namespace" @@ -14,16 +14,20 @@ import { useCacheStore } from "@/store/cache.store" const cacheStore = useCacheStore() const route = useRoute() -const router = useRouter() const namespace = ref() -const { data: rawNamespace } = await fetchNamespaceByID(route.params.id) -if (!rawNamespace.value) { - router.push("/") +if (isValidId(route.params.id, "namespace")) { + const { data: rawNamespace } = await fetchNamespaceByID(route.params.id) + + if (!rawNamespace.value) { + throw createError({ statusCode: 404, statusMessage: `Namespace ${route.params.id} not found` }) + } else { + namespace.value = rawNamespace.value[0] + cacheStore.current.namespace = namespace.value + } } else { - namespace.value = rawNamespace.value[0] - cacheStore.current.namespace = namespace.value + throw createError({ statusCode: 404, statusMessage: `Namespace ${route.params.id} not found` }) } defineOgImageComponent("NamespaceImage", { diff --git a/pages/rollup/[slug].vue b/pages/network/[slug].vue similarity index 78% rename from pages/rollup/[slug].vue rename to pages/network/[slug].vue index 61257485..48e71a94 100644 --- a/pages/rollup/[slug].vue +++ b/pages/network/[slug].vue @@ -16,13 +16,12 @@ const appStore = useAppStore() const cacheStore = useCacheStore() const route = useRoute() -const router = useRouter() const rollup = ref() const { data: rawRollup } = await fetchRollupBySlug(route.params.slug) if (!rawRollup.value) { - router.push("/rollups") + throw createError({ statusCode: 404, statusMessage: `Network ${route.params.slug} not found` }) } else { rollup.value = rawRollup.value patchRollupColor() @@ -41,13 +40,13 @@ function patchRollupColor() { } defineOgImageComponent("RollupImage", { - title: "Rollup", + title: "Network", rollup: rollup.value, cacheKey: `${rollup.value?.name}`, }) useHead({ - title: `Rollup ${rollup.value?.name} - Celenium`, + title: `Network ${rollup.value?.name} - Celenium`, link: [ { rel: "canonical", @@ -57,15 +56,15 @@ useHead({ meta: [ { name: "description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { property: "og:title", - content: `Rollup ${rollup.value?.name} - Celenium`, + content: `Network ${rollup.value?.name} - Celenium`, }, { property: "og:description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { property: "og:url", @@ -73,11 +72,11 @@ useHead({ }, { name: "twitter:title", - content: `Rollup ${rollup.value?.name} - Celenium`, + content: `Network ${rollup.value?.name} - Celenium`, }, { name: "twitter:description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { name: "twitter:card", @@ -100,13 +99,13 @@ watch( v-if="rollup" :items="[ { link: '/', name: 'Explore' }, - { link: '/rollups', name: 'Rollups Leaderboard' }, + { link: '/networks', name: 'Networks Leaderboard' }, { link: route.fullPath, name: rollup.name }, ]" /> diff --git a/pages/rollup/rank/[slug].vue b/pages/network/rank/[slug].vue similarity index 95% rename from pages/rollup/rank/[slug].vue rename to pages/network/rank/[slug].vue index 8f3133f7..34097c48 100644 --- a/pages/rollup/rank/[slug].vue +++ b/pages/network/rank/[slug].vue @@ -92,6 +92,10 @@ async function fetchData() { fetchRollupRankingBySlug(slug), ]) + if (!rollupData?.data?.value) { + throw createError({ statusCode: 404, statusMessage: `Network ${slug} not found` }) + } + let description = [] for (const [key, value] of Object.entries(rankData?.scores)) { const category = getMetricCategory(key, value) @@ -137,13 +141,13 @@ async function fetchData() { } defineOgImageComponent("RollupImage", { - title: "Rollup", + title: "Network", rollup: rollup.value, cacheKey: `${rollup.value?.name}`, }) useHead({ - title: `Rollup ${rollup.value?.name} - Celenium`, + title: `Network ${rollup.value?.name} - Celenium`, link: [ { rel: "canonical", @@ -153,15 +157,15 @@ useHead({ meta: [ { name: "description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { property: "og:title", - content: `Rollup ${rollup.value?.name} - Celenium`, + content: `Network ${rollup.value?.name} - Celenium`, }, { property: "og:description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { property: "og:url", @@ -169,11 +173,11 @@ useHead({ }, { name: "twitter:title", - content: `Rollup ${rollup.value?.name} - Celenium`, + content: `Network ${rollup.value?.name} - Celenium`, }, { name: "twitter:description", - content: `Rollup ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, + content: `Network ${rollup.value?.name} blobs, namespaces, metadata, social links, contacts and other data.`, }, { name: "twitter:card", @@ -244,7 +248,7 @@ const handleHowItWorksClick = () => { watch( () => page.value, async () => { - repos.value = await getRollupRepos(rollup.value.slug) + repos.value = await getRollupRepos(rollup.value?.slug) }, ) @@ -262,13 +266,13 @@ onMounted(() => { @@ -288,7 +292,7 @@ onMounted(() => { - @@ -424,7 +428,7 @@ onMounted(() => { No repositories found - This rollup probably doesn't have an associated github account + This network probably doesn't have an associated github account diff --git a/pages/rollups/index.vue b/pages/networks/index.vue similarity index 87% rename from pages/rollups/index.vue rename to pages/networks/index.vue index 66f6237b..bb2828e9 100644 --- a/pages/rollups/index.vue +++ b/pages/networks/index.vue @@ -31,37 +31,35 @@ import { rollupRankingServiceURL } from "@/services/config" import { fetchRollups, fetchRollupsRanking } from "@/services/api/rollup" /** Stores */ -import { useAppStore } from "@/store/app.store" import { useEnumStore } from "@/store/enums.store" -const appStore = useAppStore() const enumStore = useEnumStore() useHead({ - title: "Rollups - Celestia Explorer", + title: "Networks - Celestia Explorer", link: [ { rel: "canonical", - href: "https://celenium.io/rollups", + href: "https://celenium.io/networks", }, ], meta: [ { name: "description", content: - "View all rollups in the Celestia Blockchain. Rollup name, description, size, blobs, social links, contacts are shown.", + "View all networks in the Celestia Blockchain. Network name, description, size, blobs, social links, contacts are shown.", }, { property: "og:title", - content: "Rollups Leaderboard - Celestia Explorer", + content: "Networks Leaderboard - Celestia Explorer", }, { property: "og:description", content: - "View all rollups in the Celestia Blockchain. Rollup name, description, size, blobs, social links, contacts are shown.", + "View all networks in the Celestia Blockchain. Network name, description, size, blobs, social links, contacts are shown.", }, { property: "og:url", - content: `https://celenium.io/rollups`, + content: `https://celenium.io/networks`, }, { property: "og:image", @@ -69,12 +67,12 @@ useHead({ }, { name: "twitter:title", - content: "Rollups Leaderboard - Celestia Explorer", + content: "Networks Leaderboard - Celestia Explorer", }, { name: "twitter:description", content: - "View all rollups in the Celestia Blockchain. Rollup name, description, size, blobs, social links, contacts are shown.", + "View all networks in the Celestia Blockchain. Network name, description, size, blobs, social links, contacts are shown.", }, { name: "twitter:card", @@ -132,13 +130,13 @@ const config = reactive({ paid_per_mb: { show: true, }, - today_blobs: { - show: true, - sortPath: "stats.day_blobs_count", + type: { + show: false, + sortPath: "type", }, - avg_pfb_size: { + provider: { show: false, - sortPath: "stats.avg_pfb_size", + sortPath: "provider", }, latest_activity: { show: true, @@ -191,10 +189,14 @@ const tags = computed(() => { return res }) +const providers = computed(() => [...new Set(rollups.value + ?.map(r => r.provider))] + .filter(Boolean) + .sort((a, b) => a.localeCompare(b)) +) +const ranks = ["Legendary", "Epic", "Good", "Normal", "Offline"] const showInactive = ref(false) -const providers = ref([]) -const stacks = ref([]) const getDisplayName = (name) => { switch (name) { case "nft": @@ -208,20 +210,26 @@ const getDisplayName = (name) => { } } const popovers = reactive({ + activity_rank: false, categories: false, - types: false, + providers: false, tags: false, + types: false, }) const keyMap = { + activity_rank: "activity_rank", categories: "category", - types: "type", - tags: "tag", + providers: "provider", showInactive: "is_active", + tags: "tag", + types: "type", } const filters = reactive({ + activity_rank: ranks.reduce((a, b) => ({ ...a, [b]: false }), {}), categories: categories.value?.reduce((a, b) => ({ ...a, [b]: false }), {}), - types: types.value?.reduce((a, b) => ({ ...a, [b]: false }), {}), + providers: providers.value?.reduce((a, b) => ({ ...a, [b]: false }), {}), tags: tags.value?.reduce((a, b) => ({ ...a, [b]: false }), {}), + types: types.value?.reduce((a, b) => ({ ...a, [b]: false }), {}), showInactive: showInactive.value, }) const savedFiltersBeforeChanges = ref(null) @@ -305,13 +313,15 @@ const getRollups = async () => { if (data?.length) { rollups.value = data.map((r) => { const rank = ranking[r.slug] - if (!rank) return r + const rounded_rank = roundTo(rank?.rank / 10, 0) + const rank_category = getRankCategory(rounded_rank) return { ...r, - rank: +rank.rank, - rounded_rank: roundTo(rank.rank / 10, 0), - rank_category: getRankCategory(roundTo(rank.rank / 10, 0)), + rank: rank?.rank ? +rank.rank : 0, + rounded_rank, + rank_category, + activity_rank: rank_category.name, } }) } @@ -381,7 +391,6 @@ const handleSort = (by) => { case "asc": sort.dir = "desc" - break } @@ -421,6 +430,9 @@ watch( filters.tags = tags.value?.reduce((a, b) => ({ ...a, [b]: false }), {}) }, ) +watchEffect(() => { + filters.providers = providers.value?.reduce((a, b) => ({ ...a, [b]: false }), {}) +}) watch( () => showInactive.value, () => { @@ -447,7 +459,15 @@ watch( onBeforeMount(() => { if (localStorage.getItem("page:rollups:config:columns")) { - config.columns = JSON.parse(localStorage.getItem("page:rollups:config:columns")) + const savedConfig = JSON.parse(localStorage.getItem("page:rollups:config:columns")) + Object.keys(savedConfig).forEach(k => { + if (config.columns[k]) { + config.columns[k] = { + ...config.columns[k], + show: savedConfig[k].show + } + } + }) } }) @@ -458,12 +478,12 @@ onBeforeMount(() => { @@ -471,7 +491,7 @@ onBeforeMount(() => { - Rollups Leaderboard + Networks Leaderboard @@ -495,7 +515,7 @@ onBeforeMount(() => { - +