-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
290 additions
and
3 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
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.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
202 changes: 202 additions & 0 deletions
202
packages/atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeatureNftModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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, | ||
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> | ||
) | ||
} |
19 changes: 18 additions & 1 deletion
19
.../atlas/src/views/viewer/MarketplaceView/FeaturedNftsSection/FeaturedNftsSection.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)}; | ||
` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters