From 343547bfe48470c5739457b3cd19c55ca202e414 Mon Sep 17 00:00:00 2001 From: MarioBatalha Date: Mon, 31 Mar 2025 01:37:40 +0100 Subject: [PATCH 01/15] =?UTF-8?q?=E2=9C=A8=20feat:=20sign=20in=20call=20en?= =?UTF-8?q?dpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constants/global.ts | 2 + views/sign-in/sign-in-button.tsx | 133 ++++++++++++++++++------------- 2 files changed, 78 insertions(+), 57 deletions(-) create mode 100644 constants/global.ts diff --git a/constants/global.ts b/constants/global.ts new file mode 100644 index 0000000..267d2fb --- /dev/null +++ b/constants/global.ts @@ -0,0 +1,2 @@ +export const BASE_URL = + 'https://apiauthmemezfun-staging.up.railway.app/api/v1/auth/'; diff --git a/views/sign-in/sign-in-button.tsx b/views/sign-in/sign-in-button.tsx index 1a0219c..f383248 100644 --- a/views/sign-in/sign-in-button.tsx +++ b/views/sign-in/sign-in-button.tsx @@ -1,18 +1,22 @@ import { Button, Div } from '@stylin.js/elements'; +import { useRouter } from 'next/router'; import { FC } from 'react'; import { useFormContext } from 'react-hook-form'; import DialogCountdown from '@/components/dialog/dialog-countdown'; import { LoaderSVG } from '@/components/svg'; import { useConnectModal } from '@/components/wallet-button/wallet-button.hook'; +import { RoutesEnum } from '@/constants'; +import { BASE_URL } from '@/constants/global'; import { useDialog } from '@/hooks/use-dialog'; import { SignInFormProps } from './sign-in.types'; const SignInButton: FC = () => { - const { setValue, trigger } = useFormContext(); + const { setValue, trigger, getValues } = useFormContext(); const { dialog, handleClose } = useDialog(); const handleOpenConnectModal = useConnectModal(); + const { push } = useRouter(); const handleCreateProfile = () => { return new Promise((resolve, reject) => { @@ -34,65 +38,80 @@ const SignInButton: FC = () => { fieldsToValidate = ['username', 'password']; const isValid = await trigger(fieldsToValidate); - if (isValid) { - await dialog.promise(handleCreateProfile(), { - success: () => ({ - title: 'Sign-in successful', - button: ( - - ), - ghostButton: ( - - ), - message: 'Now connect your wallet do see all the function', - }), - loading: () => ({ - Icon: , - title: 'Signing...', - message: 'Please wait...', - }), - error: (e) => ({ - title: 'Oops! You can not sign in!', - button: { label: 'Try again', onClick: handleClose }, - message: ( - e.message || - 'Make sure you fill every field correctly and try again.' - ).replace('Invariant failed: ', ''), - ghostButton: { - label: 'Do not want to try again!', - onClick: handleClose, - }, + try { + fetch(`${BASE_URL}/sign-in`, { + method: 'POST', + body: JSON.stringify({ + username: `${getValues('username')}`, + password: `${getValues('password')}`, }), + }).then((res) => { + if (res.ok) { + if (isValid) { + dialog.promise(handleCreateProfile(), { + success: () => ({ + title: 'Sign-in successful', + button: ( + + ), + ghostButton: ( + + ), + message: 'Now connect your wallet do see all the function', + }), + loading: () => ({ + Icon: , + title: 'Signing...', + message: 'Please wait...', + }), + error: (e) => ({ + title: 'Oops! You can not sign in!', + button: { label: 'Try again', onClick: handleClose }, + message: ( + e.message || + 'Make sure you fill every field correctly and try again.' + ).replace('Invariant failed: ', ''), + ghostButton: { + label: 'Do not want to try again!', + onClick: handleClose, + }, + }), + }); + } + push(RoutesEnum.Profile); + } + if (!isValid) return; }); + } catch (error) { + console.error('Error _> ', error); } - if (!isValid) return; }; return ( From 9b29747ab5582d63ce654f4b94e20bbc9eb18733 Mon Sep 17 00:00:00 2001 From: MarioBatalha Date: Mon, 31 Mar 2025 20:48:00 +0100 Subject: [PATCH 02/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20sign=20i?= =?UTF-8?q?n=20call=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 3 +- components/profile/menu-list.tsx | 13 +- constants/global.ts | 2 +- views/sign-in/sign-in-button.tsx | 190 ++++++++++++++------------- views/sign-in/sign-in-validations.ts | 2 +- 5 files changed, 117 insertions(+), 93 deletions(-) diff --git a/.env b/.env index 572274e..7498483 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -NEXT_PUBLIC_NETWORK=testnet \ No newline at end of file +NEXT_PUBLIC_NETWORK=testnet +BASE_URL=https://apiauthmemezfun-staging.up.railway.app/api/v1/auth \ No newline at end of file diff --git a/components/profile/menu-list.tsx b/components/profile/menu-list.tsx index 7798888..51b0492 100644 --- a/components/profile/menu-list.tsx +++ b/components/profile/menu-list.tsx @@ -1,8 +1,12 @@ import { useDisconnectWallet } from '@mysten/dapp-kit'; import { Div } from '@stylin.js/elements'; +import { useRouter } from 'next/router'; import { not } from 'ramda'; import { FC, useState } from 'react'; +import { RoutesEnum } from '@/constants'; +import { BASE_URL } from '@/constants/global'; + import { DocIDSVG, ExplorerSVG, @@ -23,6 +27,13 @@ const MenuList: FC = () => { const [isAccountMenuOpen, setIsAccountMenuOpen] = useState(false); const [isExplorerMenuOpen, setIsExplorerMenuOpen] = useState(false); const { mutate: disconnectWallet } = useDisconnectWallet(); + const { push } = useRouter(); + + const handleDisconnectWallet = () => { + disconnectWallet(); + fetch(`${BASE_URL}/sign-out`).then((res) => res.json()); + push(RoutesEnum.Home); + }; return (
@@ -58,7 +69,7 @@ const MenuList: FC = () => { Icon={LogoutSVG} title="Disconnect" color="#E85965" - onClick={() => disconnectWallet()} + onClick={handleDisconnectWallet} />
); diff --git a/constants/global.ts b/constants/global.ts index 267d2fb..09eca03 100644 --- a/constants/global.ts +++ b/constants/global.ts @@ -1,2 +1,2 @@ export const BASE_URL = - 'https://apiauthmemezfun-staging.up.railway.app/api/v1/auth/'; + 'https://apiauthmemezfun-staging.up.railway.app/api/v1/auth'; diff --git a/views/sign-in/sign-in-button.tsx b/views/sign-in/sign-in-button.tsx index f383248..7bf93c7 100644 --- a/views/sign-in/sign-in-button.tsx +++ b/views/sign-in/sign-in-button.tsx @@ -1,6 +1,6 @@ -import { Button, Div } from '@stylin.js/elements'; +import { Button, Div, P } from '@stylin.js/elements'; import { useRouter } from 'next/router'; -import { FC } from 'react'; +import { FC, useState } from 'react'; import { useFormContext } from 'react-hook-form'; import DialogCountdown from '@/components/dialog/dialog-countdown'; @@ -13,109 +13,121 @@ import { useDialog } from '@/hooks/use-dialog'; import { SignInFormProps } from './sign-in.types'; const SignInButton: FC = () => { - const { setValue, trigger, getValues } = useFormContext(); + const [isError, setIsError] = useState({ status: false, message: '' }); + const { trigger, getValues } = useFormContext(); const { dialog, handleClose } = useDialog(); const handleOpenConnectModal = useConnectModal(); const { push } = useRouter(); - const handleCreateProfile = () => { - return new Promise((resolve, reject) => { - setTimeout(() => { - const isSuccess = Math.random() > 0.5; - if (isSuccess) { - setValue('success', true); - resolve('success'); - return; - } - reject('Error'); - }, 1000); - }); - }; - - const handleSignIn = async () => { + const handleCreateProfile = async () => { let fieldsToValidate: (keyof SignInFormProps)[] = []; fieldsToValidate = ['username', 'password']; const isValid = await trigger(fieldsToValidate); + const username = getValues('username'); + const password = getValues('password'); + + console.log('Base URL _>', BASE_URL); try { - fetch(`${BASE_URL}/sign-in`, { - method: 'POST', - body: JSON.stringify({ - username: `${getValues('username')}`, - password: `${getValues('password')}`, - }), - }).then((res) => { - if (res.ok) { - if (isValid) { - dialog.promise(handleCreateProfile(), { - success: () => ({ - title: 'Sign-in successful', - button: ( - - ), - ghostButton: ( - - ), - message: 'Now connect your wallet do see all the function', - }), - loading: () => ({ - Icon: , - title: 'Signing...', - message: 'Please wait...', - }), - error: (e) => ({ - title: 'Oops! You can not sign in!', - button: { label: 'Try again', onClick: handleClose }, - message: ( - e.message || - 'Make sure you fill every field correctly and try again.' - ).replace('Invariant failed: ', ''), - ghostButton: { - label: 'Do not want to try again!', - onClick: handleClose, - }, - }), - }); + const requestBody = { + username, + password, + }; + if (isValid) { + fetch(`${BASE_URL}/sign-in`, { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody), + }).then((res) => { + console.log('Res _> ', res); + if (res.ok) { + push(RoutesEnum.Profile); } - push(RoutesEnum.Profile); - } - if (!isValid) return; - }); + }); + } + if (!isValid) return; } catch (error) { - console.error('Error _> ', error); + setIsError({ status: true, message: 'User does not exist' }); + console.log('Error _> ', error); } }; + const handleSignIn = async () => { + dialog.promise(handleCreateProfile(), { + success: () => ({ + title: 'Sign-in successful', + button: ( + + ), + ghostButton: ( + + ), + message: 'Now connect your wallet do see all the function', + }), + loading: () => ({ + Icon: , + title: 'Signing...', + message: 'Please wait...', + }), + error: (e) => ({ + title: 'Oops! You can not sign in!', + button: { label: 'Try again', onClick: handleClose }, + message: ( + e.message || 'Make sure you fill every field correctly and try again.' + ).replace('Invariant failed: ', ''), + ghostButton: { + label: 'Do not want to try again!', + onClick: handleClose, + }, + }), + }); + }; + return ( -
+
+ {isError.status && ( +

+ {isError.message} +

+ )}
- - +
+ + +
{ - const [isError, setIsError] = useState({ status: false, message: '' }); - const { trigger, getValues } = useFormContext(); + const { set: setCookie } = useCookie(MEMEZ_FUN_TOKEN_AUTH); + const { getValues, handleSubmit } = useFormContext(); const { dialog, handleClose } = useDialog(); const handleOpenConnectModal = useConnectModal(); - const { push } = useRouter(); const handleCreateProfile = async () => { - let fieldsToValidate: (keyof SignInFormProps)[] = []; - - fieldsToValidate = ['username', 'password']; - const isValid = await trigger(fieldsToValidate); - const username = getValues('username'); - const password = getValues('password'); - - try { - const requestBody = { - username, - password, - }; - if (isValid) { - fetch(`${BASE_URL}/sign-in`, { - method: 'POST', - mode: 'cors', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }).then((res) => { - if (res.ok) { - push(RoutesEnum.Profile); - } - }); + const { username, password } = getValues(); + const body = JSON.stringify({ + username, + password, + }); + return await fetch(`${process.env.NEXT_PUBLIC_AUTH_URL}/sign-in`, { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + body, + }).then(async (res) => { + const response = await res.json(); + if (!res.ok) { + throw new Error(`${response.error}: ${response.message}`); } - if (!isValid) return; - } catch (error) { - setIsError({ status: true, message: 'User does not exist' }); - console.log('Error _> ', error); - } + setCookie(response.accessToken); + return; + }); }; - const handleSignIn = async () => { + const handleSignIn = () => { dialog.promise(handleCreateProfile(), { success: () => ({ title: 'Sign-in successful', @@ -120,11 +107,6 @@ const SignInButton: FC = () => { flexDirection="column" justifyContent="center" > - {isError.status && ( -

- {isError.message} -

- )}
{ const wallets = useWallets(); - const { push } = useRouter(); const { mutateAsync } = useConnectWallet(); const { dialog, handleClose } = useDialog(); @@ -19,11 +16,6 @@ const ConnectModal: FC = () => { await mutateAsync({ wallet }); }; - const handleSignInNavigation = () => { - push(RoutesEnum.SignIn); - handleClose(); - }; - const handleConnect = (wallet: WalletWithRequiredFeatures) => { dialog.promise(connectWallet(wallet), { success: () => ({ @@ -83,60 +75,15 @@ const ConnectModal: FC = () => { width={['100vw', '33.25rem']} borderRadius={['1rem 1rem 0 0', '1rem']} > -

- {wallets ? 'Select Your Wallet' : 'Go to sign in'} -

- {wallets ? ( -
    - {wallets.map((wallet) => ( -
  • handleConnect(wallet)} - > -
    - {wallet.name} -

    {wallet.name}

    -
    -

    Installed

    -
  • - ))} -
- ) : ( -
    +

    Select Your Wallet

    +
      + {wallets.map((wallet) => (
    • { border="1px solid transparent" justifyContent="space-between" nHover={{ borderColor: '#F5B72280' }} - onClick={handleSignInNavigation} + onClick={() => handleConnect(wallet)} > - Sign in +
      + {wallet.name} +

      {wallet.name}

      +
      +

      Installed

    • -
    - )} + ))} +
); }; diff --git a/components/wallet-button/index.tsx b/components/wallet-button/index.tsx index 1390f85..7fd5e10 100644 --- a/components/wallet-button/index.tsx +++ b/components/wallet-button/index.tsx @@ -1,19 +1,38 @@ -import { useCurrentAccount } from '@mysten/dapp-kit'; +import { useCurrentAccount, useSignPersonalMessage } from '@mysten/dapp-kit'; import { Button, Div } from '@stylin.js/elements'; -import { useRouter } from 'next/router'; -import { FC } from 'react'; +import { FC, useEffect } from 'react'; +import { useLocalStorage } from 'usehooks-ts'; import { WalletSVG } from '@/components/svg'; -import { Routes, RoutesEnum } from '@/constants'; import ConnectedModal from './connected-modal'; +import { useConnectModal } from './wallet-button.hook'; const WalletButton: FC = () => { + const handleOpenConnectModal = useConnectModal(); + const signMessage = useSignPersonalMessage(); + const [signedMessage, setSignedMessage] = useLocalStorage<{ + signature: string; + bytes: string; + } | null>('ww-signed-messages', null); const currentAccount = useCurrentAccount(); - const { push } = useRouter(); - if (currentAccount) return ; + const handleConnectWallet = () => { + handleOpenConnectModal(); + }; + + useEffect(() => { + if (signedMessage || !currentAccount) return; + signMessage + .mutateAsync({ + message: new TextEncoder().encode( + 'Please sign this to make sure verify your identity in our services.' + ), + }) + .then((response) => setSignedMessage(response)); + }, [currentAccount]); + if (currentAccount) return ; return ( - ), - ghostButton: ( - - ), - }), - loading: () => ({ - Icon: , - title: 'Creating...', - message: 'Creating an account...', - }), - error: (e) => ({ - title: 'Oops! You can not create an account!', - button: { label: 'Try again', onClick: handleClose }, - message: ( - e.message || 'Make sure you fill every field correctly and try again.' - ).replace('Invariant failed: ', ''), - ghostButton: { - label: 'Do not want to try again!', - onClick: handleClose, - }, - }), - }); - }; - - return ( -
- -
- ); -}; - -export default CreateProfileButton; diff --git a/views/create-profile/create-profile-validation.ts b/views/create-profile/create-profile-validation.ts deleted file mode 100644 index 0654f75..0000000 --- a/views/create-profile/create-profile-validation.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as yup from 'yup'; - -export const CreateProfileValidationSchema = yup.object({ - firstName: yup.string().required('First name is required'), - lastName: yup.string().required('Last name is required'), - username: yup.string().required('Username is required'), - email: yup - .string() - .email('Invalid email format') - .required('Email is required'), - imageUrl: yup.string().required('Avatar is required'), - password: yup.string().required('Password is required'), - description: yup - .string() - .min(80, 'Must be at least 80 characters') - .required('Description is required'), -}); diff --git a/views/create-profile/create-profile.types.ts b/views/create-profile/create-profile.types.ts deleted file mode 100644 index dad9624..0000000 --- a/views/create-profile/create-profile.types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface CreateProfileFormProps { - email: string; - imageUrl: string; - username: string; - firstName: string; - lastName: string; - password: string; - description: string; -} diff --git a/views/create-profile/index.tsx b/views/create-profile/index.tsx deleted file mode 100644 index 9837c0d..0000000 --- a/views/create-profile/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Div, H1, P } from '@stylin.js/elements'; -import { FC } from 'react'; -import { useFormContext } from 'react-hook-form'; - -import InputField from '@/components/input-field'; -import Layout from '@/components/layout'; -import UploadImage from '@/components/upload-image'; - -import { CreateProfileFormProps } from './create-profile.types'; -import CreateProfileButton from './create-profile-button'; - -const CreateProfile: FC = () => { - const { - register, - formState: { errors }, - } = useFormContext(); - - return ( - -
-
-

- Create profile -

-
-
-
-
-

- Basic Details -

- - - - - - - -
-
- -
-
-
- ); -}; - -export default CreateProfile; diff --git a/views/home/index.tsx b/views/home/index.tsx index 4c2683d..43e91bc 100644 --- a/views/home/index.tsx +++ b/views/home/index.tsx @@ -16,7 +16,7 @@ import { CARDS } from './card.data'; const Home: FC = () => { const { push } = useRouter(); - const handleCreateCoinButtonClick = () => push(Routes[RoutesEnum.CreateCoin]); + const handleCreateCoinButtonClick = () => push(Routes[RoutesEnum.Home]); return ( diff --git a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx index 845c95a..fa0ecbb 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx +++ b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx @@ -7,7 +7,7 @@ import DialogCountdown from '@/components/dialog/dialog-countdown'; import { LoaderSVG } from '@/components/svg'; import { Routes, RoutesEnum } from '@/constants'; import { useDialog } from '@/hooks/use-dialog'; -import { CreateProfileFormProps } from '@/views/create-profile/create-profile.types'; +import { CreateProfileFormProps } from '@/interface'; const EditProfileModalButton: FC = () => { const { trigger } = useFormContext(); diff --git a/views/profile/edit-profile-modal/edit-profile-modal.types.ts b/views/profile/edit-profile-modal/edit-profile-modal.types.ts index d7b1d1c..c5f9739 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal.types.ts +++ b/views/profile/edit-profile-modal/edit-profile-modal.types.ts @@ -1,4 +1,5 @@ export interface IEditProfileForm { + name: string; imageUrl: string; username: string; description: string; diff --git a/views/profile/edit-profile-modal/edit-profile.validations.ts b/views/profile/edit-profile-modal/edit-profile.validations.ts index a3f3f51..fbd4efd 100644 --- a/views/profile/edit-profile-modal/edit-profile.validations.ts +++ b/views/profile/edit-profile-modal/edit-profile.validations.ts @@ -1,6 +1,7 @@ import * as yup from 'yup'; export const editProfileValidationSchema = yup.object().shape({ + name: yup.string().required('Name is required'), username: yup.string().required('Username is required'), imageUrl: yup.string().required('Image is required'), description: yup.string().required('Description is required'), diff --git a/views/profile/edit-profile-modal/index.tsx b/views/profile/edit-profile-modal/index.tsx index 478c2fb..4e4d716 100644 --- a/views/profile/edit-profile-modal/index.tsx +++ b/views/profile/edit-profile-modal/index.tsx @@ -47,6 +47,13 @@ const EditProfileModal = () => { status={errors.imageUrl && 'error'} description={errors.imageUrl?.message} /> + { + const currentAccount = useCurrentAccount(); const clipBoardSuccessMessage = 'Address copied to the clipboard'; + const emailVerified = true; return (
{
- - Name + + name -
+
- Username + username -
- -
+ {emailVerified && ( +
+ +
+ )}
@@ -62,16 +75,21 @@ const UserInfo: FC = () => { px="0.5rem" py="0.25rem" display="flex" + width="8rem" color="#E4E7EB" gap="0.625rem" + cursor="pointer" fontSize="0.75rem" alignItems="center" - borderRadius="0.75rem" textAlign="center" + borderRadius="0.75rem" + justifyContent="center" transition="all 300ms ease-in-out" nHover={{ transform: 'scale(1.05)', color: '#F5B722' }} > - 0x2::sui::SUI + + {formatAddress(currentAccount?.address || '')} +
{ - const currentAccount = useCurrentAccount(); - const { push } = useRouter(); - - if (currentAccount) push('/'); - - return ( - -
-
-

- Sign in -

-

- Welcome back. Select method to log in -

-
-
-
- - - -
-

- Don‘t have an account?{' '} - { - push(Routes[RoutesEnum.CreateProfile]); - }} - > - Create a Profile - -

-
-
-
-
- ); -}; - -export default SignIn; diff --git a/views/sign-in/sign-in-button.tsx b/views/sign-in/sign-in-button.tsx deleted file mode 100644 index 09f5998..0000000 --- a/views/sign-in/sign-in-button.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { useDisconnectWallet } from '@mysten/dapp-kit'; -import { Button, Div } from '@stylin.js/elements'; -import { useRouter } from 'next/router'; -import { FC } from 'react'; -import { useFormContext } from 'react-hook-form'; - -import DialogCountdown from '@/components/dialog/dialog-countdown'; -import { LoaderSVG } from '@/components/svg'; -import { useConnectModal } from '@/components/wallet-button/wallet-button.hook'; -import { MEMEZ_FUN_TOKEN_AUTH, RoutesEnum } from '@/constants'; -import { useCookie } from '@/hooks/use-cookie'; -import { useDialog } from '@/hooks/use-dialog'; - -import { SignInFormProps } from './sign-in.types'; - -const SignInButton: FC = () => { - const { dialog, handleClose } = useDialog(); - const handleOpenConnectModal = useConnectModal(); - const { mutate: disconnectWallet } = useDisconnectWallet(); - const { set: setCookie } = useCookie(MEMEZ_FUN_TOKEN_AUTH); - const { getValues, handleSubmit } = useFormContext(); - const { push } = useRouter(); - - const scheduleTimeToDisconnect = () => { - const oneHour = 60 * 60 * 1000; - const disconnectTimer = setTimeout(() => { - disconnectWallet(); - push(RoutesEnum.SignIn); - }, oneHour); - - return disconnectTimer; - }; - - const onSignIn = async () => { - const { username, password } = getValues(); - const body = JSON.stringify({ - username, - password, - }); - return await fetch(`${process.env.NEXT_PUBLIC_AUTH_URL}/sign-in`, { - method: 'POST', - mode: 'cors', - headers: { - 'Content-Type': 'application/json', - }, - body, - }).then(async (res) => { - const response = await res.json(); - scheduleTimeToDisconnect(); - if (!res.ok) { - throw new Error(`${response.error}: ${response.message}`); - } - setCookie(response.accessToken); - return; - }); - }; - - const handleSignIn = () => { - dialog.promise(onSignIn(), { - success: () => ({ - title: 'Sign-in successful', - button: ( - - ), - ghostButton: ( - - ), - message: 'Now connect your wallet do see all the function', - }), - loading: () => ({ - Icon: , - title: 'Signing...', - message: 'Please wait...', - }), - error: (e) => ({ - title: 'Oops! You can not sign in!', - button: { label: 'Try again', onClick: handleClose }, - message: ( - e.message || 'Make sure you fill every field correctly and try again.' - ).replace('Invariant failed: ', ''), - ghostButton: { - label: 'Do not want to try again!', - onClick: handleClose, - }, - }), - }); - }; - - return ( -
- -
- ); -}; - -export default SignInButton; diff --git a/views/sign-in/sign-in-form.tsx b/views/sign-in/sign-in-form.tsx deleted file mode 100644 index 46933f0..0000000 --- a/views/sign-in/sign-in-form.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { Button, Div, Hr, P, Span } from '@stylin.js/elements'; -import { FC } from 'react'; -import { useFormContext } from 'react-hook-form'; - -import InputField from '@/components/input-field'; -import { XSVG } from '@/components/svg'; - -import { SignInFormProps } from './sign-in.types'; - -const SignInForm: FC = () => { - const { - register, - formState: { errors }, - } = useFormContext(); - return ( -
-
- -
-
-

Or

-
-
-
- - -
-
-
- ); -}; - -export default SignInForm; diff --git a/views/sign-in/sign-in-validations.ts b/views/sign-in/sign-in-validations.ts deleted file mode 100644 index 1e9b224..0000000 --- a/views/sign-in/sign-in-validations.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as yup from 'yup'; - -export const SignInValidationSchema = yup.object({ - username: yup.string().required('Username is required'), - password: yup - .string() - .required('Password is required') - .min(6, 'Must be at least 8 characters') - .matches(/[A-Z]/, 'Must contain an uppercase letter') - .matches(/[0-9]/, 'Must contain a number'), -}); diff --git a/views/sign-in/sign-in.types.ts b/views/sign-in/sign-in.types.ts deleted file mode 100644 index 644a574..0000000 --- a/views/sign-in/sign-in.types.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface CreateProfileProps { - avatar: string; - userName: string; - description: string; -} - -export interface SignInProps { - username: string; - password: string; -} - -export enum SignInStepEnum { - CreateProfileProps, - SignInProps, -} - -export interface SignInHeaderProps { - title: string; - description?: string; - step: SignInStepEnum; -} - -export interface SignInFormProps { - username: string; - password: string; - success?: boolean; -} From 1ff066da2ed10efb1770a2fbc3c158e15eb96f0d Mon Sep 17 00:00:00 2001 From: KipandaJr Date: Thu, 10 Apr 2025 23:38:32 +0100 Subject: [PATCH 10/15] :construction: feat: wip --- components/profile/menu-list.tsx | 9 ++ components/wallet-button/index.tsx | 135 +++++++++++++++++++++++++---- hooks/use-cookie/index.ts | 2 + 3 files changed, 129 insertions(+), 17 deletions(-) diff --git a/components/profile/menu-list.tsx b/components/profile/menu-list.tsx index 4aa0a4c..a6a7199 100644 --- a/components/profile/menu-list.tsx +++ b/components/profile/menu-list.tsx @@ -2,6 +2,9 @@ import { useDisconnectWallet } from '@mysten/dapp-kit'; import { Div } from '@stylin.js/elements'; import { not } from 'ramda'; import { FC, useState } from 'react'; +import { useLocalStorage } from 'usehooks-ts'; + +import { MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; import { DocIDSVG, @@ -24,7 +27,13 @@ const MenuList: FC = () => { const [isAccountMenuOpen, setIsAccountMenuOpen] = useState(false); const [isExplorerMenuOpen, setIsExplorerMenuOpen] = useState(false); + const [, setSignedPM] = useLocalStorage<{ + signature: string; + message: string; + cookies?: unknown; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); const handleDisconnectWallet = () => { + setSignedPM({ signature: '', message: '' }); disconnectWallet(); }; diff --git a/components/wallet-button/index.tsx b/components/wallet-button/index.tsx index 7fd5e10..1b9c27f 100644 --- a/components/wallet-button/index.tsx +++ b/components/wallet-button/index.tsx @@ -1,38 +1,139 @@ -import { useCurrentAccount, useSignPersonalMessage } from '@mysten/dapp-kit'; -import { Button, Div } from '@stylin.js/elements'; -import { FC, useEffect } from 'react'; +import { + useCurrentAccount, + useDisconnectWallet, + useSignPersonalMessage, +} from '@mysten/dapp-kit'; +import { Button, Div, Img } from '@stylin.js/elements'; +import { not } from 'ramda'; +import { FC, useEffect, useState } from 'react'; import { useLocalStorage } from 'usehooks-ts'; +import { v4 } from 'uuid'; -import { WalletSVG } from '@/components/svg'; +import { LoaderSVG, MemeZLogoSVG, WalletSVG } from '@/components/svg'; +import { MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; +import { useDialog } from '@/hooks/use-dialog'; import ConnectedModal from './connected-modal'; import { useConnectModal } from './wallet-button.hook'; const WalletButton: FC = () => { const handleOpenConnectModal = useConnectModal(); - const signMessage = useSignPersonalMessage(); - const [signedMessage, setSignedMessage] = useLocalStorage<{ - signature: string; - bytes: string; - } | null>('ww-signed-messages', null); const currentAccount = useCurrentAccount(); + const { mutate: disconnectWallet } = useDisconnectWallet(); + const [signing, setSigning] = useState(false); + const { dialog, handleClose } = useDialog(); + const { mutate: signPersonalMessage } = useSignPersonalMessage(); + const [signedPM, setSignedPM] = useLocalStorage<{ + signature: string; + message: string; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); const handleConnectWallet = () => { handleOpenConnectModal(); }; useEffect(() => { - if (signedMessage || !currentAccount) return; - signMessage - .mutateAsync({ - message: new TextEncoder().encode( - 'Please sign this to make sure verify your identity in our services.' - ), - }) - .then((response) => setSignedMessage(response)); + if (signedPM.signature || signedPM.message || !currentAccount) return; + + const newMessage = v4(); + + signPersonalMessage( + { + message: new TextEncoder().encode(newMessage), + }, + { + onSuccess: (result) => { + setSigning(not); + setSignedPM({ + signature: result.signature, + message: newMessage, + }); + }, + onError: () => disconnectWallet(), + } + ); }, [currentAccount]); + useEffect(() => { + if (!signedPM.signature || !signedPM.message || !currentAccount || !signing) + return; + const signIn = async () => { + await fetch(`${process.env.NEXT_PUBLIC_BASE_URL!}/auth/sign-in`, { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + message: signedPM.message, + signature: signedPM.signature, + address: currentAccount.address, + }), + }) + .then(async (res) => { + if (!res.ok) { + disconnectWallet(); + throw new Error('signin error'); + } + + console.log(res.headers.getSetCookie(), '>>>res'); + }) + .catch(() => { + disconnectWallet(); + setSignedPM({ + signature: '', + message: '', + }); + }); + setSigning(not); + }; + dialog.promise(signIn(), { + success: () => ({ + timeout: 10000, + title: 'Sign in!', + button: { label: 'Continue browsing', onClick: handleClose }, + message: 'Signin successfully', + Icon: ( +
+ +
+ ), + }), + loading: () => ({ + Icon: , + title: 'Signing...', + message: 'Hang tight! Signing. Please wait', + }), + error: (e) => ({ + title: 'Oops! You could not Connect!', + button: { label: 'Try again', onClick: () => signIn() }, + message: + e.message || + 'Sigin-in failed. Try to refresh the page, double-check your inputs, or reconnect your wallet.', + ghostButton: { + label: 'Do not want to connect my wallet', + onClick: handleClose, + }, + Icon: ( + Error + ), + }), + }); + }, [signing]); + if (currentAccount) return ; + return (
{ const handleOpenConnectModal = useConnectModal(); const signMessage = useSignPersonalMessage(); - const [signedMessage, setSignedMessage] = useLocalStorage<{ + const [userAuth, setUserAuth] = useLocalStorage<{ signature: string; bytes: string; } | null>('ww-signed-messages', null); @@ -22,14 +22,14 @@ const WalletButton: FC = () => { }; useEffect(() => { - if (signedMessage || !currentAccount) return; + if (userAuth || !currentAccount) return; signMessage .mutateAsync({ message: new TextEncoder().encode( 'Please sign this to make sure verify your identity in our services.' ), }) - .then((response) => setSignedMessage(response)); + .then((response) => setUserAuth(response)); }, [currentAccount]); if (currentAccount) return ; diff --git a/interface/index.ts b/interface/index.ts index 1f1cd64..74c74dc 100644 --- a/interface/index.ts +++ b/interface/index.ts @@ -21,3 +21,9 @@ export interface CreateProfileFormProps { password: string; description: string; } + +export interface UserDetailsProps extends CreateProfileFormProps { + following: number; + followers: number; + emailVerified: boolean; +} diff --git a/views/profile/user-info/index.tsx b/views/profile/user-info/index.tsx index 8a8dfe6..25fee75 100644 --- a/views/profile/user-info/index.tsx +++ b/views/profile/user-info/index.tsx @@ -1,15 +1,47 @@ import { useCurrentAccount } from '@mysten/dapp-kit'; import { formatAddress } from '@mysten/sui/utils'; import { Div, Img, Span } from '@stylin.js/elements'; -import { FC } from 'react'; +import { FC, useEffect, useState } from 'react'; import { CopySVG, VerifiedSVG } from '@/components/svg'; +import { UserDetailsProps } from '@/interface'; import { copyToClipboard } from '@/utils'; const UserInfo: FC = () => { const currentAccount = useCurrentAccount(); const clipBoardSuccessMessage = 'Address copied to the clipboard'; - const emailVerified = true; + const [user, setUser] = useState(); + // const [userAuth, setUserAuth] = useLocalStorage<{ + // signature: string; + // bytes: string; + // } | null>('user-wallet-info', null); + + const userMessage = 'Hello world'; + const userSignature = + 'ABbtymCUeVOfA2OSYy2+pJrVC14eF/hCkkRF4uYQnrc1dMM9QFotXXe0OhrNsXEsJDtAKJs7w7Pssy5LrwHjTwe544a1OxTZsL7XdFoNTtClAUXDEoDgKXWUS5GciuevHQ=='; + + console.log('Wallet address _> ', currentAccount?.address); + + const userPrifleData = () => { + fetch( + `${process.env.NEXT_PUBLIC_AUTH_URL}/users/${currentAccount?.address}`, + { + method: 'GET', + mode: 'cors', + headers: { + signature: userSignature, + message: userMessage, + address: `${currentAccount?.address}`, + }, + } + ) + .then((res) => res.json()) + .then((data) => setUser(data)); + }; + + useEffect(() => { + if (currentAccount) return userPrifleData(); + }, [currentAccount]); return (
{ justifyContent="center" > - name + {`${user?.firstName === '' && user?.lastName === '' && 'Unknown'} `}
{ alignItems="center" > - username + {user?.username} - {emailVerified && ( + {user?.emailVerified && (
From 756ab7f28b03d4707cf975db98278ca5cb0f0b71 Mon Sep 17 00:00:00 2001 From: MarioBatalha Date: Fri, 11 Apr 2025 04:07:53 +0100 Subject: [PATCH 12/15] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20profile=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/profile/profile-info.tsx | 45 +++++++++--------- components/wallet-button/index.tsx | 4 +- constants/network.ts | 1 + interface/index.ts | 2 +- .../edit-profile-modal-button.tsx | 34 +++++++------- .../edit-profile-modal.types.ts | 2 +- .../edit-profile.validations.ts | 2 +- views/profile/edit-profile-modal/index.tsx | 46 +++++++++++++++++-- views/profile/index.tsx | 33 ++++++------- views/profile/user-info/index.tsx | 22 ++++----- 10 files changed, 112 insertions(+), 79 deletions(-) diff --git a/components/profile/profile-info.tsx b/components/profile/profile-info.tsx index 61dd52e..74ce497 100644 --- a/components/profile/profile-info.tsx +++ b/components/profile/profile-info.tsx @@ -4,8 +4,14 @@ import { Div, Img, Span } from '@stylin.js/elements'; import { useRouter } from 'next/router'; import { FC, useEffect, useState } from 'react'; import toast from 'react-hot-toast'; +import { useLocalStorage } from 'usehooks-ts'; -import { Routes, RoutesEnum } from '@/constants'; +import { + BASE_URL, + MEMEZ_FUN_TOKEN_AUTH, + Routes, + RoutesEnum, +} from '@/constants'; import { useCoinBalance } from '@/hooks/use-coin-balance'; import { UserDetailsProps } from '@/interface'; import { FixedPointMath } from '@/lib/entities/fixed-point-math'; @@ -16,30 +22,21 @@ const ProfileInfo: FC = () => { const { push } = useRouter(); const currentAccount = useCurrentAccount(); const [user, setUser] = useState(); - // const [userAuth, setUserAuth] = useLocalStorage<{ - // signature: string; - // bytes: string; - // } | null>('user-wallet-info', null); - - const userMessage = 'Hello world'; - const userSignature = - 'ABbtymCUeVOfA2OSYy2+pJrVC14eF/hCkkRF4uYQnrc1dMM9QFotXXe0OhrNsXEsJDtAKJs7w7Pssy5LrwHjTwe544a1OxTZsL7XdFoNTtClAUXDEoDgKXWUS5GciuevHQ=='; - - console.log('Wallet address _> ', currentAccount?.address); + const [signedPM] = useLocalStorage<{ + signature: string; + message: string; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); const userPrifleData = () => { - fetch( - `${process.env.NEXT_PUBLIC_AUTH_URL}/users/${currentAccount?.address}`, - { - method: 'GET', - mode: 'cors', - headers: { - signature: userSignature, - message: userMessage, - address: `${currentAccount?.address}`, - }, - } - ) + fetch(`${BASE_URL}/users/${currentAccount?.address}`, { + method: 'GET', + mode: 'cors', + headers: { + message: signedPM.message, + signature: signedPM.signature, + address: currentAccount?.address ?? '', + }, + }) .then((res) => res.json()) .then((data) => setUser(data)); }; @@ -89,7 +86,7 @@ const ProfileInfo: FC = () => { height="100%" objectFit="cover" borderRadius="100%" - src="/user-default-memez-fun.png" + src={user?.imageUrl} />
{ if (!signedPM.signature || !signedPM.message || !currentAccount || !signing) return; const signIn = async () => { - await fetch(`${process.env.NEXT_PUBLIC_BASE_URL!}/auth/sign-in`, { + await fetch(`${BASE_URL}/auth/sign-in`, { method: 'POST', credentials: 'same-origin', headers: { diff --git a/constants/network.ts b/constants/network.ts index 550c567..de1f3f3 100644 --- a/constants/network.ts +++ b/constants/network.ts @@ -4,3 +4,4 @@ export enum Network { } export const NETWORK = process.env.NEXT_PUBLIC_NETWORK as Network; +export const BASE_URL = process.env.NEXT_PUBLIC_AUTH_URL as string; diff --git a/interface/index.ts b/interface/index.ts index 74c74dc..8e356d2 100644 --- a/interface/index.ts +++ b/interface/index.ts @@ -19,7 +19,7 @@ export interface CreateProfileFormProps { firstName: string; lastName: string; password: string; - description: string; + bio: string; } export interface UserDetailsProps extends CreateProfileFormProps { diff --git a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx index fa0ecbb..4ad5d8d 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx +++ b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx @@ -5,27 +5,29 @@ import { useFormContext } from 'react-hook-form'; import DialogCountdown from '@/components/dialog/dialog-countdown'; import { LoaderSVG } from '@/components/svg'; -import { Routes, RoutesEnum } from '@/constants'; +import { BASE_URL, Routes, RoutesEnum } from '@/constants'; import { useDialog } from '@/hooks/use-dialog'; -import { CreateProfileFormProps } from '@/interface'; + +import { IEditProfileForm } from './edit-profile-modal.types'; const EditProfileModalButton: FC = () => { - const { trigger } = useFormContext(); + const { trigger, getValues } = useFormContext(); + const { username, name, bio, imageUrl } = getValues(); const { dialog, handleClose } = useDialog(); const { push } = useRouter(); - const handleCreateProfile = () => { - return new Promise((resolve, reject) => { - setTimeout(() => { - const isSuccess = Math.random() > 0.5; - if (isSuccess) { - handleClose(); - resolve('success'); - return; - } - reject('Error'); - }, 1000); - }); + + const saveEditProfile = async () => { + fetch(`${BASE_URL}/users`, { + method: 'PATCH', + mode: 'cors', + body: JSON.stringify({ + name: name, + avatar: imageUrl, + bio: bio, + username: username, + }), + }).then((res) => res.ok); }; const handleEditProfiel = async () => { @@ -33,7 +35,7 @@ const EditProfileModalButton: FC = () => { if (!isValid) return; - await dialog.promise(handleCreateProfile(), { + await dialog.promise(saveEditProfile(), { success: () => ({ title: 'Profile Edited', message: 'Profile successfully edited', diff --git a/views/profile/edit-profile-modal/edit-profile-modal.types.ts b/views/profile/edit-profile-modal/edit-profile-modal.types.ts index c5f9739..5dc0be6 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal.types.ts +++ b/views/profile/edit-profile-modal/edit-profile-modal.types.ts @@ -2,5 +2,5 @@ export interface IEditProfileForm { name: string; imageUrl: string; username: string; - description: string; + bio: string; } diff --git a/views/profile/edit-profile-modal/edit-profile.validations.ts b/views/profile/edit-profile-modal/edit-profile.validations.ts index fbd4efd..09bee13 100644 --- a/views/profile/edit-profile-modal/edit-profile.validations.ts +++ b/views/profile/edit-profile-modal/edit-profile.validations.ts @@ -4,5 +4,5 @@ export const editProfileValidationSchema = yup.object().shape({ name: yup.string().required('Name is required'), username: yup.string().required('Username is required'), imageUrl: yup.string().required('Image is required'), - description: yup.string().required('Description is required'), + bio: yup.string().required('Description is required'), }); diff --git a/views/profile/edit-profile-modal/index.tsx b/views/profile/edit-profile-modal/index.tsx index 4e4d716..32e9937 100644 --- a/views/profile/edit-profile-modal/index.tsx +++ b/views/profile/edit-profile-modal/index.tsx @@ -1,9 +1,14 @@ import { yupResolver } from '@hookform/resolvers/yup'; +import { useCurrentAccount } from '@mysten/dapp-kit'; import { Div, H1, P } from '@stylin.js/elements'; +import { useEffect, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; +import { useLocalStorage } from 'usehooks-ts'; import InputField from '@/components/input-field'; import UploadImage from '@/components/upload-image'; +import { BASE_URL, MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; +import { UserDetailsProps } from '@/interface'; import { editProfileValidationSchema } from './edit-profile.validations'; import { IEditProfileForm } from './edit-profile-modal.types'; @@ -17,9 +22,44 @@ const EditProfileModal = () => { }); const { register, + setValue, formState: { errors }, } = form; + const currentAccount = useCurrentAccount(); + const [user, setUser] = useState(); + const [signedPM] = useLocalStorage<{ + signature: string; + message: string; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); + const userPrifleData = () => { + fetch(`${BASE_URL}/users/${currentAccount?.address}`, { + method: 'GET', + mode: 'cors', + headers: { + message: signedPM?.message, + signature: signedPM?.signature, + address: currentAccount?.address ?? '', + }, + }) + .then((res) => res.json()) + .then((data) => setUser(data)); + }; + + const updateFields = () => { + setValue('imageUrl', user?.imageUrl ?? ''); + setValue('name', user?.firstName ?? ''); + setValue('username', user?.username ?? ''); + setValue('bio', user?.bio ?? ''); + }; + + useEffect(() => { + if (currentAccount) return userPrifleData(); + }, [currentAccount]); + + useEffect(() => { + updateFields(); + }, [user]); return (
@@ -64,10 +104,10 @@ const EditProfileModal = () => {
diff --git a/views/profile/index.tsx b/views/profile/index.tsx index 94f3c22..e12534d 100644 --- a/views/profile/index.tsx +++ b/views/profile/index.tsx @@ -1,8 +1,10 @@ import { useCurrentAccount } from '@mysten/dapp-kit'; import { Div, Span } from '@stylin.js/elements'; import { FC, useEffect, useState } from 'react'; +import { useLocalStorage } from 'usehooks-ts'; import { Layout } from '@/components'; +import { BASE_URL, MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; import { useIsMobile } from '@/hooks/use-is-mobile'; import { UserDetailsProps } from '@/interface'; @@ -20,26 +22,21 @@ const Profile: FC = () => { const [tabSelect, setTabSelect] = useState(ProfileTabsEnum.History); const currentAccount = useCurrentAccount(); const [user, setUser] = useState(); - - const userMessage = 'Hello world'; - const userSignature = - 'ABbtymCUeVOfA2OSYy2+pJrVC14eF/hCkkRF4uYQnrc1dMM9QFotXXe0OhrNsXEsJDtAKJs7w7Pssy5LrwHjTwe544a1OxTZsL7XdFoNTtClAUXDEoDgKXWUS5GciuevHQ=='; - - console.log('Wallet address _> ', currentAccount?.address); + const [signedPM] = useLocalStorage<{ + signature: string; + message: string; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); const userPrifleData = () => { - fetch( - `${process.env.NEXT_PUBLIC_AUTH_URL}/users/${currentAccount?.address}`, - { - method: 'GET', - mode: 'cors', - headers: { - signature: userSignature, - message: userMessage, - address: `${currentAccount?.address}`, - }, - } - ) + fetch(`${BASE_URL}/users/${currentAccount?.address}`, { + method: 'GET', + mode: 'cors', + headers: { + message: signedPM?.message, + signature: signedPM?.signature, + address: currentAccount?.address ?? '', + }, + }) .then((res) => res.json()) .then((data) => setUser(data)); }; diff --git a/views/profile/user-info/index.tsx b/views/profile/user-info/index.tsx index 25fee75..120962b 100644 --- a/views/profile/user-info/index.tsx +++ b/views/profile/user-info/index.tsx @@ -2,8 +2,10 @@ import { useCurrentAccount } from '@mysten/dapp-kit'; import { formatAddress } from '@mysten/sui/utils'; import { Div, Img, Span } from '@stylin.js/elements'; import { FC, useEffect, useState } from 'react'; +import { useLocalStorage } from 'usehooks-ts'; import { CopySVG, VerifiedSVG } from '@/components/svg'; +import { MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; import { UserDetailsProps } from '@/interface'; import { copyToClipboard } from '@/utils'; @@ -11,16 +13,10 @@ const UserInfo: FC = () => { const currentAccount = useCurrentAccount(); const clipBoardSuccessMessage = 'Address copied to the clipboard'; const [user, setUser] = useState(); - // const [userAuth, setUserAuth] = useLocalStorage<{ - // signature: string; - // bytes: string; - // } | null>('user-wallet-info', null); - - const userMessage = 'Hello world'; - const userSignature = - 'ABbtymCUeVOfA2OSYy2+pJrVC14eF/hCkkRF4uYQnrc1dMM9QFotXXe0OhrNsXEsJDtAKJs7w7Pssy5LrwHjTwe544a1OxTZsL7XdFoNTtClAUXDEoDgKXWUS5GciuevHQ=='; - - console.log('Wallet address _> ', currentAccount?.address); + const [signedPM] = useLocalStorage<{ + signature: string; + message: string; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); const userPrifleData = () => { fetch( @@ -29,8 +25,8 @@ const UserInfo: FC = () => { method: 'GET', mode: 'cors', headers: { - signature: userSignature, - message: userMessage, + message: signedPM.message, + signature: signedPM.signature, address: `${currentAccount?.address}`, }, } @@ -67,7 +63,7 @@ const UserInfo: FC = () => { height="140px" objectFit="cover" borderRadius="100%" - src="/user-default-memez-fun.png" + src={user?.imageUrl} />
Date: Sat, 12 Apr 2025 02:41:58 +0100 Subject: [PATCH 13/15] =?UTF-8?q?=E2=9C=A8=20feat:=20display=20image=20pro?= =?UTF-8?q?file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/profile/profile-info.tsx | 2 +- components/upload-image/index.tsx | 6 +-- .../connected-modal/connected-wallet-item.tsx | 15 ++++-- .../wallet-button/connected-modal/index.tsx | 14 ++++-- interface/index.ts | 9 ++-- .../create-coin/components/dex-card/index.tsx | 4 +- views/create-coin/create-coin-buttons.tsx | 2 +- views/create-coin/create-coin.data.tsx | 8 ++-- views/create-coin/create-coin.types.ts | 4 +- .../steps/create-coin-details-step.tsx | 8 ++-- .../steps/create-coin-review-step.tsx | 4 +- .../steps/create-coin.validations.ts | 2 +- .../edit-profile-modal-button.tsx | 4 +- .../edit-profile-modal.types.ts | 2 +- .../edit-profile.validations.ts | 2 +- views/profile/edit-profile-modal/index.tsx | 9 ++-- views/profile/index.tsx | 9 +++- views/profile/user-info/index.tsx | 46 +++++-------------- 18 files changed, 75 insertions(+), 75 deletions(-) diff --git a/components/profile/profile-info.tsx b/components/profile/profile-info.tsx index 74ce497..2536a24 100644 --- a/components/profile/profile-info.tsx +++ b/components/profile/profile-info.tsx @@ -86,7 +86,7 @@ const ProfileInfo: FC = () => { height="100%" objectFit="cover" borderRadius="100%" - src={user?.imageUrl} + src={user?.avatar} />
= ({ status, isReview, description, - name = 'imageUrl', + name = 'avatar', }) => { const { setValue, control } = useFormContext(); - const currentImageUrl = useWatch({ control, name }); + const currentavatar = useWatch({ control, name }); const [dragging, setDragging] = useState(false); const handleChangeFile: ChangeEventHandler = async (e) => { @@ -94,7 +94,7 @@ const UploadImage: FC = ({ onDragLeave={() => setDragging(false)} onDragOver={(e) => e.preventDefault()} borderStyle={dragging ? 'solid' : 'dashed'} - backgroundImage={`url('${currentImageUrl}')`} + backgroundImage={`url('${currentavatar}')`} borderColor={dragging ? '#F6C853' : '#90939D'} > {!isReview && ( diff --git a/components/wallet-button/connected-modal/connected-wallet-item.tsx b/components/wallet-button/connected-modal/connected-wallet-item.tsx index b0173bd..1193dae 100644 --- a/components/wallet-button/connected-modal/connected-wallet-item.tsx +++ b/components/wallet-button/connected-modal/connected-wallet-item.tsx @@ -4,13 +4,13 @@ import { useSwitchAccount, } from '@mysten/dapp-kit'; import { formatAddress } from '@mysten/sui/utils'; -import { Div, Span, Strong } from '@stylin.js/elements'; +import { Div, Img, Span, Strong } from '@stylin.js/elements'; import { AnimatePresence, motion } from 'motion/react'; import Link from 'next/link'; import { FC } from 'react'; import toast from 'react-hot-toast'; -import { ChevronDownSVG, CopySVG, LogoutSVG, UserSVG } from '@/components/svg'; +import { ChevronDownSVG, CopySVG, LogoutSVG } from '@/components/svg'; import { ExplorerMode } from '@/constants'; import { useCoinBalance } from '@/hooks/use-coin-balance'; import { useGetExplorerUrl } from '@/hooks/use-get-explorer-url'; @@ -28,6 +28,7 @@ const ConnectedWalletItem: FC = ({ account }) => { const { mutate: disconnectWallet } = useDisconnectWallet(); const { balance } = useCoinBalance('0x2::sui::SUI', account.address); + const imageURL = localStorage.getItem('imageURL'); const copyAddress = () => { toast.success('Copied!'); @@ -71,8 +72,14 @@ const ConnectedWalletItem: FC = ({ account }) => { alignItems="center" borderBottom={isCurrentAccount ? '1px solid #242424' : 'none'} > - - + + { const [show, setShow] = useState(false); const { push } = useRouter(); const currentAccount = useCurrentAccount(); + const imageURL = localStorage.getItem('imageURL'); const menuRef = useClickOutsideListenerRef(() => setShow(false) @@ -56,7 +56,15 @@ const ConnectedModal: FC = () => { push(Routes[RoutesEnum.Profile]); }} > - + + +
{formatAddress(currentAccount!.address)}
diff --git a/interface/index.ts b/interface/index.ts index 8e356d2..a274c6c 100644 --- a/interface/index.ts +++ b/interface/index.ts @@ -13,17 +13,16 @@ export interface TimedSuiTransactionBlockResponse } export interface CreateProfileFormProps { - email: string; - imageUrl: string; + email?: string; + avatar: string; username: string; firstName: string; lastName: string; - password: string; bio: string; } export interface UserDetailsProps extends CreateProfileFormProps { - following: number; - followers: number; + following?: number; + followers?: number; emailVerified: boolean; } diff --git a/views/create-coin/components/dex-card/index.tsx b/views/create-coin/components/dex-card/index.tsx index d15d6d8..1254a57 100644 --- a/views/create-coin/components/dex-card/index.tsx +++ b/views/create-coin/components/dex-card/index.tsx @@ -8,7 +8,7 @@ import { DexCardProps } from '../../create-coin.types'; const DexCard: FC> = ({ dexName, onClick, - imageUrl, + avatar, isReview, isSelected, }) => ( @@ -40,7 +40,7 @@ const DexCard: FC> = ({ justifyContent="center" >
- cetus-dex + cetus-dex
diff --git a/views/create-coin/create-coin-buttons.tsx b/views/create-coin/create-coin-buttons.tsx index b599ca4..f073d12 100644 --- a/views/create-coin/create-coin-buttons.tsx +++ b/views/create-coin/create-coin-buttons.tsx @@ -69,7 +69,7 @@ const CreateCoinButtons: FC = () => { let fieldsToValidate: (keyof CreateCoinForm)[] = []; if (currentStep === CreateCoinStepEnum.CoinDetails) { - fieldsToValidate = ['name', 'description', 'dex', 'imageUrl']; + fieldsToValidate = ['name', 'description', 'dex', 'avatar']; } else if (currentStep === CreateCoinStepEnum.DexSocialMedia) { fieldsToValidate = [ 'quoteCoin', diff --git a/views/create-coin/create-coin.data.tsx b/views/create-coin/create-coin.data.tsx index fde01fb..41b67b3 100644 --- a/views/create-coin/create-coin.data.tsx +++ b/views/create-coin/create-coin.data.tsx @@ -39,25 +39,25 @@ export const CreateCoinDexData: ReadonlyArray = [ { dexId: unikey(), dexName: 'Cetus', - imageUrl: + avatar: 'https://assets.coingecko.com/markets/images/1134/large/cetus.png?1706865152', }, { dexId: unikey(), dexName: 'Raidium', - imageUrl: + avatar: 'https://assets.coingecko.com/markets/images/649/large/raydium.jpeg?1706864594', }, { dexId: unikey(), dexName: 'Shadow Exchange', - imageUrl: + avatar: 'https://assets.coingecko.com/markets/images/11810/large/shadow.jpg?1735902352', }, { dexId: unikey(), dexName: 'Hyperliquid', - imageUrl: + avatar: 'https://assets.coingecko.com/markets/images/1571/large/PFP.png?1714470912', }, ]; diff --git a/views/create-coin/create-coin.types.ts b/views/create-coin/create-coin.types.ts index cf7e1e0..e4d4204 100644 --- a/views/create-coin/create-coin.types.ts +++ b/views/create-coin/create-coin.types.ts @@ -47,7 +47,7 @@ export enum CreateCoinStepEnum { export interface DexCardProps { dexId: string; dexName: string; - imageUrl: string; + avatar: string; isReview?: boolean; isSelected?: boolean; onClick?: () => void; @@ -64,7 +64,7 @@ export interface CreateCoinForm { step?: number; success?: boolean; name: string; - imageUrl: string; + avatar: string; description: string; quoteCoin: string; supply: string; diff --git a/views/create-coin/steps/create-coin-details-step.tsx b/views/create-coin/steps/create-coin-details-step.tsx index d968675..466f0cb 100644 --- a/views/create-coin/steps/create-coin-details-step.tsx +++ b/views/create-coin/steps/create-coin-details-step.tsx @@ -32,9 +32,9 @@ const CreateCoinDetailsStep: FC = () => { Basic Details

{ { setValue('dex', dex.dexId); }} diff --git a/views/create-coin/steps/create-coin-review-step.tsx b/views/create-coin/steps/create-coin-review-step.tsx index b484441..c69c518 100644 --- a/views/create-coin/steps/create-coin-review-step.tsx +++ b/views/create-coin/steps/create-coin-review-step.tsx @@ -33,7 +33,7 @@ const CreateCoinReviewStep: FC = () => {

Avatar

- +

Details @@ -71,7 +71,7 @@ const CreateCoinReviewStep: FC = () => { ) diff --git a/views/create-coin/steps/create-coin.validations.ts b/views/create-coin/steps/create-coin.validations.ts index b02c36a..f10c4fe 100644 --- a/views/create-coin/steps/create-coin.validations.ts +++ b/views/create-coin/steps/create-coin.validations.ts @@ -3,7 +3,7 @@ import * as yup from 'yup'; export const createCoinValidationSchema = yup.object({ dex: yup.string().required('Dex is required'), name: yup.string().required('Name is required'), - imageUrl: yup.string().required('Avatar is required'), + avatar: yup.string().required('Avatar is required'), description: yup.string().required('Description is required'), quoteCoin: yup.string().required('Quote coin is required'), supply: yup diff --git a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx index 4ad5d8d..a5c5190 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx +++ b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx @@ -12,7 +12,7 @@ import { IEditProfileForm } from './edit-profile-modal.types'; const EditProfileModalButton: FC = () => { const { trigger, getValues } = useFormContext(); - const { username, name, bio, imageUrl } = getValues(); + const { username, name, bio, avatar } = getValues(); const { dialog, handleClose } = useDialog(); const { push } = useRouter(); @@ -23,7 +23,7 @@ const EditProfileModalButton: FC = () => { mode: 'cors', body: JSON.stringify({ name: name, - avatar: imageUrl, + avatar: avatar, bio: bio, username: username, }), diff --git a/views/profile/edit-profile-modal/edit-profile-modal.types.ts b/views/profile/edit-profile-modal/edit-profile-modal.types.ts index 5dc0be6..2dde190 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal.types.ts +++ b/views/profile/edit-profile-modal/edit-profile-modal.types.ts @@ -1,6 +1,6 @@ export interface IEditProfileForm { name: string; - imageUrl: string; + avatar: string; username: string; bio: string; } diff --git a/views/profile/edit-profile-modal/edit-profile.validations.ts b/views/profile/edit-profile-modal/edit-profile.validations.ts index 09bee13..bff094f 100644 --- a/views/profile/edit-profile-modal/edit-profile.validations.ts +++ b/views/profile/edit-profile-modal/edit-profile.validations.ts @@ -3,6 +3,6 @@ import * as yup from 'yup'; export const editProfileValidationSchema = yup.object().shape({ name: yup.string().required('Name is required'), username: yup.string().required('Username is required'), - imageUrl: yup.string().required('Image is required'), + avatar: yup.string().required('Image is required'), bio: yup.string().required('Description is required'), }); diff --git a/views/profile/edit-profile-modal/index.tsx b/views/profile/edit-profile-modal/index.tsx index 32e9937..cd3e607 100644 --- a/views/profile/edit-profile-modal/index.tsx +++ b/views/profile/edit-profile-modal/index.tsx @@ -31,6 +31,7 @@ const EditProfileModal = () => { signature: string; message: string; }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); + localStorage.setItem('imageURL', user?.avatar ?? ''); const userPrifleData = () => { fetch(`${BASE_URL}/users/${currentAccount?.address}`, { @@ -47,7 +48,7 @@ const EditProfileModal = () => { }; const updateFields = () => { - setValue('imageUrl', user?.imageUrl ?? ''); + setValue('avatar', user?.avatar ?? ''); setValue('name', user?.firstName ?? ''); setValue('username', user?.username ?? ''); setValue('bio', user?.bio ?? ''); @@ -83,9 +84,9 @@ const EditProfileModal = () => { Basic Details

{ borderBottomLeftRadius={['0', '0', '0', '2rem']} borderBottomRightRadius={['0', '0', '0', '2rem']} > - + { +const UserInfo: FC = ({ + avatar, + firstName, + lastName, + username, + emailVerified, +}) => { const currentAccount = useCurrentAccount(); const clipBoardSuccessMessage = 'Address copied to the clipboard'; - const [user, setUser] = useState(); - const [signedPM] = useLocalStorage<{ - signature: string; - message: string; - }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); - - const userPrifleData = () => { - fetch( - `${process.env.NEXT_PUBLIC_AUTH_URL}/users/${currentAccount?.address}`, - { - method: 'GET', - mode: 'cors', - headers: { - message: signedPM.message, - signature: signedPM.signature, - address: `${currentAccount?.address}`, - }, - } - ) - .then((res) => res.json()) - .then((data) => setUser(data)); - }; - - useEffect(() => { - if (currentAccount) return userPrifleData(); - }, [currentAccount]); return (
{ height="140px" objectFit="cover" borderRadius="100%" - src={user?.imageUrl} + src={avatar} />
{ justifyContent="center" > - {`${user?.firstName === '' && user?.lastName === '' && 'Unknown'} `} + {`${firstName === '' && lastName === '' && 'Unknown'} `}
{ alignItems="center" > - {user?.username} + {username} - {user?.emailVerified && ( + {emailVerified && (
From b526611ffa549d3cd231d923aa4af0cefd002b55 Mon Sep 17 00:00:00 2001 From: MarioBatalha Date: Sun, 13 Apr 2025 02:37:22 +0100 Subject: [PATCH 14/15] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20user=20metric?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engagement-counter-item.tsx | 8 +-- .../engagement-counter.types.ts | 4 +- components/engagement-counter/index.tsx | 18 +++--- components/profile/profile-info.tsx | 4 +- .../connected-modal/connected-wallet-item.tsx | 19 +++--- .../wallet-button/connected-modal/index.tsx | 20 +++--- constants/index.ts | 2 + .../edit-profile-modal-button.tsx | 4 +- views/profile/edit-profile-modal/index.tsx | 4 +- views/profile/metric/index.tsx | 64 +++++++++++++++++-- 10 files changed, 102 insertions(+), 45 deletions(-) diff --git a/components/engagement-counter/engagement-counter-item.tsx b/components/engagement-counter/engagement-counter-item.tsx index 282f892..38bfe08 100644 --- a/components/engagement-counter/engagement-counter-item.tsx +++ b/components/engagement-counter/engagement-counter-item.tsx @@ -7,8 +7,8 @@ import { ArrowUpRightFromSquareSVG } from '../svg'; import { EngagementCounterModalItemProps } from './engagement-counter.types'; const EngagementCounterModalItem: FC = ({ - userName, - userAvatar, + username, + avatar, }) => { return (
= ({ }} >
- + - {userName} + {username}
diff --git a/components/engagement-counter/engagement-counter.types.ts b/components/engagement-counter/engagement-counter.types.ts index b81527a..5186598 100644 --- a/components/engagement-counter/engagement-counter.types.ts +++ b/components/engagement-counter/engagement-counter.types.ts @@ -1,6 +1,6 @@ export interface EngagementCounterModalItemProps { - userName: string; - userAvatar: string; + username: string; + avatar: string; } export interface EngagementCounterModalProps { diff --git a/components/engagement-counter/index.tsx b/components/engagement-counter/index.tsx index 470ef60..848e700 100644 --- a/components/engagement-counter/index.tsx +++ b/components/engagement-counter/index.tsx @@ -5,11 +5,13 @@ import { v4 } from 'uuid'; import { useModal } from '@/hooks/use-modal'; import { TimesSVG } from '../svg'; -import { DATA } from './engagement-counter.data'; import { EngagementCounterModalProps } from './engagement-counter.types'; import UserLikeItem from './engagement-counter-item'; -const EngagementCounterModal: FC = ({ title }) => { +const EngagementCounterModal: FC = ({ + title, + data, +}) => { const { handleClose } = useModal(); return ( @@ -37,7 +39,7 @@ const EngagementCounterModal: FC = ({ title }) => { {title} - All(14) + All {data?.length ?? 0}
= ({ title }) => { gap="1rem" pt="1rem" display="flex" - overflowY="scroll" + overflowY="auto" flexDirection="column" className="engagement-scroll" > - {DATA.map(({ userName, userAvatar }) => ( - + {data?.map(({ username, avatar }) => ( + ))}
diff --git a/components/profile/profile-info.tsx b/components/profile/profile-info.tsx index 2536a24..6c16248 100644 --- a/components/profile/profile-info.tsx +++ b/components/profile/profile-info.tsx @@ -8,6 +8,7 @@ import { useLocalStorage } from 'usehooks-ts'; import { BASE_URL, + DEFAULT_IMAGE, MEMEZ_FUN_TOKEN_AUTH, Routes, RoutesEnum, @@ -84,9 +85,10 @@ const ProfileInfo: FC = () => {
= ({ account }) => { const getExplorerUrl = useGetExplorerUrl(); const { mutate: switchAccount } = useSwitchAccount(); const { mutate: disconnectWallet } = useDisconnectWallet(); - const { balance } = useCoinBalance('0x2::sui::SUI', account.address); const imageURL = localStorage.getItem('imageURL'); @@ -72,15 +72,14 @@ const ConnectedWalletItem: FC = ({ account }) => { alignItems="center" borderBottom={isCurrentAccount ? '1px solid #242424' : 'none'} > - - - + { @@ -56,15 +57,14 @@ const ConnectedModal: FC = () => { push(Routes[RoutesEnum.Profile]); }} > - - - +
{formatAddress(currentAccount!.address)}
diff --git a/constants/index.ts b/constants/index.ts index 4b589b5..5444b99 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -5,3 +5,5 @@ export * from './routes'; export const DROPDOWN_DATA = ['Days', 'Weeks', 'Months']; export const MEMEZ_FUN_TOKEN_AUTH = 'MEMEZ_FUN_TOKEN_AUTH'; +export const DEFAULT_IMAGE = + 'https://icons.veryicon.com/png/o/internet--web/prejudice/user-128.png'; diff --git a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx index a5c5190..519437b 100644 --- a/views/profile/edit-profile-modal/edit-profile-modal-button.tsx +++ b/views/profile/edit-profile-modal/edit-profile-modal-button.tsx @@ -30,7 +30,7 @@ const EditProfileModalButton: FC = () => { }).then((res) => res.ok); }; - const handleEditProfiel = async () => { + const handleEditProfile = async () => { const isValid = await trigger(); if (!isValid) return; @@ -112,7 +112,7 @@ const EditProfileModalButton: FC = () => { transition="all .3s" justifyContent="center" border="1px solid #F6C853" - onClick={async () => await handleEditProfiel()} + onClick={async () => handleEditProfile()} nHover={{ transform: 'scale(1.05)', }} diff --git a/views/profile/edit-profile-modal/index.tsx b/views/profile/edit-profile-modal/index.tsx index cd3e607..adfe864 100644 --- a/views/profile/edit-profile-modal/index.tsx +++ b/views/profile/edit-profile-modal/index.tsx @@ -63,7 +63,7 @@ const EditProfileModal = () => { }, [user]); return ( -
+
{ diff --git a/views/profile/metric/index.tsx b/views/profile/metric/index.tsx index 4afa11b..ad7bc7c 100644 --- a/views/profile/metric/index.tsx +++ b/views/profile/metric/index.tsx @@ -1,7 +1,10 @@ +import { useCurrentAccount } from '@mysten/dapp-kit'; import { Div, Span } from '@stylin.js/elements'; -import { FC } from 'react'; +import { FC, useEffect, useState } from 'react'; +import { useLocalStorage } from 'usehooks-ts'; import EngagementCounterModal from '@/components/engagement-counter'; +import { BASE_URL, MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; import { useModal } from '@/hooks/use-modal'; import { MetricProps } from './metric.types'; @@ -13,9 +16,61 @@ const Metric: FC = ({ totalValueCoin, }) => { const { setContent, onClose } = useModal(); + const currentAccount = useCurrentAccount(); + const [followingData, setFollowingData] = useState(); + const [followersData, setFollowersData] = useState(); + const [signedPM] = useLocalStorage<{ + signature: string; + message: string; + cookies?: unknown; + }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); - const handleClick = () => - setContent(, { onClose }); + const getFollowing = () => { + fetch(`${BASE_URL}/users/${currentAccount?.address}/following`, { + method: 'GET', + mode: 'cors', + headers: { + message: signedPM?.message, + signature: signedPM?.signature, + address: currentAccount?.address ?? '', + }, + }) + .then((res) => res.json()) + .then((data) => setFollowingData(data)); + }; + + const getFollowers = () => { + fetch(`${BASE_URL}/users/${currentAccount?.address}/followers`, { + method: 'GET', + mode: 'cors', + headers: { + message: signedPM?.message, + signature: signedPM?.signature, + address: currentAccount?.address ?? '', + }, + }) + .then((res) => res.json()) + .then((data) => setFollowersData(data)); + }; + + useEffect(() => { + if (currentAccount) { + getFollowing(); + getFollowers(); + } + }, [currentAccount]); + + const handleFollowers = () => + setContent( + , + { onClose } + ); + + const handleFollowing = () => + setContent( + , + { onClose } + ); return (
= ({ cursor="pointer" transition="0.3s" alignItems="center" - onClick={handleClick} + onClick={handleFollowers} flexDirection="column" nHover={{ opacity: '0.8', @@ -50,6 +105,7 @@ const Metric: FC = ({
Date: Tue, 15 Apr 2025 21:48:14 +0100 Subject: [PATCH 15/15] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20followers=20pro?= =?UTF-8?q?file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/avatar-group/avatar/index.tsx | 1 + .../engagement-counter-item.tsx | 35 ++++++++++++++++--- .../engagement-counter.types.ts | 8 +++-- components/engagement-counter/index.tsx | 15 +++++--- constants/index.ts | 15 ++++++++ interface/index.ts | 7 ++++ views/profile/edit-profile-modal/index.tsx | 2 +- views/profile/index.tsx | 6 ++-- views/profile/metric/index.tsx | 17 +++++---- views/profile/user-info/profile.types.ts | 6 ++++ 10 files changed, 92 insertions(+), 20 deletions(-) create mode 100644 views/profile/user-info/profile.types.ts diff --git a/components/avatar-group/avatar/index.tsx b/components/avatar-group/avatar/index.tsx index e7ff4a3..f6d002e 100644 --- a/components/avatar-group/avatar/index.tsx +++ b/components/avatar-group/avatar/index.tsx @@ -35,6 +35,7 @@ const Avatar: FC = ({ return (
= ({ - username, + id, + bio, avatar, + username, }) => { + const { handleClose } = useModal(); + const hasAvatar = avatar === '' ? DEFAULT_IMAGE : avatar; + const hasUsername = username === '' ? 'Unknown' : username; + + const onSelect = () => { + const selectedUser = [ + { + id, + bio, + avatar, + username, + }, + ]; + console.log('Selected user _>', selectedUser); + handleClose(); + return selectedUser; + }; return (
= ({ }} >
- + - {username} + {hasUsername}
-
+
; + data?: Array; } diff --git a/components/engagement-counter/index.tsx b/components/engagement-counter/index.tsx index 848e700..3d690a5 100644 --- a/components/engagement-counter/index.tsx +++ b/components/engagement-counter/index.tsx @@ -62,15 +62,22 @@ const EngagementCounterModal: FC = ({
- {data?.map(({ username, avatar }) => ( - + {data?.map(({ id, bio, username, avatar }) => ( + ))}
diff --git a/constants/index.ts b/constants/index.ts index 5444b99..94df53f 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -7,3 +7,18 @@ export const DROPDOWN_DATA = ['Days', 'Weeks', 'Months']; export const MEMEZ_FUN_TOKEN_AUTH = 'MEMEZ_FUN_TOKEN_AUTH'; export const DEFAULT_IMAGE = 'https://icons.veryicon.com/png/o/internet--web/prejudice/user-128.png'; + +export const MOCK_FOLLOWING_DATA = [ + { + id: '0xa2322d5addc8d76e8478bf57826e524f269139924037f6b0309a6c9b56cb4958', + username: 'marcopitra', + avatar: '', + bio: '', + }, + { + id: '0x9936f0786da648d7e22ba5d606a73f12fed8f2d1d9307025cdfeaedef45b64b3', + username: '', + avatar: '', + bio: '', + }, +]; diff --git a/interface/index.ts b/interface/index.ts index a274c6c..5e5d812 100644 --- a/interface/index.ts +++ b/interface/index.ts @@ -21,6 +21,13 @@ export interface CreateProfileFormProps { bio: string; } +export interface UserProps { + id: string; + bio: string; + avatar: string; + username: string; +} + export interface UserDetailsProps extends CreateProfileFormProps { following?: number; followers?: number; diff --git a/views/profile/edit-profile-modal/index.tsx b/views/profile/edit-profile-modal/index.tsx index adfe864..85c6c19 100644 --- a/views/profile/edit-profile-modal/index.tsx +++ b/views/profile/edit-profile-modal/index.tsx @@ -49,7 +49,7 @@ const EditProfileModal = () => { const updateFields = () => { setValue('avatar', user?.avatar ?? ''); - setValue('name', user?.firstName ?? ''); + setValue('name', `${user?.firstName + ' ' + user?.lastName}`); setValue('username', user?.username ?? ''); setValue('bio', user?.bio ?? ''); }; diff --git a/views/profile/index.tsx b/views/profile/index.tsx index 6a4bba8..bc882ee 100644 --- a/views/profile/index.tsx +++ b/views/profile/index.tsx @@ -19,15 +19,15 @@ import UserInfo from './user-info'; const Profile: FC = () => { const isMyProfile = true; const { isMobile } = useIsMobile(); - const [tabSelect, setTabSelect] = useState(ProfileTabsEnum.History); const currentAccount = useCurrentAccount(); + const [tabSelect, setTabSelect] = useState(ProfileTabsEnum.History); const [user, setUser] = useState(); const [signedPM] = useLocalStorage<{ signature: string; message: string; }>(MEMEZ_FUN_TOKEN_AUTH, { signature: '', message: '' }); - const userPrifleData = () => { + const userProfileData = () => { fetch(`${BASE_URL}/users/${currentAccount?.address}`, { method: 'GET', mode: 'cors', @@ -42,7 +42,7 @@ const Profile: FC = () => { }; useEffect(() => { - if (currentAccount) return userPrifleData(); + if (currentAccount) return userProfileData(); }, [currentAccount]); const onSelect = (tab: ProfileTabsEnum) => { diff --git a/views/profile/metric/index.tsx b/views/profile/metric/index.tsx index ad7bc7c..3498c5e 100644 --- a/views/profile/metric/index.tsx +++ b/views/profile/metric/index.tsx @@ -4,7 +4,11 @@ import { FC, useEffect, useState } from 'react'; import { useLocalStorage } from 'usehooks-ts'; import EngagementCounterModal from '@/components/engagement-counter'; -import { BASE_URL, MEMEZ_FUN_TOKEN_AUTH } from '@/constants'; +import { + BASE_URL, + MEMEZ_FUN_TOKEN_AUTH, + MOCK_FOLLOWING_DATA, +} from '@/constants'; import { useModal } from '@/hooks/use-modal'; import { MetricProps } from './metric.types'; @@ -36,7 +40,7 @@ const Metric: FC = ({ }, }) .then((res) => res.json()) - .then((data) => setFollowingData(data)); + .then((userFollowing) => setFollowingData(userFollowing.data)); }; const getFollowers = () => { @@ -50,7 +54,7 @@ const Metric: FC = ({ }, }) .then((res) => res.json()) - .then((data) => setFollowersData(data)); + .then((userFollowers) => setFollowersData(userFollowers.data)); }; useEffect(() => { @@ -58,19 +62,20 @@ const Metric: FC = ({ getFollowing(); getFollowers(); } - }, [currentAccount]); + }, [currentAccount, followersData, followingData]); const handleFollowers = () => setContent( - , + , { onClose } ); const handleFollowing = () => setContent( - , + , { onClose } ); + return (
; + isUserProfile: boolean; +}