diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d1e0e9cc..e371fc8bee 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 9ba346e41c..bb9eee18ab 100644 --- a/packages/atlas/atlas.config.yml +++ b/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 diff --git a/packages/atlas/package.json b/packages/atlas/package.json index 230bc448db..2c1f461d91 100644 --- a/packages/atlas/package.json +++ b/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", diff --git a/packages/atlas/src/MainLayout.tsx b/packages/atlas/src/MainLayout.tsx index 8659b9de92..86d975aacf 100644 --- a/packages/atlas/src/MainLayout.tsx +++ b/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(() => { diff --git a/packages/atlas/src/api/hooks/notifications.ts b/packages/atlas/src/api/hooks/notifications.ts index 617c711425..a629418941 100644 --- a/packages/atlas/src/api/hooks/notifications.ts +++ b/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 +) => { + 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, diff --git a/packages/atlas/src/api/queries/__generated__/notifications.generated.tsx b/packages/atlas/src/api/queries/__generated__/notifications.generated.tsx index eff9a3684a..54a0bd60af 100644 --- a/packages/atlas/src/api/queries/__generated__/notifications.generated.tsx +++ b/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 + 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 + 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 + 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 + 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 + 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 - 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 + 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 - 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 + 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 - 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 + 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 - 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 + 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 - 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 + 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 export type GetNftHistoryLazyQueryHookResult = ReturnType export type GetNftHistoryQueryResult = Apollo.QueryResult -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 +) { + const options = { ...defaultOptions, ...baseOptions } + return Apollo.useQuery( + GetNftActivitiesCountDocument, + options + ) +} +export function useGetNftActivitiesCountLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions +) { + const options = { ...defaultOptions, ...baseOptions } + return Apollo.useLazyQuery( + GetNftActivitiesCountDocument, + options + ) +} +export type GetNftActivitiesCountQueryHookResult = ReturnType +export type GetNftActivitiesCountLazyQueryHookResult = ReturnType +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 } } } diff --git a/packages/atlas/src/api/queries/__generated__/storage.generated.tsx b/packages/atlas/src/api/queries/__generated__/storage.generated.tsx index 04f5b6d493..4736641b78 100644 --- a/packages/atlas/src/api/queries/__generated__/storage.generated.tsx +++ b/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 +}> + +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( + GetAvailableStorageBucketsForBagDocument, + options + ) +} +export function useGetAvailableStorageBucketsForBagLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GetAvailableStorageBucketsForBagQuery, + GetAvailableStorageBucketsForBagQueryVariables + > +) { + const options = { ...defaultOptions, ...baseOptions } + return Apollo.useLazyQuery( + GetAvailableStorageBucketsForBagDocument, + options + ) +} +export type GetAvailableStorageBucketsForBagQueryHookResult = ReturnType< + typeof useGetAvailableStorageBucketsForBagQuery +> +export type GetAvailableStorageBucketsForBagLazyQueryHookResult = ReturnType< + typeof useGetAvailableStorageBucketsForBagLazyQuery +> +export type GetAvailableStorageBucketsForBagQueryResult = Apollo.QueryResult< + GetAvailableStorageBucketsForBagQuery, + GetAvailableStorageBucketsForBagQueryVariables +> diff --git a/packages/atlas/src/api/queries/notifications.graphql b/packages/atlas/src/api/queries/notifications.graphql index fe6cf16f3a..8d6410a17b 100644 --- a/packages/atlas/src/api/queries/notifications.graphql +++ b/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 } } } diff --git a/packages/atlas/src/api/queries/storage.graphql b/packages/atlas/src/api/queries/storage.graphql index cefca8ec67..1bf0f6352d 100644 --- a/packages/atlas/src/api/queries/storage.graphql +++ b/packages/atlas/src/api/queries/storage.graphql @@ -42,3 +42,9 @@ query GetBasicStorageBuckets { id } } + +query GetAvailableStorageBucketsForBag($where: StorageBucketWhereInput) { + storageBuckets(limit: 500, where: $where) { + id + } +} diff --git a/packages/atlas/src/components/_nft/NftTile/NftTile.tsx b/packages/atlas/src/components/_nft/NftTile/NftTile.tsx index 3e88e9ac09..c9c5644c63 100644 --- a/packages/atlas/src/components/_nft/NftTile/NftTile.tsx +++ b/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 = ({ status, + isInCarousel, thumbnail, loading, title, @@ -99,6 +101,7 @@ export const NftTile: FC = ({ }} /> = ({ - 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() - const { ref: contentRef } = useResizeObserver({ - 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 = 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() + const { ref: contentRef } = useResizeObserver({ + 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(null) + const contextMenuInstanceRef = useRef(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 ( - - - - - ) - } - switch (nftStatus) { - case 'idle': + const getDetails = useMemo(() => { + if (loading) { return ( - } - secondary - /> + + + + ) - case 'buy-now': - return ( - } + } + switch (nftStatus) { + case 'idle': + return ( + } + secondary + /> + ) + case 'buy-now': + return ( + } + /> + ) + case 'auction': + return ( + <> + {topBid ? ( + } + /> + ) : ( + } + /> + )} + {!!buyNowPrice && ( + } + /> + )} + + ) + } + }, [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 ( + +
+ - ) - case 'auction': - return ( - <> - {topBid ? ( - } + {contextMenuItems && ( +
+ } + variant="tertiary" + size="small" + isActive={!loading} + onClick={(e) => { + e.stopPropagation() + e.preventDefault() + }} /> - ) : ( - } + { + setOpenedContextMenuId(id) + }} + items={contextMenuItems} + trigger={null} + triggerTarget={ref.current} /> - )} - {!!buyNowPrice && ( - } - /> - )} - - ) - } - }, [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 ( - -
- - {contextMenuItems && ( -
{ - e.stopPropagation() - e.preventDefault() - }} - > - } variant="tertiary" size="small" isActive={!loading} /> - } - /> -
+
+ )} +
+ {loading ? ( + + ) : ( + + {title} + )} - - {loading ? ( - - ) : ( - - {title} - - )} -
{getDetails}
-
- ) -} +
{getDetails}
+ + ) + } +) + +NftTileDetails.displayName = 'NftTileDetails' type DetailsContentProps = { caption: string diff --git a/packages/atlas/src/components/_nft/NftTileViewer/NftTileViewer.tsx b/packages/atlas/src/components/_nft/NftTileViewer/NftTileViewer.tsx index 266416fabe..3b781b5c5f 100644 --- a/packages/atlas/src/components/_nft/NftTileViewer/NftTileViewer.tsx +++ b/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 = ({ nftId }) => { +export const NftTileViewer: FC = ({ 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 = ({ nftId }) => { return ( -export const ContextMenu: FC = ({ - children, - items, - scrollable = false, - size = 'medium', - ...rest -}) => { - const contextMenuInstanceRef = useRef(null) - return ( - - ({ - ...item, - onClick: (e) => { - item.onClick?.(e) - contextMenuInstanceRef.current?.hide() - }, - }))} - /> - - ) -} +export const ContextMenu = forwardRef( + ({ children, items, scrollable = false, size = 'medium', ...rest }, ref) => { + const contextMenuInstanceRef = useRef(null) + return ( + + ({ + ...item, + onClick: (e) => { + item.onClick?.(e) + contextMenuInstanceRef.current?.hide() + }, + }))} + /> + + ) + } +) +ContextMenu.displayName = 'ContextMenu' export const StyledList = styled(List)` width: 192px; ` diff --git a/packages/atlas/src/components/_overlays/Popover/Popover.tsx b/packages/atlas/src/components/_overlays/Popover/Popover.tsx index 9b5808fcc7..28dddf17c0 100644 --- a/packages/atlas/src/components/_overlays/Popover/Popover.tsx +++ b/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 { onTrigger(instance) - onShow?.() + onShow?.(instance) }} onHide={(instance) => { const box = instance.popper?.firstElementChild @@ -140,13 +140,16 @@ const _Popover: ForwardRefRenderFunction - {trigger} + + {trigger} + ) } -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 }>` diff --git a/packages/atlas/src/config/routes.ts b/packages/atlas/src/config/routes.ts index be4a177079..9a0fcf2824 100644 --- a/packages/atlas/src/config/routes.ts +++ b/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 diff --git a/packages/atlas/src/providers/assets/assets.hooks.ts b/packages/atlas/src/providers/assets/assets.hooks.ts index dc988e8d14..8861d5fae5 100644 --- a/packages/atlas/src/providers/assets/assets.hooks.ts +++ b/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(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 } diff --git a/packages/atlas/src/providers/assets/assets.provider.tsx b/packages/atlas/src/providers/assets/assets.provider.tsx index bd2310b6b7..4f175f7290 100644 --- a/packages/atlas/src/providers/assets/assets.provider.tsx +++ b/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 => { diff --git a/packages/atlas/src/providers/misc/store.ts b/packages/atlas/src/providers/misc/store.ts new file mode 100644 index 0000000000..6a1712283e --- /dev/null +++ b/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({ + state: { + openedContexMenuId: '', + }, + actionsFactory: (set) => ({ + setOpenedContextMenuId: (id) => { + set((state) => { + state.openedContexMenuId = id + }) + }, + }), +}) diff --git a/packages/atlas/src/providers/notifications/notifications.hooks.ts b/packages/atlas/src/providers/notifications/notifications.hooks.ts index 34fabcf737..de40746e0b 100644 --- a/packages/atlas/src/providers/notifications/notifications.hooks.ts +++ b/packages/atlas/src/providers/notifications/notifications.hooks.ts @@ -18,7 +18,7 @@ export const useNotifications = ( opts?: QueryHookOptions ) => { 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': diff --git a/packages/atlas/src/utils/logs/sentry.ts b/packages/atlas/src/utils/logs/sentry.ts index 0f6d1803fc..3106c364c4 100644 --- a/packages/atlas/src/utils/logs/sentry.ts +++ b/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 + 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 } diff --git a/packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.tsx b/packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.tsx index 374aba631c..fc89e98efb 100644 --- a/packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.tsx +++ b/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) => ), + children: items.map((nft, idx) => ), spaceBetween: mdMatch ? 24 : 16, breakpoints: responsive, }} diff --git a/packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx b/packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx index d89c580e4a..dd0dbcf401 100644 --- a/packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx +++ b/packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx @@ -36,7 +36,7 @@ export const ActivityItem: FC = ({ type, title, description, - thumbnailUri: thumnailUri, + thumbnailUri, thumbnailLoading, loading, onItemClick, @@ -47,11 +47,11 @@ export const ActivityItem: FC = ({ 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 = ({ const isImageLoading = loading || thumbnailLoading || !thumbnailLoaded return ( - {isImageLoading ? : } + {isImageLoading ? : } {loading ? ( diff --git a/packages/atlas/src/views/viewer/MemberView/MemberActivity.hooks.ts b/packages/atlas/src/views/viewer/MemberView/MemberActivity.hooks.ts index 7232415110..037859bcb4 100644 --- a/packages/atlas/src/views/viewer/MemberView/MemberActivity.hooks.ts +++ b/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 ) => { - 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 diff --git a/packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx b/packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx index 0eaea426a9..4d34679603 100644 --- a/packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx +++ b/packages/atlas/src/views/viewer/MemberView/MemberActivity.tsx @@ -39,14 +39,26 @@ const getDescription = (activity: ActivitiesRecord) => { case 'Bid': return ( <> - {fromHandle} placed a bid for{' '} + e.stopPropagation()} + > + {fromHandle} + {' '} + placed a bid for{' '} ) case 'Sale': return ( <> - {fromHandle} sold NFT to{' '} + e.stopPropagation()} + > + {fromHandle} + {' '} + sold NFT to{' '} e.stopPropagation()}> {activity.to?.handle} {' '} @@ -60,13 +72,25 @@ const getDescription = (activity: ActivitiesRecord) => { {activity.to?.handle}{' '} {' '} purchased NFT for {' '} - from {fromHandle} + from{' '} + e.stopPropagation()} + > + {fromHandle} + {' '} ) case 'Listing': return ( <> - {fromHandle} listed NFT{' '} + e.stopPropagation()} + > + {fromHandle} + {' '} + listed NFT{' '} {activity.typeName === 'NftSellOrderMadeEventData' && activity.price && ( <> for @@ -75,16 +99,51 @@ const getDescription = (activity: ActivitiesRecord) => { ) case 'Removal': - return <>{fromHandle} removed NFT from sale + return ( + <> + e.stopPropagation()} + > + {fromHandle} + {' '} + removed NFT from sale + + ) case 'Mint': - return <>{fromHandle} minted new NFT + return ( + <> + e.stopPropagation()} + > + {fromHandle} + {' '} + minted new NFT + + ) case 'Withdrawal': - return <>{fromHandle} withdrew a bid + return ( + <> + e.stopPropagation()} + > + {fromHandle} + {' '} + withdrew a bid + + ) case 'Price change': return ( <> - {fromHandle} changed price to{' '} - + e.stopPropagation()} + > + {fromHandle} + {' '} + changed price to ) } diff --git a/packages/atlas/src/views/viewer/MemberView/MemberView.tsx b/packages/atlas/src/views/viewer/MemberView/MemberView.tsx index 6449780ed7..16ebcf4bb4 100644 --- a/packages/atlas/src/views/viewer/MemberView/MemberView.tsx +++ b/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.CreatedAtDesc) const [sortByTimestamp, setSortByTimestamp] = useState( NftActivityOrderByInput.EventTimestampDesc ) - const [currentTab, setCurrentTab] = useState(null) + const navigate = useNavigate() + const [currentTab, setCurrentTab] = useState(null) const [nftCount, setNftCount] = useState() 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) => ({ diff --git a/yarn.lock b/yarn.lock index 5658136bcd..89339078d5 100644 --- a/yarn.lock +++ b/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