diff --git a/packages/client/src/helpers/constants.js b/packages/client/src/helpers/constants.js
index 1a952ddef3..c40d85552f 100644
--- a/packages/client/src/helpers/constants.js
+++ b/packages/client/src/helpers/constants.js
@@ -54,14 +54,7 @@ export const ConfirmType = Object.freeze({
* Enum for result of validation public room keys.
* @readonly
*/
-export const ValidationStatus = Object.freeze({
- Ok: 0,
- Invalid: 1,
- Expired: 2,
- Password: 3,
- InvalidPassword: 4,
- ExternalAccessDenied: 5,
-});
+
export const GUID_EMPTY = "00000000-0000-0000-0000-000000000000";
export const ID_NO_GROUP_MANAGER = "4a515a15-d4d6-4b8e-828e-e0586f18f3a3";
diff --git a/packages/client/src/pages/PublicPreview/PublicPreview.tsx b/packages/client/src/pages/PublicPreview/PublicPreview.tsx
index 51cad7d915..2e3a84f839 100644
--- a/packages/client/src/pages/PublicPreview/PublicPreview.tsx
+++ b/packages/client/src/pages/PublicPreview/PublicPreview.tsx
@@ -4,7 +4,7 @@ import { observer, inject } from "mobx-react";
import { useParams, useSearchParams } from "react-router-dom";
import api from "@docspace/shared/api";
-import { UrlActionType } from "@docspace/shared/enums";
+import { UrlActionType, ValidationStatus } from "@docspace/shared/enums";
import { toastr } from "@docspace/shared/components/toast";
import MediaViewer from "@docspace/shared/components/media-viewer/MediaViewer";
import { ViewerLoader } from "@docspace/shared/components/media-viewer/sub-components/ViewerLoader";
@@ -17,8 +17,6 @@ import type {
PlaylistType,
} from "@docspace/shared/components/media-viewer/MediaViewer.types";
-import { ValidationStatus } from "SRC_DIR/helpers/constants";
-
import type { PublicPreviewProps } from "./PublicPreview.types";
import { DEFAULT_EXTS_IMAGE } from "./PublicPreview.constants";
import { isAxiosError, useDeviceType } from "./PublicPreview.helpers";
diff --git a/packages/client/src/pages/PublicRoom/index.js b/packages/client/src/pages/PublicRoom/index.js
index a62d372f29..c02d9b02a0 100644
--- a/packages/client/src/pages/PublicRoom/index.js
+++ b/packages/client/src/pages/PublicRoom/index.js
@@ -29,7 +29,7 @@ import { observer, inject } from "mobx-react";
import { useNavigate, useLocation, useSearchParams } from "react-router-dom";
import Section from "@docspace/shared/components/section";
import { Loader } from "@docspace/shared/components/loader";
-import { ValidationStatus } from "../../helpers/constants";
+import { ValidationStatus } from "@docspace/shared/enums";
import SectionWrapper from "SRC_DIR/components/Section";
import RoomPassword from "./sub-components/RoomPassword";
import RoomErrors from "./sub-components/RoomErrors";
diff --git a/packages/client/src/pages/PublicRoom/sub-components/RoomPassword.js b/packages/client/src/pages/PublicRoom/sub-components/RoomPassword.js
index 38d393fc56..b74f86f907 100644
--- a/packages/client/src/pages/PublicRoom/sub-components/RoomPassword.js
+++ b/packages/client/src/pages/PublicRoom/sub-components/RoomPassword.js
@@ -37,7 +37,7 @@ import { frameCallCommand } from "@docspace/shared/utils/common";
import { toastr } from "@docspace/shared/components/toast";
import { FormWrapper } from "@docspace/shared/components/form-wrapper";
import PortalLogo from "@docspace/shared/components/portal-logo/PortalLogo";
-import { ValidationStatus } from "../../../helpers/constants";
+import { ValidationStatus } from "@docspace/shared/enums";
import PublicRoomIcon from "PUBLIC_DIR/images/icons/32/room/public.svg";
@@ -167,6 +167,7 @@ const RoomPassword = (props) => {
isDisabled={isLoading}
isDisableTooltip
forwardedRef={inputRef}
+ isAutoFocussed
/>
diff --git a/packages/client/src/store/PublicRoomStore.js b/packages/client/src/store/PublicRoomStore.js
index f8798e1112..f43bc697e1 100644
--- a/packages/client/src/store/PublicRoomStore.js
+++ b/packages/client/src/store/PublicRoomStore.js
@@ -38,7 +38,8 @@ import {
import { CategoryType } from "SRC_DIR/helpers/constants";
import { getCategoryUrl } from "SRC_DIR/helpers/utils";
-import { LinkType, ValidationStatus } from "../helpers/constants";
+import { LinkType } from "../helpers/constants";
+import { ValidationStatus } from "@docspace/shared/enums";
class PublicRoomStore {
externalLinks = [];
diff --git a/packages/doceditor/src/app/(root)/page.tsx b/packages/doceditor/src/app/(root)/page.tsx
index 03fc442ef0..c7652637a4 100644
--- a/packages/doceditor/src/app/(root)/page.tsx
+++ b/packages/doceditor/src/app/(root)/page.tsx
@@ -29,9 +29,11 @@ import { headers } from "next/headers";
import { getSelectorsByUserAgent } from "react-device-detect";
-import { getData } from "@/utils/actions";
+import { getData, validatePublicRoomKey } from "@/utils/actions";
import { RootPageProps } from "@/types";
import Root from "@/components/Root";
+import FilePassword from "@/components/file-password";
+import { ValidationStatus } from "@docspace/shared/enums";
const initialSearchParams: RootPageProps["searchParams"] = {
fileId: undefined,
@@ -58,6 +60,17 @@ async function Page({ searchParams }: RootPageProps) {
if (isMobile) type = "mobile";
}
+ if (share) {
+ const roomData = await validatePublicRoomKey(share, fileId ?? fileid ?? "");
+ if (!roomData) return;
+
+ const { status } = roomData.response;
+
+ if (status === ValidationStatus.Password) {
+ return ;
+ }
+ }
+
const startDate = new Date();
const data = await getData(
diff --git a/packages/doceditor/src/components/file-password/FilePassword.styled.ts b/packages/doceditor/src/components/file-password/FilePassword.styled.ts
new file mode 100644
index 0000000000..9173ea5248
--- /dev/null
+++ b/packages/doceditor/src/components/file-password/FilePassword.styled.ts
@@ -0,0 +1,202 @@
+// (c) Copyright Ascensio System SIA 2009-2024
+//
+// This program is a free software product.
+// You can redistribute it and/or modify it under the terms
+// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
+// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
+// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
+// any third-party rights.
+//
+// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
+// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
+//
+// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
+//
+// The interactive user interfaces in modified source and object code versions of the Program must
+// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
+//
+// Pursuant to Section 7(b) of the License you must retain the original Product logo when
+// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
+// trademark law for use of our trademarks.
+//
+// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
+// 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 styled, { css } from "styled-components";
+import { mobile, tablet } from "@docspace/shared/utils";
+import { isIOS, isFirefox } from "react-device-detect";
+
+import BackgroundPatternReactSvgUrl from "PUBLIC_DIR/images/background.pattern.react.svg?url";
+
+export const StyledPage = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 0 auto;
+ box-sizing: border-box;
+ background-image: url("${BackgroundPatternReactSvgUrl}");
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-size: cover;
+
+ .logo-wrapper {
+ display: block;
+ }
+
+ @media ${mobile} {
+ background-image: none;
+ height: 0;
+
+ .logo-wrapper {
+ display: none;
+ }
+ }
+
+ height: ${isIOS && !isFirefox ? "calc(var(--vh, 1vh) * 100)" : "100vh"};
+ width: 100vw;
+
+ @media ${tablet} {
+ padding: 0 16px;
+ }
+
+ @media ${mobile} {
+ ${(props) =>
+ props.theme.interfaceDirection === "rtl"
+ ? css`
+ padding: 0 16px 0 8px;
+ `
+ : css`
+ padding: 0 8px 0 16px;
+ `}
+ }
+
+ .subtitle {
+ margin-bottom: 32px;
+ }
+
+ .password-form {
+ width: 100%;
+ margin-bottom: 8px;
+ }
+
+ .subtitle {
+ margin-bottom: 32px;
+ }
+
+ .public-room-content {
+ padding-top: 9%;
+ justify-content: unset;
+ min-height: unset;
+
+ .public-room-text {
+ margin: 8px 0;
+ white-space: wrap;
+ }
+
+ .public-room-name {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 32px;
+ }
+
+ .public-room-icon {
+ min-width: 32px;
+ min-height: 32px;
+ }
+ }
+`;
+
+export const StyledContent = styled.div`
+ min-height: 100vh;
+ flex: 1 0 auto;
+ flex-direction: column;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+
+ @media ${mobile} {
+ justify-content: start;
+ min-height: 100%;
+ }
+
+ .bold {
+ font-weight: 600;
+ }
+`;
+
+export const StyledBody = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 56px auto;
+
+ @media ${mobile} {
+ width: 100%;
+ margin: 0 auto;
+ }
+
+ .title {
+ margin-bottom: 32px;
+ text-align: center;
+ }
+
+ .subtitle {
+ margin-bottom: 32px;
+ }
+
+ .portal-logo {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding-bottom: 64px;
+ }
+
+ .password-field-wrapper {
+ width: 100%;
+ }
+
+ .password-change-form {
+ margin-top: 32px;
+ margin-bottom: 16px;
+ }
+
+ .phone-input {
+ margin-bottom: 24px;
+ }
+
+ .delete-profile-confirm {
+ margin-bottom: 8px;
+ }
+
+ .phone-title {
+ margin-bottom: 8px;
+ }
+`;
+
+export const StyledSimpleNav = styled.div`
+ display: none;
+ height: 48px;
+ align-items: center;
+ justify-content: center;
+ background-color: ${(props) => props.theme?.login?.navBackground};
+
+ .logo {
+ height: 24px;
+ }
+
+ @media ${mobile} {
+ display: flex;
+
+ .language-combo-box {
+ position: absolute;
+ top: 7px;
+ right: 8px;
+ }
+ }
+`;
diff --git a/packages/doceditor/src/components/file-password/FilePassword.types.ts b/packages/doceditor/src/components/file-password/FilePassword.types.ts
new file mode 100644
index 0000000000..f0e229cc99
--- /dev/null
+++ b/packages/doceditor/src/components/file-password/FilePassword.types.ts
@@ -0,0 +1,34 @@
+// (c) Copyright Ascensio System SIA 2009-2024
+//
+// This program is a free software product.
+// You can redistribute it and/or modify it under the terms
+// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
+// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
+// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
+// any third-party rights.
+//
+// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
+// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
+//
+// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
+//
+// The interactive user interfaces in modified source and object code versions of the Program must
+// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
+//
+// Pursuant to Section 7(b) of the License you must retain the original Product logo when
+// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
+// trademark law for use of our trademarks.
+//
+// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
+// 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
+
+export interface FilePasswordProps {
+ shareKey: string;
+ title: string;
+ id: string;
+ status: string;
+ roomType: string;
+ entryTitle: string;
+}
diff --git a/packages/doceditor/src/components/file-password/index.tsx b/packages/doceditor/src/components/file-password/index.tsx
new file mode 100644
index 0000000000..231ae0d68b
--- /dev/null
+++ b/packages/doceditor/src/components/file-password/index.tsx
@@ -0,0 +1,211 @@
+// (c) Copyright Ascensio System SIA 2009-2024
+//
+// This program is a free software product.
+// You can redistribute it and/or modify it under the terms
+// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
+// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
+// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
+// any third-party rights.
+//
+// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
+// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
+//
+// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
+//
+// The interactive user interfaces in modified source and object code versions of the Program must
+// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
+//
+// Pursuant to Section 7(b) of the License you must retain the original Product logo when
+// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
+// trademark law for use of our trademarks.
+//
+// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
+// 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
+
+"use client";
+
+import React, { useState } from "react";
+import { Trans, useTranslation } from "react-i18next";
+
+import { Text } from "@docspace/shared/components/text";
+import { Button, ButtonSize } from "@docspace/shared/components/button";
+import { FieldContainer } from "@docspace/shared/components/field-container";
+import { PasswordInput } from "@docspace/shared/components/password-input";
+import { FormWrapper } from "@docspace/shared/components/form-wrapper";
+
+import {
+ StyledPage,
+ StyledContent,
+ StyledBody,
+ StyledSimpleNav,
+} from "./FilePassword.styled";
+import { FilePasswordProps } from "./FilePassword.types";
+
+import PublicRoomIcon from "PUBLIC_DIR/images/icons/32/room/public.svg";
+import { InputSize, InputType } from "@docspace/shared/components/text-input";
+import { toastr } from "@docspace/shared/components/toast";
+import { TData } from "@docspace/shared/components/toast/Toast.type";
+
+import { getLogoUrl } from "@docspace/shared/utils";
+import { useTheme } from "styled-components";
+import { ValidationStatus, WhiteLabelLogoType } from "@docspace/shared/enums";
+import { validatePublicRoomPassword } from "@docspace/shared/api/rooms";
+import Image from "next/image";
+
+const FilesPassword = ({ shareKey, title, entryTitle }: FilePasswordProps) => {
+ const { t } = useTranslation(["Common"]);
+
+ const theme = useTheme();
+
+ const [password, setPassword] = useState("");
+ const [passwordValid, setPasswordValid] = useState(true);
+ const [isLoading, setIsLoading] = useState(false);
+ const [errorMessage, setErrorMessage] = useState("");
+
+ const onChangePassword = (e: React.ChangeEvent) => {
+ setPassword(e.target.value);
+ !passwordValid && setPasswordValid(true);
+ };
+ const onKeyPress = (event: React.KeyboardEvent) => {
+ if (event.key === "Enter") {
+ onSubmit();
+ }
+ };
+
+ const onSubmit = async () => {
+ if (!password.trim()) {
+ setPasswordValid(false);
+ setErrorMessage(t("Common:RequiredField"));
+ } else {
+ setErrorMessage("");
+ }
+
+ if (!passwordValid || !password.trim()) {
+ setIsLoading(false);
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ const res = await validatePublicRoomPassword(shareKey, password);
+
+ if (res?.status === ValidationStatus.Ok) {
+ return window.location.reload();
+ }
+
+ setIsLoading(false);
+
+ if (res?.status === ValidationStatus.InvalidPassword) {
+ setErrorMessage(t("Common:IncorrectPassword"));
+ return;
+ }
+ } catch (error) {
+ toastr.error(error as TData);
+ setIsLoading(false);
+ }
+ };
+
+ const logoUrl = getLogoUrl(WhiteLabelLogoType.LoginPage, !theme.isBase);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+