Web: Files: EditLinkPanel: added password settings

This commit is contained in:
Nikita Gopienko 2024-08-29 18:19:39 +03:00
parent 153bae389c
commit c3e7777667
3 changed files with 113 additions and 20 deletions

View File

@ -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) => {
<div className="edit-link_password-block">
<FieldContainer
isVertical
hasError={!isPasswordValid}
errorMessage={t("Common:RequiredField")}
hasError={hasError}
errorMessage={errorMessage}
className="edit-link_password-block"
>
<SimulatePassword
<PasswordInput
id="conversion-password"
className="edit-link_password-input"
ref={passwordInputRef}
isDisabled={isLoading}
hasError={!isPasswordValid}
inputValue={passwordValue}
onChange={onChangePassword}
inputMaxWidth="100%"
passwordSettings={passwordSettings}
simpleView={false}
placeholder={t("Common:Password")}
hasError={hasError}
// onBlur={onBlurPassword}
onValidateInput={onValidatePassword}
isSimulateType
simulateSymbol="•"
{...tooltipData}
/>
</FieldContainer>

View File

@ -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 && (
<ToggleBlock
@ -313,11 +336,13 @@ export default inject(
} = dialogsStore;
const { externalLinks, editExternalLink, setExternalLink } =
publicRoomStore;
const { currentDeviceType, passwordSettings, getPortalPasswordSettings } =
settingsStore;
const { isEdit, roomId, isPublic, isFormRoom } = linkParams;
const linkId = linkParams?.link?.sharedTo?.id;
const link = externalLinks.find((l) => 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),
),
);

View File

@ -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<string>();
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<HTMLInputElement>);
}
}, [inputValue, prevInputValue, onChangeAction]);
React.useImperativeHandle(
ref,
() => {