Bladeren bron

Release 3.3.2

attemka 1 jaar geleden
bovenliggende
commit
e68a8fcf9d
26 gewijzigde bestanden met toevoegingen van 1028 en 658 verwijderingen
  1. 17 0
      CHANGELOG.md
  2. 34 63
      packages/atlas/atlas.config.yml
  3. 2 2
      packages/atlas/package.json
  4. 19 0
      packages/atlas/src/MainLayout.tsx
  5. 24 4
      packages/atlas/src/api/hooks/notifications.ts
  6. 367 220
      packages/atlas/src/api/queries/__generated__/notifications.generated.tsx
  7. 67 0
      packages/atlas/src/api/queries/__generated__/storage.generated.tsx
  8. 61 85
      packages/atlas/src/api/queries/notifications.graphql
  9. 6 0
      packages/atlas/src/api/queries/storage.graphql
  10. 3 0
      packages/atlas/src/components/_nft/NftTile/NftTile.tsx
  11. 190 155
      packages/atlas/src/components/_nft/NftTile/NftTileDetails.tsx
  12. 3 1
      packages/atlas/src/components/_nft/NftTileViewer/NftTileViewer.tsx
  13. 23 25
      packages/atlas/src/components/_overlays/ContextMenu/ContextMenu.tsx
  14. 8 5
      packages/atlas/src/components/_overlays/Popover/Popover.tsx
  15. 5 1
      packages/atlas/src/config/routes.ts
  16. 4 4
      packages/atlas/src/providers/assets/assets.hooks.ts
  17. 29 1
      packages/atlas/src/providers/assets/assets.provider.tsx
  18. 22 0
      packages/atlas/src/providers/misc/store.ts
  19. 3 4
      packages/atlas/src/providers/notifications/notifications.hooks.ts
  20. 7 10
      packages/atlas/src/utils/logs/sentry.ts
  21. 1 1
      packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.tsx
  22. 4 4
      packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx
  23. 8 12
      packages/atlas/src/views/viewer/MemberView/MemberActivity.hooks.ts
  24. 68 9
      packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx
  25. 7 6
      packages/atlas/src/views/viewer/MemberView/MemberView.tsx
  26. 46 46
      yarn.lock

+ 17 - 0
CHANGELOG.md

@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [3.3.2] - 2023-06-02
+
+### Added
+
+- Added Telugu language
+
+### Changed
+
+- Sentry replays are enabled on YPP page only
+- Atlas TnC were updated
+
+### Fixed
+
+- Fixed issue when Atlas was sending incorrect storage buckets number
+- Fixed bug with multiple context menus for carousel NFT items
+- Fixed recurring updates on user activity page
+
 ## [3.3.1] - 2023-05-25
 
 ### Changed

+ 34 - 63
packages/atlas/atlas.config.yml

@@ -97,30 +97,6 @@ features:
       yppTnC: |
         # YouTube Partnership Program (YPP)
 
-        ## Rewards
-          - Sign up to YouTube Partnership Program: 5000 Joy
-          - Refer new program subscribers: 1000 Joy
-          - For every new video synced from YouTube: 300 Joy
-
-        ❗️ The tokens pool allocated for this program is limited, so the program has limited duration.
-
-        ## Tiers Multiplier
-
-        Based on the YouTube channel followers count, a popularity Tier is assigned to each participant. Popularity tier results in multiplication effect on all rewards of the program.
-
-          - Tier 1 - x1 rewards - 50 to 5,000 subscribers
-          - Tier 2 - x2.5 rewards - 5,000 to 50,000 subscribers
-          - Tier 3 - x5 rewards - 50,000+ subscribers
-
-        Referrals multiplier depends on the popularity tier of the channel signed up using referral link.
-
-        ## Example Rewards Calculation
-
-        For a channel with 7000 subscribers, which signed up and remained in the program with auto-sync service enabled for 1 month. During this month that channel uploaded 5 new videos to their YouTube channel, 2 videos directly to Joystream channel with manual upload, and successfully referred 3 other YouTube channels that had circa 10k subscribers each. 
-
-        In the end of this month, the payout to this channel's account will be:
-          5000 * 2.5 + 5 * 300 * 2.5 + 1000 * 3 * 2.5 = **23,750** JOY
-
         ## When you accept the Terms and Conditions you
 
         1. Become enrolled to the YouTube Partnership Program.
@@ -132,50 +108,17 @@ features:
 
         Rewards are subject to the verified status of the channel, assigned by the program operators.
 
-        # Terms and Conditions
+        ## YouTube Partnership Program Terms and Conditions
 
         ## Who is the program for
 
         The program is targeted at YouTube creators that already have a channel focussed on Crypto and Web3 content, with substantial published videos and subscribers. The program involves compensation in JOY tokens for YouTube creators who choose to opt in, meet the qualification criteria and perform actions that are linked to rewards.
 
