diff --git a/src/constants/translations/en/edit-profile.json b/src/constants/translations/en/edit-profile.json index 7f645fcb0..ee9fc8fc6 100644 --- a/src/constants/translations/en/edit-profile.json +++ b/src/constants/translations/en/edit-profile.json @@ -22,6 +22,14 @@ "videoPresentationDesc": "Add the link to your presentational video. Your video needs to be public and uploaded to YouTube.", "errorTooltip": "There is a problem with your changes" }, + "profileTab": { + "saveUnsavedChangesModal": { + "title": "You have unsaved changes", + "description": "Are you sure you want to leave this page without updating? All your recent changes will be discarded.", + "cancelBtn": "Leave", + "submitBtn": "Update" + } + }, "professionalTab": { "tabTitle": "Professional info", "mainTitle": "Professional information", diff --git a/src/pages/edit-profile/EditProfile.tsx b/src/pages/edit-profile/EditProfile.tsx index 92ab06b3a..07fe50379 100644 --- a/src/pages/edit-profile/EditProfile.tsx +++ b/src/pages/edit-profile/EditProfile.tsx @@ -1,7 +1,8 @@ -import { useEffect, useState, useMemo } from 'react' +import { useEffect, useState, useMemo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Link, + useBlocker, useLocation, useNavigate, useSearchParams @@ -103,6 +104,63 @@ const EditProfile = () => { } }, [loading, profileState, initialEditProfileState]) + const changedProfileFields = useMemo>(() => { + if (!initialEditProfileState || !profileState) return {} + + const changedInitial = { + firstName: initialEditProfileState.firstName, + lastName: initialEditProfileState.lastName, + photo: initialEditProfileState.photo, + city: initialEditProfileState.city, + country: initialEditProfileState.country, + nativeLanguage: initialEditProfileState.nativeLanguage, + professionalSummary: initialEditProfileState.professionalSummary, + videoLink: initialEditProfileState.videoLink + } + + const changedCurrent = { + firstName: profileState.firstName, + lastName: profileState.lastName, + photo: profileState.photo, + city: profileState.city, + country: profileState.country, + nativeLanguage: profileState.nativeLanguage, + professionalSummary: profileState.professionalSummary, + videoLink: profileState.videoLink + } + + const hasPhotoChanged = hasPhotoChanges( + changedInitial.photo, + changedCurrent.photo + ) + + const hasChanged = + hasChanges(changedInitial, changedCurrent) || hasPhotoChanged + + if (hasChanged) { + const changes: Partial = { + ...changedCurrent + } + + if (!hasChanges(changedInitial.videoLink, changedCurrent.videoLink)) { + delete changes.videoLink + } + + if (hasPhotoChanged) { + changes.photo = changedCurrent.photo + } + + return changes + } else { + return {} + } + }, [profileState, initialEditProfileState]) + + const isProfileChanged = useMemo( + () => Object.keys(changedProfileFields).length > 0, + [changedProfileFields] + ) + const changedFields = useMemo>(() => { if (!initialEditProfileState || !profileState) return {} const { videoLink: initialVideoLink } = initialEditProfileState @@ -153,10 +211,10 @@ const EditProfile = () => { } } - const { hash } = useLocation() + const { hash, search, pathname } = useLocation() const navigate = useNavigate() - const handleUpdateUser = async (): Promise => { + const handleUpdateUser = useCallback(async (): Promise => { const { country, city } = profileState const { videoLink, @@ -213,7 +271,41 @@ const EditProfile = () => { if (hash) { navigate(`${authRoutes.myProfile.path}#complete`) } - } + }, [profileState, changedFields]) + + const blocker = useBlocker(isProfileChanged) + const { openDialog } = useConfirm() + + useEffect(() => { + if (blocker && blocker.location) { + const hasPathnameChanged = pathname !== blocker.location.pathname + const hasSearchChanged = search !== blocker.location.search + + if (hasSearchChanged && !hasPathnameChanged) { + blocker.proceed?.() + } + + if (hasPathnameChanged) { + openDialog({ + title: t( + 'editProfilePage.profile.profileTab.saveUnsavedChangesModal.title' + ), + message: t( + 'editProfilePage.profile.profileTab.saveUnsavedChangesModal.description' + ), + cancelButton: t( + 'editProfilePage.profile.profileTab.saveUnsavedChangesModal.cancelBtn' + ), + confirmButton: t( + 'editProfilePage.profile.profileTab.saveUnsavedChangesModal.submitBtn' + ), + sendConfirm: (isConfirmed) => { + isConfirmed ? void handleUpdateUser() : blocker.proceed?.() + } + }) + } + } + }, [blocker, pathname, openDialog, search, t, handleUpdateUser]) const cooperationContent = activeTab && tabsData[activeTab]?.content