From 73e10027bb4e8ea090029d6a125b6a04e3715742 Mon Sep 17 00:00:00 2001 From: dat Date: Thu, 12 Oct 2023 20:54:19 +0700 Subject: [PATCH 1/2] Profiles: social media panel feedback --- .../LinkForm/LinkForm.tsx | 54 +++++++++++++++---- .../ModifyMemberLinksModal.tsx | 2 +- .../link-types.config.ts | 34 ++++++------ 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx index a9a35ced3..8994a0172 100644 --- a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx +++ b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx @@ -1,10 +1,19 @@ import { trim } from 'lodash' -import { FC, forwardRef, ForwardRefExoticComponent, SVGProps, useEffect, useImperativeHandle, useState } from 'react' +import { + FC, + forwardRef, + ForwardRefExoticComponent, + SVGProps, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react' import classNames from 'classnames' import { Button, IconOutline, InputSelect, InputText } from '~/libs/ui' -import { linkTypes } from '../link-types.config' +import { additionalLinkTypes } from '../link-types.config' import { isValidURL } from '../../../../lib' import { renderLinkIcon } from '../../MemberLinks' @@ -41,6 +50,8 @@ const LinkForm: ForwardRefExoticComponent< const [selectedLinkType, setSelectedLinkType] = useState() const [selectedLinkURL, setSelectedLinkURL] = useState() const [shouldValidateForm, setShouldValidateForm] = useState(false) + const canShowTypeError = useRef(false) + const canShowUrlError = useRef(false) useEffect(() => { if (shouldValidateForm) { @@ -52,37 +63,58 @@ const LinkForm: ForwardRefExoticComponent< resetForm() { setShouldValidateForm(false) setFormErrors({}) + canShowTypeError.current = false + canShowUrlError.current = false }, validateForm() { + canShowTypeError.current = true + canShowUrlError.current = true handleFormAction() }, })) function handleSelectedLinkTypeChange(event: React.ChangeEvent): void { + canShowTypeError.current = true setSelectedLinkType(event.target.value) setShouldValidateForm(true) } function handleURLChange(event: React.ChangeEvent): void { + canShowUrlError.current = true setSelectedLinkURL(event.target.value) setShouldValidateForm(true) } - function handleFormAction(): void { + function getFormError(): boolean { setFormErrors({}) + let isError = false if (!selectedLinkType) { - setFormErrors({ selectedLinkType: 'Please select a link type' }) - return + isError = true + if (canShowTypeError.current) { + setFormErrors({ selectedLinkType: 'Please select a link type' }) + } } if (!props.allowEmptyUrl && !trim(selectedLinkURL)) { - setFormErrors({ url: 'Please enter a URL' }) - return + isError = true + if (canShowUrlError.current) { + setFormErrors({ url: 'Please enter a URL' }) + } } if (selectedLinkURL && !isValidURL(selectedLinkURL as string)) { - setFormErrors({ url: 'Invalid URL' }) + isError = true + if (canShowUrlError.current) { + setFormErrors({ url: 'Invalid URL' }) + } + } + + return isError + } + + function handleFormAction(): void { + if (getFormError()) { return } @@ -91,14 +123,14 @@ const LinkForm: ForwardRefExoticComponent< if (absoluteURL.indexOf('://') > 0 || absoluteURL.indexOf('//') === 0) { props.onSave({ - name: selectedLinkType, + name: selectedLinkType ?? '', url: absoluteURL, }) } else { absoluteURL = absoluteURL ? `https://${absoluteURL}` : '' props.onSave({ - name: selectedLinkType, + name: selectedLinkType ?? '', url: absoluteURL, }) } @@ -124,7 +156,7 @@ const LinkForm: ForwardRefExoticComponent<
{props.allowEditType ? ( = (props: ModifyMe
diff --git a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/link-types.config.ts b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/link-types.config.ts index 2298a4983..4af0af4bf 100644 --- a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/link-types.config.ts +++ b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/link-types.config.ts @@ -1,30 +1,34 @@ -export const linkTypes = [ +export const additionalLinkTypes = [ { - name: 'Facebook', - value: 'Facebook', + name: 'Twitter', + value: 'X / Twitter', }, { - name: 'GitHub', - value: 'GitHub', + name: 'Website', + value: 'Website', }, { - name: 'Instagram', - value: 'Instagram', + name: 'YouTube', + value: 'YouTube', }, { - name: 'LinkedIn', - value: 'LinkedIn', + name: 'Facebook', + value: 'Facebook', }, +] + +export const linkTypes = [ { - name: 'Twitter', - value: 'X / Twitter', + name: 'LinkedIn', + value: 'LinkedIn', }, { - name: 'Website', - value: 'Website', + name: 'GitHub', + value: 'GitHub', }, { - name: 'YouTube', - value: 'YouTube', + name: 'Instagram', + value: 'Instagram', }, + ...additionalLinkTypes, ] From 760546188561e0120c397a8c549b3253aa17c97e Mon Sep 17 00:00:00 2001 From: dat Date: Fri, 13 Oct 2023 11:57:34 +0700 Subject: [PATCH 2/2] Profiles Add New Social Media Save Feature is Confusing --- .../src/member-profile/links/MemberLinks.tsx | 2 + .../LinkForm/LinkForm.tsx | 10 +- .../ModifyMemberLinksModal.tsx | 93 +++++++------------ .../link-types.config.ts | 2 +- 4 files changed, 38 insertions(+), 69 deletions(-) diff --git a/src/apps/profiles/src/member-profile/links/MemberLinks.tsx b/src/apps/profiles/src/member-profile/links/MemberLinks.tsx index 37328a1fc..4631b9678 100644 --- a/src/apps/profiles/src/member-profile/links/MemberLinks.tsx +++ b/src/apps/profiles/src/member-profile/links/MemberLinks.tsx @@ -32,6 +32,8 @@ export function renderLinkIcon(linkName: string): JSX.Element { return case 'Twitter': return + case 'X / Twitter': + return case 'LinkedIn': return case 'Instagram': diff --git a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx index 8994a0172..f6fea3acb 100644 --- a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx +++ b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx @@ -33,7 +33,6 @@ interface LinkFormProps { onRemove?: () => void removeIcon?: FC> hideRemoveIcon?: boolean - allowEmptyUrl?: boolean labelUrlField?: string disabled?: boolean } @@ -96,14 +95,7 @@ const LinkForm: ForwardRefExoticComponent< } } - if (!props.allowEmptyUrl && !trim(selectedLinkURL)) { - isError = true - if (canShowUrlError.current) { - setFormErrors({ url: 'Please enter a URL' }) - } - } - - if (selectedLinkURL && !isValidURL(selectedLinkURL as string)) { + if (selectedLinkURL && trim(selectedLinkURL) && !isValidURL(selectedLinkURL as string)) { isError = true if (canShowUrlError.current) { setFormErrors({ url: 'Invalid URL' }) diff --git a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/ModifyMemberLinksModal.tsx b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/ModifyMemberLinksModal.tsx index 87540dccf..76ce9f68a 100644 --- a/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/ModifyMemberLinksModal.tsx +++ b/src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/ModifyMemberLinksModal.tsx @@ -1,5 +1,5 @@ -import { find, findIndex, omit, reject, uniqBy } from 'lodash' -import { FC, useEffect, useRef, useState } from 'react' +import { cloneDeep, findIndex, isEqual, omit, reject, uniqBy } from 'lodash' +import { FC, useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'react-toastify' import classNames from 'classnames' @@ -28,7 +28,6 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe const inputRef = useRef() const [isSaving, setIsSaving] = useState(false) - const [hasChanges, setHasChanges] = useState(false) const [currentMemberLinks, setCurrentMemberLinks] = useState( [], ) @@ -44,16 +43,33 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe name: 'Instagram', url: '', }) - const [newLink, setNewLink] = useState({ + const [defaultLink, setDefaultLink] = useState({ name: '', url: '', }) + const updatedLinks = useMemo(() => uniqBy( + [ + defaultLinkedIn, + defaultGitHub, + defaultInstagram, + defaultLink, + ...currentMemberLinks, + ].filter( + l => l.name && l.url, + ), + e => `${e.name}-${e.url}`, + ) + .map( + item => omit(item, ['id']), + ), [defaultLinkedIn, defaultGitHub, defaultInstagram, defaultLink, currentMemberLinks]) + const hasChanges = useMemo(() => !isEqual(updatedLinks, props.memberLinks), [updatedLinks]) + const addNewLinkRef = useRef(null) useEffect(() => { const memberLinks = [ - ...(props.memberLinks ?? []), + ...cloneDeep(props.memberLinks ?? []), ] const firstLinkedInIndex = findIndex(memberLinks, { name: 'LinkedIn', @@ -76,6 +92,10 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe setDefaultInstagram(memberLinks.splice(firstInstagramIndex, 1)[0]) } + if (memberLinks.length > 0) { + setDefaultLink(memberLinks.splice(0, 1)[0]) + } + setCurrentMemberLinks(memberLinks.map((item: UserTrait, index: number) => ({ ...item, id: `id-${index}-${(new Date()) @@ -85,30 +105,12 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe }, [props.memberLinks]) function handleAddAdditional(): void { - if (newLink.url && newLink.name) { - const updatedLinks: UserTrait[] = uniqBy([ - defaultLinkedIn, - defaultGitHub, - defaultInstagram, - ...currentMemberLinks, - ].filter(l => l.name && l.url), e => `${e.name}-${e.url}`) - if (!find(updatedLinks, newLink)) { - setCurrentMemberLinks(links => [...links, { - ...newLink, - id: `id-${(new Date()) - .getTime()}`, - }]) - } - - addNewLinkRef.current?.resetForm() - setNewLink({ - name: '', - url: '', - }) - setHasChanges(true) - } else { - addNewLinkRef.current?.validateForm() - } + setCurrentMemberLinks(links => [...links, { + id: `id-${(new Date()) + .getTime()}`, + name: '', + url: '', + }]) } function handleRemoveLink(index: number): void { @@ -118,15 +120,12 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe ...currentMemberLinks, ], ) - setHasChanges(true) } function handleSaveLink(link: UserTrait, index: number): void { setCurrentMemberLinks(links => (links ?? []).map((l, i) => ( i === index ? link : l ))) - - setHasChanges(true) } function handleLinksSave(): void { @@ -135,21 +134,6 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe const updatedPersonalizationTraits: UserTrait[] = reject(props.memberPersonalizationTraitsFullData, (trait: UserTrait) => trait.links) - const updatedLinks: UserTrait[] = uniqBy( - [ - defaultLinkedIn, - defaultGitHub, - defaultInstagram, - ...currentMemberLinks, - ].filter( - l => l.name && l.url, - ), - e => `${e.name}-${e.url}`, - ) - .map( - item => omit(item, ['id']), - ) - updateOrCreateMemberTraitsAsync(props.profile.handle, [{ categoryName: UserTraitCategoryNames.personalization, traitId: UserTraitIds.personalization, @@ -204,19 +188,16 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe link={defaultLinkedIn as UserLink} onSave={function onSave(link: UserLink) { setDefaultLinkedIn(link) - setHasChanges(true) }} onRemove={function onRemove() { setDefaultLinkedIn({ ...defaultLinkedIn, url: '', }) - setHasChanges(true) }} placeholder='Add URL' removeIcon={IconOutline.XCircleIcon} hideRemoveIcon={!defaultLinkedIn.url} - allowEmptyUrl disabled={isSaving} labelUrlField='Linkedin' /> @@ -224,19 +205,16 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe link={defaultGitHub as UserLink} onSave={function onSave(link: UserLink) { setDefaultGitHub(link) - setHasChanges(true) }} onRemove={function onRemove() { setDefaultGitHub({ ...defaultGitHub, url: '', }) - setHasChanges(true) }} placeholder='Add URL' removeIcon={IconOutline.XCircleIcon} hideRemoveIcon={!defaultGitHub.url} - allowEmptyUrl disabled={isSaving} labelUrlField='Git' /> @@ -244,19 +222,16 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe link={defaultInstagram as UserLink} onSave={function onSave(link: UserLink) { setDefaultInstagram(link) - setHasChanges(true) }} onRemove={function onRemove() { setDefaultInstagram({ ...defaultInstagram, url: '', }) - setHasChanges(true) }} placeholder='Add URL' removeIcon={IconOutline.XCircleIcon} hideRemoveIcon={!defaultInstagram.url} - allowEmptyUrl disabled={isSaving} labelUrlField='Instagram' /> @@ -265,8 +240,8 @@ const ModifyMemberLinksModal: FC = (props: ModifyMe
= (props: ModifyMe