diff --git a/cypress/e2e/addContent/addTweet.cy.ts b/cypress/e2e/addContent/addTweet.cy.ts index fd1ea367c..bd053d7eb 100644 --- a/cypress/e2e/addContent/addTweet.cy.ts +++ b/cypress/e2e/addContent/addTweet.cy.ts @@ -14,27 +14,21 @@ describe('Add Tweet Content', () => { cy.get('[data-testid="add-content-btn"]').click() cy.get('[data-testid="check-icon"]').click() - // cy.wait('@addTweet').then((interception) => { - // // check we get a 402 response code, when trying to add content for the first time - // expect(interception.response.statusCode).to.eq(402) - // }) - - // cy.intercept({ - // method: 'POST', - // url: 'http://localhost:8444/api/add_node*', - // }).as('addTweet2') - - // cy.wait('@addTweet2') // This is because add source is currently skipped, - cy.wait('@addTweet').then((interception) => { - console.log('Add Tweet Response:', interception.response) - - if (interception.response.statusCode !== 200) { - console.error('Error Status Code:', interception.response.statusCode) - console.error('Error Response:', interception.response.body) - } + // check we get a 402 response code, when trying to add content for the first time + expect(interception.response.statusCode).to.eq(402) }) + cy.intercept({ + method: 'POST', + url: 'http://localhost:8444/api/add_node*', + }).as('addTweet2') + + cy.wait('@addTweet2').then((intersection) => { + console.log(intersection.response) + cy.log(JSON.stringify(intersection.response)) + }) // This is because add source is currently skipped, + cy.get('.Toastify__toast-body').should('have.text', 'Content Added') cy.get('#addContent').should('not.exist') diff --git a/src/components/AddContentModal/index.tsx b/src/components/AddContentModal/index.tsx index 738aca397..fce0de790 100644 --- a/src/components/AddContentModal/index.tsx +++ b/src/components/AddContentModal/index.tsx @@ -1,244 +1,248 @@ -import { useEffect, useState } from 'react' -import { FieldValues, FormProvider, useForm } from 'react-hook-form' -import * as sphinx from 'sphinx-bridge' -import { BaseModal } from '~/components/Modal' -import { - DOCUMENT, - GITHUB_REPOSITORY, - isE2E, - LINK, - NODE_ADD_ERROR, - RSS, - TWITTER_HANDLE, - TWITTER_SOURCE, - WEB_PAGE, - YOUTUBE_CHANNEL, -} from '~/constants' -import { api } from '~/network/api' -import { useDataStore } from '~/stores/useDataStore' -import { useModal } from '~/stores/useModalStore' -import { useUserStore } from '~/stores/useUserStore' -import { sphinxBridge } from '~/testSphinxBridge' -import { SubmitErrRes } from '~/types' -import { getLSat, payLsat, updateBudget } from '~/utils' -import { SuccessNotify } from '../common/SuccessToast' -import { BudgetStep } from './BudgetStep' -import { SourceStep } from './SourceStep' -import { validateSourceURL } from './SourceStep/utils' -import { SourceTypeStep } from './SourceTypeStep' -import { getInputType, isSource, twitterHandlePattern } from './utils' - -export type FormData = { - input: string - inputType: string - source: string - longitude: string - latitude: string -} - -const handleSubmitForm = async ( - data: FieldValues, - sourceType: string, - setBudget: (value: number | null) => void, - setRunningProjectId: (value: string) => void, -): Promise => { - const endPoint = isSource(sourceType) ? 'radar' : 'add_node' - - const body: { [index: string]: unknown } = {} - - if (sourceType === LINK) { - body.media_url = data.source - body.content_type = 'audio_video' - } else if (sourceType === TWITTER_SOURCE) { - const regex = /(?:https?:\/\/)?(?:www\.)?(twitter|x)\.com\/\w+\/status\/(\d+)/s - - if (regex.test(data.source)) { - const idRegex = /\/status\/(\d+)/ - - const match = data.source.match(idRegex) - - if (match?.[1]) { - const [, id] = match - - body.tweet_id = id - } - } else { - body.tweet_id = data.source - } - - body.content_type = 'tweet' - } else if (sourceType === WEB_PAGE) { - body.content_type = 'webpage' - body.web_page = data.source - } else if (sourceType === DOCUMENT) { - body.content_type = 'document' - body.text = data.source - } else if (sourceType === TWITTER_HANDLE) { - const [, match] = (data.source || '').match(twitterHandlePattern) || [] - - if (match) { - body.source = match - body.source_type = sourceType - } else { - return - } - } else if (sourceType === YOUTUBE_CHANNEL || sourceType === RSS || sourceType === GITHUB_REPOSITORY) { - body.source = data.source - body.source_type = sourceType - } - - if (data.latitude && data.longitude) { - body.latitude = data.latitude - body.longitude = data.longitude - } - - let lsatToken = '' - - let enable - - if (!isE2E) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - enable = await sphinx.enable() - } else { - enable = await sphinxBridge.enable() - } - - body.pubkey = enable?.pubkey - - lsatToken = await getLSat() - - try { - const res: SubmitErrRes = await api.post(`/${endPoint}`, JSON.stringify(body), { - Authorization: lsatToken, - }) - - await updateBudget(setBudget) - - if (res.data.project_id) { - setRunningProjectId(res.data.project_id) - } - - if (res.error) { - const { message } = res.error - - throw new Error(message) - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - if (err.status === 402) { - await payLsat(setBudget) - - await handleSubmitForm(data, sourceType, setBudget, setRunningProjectId) - } else { - let errorMessage = NODE_ADD_ERROR - - if (err.status === 400) { - try { - const errorRes = await err.json() - - errorMessage = errorRes.message || errorRes.status || errorRes?.errorCode || NODE_ADD_ERROR - } catch (parseError) { - errorMessage = NODE_ADD_ERROR - } - } else if (err instanceof Error) { - errorMessage = err.message || NODE_ADD_ERROR - } - - throw new Error(errorMessage) - } - } -} - -export const AddContentModal = () => { - const [currentStep, setCurrentStep] = useState(0) - const { close, visible } = useModal('addContent') - const { setBudget } = useUserStore((s) => s) - const { setRunningProjectId } = useDataStore((s) => s) - const form = useForm({ mode: 'onChange' }) - const { watch, setValue, reset } = form - const [loading, setLoading] = useState(false) - const [error, setError] = useState('') - - useEffect( - () => () => { - setError('') - setCurrentStep(0) - reset() - }, - [visible, reset], - ) - - const type = watch('inputType') - const sourceValue = watch('source') - - const source = watch('source') - - const isValidSource = validateSourceURL(sourceValue) - - useEffect(() => { - const updateInputType = async () => { - const inputType = await getInputType(source) - - setValue('inputType', inputType) - } - - updateInputType() - }, [source, setValue]) - - const handleClose = () => { - close() - } - - const onNextStep = () => { - if (currentStep === 0) { - setCurrentStep(isSource(type) ? 1 : 2) - } else { - setCurrentStep(currentStep + 1) - } - } - - const onPrevStep = () => { - setCurrentStep(currentStep - 1) - } - - const onSubmit = form.handleSubmit(async (data) => { - setLoading(true) - - try { - await handleSubmitForm(data, type, setBudget, setRunningProjectId) - SuccessNotify('Content Added') - handleClose() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - let errorMessage = NODE_ADD_ERROR - - if (err?.status === 400) { - const errorRes = await err.json() - - errorMessage = errorRes.errorCode || errorRes?.status || NODE_ADD_ERROR - } else if (err instanceof Error) { - errorMessage = err.message - } - - setError(String(errorMessage)) - } finally { - setLoading(false) - } - }) - - return ( - - -
- {currentStep === 0 && } - {currentStep === 1 && isSource(type) && ( - - )} - {currentStep === 2 && null} type={type} />} - -
-
- ) -} +import { useEffect, useState } from 'react' +import { FieldValues, FormProvider, useForm } from 'react-hook-form' +import * as sphinx from 'sphinx-bridge' +import { BaseModal } from '~/components/Modal' +import { + DOCUMENT, + GITHUB_REPOSITORY, + isE2E, + LINK, + NODE_ADD_ERROR, + RSS, + TWITTER_HANDLE, + TWITTER_SOURCE, + WEB_PAGE, + YOUTUBE_CHANNEL, +} from '~/constants' +import { api } from '~/network/api' +import { useDataStore } from '~/stores/useDataStore' +import { useModal } from '~/stores/useModalStore' +import { useUserStore } from '~/stores/useUserStore' +import { sphinxBridge } from '~/testSphinxBridge' +import { SubmitErrRes } from '~/types' +import { getLSat, payLsat, updateBudget } from '~/utils' +import { SuccessNotify } from '../common/SuccessToast' +import { BudgetStep } from './BudgetStep' +import { SourceStep } from './SourceStep' +import { validateSourceURL } from './SourceStep/utils' +import { SourceTypeStep } from './SourceTypeStep' +import { getInputType, isSource, twitterHandlePattern } from './utils' + +export type FormData = { + input: string + inputType: string + source: string + longitude: string + latitude: string +} + +const handleSubmitForm = async ( + data: FieldValues, + sourceType: string, + setBudget: (value: number | null) => void, + setRunningProjectId: (value: string) => void, +): Promise => { + const endPoint = isSource(sourceType) ? 'radar' : 'add_node' + + const body: { [index: string]: unknown } = {} + + if (sourceType === LINK) { + body.media_url = data.source + body.content_type = 'audio_video' + } else if (sourceType === TWITTER_SOURCE) { + const regex = /(?:https?:\/\/)?(?:www\.)?(twitter|x)\.com\/\w+\/status\/(\d+)/s + + if (regex.test(data.source)) { + const idRegex = /\/status\/(\d+)/ + + const match = data.source.match(idRegex) + + if (match?.[1]) { + const [, id] = match + + body.tweet_id = id + } + } else { + body.tweet_id = data.source + } + + body.content_type = 'tweet' + } else if (sourceType === WEB_PAGE) { + body.content_type = 'webpage' + body.web_page = data.source + } else if (sourceType === DOCUMENT) { + body.content_type = 'document' + body.text = data.source + } else if (sourceType === TWITTER_HANDLE) { + const [, match] = (data.source || '').match(twitterHandlePattern) || [] + + if (match) { + body.source = match + body.source_type = sourceType + } else { + return + } + } else if (sourceType === YOUTUBE_CHANNEL || sourceType === RSS || sourceType === GITHUB_REPOSITORY) { + body.source = data.source + body.source_type = sourceType + } + + if (data.latitude && data.longitude) { + body.latitude = data.latitude + body.longitude = data.longitude + } + + let lsatToken = '' + + let enable + + if (!isE2E) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + enable = await sphinx.enable() + } else { + enable = await sphinxBridge.enable() + } + + body.pubkey = enable?.pubkey + + lsatToken = await getLSat() + + try { + const res: SubmitErrRes = await api.post(`/${endPoint}`, JSON.stringify(body), { + Authorization: lsatToken, + }) + + await updateBudget(setBudget) + + if (res.data.project_id) { + setRunningProjectId(res.data.project_id) + } + + if (res.error) { + const { message } = res.error + + throw new Error(message) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + if (err.status === 402) { + await payLsat(setBudget) + + await handleSubmitForm(data, sourceType, setBudget, setRunningProjectId) + } else { + let errorMessage = NODE_ADD_ERROR + + if (err.status === 400) { + try { + const errorRes = await err.json() + + errorMessage = errorRes.message || errorRes.status || errorRes?.errorCode || NODE_ADD_ERROR + } catch (parseError) { + errorMessage = NODE_ADD_ERROR + } + } else if (err instanceof Error) { + errorMessage = err.message || NODE_ADD_ERROR + } + + throw new Error(errorMessage) + } + } +} + +export const AddContentModal = () => { + const [currentStep, setCurrentStep] = useState(0) + const { close, visible } = useModal('addContent') + const { setBudget } = useUserStore((s) => s) + const { setRunningProjectId } = useDataStore((s) => s) + const form = useForm({ mode: 'onChange' }) + const { watch, setValue, reset } = form + const [loading, setLoading] = useState(false) + const [error, setError] = useState('') + + useEffect( + () => () => { + setError('') + setCurrentStep(0) + reset() + }, + [visible, reset], + ) + + const type = watch('inputType') + const sourceValue = watch('source') + + const source = watch('source') + + const isValidSource = validateSourceURL(sourceValue) + + useEffect(() => { + const updateInputType = async () => { + const inputType = await getInputType(source) + + setValue('inputType', inputType) + } + + updateInputType() + }, [source, setValue]) + + const handleClose = () => { + close() + } + + const onNextStep = () => { + if (currentStep === 0) { + setCurrentStep(isSource(type) ? 1 : 2) + } else { + setCurrentStep(currentStep + 1) + } + } + + const onPrevStep = () => { + setCurrentStep(currentStep - 1) + } + + const onSubmit = form.handleSubmit(async (data) => { + setLoading(true) + + try { + await handleSubmitForm(data, type, setBudget, setRunningProjectId) + SuccessNotify('Content Added') + handleClose() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + let errorMessage = NODE_ADD_ERROR + + if (err?.status === 400) { + try { + const errorRes = await err.json() + + errorMessage = errorRes.errorCode || errorRes?.status || NODE_ADD_ERROR + } catch (parseError) { + errorMessage = NODE_ADD_ERROR + } + } else if (err instanceof Error) { + errorMessage = err.message + } + + setError(String(errorMessage)) + } finally { + setLoading(false) + } + }) + + return ( + + +
+ {currentStep === 0 && } + {currentStep === 1 && isSource(type) && ( + + )} + {currentStep === 2 && null} type={type} />} + +
+
+ ) +}