-        ## Who Qualifies
-
-        YouTube channel has to:
-          1. Be created not earlier than 90 days before the sign up.
-          2. Have at least 50 followers and the channel followers must be set to public view.
-          3. Have at least 10 videos, each created at least 30 days before the sign up.
-          4. Channel must be focussed on Web3/ Crypto content, matching one of the categories supported by $VITE_APP_NAME App.
-
-        Newly created Joystream channel has to have description, avatar and background image set up. The criteria for qualification can be reviewed at any time without prior notice.
-
-        ## How to sign up
-
-        To sign up, user has to go through the onboarding flow provided in the [$VITE_APP_NAME web app](https://gleev.xyz/ypp) and authorise with the Google Account, connected to their YouTube channel. During the authorisation, the access to the YouTube content `youtube.readonly` scope, to fetch channel meta data and content information; and access to email address for the YouTube account has to be granted for the JSG operated Backend Application (API client) that connects to YouTube API.
-
-          Mandatory fields need to be populated to the web input form to progress, such as email, video category, and terms and conditions need to be accepted.
-
-        ## How to get rewards
-
-        Verified YouTube creators will be offered compensation in JOY tokens, native asset of Joystream blockchain for the participation in the program. In order to earn rewards, participants need to complete the rewarded tasks, namely:
-
-        1. New sign up: Authorise with their YouTube channel and choose an optional opt in to auto-sync service. One-off compensation only for new sign ups.
-        2. Referral: Refer other YouTube Web3/ Crypto content creators to the program, which results in successful signups. It can be done by generating unique referral code (link), which can be followed to create new membership. Once successful the new memberships created with the referred link, this will result in rewards for the originating account. The same code can be used by multiple new joiners. Alternatively referred channels can enter referring channel name to the referral field during sign up flow.
-        3. Auto-synced content: Simply post new Web3 and Crypto-focussed videos to the connected YouTube channel, and remain opted in to the program and auto-sync feature till they are fully uploaded to your channel.
+        ## YPP Rewards and Key Information
 
-        ## How rewards are calculated
+        There is a separate document, [YouTube Partner Program Outline](https://www.notion.so/joystream/YouTube-Partner-Program-Outline-d492c2eb88ff4ace955b5f2902ec21fb?pvs=4). All the program particulars, such as: how to join YouTube Partnership Program, rewards calculation explanation and example, payment information are contained in the program document on Notion. The company has the right to update the terms of the program upon its discretion, unilaterally and without any prior notice. Significant changes will be communicated via public communication channels, such as Discord server or social media accounts.
 
-        Rewards are based on the popularity Tiers assigned to the channels upon program registration tiers are calculated based on the number of subscribers. The ranges of subscribers qualifying for each Tier can be reviewed and updated by the program operators at any time without prior notice.
-
-        Your current Tier and tier information is contained in the Dashboard, available on the gleev.xyz/ypp page for the signed in members, already opted in to the program.
-
-        Rewards are calculated based on the internal DB records, generated automatically or added by the JSG team operating the program.
-          - For new signups, the sign up date will be reviewed in defining which period the payout should be made.
-          - For referrals, each new channel who signs up adding the referrer's channel name to the referrer field in the onboarding flow generates a record of the referral. In the end of the related payment cycle, number of referring channel records for new sign up is multiplied by referral base payment and tier multiplier of the referring channel.
-          - For the videos synced from YouTube, all video content will be monitored by program operator, Jsgenesis team and its partners. To qualify for compensation it has to adhere to reasonable quality, focus on Web3-Crypto topic, and match the category selected for the video. It must adhere to [Copyright Policy ](https://gleev.xyz/legal/copyright) and general [Terms of Service](https://gleev.xyz/legal/tos).
-
-        To qualify for rewards the channel needs to be verified by JSGenesis team and/or its partners operating the program. This is a manual process, including visual review of the YouTube channel title, description and uploaded content. To be verified the content has to be of reasonable quality, focussing on Crypto/Web3 and match the selected category selected during upload.
-
-        ## How rewards are paid
+        ## How YouTube Partnership Program rewards are paid
 
         When YouTube creators create Joystream channel app, they get an auto-generated polkadot account assigned to this channel automatically. This account is bound to their channel, and cannot be updated. This account can hold JOY tokens.
 
@@ -183,6 +126,14 @@ features:
 
         Rewards are paid only for verified channels. Rewards are paid via direct transfers from JS Genesis specific account to the creators' Joystream channel account. Rewards are paid by JS Genesis company, from the funds (tokens) budgeted for the purpose of operational support.
 
+        ## Other ways Joystream Channels can get rewards
+
+        Joystream DAO operating model encapsulates rewards for Joystream channels paid out outside of the Youtube Partnership Program. Gleev Operator, JS Genesis AS has no responsibility or control over such mechanisms and they are listed below.
+
+        1. Payout Proposal to Channels by DAO Council.
+        2. Direct Payment from DAO Working Group Budget by WG Lead.
+        3. Direct Member Payments from any member of Joystream Network.
+
         ## How to withdraw rewards
 
         In order to create Joystream channel, a Joystream membership is required. It can be created free of charge using the $VITE_APP_NAME App, hosted on gleev.xyz or Pioneer app hosted on  pioneerapp.xyz. Joystream membership requires a substrate account, created using any wallets compatible with Polkadot ecosystem.
@@ -209,6 +160,18 @@ features:
 
         If access of the JSGenesis operated backend app to your YouTube channel is disabled on the side of Google account, then you are automatically opted out from the YPP program and no longer qualify for rewards.
 
+        #### Rights of Jsgenesis and Third Party Applications
+
+        Accepting these terms and conditions between the creator and JS Genesis AS, warrants the rights for $VITE_APP_NAME App to sync the content from creator’s YouTube Channel with the Joystream Network. 
+
+        Syncing content means replicating or uploading the creator’s content to the storage nodes and make it available to content distribution nodes of Joystream Network infrastructure which is operated by Joystream DAO. The content record of ownership by the channel created by YouTube creators signing up to YouTube Partnership Program, is kept on the Joystream Blockchain, that is a public ledger. Joystream platform is an open source project and its operating model implies multiple Apps and Gateways connected to Joystream blockchain and network infrastructure. 
+
+        Synced content can be monetized by any participants of the Joystream Network, and specifically gateways (Apps) connected to Joystream blockchain and infrastructure. All connected Apps can distribute, display and monetize creator's video content and its attributes, such as likes by members of network, comments, reactions, metadata of channel and membership owning the video content.
+
+        Creator has the rights to claim monetization of their content as part of general platform T&Cs, and the compensation in JOY tokens from JS Genesis AS for the synced content from YouTube as part of the YouTube Partnership Program agreement.
+
+        For the full duration of the content existing on Joystream Network and as long as creator does not delete their content and/or channel from the blockchain, any third party application can use the content of such channel as described in this paragraph. This applies even to the cases when Jsgenesis AS is no longer operating $VITE_APP_NAME App and seizes to exist as a legal entity.
+
         ## License
 
         Auto-sync service will read the videos meta-data fields, including title, description, date created, duration, language and license and transfer them to the newly created video on the creators Joystream channel.
@@ -474,6 +437,8 @@ content:
       name: Swedish
     - isoCode: ta
       name: Tamil
+    - isoCode: te
+      name: Telugu
     - isoCode: th
       name: Thai
     - isoCode: tr
@@ -501,7 +466,7 @@ legal:
   termsOfService: |
     # Terms of Service
 
-    Last updated on the 25th of January 2023
+    Last updated on the 29th of May 2023
 
     This Terms of Service ("Agreement") is a binding obligation between you ("User") and Jsgenesis AS ("Company", "We", "Us", "Our") for use of our Joystream Player interface ("$VITE_APP_NAME") hosted at play.joystream.org and all other products (collectively "Software") developed and published by Us.
 
@@ -511,7 +476,13 @@ legal:
 
     ## 2. Changes to Terms
 
-    This Agreement may be modified or updated at the sole discretion of Company without notice. Your continued use of our Software is confirmation of your acceptance of the latest Agreement.
+    This Agreement may be modified or updated at the sole discretion of Company in a unilateral way, without prior notice. In case of significant change of terms the Company takes the obligation to take reasonable efforts to notify its customers about the change of the Terms.
+
+    This may take a non-personalised form, such as a banner in the app for all users, post on Company's Discord server channel or Company's social media accounts. It is your obligation to follow the Company's communication channels to stay informed about the changes. 
+
+    Upon the receipt of the notification you may choose to continue using the software, or conduct cessation of services via request to remove your membership and channel from Joystream Network. This request must be sent via email to leave@jsgenesis.com. 
+
+    Your continued use of our Software is confirmation of your acceptance of the latest Agreement.
 
     ## 3. Privacy Policy
 

+ 2 - 2
packages/atlas/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@joystream/atlas",
   "description": "UI for consuming Joystream - a user governed video platform",
-  "version": "3.3.1",
+  "version": "3.3.2",
   "license": "GPL-3.0",
   "scripts": {
     "start": "vite",
@@ -43,7 +43,7 @@
     "@livesession/sdk": "^1.1.4",
     "@loadable/component": "^5.15.2",
     "@lottiefiles/react-lottie-player": "^3.5.0",
-    "@sentry/react": "^7.47.0",
+    "@sentry/react": "^7.53.1",
     "@talismn/connect-wallets": "^1.2.1",
     "@tippyjs/react": "^4.2.6",
     "aos": "^2.3.4",

+ 19 - 0
packages/atlas/src/MainLayout.tsx

@@ -4,10 +4,12 @@ import { Route, Routes, useLocation, useNavigationType } from 'react-router-dom'
 
 import { StudioLoading } from '@/components/_loaders/StudioLoading'
 import { CookiePopover } from '@/components/_overlays/CookiePopover'
+import { atlasConfig } from '@/config'
 import { BASE_PATHS, absoluteRoutes } from '@/config/routes'
 import { transitions } from '@/styles'
 import { RoutingState } from '@/types/routing'
 import { isBrowserOutdated } from '@/utils/browser'
+import { SentryLogger } from '@/utils/logs'
 
 import { AppLogo } from './components/AppLogo'
 import { TopbarBase } from './components/_navigation/TopbarBase'
@@ -53,6 +55,23 @@ export const MainLayout: FC = () => {
     onExitClick: () => closeDialog(),
   })
 
+  useEffect(() => {
+    if (!atlasConfig.analytics.sentry?.dsn) {
+      return
+    }
+    const stopReplay = async () => await SentryLogger.replay?.stop()
+
+    if (location.pathname === absoluteRoutes.viewer.ypp()) {
+      if (SentryLogger.initialized && !SentryLogger.replay?.getReplayId()) {
+        SentryLogger.replay?.start()
+      }
+    } else {
+      if (SentryLogger.replay?.getReplayId()) {
+        stopReplay()
+      }
+    }
+  }, [location.pathname])
+
   const { clearOverlays } = useOverlayManager()
 
   useEffect(() => {

+ 24 - 4
packages/atlas/src/api/hooks/notifications.ts

@@ -1,10 +1,13 @@
 import { QueryHookOptions } from '@apollo/client'
 
 import {
+  GetNftActivitiesCountQuery,
+  GetNftActivitiesCountQueryVariables,
   GetNftActivitiesQuery,
   GetNftActivitiesQueryVariables,
   GetNotificationsConnectionQuery,
   GetNotificationsConnectionQueryVariables,
+  useGetNftActivitiesCountQuery,
   useGetNftActivitiesQuery,
   useGetNotificationsConnectionQuery,
 } from '@/api/queries/__generated__/notifications.generated'
@@ -31,6 +34,27 @@ export const useRawNotifications = (
   }
 }
 
+export const useActivitiesCount = (
+  memberId?: string,
+  opts?: QueryHookOptions<GetNftActivitiesCountQuery, GetNftActivitiesCountQueryVariables>
+) => {
+  const { data, ...rest } = useGetNftActivitiesCountQuery({
+    ...opts,
+    variables: {
+      memberId: memberId || '',
+    },
+    skip: !memberId,
+  })
+
+  return {
+    nftsBiddedTotalCount: data?.nftsBidded.totalCount,
+    nftsBoughtTotalCount: data?.nftsBought.totalCount,
+    nftsSoldTotalCount: data?.nftsSold.totalCount,
+    nftsIssuedTotalCount: data?.nftsIssued.totalCount,
+    ...rest,
+  }
+}
+
 export const useRawActivities = (
   memberId?: string,
   sort: NftActivityOrderByInput = NftActivityOrderByInput.EventTimestampDesc,
@@ -47,10 +71,6 @@ export const useRawActivities = (
   })
 
   return {
-    nftsBiddedTotalCount: data?.nftsBidded.totalCount,
-    nftsBoughtTotalCount: data?.nftsBought.totalCount,
-    nftsSoldTotalCount: data?.nftsSold.totalCount,
-    nftsIssuedTotalCount: data?.nftsIssued.totalCount,
     totalCount: data?.nftActivitiesConnection.totalCount,
     pageInfo: data?.nftActivitiesConnection.pageInfo,
     activities: data?.nftActivitiesConnection.edges,

+ 367 - 220
packages/atlas/src/api/queries/__generated__/notifications.generated.tsx

@@ -109,9 +109,32 @@ export type GetNotificationsConnectionQuery = {
                       } | null
                     }
                   } | null
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: { __typename?: 'OwnedNft'; video: { __typename?: 'Video'; id: string; title?: string | null } }
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
+                        id: string
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
+                    }
                   }
                 }
                 nftOwner:
@@ -233,7 +256,33 @@ export type GetNotificationsConnectionQuery = {
                         | null
                     } | null
                   }
-                  nft: { __typename?: 'OwnedNft'; video: { __typename?: 'Video'; id: string; title?: string | null } }
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
+                        id: string
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
+                    }
+                  }
                 }
                 previousNftOwner:
                   | {
@@ -403,9 +452,32 @@ export type GetNotificationsConnectionQuery = {
                         | null
                     } | null
                   }
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: { __typename?: 'OwnedNft'; video: { __typename?: 'Video'; id: string; title?: string | null } }
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
+                        id: string
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
+                    }
                   }
                 }
                 previousNftOwner:
@@ -527,7 +599,33 @@ export type GetNotificationsConnectionQuery = {
                       | null
                   } | null
                 }
-                nft: { __typename?: 'OwnedNft'; video: { __typename?: 'Video'; id: string; title?: string | null } }
+                nft: {
+                  __typename?: 'OwnedNft'
+                  video: {
+                    __typename?: 'Video'
+                    id: string
+                    title?: string | null
+                    thumbnailPhoto?: {
+                      __typename?: 'StorageDataObject'
+                      id: string
+                      resolvedUrls: Array<string>
+                      resolvedUrl?: string | null
+                      createdAt: Date
+                      size: string
+                      isAccepted: boolean
+                      ipfsHash: string
+                      storageBag: { __typename?: 'StorageBag'; id: string }
+                      type?:
+                        | { __typename: 'DataObjectTypeChannelAvatar' }
+                        | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                        | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                        | { __typename: 'DataObjectTypeVideoMedia' }
+                        | { __typename: 'DataObjectTypeVideoSubtitle' }
+                        | { __typename: 'DataObjectTypeVideoThumbnail' }
+                        | null
+                    } | null
+                  }
+                }
               }
             | { __typename?: 'NftIssuedEventData' }
             | { __typename?: 'NftSellOrderMadeEventData' }
@@ -570,9 +668,32 @@ export type GetNotificationsConnectionQuery = {
                         | null
                     } | null
                   }
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: { __typename?: 'OwnedNft'; video: { __typename?: 'Video'; id: string; title?: string | null } }
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
+                        id: string
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
+                    }
                   }
                 }
                 previousNftOwner:
@@ -1728,6 +1849,18 @@ export type GetNftHistoryQuery = {
   }>
 }
 
