-
-
Notifications
You must be signed in to change notification settings - Fork 364
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5345 from daiagi/collection-activity-tab
✨ Collection Activity Tab
- Loading branch information
Showing
49 changed files
with
2,243 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<template> | ||
<div class="is-flex"> | ||
<SidebarFilter /> | ||
<div ref="wrapper" class="w-full mt-5"> | ||
<div v-if="tablet"> | ||
<div class="columns"> | ||
<div class="column is-two-thirds"> | ||
<ActivityChart :events="events" /> | ||
</div> | ||
<div class="column"> | ||
<OwnerInsights :owners="owners" :flippers="flippers" /> | ||
</div> | ||
</div> | ||
<BreadcrumbsFilter /> | ||
</div> | ||
<div v-else> | ||
<div class="is-flex is-flex-direction-column gap"> | ||
<OwnerInsights :owners="owners" :flippers="flippers" /> | ||
<div class="max-width"> | ||
<ActivityChart :events="events" /> | ||
</div> | ||
</div> | ||
</div> | ||
<hr class="mb-40" :class="{ 'my-40': !isBreadCrumbsShowing }" /> | ||
<Events :events="sortedEventsWithOffersDesc" /> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import ActivityChart from './ActivityChart.vue' | ||
import OwnerInsights from './OwnerInsights.vue' | ||
import Events from './events/Events.vue' | ||
import BreadcrumbsFilter from '@/components/shared/BreadcrumbsFilter.vue' | ||
import { Interaction } from '@kodadot1/minimark' | ||
import { useResizeObserver } from '@vueuse/core' | ||
import SidebarFilter from '@/components/shared/filters/SidebarFilter.vue' | ||
import { isAnyActivityFilterActive } from './utils' | ||
import { mintInteraction } from '@/composables/collectionActivity/helpers' | ||
import { useCollectionActivity } from '@/composables/collectionActivity/useCollectionActivity' | ||
const mobileBreakpoint = 800 | ||
const route = useRoute() | ||
const tablet = ref(true) | ||
const wrapper = ref<HTMLDivElement | null>(null) | ||
const isBreadCrumbsShowing = computed( | ||
() => isAnyActivityFilterActive() && tablet.value | ||
) | ||
const collectionId = computed(() => route.params.id) | ||
const { events, flippers, owners, offers } = useCollectionActivity({ | ||
collectionId: collectionId.value, | ||
}) | ||
const InteractionIncluded = [ | ||
Interaction.BUY, | ||
Interaction.LIST, | ||
mintInteraction(), | ||
Interaction.SEND, | ||
] | ||
const filteredEvents = computed(() => | ||
events.value.filter((event) => | ||
InteractionIncluded.includes(event.interaction as Interaction) | ||
) | ||
) | ||
const withOffers = computed(() => [...filteredEvents.value, ...offers.value]) | ||
// newest events first (bigger timestamp first) | ||
const sortedEventsWithOffersDesc = computed(() => | ||
withOffers.value.sort((a, b) => b.timestamp - a.timestamp) | ||
) | ||
useResizeObserver(wrapper, (entry) => { | ||
if (entry[0].contentRect.width >= mobileBreakpoint) { | ||
tablet.value = true | ||
} else { | ||
tablet.value = false | ||
} | ||
}) | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
.gap { | ||
gap: 2.5rem; | ||
} | ||
.my-40 { | ||
margin: 2.5rem 0; | ||
} | ||
.mb-40 { | ||
margin-bottom: 2.5rem; | ||
} | ||
.is-flex-basis-two-thirds { | ||
flex-basis: 66.6%; | ||
} | ||
.is-flex-basis-auto { | ||
flex-basis: auto; | ||
} | ||
//hack to make the chart responsive | ||
.max-width { | ||
max-width: calc(100% - 1px); | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<template> | ||
<PriceChart | ||
v-if="events.length > 0" | ||
:price-chart-data="chartData" | ||
chart-height="350px" /> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { ActivityInteraction } from '@/components/rmrk/service/scheme' | ||
import { Interaction } from '@kodadot1/minimark' | ||
import PriceChart from '@/components/chart/PriceChart.vue' | ||
import { bin, displayValue, sortAsc, toDataPoint } from './utils' | ||
const props = withDefaults( | ||
defineProps<{ | ||
events: ActivityInteraction[] | ||
}>(), | ||
{ | ||
events: () => [], | ||
} | ||
) | ||
const buyEvents = computed(() => | ||
sortAsc( | ||
props.events | ||
.filter((e) => e.interaction === Interaction.BUY) | ||
.map(toDataPoint) | ||
) | ||
) | ||
const listEvents = computed(() => | ||
sortAsc( | ||
props.events | ||
.filter((e) => e.interaction === Interaction.LIST) | ||
.map(toDataPoint) | ||
) | ||
) | ||
const chartData = computed(() => { | ||
const buyBins = bin(buyEvents.value, { days: 1 }) | ||
const listBins = bin(listEvents.value, { days: 1 }) | ||
const binnedBuyEvents = buyBins.map(({ timestamp, value }) => [ | ||
new Date(timestamp), | ||
displayValue(value), | ||
]) as [Date, number][] | ||
const binnedListEvents = listBins.map(({ timestamp, value }) => [ | ||
new Date(timestamp), | ||
displayValue(value), | ||
]) as [Date, number][] | ||
return [binnedBuyEvents, binnedListEvents] | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<template> | ||
<div class="fixed-height border"> | ||
<div class="py-4 px-5 is-flex border-bottom" aria-label="controls"> | ||
<div | ||
class="mr-4 is-clickable" | ||
:class="{ 'has-text-weight-bold': activeTab === Tabs.holders }" | ||
@click="activeTab = Tabs.holders"> | ||
{{ $t('holders') }} | ||
</div> | ||
<div | ||
class="is-clickable" | ||
:class="{ 'has-text-weight-bold': activeTab === Tabs.flippers }" | ||
@click="activeTab = Tabs.flippers"> | ||
{{ $t('flippers') }} | ||
</div> | ||
</div> | ||
<div class="py-4 limit-height is-scrollable"> | ||
<HoldersTab v-if="activeTab === Tabs.holders" :owners="owners" /> | ||
<FlippersTab v-if="activeTab === Tabs.flippers" :flippers="flippers" /> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import HoldersTab from './ownerInsightsTabs/HolderTab.vue' | ||
import FlippersTab from './ownerInsightsTabs/FlipperTab.vue' | ||
import { Flippers, Owners } from '@/composables/collectionActivity/types' | ||
enum Tabs { | ||
holders, | ||
flippers, | ||
} | ||
defineProps<{ | ||
owners?: Owners | ||
flippers?: Flippers | ||
}>() | ||
const activeTab = ref(Tabs.holders) | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
.fixed-height { | ||
height: 350px; | ||
} | ||
.limit-height { | ||
max-height: 290px; | ||
} | ||
.is-scrollable { | ||
overflow-y: auto; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
<div> | ||
<EventRowDesktop v-if="variant === 'Desktop'" :event="event" /> | ||
<EventRowTablet v-else :event="event" /> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { | ||
InteractionWithNFT, | ||
Offer, | ||
} from '@/composables/collectionActivity/types' | ||
import EventRowDesktop from './eventRow/EventRowDesktop.vue' | ||
import EventRowTablet from './eventRow/EventRowTablet.vue' | ||
defineProps<{ | ||
event: InteractionWithNFT | Offer | ||
variant: 'Touch' | 'Desktop' | ||
}>() | ||
</script> |
Oops, something went wrong.