diff --git a/src/helpers/getSocialMediaIcon.tsx b/src/helpers/getSocialMediaIcon.tsx new file mode 100644 index 0000000..ca06d62 --- /dev/null +++ b/src/helpers/getSocialMediaIcon.tsx @@ -0,0 +1,24 @@ +import { FaFacebook, FaGithub, FaInstagram, FaLinkedin, FaXTwitter, FaYoutube } from "react-icons/fa6"; +import { GiWorld } from "react-icons/gi"; + +export const getSocialMediaIcon = (platform: string, size?: string) => { + if (!platform) return null; + + if (platform.includes("Youtube")) { + return ; + } else if (platform.includes("Twitter")) { + return ; + } else if (platform.includes("GitHub")) { + return ; + } else if (platform.includes("LinkedIn")) { + return ; + } else if (platform.includes("Website")) { + return ; + } else if (platform.includes("Facebook")) { + return ; + } else if (platform.includes("Instagram")) { + return ; + } + + return null; +}; diff --git a/src/pages/Articles/show/index.tsx b/src/pages/Articles/show/index.tsx index f52283f..1c868ec 100644 --- a/src/pages/Articles/show/index.tsx +++ b/src/pages/Articles/show/index.tsx @@ -21,6 +21,8 @@ import { useCreateFollowUser } from '@hooks/user/useCreateFollowUser' import { useCreateOnFollowUser } from '@hooks/user/useCreateUnFollowUser' import useScrollToTop from '@hooks/useScrollToTop' import { useUser } from '@context/userContext' +import { ISocial } from 'src/types' +import { getSocialMediaIcon } from '@helpers/getSocialMediaIcon' const ShowArticle: FunctionComponent = () => { const { id } = useParams(); @@ -52,21 +54,19 @@ const ShowArticle: FunctionComponent = () => { const renderAuthorSocialLinks = () => ( - {data?.data?.author?.info_details?.gitHub && ( - - - - )} - {data?.data?.author?.info_details?.twitter && ( - - - - )} - {data?.data?.author?.info_details?.website && ( - - + {data?.data?.author?.info_details?.socials?.map((social: ISocial) => ( + + + {getSocialMediaIcon(social.platform, "24px")} + - )} + ))} ); diff --git a/src/pages/Users/Profile/index.tsx b/src/pages/Users/Profile/index.tsx index 1cd172d..47b4325 100644 --- a/src/pages/Users/Profile/index.tsx +++ b/src/pages/Users/Profile/index.tsx @@ -11,14 +11,14 @@ import { Image, Text } from '@chakra-ui/react' -import { FaGithub, FaXTwitter } from 'react-icons/fa6' -import { GiWorld } from 'react-icons/gi' import { Helmet } from 'react-helmet-async' import { FollowCard, Button } from '@components/index' import CoverPic from '@assets/images/cover.jpg' import { useUser } from '@context/userContext' import { colors } from '../../../colors' +import { ISocial } from 'src/types' +import { getSocialMediaIcon } from '@helpers/getSocialMediaIcon' const Profile: FunctionComponent = () => { const { user } = useUser(); @@ -96,47 +96,19 @@ const Profile: FunctionComponent = () => { spacing={3} my={"10px"} > - {user?.data?.gitHub && ( + {user?.data?.socials?.map((social: ISocial) => ( - + {getSocialMediaIcon(social.platform, "24px")} - )} - - {user?.data?.twitter && ( - - - - - - )} - - {user?.data?.website && ( - - - - - - )} + ))} diff --git a/src/pages/Users/Settings/UpdateProfile/index.tsx b/src/pages/Users/Settings/UpdateProfile/index.tsx index c606b5d..b2d3d87 100644 --- a/src/pages/Users/Settings/UpdateProfile/index.tsx +++ b/src/pages/Users/Settings/UpdateProfile/index.tsx @@ -4,12 +4,17 @@ import { Box, Card, CardBody, + Flex, FormControl, FormErrorMessage, FormLabel, IconButton, + Select, SimpleGrid, Spinner, + Tag, + TagCloseButton, + TagLabel, Text } from '@chakra-ui/react' import { Formik, Field } from 'formik' @@ -23,30 +28,54 @@ import { useImageUpload } from '@hooks/useImageUpload' import { updateProfileValidationSchema } from '@validations/updateProfile' import { useUser } from '@context/userContext' import { capitalizeFirstLetter } from '@helpers/capitalize' -import { IUserProfile } from 'src/types' +import { getSocialMediaIcon } from '@helpers/getSocialMediaIcon' +import { ISocial, IUserProfile } from 'src/types' +import { errorNotification } from '@helpers/notification' -const UpdateProfile: FunctionComponent = () => { - const fileInputRef = useRef(null); +const socialOptions = ['Twitter', 'GitHub', 'LinkedIn', 'Facebook', 'Instagram', "Youtube", "Website"]; +const UpdateProfile: FunctionComponent = () => { const { user } = useUser(); const { updateProfileMutation } = useUpdateProfile(); const { updateProfileAvatarMutation } = useUpdateAvatar(); const res = user?.data; + const fileInputRef = useRef(null); + const [socials, setSocials] = useState([]); const [initialValues, setInitialValues] = useState({ fullname: '', username: '', email: '', avatar: '', - twitter: '', - gitHub: '', - website: '', profile_headlines: '', state: '', country: '', bio: '', }); + const handleAddSocial = (platform: string) => { + if (socials.length >= 4) { + errorNotification('You can only add up to 3 social media accounts.'); + return; + } + + if (!socials.some((social) => social.platform === platform)) { + setSocials([...socials, { platform, link: '' }]); + } + }; + + const handleRemoveSocial = (platform: string) => { + setSocials(socials.filter((social) => social.platform !== platform)); + }; + + const handleSocialLinkChange = (platform: string, value: string) => { + setSocials((prev) => + prev.map((social) => + social.platform === platform ? { ...social, link: value } : social + ) + ); + }; + const { handleFileUpload, loading: imageUploadLoading } = useImageUpload({ onSuccess: (data) => { updateProfileAvatarMutation.mutate({ avatar: data?.data?.imageUploadUrl }); @@ -54,12 +83,14 @@ const UpdateProfile: FunctionComponent = () => { }); const handleUpdateProfile = (values: IUserProfile) => { - updateProfileMutation.mutate(values); + const payload = { + ...values, + socials + } + updateProfileMutation.mutate(payload); }; - const handleClick = () => { - fileInputRef.current?.click(); - }; + const handleClick = () => fileInputRef.current?.click(); useEffect(() => { if (res) { @@ -68,159 +99,210 @@ const UpdateProfile: FunctionComponent = () => { username: res?.username ?? '', email: res?.email ?? '', avatar: res?.avatar ?? '', - twitter: res?.twitter ?? '', - gitHub: res?.gitHub ?? '', - website: res?.website ?? '', profile_headlines: res?.profile_headlines ?? '', state: res?.state ?? '', country: res?.country ?? '', bio: res?.bio ?? '' }) + + if (res.socials && Array.isArray(res.socials)) { + setSocials( + res.socials.map((social: ISocial) => ({ + platform: social.platform, + link: social.link, + })) + ); + } } }, [res]); return ( <> - - - - - {imageUploadLoading ? ( - - ) : ( - <> - - - } - onClick={handleClick} + + + + + {imageUploadLoading ? ( + - - - )} + ) : ( + <> + + + } + onClick={handleClick} + position="absolute" + bottom="0" + right="0" + borderRadius="full" + bg={colors.primary} + boxShadow="md" + size="lg" + m={2} + /> + + + )} + - - - - The profile image must be a file of type: jpg, png, jpeg, JPG, PNG. - - - {({ handleSubmit, errors, touched }) => ( -
- - {[ - { name: 'fullname', type: 'text', placeholder: 'eg, John Doe', isRequired: true }, - { name: 'username', type: 'text', placeholder: 'eg, john-doe', isRequired: true }, - { name: 'email', type: 'email', disabled: true, isRequired: true }, - { name: 'profile_headlines', type: 'text', placeholder: 'eg, Frontend Developer || React', isRequired: true }, - { name: 'state', type: 'text', placeholder: 'Ebonyi', isRequired: true }, - { name: 'country', type: 'text', placeholder: 'Nigeria', isRequired: true }, - { name: 'twitter', type: 'text', placeholder: 'eg, @johndoe', isRequired: true }, - { name: 'gitHub', type: 'text', placeholder: 'eg, doejohn_', isRequired: false }, - { name: 'website', type: 'text', placeholder: 'eg, www.johndoe.com', isRequired: false }, - ].map(({ name, type, disabled, placeholder, isRequired }) => ( + + + The profile image must be a file of type: jpg, png, jpeg, JPG, PNG. + + + {({ handleSubmit, errors, touched }) => ( + + + {[ + { name: 'fullname', type: 'text', placeholder: 'eg, John Doe', isRequired: true }, + { name: 'username', type: 'text', placeholder: 'eg, john-doe', isRequired: true }, + { name: 'email', type: 'email', disabled: true, isRequired: true }, + { name: 'profile_headlines', type: 'text', placeholder: 'eg, Frontend Developer || React', isRequired: true }, + { name: 'state', type: 'text', placeholder: 'Ebonyi', isRequired: true }, + { name: 'country', type: 'text', placeholder: 'Nigeria', isRequired: true }, + ].map(({ name, type, disabled, placeholder, isRequired }) => ( + + + {capitalizeFirstLetter(name.replace('_', ' '))} + + + {errors[name as keyof typeof errors]} + + ))} + + + - - {capitalizeFirstLetter(name.replace('_', ' '))} - + Bio - {errors[name as keyof typeof errors]} + {errors.bio} - ))} - - - - - Bio - - {errors.bio} - - - - - - -
- )} -
-
-
+ + + + Social Accounts + + + + {socials.length > 0 && ( + + Added Socials + {socials.map((social: ISocial) => ( + + + {getSocialMediaIcon(social.platform)} + handleRemoveSocial(social.platform)} + /> + + + handleSocialLinkChange(social.platform, e.target.value) + } + /> + + ))} + + )} + + + + + + )} + + + ); }; -export default UpdateProfile; +export default UpdateProfile; \ No newline at end of file diff --git a/src/pages/Users/show/about.tsx b/src/pages/Users/show/about.tsx index fad8d2a..c9784e2 100644 --- a/src/pages/Users/show/about.tsx +++ b/src/pages/Users/show/about.tsx @@ -1,10 +1,9 @@ import { FunctionComponent } from 'react' import { Link } from 'react-router-dom' import { Box, HStack, Text } from '@chakra-ui/react' -import { FaGithub, FaXTwitter } from 'react-icons/fa6' -import { GiWorld } from 'react-icons/gi' -import { IUser } from 'src/types' +import { ISocial, IUser } from 'src/types' +import { getSocialMediaIcon } from '@helpers/getSocialMediaIcon' interface PublicUserAboutDetailsProps { data: IUser['data']; @@ -22,7 +21,7 @@ const PublicUserAboutDetails: FunctionComponent = ( - {data?.twitter && ( + {data?.socials && data?.socials.length > 0 && ( Connect with {data?.fullname} )} @@ -30,47 +29,19 @@ const PublicUserAboutDetails: FunctionComponent = ( spacing={3} my={"15px"} > - {data?.gitHub && ( + {data?.socials?.map((social: ISocial) => ( - + {getSocialMediaIcon(social.platform, "24px")} - )} - - {data?.twitter && ( - - - - - - )} - - {data?.website && ( - - - - - - )} + ))} diff --git a/src/types/common.ts b/src/types/common.ts index a685e5b..14ffa1c 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -1,8 +1,8 @@ +import { ISocial } from "./user"; + export interface AuthorDetails { bio: string | null; - twitter: string | null; - gitHub: string | null; - website: string | null; + socials: ISocial[]; } export interface CreatedAt { diff --git a/src/types/user/index.ts b/src/types/user/index.ts index c62f4dd..989bdce 100644 --- a/src/types/user/index.ts +++ b/src/types/user/index.ts @@ -6,13 +6,11 @@ export interface IUserProps { username: string; email: string; avatar: string; - twitter: string; - gitHub: string; - website: string; profile_headlines: string; state: string; country: string; bio: string; + socials?: ISocial[]; followers?: number; followings?: number; is_following?: boolean; @@ -22,7 +20,12 @@ export interface IUser { data: IUserProps } -export interface IUserProfile extends Omit {} +export interface ISocial { + platform: string; + link: string; +} + +export interface IUserProfile extends Omit { } export interface IUserProfileAvatar { avatar: string;