diff --git a/packages/client/src/components/panels/EditLinkPanel/PasswordAccessBlock.js b/packages/client/src/components/panels/EditLinkPanel/PasswordAccessBlock.js index aa3216f61a..942ed9ec43 100644 --- a/packages/client/src/components/panels/EditLinkPanel/PasswordAccessBlock.js +++ b/packages/client/src/components/panels/EditLinkPanel/PasswordAccessBlock.js @@ -24,6 +24,7 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode +import { useRef } from "react"; import ToggleBlock from "./ToggleBlock"; import { IconButton } from "@docspace/shared/components/icon-button"; import { Link } from "@docspace/shared/components/link"; @@ -31,8 +32,8 @@ import RefreshReactSvgUrl from "PUBLIC_DIR/images/refresh.react.svg?url"; import { FieldContainer } from "@docspace/shared/components/field-container"; import copy from "copy-to-clipboard"; import { toastr } from "@docspace/shared/components/toast"; -import SimulatePassword from "../../../components/SimulatePassword"; -import { getNewPassword } from "@docspace/shared/utils"; +import { ALLOWED_PASSWORD_CHARACTERS } from "@docspace/shared/constants"; +import { PasswordInput } from "@docspace/shared/components/password-input"; const PasswordAccessBlock = (props) => { const { @@ -43,15 +44,21 @@ const PasswordAccessBlock = (props) => { setPasswordValue, isPasswordValid, setIsPasswordValid, + passwordSettings, + isPasswordErrorShow, + setIsPasswordErrorShow, } = props; + const passwordInputRef = useRef(null); + const onGeneratePasswordClick = () => { - const password = getNewPassword(); - setPasswordValue(password); + passwordInputRef.current.onGeneratePassword(); }; - const onCleanClick = () => { + const onCleanClick = (e) => { + e.stopPropagation(); setPasswordValue(""); + setIsPasswordValid(false); }; const onCopyClick = () => { @@ -62,9 +69,36 @@ const PasswordAccessBlock = (props) => { } }; - const onChangePassword = (password) => { - setPasswordValue(password); + const onChangePassword = (e, value) => { + setPasswordValue(value); setIsPasswordValid(true); + setIsPasswordErrorShow(false); + }; + + // const onBlurPassword = () => { + // setIsPasswordErrorShow(true); + // }; + + const onValidatePassword = (isValidate) => { + setIsPasswordValid(isValidate); + }; + + const errorMessage = !passwordValue + ? t("Common:RequiredField") + : t("Common:IncorrectPassword"); + + const hasError = isPasswordErrorShow && !isPasswordValid; + + const tooltipData = { + tooltipPasswordTitle: `${t("Common:PasswordLimitMessage")}:`, + tooltipPasswordLength: `${t("Common:PasswordMinimumLength")}: ${ + passwordSettings ? passwordSettings.minLength : 8 + }`, + tooltipPasswordDigits: `${t("Common:PasswordLimitDigits")}`, + tooltipPasswordCapital: `${t("Common:PasswordLimitUpperCase")}`, + tooltipPasswordSpecial: `${t("Common:PasswordLimitSpecialSymbols")}`, + generatePasswordTitle: t("Wizard:GeneratePassword"), + tooltipAllowedCharacters: `${t("Common:AllowedCharacters")}: ${ALLOWED_PASSWORD_CHARACTERS}`, }; return ( @@ -74,17 +108,26 @@ const PasswordAccessBlock = (props) => {
- diff --git a/packages/client/src/components/panels/EditLinkPanel/index.js b/packages/client/src/components/panels/EditLinkPanel/index.js index 91b4c51bdc..ac34828e0b 100644 --- a/packages/client/src/components/panels/EditLinkPanel/index.js +++ b/packages/client/src/components/panels/EditLinkPanel/index.js @@ -65,6 +65,8 @@ const EditLinkPanel = (props) => { isFormRoom, currentDeviceType, setLinkParams, + passwordSettings, + getPortalPasswordSettings, } = props; const [isLoading, setIsLoading] = useState(false); @@ -80,6 +82,7 @@ const EditLinkPanel = (props) => { const [isExpired, setIsExpired] = useState(isExpiredDate); const [isPasswordValid, setIsPasswordValid] = useState(true); + const [isPasswordErrorShow, setIsPasswordErrorShow] = useState(false); const [linkValue, setLinkValue] = useState(shareLink); const [hasChanges, setHasChanges] = useState(false); @@ -101,10 +104,12 @@ const EditLinkPanel = (props) => { const onClose = () => setIsVisible(false); const onSave = () => { - const isPasswordValid = !!passwordValue.trim(); - - if (!isPasswordValid && passwordAccessIsChecked) { + if ( + (!passwordValue.trim() || !isPasswordValid) && + passwordAccessIsChecked + ) { setIsPasswordValid(false); + setIsPasswordErrorShow(true); return; } @@ -140,11 +145,14 @@ const EditLinkPanel = (props) => { toastr.success(t("Files:LinkSuccessfullyCreatedAndCopied")); } + onClose(); + }) + .catch((err) => { + const error = err?.response?.data?.error?.message ?? err?.message; + toastr.error(error); }) - .catch((err) => toastr.error(err?.message)) .finally(() => { setIsLoading(false); - onClose(); }); }; @@ -183,6 +191,18 @@ const EditLinkPanel = (props) => { return () => window.removeEventListener("keydown", onKeyPress); }, [onKeyPress]); + const getPasswordSettings = async () => { + setIsLoading(true); + await getPortalPasswordSettings(); + setIsLoading(false); + }; + + useEffect(() => { + if (!passwordSettings) { + getPasswordSettings(); + } + }, [passwordSettings]); + const linkNameIsValid = !!linkNameValue.trim(); const expiredLinkText = isExpired @@ -239,6 +259,9 @@ const EditLinkPanel = (props) => { setPasswordValue={setPasswordValue} setIsPasswordValid={setIsPasswordValid} onChange={onPasswordAccessChange} + passwordSettings={passwordSettings} + isPasswordErrorShow={isPasswordErrorShow} + setIsPasswordErrorShow={setIsPasswordErrorShow} /> {!isFormRoom && ( l?.sharedTo?.id === linkId); - const shareLink = link?.sharedTo?.shareLink; return { @@ -340,10 +365,14 @@ export default inject( language: authStore.language, isPublic, isFormRoom, - currentDeviceType: settingsStore.currentDeviceType, + currentDeviceType, setLinkParams, + passwordSettings, + getPortalPasswordSettings, }; }, )( - withTranslation(["SharingPanel", "Common", "Files"])(observer(EditLinkPanel)), + withTranslation(["SharingPanel", "Common", "Files", "Wizard"])( + observer(EditLinkPanel), + ), ); diff --git a/packages/shared/components/password-input/PasswordInput.tsx b/packages/shared/components/password-input/PasswordInput.tsx index 8ea516b831..e96cf75b98 100644 --- a/packages/shared/components/password-input/PasswordInput.tsx +++ b/packages/shared/components/password-input/PasswordInput.tsx @@ -32,6 +32,7 @@ import React, { ChangeEvent, FocusEvent, MouseEvent, + useEffect, } from "react"; import { TooltipRefProps } from "react-tooltip"; @@ -109,6 +110,16 @@ const PasswordInput = React.forwardRef( }: PasswordInputProps, ref, ) => { + const usePrevious = (value: string) => { + const inputValueRef = useRef(); + useEffect(() => { + inputValueRef.current = value; + }); + return inputValueRef.current; + }; + + const prevInputValue = usePrevious(inputValue ?? ""); + const [state, setState] = useState({ type: inputType, value: inputValue, @@ -276,6 +287,8 @@ const PasswordInput = React.forwardRef( (newPassword: string) => { let newValue; + if (!state.value) return newPassword; + const oldPassword = state.value ?? ""; const oldPasswordLength = oldPassword.length; const newCaretPosition = document.getElementById( @@ -465,6 +478,14 @@ const PasswordInput = React.forwardRef( } }, [caretPosition, state.type, state.value, isSimulateType]); + useEffect(() => { + if (inputValue !== prevInputValue) { + onChangeAction?.({ + target: { value: inputValue }, + } as ChangeEvent); + } + }, [inputValue, prevInputValue, onChangeAction]); + React.useImperativeHandle( ref, () => {