+export type GetNftActivitiesCountQueryVariables = Types.Exact<{
+  memberId: Types.Scalars['String']
+}>
+
+export type GetNftActivitiesCountQuery = {
+  __typename?: 'Query'
+  nftsBought: { __typename?: 'NftActivitiesConnection'; totalCount: number }
+  nftsSold: { __typename?: 'NftActivitiesConnection'; totalCount: number }
+  nftsIssued: { __typename?: 'NftActivitiesConnection'; totalCount: number }
+  nftsBidded: { __typename?: 'NftActivitiesConnection'; totalCount: number }
+}
+
 export type GetNftActivitiesQueryVariables = Types.Exact<{
   memberId: Types.Scalars['String']
   first: Types.Scalars['Int']
@@ -1737,10 +1870,6 @@ export type GetNftActivitiesQueryVariables = Types.Exact<{
 
 export type GetNftActivitiesQuery = {
   __typename?: 'Query'
-  nftsBought: { __typename?: 'NftActivitiesConnection'; totalCount: number }
-  nftsSold: { __typename?: 'NftActivitiesConnection'; totalCount: number }
-  nftsIssued: { __typename?: 'NftActivitiesConnection'; totalCount: number }
-  nftsBidded: { __typename?: 'NftActivitiesConnection'; totalCount: number }
   nftActivitiesConnection: {
     __typename?: 'NftActivitiesConnection'
     totalCount: number
@@ -1794,34 +1923,31 @@ export type GetNftActivitiesQuery = {
                 }
                 bid: {
                   __typename?: 'Bid'
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: {
-                      __typename?: 'OwnedNft'
-                      video: {
-                        __typename?: 'Video'
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
                         id: string
-                        title?: string | null
-                        thumbnailPhoto?: {
-                          __typename?: 'StorageDataObject'
-                          id: string
-                          resolvedUrls: Array<string>
-                          resolvedUrl?: string | null
-                          createdAt: Date
-                          size: string
-                          isAccepted: boolean
-                          ipfsHash: string
-                          storageBag: { __typename?: 'StorageBag'; id: string }
-                          type?:
-                            | { __typename: 'DataObjectTypeChannelAvatar' }
-                            | { __typename: 'DataObjectTypeChannelCoverPhoto' }
-                            | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
-                            | { __typename: 'DataObjectTypeVideoMedia' }
-                            | { __typename: 'DataObjectTypeVideoSubtitle' }
-                            | { __typename: 'DataObjectTypeVideoThumbnail' }
-                            | null
-                        } | null
-                      }
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
                     }
                   }
                 }
@@ -1980,34 +2106,31 @@ export type GetNftActivitiesQuery = {
                       } | null
                     }
                   } | null
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: {
-                      __typename?: 'OwnedNft'
-                      video: {
-                        __typename?: 'Video'
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
                         id: string
-                        title?: string | null
-                        thumbnailPhoto?: {
-                          __typename?: 'StorageDataObject'
-                          id: string
-                          resolvedUrls: Array<string>
-                          resolvedUrl?: string | null
-                          createdAt: Date
-                          size: string
-                          isAccepted: boolean
-                          ipfsHash: string
-                          storageBag: { __typename?: 'StorageBag'; id: string }
-                          type?:
-                            | { __typename: 'DataObjectTypeChannelAvatar' }
-                            | { __typename: 'DataObjectTypeChannelCoverPhoto' }
-                            | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
-                            | { __typename: 'DataObjectTypeVideoMedia' }
-                            | { __typename: 'DataObjectTypeVideoSubtitle' }
-                            | { __typename: 'DataObjectTypeVideoThumbnail' }
-                            | null
-                        } | null
-                      }
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
                     }
                   }
                 }
@@ -2240,34 +2363,31 @@ export type GetNftActivitiesQuery = {
                         | null
                     } | null
                   }
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: {
-                      __typename?: 'OwnedNft'
-                      video: {
-                        __typename?: 'Video'
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
                         id: string
-                        title?: string | null
-                        thumbnailPhoto?: {
-                          __typename?: 'StorageDataObject'
-                          id: string
-                          resolvedUrls: Array<string>
-                          resolvedUrl?: string | null
-                          createdAt: Date
-                          size: string
-                          isAccepted: boolean
-                          ipfsHash: string
-                          storageBag: { __typename?: 'StorageBag'; id: string }
-                          type?:
-                            | { __typename: 'DataObjectTypeChannelAvatar' }
-                            | { __typename: 'DataObjectTypeChannelCoverPhoto' }
-                            | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
-                            | { __typename: 'DataObjectTypeVideoMedia' }
-                            | { __typename: 'DataObjectTypeVideoSubtitle' }
-                            | { __typename: 'DataObjectTypeVideoThumbnail' }
-                            | null
-                        } | null
-                      }
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
                     }
                   }
                 }
@@ -2613,34 +2733,31 @@ export type GetNftActivitiesQuery = {
                         | null
                     } | null
                   }
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: {
-                      __typename?: 'OwnedNft'
-                      video: {
-                        __typename?: 'Video'
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
                         id: string
-                        title?: string | null
-                        thumbnailPhoto?: {
-                          __typename?: 'StorageDataObject'
-                          id: string
-                          resolvedUrls: Array<string>
-                          resolvedUrl?: string | null
-                          createdAt: Date
-                          size: string
-                          isAccepted: boolean
-                          ipfsHash: string
-                          storageBag: { __typename?: 'StorageBag'; id: string }
-                          type?:
-                            | { __typename: 'DataObjectTypeChannelAvatar' }
-                            | { __typename: 'DataObjectTypeChannelCoverPhoto' }
-                            | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
-                            | { __typename: 'DataObjectTypeVideoMedia' }
-                            | { __typename: 'DataObjectTypeVideoSubtitle' }
-                            | { __typename: 'DataObjectTypeVideoThumbnail' }
-                            | null
-                        } | null
-                      }
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
                     }
                   }
                 }
@@ -3157,34 +3274,31 @@ export type GetNftActivitiesQuery = {
                         | null
                     } | null
                   }
-                  auction: {
-                    __typename?: 'Auction'
-                    nft: {
-                      __typename?: 'OwnedNft'
-                      video: {
-                        __typename?: 'Video'
+                  nft: {
+                    __typename?: 'OwnedNft'
+                    video: {
+                      __typename?: 'Video'
+                      id: string
+                      title?: string | null
+                      thumbnailPhoto?: {
+                        __typename?: 'StorageDataObject'
                         id: string
-                        title?: string | null
-                        thumbnailPhoto?: {
-                          __typename?: 'StorageDataObject'
-                          id: string
-                          resolvedUrls: Array<string>
-                          resolvedUrl?: string | null
-                          createdAt: Date
-                          size: string
-                          isAccepted: boolean
-                          ipfsHash: string
-                          storageBag: { __typename?: 'StorageBag'; id: string }
-                          type?:
-                            | { __typename: 'DataObjectTypeChannelAvatar' }
-                            | { __typename: 'DataObjectTypeChannelCoverPhoto' }
-                            | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
-                            | { __typename: 'DataObjectTypeVideoMedia' }
-                            | { __typename: 'DataObjectTypeVideoSubtitle' }
-                            | { __typename: 'DataObjectTypeVideoThumbnail' }
-                            | null
-                        } | null
-                      }
+                        resolvedUrls: Array<string>
+                        resolvedUrl?: string | null
+                        createdAt: Date
+                        size: string
+                        isAccepted: boolean
+                        ipfsHash: string
+                        storageBag: { __typename?: 'StorageBag'; id: string }
+                        type?:
+                          | { __typename: 'DataObjectTypeChannelAvatar' }
+                          | { __typename: 'DataObjectTypeChannelCoverPhoto' }
+                          | { __typename: 'DataObjectTypeChannelPayoutsPayload' }
+                          | { __typename: 'DataObjectTypeVideoMedia' }
+                          | { __typename: 'DataObjectTypeVideoSubtitle' }
+                          | { __typename: 'DataObjectTypeVideoThumbnail' }
+                          | null
+                      } | null
                     }
                   }
                 }
@@ -3416,12 +3530,9 @@ export const GetNotificationsConnectionDocument = gql`
                       ...BasicMembershipFields
                     }
                   }
-                  auction {
-                    nft {
-                      video {
-                        id
-                        title
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3436,8 +3547,7 @@ export const GetNotificationsConnectionDocument = gql`
                 price
                 nft {
                   video {
-                    id
-                    title
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -3449,8 +3559,7 @@ export const GetNotificationsConnectionDocument = gql`
                   amount
                   nft {
                     video {
-                      id
-                      title
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3464,12 +3573,9 @@ export const GetNotificationsConnectionDocument = gql`
                   bidder {
                     ...BasicMembershipFields
                   }
-                  auction {
-                    nft {
-                      video {
-                        id
-                        title
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3482,12 +3588,9 @@ export const GetNotificationsConnectionDocument = gql`
                   bidder {
                     ...BasicMembershipFields
                   }
-                  auction {
-                    nft {
-                      video {
-                        id
-                        title
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3517,6 +3620,7 @@ export const GetNotificationsConnectionDocument = gql`
     }
   }
   ${BasicMembershipFieldsFragmentDoc}
+  ${BasicVideoActivityFieldsFragmentDoc}
   ${BasicNftOwnerFieldsFragmentDoc}
 `
 
@@ -3696,18 +3800,14 @@ export function useGetNftHistoryLazyQuery(
 export type GetNftHistoryQueryHookResult = ReturnType<typeof useGetNftHistoryQuery>
 export type GetNftHistoryLazyQueryHookResult = ReturnType<typeof useGetNftHistoryLazyQuery>
 export type GetNftHistoryQueryResult = Apollo.QueryResult<GetNftHistoryQuery, GetNftHistoryQueryVariables>
-export const GetNftActivitiesDocument = gql`
-  query GetNftActivities(
-    $memberId: String!
-    $first: Int!
-    $after: String
-    $orderBy: [NftActivityOrderByInput!] = event_timestamp_DESC
-  ) {
+export const GetNftActivitiesCountDocument = gql`
+  query GetNftActivitiesCount($memberId: String!) {
     nftsBought: nftActivitiesConnection(
       where: {
-        event: {
-          OR: [
-            {
+        OR: [
+          {
+            member: { id_eq: $memberId }
+            event: {
               data: {
                 isTypeOf_in: [
                   "EnglishAuctionSettledEventData"
@@ -3717,19 +3817,23 @@ export const GetNftActivitiesDocument = gql`
                 winningBid: { bidder: { id_eq: $memberId } }
               }
             }
-            { data: { isTypeOf_eq: "NftBoughtEventData", buyer: { id_eq: $memberId } } }
-          ]
-        }
+          }
+          {
+            member: { id_eq: $memberId }
+            event: { data: { isTypeOf_eq: "NftBoughtEventData", buyer: { id_eq: $memberId } } }
+          }
+        ]
       }
