diff --git a/packages/app/.gitignore b/packages/app/.gitignore index 9ad7c8e..ccef523 100755 --- a/packages/app/.gitignore +++ b/packages/app/.gitignore @@ -33,4 +33,7 @@ yarn-error.log* next-env.d.ts # build artifacts -/generated \ No newline at end of file +/generated + +# performance metrics +.million \ No newline at end of file diff --git a/packages/app/app/_providers/jotai.provider.tsx b/packages/app/app/_providers/jotai.provider.tsx index e6440e0..203862f 100644 --- a/packages/app/app/_providers/jotai.provider.tsx +++ b/packages/app/app/_providers/jotai.provider.tsx @@ -21,9 +21,6 @@ const CurrentUserProvider = ({ children }: PropsWithChildren) => { [triggerCurrentUserIdAtom, session?.user?.id], ] as const); - // useEffect(() => { - // if (session && (!triggerId || triggerId !== session.user?.id)) setTriggerId(session?.user?.id); - // }, [session, triggerId, setTriggerId]); return children; }; diff --git a/packages/app/components/Navbar.tsx b/packages/app/components/Navbar.tsx index ccffe88..1b71e22 100755 --- a/packages/app/components/Navbar.tsx +++ b/packages/app/components/Navbar.tsx @@ -22,6 +22,7 @@ import { PATHS } from '@/config/constants'; import { currentUserAtom, currentUserAvatarUrlAtom } from '@/hooks/state/currentUser'; import useAuth from '@/hooks/useAuth'; import { getUserDisplayName } from '@/utils'; +import OnboardingModal from './OnboardingModal'; import { ThemeSwitch } from './ThemeSwitch'; const Navbar: React.FC = () => { @@ -37,120 +38,123 @@ const Navbar: React.FC = () => { const isPathDao = pathname === PATHS.dao; return ( - - - - - -

Quilombo

-
-
-
+ <> + + + + + +

Quilombo

