Merge branch 'release/v2.6.0' into feature/refacroting-selector

This commit is contained in:
Timofey Boyko 2024-07-04 12:24:15 +03:00
commit d6de247212
19 changed files with 168 additions and 24 deletions

View File

@ -74,6 +74,7 @@
"@babel/preset-typescript": "^7.21.0",
"@svgr/webpack": "^5.5.0",
"@types/eslint": "^8.44.7",
"@types/he": "^1.2.3",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"babel-loader": "^8.3.0",

View File

@ -21,6 +21,5 @@
"UploadFromPortalDescription": "Upload any type files from Documents or Rooms",
"UploadFromPortalTitle": "Upload from {{productName}}",
"UploadPDFFormOptionDescription": "Select a ready PDF form available in {{productName}} and upload it to the room.",
"UserEmptyDescription": "Files and folders uploaded by admins will appeared here.",
"UserEmptyTitle": "No docs here yet"
"UserEmptyDescription": "Files and folders uploaded by admins will appeared here."
}

View File

@ -90,6 +90,7 @@ const EmptyFolderContainer = ({
isFolder={!isRoom}
folderId={folderId}
parentRoomType={parentRoomType}
isArchiveFolderRoot={isArchiveFolderRoot}
/>
);
}

View File

@ -63,6 +63,7 @@ export const getDescription = (
isFolder: boolean,
folderType: Nullable<FolderType>,
parentRoomType: Nullable<FolderType>,
isArchiveFolderRoot: boolean,
): string => {
const isCollaborator = access === ShareAccessRights.Collaborator;
@ -88,8 +89,13 @@ export const getDescription = (
],
() => t("Files:EmptyFormSubFolderHeaderText"),
)
.with([FolderType.FormRoom, null, P.when(() => isNotAdmin)], () =>
t("EmptyView:FormFolderDefaultUserDescription"),
.with(
[
FolderType.FormRoom,
null,
P.when(() => isNotAdmin || isArchiveFolderRoot),
],
() => t("EmptyView:FormFolderDefaultUserDescription"),
)
.with([FolderType.FormRoom, null, P._], () =>
t("EmptyView:FormFolderDefaultDescription", {
@ -99,7 +105,8 @@ export const getDescription = (
.otherwise(() => "");
}
if (isNotAdmin) return t("EmptyView:UserEmptyDescription");
if (isNotAdmin || isArchiveFolderRoot)
return t("EmptyView:UserEmptyDescription");
if (isCollaborator) return t("EmptyView:CollaboratorEmptyDesciprtion");
@ -113,6 +120,7 @@ export const getTitle = (
isFolder: boolean,
folderType: Nullable<FolderType>,
parentRoomType: Nullable<FolderType>,
isArchiveFolderRoot: boolean,
): string => {
const isCollaborator = access === ShareAccessRights.Collaborator;
@ -135,8 +143,13 @@ export const getTitle = (
.with([P._, FolderType.SubFolderInProgress, P._], () =>
t("Files:EmptyFormSubFolderProgressDescriptionText"),
)
.with([FolderType.FormRoom, null, P.when(() => isNotAdmin)], () =>
t("EmptyView:FormFolderDefaultUserTitle"),
.with(
[
FolderType.FormRoom,
null,
P.when(() => isNotAdmin || isArchiveFolderRoot),
],
() => t("EmptyView:FormFolderDefaultUserTitle"),
)
.with([FolderType.FormRoom, null, P._], () =>
t("EmptyView:FormFolderDefaultTitle"),
@ -146,7 +159,7 @@ export const getTitle = (
if (isCollaborator) return t("EmptyView:CollaboratorEmptyTitle");
if (isNotAdmin) return t("EmptyView:UserEmptyTitle");
if (isNotAdmin || isArchiveFolderRoot) return t("Files:EmptyScreenFolder");
switch (type) {
case RoomsType.FormRoom:
@ -317,6 +330,7 @@ export const getOptions = (
isFolder: boolean,
folderType: Nullable<FolderType>,
parentRoomType: Nullable<FolderType>,
isArchiveFolderRoot: boolean,
actions: OptionActions,
): EmptyViewItemType[] => {
const isFormFiller = access === ShareAccessRights.FormFilling;
@ -417,6 +431,8 @@ export const getOptions = (
],
};
if (isArchiveFolderRoot) return [];
if (isFolder) {
return match([parentRoomType, folderType, access])
.with(

View File

@ -30,6 +30,7 @@ const EmptyViewContainer = observer(
folderType,
selectedFolder,
parentRoomType,
isArchiveFolderRoot,
onClickInviteUsers,
onCreateAndCopySharedLink,
setSelectFileFormRoomDialogVisible,
@ -95,6 +96,7 @@ const EmptyViewContainer = observer(
isFolder,
folderType,
parentRoomType,
isArchiveFolderRoot,
);
const title = getTitle(
type,
@ -103,6 +105,7 @@ const EmptyViewContainer = observer(
isFolder,
folderType,
parentRoomType,
isArchiveFolderRoot,
);
const icon = getIcon(
type,
@ -114,7 +117,16 @@ const EmptyViewContainer = observer(
);
return { description, title, icon };
}, [type, t, theme.isBase, access, isFolder, folderType, parentRoomType]);
}, [
type,
t,
theme.isBase,
access,
isFolder,
folderType,
parentRoomType,
isArchiveFolderRoot,
]);
const options = useMemo(
() =>
@ -126,6 +138,7 @@ const EmptyViewContainer = observer(
isFolder,
folderType,
parentRoomType,
isArchiveFolderRoot,
{
inviteUser,
onCreate,
@ -141,6 +154,7 @@ const EmptyViewContainer = observer(
isFolder,
folderType,
parentRoomType,
isArchiveFolderRoot,
t,
inviteUser,
uploadFromDocspace,

View File

@ -28,6 +28,7 @@ export interface EmptyViewContainerProps {
parentRoomType: Nullable<FolderType>;
folderType: Nullable<FolderType>;
isFolder: boolean;
isArchiveFolderRoot: boolean;
onClickInviteUsers?: (folderId: string | number, roomType: RoomsType) => void;
setSelectFileFormRoomDialogVisible?: TStore["dialogsStore"]["setSelectFileFormRoomDialogVisible"];
onCreateAndCopySharedLink?: TStore["contextOptionsStore"]["onCreateAndCopySharedLink"];

View File

@ -109,6 +109,7 @@ const ConfirmRoute = ({
switch (validationResult) {
case ValidationResult.Ok:
case ValidationResult.UserExisted:
const confirmHeader = search.slice(1);
const linkData = {
...confirmLinkData,
@ -129,7 +130,10 @@ const ConfirmRoute = ({
setState((val) => ({ ...val, isLoaded: true, linkData, roomData }));
break;
case ValidationResult.Invalid:
console.error("invlid link", { confirmLinkData, validationResult });
console.error("invalid link", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl(
window.ClientConfig?.proxy?.url,
path,
@ -159,6 +163,24 @@ const ConfirmRoute = ({
"/error?messageKey=20",
);
break;
case ValidationResult.QuotaFailed:
console.error("access below quota", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl(
window.ClientConfig?.proxy?.url,
path,
"/error",
);
break;
case ValidationResult.UserExcluded:
console.error("user excluded", {
confirmLinkData,
validationResult,
});
window.location.replace(defaultPage);
break;
default:
console.error("unknown link", {
confirmLinkData,

View File

@ -39,4 +39,7 @@ export const enum ValidationResult {
Invalid = 1,
Expired = 2,
TariffLimit = 3,
UserExisted = 4,
UserExcluded = 5,
QuotaFailed = 6,
}

View File

@ -122,6 +122,7 @@ const CreateUserForm = (props) => {
const emailFromLink = linkData?.email ? linkData.email : "";
const roomName = roomData?.title;
const roomId = roomData?.roomId;
const [email, setEmail] = useState(emailFromLink);
const [emailValid, setEmailValid] = useState(true);
@ -199,6 +200,7 @@ const CreateUserForm = (props) => {
roomName,
firstName: user.firstName,
lastName: user.lastName,
linkData: linkData,
}),
),
);
@ -209,6 +211,14 @@ const CreateUserForm = (props) => {
"max-age": COOKIE_EXPIRATION_YEAR,
});
const finalUrl = roomId
? `/rooms/shared/filter?folder=${roomId}`
: defaultPage;
if (roomId) {
sessionStorage.setItem("referenceUrl", finalUrl);
}
window.location.href = combineUrl(
window.ClientConfig?.proxy?.url,
"/login",

View File

@ -24,13 +24,14 @@
// 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 { inject, observer } from "mobx-react";
import { decode } from "he";
import { withTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import { Link } from "@docspace/shared/components/link";
import { toastr } from "@docspace/shared/components/toast";
import { withTranslation } from "react-i18next";
import { Text } from "@docspace/shared/components/text";
import { RoomsType } from "@docspace/shared/enums";
import { TTranslation } from "@docspace/shared/types";
import { StyledHistoryLink } from "../../../styles/history";
@ -65,7 +66,9 @@ interface HistoryRoomExternalLinkProps {
isEdit: boolean;
link: TFeedData;
roomId: number | string;
isFormRoom?: boolean;
}) => void;
isFormRoom?: boolean;
}
const HistoryRoomExternalLink = ({
@ -75,6 +78,7 @@ const HistoryRoomExternalLink = ({
setEditLinkPanelIsVisible,
setLinkParams,
roomId,
isFormRoom,
}: HistoryRoomExternalLinkProps) => {
const onEditLink = () => {
if (!feedData.sharedTo) {
@ -82,7 +86,12 @@ const HistoryRoomExternalLink = ({
return;
}
setLinkParams({ isEdit: true, link: feedData, roomId });
setLinkParams({
isEdit: true,
link: feedData,
roomId,
isFormRoom,
});
setEditLinkPanelIsVisible(true);
};
@ -90,29 +99,31 @@ const HistoryRoomExternalLink = ({
<StyledHistoryLink>
{canEditLink ? (
<Link className="text link" onClick={onEditLink}>
{decode(feedData.title || feedData.sharedTo?.title)}
{decode((feedData.title || feedData.sharedTo?.title) ?? "")}
</Link>
) : (
<Text as="span" className="text">
{decode(feedData.title || feedData.sharedTo?.title)}
{decode((feedData.title || feedData.sharedTo?.title) ?? "")}
</Text>
)}
</StyledHistoryLink>
);
};
export default inject(({ userStore, dialogsStore, infoPanelStore }) => {
export default inject<TStore>(({ userStore, dialogsStore, infoPanelStore }) => {
const { infoPanelSelection } = infoPanelStore;
const { setLinkParams, setEditLinkPanelIsVisible } = dialogsStore;
const { user } = userStore;
const { id } = infoPanelSelection;
const { id, roomType } = infoPanelSelection!;
const cannotEdit = user.isVisitor || user.isCollaborator;
const isFormRoom = roomType === RoomsType.FormRoom;
const cannotEdit = user?.isVisitor || user?.isCollaborator;
return {
canEditLink: !cannotEdit,
setEditLinkPanelIsVisible,
setLinkParams,
roomId: id,
isFormRoom,
};
})(withTranslation(["InfoPanel"])(observer(HistoryRoomExternalLink)));

View File

@ -26,6 +26,7 @@
import React from "react";
import styled, { css } from "styled-components";
import { ReactSVG } from "react-svg";
import { Base } from "@docspace/shared/themes";
@ -90,9 +91,10 @@ const PresetTile = (props) => {
<Text fontSize="16px" lineHeight="22px" fontWeight={700}>
{title}
</Text>
<img height={180} width={310} src={image} alt={title} />
<ReactSVG src={image} />
<Text lineHeight="20px">{description}</Text>
</div>
<Button
className="navigationButton"
label={t("SetUp")}

View File

@ -105,7 +105,7 @@ const confirmRoutes = [
{
path: "LinkInvite",
element: (
<ConfirmRoute doAuthenticated={AuthenticatedAction.Redirect}>
<ConfirmRoute doAuthenticated={AuthenticatedAction.None}>
<CreateUserForm />
</ConfirmRoute>
),

View File

@ -48,7 +48,7 @@ export const metadata: Metadata = {
};
async function Page({ searchParams }: RootPageProps) {
const { fileId, fileid, version, doc, action, share, editorType } =
const { fileId, fileid, version, doc, action, share, editorType, error } =
searchParams ?? initialSearchParams;
const startDate = new Date();
@ -64,6 +64,9 @@ async function Page({ searchParams }: RootPageProps) {
const timer = new Date().getTime() - startDate.getTime();
if (data.error?.status === "not-found" && error) {
data.error.message = error;
}
return <Root {...data} timer={timer} />;
}

View File

@ -25,6 +25,7 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React from "react";
import { usePathname, useSearchParams } from "next/navigation";
import IConfig from "@onlyoffice/document-editor-react/dist/esm/types/model/config";
@ -83,6 +84,9 @@ const useEditorEvents = ({
openOnNewPage,
t,
}: UseEventsProps) => {
const searchParams = useSearchParams();
const pathname = usePathname();
const [events, setEvents] = React.useState<IConfigEvents>({});
const [documentReady, setDocumentReady] = React.useState(false);
const [createUrl, setCreateUrl] = React.useState<Nullable<string>>(null);
@ -163,7 +167,24 @@ const useEditorEvents = ({
if (config?.Error) docEditor?.showMessage?.(config.Error);
}
}
}, [config?.Error, errorMessage, isSkipError, t]);
const message = searchParams.get("message");
if (message) {
docEditor?.showMessage?.(message);
let search = "?";
let idx = 0;
searchParams.forEach((value, key) => {
if (key !== "message") {
if (idx) search += "&";
idx++;
search += `${key}=${value}`;
}
});
history.pushState({}, "", `${pathname}${search}`);
}
}, [config?.Error, errorMessage, isSkipError, searchParams, pathname, t]);
const onDocumentReady = React.useCallback(() => {
// console.log("onDocumentReady", { docEditor });

View File

@ -68,6 +68,7 @@ export type RootPageProps = {
action: ActionType;
share: string;
editorType: string;
error?: string;
}>;
};
export type TDocumentInfo = {

View File

@ -46,12 +46,12 @@ import { createPasswordHash } from "@docspace/shared/utils/common";
import { checkPwd } from "@docspace/shared/utils/desktop";
import { login } from "@docspace/shared/utils/loginUtils";
import { toastr } from "@docspace/shared/components/toast";
import { thirdPartyLogin } from "@docspace/shared/api/user";
import { thirdPartyLogin, checkConfirmLink } from "@docspace/shared/api/user";
import { setWithCredentialsStatus } from "@docspace/shared/api/client";
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
import { LoginFormProps } from "@/types";
import { getEmailFromInvitation } from "@/utils";
import { getEmailFromInvitation, getConfirmDataFromInvitation } from "@/utils";
import EmailContainer from "./sub-components/EmailContainer";
import PasswordContainer from "./sub-components/PasswordContainer";
@ -250,13 +250,18 @@ const LoginForm = ({
const hash = !isLdapLoginChecked
? createPasswordHash(pass, hashSettings)
: undefined;
const pwd = isLdapLoginChecked ? pass : undefined;
const confirmData = getConfirmDataFromInvitation(loginData);
isDesktop && checkPwd();
const session = !isChecked;
login(user, hash, pwd, session, captchaToken, currentCulture, reCaptchaType)
.then((res: string | object) => {
checkConfirmLink(confirmData);
const isConfirm = typeof res === "string" && res.includes("confirm");
const redirectPath =
referenceUrl || sessionStorage.getItem("referenceUrl");

View File

@ -126,6 +126,12 @@ export const getInvitationLinkData = (encodeString: string) => {
firstName: string;
lastName: string;
type: string;
linkData?: {
confirmHeader?: string;
key: string;
type: string;
uid?: string;
};
};
return queryParams;
@ -140,3 +146,15 @@ export const getEmailFromInvitation = (encodeString: Nullable<string>) => {
return queryParams.email;
};
export const getConfirmDataFromInvitation = (
encodeString: Nullable<string>,
) => {
if (!encodeString) return "";
const queryParams = getInvitationLinkData(encodeString);
if (!queryParams || !queryParams.linkData) return {};
return queryParams.linkData;
};

View File

@ -310,6 +310,12 @@ const SubMenu = (props: {
onItemClick(e, item);
};
const onMouseDown = (e: React.MouseEvent) => {
if (e.button !== 1) return;
onClick(e);
};
let content = (
<a
href={item.url || "#"}
@ -355,6 +361,7 @@ const SubMenu = (props: {
className={className || ""}
style={{ ...item.style, ...style }}
onClick={onClick}
onMouseDown={onMouseDown}
onMouseEnter={(e) => onItemMouseEnter(e, item)}
>
{content}
@ -376,6 +383,7 @@ const SubMenu = (props: {
className={className || ""}
style={{ ...item.style, ...style }}
onClick={onClick}
onMouseDown={onMouseDown}
onMouseEnter={(e) => onItemMouseEnter(e, item)}
>
{content}

View File

@ -2581,6 +2581,7 @@ __metadata:
"@codemirror/lang-javascript": "npm:^6.2.2"
"@svgr/webpack": "npm:^5.5.0"
"@types/eslint": "npm:^8.44.7"
"@types/he": "npm:^1.2.3"
"@typescript-eslint/eslint-plugin": "npm:^6.12.0"
"@typescript-eslint/parser": "npm:^6.12.0"
"@uiw/codemirror-theme-github": "npm:^4.21.25"
@ -7756,6 +7757,13 @@ __metadata:
languageName: node
linkType: hard
"@types/he@npm:^1.2.3":
version: 1.2.3
resolution: "@types/he@npm:1.2.3"
checksum: 10/e77851c73dd7b9902d92fe0118a26246a7f3676a3a1c6eb1408305187ef73b57c22550b1435946b983267f961d935554d5d0e1b458416932552f31e763e1aa41
languageName: node
linkType: hard
"@types/hoist-non-react-statics@npm:*":
version: 3.3.5
resolution: "@types/hoist-non-react-statics@npm:3.3.5"