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 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { useRef } from "react";
import ToggleBlock from "./ToggleBlock"; import ToggleBlock from "./ToggleBlock";
import { IconButton } from "@docspace/shared/components/icon-button"; import { IconButton } from "@docspace/shared/components/icon-button";
import { Link } from "@docspace/shared/components/link"; 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 { FieldContainer } from "@docspace/shared/components/field-container";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { toastr } from "@docspace/shared/components/toast"; import { toastr } from "@docspace/shared/components/toast";
import SimulatePassword from "../../../components/SimulatePassword"; import { ALLOWED_PASSWORD_CHARACTERS } from "@docspace/shared/constants";
import { getNewPassword } from "@docspace/shared/utils"; import { PasswordInput } from "@docspace/shared/components/password-input";
const PasswordAccessBlock = (props) => { const PasswordAccessBlock = (props) => {
const { const {
@ -43,15 +44,21 @@ const PasswordAccessBlock = (props) => {
setPasswordValue, setPasswordValue,
isPasswordValid, isPasswordValid,
setIsPasswordValid, setIsPasswordValid,
passwordSettings,
isPasswordErrorShow,
setIsPasswordErrorShow,
} = props; } = props;
const passwordInputRef = useRef(null);
const onGeneratePasswordClick = () => { const onGeneratePasswordClick = () => {
const password = getNewPassword(); passwordInputRef.current.onGeneratePassword();
setPasswordValue(password);
}; };
const onCleanClick = () => { const onCleanClick = (e) => {
e.stopPropagation();
setPasswordValue(""); setPasswordValue("");
setIsPasswordValid(false);
}; };
const onCopyClick = () => { const onCopyClick = () => {
@ -62,9 +69,36 @@ const PasswordAccessBlock = (props) => {
} }
}; };
const onChangePassword = (password) => { const onChangePassword = (e, value) => {
setPasswordValue(password); setPasswordValue(value);
setIsPasswordValid(true); 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 ( return (
@ -74,17 +108,26 @@ const PasswordAccessBlock = (props) => {
<div className="edit-link_password-block"> <div className="edit-link_password-block">
<FieldContainer <FieldContainer
isVertical isVertical
hasError={!isPasswordValid} hasError={hasError}
errorMessage={t("Common:RequiredField")} errorMessage={errorMessage}
className="edit-link_password-block" className="edit-link_password-block"
> >
<SimulatePassword <PasswordInput
id="conversion-password"
className="edit-link_password-input" className="edit-link_password-input"
ref={passwordInputRef}
isDisabled={isLoading} isDisabled={isLoading}
hasError={!isPasswordValid}
inputValue={passwordValue} inputValue={passwordValue}
onChange={onChangePassword} onChange={onChangePassword}
inputMaxWidth="100%" passwordSettings={passwordSettings}
simpleView={false}
placeholder={t("Common:Password")}
hasError={hasError}
// onBlur={onBlurPassword}
onValidateInput={onValidatePassword}
isSimulateType
simulateSymbol="•"
{...tooltipData}
/> />
</FieldContainer> </FieldContainer>

View File

@ -65,6 +65,8 @@ const EditLinkPanel = (props) => {
isFormRoom, isFormRoom,
currentDeviceType, currentDeviceType,
setLinkParams, setLinkParams,
passwordSettings,
getPortalPasswordSettings,
} = props; } = props;
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@ -80,6 +82,7 @@ const EditLinkPanel = (props) => {
const [isExpired, setIsExpired] = useState(isExpiredDate); const [isExpired, setIsExpired] = useState(isExpiredDate);
const [isPasswordValid, setIsPasswordValid] = useState(true); const [isPasswordValid, setIsPasswordValid] = useState(true);
const [isPasswordErrorShow, setIsPasswordErrorShow] = useState(false);
const [linkValue, setLinkValue] = useState(shareLink); const [linkValue, setLinkValue] = useState(shareLink);
const [hasChanges, setHasChanges] = useState(false); const [hasChanges, setHasChanges] = useState(false);
@ -101,10 +104,12 @@ const EditLinkPanel = (props) => {
const onClose = () => setIsVisible(false); const onClose = () => setIsVisible(false);
const onSave = () => { const onSave = () => {
const isPasswordValid = !!passwordValue.trim(); if (
(!passwordValue.trim() || !isPasswordValid) &&
if (!isPasswordValid && passwordAccessIsChecked) { passwordAccessIsChecked
) {
setIsPasswordValid(false); setIsPasswordValid(false);
setIsPasswordErrorShow(true);
return; return;
} }
@ -140,11 +145,14 @@ const EditLinkPanel = (props) => {
toastr.success(t("Files:LinkSuccessfullyCreatedAndCopied")); 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(() => { .finally(() => {
setIsLoading(false); setIsLoading(false);
onClose();
}); });
}; };
@ -183,6 +191,18 @@ const EditLinkPanel = (props) => {
return () => window.removeEventListener("keydown", onKeyPress); return () => window.removeEventListener("keydown", onKeyPress);
}, [onKeyPress]); }, [onKeyPress]);
const getPasswordSettings = async () => {
setIsLoading(true);
await getPortalPasswordSettings();
setIsLoading(false);
};
useEffect(() => {
if (!passwordSettings) {
getPasswordSettings();
}
}, [passwordSettings]);
const linkNameIsValid = !!linkNameValue.trim(); const linkNameIsValid = !!linkNameValue.trim();
const expiredLinkText = isExpired const expiredLinkText = isExpired
@ -239,6 +259,9 @@ const EditLinkPanel = (props) => {
setPasswordValue={setPasswordValue} setPasswordValue={setPasswordValue}
setIsPasswordValid={setIsPasswordValid} setIsPasswordValid={setIsPasswordValid}
onChange={onPasswordAccessChange} onChange={onPasswordAccessChange}
passwordSettings={passwordSettings}
isPasswordErrorShow={isPasswordErrorShow}
setIsPasswordErrorShow={setIsPasswordErrorShow}
/> />
{!isFormRoom && ( {!isFormRoom && (
<ToggleBlock <ToggleBlock
@ -313,11 +336,13 @@ export default inject(
} = dialogsStore; } = dialogsStore;
const { externalLinks, editExternalLink, setExternalLink } = const { externalLinks, editExternalLink, setExternalLink } =
publicRoomStore; publicRoomStore;
const { currentDeviceType, passwordSettings, getPortalPasswordSettings } =
settingsStore;
const { isEdit, roomId, isPublic, isFormRoom } = linkParams; const { isEdit, roomId, isPublic, isFormRoom } = linkParams;
const linkId = linkParams?.link?.sharedTo?.id; const linkId = linkParams?.link?.sharedTo?.id;
const link = externalLinks.find((l) => l?.sharedTo?.id === linkId); const link = externalLinks.find((l) => l?.sharedTo?.id === linkId);
const shareLink = link?.sharedTo?.shareLink; const shareLink = link?.sharedTo?.shareLink;
return { return {
@ -340,10 +365,14 @@ export default inject(
language: authStore.language, language: authStore.language,
isPublic, isPublic,
isFormRoom, isFormRoom,
currentDeviceType: settingsStore.currentDeviceType, currentDeviceType,
setLinkParams, 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, ChangeEvent,
FocusEvent, FocusEvent,
MouseEvent, MouseEvent,
useEffect,
} from "react"; } from "react";
import { TooltipRefProps } from "react-tooltip"; import { TooltipRefProps } from "react-tooltip";
@ -109,6 +110,16 @@ const PasswordInput = React.forwardRef(
}: PasswordInputProps, }: PasswordInputProps,
ref, ref,
) => { ) => {
const usePrevious = (value: string) => {
const inputValueRef = useRef<string>();
useEffect(() => {
inputValueRef.current = value;
});
return inputValueRef.current;
};
const prevInputValue = usePrevious(inputValue ?? "");
const [state, setState] = useState({ const [state, setState] = useState({
type: inputType, type: inputType,
value: inputValue, value: inputValue,
@ -276,6 +287,8 @@ const PasswordInput = React.forwardRef(
(newPassword: string) => { (newPassword: string) => {
let newValue; let newValue;
if (!state.value) return newPassword;
const oldPassword = state.value ?? ""; const oldPassword = state.value ?? "";
const oldPasswordLength = oldPassword.length; const oldPasswordLength = oldPassword.length;
const newCaretPosition = document.getElementById( const newCaretPosition = document.getElementById(
@ -465,6 +478,14 @@ const PasswordInput = React.forwardRef(
} }
}, [caretPosition, state.type, state.value, isSimulateType]); }, [caretPosition, state.type, state.value, isSimulateType]);
useEffect(() => {
if (inputValue !== prevInputValue) {
onChangeAction?.({
target: { value: inputValue },
} as ChangeEvent<HTMLInputElement>);
}
}, [inputValue, prevInputValue, onChangeAction]);
React.useImperativeHandle( React.useImperativeHandle(
ref, ref,
() => { () => {