-      orderBy: $orderBy
+      orderBy: event_timestamp_DESC
     ) {
       totalCount
     }
     nftsSold: nftActivitiesConnection(
       where: {
-        event: {
-          OR: [
-            {
+        OR: [
+          {
+            member: { id_eq: $memberId }
+            event: {
               data: {
                 isTypeOf_in: [
                   "EnglishAuctionSettledEventData"
@@ -3740,7 +3844,10 @@ export const GetNftActivitiesDocument = gql`
                 previousNftOwner: { member: { id_eq: $memberId } }
               }
             }
-            {
+          }
+          {
+            member: { id_eq: $memberId }
+            event: {
               data: {
                 isTypeOf_in: [
                   "EnglishAuctionSettledEventData"
@@ -3751,10 +3858,10 @@ export const GetNftActivitiesDocument = gql`
                 previousNftOwner: { channel: { ownerMember: { id_eq: $memberId } } }
               }
             }
-          ]
-        }
+          }
+        ]
       }
-      orderBy: $orderBy
+      orderBy: event_timestamp_DESC
     ) {
       totalCount
     }
@@ -3769,16 +3876,66 @@ export const GetNftActivitiesDocument = gql`
           ]
         }
       }
-      orderBy: $orderBy
+      orderBy: event_timestamp_DESC
     ) {
       totalCount
     }
     nftsBidded: nftActivitiesConnection(
       where: { event: { data: { isTypeOf_eq: "AuctionBidMadeEventData", bid: { bidder: { id_eq: $memberId } } } } }
-      orderBy: $orderBy
+      orderBy: event_timestamp_DESC
     ) {
       totalCount
     }
+  }
+`
+
+/**
+ * __useGetNftActivitiesCountQuery__
+ *
+ * To run a query within a React component, call `useGetNftActivitiesCountQuery` and pass it any options that fit your needs.
+ * When your component renders, `useGetNftActivitiesCountQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * you can use to render your UI.
+ *
+ * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
+ *
+ * @example
+ * const { data, loading, error } = useGetNftActivitiesCountQuery({
+ *   variables: {
+ *      memberId: // value for 'memberId'
+ *   },
+ * });
+ */
+export function useGetNftActivitiesCountQuery(
+  baseOptions: Apollo.QueryHookOptions<GetNftActivitiesCountQuery, GetNftActivitiesCountQueryVariables>
+) {
+  const options = { ...defaultOptions, ...baseOptions }
+  return Apollo.useQuery<GetNftActivitiesCountQuery, GetNftActivitiesCountQueryVariables>(
+    GetNftActivitiesCountDocument,
+    options
+  )
+}
+export function useGetNftActivitiesCountLazyQuery(
+  baseOptions?: Apollo.LazyQueryHookOptions<GetNftActivitiesCountQuery, GetNftActivitiesCountQueryVariables>
+) {
+  const options = { ...defaultOptions, ...baseOptions }
+  return Apollo.useLazyQuery<GetNftActivitiesCountQuery, GetNftActivitiesCountQueryVariables>(
+    GetNftActivitiesCountDocument,
+    options
+  )
+}
+export type GetNftActivitiesCountQueryHookResult = ReturnType<typeof useGetNftActivitiesCountQuery>
+export type GetNftActivitiesCountLazyQueryHookResult = ReturnType<typeof useGetNftActivitiesCountLazyQuery>
+export type GetNftActivitiesCountQueryResult = Apollo.QueryResult<
+  GetNftActivitiesCountQuery,
+  GetNftActivitiesCountQueryVariables
+>
+export const GetNftActivitiesDocument = gql`
+  query GetNftActivities(
+    $memberId: String!
+    $first: Int!
+    $after: String
+    $orderBy: [NftActivityOrderByInput!] = event_timestamp_DESC
+  ) {
     nftActivitiesConnection(first: $first, after: $after, orderBy: $orderBy, where: { member: { id_eq: $memberId } }) {
       totalCount
       pageInfo {
@@ -3807,11 +3964,9 @@ export const GetNftActivitiesDocument = gql`
                       ...BasicMembershipFields
                     }
                   }
