Skip to content

Commit 7f35e3d

Browse files
authored
Merge pull request #925 from topcoder-platform/issue-923
Profiles: social media panel feedback
2 parents 5fdbf03 + 7605461 commit 7f35e3d

File tree

4 files changed

+92
-87
lines changed

4 files changed

+92
-87
lines changed

src/apps/profiles/src/member-profile/links/MemberLinks.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export function renderLinkIcon(linkName: string): JSX.Element {
3232
return <GitHubLinkIcon />
3333
case 'Twitter':
3434
return <SocialIconTwitter />
35+
case 'X / Twitter':
36+
return <SocialIconTwitter />
3537
case 'LinkedIn':
3638
return <LinkedInLinkIcon />
3739
case 'Instagram':

src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/LinkForm/LinkForm.tsx

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import { trim } from 'lodash'
2-
import { FC, forwardRef, ForwardRefExoticComponent, SVGProps, useEffect, useImperativeHandle, useState } from 'react'
2+
import {
3+
FC,
4+
forwardRef,
5+
ForwardRefExoticComponent,
6+
SVGProps,
7+
useEffect,
8+
useImperativeHandle,
9+
useRef,
10+
useState,
11+
} from 'react'
312
import classNames from 'classnames'
413

514
import { Button, IconOutline, InputSelect, InputText } from '~/libs/ui'
615

7-
import { linkTypes } from '../link-types.config'
16+
import { additionalLinkTypes } from '../link-types.config'
817
import { isValidURL } from '../../../../lib'
918
import { renderLinkIcon } from '../../MemberLinks'
1019

@@ -24,7 +33,6 @@ interface LinkFormProps {
2433
onRemove?: () => void
2534
removeIcon?: FC<SVGProps<SVGSVGElement>>
2635
hideRemoveIcon?: boolean
27-
allowEmptyUrl?: boolean
2836
labelUrlField?: string
2937
disabled?: boolean
3038
}
@@ -41,6 +49,8 @@ const LinkForm: ForwardRefExoticComponent<
4149
const [selectedLinkType, setSelectedLinkType] = useState<string | undefined>()
4250
const [selectedLinkURL, setSelectedLinkURL] = useState<string | undefined>()
4351
const [shouldValidateForm, setShouldValidateForm] = useState<boolean>(false)
52+
const canShowTypeError = useRef(false)
53+
const canShowUrlError = useRef(false)
4454

4555
useEffect(() => {
4656
if (shouldValidateForm) {
@@ -52,37 +62,51 @@ const LinkForm: ForwardRefExoticComponent<
5262
resetForm() {
5363
setShouldValidateForm(false)
5464
setFormErrors({})
65+
canShowTypeError.current = false
66+
canShowUrlError.current = false
5567
},
5668
validateForm() {
69+
canShowTypeError.current = true
70+
canShowUrlError.current = true
5771
handleFormAction()
5872
},
5973
}))
6074

6175
function handleSelectedLinkTypeChange(event: React.ChangeEvent<HTMLInputElement>): void {
76+
canShowTypeError.current = true
6277
setSelectedLinkType(event.target.value)
6378
setShouldValidateForm(true)
6479
}
6580

6681
function handleURLChange(event: React.ChangeEvent<HTMLInputElement>): void {
82+
canShowUrlError.current = true
6783
setSelectedLinkURL(event.target.value)
6884
setShouldValidateForm(true)
6985
}
7086

71-
function handleFormAction(): void {
87+
function getFormError(): boolean {
7288
setFormErrors({})
7389

90+
let isError = false
7491
if (!selectedLinkType) {
75-
setFormErrors({ selectedLinkType: 'Please select a link type' })
76-
return
92+
isError = true
93+
if (canShowTypeError.current) {
94+
setFormErrors({ selectedLinkType: 'Please select a link type' })
95+
}
7796
}
7897

79-
if (!props.allowEmptyUrl && !trim(selectedLinkURL)) {
80-
setFormErrors({ url: 'Please enter a URL' })
81-
return
98+
if (selectedLinkURL && trim(selectedLinkURL) && !isValidURL(selectedLinkURL as string)) {
99+
isError = true
100+
if (canShowUrlError.current) {
101+
setFormErrors({ url: 'Invalid URL' })
102+
}
82103
}
83104

84-
if (selectedLinkURL && !isValidURL(selectedLinkURL as string)) {
85-
setFormErrors({ url: 'Invalid URL' })
105+
return isError
106+
}
107+
108+
function handleFormAction(): void {
109+
if (getFormError()) {
86110
return
87111
}
88112

@@ -91,14 +115,14 @@ const LinkForm: ForwardRefExoticComponent<
91115
if (absoluteURL.indexOf('://') > 0 || absoluteURL.indexOf('//') === 0) {
92116

93117
props.onSave({
94-
name: selectedLinkType,
118+
name: selectedLinkType ?? '',
95119
url: absoluteURL,
96120
})
97121
} else {
98122
absoluteURL = absoluteURL ? `https://${absoluteURL}` : ''
99123

100124
props.onSave({
101-
name: selectedLinkType,
125+
name: selectedLinkType ?? '',
102126
url: absoluteURL,
103127
})
104128
}
@@ -124,7 +148,7 @@ const LinkForm: ForwardRefExoticComponent<
124148
<div className={styles.form}>
125149
{props.allowEditType ? (
126150
<InputSelect
127-
options={linkTypes}
151+
options={additionalLinkTypes}
128152
value={selectedLinkType}
129153
onChange={handleSelectedLinkTypeChange}
130154
name='linkType'

src/apps/profiles/src/member-profile/links/ModifyMemberLinksModal/ModifyMemberLinksModal.tsx

Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { find, findIndex, omit, reject, uniqBy } from 'lodash'
2-
import { FC, useEffect, useRef, useState } from 'react'
1+
import { cloneDeep, findIndex, isEqual, omit, reject, uniqBy } from 'lodash'
2+
import { FC, useEffect, useMemo, useRef, useState } from 'react'
33
import { toast } from 'react-toastify'
44
import classNames from 'classnames'
55

@@ -28,7 +28,6 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
2828
const inputRef = useRef<HTMLInputElement | any>()
2929

3030
const [isSaving, setIsSaving] = useState<boolean>(false)
31-
const [hasChanges, setHasChanges] = useState<boolean>(false)
3231
const [currentMemberLinks, setCurrentMemberLinks] = useState<UserTrait[]>(
3332
[],
3433
)
@@ -44,16 +43,33 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
4443
name: 'Instagram',
4544
url: '',
4645
})
47-
const [newLink, setNewLink] = useState<UserTrait>({
46+
const [defaultLink, setDefaultLink] = useState<UserTrait>({
4847
name: '',
4948
url: '',
5049
})
5150

51+
const updatedLinks = useMemo(() => uniqBy(
52+
[
53+
defaultLinkedIn,
54+
defaultGitHub,
55+
defaultInstagram,
56+
defaultLink,
57+
...currentMemberLinks,
58+
].filter(
59+
l => l.name && l.url,
60+
),
61+
e => `${e.name}-${e.url}`,
62+
)
63+
.map(
64+
item => omit(item, ['id']),
65+
), [defaultLinkedIn, defaultGitHub, defaultInstagram, defaultLink, currentMemberLinks])
66+
const hasChanges = useMemo(() => !isEqual(updatedLinks, props.memberLinks), [updatedLinks])
67+
5268
const addNewLinkRef = useRef<LinkFormHandle>(null)
5369

5470
useEffect(() => {
5571
const memberLinks = [
56-
...(props.memberLinks ?? []),
72+
...cloneDeep(props.memberLinks ?? []),
5773
]
5874
const firstLinkedInIndex = findIndex(memberLinks, {
5975
name: 'LinkedIn',
@@ -76,6 +92,10 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
7692
setDefaultInstagram(memberLinks.splice(firstInstagramIndex, 1)[0])
7793
}
7894

95+
if (memberLinks.length > 0) {
96+
setDefaultLink(memberLinks.splice(0, 1)[0])
97+
}
98+
7999
setCurrentMemberLinks(memberLinks.map((item: UserTrait, index: number) => ({
80100
...item,
81101
id: `id-${index}-${(new Date())
@@ -85,30 +105,12 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
85105
}, [props.memberLinks])
86106

87107
function handleAddAdditional(): void {
88-
if (newLink.url && newLink.name) {
89-
const updatedLinks: UserTrait[] = uniqBy([
90-
defaultLinkedIn,
91-
defaultGitHub,
92-
defaultInstagram,
93-
...currentMemberLinks,
94-
].filter(l => l.name && l.url), e => `${e.name}-${e.url}`)
95-
if (!find(updatedLinks, newLink)) {
96-
setCurrentMemberLinks(links => [...links, {
97-
...newLink,
98-
id: `id-${(new Date())
99-
.getTime()}`,
100-
}])
101-
}
102-
103-
addNewLinkRef.current?.resetForm()
104-
setNewLink({
105-
name: '',
106-
url: '',
107-
})
108-
setHasChanges(true)
109-
} else {
110-
addNewLinkRef.current?.validateForm()
111-
}
108+
setCurrentMemberLinks(links => [...links, {
109+
id: `id-${(new Date())
110+
.getTime()}`,
111+
name: '',
112+
url: '',
113+
}])
112114
}
113115

114116
function handleRemoveLink(index: number): void {
@@ -118,15 +120,12 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
118120
...currentMemberLinks,
119121
],
120122
)
121-
setHasChanges(true)
122123
}
123124

124125
function handleSaveLink(link: UserTrait, index: number): void {
125126
setCurrentMemberLinks(links => (links ?? []).map((l, i) => (
126127
i === index ? link : l
127128
)))
128-
129-
setHasChanges(true)
130129
}
131130

132131
function handleLinksSave(): void {
@@ -135,21 +134,6 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
135134
const updatedPersonalizationTraits: UserTrait[]
136135
= reject(props.memberPersonalizationTraitsFullData, (trait: UserTrait) => trait.links)
137136

138-
const updatedLinks: UserTrait[] = uniqBy(
139-
[
140-
defaultLinkedIn,
141-
defaultGitHub,
142-
defaultInstagram,
143-
...currentMemberLinks,
144-
].filter(
145-
l => l.name && l.url,
146-
),
147-
e => `${e.name}-${e.url}`,
148-
)
149-
.map(
150-
item => omit(item, ['id']),
151-
)
152-
153137
updateOrCreateMemberTraitsAsync(props.profile.handle, [{
154138
categoryName: UserTraitCategoryNames.personalization,
155139
traitId: UserTraitIds.personalization,
@@ -204,59 +188,50 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
204188
link={defaultLinkedIn as UserLink}
205189
onSave={function onSave(link: UserLink) {
206190
setDefaultLinkedIn(link)
207-
setHasChanges(true)
208191
}}
209192
onRemove={function onRemove() {
210193
setDefaultLinkedIn({
211194
...defaultLinkedIn,
212195
url: '',
213196
})
214-
setHasChanges(true)
215197
}}
216198
placeholder='Add URL'
217199
removeIcon={IconOutline.XCircleIcon}
218200
hideRemoveIcon={!defaultLinkedIn.url}
219-
allowEmptyUrl
220201
disabled={isSaving}
221202
labelUrlField='Linkedin'
222203
/>
223204
<LinkForm
224205
link={defaultGitHub as UserLink}
225206
onSave={function onSave(link: UserLink) {
226207
setDefaultGitHub(link)
227-
setHasChanges(true)
228208
}}
229209
onRemove={function onRemove() {
230210
setDefaultGitHub({
231211
...defaultGitHub,
232212
url: '',
233213
})
234-
setHasChanges(true)
235214
}}
236215
placeholder='Add URL'
237216
removeIcon={IconOutline.XCircleIcon}
238217
hideRemoveIcon={!defaultGitHub.url}
239-
allowEmptyUrl
240218
disabled={isSaving}
241219
labelUrlField='Git'
242220
/>
243221
<LinkForm
244222
link={defaultInstagram as UserLink}
245223
onSave={function onSave(link: UserLink) {
246224
setDefaultInstagram(link)
247-
setHasChanges(true)
248225
}}
249226
onRemove={function onRemove() {
250227
setDefaultInstagram({
251228
...defaultInstagram,
252229
url: '',
253230
})
254-
setHasChanges(true)
255231
}}
256232
placeholder='Add URL'
257233
removeIcon={IconOutline.XCircleIcon}
258234
hideRemoveIcon={!defaultInstagram.url}
259-
allowEmptyUrl
260235
disabled={isSaving}
261236
labelUrlField='Instagram'
262237
/>
@@ -265,8 +240,8 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
265240
<hr className={styles.spacer} />
266241

267242
<LinkForm
268-
link={newLink as UserLink}
269-
onSave={setNewLink}
243+
link={defaultLink as UserLink}
244+
onSave={setDefaultLink}
270245
allowEditType
271246
placeholder='http://'
272247
ref={addNewLinkRef}
Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
1-
export const linkTypes = [
1+
export const additionalLinkTypes = [
22
{
3-
name: 'Facebook',
4-
value: 'Facebook',
3+
name: 'X / Twitter',
4+
value: 'X / Twitter',
55
},
66
{
7-
name: 'GitHub',
8-
value: 'GitHub',
7+
name: 'Website',
8+
value: 'Website',
99
},
1010
{
11-
name: 'Instagram',
12-
value: 'Instagram',
11+
name: 'YouTube',
12+
value: 'YouTube',
1313
},
1414
{
15-
name: 'LinkedIn',
16-
value: 'LinkedIn',
15+
name: 'Facebook',
16+
value: 'Facebook',
1717
},
18+
]
19+
20+
export const linkTypes = [
1821
{
19-
name: 'Twitter',
20-
value: 'X / Twitter',
22+
name: 'LinkedIn',
23+
value: 'LinkedIn',
2124
},
2225
{
23-
name: 'Website',
24-
value: 'Website',
26+
name: 'GitHub',
27+
value: 'GitHub',
2528
},
2629
{
27-
name: 'YouTube',
28-
value: 'YouTube',
30+
name: 'Instagram',
31+
value: 'Instagram',
2932
},
33+
...additionalLinkTypes,
3034
]

0 commit comments

Comments
 (0)