+
+
+
- - - - Search - - - - - Axé - - - - - Organization - - - + + + + Search + + + + + Axé + + + + + Organization + + + - - - {session && !!user && ( - - - - - - -

Signed in as

-

{getUserDisplayName(user)}

-
- - My Profile - - - My Group - - - Admin - - - Log out - -
-
- )} -
- - - setIsMenuOpen(false)} - > - Search - - - - setIsMenuOpen(false)} - > - Axé - - - - setIsMenuOpen(false)} - > - Organization - - - -
+ + + {session && !!user && ( + + + + + + +

Signed in as

+

{getUserDisplayName(user)}

+
+ + My Profile + + + My Group + + + Admin + + + Log out + +
+
+ )} +
+ + + setIsMenuOpen(false)} + > + Search + + + + setIsMenuOpen(false)} + > + Axé + + + + setIsMenuOpen(false)} + > + Organization + + + +
+ + ); }; diff --git a/packages/app/components/OnboardingModal.tsx b/packages/app/components/OnboardingModal.tsx new file mode 100644 index 0000000..fb993d4 --- /dev/null +++ b/packages/app/components/OnboardingModal.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { Button } from '@nextui-org/button'; +import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure } from '@nextui-org/modal'; +import { useAtomValue } from 'jotai'; +import { useCallback, useEffect } from 'react'; + +import { PATHS } from '@/config/constants'; +import { currentUserAtom } from '@/hooks/state/currentUser'; +import { getCookie, setCookie } from 'cookies-next'; +import { useRouter } from 'next/navigation'; + +/** + * This modal automatically opens on first login (and page reload) if the user hasn't filled out + * at least a name or nickname. It sets a cookie that expires after 24 hours to prevent the modal from + * showing up again. If the user still hasn't completed the described fields after that + * time, the modal would be displayed again. + */ +const OnboardingModal = () => { + const { isOpen, onOpen, onOpenChange } = useDisclosure(); + const router = useRouter(); + const { data: user } = useAtomValue(currentUserAtom); + + useEffect(() => { + if (user && !user.name && !user.nickname) { + const skipOnboarding = getCookie('quilombo.skipOnboarding'); + if (skipOnboarding === 'true') return; + onOpen(); + } + }, [user, onOpen]); + + const handleClose = useCallback( + async (target: string) => { + setCookie('quilombo.skipOnboarding', true, { + expires: new Date(Date.now() + 1000 * 60 * 60 * 24), // we give the user 24 hrs before reminding again + }); + + router.push(target); + }, + [router], + ); + + return ( + + + {(onClose) => ( + <> + Hi There! Que bom te ver. + +

+ Nice to meet you! You probably just signed up or haven't filled out some basic information, yet, so + we don't know how to address you. +

+

Do you have a nickname? Are you a mestre or mestra ... or initiante? Which group do you belong to?

+

+ Head over to 'My Profile' and let the community know who you are. You can also do this + later at any time by going to 'My Profile' in the user menu on the top right. +

+
+ + + + + + )} +
+
+ ); +}; + +export default OnboardingModal; diff --git a/packages/app/components/SignInForm.tsx b/packages/app/components/SignInForm.tsx index 6e8a58b..57cd445 100755 --- a/packages/app/components/SignInForm.tsx +++ b/packages/app/components/SignInForm.tsx @@ -1,15 +1,19 @@ 'use client'; import { Button } from '@nextui-org/button'; +import { useDisclosure } from '@nextui-org/use-disclosure'; import { useSession } from 'next-auth/react'; import { useAccount } from 'wagmi'; import useAuth from '@/hooks/useAuth'; +import { Link } from '@nextui-org/link'; import ErrorText from './ErrorText'; +import SignInHelpModal from './SignInHelpModal'; const SignInForm = () => { const { data: session } = useSession(); - const { address } = useAccount(); + const { isOpen, onOpenChange } = useDisclosure(); + const { address, isConnecting, isConnected } = useAccount(); const { signIn, connect, @@ -18,31 +22,39 @@ const SignInForm = () => { return (
-

- Login to the Quilombo App -

+

Quilombo Login

{!address && ( -
-

- - Silk - {' '} - is a digital identity app that allows you to securely use the Quilombo App. Please click the below button - and follow the instructions to create a Silk account or log into an existing one. -

-
)} - {address && !session && ( + {address && isConnected && !session && (

Your Silk account is connected. Fantastic!

In order to complete Login we ask you to sign a message. By doing so you accept the Terms and Conditions of - the app. Click the below button to proceed. + the Quilombo app. Click the button below to proceed.

)} {error && } +
); }; diff --git a/packages/app/components/SignInHelpModal.tsx b/packages/app/components/SignInHelpModal.tsx new file mode 100644 index 0000000..766ca23 --- /dev/null +++ b/packages/app/components/SignInHelpModal.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { Button } from '@nextui-org/button'; +import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@nextui-org/modal'; + +type Props = { isOpen: boolean; onOpenChange: () => void }; + +/** + * + * @returns + */ +const SignInHelpModal = ({ isOpen, onOpenChange }: Props) => { + return ( + + + {(onClose) => ( + <> + Hi There! + +

+ Here at Quilombo we use a ‘Digital Identity’ for login and to manage your Capoeira + profile. A digital identity is a secure way to prove who you are online. This is done with the help of + our partner{' '} + + Silk + + . +

+

+ If you don't have a Silk account, yet, you can create one in a few easy steps and it's free: +

+
    +
  1. + Click on Connect with Silk +
  2. +
  3. Enter your email and continue
  4. +
  5. Choose a name and a password and submit
  6. +
  7. Check your email inbox for a confirmation link from Silk and click it
  8. +
  9. + Complete a bot challenge with a slider to proof you're human. +
  10. +
+

+ Important: logging into your Silk account always requires both steps: your password and + clicking a confirmation link sent via email. +

+

+ After your Silk account is connected, you can sign in to Quilombo by{' '} + ‘signing a message’. This digital signature proves that you have unlocked your Silk + account 😉 +

+
+ + + + + )} +
+
+ ); +}; + +export default SignInHelpModal; diff --git a/packages/app/components/axe/Transfer.tsx b/packages/app/components/axe/Transfer.tsx index 7d663da..b409689 100755 --- a/packages/app/components/axe/Transfer.tsx +++ b/packages/app/components/axe/Transfer.tsx @@ -2,7 +2,7 @@ import { Button } from '@nextui-org/button'; import { useDisclosure } from '@nextui-org/modal'; -import { Field, FieldProps, Form, Formik, FormikProps } from 'formik'; +import { Field, Form, Formik, FormikProps } from 'formik'; import { enqueueSnackbar } from 'notistack'; import { useEffect, useState } from 'react'; import { Address, formatUnits, parseUnits } from 'viem'; @@ -80,11 +80,14 @@ const Transfer: React.FC = () => { Available: {formatUnits(axeBalance || BigInt(0), 18)} Axé - - {({ field }: FieldProps) => ( - - )} - +