-                  auction {
-                    nft {
-                      video {
-                        ...BasicVideoActivityFields
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3825,11 +3980,9 @@ export const GetNftActivitiesDocument = gql`
                     ...BasicMembershipFields
                   }
                   amount
-                  auction {
-                    nft {
-                      video {
-                        ...BasicVideoActivityFields
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3856,11 +4009,9 @@ export const GetNftActivitiesDocument = gql`
                   bidder {
                     ...BasicMembershipFields
                   }
-                  auction {
-                    nft {
-                      video {
-                        ...BasicVideoActivityFields
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                   amount
@@ -3872,11 +4023,9 @@ export const GetNftActivitiesDocument = gql`
                   bidder {
                     ...BasicMembershipFields
                   }
-                  auction {
-                    nft {
-                      video {
-                        ...BasicVideoActivityFields
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }
@@ -3924,11 +4073,9 @@ export const GetNftActivitiesDocument = gql`
                   ...BasicMembershipFields
                 }
                 bid {
-                  auction {
-                    nft {
-                      video {
-                        ...BasicVideoActivityFields
-                      }
+                  nft {
+                    video {
+                      ...BasicVideoActivityFields
                     }
                   }
                 }

+ 67 - 0
packages/atlas/src/api/queries/__generated__/storage.generated.tsx

@@ -42,6 +42,15 @@ export type GetBasicStorageBucketsQuery = {
   storageBuckets: Array<{ __typename?: 'StorageBucket'; id: string }>
 }
 
+export type GetAvailableStorageBucketsForBagQueryVariables = Types.Exact<{
+  where?: Types.InputMaybe<Types.StorageBucketWhereInput>
+}>
+
+export type GetAvailableStorageBucketsForBagQuery = {
+  __typename?: 'Query'
+  storageBuckets: Array<{ __typename?: 'StorageBucket'; id: string }>
+}
+
 export const GetStorageBucketsWithBagsDocument = gql`
   query GetStorageBucketsWithBags {
     storageBuckets(
@@ -207,3 +216,61 @@ export type GetBasicStorageBucketsQueryResult = Apollo.QueryResult<
   GetBasicStorageBucketsQuery,
   GetBasicStorageBucketsQueryVariables
 >
+export const GetAvailableStorageBucketsForBagDocument = gql`
+  query GetAvailableStorageBucketsForBag($where: StorageBucketWhereInput) {
+    storageBuckets(limit: 500, where: $where) {
+      id
+    }
+  }
+`
+
+/**
+ * __useGetAvailableStorageBucketsForBagQuery__
+ *
+ * To run a query within a React component, call `useGetAvailableStorageBucketsForBagQuery` and pass it any options that fit your needs.
+ * When your component renders, `useGetAvailableStorageBucketsForBagQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * you can use to render your UI.
+ *
+ * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
+ *
+ * @example
+ * const { data, loading, error } = useGetAvailableStorageBucketsForBagQuery({
+ *   variables: {
+ *      where: // value for 'where'
+ *   },
+ * });
+ */
+export function useGetAvailableStorageBucketsForBagQuery(
+  baseOptions?: Apollo.QueryHookOptions<
+    GetAvailableStorageBucketsForBagQuery,
+    GetAvailableStorageBucketsForBagQueryVariables
+  >
+) {
+  const options = { ...defaultOptions, ...baseOptions }
+  return Apollo.useQuery<GetAvailableStorageBucketsForBagQuery, GetAvailableStorageBucketsForBagQueryVariables>(
+    GetAvailableStorageBucketsForBagDocument,
+    options
+  )
+}
+export function useGetAvailableStorageBucketsForBagLazyQuery(
+  baseOptions?: Apollo.LazyQueryHookOptions<
+    GetAvailableStorageBucketsForBagQuery,
+    GetAvailableStorageBucketsForBagQueryVariables
+  >
+) {
+  const options = { ...defaultOptions, ...baseOptions }
+  return Apollo.useLazyQuery<GetAvailableStorageBucketsForBagQuery, GetAvailableStorageBucketsForBagQueryVariables>(
+    GetAvailableStorageBucketsForBagDocument,
+    options
+  )
+}
+export type GetAvailableStorageBucketsForBagQueryHookResult = ReturnType<
+  typeof useGetAvailableStorageBucketsForBagQuery
+>
+export type GetAvailableStorageBucketsForBagLazyQueryHookResult = ReturnType<
+  typeof useGetAvailableStorageBucketsForBagLazyQuery
+>
+export type GetAvailableStorageBucketsForBagQueryResult = Apollo.QueryResult<
+  GetAvailableStorageBucketsForBagQuery,
+  GetAvailableStorageBucketsForBagQueryVariables
+>

+ 61 - 85
packages/atlas/src/api/queries/notifications.graphql

@@ -1,11 +1,3 @@
-# CHANGE: Throught this file we're now using one `events` query to receive all related events
-# (instead of separate queries for each event type)
-
-# Some redundant fields (result of schema "flattening") were also removed from the events,
-# as they can now be accessed though deep filtering.
-
-# Note that in this case `orderBy` and `limit` now applies to all events together, not on per-type basis
-
 query GetNotificationsConnection($memberId: String!, $first: Int!, $after: String) {
   notificationsConnection(
     first: $first
@@ -37,12 +29,9 @@ query GetNotificationsConnection($memberId: String!, $first: Int!, $after: Strin
                     ...BasicMembershipFields
                   }
                 }
-                auction {
-                  nft {
-                    video {
-                      id
-                      title
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -58,8 +47,7 @@ query GetNotificationsConnection($memberId: String!, $first: Int!, $after: Strin
               price
               nft {
                 video {
-                  id
-                  title
+                  ...BasicVideoActivityFields
                 }
               }
             }
@@ -72,8 +60,7 @@ query GetNotificationsConnection($memberId: String!, $first: Int!, $after: Strin
                 amount
                 nft {
                   video {
-                    id
-                    title
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -88,12 +75,9 @@ query GetNotificationsConnection($memberId: String!, $first: Int!, $after: Strin
                 bidder {
                   ...BasicMembershipFields
                 }
-                auction {
-                  nft {
-                    video {
-                      id
-                      title
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -107,12 +91,9 @@ query GetNotificationsConnection($memberId: String!, $first: Int!, $after: Strin
                 bidder {
                   ...BasicMembershipFields
                 }
-                auction {
-                  nft {
-                    video {
-                      id
-                      title
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -254,17 +235,13 @@ query GetNftHistory($nftId: String!) {
   }
 }
 
-query GetNftActivities(
-  $memberId: String!
-  $first: Int!
-  $after: String
-  $orderBy: [NftActivityOrderByInput!] = event_timestamp_DESC
-) {
+query GetNftActivitiesCount($memberId: String!) {
   nftsBought: nftActivitiesConnection(
     where: {
-      event: {
-        OR: [
-          {
+      OR: [
+        {
+          member: { id_eq: $memberId }
+          event: {
             data: {
               isTypeOf_in: [
                 "EnglishAuctionSettledEventData"
@@ -274,20 +251,24 @@ query GetNftActivities(
               winningBid: { bidder: { id_eq: $memberId } }
             }
           }
-          { data: { isTypeOf_eq: "NftBoughtEventData", buyer: { id_eq: $memberId } } }
-        ]
-      }
+        }
+        {
+          member: { id_eq: $memberId }
+          event: { data: { isTypeOf_eq: "NftBoughtEventData", buyer: { id_eq: $memberId } } }
+        }
+      ]
     }
-    orderBy: $orderBy
+    orderBy: event_timestamp_DESC
   ) {
     totalCount
   }
 
   nftsSold: nftActivitiesConnection(
     where: {
-      event: {
-        OR: [
-          {
+      OR: [
+        {
+          member: { id_eq: $memberId }
+          event: {
             data: {
               isTypeOf_in: [
                 "EnglishAuctionSettledEventData"
@@ -298,7 +279,10 @@ query GetNftActivities(
               previousNftOwner: { member: { id_eq: $memberId } }
             }
           }
-          {
+        }
+        {
+          member: { id_eq: $memberId }
+          event: {
             data: {
               isTypeOf_in: [
                 "EnglishAuctionSettledEventData"
@@ -309,10 +293,10 @@ query GetNftActivities(
               previousNftOwner: { channel: { ownerMember: { id_eq: $memberId } } }
             }
           }
-        ]
-      }
+        }
+      ]
     }
-    orderBy: $orderBy
+    orderBy: event_timestamp_DESC
   ) {
     totalCount
   }
@@ -326,25 +310,26 @@ query GetNftActivities(
         ]
       }
     }
-    orderBy: $orderBy
+    orderBy: event_timestamp_DESC
   ) {
     totalCount
   }
 
   nftsBidded: nftActivitiesConnection(
     where: { event: { data: { isTypeOf_eq: "AuctionBidMadeEventData", bid: { bidder: { id_eq: $memberId } } } } }
-    orderBy: $orderBy
+    orderBy: event_timestamp_DESC
   ) {
     totalCount
   }
+}
 
-  nftActivitiesConnection(
-    first: $first
-    after: $after
-    orderBy: $orderBy # CHANGE: `event_timestamp` now used instead of `createdAt` (which is no longer available)
-    where: { member: { id_eq: $memberId } } # CHANGE: Simplified filtering
-  ) {
-    # CHANGE: The actual `event` is now nested as a property of `NftActivity`
+query GetNftActivities(
+  $memberId: String!
+  $first: Int!
+  $after: String
+  $orderBy: [NftActivityOrderByInput!] = event_timestamp_DESC
+) {
+  nftActivitiesConnection(first: $first, after: $after, orderBy: $orderBy, where: { member: { id_eq: $memberId } }) {
     totalCount
     pageInfo {
       endCursor
@@ -355,7 +340,7 @@ query GetNftActivities(
       node {
         event {
           id
-          timestamp # CHANGE: `timestamp` now used instead of `createdAt` (which is no longer available)
+          timestamp
           inBlock
           data {
             ... on AuctionBidMadeEventData {
@@ -372,11 +357,9 @@ query GetNftActivities(
                     ...BasicMembershipFields
                   }
                 }
-                auction {
-                  nft {
-                    video {
-                      ...BasicVideoActivityFields
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -390,11 +373,9 @@ query GetNftActivities(
                   ...BasicMembershipFields
                 }
                 amount
-                auction {
-                  nft {
-                    video {
-                      ...BasicVideoActivityFields
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -421,11 +402,10 @@ query GetNftActivities(
                 bidder {
                   ...BasicMembershipFields
                 }
-                auction {
-                  nft {
-                    video {
-                      ...BasicVideoActivityFields
-                    }
+
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
                 amount
@@ -437,11 +417,9 @@ query GetNftActivities(
                 bidder {
                   ...BasicMembershipFields
                 }
-                auction {
-                  nft {
-                    video {
-                      ...BasicVideoActivityFields
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }
@@ -489,11 +467,9 @@ query GetNftActivities(
                 ...BasicMembershipFields
               }
               bid {
-                auction {
-                  nft {
-                    video {
-                      ...BasicVideoActivityFields
-                    }
+                nft {
+                  video {
+                    ...BasicVideoActivityFields
                   }
                 }
               }

+ 6 - 0
packages/atlas/src/api/queries/storage.graphql

@@ -42,3 +42,9 @@ query GetBasicStorageBuckets {
     id
   }
 }
+
+query GetAvailableStorageBucketsForBag($where: StorageBucketWhereInput) {
+  storageBuckets(limit: 500, where: $where) {
+    id
+  }
+}

+ 3 - 0
packages/atlas/src/components/_nft/NftTile/NftTile.tsx

@@ -16,6 +16,7 @@ import { Member, NftTileDetails } from './NftTileDetails'
 
 export type NftTileProps = {
   status?: 'idle' | 'buy-now' | 'auction'
+  isInCarousel?: boolean
   thumbnail?: VideoThumbnailProps
   title?: string | null
   owner?: Member
@@ -38,6 +39,7 @@ export type NftTileProps = {
 
 export const NftTile: FC<NftTileProps> = ({
   status,
+  isInCarousel,
   thumbnail,
   loading,
   title,
@@ -99,6 +101,7 @@ export const NftTile: FC<NftTileProps> = ({
         }}
       />
       <NftTileDetails
+        isInCarousel={isInCarousel}
         videoHref={thumbnail?.videoHref as string}
         hovered={hovered}
         owner={owner}

+ 190 - 155
packages/atlas/src/components/_nft/NftTile/NftTileDetails.tsx

@@ -1,4 +1,4 @@
-import { FC, ReactElement, ReactNode, memo, useMemo, useState } from 'react'
+import { FC, ReactElement, ReactNode, memo, useEffect, useId, useMemo, useRef, useState } from 'react'
 import useResizeObserver from 'use-resize-observer'
 
 import { SvgActionMore, SvgActionNotForSale } from '@/assets/icons'
@@ -8,6 +8,8 @@ import { NumberFormat } from '@/components/NumberFormat'
 import { Text } from '@/components/Text'
 import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
 import { ContextMenu } from '@/components/_overlays/ContextMenu'
+import { PopoverImperativeHandle } from '@/components/_overlays/Popover'
+import { useMiscStore } from '@/providers/misc/store'
 import { cVar } from '@/styles'
 
 import {
@@ -30,6 +32,7 @@ export type Member = {
 
 export type NftTileDetailsProps = {
   loading?: boolean
+  isInCarousel?: boolean
   owner?: Member
   creator?: Member
   role?: 'owner' | 'viewer'
@@ -48,169 +51,201 @@ type TileSize = 'small' | 'medium' | 'big' | 'bigSmall'
 
 const SMALL_SIZE_WIDTH = 288
 
-export const NftTileDetails: FC<NftTileDetailsProps> = ({
-  loading,
-  creator,
-  owner,
-  nftStatus,
-  startingPrice,
-  buyNowPrice,
-  topBid,
-  title,
-  hovered,
-  videoHref,
-  interactable = true,
-  contextMenuItems,
-}) => {
-  const [contentHovered, setContentHovered] = useState(false)
-  const toggleContentHover = () => setContentHovered((prevState) => !prevState)
-  const [tileSize, setTileSize] = useState<TileSize>()
-  const { ref: contentRef } = useResizeObserver<HTMLAnchorElement>({
-    box: 'border-box',
-    onResize: (size) => {
-      const { width } = size
-      if (width) {
-        if (tileSize !== 'small' && width < SMALL_SIZE_WIDTH) {
-          setTileSize('small')
-        }
-        if (tileSize !== 'medium' && width >= SMALL_SIZE_WIDTH) {
-          setTileSize('medium')
+export const NftTileDetails: FC<NftTileDetailsProps> = memo(
+  ({
+    loading,
+    isInCarousel,
+    creator,
+    owner,
+    nftStatus,
+    startingPrice,
+    buyNowPrice,
+    topBid,
+    title,
+    hovered,
+    videoHref,
+    interactable = true,
+    contextMenuItems,
+  }) => {
+    const [contentHovered, setContentHovered] = useState(false)
+    const setOpenedContextMenuId = useMiscStore((state) => state.actions.setOpenedContextMenuId)
+    const openedContexMenuId = useMiscStore((state) => state.openedContexMenuId)
+    const toggleContentHover = () => setContentHovered((prevState) => !prevState)
+    const [tileSize, setTileSize] = useState<TileSize>()
+    const { ref: contentRef } = useResizeObserver<HTMLAnchorElement>({
+      box: 'border-box',
+      onResize: (size) => {
+        const { width } = size
+        if (width) {
+          if (tileSize !== 'small' && width < SMALL_SIZE_WIDTH) {
+            setTileSize('small')
+          }
+          if (tileSize !== 'medium' && width >= SMALL_SIZE_WIDTH) {
+            setTileSize('medium')
+          }
         }
+      },
+    })
+    const id = useId()
+    const ref = useRef<HTMLButtonElement>(null)
+    const contextMenuInstanceRef = useRef<PopoverImperativeHandle>(null)
+
+    // This useEffect is called only inside carousel and it's a workaround fix for https://github.com/Joystream/atlas/issues/4239
+    // We need manually remove all popovers, because tippy is not working well with swiper carousel
+    useEffect(() => {
+      if (!openedContexMenuId || !isInCarousel) {
+        return
       }
-    },
-  })
+      if (openedContexMenuId !== id) {
+        contextMenuInstanceRef.current?.hide()
+      }
+    }, [id, isInCarousel, openedContexMenuId])
 
-  const getDetails = useMemo(() => {
-    if (loading) {
-      return (
-        <CaptionSkeletonWrapper>
-          <SkeletonLoader width="17%" height={tileSize === 'medium' ? 20 : 16} bottomSpace={4} />
-          <SkeletonLoader width="28%" height={tileSize === 'medium' ? 24 : 20} />
-        </CaptionSkeletonWrapper>
-      )
-    }
-    switch (nftStatus) {
-      case 'idle':
+    const getDetails = useMemo(() => {
+      if (loading) {
         return (
-          <DetailsContent
-            tileSize={tileSize}
-            caption="Status"
-            content="Not for sale"
-            icon={<SvgActionNotForSale />}
-            secondary
-          />
+          <CaptionSkeletonWrapper>
+            <SkeletonLoader width="17%" height={tileSize === 'medium' ? 20 : 16} bottomSpace={4} />
+            <SkeletonLoader width="28%" height={tileSize === 'medium' ? 24 : 20} />
+          </CaptionSkeletonWrapper>
         )
-      case 'buy-now':
-        return (
-          <DetailsContent
-            tileSize={tileSize}
-            caption="Buy now"
-            content={buyNowPrice ?? 0}
-            icon={<JoyTokenIcon size={16} variant="regular" />}
+      }
+      switch (nftStatus) {
+        case 'idle':
+          return (
+            <DetailsContent
+              tileSize={tileSize}
+              caption="Status"
+              content="Not for sale"
+              icon={<SvgActionNotForSale />}
+              secondary
+            />
+          )
+        case 'buy-now':
+          return (
+            <DetailsContent
+              tileSize={tileSize}
+              caption="Buy now"
+              content={buyNowPrice ?? 0}
+              icon={<JoyTokenIcon size={16} variant="regular" />}
+            />
+          )
+        case 'auction':
+          return (
+            <>
+              {topBid ? (
+                <DetailsContent
+                  tileSize={tileSize}
+                  caption="Top bid"
+                  content={topBid}
+                  icon={<JoyTokenIcon size={16} variant="regular" />}
+                />
+              ) : (
+                <DetailsContent
+                  tileSize={tileSize}
+                  caption="Min bid"
+                  content={startingPrice ?? 0}
+                  icon={<JoyTokenIcon size={16} variant="regular" />}
+                />
+              )}
+              {!!buyNowPrice && (
+                <DetailsContent
+                  tileSize={tileSize}
+                  caption="Buy now"
+                  content={buyNowPrice}
+                  icon={<JoyTokenIcon size={16} variant="regular" />}
+                />
+              )}
+            </>
+          )
+      }
+    }, [loading, nftStatus, tileSize, buyNowPrice, topBid, startingPrice])
+
+    const avatars = useMemo(
+      () => [
+        {
+          url: creator?.assetUrl,
+          tooltipText: `Creator: ${creator?.name}`,
+          onClick: creator?.onClick,
+          loading: creator?.loading,
+        },
+        ...(owner
+          ? [
+              {
+                url: owner?.assetUrl,
+                tooltipText: `Owner: ${owner?.name}`,
+                onClick: owner?.onClick,
+                loading: owner.loading,
+              },
+            ]
+          : []),
+      ],
+      [creator?.assetUrl, creator?.loading, creator?.name, creator?.onClick, owner]
+    )
+
+    return (
+      <Content
+        to={videoHref || ''}
+        ref={contentRef}
+        loading={loading}
+        onMouseEnter={toggleContentHover}
+        onMouseLeave={toggleContentHover}
+        tileSize={tileSize}
+        shouldHover={(contentHovered || hovered) && interactable}
+      >
+        <Header>
+          <StyledAvatarGroup
+            avatarStrokeColor={
+              (contentHovered || hovered) && interactable
+                ? cVar('colorBackground', true)
+                : cVar('colorBackgroundMuted', true)
+            }
+            loading={loading}
+            avatars={avatars}
           />
-        )
-      case 'auction':
-        return (
-          <>
-            {topBid ? (
-              <DetailsContent
-                tileSize={tileSize}
-                caption="Top bid"
-                content={topBid}
-                icon={<JoyTokenIcon size={16} variant="regular" />}
+          {contextMenuItems && (
+            <div>
+              <KebabMenuButtonIcon
+                ref={ref}
+                icon={<SvgActionMore />}
+                variant="tertiary"
+                size="small"
+                isActive={!loading}
+                onClick={(e) => {
+                  e.stopPropagation()
+                  e.preventDefault()
+                }}
               />
-            ) : (
-              <DetailsContent
-                tileSize={tileSize}
-                caption="Min bid"
-                content={startingPrice ?? 0}
-                icon={<JoyTokenIcon size={16} variant="regular" />}
+              <ContextMenu
+                ref={contextMenuInstanceRef}
+                appendTo={document.body}
+                placement="bottom-end"
+                flipEnabled={false}
+                disabled={loading}
+                onShow={() => {
+                  setOpenedContextMenuId(id)
+                }}
+                items={contextMenuItems}
+                trigger={null}
+                triggerTarget={ref.current}
               />
-            )}
-            {!!buyNowPrice && (
-              <DetailsContent
-                tileSize={tileSize}
-                caption="Buy now"
-                content={buyNowPrice}
-                icon={<JoyTokenIcon size={16} variant="regular" />}
-              />
-            )}
-          </>
-        )
-    }
-  }, [loading, nftStatus, tileSize, buyNowPrice, topBid, startingPrice])
-
-  const avatars = useMemo(
-    () => [
-      {
-        url: creator?.assetUrl,
-        tooltipText: `Creator: ${creator?.name}`,
-        onClick: creator?.onClick,
-        loading: creator?.loading,
-      },
-      ...(owner
-        ? [
-            {
-              url: owner?.assetUrl,
-              tooltipText: `Owner: ${owner?.name}`,
-              onClick: owner?.onClick,
-              loading: owner.loading,
-            },
-          ]
-        : []),
-    ],
-    [creator?.assetUrl, creator?.loading, creator?.name, creator?.onClick, owner]
-  )
-
-  return (
-    <Content
-      to={videoHref || ''}
-      ref={contentRef}
-      loading={loading}
-      onMouseEnter={toggleContentHover}
-      onMouseLeave={toggleContentHover}
-      tileSize={tileSize}
-      shouldHover={(contentHovered || hovered) && interactable}
-    >
-      <Header>
-        <StyledAvatarGroup
-          avatarStrokeColor={
-            (contentHovered || hovered) && interactable
-              ? cVar('colorBackground', true)
-              : cVar('colorBackgroundMuted', true)
-          }
-          loading={loading}
-          avatars={avatars}
-        />
-        {contextMenuItems && (
-          <div
-            onClick={(e) => {
-              e.stopPropagation()
-              e.preventDefault()
-            }}
-          >
-            <ContextMenu
-              placement="bottom-end"
-              disabled={loading}
-              items={contextMenuItems}
-              trigger={
-                <KebabMenuButtonIcon icon={<SvgActionMore />} variant="tertiary" size="small" isActive={!loading} />
-              }
-            />
-          </div>
+            </div>
+          )}
+        </Header>
+        {loading ? (
+          <SkeletonLoader width="55.6%" height={24} />
+        ) : (
+          <Title as="h3" variant={tileSize === 'medium' ? 'h400' : 'h300'}>
+            {title}
+          </Title>
         )}
-      </Header>
-      {loading ? (
-        <SkeletonLoader width="55.6%" height={24} />
-      ) : (
-        <Title as="h3" variant={tileSize === 'medium' ? 'h400' : 'h300'}>
-          {title}
-        </Title>
-      )}
-      <Details>{getDetails}</Details>
-    </Content>
-  )
-}
+        <Details>{getDetails}</Details>
+      </Content>
+    )
+  }
+)
+
+NftTileDetails.displayName = 'NftTileDetails'
 
 type DetailsContentProps = {
   caption: string

+ 3 - 1
packages/atlas/src/components/_nft/NftTileViewer/NftTileViewer.tsx

@@ -13,9 +13,10 @@ import { NftTile, NftTileProps } from '../NftTile'
 
 type NftTileViewerProps = {
   nftId?: string
+  isInCarousel?: boolean
 }
 
-export const NftTileViewer: FC<NftTileViewerProps> = ({ nftId }) => {
+export const NftTileViewer: FC<NftTileViewerProps> = ({ nftId, isInCarousel }) => {
   const { nftStatus, nft, loading } = useNft(nftId || '')
   const navigate = useNavigate()
   const thumbnailUrl = nft?.video.thumbnailPhoto?.resolvedUrl
@@ -100,6 +101,7 @@ export const NftTileViewer: FC<NftTileViewerProps> = ({ nftId }) => {
   return (
     <NftTile
       {...nftCommonProps}
+      isInCarousel={isInCarousel}
       timerLoading={timerLoading}
       buyNowPrice={
         nftStatus?.status === 'auction' || nftStatus?.status === 'buy-now' ? nftStatus.buyNowPrice : undefined

+ 23 - 25
packages/atlas/src/components/_overlays/ContextMenu/ContextMenu.tsx

@@ -1,5 +1,6 @@
 import styled from '@emotion/styled'
-import { FC, useRef } from 'react'
+import { forwardRef, useRef } from 'react'
+import { mergeRefs } from 'react-merge-refs'
 
 import { List } from '@/components/List'
 import { ListItemProps, ListItemSizes } from '@/components/ListItem'
@@ -12,31 +13,28 @@ export type ContextMenuProps = {
   size?: ListItemSizes
 } & Omit<PopoverProps, 'content' | 'instanceRef'>
 
-export const ContextMenu: FC<ContextMenuProps> = ({
-  children,
-  items,
-  scrollable = false,
-  size = 'medium',
-  ...rest
-}) => {
-  const contextMenuInstanceRef = useRef<PopoverImperativeHandle>(null)
-  return (
-    <Popover hideOnClick ref={contextMenuInstanceRef} {...rest}>
-      <StyledList
-        scrollable={scrollable}
-        size={size}
-        items={items.map((item) => ({
-          ...item,
-          onClick: (e) => {
-            item.onClick?.(e)
-            contextMenuInstanceRef.current?.hide()
-          },
-        }))}
-      />
-    </Popover>
-  )
-}
+export const ContextMenu = forwardRef<PopoverImperativeHandle, ContextMenuProps>(
+  ({ children, items, scrollable = false, size = 'medium', ...rest }, ref) => {
+    const contextMenuInstanceRef = useRef<PopoverImperativeHandle>(null)
+    return (
+      <Popover hideOnClick ref={mergeRefs([contextMenuInstanceRef, ref])} {...rest}>
+        <StyledList
+          scrollable={scrollable}
+          size={size}
+          items={items.map((item) => ({
+            ...item,
+            onClick: (e) => {
+              item.onClick?.(e)
+              contextMenuInstanceRef.current?.hide()
+            },
+          }))}
+        />
+      </Popover>
+    )
+  }
+)
 
+ContextMenu.displayName = 'ContextMenu'
 export const StyledList = styled(List)`
   width: 192px;
 `

+ 8 - 5
packages/atlas/src/components/_overlays/Popover/Popover.tsx

@@ -21,7 +21,7 @@ export type PopoverProps = PropsWithChildren<{
   className?: string
   appendTo?: Element | 'parent' | ((ref: Element) => Element) | undefined
   onHide?: () => void
-  onShow?: () => void
+  onShow?: (instance?: Instance) => void
   disabled?: boolean
   flipEnabled?: boolean
   animation?: boolean
@@ -103,7 +103,7 @@ const _Popover: ForwardRefRenderFunction<PopoverImperativeHandle | undefined, Po
       onTrigger={onTrigger}
       onShow={(instance) => {
         onTrigger(instance)
-        onShow?.()
+        onShow?.(instance)
       }}
       onHide={(instance) => {
         const box = instance.popper?.firstElementChild
@@ -140,13 +140,16 @@ const _Popover: ForwardRefRenderFunction<PopoverImperativeHandle | undefined, Po
       placement={placement}
       offset={offset}
     >
-      <TriggerContainer tabIndex={1}>{trigger}</TriggerContainer>
+      <TriggerContainer tabIndex={1} isTrigger={!!trigger}>
+        {trigger}
+      </TriggerContainer>
     </Tippy>
   )
 }
 
-const TriggerContainer = styled.div`
-  height: max-content;
+const TriggerContainer = styled.div<{ isTrigger: boolean }>`
+  /* if we use triggerElement, don't set height */
+  height: ${({ isTrigger }) => (isTrigger ? 'max-content' : 'unset')};
 `
 
 const ContentContainer = styled.div<{ animation?: boolean }>`

+ 5 - 1
packages/atlas/src/config/routes.ts

@@ -29,7 +29,8 @@ export const relativeRoutes = {
     channels: () => 'channels',
     video: (id = ':id', query?: { [QUERY_PARAMS.COMMENT_ID]?: string }) => withQueryParameters(`video/${id}`, query),
     editMembership: () => 'member/edit',
-    member: (handle = ':handle') => `member/${handle}`,
+    member: (handle = ':handle', query?: { [QUERY_PARAMS.TAB]?: MemberTabs }) =>
+      withQueryParameters(`member/${handle}`, query),
     notifications: () => 'notifications',
     marketplace: () => 'marketplace',
     ypp: (query?: { [QUERY_PARAMS.REFERRER_ID]?: string }) => withQueryParameters('ypp', query),
@@ -81,8 +82,11 @@ export const absoluteRoutes = Object.entries(BASE_PATHS).reduce((absoluteRoutesA
   return absoluteRoutesAcc
 }, {} as typeof relativeRoutes)
 
+export type MemberTabs = 'NFTs owned' | 'Activity' | 'About'
+
 export const QUERY_PARAMS = {
   SEARCH: 'query',
   COMMENT_ID: 'commentId',
   REFERRER_ID: 'referrerId',
+  TAB: 'tab',
 } as const

+ 4 - 4
packages/atlas/src/providers/assets/assets.hooks.ts

@@ -6,7 +6,7 @@ import { createChannelBagId } from '@/utils/asset'
 
 export const useChannelsStorageBucketsCount = (channelId: ChannelId | null): number => {
   const [bucketsCount, setBucketsCount] = useState<number | null>(null)
-  const { getAllStorageOperatorsForBag } = useStorageOperators()
+  const { getAvailableBucketsCountForBag } = useStorageOperators()
 
   // update bucketsCount whenever channel changes
   useEffect(() => {
@@ -17,10 +17,10 @@ export const useChannelsStorageBucketsCount = (channelId: ChannelId | null): num
 
     const bagId = createChannelBagId(channelId)
 
-    getAllStorageOperatorsForBag(bagId, true).then((operators) => {
-      setBucketsCount(operators?.length ?? null)
+    getAvailableBucketsCountForBag(bagId).then((length) => {
+      setBucketsCount(length ?? null)
     })
-  }, [channelId, getAllStorageOperatorsForBag])
+  }, [channelId, getAvailableBucketsCountForBag])
 
   return bucketsCount ?? 0
 }

+ 29 - 1
packages/atlas/src/providers/assets/assets.provider.tsx

@@ -19,6 +19,9 @@ import { useMutation } from 'react-query'
 import { useLocation } from 'react-router'
 
 import {
+  GetAvailableStorageBucketsForBagDocument,
+  GetAvailableStorageBucketsForBagQuery,
+  GetAvailableStorageBucketsForBagQueryVariables,
   GetStorageBucketsWithBagsDocument,
   GetStorageBucketsWithBagsQuery,
   GetStorageBucketsWithBagsQueryVariables,
@@ -171,6 +174,7 @@ export const useOperatorsContext = () => {
 }
 
 export const useStorageOperators = () => {
+  const client = useApolloClient()
   const { storageOperatorsMappingPromiseRef, failedStorageOperatorIds, setFailedStorageOperatorIds } =
     useOperatorsContext()
 
@@ -196,6 +200,25 @@ export const useStorageOperators = () => {
     [failedStorageOperatorIds, storageOperatorsMappingPromiseRef]
   )
 
+  const getAvailableBucketsCountForBag = useCallback(
+    async (storageBagId: string) => {
+      const getStorageBucketsForBagPromise = client.query<
+        GetAvailableStorageBucketsForBagQuery,
+        GetAvailableStorageBucketsForBagQueryVariables
+      >({
+        query: GetAvailableStorageBucketsForBagDocument,
+        fetchPolicy: 'network-only',
+        variables: { where: { bags_some: { id_contains: storageBagId } } },
+      })
+
+      const availableBucketsResult = await getStorageBucketsForBagPromise
+      const storageBuckets = availableBucketsResult.data.storageBuckets
+
+      return storageBuckets.length
+    },
+    [client]
+  )
+
   const getClosestStorageOperatorForBag = useCallback(
     async (storageBagId: string) => {
       const workingStorageOperators = await getAllStorageOperatorsForBag(storageBagId)
@@ -222,7 +245,12 @@ export const useStorageOperators = () => {
     [setFailedStorageOperatorIds]
   )
 
-  return { getAllStorageOperatorsForBag, getClosestStorageOperatorForBag, markStorageOperatorFailed }
+  return {
+    getAllStorageOperatorsForBag,
+    getClosestStorageOperatorForBag,
+    markStorageOperatorFailed,
+    getAvailableBucketsCountForBag,
+  }
 }
 
 const removeBagOperatorsDuplicates = (mapping: BagOperatorsMapping): BagOperatorsMapping => {

+ 22 - 0
packages/atlas/src/providers/misc/store.ts

@@ -0,0 +1,22 @@
+import { createStore } from '@/utils/store'
+
+export type MiscStoreState = {
+  openedContexMenuId?: string
+}
+
+type MiscStoreActions = {
+  setOpenedContextMenuId: (id: string) => void
+}
+
+export const useMiscStore = createStore<MiscStoreState, MiscStoreActions>({
+  state: {
+    openedContexMenuId: '',
+  },
+  actionsFactory: (set) => ({
+    setOpenedContextMenuId: (id) => {
+      set((state) => {
+        state.openedContexMenuId = id
+      })
+    },
+  }),
+})

+ 3 - 4
packages/atlas/src/providers/notifications/notifications.hooks.ts

@@ -18,7 +18,7 @@ export const useNotifications = (
   opts?: QueryHookOptions<GetNotificationsConnectionQuery, GetNotificationsConnectionQueryVariables>
 ) => {
   const { memberId } = useUser()
-  const { notifications: rawNotifications, ...rest } = useRawNotifications(memberId, opts)
+  const { notifications: rawNotifications, ...rest } = useRawNotifications('111', opts)
   const {
     readNotificationsIdsMap,
     lastSeenNotificationBlock,
@@ -49,12 +49,11 @@ const getVideoDataFromEvent = ({
 }: GetNotificationsConnectionQuery['notificationsConnection']['edges'][number]) => {
   switch (notification.event.data.__typename) {
     case 'AuctionBidMadeEventData':
-      return notification.event.data.bid.auction.nft.video
+      return notification.event.data.bid.nft.video
     case 'BidMadeCompletingAuctionEventData':
-      return notification.event.data.winningBid.nft.video
     case 'EnglishAuctionSettledEventData':
     case 'OpenAuctionBidAcceptedEventData':
-      return notification.event.data.winningBid.auction.nft.video
+      return notification.event.data.winningBid.nft.video
     case 'CommentCreatedEventData':
       return notification.event.data.comment.video
     case 'NftBoughtEventData':

+ 7 - 10
packages/atlas/src/utils/logs/sentry.ts

@@ -1,5 +1,5 @@
 import * as Sentry from '@sentry/react'
-import { Severity, SeverityLevel } from '@sentry/react'
+import { Replay, Severity, SeverityLevel } from '@sentry/react'
 
 import { ConsoleLogger } from './console'
 
@@ -17,8 +17,9 @@ class SentryError extends Error {
 }
 
 class _SentryLogger {
-  private initialized = false
   private user?: Record<string, unknown>
+  public initialized = false
+  public replay?: Replay
 
   initialize(dsn: string | undefined | null) {
     if (!dsn) return
@@ -29,15 +30,11 @@ class _SentryLogger {
         'ResizeObserver loop limit exceeded',
         'ResizeObserver loop completed with undelivered notifications',
       ],
-      // This sets the sample rate to be 10%. You may want this to be 100% while
-      // in development and sample at a lower rate in production
-      replaysSessionSampleRate: 0.1,
-      // If the entire session is not sampled, use the below sample rate to sample
-      // sessions when an error occurs.
-      replaysOnErrorSampleRate: 1.0,
-
-      integrations: [new Sentry.Replay()],
+      // This sets the sample rate to be 0%, so we'll only use manually recorded replays
+      replaysSessionSampleRate: 0,
+      replaysOnErrorSampleRate: 0,
     })
+    this.replay = new Sentry.Replay({ sessionSampleRate: 0, errorSampleRate: 0 })
     this.initialized = true
   }
 

+ 1 - 1
packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.tsx

@@ -147,7 +147,7 @@ export const FeaturedNftsSection: FC = () => {
             }}
             contentProps={{
               type: 'carousel',
-              children: items.map((nft, idx) => <NftTileViewer nftId={nft.id} key={idx} />),
+              children: items.map((nft, idx) => <NftTileViewer isInCarousel nftId={nft.id} key={idx} />),
               spaceBetween: mdMatch ? 24 : 16,
               breakpoints: responsive,
             }}

+ 4 - 4
packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx

@@ -36,7 +36,7 @@ export const ActivityItem: FC<ActivityItemProps> = ({
   type,
   title,
   description,
-  thumbnailUri: thumnailUri,
+  thumbnailUri,
   thumbnailLoading,
   loading,
   onItemClick,
@@ -47,11 +47,11 @@ export const ActivityItem: FC<ActivityItemProps> = ({
 
   useEffect(() => {
     const validateImg = async () => {
-      const res = await imageUrlValidation(thumnailUri)
+      const res = await imageUrlValidation(thumbnailUri)
       setThumbnailLoaded(res)
     }
     validateImg()
-  }, [thumnailUri])
+  }, [thumbnailUri])
 
   const getTitleTextVariant = () => {
     if (lgMatch) {
@@ -66,7 +66,7 @@ export const ActivityItem: FC<ActivityItemProps> = ({
   const isImageLoading = loading || thumbnailLoading || !thumbnailLoaded
   return (
     <ActivityItemContainer loading={loading} onClick={onItemClick}>
-      {isImageLoading ? <ThumbnailSkeletonLoader /> : <Thumbnail src={thumnailUri} />}
+      {isImageLoading ? <ThumbnailSkeletonLoader /> : <Thumbnail src={thumbnailUri} />}
       <TitleAndDescriptionContainer>
         {loading ? (
           <TitleSkeletonLoader />

+ 8 - 12
packages/atlas/src/views/viewer/MemberView/MemberActivity.hooks.ts

@@ -2,7 +2,7 @@ import { QueryHookOptions } from '@apollo/client'
 import BN from 'bn.js'
 import { useMemo } from 'react'
 
-import { useRawActivities } from '@/api/hooks/notifications'
+import { useActivitiesCount, useRawActivities } from '@/api/hooks/notifications'
 import { NftActivityOrderByInput } from '@/api/queries/__generated__/baseTypes.generated'
 import {
   BasicMembershipFieldsFragment,
@@ -74,11 +74,11 @@ const getVideoDataFromEvent = (
   switch (nftActivity.event.data.__typename) {
     case 'AuctionBidMadeEventData':
     case 'AuctionBidCanceledEventData':
-      return nftActivity.event.data.bid.auction.nft.video
-    case 'EnglishAuctionSettledEventData':
+      return nftActivity.event.data.bid.nft.video
     case 'BidMadeCompletingAuctionEventData':
+    case 'EnglishAuctionSettledEventData':
     case 'OpenAuctionBidAcceptedEventData':
-      return nftActivity.event.data.winningBid.auction.nft.video
+      return nftActivity.event.data.winningBid.nft.video
     case 'NftBoughtEventData':
     case 'NftSellOrderMadeEventData':
     case 'BuyNowCanceledEventData':
@@ -245,14 +245,10 @@ export const useActivities = (
   sort?: NftActivityOrderByInput,
   opts?: QueryHookOptions<GetNftActivitiesQuery, GetNftActivitiesQueryVariables>
 ) => {
-  const {
-    activities: rawActivities,
-    nftsBiddedTotalCount,
-    nftsIssuedTotalCount,
-    nftsSoldTotalCount,
-    nftsBoughtTotalCount,
-    ...rest
-  } = useRawActivities(memberId, sort, opts)
+  const { activities: rawActivities, ...rest } = useRawActivities(memberId, sort, opts)
+
+  const { nftsBiddedTotalCount, nftsBoughtTotalCount, nftsIssuedTotalCount, nftsSoldTotalCount } =
+    useActivitiesCount(memberId)
   const parsedActivities = rawActivities && rawActivities.map((a) => parseActivities(a, memberId))
   const activities = parsedActivities ? parsedActivities.filter((a): a is ActivitiesRecord => !!a) : undefined
 

+ 68 - 9
packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx

@@ -39,14 +39,26 @@ const getDescription = (activity: ActivitiesRecord) => {
     case 'Bid':
       return (
         <>
-          {fromHandle} placed a bid for{' '}
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          placed a bid for{' '}
           <NumberFormat as="span" color="inherit" format="short" value={activity.bidAmount} withToken />
         </>
       )
     case 'Sale':
       return (
         <>
-          {fromHandle} sold NFT to{' '}
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          sold NFT to{' '}
           <StyledLink to={absoluteRoutes.viewer.member(activity.to?.handle)} onClick={(e) => e.stopPropagation()}>
             {activity.to?.handle}
           </StyledLink>{' '}
@@ -60,13 +72,25 @@ const getDescription = (activity: ActivitiesRecord) => {
             {activity.to?.handle}{' '}
           </StyledLink>{' '}
           purchased NFT for <NumberFormat as="span" color="inherit" format="short" value={activity.price} withToken />{' '}
-          from {fromHandle}
+          from{' '}
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
         </>
       )
     case 'Listing':
       return (
         <>
-          {fromHandle} listed NFT{' '}
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          listed NFT{' '}
           {activity.typeName === 'NftSellOrderMadeEventData' && activity.price && (
             <>
               for <NumberFormat as="span" color="inherit" format="short" value={activity.price} withToken />
@@ -75,16 +99,51 @@ const getDescription = (activity: ActivitiesRecord) => {
         </>
       )
     case 'Removal':
-      return <>{fromHandle} removed NFT from sale</>
+      return (
+        <>
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          removed NFT from sale
+        </>
+      )
     case 'Mint':
-      return <>{fromHandle} minted new NFT</>
+      return (
+        <>
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          minted new NFT
+        </>
+      )
     case 'Withdrawal':
-      return <>{fromHandle} withdrew a bid</>
+      return (
+        <>
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          withdrew a bid
+        </>
+      )
     case 'Price change':
       return (
         <>
-          {fromHandle} changed price to{' '}
-          <NumberFormat as="span" color="inherit" format="short" value={activity.price} withToken />
+          <StyledLink
+            to={absoluteRoutes.viewer.member(fromHandle, { tab: 'NFTs owned' })}
+            onClick={(e) => e.stopPropagation()}
+          >
+            {fromHandle}
+          </StyledLink>{' '}
+          changed price to <NumberFormat as="span" color="inherit" format="short" value={activity.price} withToken />
         </>
       )
   }

+ 7 - 6
packages/atlas/src/views/viewer/MemberView/MemberView.tsx

@@ -1,5 +1,5 @@
 import { FC, useEffect, useMemo, useRef, useState } from 'react'
-import { useParams } from 'react-router'
+import { useNavigate, useParams } from 'react-router'
 import { useSearchParams } from 'react-router-dom'
 
 import { useMemberships } from '@/api/hooks/membership'
@@ -12,7 +12,7 @@ import { ViewErrorFallback } from '@/components/ViewErrorFallback'
 import { ViewWrapper } from '@/components/ViewWrapper'
 import { Button } from '@/components/_buttons/Button'
 import { Select } from '@/components/_inputs/Select'
-import { absoluteRoutes } from '@/config/routes'
+import { MemberTabs, QUERY_PARAMS, absoluteRoutes } from '@/config/routes'
 import { NFT_SORT_ACTIVITY_OPTIONS, NFT_SORT_OPTIONS } from '@/config/sorting'
 import { useHeadTags } from '@/hooks/useHeadTags'
 import { getMemberAvatar } from '@/providers/assets/assets.helpers'
@@ -32,16 +32,17 @@ import {
   TabsWrapper,
 } from './MemberView.styles'
 
-const TABS = ['NFTs owned', 'Activity', 'About'] as const
+const TABS: MemberTabs[] = ['NFTs owned', 'Activity', 'About']
 
 export const MemberView: FC = () => {
   const [searchParams, setSearchParams] = useSearchParams()
-  const currentTabName = searchParams.get('tab') as typeof TABS[number] | null
+  const currentTabName = searchParams.get(QUERY_PARAMS.TAB) as MemberTabs | null
   const [sortBy, setSortBy] = useState<OwnedNftOrderByInput>(OwnedNftOrderByInput.CreatedAtDesc)
   const [sortByTimestamp, setSortByTimestamp] = useState<NftActivityOrderByInput>(
     NftActivityOrderByInput.EventTimestampDesc
   )
-  const [currentTab, setCurrentTab] = useState<typeof TABS[number] | null>(null)
+  const navigate = useNavigate()
+  const [currentTab, setCurrentTab] = useState<MemberTabs | null>(null)
   const [nftCount, setNftCount] = useState<number | undefined>()
   const { memberId, activeMembership } = useUser()
   const { handle } = useParams()
@@ -82,7 +83,7 @@ export const MemberView: FC = () => {
     }
   }
   const handleSetCurrentTab = async (tab: number) => {
-    setSearchParams({ 'tab': TABS[tab] }, { replace: true })
+    navigate(absoluteRoutes.viewer.member(handle, { tab: TABS[tab] }))
   }
 
   const mappedTabs = TABS.map((tab) => ({

+ 46 - 46
yarn.lock

@@ -4069,7 +4069,7 @@ __metadata:
     "@lottiefiles/react-lottie-player": ^3.5.0
     "@modyfi/vite-plugin-yaml": ^1.0.3
     "@rollup/plugin-babel": ^6.0.3
-    "@sentry/react": ^7.47.0
+    "@sentry/react": ^7.53.1
     "@storybook/addon-actions": 7.0.7
     "@storybook/addon-docs": 7.0.7
     "@storybook/addon-essentials": 7.0.7
@@ -5184,83 +5184,83 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@sentry-internal/tracing@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry-internal/tracing@npm:7.47.0"
+"@sentry-internal/tracing@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry-internal/tracing@npm:7.53.1"
   dependencies:
-    "@sentry/core": 7.47.0
-    "@sentry/types": 7.47.0
-    "@sentry/utils": 7.47.0
+    "@sentry/core": 7.53.1
+    "@sentry/types": 7.53.1
+    "@sentry/utils": 7.53.1
     tslib: ^1.9.3
-  checksum: 46cb6cdd5b64841acd7b38cdde156e15dcf2c225ccd36219ea5796b7ebad34a3c0db1f4c7545e1fec1a1e5897d4368003b1101845404bcd1c27a21c09784cbd3
+  checksum: 99fadf422e619faeea436a96b37088ba10850f8e77fca10d7fea6de0c3f1d60b0e8ec1da3b9230f4213fa25f044c99aba585562447ddca0a443d7cbd88c3b81d
   languageName: node
   linkType: hard
 
-"@sentry/browser@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/browser@npm:7.47.0"
+"@sentry/browser@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/browser@npm:7.53.1"
   dependencies:
-    "@sentry-internal/tracing": 7.47.0
-    "@sentry/core": 7.47.0
-    "@sentry/replay": 7.47.0
-    "@sentry/types": 7.47.0
-    "@sentry/utils": 7.47.0
+    "@sentry-internal/tracing": 7.53.1
+    "@sentry/core": 7.53.1
+    "@sentry/replay": 7.53.1
+    "@sentry/types": 7.53.1
+    "@sentry/utils": 7.53.1
     tslib: ^1.9.3
-  checksum: fdceac3a68195b4fcc58ab9d7907009cb73350646314100853ed56f4f2f6e00b2916c35037ea055c3d583accdf2356ed0690b59bf63038043b85e737a38382dc
+  checksum: 6250949dfbef169669d9e932515f0d965f449ef82be5fb6b813ab170944cef033c758857a3412aa1a1023d77622f88d8599df8e4b0ea126479f8713bd4aa1838
   languageName: node
   linkType: hard
 
-"@sentry/core@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/core@npm:7.47.0"
+"@sentry/core@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/core@npm:7.53.1"
   dependencies:
-    "@sentry/types": 7.47.0
-    "@sentry/utils": 7.47.0
+    "@sentry/types": 7.53.1
+    "@sentry/utils": 7.53.1
     tslib: ^1.9.3
-  checksum: 1cfd41020e5707c7e142dbb7c99118a264bcc5fa64fc06f1e51d8c6166dea0a50fd5896ce5287a4805d088257dc3757cc53ab4f5092166cd759ffeeea1cd8d2e
+  checksum: 5d42bc30a59cb4459eb99441b631e3f9daec5b2e98c24377aae0329127f8c474a9c32353a96e3f4765e3fb18370756e85f6b873cd26bd591e0040917b7fa9cf4
   languageName: node
   linkType: hard
 
-"@sentry/react@npm:^7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/react@npm:7.47.0"
+"@sentry/react@npm:^7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/react@npm:7.53.1"
   dependencies:
-    "@sentry/browser": 7.47.0
-    "@sentry/types": 7.47.0
-    "@sentry/utils": 7.47.0
+    "@sentry/browser": 7.53.1
+    "@sentry/types": 7.53.1
+    "@sentry/utils": 7.53.1
     hoist-non-react-statics: ^3.3.2
     tslib: ^1.9.3
   peerDependencies:
     react: 15.x || 16.x || 17.x || 18.x
-  checksum: 7e42c91c48cf84e36d20433a1b28bf03c801ebed67ccd18e40de279c3a800e8f0818b4af9b9f40471070636a0aa1f58a77610383908f780e0b98efe8eb6c6ebd
+  checksum: ee08c6a851cae421a134d62e217c192a961930254ea8775e1960420307f4ff08b659405bd883b12698a55085ad1070ac58e618f5799a7edc775368382e807e2d
   languageName: node
   linkType: hard
 
-"@sentry/replay@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/replay@npm:7.47.0"
+"@sentry/replay@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/replay@npm:7.53.1"
   dependencies:
-    "@sentry/core": 7.47.0
-    "@sentry/types": 7.47.0
-    "@sentry/utils": 7.47.0
-  checksum: e4f69a6b698bd38eda4bd16275987d69948ded1dc34253eb804e3b6d09be675fa3c0fcfb85fbfef02c2e863eb80b7148da18b0dd1a7dadaab4353a315733e931
+    "@sentry/core": 7.53.1
+    "@sentry/types": 7.53.1
+    "@sentry/utils": 7.53.1
+  checksum: c3757077a971183f5f9f87006449a110c0f951c76a777aa45cb2f057100ec5bc8fcf50e4c58e8117ddc0f44fdaada101384a45b2b5c427048b4ce57eb780711d
   languageName: node
   linkType: hard
 
-"@sentry/types@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/types@npm:7.47.0"
-  checksum: 42f1f58bebb2689d609ebc614a04462cb15f749d304dcadc2c93b851ecb88a20d59853b01f22b91f37c7cfb41ed7d0992843043ada7a7592ea1043191092e350
+"@sentry/types@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/types@npm:7.53.1"
+  checksum: 5f76d7d66d5df5d48f66a755e18ae133ea02a9405093b652eb4921ca62c3114343561a2d8067f2be6d9df8dd32d10e30d3ce58150f795600680e6230fb136681
   languageName: node
   linkType: hard
 
-"@sentry/utils@npm:7.47.0":
-  version: 7.47.0
-  resolution: "@sentry/utils@npm:7.47.0"
+"@sentry/utils@npm:7.53.1":
+  version: 7.53.1
+  resolution: "@sentry/utils@npm:7.53.1"
   dependencies:
-    "@sentry/types": 7.47.0
+    "@sentry/types": 7.53.1
     tslib: ^1.9.3
-  checksum: d37d6299ef28c7ce32b1af7644f59ab5a5fd7a8155e97b3758c87d11a4b2cb406d4e3d2b260afcd200dadef99582f92b08ef6b89543f11611d2e034b3940d7b3
+  checksum: d9556f161a0eed0e1bb279bac12b99492b6212d9b6cabebbcb2d0f196f99b96c319305ec347d8f06382e2dde5782d5ba20a00f762fbfd4e8699e9100ef921804
   languageName: node
   linkType: hard