Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce get featured flow #4126

Merged
merged 3 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions packages/atlas/src/api/queries/__generated__/nfts.generated.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions packages/atlas/src/api/queries/nfts.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,11 @@ query GetFeaturedNftsVideos($limit: Int) {
}
}
}

mutation RequestNftFeatured($nftId: String!, $rationale: String!) {
requestNftFeatured(nftId: $nftId, rationale: $rationale) {
rationale
nftId
createdAt
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { useApolloClient } from '@apollo/client'
import debouncePromise from 'awesome-debounce-promise'
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useForm } from 'react-hook-form'

import {
GetNftDocument,
GetNftQuery,
GetNftQueryVariables,
useRequestNftFeaturedMutation,
} from '@/api/queries/__generated__/nfts.generated'
import { Text } from '@/components/Text'
import { Input } from '@/components/_inputs/Input'
import { DialogModal } from '@/components/_overlays/DialogModal'
import { VideoTileViewer } from '@/components/_video/VideoTileViewer'
import { useSnackbar } from '@/providers/snackbars'
import { SentryLogger } from '@/utils/logs'

import { PreviewWrapper, StyledFormField } from './FeaturedNftsSection.styles'

type FeatureNftModalProps = {
isOpen: boolean
onClose: () => void
}

export const FeatureNftModal: FC<FeatureNftModalProps> = ({ isOpen, onClose }) => {
const {
register,
handleSubmit,
trigger,
reset,
formState: { errors },
} = useForm<{ url: string }>()
const [isInputValidating, setIsInputValidating] = useState(false)

const handleClose = () => {
onClose()
setVideoId('')
reset({ url: '' })
}
const [videoId, setVideoId] = useState('')
const [requestNftFeaturedMutation, { loading }] = useRequestNftFeaturedMutation({
onError: (error) => {
displaySnackbar({
title: 'Something went wrong',
description: 'There was a problem with sending your request. Please try again later.',
iconType: 'error',
})
SentryLogger.error('Error during sending requestNftFeaturedMutation', 'FeatureNftModal', { videoId, error })
},
onCompleted: () => {
displaySnackbar({
title: 'Video submitted succesfully',
description: 'Thanks! We will review your video shortly. Keep an eye on the featured section.',
iconType: 'success',
})
handleClose()
},
})

const { displaySnackbar } = useSnackbar()

const inputRef = useRef<HTMLInputElement | null>(null)

const submit = () => {
handleSubmit(async (data) => {
const videoId = /[^/]*$/.exec(data.url)?.[0]
if (!videoId) {
return
}

await requestNftFeaturedMutation({
variables: {
nftId: videoId,
rationale: '',
},
})
})()
}

const client = useApolloClient()

useEffect(() => {
if (isOpen) {
inputRef.current?.focus()
}
}, [isOpen])

const validateNft = useCallback(
async (id?: string) => {
if (!id) {
return 'Enter a valid link to your video NFT.'
}
const {
data: { ownedNftById },
} = await client.query<GetNftQuery, GetNftQueryVariables>({
query: GetNftDocument,
variables: {
id: id,
},
})
if (!ownedNftById) {
return 'This video is not an NFT.'
}
setVideoId(id)
if (
ownedNftById.transactionalStatus?.__typename === 'TransactionalStatusIdle' ||
ownedNftById.transactionalStatus?.__typename === 'TransactionalStatusInitiatedOfferToMember'
) {
return "This video's NFT is not put on sale."
}

if (
ownedNftById.transactionalStatus?.__typename === 'TransactionalStatusAuction' &&
ownedNftById.transactionalStatus.auction.isCompleted
) {
return "This video's NFT is not put on sale."
}
return true
},
[client]
)

const { ref, ...inputRefRest } = useMemo(() => {
return register('url', {
onChange: debouncePromise(
async () => {
await trigger('url')
setIsInputValidating(false)
},
500,
{
key() {
setIsInputValidating(true)
return null
},
}
),
required: { value: true, message: 'Enter link to your video NFT.' },
validate: {
validUrl: (val) => {
return val.startsWith(window.location.origin + '/video/') ? true : 'Enter a valid link to your video NFT.'
},
nftIsValid: async (val) => {
// get the last string after slash
const videoId = /[^/]*$/.exec(val)?.[0]
const validation = await validateNft(videoId)

return validation
},
},
})
}, [register, trigger, validateNft])

const isInputRefActiveElement = document.activeElement === inputRef.current
return (
<DialogModal
title="Apply for featured section"
show={isOpen}
noContentPadding
primaryButton={{
text: loading ? 'Please wait...' : 'Submit video',
disabled: loading,
onClick: submit,
}}
secondaryButton={{
text: 'Cancel',
disabled: loading,
drillprop marked this conversation as resolved.
Show resolved Hide resolved
onClick: handleClose,
}}
onExitClick={handleClose}
>
<StyledFormField
disableErrorAnimation={isInputRefActiveElement}
error={errors.url?.message}
label="Link to your video NFT"
description="Make sure your video NFT is listed on sale (or an upcoming sale) at the time of submission."
>
<Input
{...inputRefRest}
ref={(e) => {
ref(e)
inputRef.current = e
}}
processing={isInputValidating}
autoComplete="off"
placeholder="Paste your link here"
error={!!errors.url}
/>
</StyledFormField>
<PreviewWrapper>
{videoId ? (
<VideoTileViewer direction="horizontal" id={videoId} detailsVariant="withChannelName" />
) : (
<Text variant="t200" as="p" color="colorTextMuted">
Preview of your video will appear here
</Text>
)}
</PreviewWrapper>
</DialogModal>
)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import styled from '@emotion/styled'

import { sizes } from '@/styles'
import { FormField } from '@/components/_inputs/FormField'
import { cVar, sizes } from '@/styles'

export const FeaturedNftsWrapper = styled.div`
display: flex;
flex-direction: column;
gap: ${sizes(8)};
`

export const StyledFormField = styled(FormField)`
padding: ${sizes(6)} ${sizes(5)} ${sizes(5)};
`

export const PreviewWrapper = styled.div`
display: flex;
padding: ${sizes(5)};
align-items: center;
justify-content: center;
height: 164px;
background: ${cVar('colorBackgroundMutedAlpha')};
box-shadow: ${cVar('effectDividersBottom')}, ${cVar('effectDividersTop')};
width: 100%;
margin-bottom: ${sizes(5)};
`
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react'
import { FC, useState } from 'react'

import { useNfts } from '@/api/hooks/nfts'
import { OwnedNftOrderByInput } from '@/api/queries/__generated__/baseTypes.generated'
Expand All @@ -13,6 +13,7 @@ import { useUser } from '@/providers/user/user.hooks'
import { breakpoints } from '@/styles'
import { createPlaceholderData } from '@/utils/data'

import { FeatureNftModal } from './FeatureNftModal'
import { FeaturedNftsWrapper } from './FeaturedNftsSection.styles'

const responsive: CarouselProps['breakpoints'] = {
Expand Down Expand Up @@ -48,6 +49,7 @@ const responsive: CarouselProps['breakpoints'] = {

export const FeaturedNftsSection: FC = () => {
const { activeChannel } = useUser()
const [isFeatureNftModalOpen, setIsFeatureNfrModalOpen] = useState(false)

const { nfts, loading } = useNfts({
variables: {
Expand Down Expand Up @@ -75,6 +77,7 @@ export const FeaturedNftsSection: FC = () => {

return (
<FeaturedNftsWrapper>
<FeatureNftModal isOpen={isFeatureNftModalOpen} onClose={() => setIsFeatureNfrModalOpen(false)} />
{items.length >= 4 && (
<Section
headerProps={{
Expand Down Expand Up @@ -106,7 +109,7 @@ export const FeaturedNftsSection: FC = () => {
actionButton={{
text: 'Submit your video NFT to be featured',
onClick: () => {
// todo
setIsFeatureNfrModalOpen(true)
},
}}
/>
Expand Down