From dea873f4a27404facf257d0ba04bf647d3f0f9f2 Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Fri, 31 Mar 2023 01:28:11 +0800 Subject: [PATCH 01/65] feat: notification box --- components/Navbar.vue | 5 + .../NotificationBox/NotificationBoxModal.vue | 291 ++++++++++++++++++ .../NotificationBox/NotificationItem.vue | 85 +++++ components/common/NotificationBox/types.ts | 26 ++ .../common/NotificationBox/useNotification.ts | 38 +++ .../NotificationBox/useNotificationBox.ts | 7 + components/navbar/NotificationBoxButton.vue | 37 +++ locales/en.json | 13 +- 8 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 components/common/NotificationBox/NotificationBoxModal.vue create mode 100644 components/common/NotificationBox/NotificationItem.vue create mode 100644 components/common/NotificationBox/types.ts create mode 100644 components/common/NotificationBox/useNotification.ts create mode 100644 components/common/NotificationBox/useNotificationBox.ts create mode 100644 components/navbar/NotificationBoxButton.vue diff --git a/components/Navbar.vue b/components/Navbar.vue index cb061d113f..2703adee5a 100644 --- a/components/Navbar.vue +++ b/components/Navbar.vue @@ -77,6 +77,10 @@ id="NavChainSelect" class="navbar-chain custom-navbar-item" data-cy="chain-select" /> + + + + + + diff --git a/components/common/NotificationBox/NotificationItem.vue b/components/common/NotificationBox/NotificationItem.vue new file mode 100644 index 0000000000..9aabe1e61d --- /dev/null +++ b/components/common/NotificationBox/NotificationItem.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/components/common/NotificationBox/types.ts b/components/common/NotificationBox/types.ts new file mode 100644 index 0000000000..1959963295 --- /dev/null +++ b/components/common/NotificationBox/types.ts @@ -0,0 +1,26 @@ +export type FilterOption = { + id: string + name: string +} + +interface NFT { + name: string + id: string + price: string + metadata: string + meta: { + id: string + image: string + } + collection: { + id: string + } +} + +export type Event = { + id: string + interaction: string + timestamp: string + caller: string + nft: NFT +} diff --git a/components/common/NotificationBox/useNotification.ts b/components/common/NotificationBox/useNotification.ts new file mode 100644 index 0000000000..fc3897b2cf --- /dev/null +++ b/components/common/NotificationBox/useNotification.ts @@ -0,0 +1,38 @@ +import { Event, FilterOption } from './types' + +export const useNotification = (account: string) => { + const collections = ref([]) + const events = ref([]) + const nfts = ref([]) + + const { data: collectionData } = useGraphql({ + queryName: 'collectionByAccount', + variables: { + account, + }, + }) + const { data: eventsData } = useGraphql({ + queryName: 'eventListByAccount', + variables: { + account, + }, + }) + + watch(collectionData, (result) => { + if (result.collectionEntities) { + collections.value = result.collectionEntities + } + }) + + watch(eventsData, (result) => { + if (result.events) { + events.value = result.events + } + }) + + return { + collections, + events, + nfts, + } +} diff --git a/components/common/NotificationBox/useNotificationBox.ts b/components/common/NotificationBox/useNotificationBox.ts new file mode 100644 index 0000000000..45a9a6f47f --- /dev/null +++ b/components/common/NotificationBox/useNotificationBox.ts @@ -0,0 +1,7 @@ +import NotificationBoxModal from './NotificationBoxModal.vue' + +export const NotificationBoxModalConfig = { + component: NotificationBoxModal, + canCancel: true, + customClass: 'notification-box-modal', +} diff --git a/components/navbar/NotificationBoxButton.vue b/components/navbar/NotificationBoxButton.vue new file mode 100644 index 0000000000..b76a31e287 --- /dev/null +++ b/components/navbar/NotificationBoxButton.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/locales/en.json b/locales/en.json index 342f4b518b..ecd266a184 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1288,10 +1288,21 @@ "uploadDesc": "Upload Description File (Optional)" }, "filters": { + "buy": "Buy", "sale": "Sale", "offer": "Offer", + "acceptedOffer": "Accepted offer", "listing": "Listing", "mint": "Mint", "transfer": "Transfer" + }, + "notification": { + "notifications": "Notifications", + "filters": "Filters", + "add": "Add", + "done": "Done", + "byCollection": "By collection", + "byEvent": "By event", + "emptyTip": "Don't wait for notifications,
make your own buzz with your art." } -} \ No newline at end of file +} From c8b61e611f023ff5e3d945b11a1f1e1e2b1b6fc3 Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Fri, 31 Mar 2023 17:19:46 +0800 Subject: [PATCH 02/65] feat: update graphql file --- .../general/collectionByAccount.graphql | 9 ++++++++ .../general/eventListByAccount.graphql | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 queries/subsquid/general/collectionByAccount.graphql create mode 100644 queries/subsquid/general/eventListByAccount.graphql diff --git a/queries/subsquid/general/collectionByAccount.graphql b/queries/subsquid/general/collectionByAccount.graphql new file mode 100644 index 0000000000..88a32bcadd --- /dev/null +++ b/queries/subsquid/general/collectionByAccount.graphql @@ -0,0 +1,9 @@ +query collectionByAccount($account: String!) { + collectionEntities( + where: { currentOwner_eq: $account } + orderBy: blockNumber_DESC + ) { + id + name + } +} diff --git a/queries/subsquid/general/eventListByAccount.graphql b/queries/subsquid/general/eventListByAccount.graphql new file mode 100644 index 0000000000..9ba4eefc6b --- /dev/null +++ b/queries/subsquid/general/eventListByAccount.graphql @@ -0,0 +1,21 @@ +query eventListByAccount($account: String!) { + events(where: { caller_eq: $account }, orderBy: timestamp_DESC) { + id + interaction + timestamp + caller + nft { + name + id + metadata + price + meta { + id + image + } + collection { + id + } + } + } +} From be4d3856910c531588c1f99bd5c49efd8fef73c4 Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Fri, 31 Mar 2023 17:20:53 +0800 Subject: [PATCH 03/65] feat: remove suffix --- components/common/NotificationBox/NotificationItem.vue | 2 +- utils/format/time.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/common/NotificationBox/NotificationItem.vue b/components/common/NotificationBox/NotificationItem.vue index 9aabe1e61d..08bdd7ead8 100644 --- a/components/common/NotificationBox/NotificationItem.vue +++ b/components/common/NotificationBox/NotificationItem.vue @@ -33,7 +33,7 @@
- {{ formatToNow(new Date(event.timestamp)) }} + {{ formatToNow(new Date(event.timestamp), false) }}
diff --git a/utils/format/time.ts b/utils/format/time.ts index 7ffe0b83cb..933acc6ae5 100644 --- a/utils/format/time.ts +++ b/utils/format/time.ts @@ -35,6 +35,6 @@ export const endDate = (seconds: number): string => { return parseDate(addSeconds(new Date(), seconds)) } -export const formatToNow = (date: Date): string => { - return formatDistanceToNowStrict(new Date(date), { addSuffix: true }) +export const formatToNow = (date: Date, addSuffix = true): string => { + return formatDistanceToNowStrict(new Date(date), { addSuffix: addSuffix }) } From cfadd6b77cd588d88bef1c63c9674f23905c778e Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Fri, 31 Mar 2023 17:23:42 +0800 Subject: [PATCH 04/65] feat: watch filter change --- .../common/NotificationBox/NotificationBoxModal.vue | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/common/NotificationBox/NotificationBoxModal.vue b/components/common/NotificationBox/NotificationBoxModal.vue index ebe5f9e817..8c894d8bb7 100644 --- a/components/common/NotificationBox/NotificationBoxModal.vue +++ b/components/common/NotificationBox/NotificationBoxModal.vue @@ -25,7 +25,7 @@ v-else no-shadow class="button-rounded" - @click.native="onClickDone"> + @click.native="showFilter = !showFilter"> {{ $t('notification.done') }} @@ -169,10 +169,7 @@ const doSearch = () => { ) } } -const onClickDone = () => { - doSearch() - showFilter.value = false -} + const removeFilter = (key: FilterType, target: FilterOption) => { toggleFilter(key, target) doSearch() @@ -180,6 +177,7 @@ const removeFilter = (key: FilterType, target: FilterOption) => { watch(allEvents, doSearch, { immediate: true, }) +watch(filters, doSearch, { deep: true }) const emit = defineEmits(['close']) From bfc652a1c2382c97e982b70c57c36012dc94e26d Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Mon, 3 Apr 2023 16:17:16 +0800 Subject: [PATCH 05/65] feat: add limit for notification --- queries/subsquid/general/eventListByAccount.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries/subsquid/general/eventListByAccount.graphql b/queries/subsquid/general/eventListByAccount.graphql index 9ba4eefc6b..ce54775466 100644 --- a/queries/subsquid/general/eventListByAccount.graphql +++ b/queries/subsquid/general/eventListByAccount.graphql @@ -1,5 +1,5 @@ query eventListByAccount($account: String!) { - events(where: { caller_eq: $account }, orderBy: timestamp_DESC) { + events(where: { caller_eq: $account }, orderBy: timestamp_DESC, limit: 30) { id interaction timestamp From 5156529a28826754938a109fff1d4f349bb0b2c4 Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Tue, 4 Apr 2023 00:06:16 +0800 Subject: [PATCH 06/65] refactor: only select one collection --- .../NotificationBox/NotificationBoxModal.vue | 116 +++++++++--------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/components/common/NotificationBox/NotificationBoxModal.vue b/components/common/NotificationBox/NotificationBoxModal.vue index 8c894d8bb7..fbecc3bb1a 100644 --- a/components/common/NotificationBox/NotificationBoxModal.vue +++ b/components/common/NotificationBox/NotificationBoxModal.vue @@ -37,15 +37,15 @@ {{ $t('notification.byCollection') }} -
+
+ @click="toggleCollectionFilter(item)"> {{ item.name }}
@@ -54,39 +54,34 @@ {{ $t('notification.byEvent') }} -
+
+ @click="toggleEventFilter(item)"> {{ item.name }}
-
+
- {{ item.name }} - + v-if="collectionFilter" + class="filter-item no-wrap mr-1 px-3 py-1 mb-1"> + {{ collectionFilter.name }} +
- {{ item.name }} - + v-for="event in eventFilter" + :key="event.id" + class="filter-item no-wrap px-3 py-1 mb-1"> + {{ event.name }} +
@@ -110,8 +105,9 @@ import { NeoButton, NeoIcon } from '@kodadot1/brick' import { Interaction } from '@kodadot1/minimark' import NotificationItem from './NotificationItem.vue' import { useNotification } from './useNotification' + const { $i18n, $store } = useNuxtApp() -type FilterType = 'collection' | 'event' + const eventTypes = ref([ { id: Interaction.BUY, @@ -128,56 +124,56 @@ const eventTypes = ref([ name: $i18n.t('filters.acceptedOffer'), }, ]) + const { collections, events: allEvents } = useNotification( $store.getters.getAuthAddress ) -const showFilter = ref(false) -const isFilterEmpty = computed( - () => - filters.value.collection.length === 0 && filters.value.event.length === 0 -) -const filters = ref<{ - collection: FilterOption[] - event: FilterOption[] -}>({ - collection: [], - event: [], -}) -const isInFilter = (key: FilterType, target: FilterOption) => { - return filters.value[key].some((x) => x.id === target.id) + +const collectionFilter = ref(null) +const toggleCollectionFilter = (target: FilterOption) => { + if (collectionFilter.value?.id === target.id) { + collectionFilter.value = null + } else { + collectionFilter.value = target + } } -const toggleFilter = (key: FilterType, target: FilterOption) => { - if (isInFilter(key, target)) { - const index = filters.value[key].findIndex((x) => x.id === target.id) - filters.value[key].splice(index, 1) + +const eventFilter = ref([]) +const toggleEventFilter = (target: FilterOption) => { + const index = eventFilter.value.findIndex( + (x: FilterOption) => x.id === target.id + ) + if (index === -1) { + eventFilter.value.push(target) } else { - filters.value[key].push(target) + eventFilter.value.splice(index, 1) } } + +const showFilter = ref(false) +const isFilterEmpty = computed( + () => !collectionFilter.value && eventFilter.value.length === 0 +) + const displayedEvents = ref([]) + const doSearch = () => { - const { collection, event } = filters.value - displayedEvents.value = allEvents.value - if (collection.length > 0) { - displayedEvents.value = displayedEvents.value.filter((item) => - collection.some((x) => x.id === item.nft.collection.id) - ) - } - if (event.length > 0) { - displayedEvents.value = displayedEvents.value.filter((item) => - event.some((x) => x.id === item.interaction) - ) - } + displayedEvents.value = allEvents.value.filter( + (item) => + (!collectionFilter.value || + collectionFilter.value.id === item.nft.collection.id) && + (eventFilter.value.length === 0 || + eventFilter.value.some((x) => x.id === item.interaction)) + ) } -const removeFilter = (key: FilterType, target: FilterOption) => { - toggleFilter(key, target) - doSearch() -} watch(allEvents, doSearch, { immediate: true, }) -watch(filters, doSearch, { deep: true }) + +watch(collectionFilter, doSearch, { deep: true }) +watch(eventFilter, doSearch, { deep: true }) + const emit = defineEmits(['close']) @@ -247,7 +243,6 @@ const emit = defineEmits(['close']) } } .filter-list { - overflow-x: auto; .filter-item { @include ktheme() { border: 1px solid theme('k-shade'); @@ -260,7 +255,6 @@ const emit = defineEmits(['close']) } } .filter-display-list { - overflow-x: auto; .filter-item { @include ktheme() { border: 1px solid theme('k-primary'); From 169d1ecf199b3608c28347d81d0de8e67ef9dd79 Mon Sep 17 00:00:00 2001 From: Floyd Li Date: Tue, 4 Apr 2023 00:45:29 +0800 Subject: [PATCH 07/65] feat: ui update --- .../NotificationBox/NotificationBoxModal.vue | 42 +++++++++---------- .../NotificationBox/NotificationItem.vue | 15 ++++++- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/components/common/NotificationBox/NotificationBoxModal.vue b/components/common/NotificationBox/NotificationBoxModal.vue index fbecc3bb1a..2f228bddf1 100644 --- a/components/common/NotificationBox/NotificationBoxModal.vue +++ b/components/common/NotificationBox/NotificationBoxModal.vue @@ -41,7 +41,7 @@
-
+ class="no-wrap mr-1 mb-1" + @close="collectionFilter = null"> {{ collectionFilter.name }} - -
-
+ + class="no-wrap mb-1 mr-1" + @close="toggleEventFilter(event)"> {{ event.name }} - -
+
@@ -102,6 +102,8 @@ diff --git a/plugins/handleRedirect.ts b/plugins/handleRedirect.ts new file mode 100644 index 0000000000..2068b6a6d9 --- /dev/null +++ b/plugins/handleRedirect.ts @@ -0,0 +1,53 @@ +import { + EXTERNAL_LINK_BLACKLIST, + EXTERNAL_LINK_WHITELIST, +} from '../utils/constants' + +function isExternal(url: string) { + return !url.startsWith(window.location.origin) +} + +function getRedirectUrl(url: string) { + const urlObj = new URL(url) + const redirectHost = urlObj.host.toLocaleLowerCase() + if (EXTERNAL_LINK_WHITELIST.includes(redirectHost)) { + return url + } + const redirectUrl = encodeURIComponent(url) + if (EXTERNAL_LINK_BLACKLIST.includes(redirectHost)) { + return `/redirect?redirect=${redirectUrl}&warn=blacklist` + } + return `/redirect?redirect=${redirectUrl}` +} + +function handleLink() { + document.body.addEventListener('click', (event) => { + if ((event.target as HTMLElement).tagName === 'A') { + const target = event.target as HTMLLinkElement + if (isExternal(target.href)) { + event.preventDefault() + window.open(target.href) + } + } + }) +} + +function handleOpen() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const _window = window as any + _window._open = _window._open || _window.open + window.open = (url, target, ...args) => { + let redirectUrl = url + if (isExternal(url as string)) { + redirectUrl = getRedirectUrl(url as string) + target = '_blank' + } + _window._open(redirectUrl, target, ...args) + return window + } +} + +document.addEventListener('DOMContentLoaded', () => { + handleLink() + handleOpen() +}) diff --git a/utils/constants.ts b/utils/constants.ts index e04d99f405..37ebdb9234 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -110,3 +110,7 @@ export const getChainTestList = () => { } export const MIN_OFFER_PRICE = 0.01 + +export const EXTERNAL_LINK_WHITELIST = ['docs.kodadot.xyz'] + +export const EXTERNAL_LINK_BLACKLIST = ['github.com'] From 8a39b7286f663ac9d2cea90fd6af04300773c3a6 Mon Sep 17 00:00:00 2001 From: Viki Val Date: Wed, 29 Mar 2023 17:55:53 +0200 Subject: [PATCH 09/65] Update pages/redirect.vue --- pages/redirect.vue | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pages/redirect.vue b/pages/redirect.vue index 0e24c1eacb..c0d339f4a4 100644 --- a/pages/redirect.vue +++ b/pages/redirect.vue @@ -1,10 +1,3 @@ -