@@ -47,4 +49,4 @@ const OnboardingComplete = () => {
)
}
-export default OnboardingComplete
+export { OnboardingComplete }
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/GithubOrganizations.tsx b/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/GithubOrganizations.tsx
index bc2f7de63..079532935 100644
--- a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/GithubOrganizations.tsx
+++ b/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/GithubOrganizations.tsx
@@ -6,7 +6,7 @@ import ListEmpty from '../ListEmpty'
import OAuthProfile from '../OAuthProfile'
import PreviousStep from '../PreviousStep'
import { Formik, Form, Field } from 'formik'
-import { FormikCheckbox, FormikInput } from '../../../../../components/Formik'
+import { FormikInput } from '../../../../../components/Formik'
import yup from '../../../../yup-extended'
import { TOAuthSession } from '../../../../types/oauth.types'
import { useOnboardingData } from '../../../../hooks/onboarding.hooks'
@@ -22,19 +22,19 @@ type TGithubOrganizationsProps = {
const GithubOrganizations = (props: TGithubOrganizationsProps) => {
const { oauth, signoutOAuth } = props
- const { data, invites, organizations, repositories, updateData, getOrganizations } =
+ const { data, organizations, repositories, updateData, getOrganizations, upload } =
useOnboardingData(oauth)
const onBackClick = () => {
updateData({ step: 'invites' })
}
- const onContinueClick = (values: any) => {
- updateData({
- step: 'phrase',
- isEmailPublic: values.is_email_public,
- emailOther: values.email_other,
- })
+ const onFormSubmit = async (values: { email_other: string }) => {
+ try {
+ await upload({ email: values.email_other })
+ } catch (e: any) {
+ console.error(e.message)
+ }
}
useEffect(() => {
@@ -58,11 +58,7 @@ const GithubOrganizations = (props: TGithubOrganizationsProps) => {
- {!invites.items.length ? (
-
- ) : (
-
- )}
+
@@ -75,33 +71,15 @@ const GithubOrganizations = (props: TGithubOrganizationsProps) => {
- {() => (
+ {({ isSubmitting }) => (
-
{
component={FormikInput}
autoComplete="off"
placeholder="Email for notifications"
+ disabled={isSubmitting}
help="You can input another email to send notifications to"
/>
@@ -117,7 +96,10 @@ const GithubOrganizations = (props: TGithubOrganizationsProps) => {
Upload
@@ -125,21 +107,8 @@ const GithubOrganizations = (props: TGithubOrganizationsProps) => {
)}
-
- {!repositories.selected.length &&
- !!invites.items.filter((i) => i.accepted === true).length && (
-
-
- Skip this step
-
-
- )}
+
{
)
}
-export default GithubOrganizations
+export { GithubOrganizations }
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/ListItem.tsx b/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/ListItem.tsx
index 7b427e6b7..4f81bac87 100644
--- a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/ListItem.tsx
+++ b/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/ListItem.tsx
@@ -3,9 +3,9 @@ import { TOnboardingOrganization } from '../../../../types/onboarding.types'
import { TOAuthSession } from '../../../../types/oauth.types'
import { useOnboardingData } from '../../../../hooks/onboarding.hooks'
import classNames from 'classnames'
-import GithubRepositories from '../GithubRepositories'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronUp } from '@fortawesome/free-solid-svg-icons'
+import { GithubRepositories } from '../GithubRepositories'
type TOrganizationListItemProps = {
oauth: TOAuthSession
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/index.ts b/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/index.ts
deleted file mode 100644
index 9ad9254a8..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GithubOrganizations/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './GithubOrganizations'
-export { default } from './GithubOrganizations'
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GithubRepositories.tsx b/web/src/v6.1.0/pages/Onboarding/components/GithubRepositories.tsx
index 5e211617e..c669a548d 100644
--- a/web/src/v6.1.0/pages/Onboarding/components/GithubRepositories.tsx
+++ b/web/src/v6.1.0/pages/Onboarding/components/GithubRepositories.tsx
@@ -7,7 +7,7 @@ import ListEmpty from './ListEmpty'
import { toast } from 'react-toastify'
import { ToastError } from '../../../../components/Toast'
import { useOnboardingRepositories } from '../../../hooks/onboarding.hooks'
-import Loader from '../../../../components/Loader/Loader'
+import Loader from '../../../../components/Loader'
type TGithubRepositoriesProps = {
organization: TOnboardingOrganization
@@ -92,4 +92,4 @@ const GithubRepositories = (props: TGithubRepositoriesProps) => {
)
}
-export default GithubRepositories
+export { GithubRepositories }
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/GoshDaoInvites.tsx b/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/GoshDaoInvites.tsx
deleted file mode 100644
index ab912a2f7..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/GoshDaoInvites.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { faRotateRight } from '@fortawesome/free-solid-svg-icons'
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { useEffect } from 'react'
-import Spinner from '../../../../../components/Spinner'
-import ListEmpty from '../ListEmpty'
-import { TOAuthSession } from '../../../../types/oauth.types'
-import OAuthProfile from '../OAuthProfile'
-import { toast } from 'react-toastify'
-import { ToastError } from '../../../../../components/Toast'
-import { useOnboardingData } from '../../../../hooks/onboarding.hooks'
-import { Button } from '../../../../../components/Form'
-import DaoInviteListItem from './ListItem'
-
-type TGoshDaoInvitesProps = {
- oauth: TOAuthSession
- signoutOAuth(): Promise
-}
-
-const GoshDaoInvites = (props: TGoshDaoInvitesProps) => {
- const { oauth, signoutOAuth } = props
- const { invites, getDaoInvites, updateData } = useOnboardingData(oauth)
-
- const onContinueClick = () => {
- updateData({ step: 'organizations' })
- }
-
- useEffect(() => {
- const _getDaoInvites = async () => {
- try {
- const hasItems = await getDaoInvites()
- if (!hasItems) {
- onContinueClick()
- }
- } catch (e: any) {
- console.error(e.message)
- toast.error( )
- onContinueClick()
- }
- }
- _getDaoInvites()
- }, [])
-
- if (!oauth.session) {
- return null
- }
- return (
-
-
-
-
-
-
- Accept or decline invitations to the DAO
-
-
- i.accepted === null).length
- }
- >
- Next step
-
-
-
-
-
-
- {invites.isFetching ? (
-
- ) : (
-
- )}
- Refresh
-
-
-
- {!invites.isFetching && !invites.items.length && (
-
You have no pending invites to DAOs on GOSH
- )}
-
-
- {invites.items.map((item, index) => (
-
- ))}
-
-
-
- )
-}
-
-export default GoshDaoInvites
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/index.ts b/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/index.ts
deleted file mode 100644
index 4654e00e7..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './GoshDaoInvites'
-export { default } from './GoshDaoInvites'
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshPhrase.tsx b/web/src/v6.1.0/pages/Onboarding/components/GoshPhrase.tsx
deleted file mode 100644
index a1e422b04..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshPhrase.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Field } from 'formik'
-import { useEffect } from 'react'
-import { toast } from 'react-toastify'
-import { ToastError } from '../../../../components/Toast'
-import { FormikCheckbox } from '../../../../components/Formik'
-import PreviousStep from './PreviousStep'
-import { AppConfig } from '../../../../appconfig'
-import { EGoshError, GoshError } from '../../../../errors'
-import PhraseForm from '../../../../components/PhraseForm/PhraseForm'
-import { TOAuthSession } from '../../../types/oauth.types'
-import { useOnboardingData } from '../../../hooks/onboarding.hooks'
-import Alert from '../../../../components/Alert/Alert'
-import yup from '../../../yup-extended'
-
-type TGoshPhraseProps = {
- oauth: TOAuthSession
-}
-
-const GoshPhrase = (props: TGoshPhraseProps) => {
- const { oauth } = props
- const {
- data: { phrase },
- updateData,
- } = useOnboardingData(oauth)
-
- const onBackClick = () => {
- updateData({ step: 'organizations' })
- }
-
- const onFormSubmit = async (values: { words: string[] }) => {
- try {
- const { words } = values
- const { valid } = await AppConfig.goshclient.crypto.mnemonic_verify({
- phrase: words.join(' '),
- })
- if (!valid) {
- throw new GoshError(EGoshError.PHRASE_INVALID)
- }
- updateData({ phrase: words, step: 'phrase-check' })
- } catch (e: any) {
- console.error(e.message)
- toast.error( )
- }
- }
-
- useEffect(() => {
- const _setRandomPhrase = async () => {
- const { phrase } = await AppConfig.goshclient.crypto.mnemonic_from_random({})
- updateData({ phrase: phrase.split(' ') })
- }
-
- if (!phrase.length) {
- _setRandomPhrase()
- }
- }, [phrase])
-
- return (
-
-
-
-
-
- Let's set up your GOSH account
-
-
-
- Write down the seed phrase in a safe place or enter an existing one if
- you already have a GOSH account
-
-
-
-
-
updateData({ phrase: words })}
- >
-
-
- GOSH cannot reset this phrase! If you forget it, you might
- lose access to your account
-
-
-
-
-
-
-
-
-
- )
-}
-
-export default GoshPhrase
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshPhraseCheck.tsx b/web/src/v6.1.0/pages/Onboarding/components/GoshPhraseCheck.tsx
deleted file mode 100644
index 0764c85a7..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshPhraseCheck.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { useEffect, useState } from 'react'
-import { toast } from 'react-toastify'
-import { ToastError } from '../../../../components/Toast'
-import PreviousStep from './PreviousStep'
-import { GoshError } from '../../../../errors'
-import PhraseForm from '../../../../components/PhraseForm/PhraseForm'
-import { TOAuthSession } from '../../../types/oauth.types'
-import { useOnboardingData } from '../../../hooks/onboarding.hooks'
-
-type TGoshPhraseProps = {
- oauth: TOAuthSession
-}
-
-const generateRandomWordNumbers = () => {
- const min = 0
- const max = 11
- const numbers: number[] = []
- while (true) {
- const num = Math.floor(Math.random() * (max - min + 1)) + min
- if (numbers.indexOf(num) < 0) {
- numbers.push(num)
- }
- if (numbers.length === 3) {
- break
- }
- }
- return numbers.sort((a, b) => a - b)
-}
-
-const GoshPhraseCheck = (props: TGoshPhraseProps) => {
- const { oauth } = props
- const {
- data: { phrase },
- updateData,
- } = useOnboardingData(oauth)
- const [rndNumbers, setRndNumbers] = useState([])
-
- const onBackClick = () => {
- updateData({ step: 'phrase' })
- }
-
- const onFormSubmit = async (values: { words: string[] }) => {
- try {
- const { words } = values
- const validated = words.map((w, index) => {
- return w === phrase[rndNumbers[index]]
- })
- if (!validated.every((v) => !!v)) {
- throw new GoshError('Words check failed')
- }
- updateData({ step: 'username' })
- } catch (e: any) {
- console.error(e.message)
- toast.error( )
- }
- }
-
- useEffect(() => {
- setRndNumbers(generateRandomWordNumbers())
- }, [])
-
- return (
-
-
-
-
-
- Let's set up your GOSH account
-
-
-
- Please input requested words from your phrase to ensure it is written
- correctly
-
-
-
-
-
- Input words{' '}
-
- {rndNumbers.map((n) => n + 1).join(' - ')}
- {' '}
- of your phrase
-
-
-
-
- )
-}
-
-export default GoshPhraseCheck
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshUsername.tsx b/web/src/v6.1.0/pages/Onboarding/components/GoshUsername.tsx
deleted file mode 100644
index 42b3b95e1..000000000
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshUsername.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import { Field, Form, Formik } from 'formik'
-import { useSetRecoilState } from 'recoil'
-import yup from '../../../yup-extended'
-import { PinCodeModal } from '../../../components/Modal'
-import { appModalStateAtom } from '../../../../store/app.state'
-import { TOAuthSession } from '../../../types/oauth.types'
-import PreviousStep from './PreviousStep'
-import { FormikInput } from '../../../../components/Formik'
-import { Button } from '../../../../components/Form'
-import Alert from '../../../../components/Alert'
-import { useOnboardingData, useOnboardingSignup } from '../../../hooks/onboarding.hooks'
-
-type TGoshUsernameProps = {
- oauth: TOAuthSession
- signoutOAuth(): Promise
-}
-
-const GoshUsername = (props: TGoshUsernameProps) => {
- const { oauth, signoutOAuth } = props
- const { data, updateData } = useOnboardingData(oauth)
- const { signup } = useOnboardingSignup(oauth)
- const setModal = useSetRecoilState(appModalStateAtom)
-
- const onBackClick = () => {
- updateData({ step: 'phrase' })
- }
-
- const onFormSubmit = async (values: { username: string }) => {
- try {
- // Signup with onboarding
- const username = values.username.trim().toLowerCase()
- const seed = data.phrase.join(' ')
- const isAllValid = await signup(username)
-
- // Create PIN-code
- setModal({
- static: true,
- isOpen: true,
- element: (
- {
- if (isAllValid) {
- await signoutOAuth()
- }
- updateData({
- step: 'complete',
- username,
- email: oauth.session!.user.email!,
- redirectTo: isAllValid ? '/a/orgs' : '/onboarding/status',
- })
- }}
- />
- ),
- })
- } catch (e: any) {
- console.error(e.message)
- }
- }
-
- return (
-
-
-
-
Choose a short nickname
-
or create a new one
-
-
-
-
-
- {oauth.session?.user.user_metadata.user_name}
-
-
- your GOSH nickname
-
-
-
- {({ isSubmitting, setFieldValue }) => (
-
- )}
-
-
-
-
- )
-}
-
-export default GoshUsername
diff --git a/web/src/v6.1.0/pages/Onboarding/components/OAuthSignin.tsx b/web/src/v6.1.0/pages/Onboarding/components/OAuthSignin.tsx
index f3af03408..af8515c4c 100644
--- a/web/src/v6.1.0/pages/Onboarding/components/OAuthSignin.tsx
+++ b/web/src/v6.1.0/pages/Onboarding/components/OAuthSignin.tsx
@@ -27,7 +27,7 @@ const OAuthSignin = (props: TOAuthSigninProps) => {
- Create account with Github
+ Sign in with Github
@@ -35,4 +35,4 @@ const OAuthSignin = (props: TOAuthSigninProps) => {
)
}
-export default OAuthSignin
+export { OAuthSignin }
diff --git a/web/src/v6.1.0/pages/Onboarding/components/index.ts b/web/src/v6.1.0/pages/Onboarding/components/index.ts
new file mode 100644
index 000000000..fd7f6ed0c
--- /dev/null
+++ b/web/src/v6.1.0/pages/Onboarding/components/index.ts
@@ -0,0 +1,4 @@
+export * from './OAuthSignin'
+export * from './GithubOrganizations/GithubOrganizations'
+export * from './GithubRepositories'
+export * from './Complete'
diff --git a/web/src/v6.1.0/pages/Signup/Signup.tsx b/web/src/v6.1.0/pages/Signup/Signup.tsx
index 0d92e0a88..86c2120bb 100644
--- a/web/src/v6.1.0/pages/Signup/Signup.tsx
+++ b/web/src/v6.1.0/pages/Signup/Signup.tsx
@@ -1,7 +1,55 @@
-import { Navigate } from 'react-router-dom'
+import { AnimatePresence, motion } from 'framer-motion'
+import { useUserSignup } from '../../hooks/user.hooks'
+import {
+ CompleteForm,
+ DaoInvitesForm,
+ PhraseCheckForm,
+ PhraseCreateForm,
+ UsernameForm,
+} from './components'
+import { withRouteAnimation } from '../../hocs'
+
+const motionProps = {
+ initial: { opacity: 0 },
+ animate: { opacity: 1 },
+ exit: { opacity: 0 },
+ transition: { duration: 0.25 },
+}
const SignupPage = () => {
- return
+ const { data } = useUserSignup()
+
+ return (
+
+
+ {data.step === 'username' && (
+
+
+
+ )}
+ {data.step === 'daoinvite' && (
+
+
+
+ )}
+ {data.step === 'phrase' && (
+
+
+
+ )}
+ {data.step === 'phrasecheck' && (
+
+
+
+ )}
+ {data.step === 'complete' && (
+
+
+
+ )}
+
+
+ )
}
-export default SignupPage
+export default withRouteAnimation(SignupPage)
diff --git a/web/src/v6.1.0/pages/Signup/components/CompleteForm.tsx b/web/src/v6.1.0/pages/Signup/components/CompleteForm.tsx
new file mode 100644
index 000000000..61da9b648
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/CompleteForm.tsx
@@ -0,0 +1,84 @@
+import { Form, Formik } from 'formik'
+import { Button } from '../../../../components/Form'
+import { useUserSignup } from '../../../hooks/user.hooks'
+import { useNavigate } from 'react-router-dom'
+import { useState } from 'react'
+
+const CompleteForm = () => {
+ const navigate = useNavigate()
+ const { submitCompleteStep } = useUserSignup()
+ const [isAnySubmitting, setIsAnySubmitting] = useState
(false)
+
+ const onGithubSubmit = async () => {
+ try {
+ setIsAnySubmitting(true)
+ await submitCompleteStep({ provider: 'github' })
+ } catch (e: any) {
+ console.error(e.message)
+ } finally {
+ setIsAnySubmitting(false)
+ }
+ }
+
+ const onSkipSubmit = async () => {
+ try {
+ setIsAnySubmitting(true)
+ await submitCompleteStep({ provider: null })
+ navigate('/a/orgs')
+ } catch (e: any) {
+ console.error(e.message)
+ } finally {
+ setIsAnySubmitting(false)
+ }
+ }
+
+ return (
+
+
+
+
+
+ Do you want to upload your repository from GitHub
+
+
+
+
+ {({ isSubmitting }) => (
+
+ )}
+
+
+
+
+ {({ isSubmitting }) => (
+
+ )}
+
+
+
+
+ )
+}
+
+export { CompleteForm }
diff --git a/web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/DaoInvitesForm.tsx b/web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/DaoInvitesForm.tsx
new file mode 100644
index 000000000..1348d246b
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/DaoInvitesForm.tsx
@@ -0,0 +1,64 @@
+import { useState } from 'react'
+import { Button } from '../../../../../components/Form'
+import DaoInviteListItem from './ListItem'
+import { useUserSignup } from '../../../../hooks/user.hooks'
+import { Form, Formik } from 'formik'
+import { PreviousStep } from '../PreviousStep'
+
+const DaoInvitesForm = () => {
+ const { data, submitDaoInvitesStep } = useUserSignup()
+ const [isSubmitting, setIsSubmitting] = useState(false)
+
+ const onFormSubmit = async () => {
+ try {
+ setIsSubmitting(true)
+ await submitDaoInvitesStep()
+ } catch (e: any) {
+ console.error(e.message)
+ } finally {
+ setIsSubmitting(false)
+ }
+ }
+
+ return (
+
+
+
+
+ Accept or decline invitations to the DAO
+
+
+ {({ isSubmitting }) => (
+
+ )}
+
+
+
+
+ {data.daoinvites.map((item, index) => (
+
+ ))}
+
+
+
+ )
+}
+
+export { DaoInvitesForm }
diff --git a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/ListItem.tsx b/web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/ListItem.tsx
similarity index 85%
rename from web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/ListItem.tsx
rename to web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/ListItem.tsx
index 836bc7ac5..6ccdfb2a6 100644
--- a/web/src/v6.1.0/pages/Onboarding/components/GoshDaoInvites/ListItem.tsx
+++ b/web/src/v6.1.0/pages/Signup/components/DaoInvitesForm/ListItem.tsx
@@ -1,18 +1,17 @@
-import { TOnboardingInvite } from '../../../../types/onboarding.types'
-import { TOAuthSession } from '../../../../types/oauth.types'
-import { useOnboardingData } from '../../../../hooks/onboarding.hooks'
import classNames from 'classnames'
import emptylogo from '../../../../../assets/images/emptylogo.svg'
import { Button } from '../../../../../components/Form'
+import { TDBDaoInvite } from '../../../../types/dao.types'
+import { useUserSignup } from '../../../../hooks/user.hooks'
type TOrganizationListItemProps = {
- oauth: TOAuthSession
- item: TOnboardingInvite
+ item: TDBDaoInvite
+ disabled?: boolean
}
const DaoInviteListItem = (props: TOrganizationListItemProps) => {
- const { oauth, item } = props
- const { toggleDaoInvite } = useOnboardingData(oauth)
+ const { item, disabled } = props
+ const { setDaoInviteStatus } = useUserSignup()
return (
@@ -40,8 +39,9 @@ const DaoInviteListItem = (props: TOrganizationListItemProps) => {
? '!bg-red-ff3b30 !text-white !border-transparent'
: null,
)}
+ disabled={disabled}
onClick={() => {
- toggleDaoInvite(false, item)
+ setDaoInviteStatus(item.id, false)
}}
>
Reject
@@ -59,8 +59,9 @@ const DaoInviteListItem = (props: TOrganizationListItemProps) => {
? '!bg-green-600 !text-white !border-transparent'
: null,
)}
+ disabled={disabled}
onClick={() => {
- toggleDaoInvite(true, item)
+ setDaoInviteStatus(item.id, true)
}}
>
Accept
diff --git a/web/src/v6.1.0/pages/Signup/components/PhraseCheckForm.tsx b/web/src/v6.1.0/pages/Signup/components/PhraseCheckForm.tsx
new file mode 100644
index 000000000..cb07b6d21
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/PhraseCheckForm.tsx
@@ -0,0 +1,91 @@
+import { useEffect, useState } from 'react'
+import PhraseForm from '../../../../components/PhraseForm'
+import { PreviousStep } from './PreviousStep'
+import { useUserSignup } from '../../../hooks/user.hooks'
+import { useSetRecoilState } from 'recoil'
+import { appModalStateAtom } from '../../../../store/app.state'
+import { PinCodeModal } from '../../../components/Modal'
+
+const generateRandomWordNumbers = () => {
+ const min = 0
+ const max = 11
+ const numbers: number[] = []
+ while (true) {
+ const num = Math.floor(Math.random() * (max - min + 1)) + min
+ if (numbers.indexOf(num) < 0) {
+ numbers.push(num)
+ }
+ if (numbers.length === 3) {
+ break
+ }
+ }
+ return numbers.sort((a, b) => a - b)
+}
+
+const PhraseCheckForm = () => {
+ const { data, setStep, submitPhraseCheckStep } = useUserSignup()
+ const setModal = useSetRecoilState(appModalStateAtom)
+ const [rndNumbers, setRndNumbers] = useState
([])
+ const [isSubmitting, setIsSubmitting] = useState(false)
+
+ const onFormSubmit = async (values: { words: string[] }) => {
+ try {
+ setIsSubmitting(true)
+ await submitPhraseCheckStep({ words: values.words, numbers: rndNumbers })
+ setModal({
+ static: true,
+ isOpen: true,
+ element: (
+ setStep('complete')}
+ />
+ ),
+ })
+ } catch (e: any) {
+ console.error(e.message)
+ } finally {
+ setIsSubmitting(false)
+ }
+ }
+
+ useEffect(() => {
+ setRndNumbers(generateRandomWordNumbers())
+ }, [])
+
+ return (
+
+
+
+
+ Let's set up your GOSH account
+
+
+ Please input requested words from your phrase to ensure it is written
+ correctly
+
+
+
+
+
+
+ Input words{' '}
+
+ {rndNumbers.map((n) => n + 1).join(' - ')}
+ {' '}
+ of your phrase
+
+
+
+
+
+ )
+}
+
+export { PhraseCheckForm }
diff --git a/web/src/v6.1.0/pages/Signup/components/PhraseCreateForm.tsx b/web/src/v6.1.0/pages/Signup/components/PhraseCreateForm.tsx
new file mode 100644
index 000000000..2bafb2dcf
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/PhraseCreateForm.tsx
@@ -0,0 +1,86 @@
+import { Field } from 'formik'
+import { FormikCheckbox } from '../../../../components/Formik'
+import Alert from '../../../../components/Alert/Alert'
+import yup from '../../../yup-extended'
+import PhraseForm from '../../../../components/PhraseForm'
+import { PreviousStep } from './PreviousStep'
+import { useUserSignup } from '../../../hooks/user.hooks'
+import { useState } from 'react'
+
+const PhraseCreateForm = () => {
+ const { data, setPhrase, submitPhraseCreateStep } = useUserSignup()
+ const [isSubmitting, setIsSubmitting] = useState(false)
+
+ const onFormSubmit = async (values: { words: string[] }) => {
+ try {
+ setIsSubmitting(true)
+ await submitPhraseCreateStep(values.words)
+ } catch (e: any) {
+ console.error(e.message)
+ } finally {
+ setIsSubmitting(false)
+ }
+ }
+
+ return (
+
+
+
+
+ Let's set up your GOSH account
+
+
+ Write down the seed phrase in a safe place or enter an existing one if
+ you already have a GOSH account
+
+
+
+
+
+
setPhrase(words)}
+ >
+
+
+ GOSH cannot reset this phrase! If you forget it, you might
+ lose access to your account
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export { PhraseCreateForm }
diff --git a/web/src/v6.1.0/pages/Signup/components/PreviousStep.tsx b/web/src/v6.1.0/pages/Signup/components/PreviousStep.tsx
new file mode 100644
index 000000000..948750404
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/PreviousStep.tsx
@@ -0,0 +1,36 @@
+import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { Button } from '../../../../components/Form'
+import { useUserSignup } from '../../../hooks/user.hooks'
+
+type TPreviousStepProps = {
+ step: 'username' | 'daoinvite' | 'phrase'
+ disabled?: boolean
+}
+
+const PreviousStep = (props: TPreviousStepProps) => {
+ const { step, disabled } = props
+ const { setStep } = useUserSignup()
+
+ const onButtonClick = () => {
+ setStep(step)
+ }
+
+ return (
+
+
+
+
+ Back
+
+ )
+}
+
+export { PreviousStep }
diff --git a/web/src/v6.1.0/pages/Signup/components/UsernameForm.tsx b/web/src/v6.1.0/pages/Signup/components/UsernameForm.tsx
new file mode 100644
index 000000000..959a1392d
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/UsernameForm.tsx
@@ -0,0 +1,99 @@
+import { Field, Form, Formik } from 'formik'
+import yup from '../../../yup-extended'
+import { FormikInput } from '../../../../components/Formik'
+import Alert from '../../../../components/Alert'
+import { Button } from '../../../../components/Form'
+import { useUserSignup } from '../../../hooks/user.hooks'
+
+type TFormValues = {
+ email: string
+ username: string
+}
+
+const UsernameForm = () => {
+ const { data, submitUsernameStep } = useUserSignup()
+
+ const onFormSubmit = async (values: TFormValues) => {
+ try {
+ await submitUsernameStep(values)
+ } catch (e: any) {
+ console.error(e.message)
+ }
+ }
+
+ return (
+
+
+
Welcome to Gosh
+
Choose a short Nickname and Email
+
+
+
+
+
+ {({ isSubmitting, setFieldValue }) => (
+
+ )}
+
+
+
+
+ )
+}
+
+export { UsernameForm }
diff --git a/web/src/v6.1.0/pages/Signup/components/index.ts b/web/src/v6.1.0/pages/Signup/components/index.ts
new file mode 100644
index 000000000..64caad8fd
--- /dev/null
+++ b/web/src/v6.1.0/pages/Signup/components/index.ts
@@ -0,0 +1,6 @@
+export * from './PreviousStep'
+export * from './UsernameForm'
+export * from './DaoInvitesForm/DaoInvitesForm'
+export * from './PhraseCreateForm'
+export * from './PhraseCheckForm'
+export * from './CompleteForm'
diff --git a/web/src/v6.1.0/pages/UserDaoList/UserDaoList.tsx b/web/src/v6.1.0/pages/UserDaoList/UserDaoList.tsx
index 9532b1c2e..a52083def 100644
--- a/web/src/v6.1.0/pages/UserDaoList/UserDaoList.tsx
+++ b/web/src/v6.1.0/pages/UserDaoList/UserDaoList.tsx
@@ -1,12 +1,14 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'
import { ButtonLink, Input } from '../../../components/Form'
-import { ListBoundary } from './components'
+import { ListBoundaryUser, ListBoundaryPartner } from './components'
import Loader from '../../../components/Loader'
-import { useUserDaoList } from '../../hooks/dao.hooks'
+import { usePartnerDaoList, useUserDaoList } from '../../hooks/dao.hooks'
+import { PARTNER_DAO_NAMES } from '../../../constants'
const UserDaoListPage = () => {
const userDaoList = useUserDaoList()
+ const partnerDaoList = usePartnerDaoList()
return (
<>
@@ -39,14 +41,27 @@ const UserDaoListPage = () => {
-
-
Your organizations
- {userDaoList.isFetching && (
-
Updating...
- )}
+
+
+
Your organizations
+ {userDaoList.isFetching && (
+ Updating...
+ )}
+
+
-
+ {!!PARTNER_DAO_NAMES.length && (
+
+
+
Partners
+ {partnerDaoList.isFetching && (
+ Updating...
+ )}
+
+
+
+ )}
>
)
}
diff --git a/web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryPartner.tsx b/web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryPartner.tsx
new file mode 100644
index 000000000..92604f8e4
--- /dev/null
+++ b/web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryPartner.tsx
@@ -0,0 +1,45 @@
+import { useErrorBoundary, withErrorBoundary } from 'react-error-boundary'
+import Alert from '../../../../components/Alert'
+import { usePartnerDaoList } from '../../../hooks/dao.hooks'
+import { ListItem, ListItemSkeleton } from './ListItem'
+import { useEffect } from 'react'
+
+const ListBoundaryInner = () => {
+ const partnerDaoList = usePartnerDaoList({ initialize: true })
+ const { showBoundary } = useErrorBoundary()
+
+ useEffect(() => {
+ if (partnerDaoList.error) {
+ showBoundary(partnerDaoList.error)
+ }
+ }, [partnerDaoList.error])
+
+ return (
+
+
+ {partnerDaoList.isFetching && !partnerDaoList.items.length && (
+
+
+
+ )}
+
+ {partnerDaoList.items.map((item, index) => (
+
+
+
+ ))}
+
+
+ )
+}
+
+const ListBoundaryPartner = withErrorBoundary(ListBoundaryInner, {
+ fallbackRender: ({ error }) => (
+
+ Fetch partner DAO list error
+ {error.message}
+
+ ),
+})
+
+export { ListBoundaryPartner }
diff --git a/web/src/v6.1.0/pages/UserDaoList/components/ListBoundary.tsx b/web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryUser.tsx
similarity index 96%
rename from web/src/v6.1.0/pages/UserDaoList/components/ListBoundary.tsx
rename to web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryUser.tsx
index fefcdf64e..d9c5ff898 100644
--- a/web/src/v6.1.0/pages/UserDaoList/components/ListBoundary.tsx
+++ b/web/src/v6.1.0/pages/UserDaoList/components/ListBoundaryUser.tsx
@@ -67,7 +67,7 @@ const ListBoundaryInner = () => {
)
}
-const ListBoundary = withErrorBoundary(ListBoundaryInner, {
+const ListBoundaryUser = withErrorBoundary(ListBoundaryInner, {
fallbackRender: ({ error }) => (
Fetch DAO list error
@@ -76,4 +76,4 @@ const ListBoundary = withErrorBoundary(ListBoundaryInner, {
),
})
-export { ListBoundary }
+export { ListBoundaryUser }
diff --git a/web/src/v6.1.0/pages/UserDaoList/components/index.ts b/web/src/v6.1.0/pages/UserDaoList/components/index.ts
index ec586c004..1ddef22df 100644
--- a/web/src/v6.1.0/pages/UserDaoList/components/index.ts
+++ b/web/src/v6.1.0/pages/UserDaoList/components/index.ts
@@ -1,2 +1,3 @@
-export * from './ListBoundary'
+export * from './ListBoundaryUser'
+export * from './ListBoundaryPartner'
export * from './ListItem'
diff --git a/web/src/v6.1.0/store/dao.state.ts b/web/src/v6.1.0/store/dao.state.ts
index 9f225f4e4..eeeefe8ce 100644
--- a/web/src/v6.1.0/store/dao.state.ts
+++ b/web/src/v6.1.0/store/dao.state.ts
@@ -13,6 +13,14 @@ import {
TTaskDetails,
} from '../types/dao.types'
+export const partnerDaoListAtom = atom({
+ key: `PartnerDaoListAtom_${contextVersion}`,
+ default: {
+ isFetching: false,
+ items: [],
+ },
+})
+
export const userDaoListAtom = atom({
key: `UserDaoListAtom_${contextVersion}`,
default: {
diff --git a/web/src/v6.1.0/store/onboarding.state.ts b/web/src/v6.1.0/store/onboarding.state.ts
index 412ef8c99..344fcbb35 100644
--- a/web/src/v6.1.0/store/onboarding.state.ts
+++ b/web/src/v6.1.0/store/onboarding.state.ts
@@ -3,7 +3,6 @@ import { atom, selector, selectorFamily } from 'recoil'
import { contextVersion } from '../constants'
import {
TOnboardingData,
- TOnboardingInvite,
TOnboardingOrganization,
TOnboardingRepository,
TOnboardingStatusDao,
@@ -13,11 +12,7 @@ import { OAuthSessionAtom } from './oauth.state'
export const onboardingDataAtom = atom({
key: `OnboardingDataAtom_${contextVersion}`,
default: {
- invites: { items: [], isFetching: false },
organizations: { items: [], isFetching: false },
- phrase: [],
- isEmailPublic: true,
- username: '',
emailOther: '',
},
})
@@ -97,19 +92,3 @@ export const repositoriesSelector = selectorFamily<
}))
},
})
-
-export const daoInvitesSelector = selector<{
- items: TOnboardingInvite[]
- isFetching: boolean
-}>({
- key: `DaoInvitesSelector_${contextVersion}`,
- get: ({ get }) => {
- return get(onboardingDataAtom).invites
- },
- set: ({ set }, value) => {
- set(onboardingDataAtom, (state) => ({
- ...state,
- invites: value as any,
- }))
- },
-})
diff --git a/web/src/v6.1.0/store/signup.state.ts b/web/src/v6.1.0/store/signup.state.ts
new file mode 100644
index 000000000..dbcb544a4
--- /dev/null
+++ b/web/src/v6.1.0/store/signup.state.ts
@@ -0,0 +1,20 @@
+import { atom } from 'recoil'
+import { contextVersion } from '../constants'
+import { TDBDaoInvite } from '../types/dao.types'
+
+export const userSignupAtom = atom<{
+ username: string
+ email: string
+ phrase: string[]
+ daoinvites: TDBDaoInvite[]
+ step: 'username' | 'daoinvite' | 'phrase' | 'phrasecheck' | 'complete'
+}>({
+ key: `UserSignupAtom_${contextVersion}`,
+ default: {
+ username: '',
+ email: '',
+ phrase: [],
+ daoinvites: [],
+ step: 'username',
+ },
+})
diff --git a/web/src/v6.1.0/types/dao.types.ts b/web/src/v6.1.0/types/dao.types.ts
index 575464374..be6ee8c52 100644
--- a/web/src/v6.1.0/types/dao.types.ts
+++ b/web/src/v6.1.0/types/dao.types.ts
@@ -12,6 +12,13 @@ export enum EDaoMemberType {
User = 'user',
}
+export enum EDaoInviteStatus {
+ ACCEPTED = 'accepted',
+ REJECTED = 'rejected',
+ REVOKED = 'revoked',
+ PROPOSAL_CREATED = 'proposal_created',
+}
+
export type TDaoListItem = {
account: Dao | null
name: string
@@ -220,3 +227,9 @@ export type TDaoTaskList = {
hasNext?: boolean
error?: any
}
+
+export type TDBDaoInvite = {
+ id: string
+ daoname: string
+ accepted: boolean | null
+}
diff --git a/web/src/v6.1.0/types/onboarding.types.ts b/web/src/v6.1.0/types/onboarding.types.ts
index 61b8806ea..418343fb6 100644
--- a/web/src/v6.1.0/types/onboarding.types.ts
+++ b/web/src/v6.1.0/types/onboarding.types.ts
@@ -1,32 +1,11 @@
import { TValidationResult } from '../../types/validator.types'
-export enum EDaoInviteStatus {
- ACCEPTED = 'accepted',
- REJECTED = 'rejected',
- REVOKED = 'revoked',
- PROPOSAL_CREATED = 'proposal_created',
-}
-
export type TOnboardingData = {
- step?:
- | 'signin'
- | 'invites'
- | 'organizations'
- | 'phrase'
- | 'phrase-check'
- | 'username'
- | 'complete'
- invites: {
- items: TOnboardingInvite[]
- isFetching: boolean
- }
+ step?: 'signin' | 'organizations' | 'complete'
organizations: {
items: TOnboardingOrganization[]
isFetching: boolean
}
- phrase: string[]
- isEmailPublic: boolean
- username: string
emailOther: string
redirectTo?: string
}
@@ -55,12 +34,6 @@ export type TOnboardingRepository = {
isSelected: boolean
}
-export type TOnboardingInvite = {
- id: string
- daoname: string
- accepted: boolean | null
-}
-
export type TOnboardingStatusDao = {
name: string
repos: TOnboardingStatusRepo[]
diff --git a/web/src/v6.1.0/validators.ts b/web/src/v6.1.0/validators.ts
index 0ed7f76fe..f83583a1f 100644
--- a/web/src/v6.1.0/validators.ts
+++ b/web/src/v6.1.0/validators.ts
@@ -116,8 +116,8 @@ export const validateOnboardingDao = async (name: string): Promise