Merge branch 'develop' into feature/security-active-sessions

This commit is contained in:
Alexey Safronov 2024-07-12 11:41:28 +04:00
commit 2599b59ad9
28 changed files with 351 additions and 4199 deletions

View File

@ -51906,6 +51906,138 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>RoomsPinLimitMessage</name>
<description/>
<comment/>
<default_text/>
<translations>
<translation>
<language>ar-SA</language>
<approved>false</approved>
</translation>
<translation>
<language>az-Latn-AZ</language>
<approved>false</approved>
</translation>
<translation>
<language>bg-BG</language>
<approved>false</approved>
</translation>
<translation>
<language>cs-CZ</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>el-GR</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-ES</language>
<approved>false</approved>
</translation>
<translation>
<language>fi-FI</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>hy-AM</language>
<approved>false</approved>
</translation>
<translation>
<language>it-IT</language>
<approved>false</approved>
</translation>
<translation>
<language>ja-JP</language>
<approved>false</approved>
</translation>
<translation>
<language>ko-KR</language>
<approved>false</approved>
</translation>
<translation>
<language>lo-LA</language>
<approved>false</approved>
</translation>
<translation>
<language>lv-LV</language>
<approved>false</approved>
</translation>
<translation>
<language>nl-NL</language>
<approved>false</approved>
</translation>
<translation>
<language>pl-PL</language>
<approved>false</approved>
</translation>
<translation>
<language>pt-BR</language>
<approved>false</approved>
</translation>
<translation>
<language>pt-PT</language>
<approved>false</approved>
</translation>
<translation>
<language>ro-RO</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>si-SI</language>
<approved>false</approved>
</translation>
<translation>
<language>sk-SK</language>
<approved>false</approved>
</translation>
<translation>
<language>sl-SI</language>
<approved>false</approved>
</translation>
<translation>
<language>sr-Cyrl-RS</language>
<approved>false</approved>
</translation>
<translation>
<language>sr-Latn-RS</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
<translation>
<language>uk-UA</language>
<approved>false</approved>
</translation>
<translation>
<language>vi-VN</language>
<approved>false</approved>
</translation>
<translation>
<language>zh-CN</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>RoomsPinned</name>
<description/>

View File

@ -149,6 +149,7 @@
"RoomOwner": "Room owner",
"RoomPinned": "Room pinned",
"RoomRemoved": "Room removed",
"RoomsPinLimitMessage": "You can't pin more than 10 rooms to the top. Unpin some that are currently pinned.",
"RoomsPinned": "Rooms pinned: {{count}}",
"RoomsRemoved": "Rooms removed",
"RoomsUnpinned": "Rooms unpinned: {{count}}",

View File

@ -179,8 +179,11 @@ const EditRoomEvent = ({
actions.push(addTagsToRoom(room.id, newTags));
room.tags = tags;
}
if (removedTags.length)
if (removedTags.length) {
actions.push(removeTagsFromRoom(room.id, removedTags));
room.tags = tags;
}
await Promise.all(actions);

View File

@ -57,11 +57,10 @@ const Main = (props) => {
React.useEffect(() => {
window.addEventListener("resize", onResize);
window.visualViewport.addEventListener("resize", onResize);
return () => {
window.addEventListener("resize", onResize);
window.visualViewport.removeEventListener("resize", onResize);
clearTimeout(updateSizeRef.current);
};
}, [onResize, isFrame]);

View File

@ -1112,12 +1112,9 @@ const SectionFilterContent = ({
label = t("Media");
break;
case FilterType.FilesOnly.toString():
label = t("AllFiles");
label = t("Translations:Files");
break;
case FilterType.OFormTemplateOnly.toString():
label = t("FormsTemplates");
break;
case FilterType.OFormOnly.toString():
case FilterType.Pdf.toString():
label = t("Forms");
break;
}
@ -1640,18 +1637,18 @@ const SectionFilterContent = ({
isLast: !isTrash,
},
...folders,
{
id: "filter_type-all-files",
key: FilterType.FilesOnly.toString(),
group: FilterGroups.filterType,
label: t("Translations:Files").toLowerCase(),
},
{
id: "filter_type-documents",
key: FilterType.DocumentsOnly.toString(),
group: FilterGroups.filterType,
label: t("Common:Documents").toLowerCase(),
},
{
id: "filter_type-presentations",
key: FilterType.PresentationsOnly.toString(),
group: FilterGroups.filterType,
label: t("Translations:Presentations").toLowerCase(),
},
{
id: "filter_type-spreadsheets",
key: FilterType.SpreadsheetsOnly.toString(),
@ -1659,27 +1656,20 @@ const SectionFilterContent = ({
label: t("Translations:Spreadsheets").toLowerCase(),
},
{
id: "filter_type-form-templates",
key: FilterType.OFormTemplateOnly.toString(),
id: "filter_type-presentations",
key: FilterType.PresentationsOnly.toString(),
group: FilterGroups.filterType,
label: t("FormsTemplates").toLowerCase(),
label: t("Translations:Presentations").toLowerCase(),
},
{
id: "filter_type-forms",
key: FilterType.OFormOnly.toString(),
key: FilterType.Pdf.toString(),
group: FilterGroups.filterType,
label: t("Forms").toLowerCase(),
},
...archives,
...images,
...media,
{
id: "filter_type-all-files",
key: FilterType.FilesOnly.toString(),
group: FilterGroups.filterType,
label: t("AllFiles").toLowerCase(),
},
];
const subjectOptions = [

View File

@ -159,6 +159,7 @@ const PureHome = (props) => {
userId,
getFolderModel,
scrollToTop,
isEmptyGroups,
} = props;
//console.log(t("ComingSoon"))
@ -173,6 +174,7 @@ const PureHome = (props) => {
const isPeopleAccounts = location.pathname.includes("accounts/people");
const isGroupsAccounts =
location.pathname.includes("accounts/groups") && !groupId;
const isAccountsEmptyFilter = isGroupsAccounts && isEmptyGroups;
const { onDrop } = useFiles({
t,
@ -387,8 +389,10 @@ const PureHome = (props) => {
</Section.SectionWarning>
)}
{(((!isEmptyPage || showFilterLoader) && !isErrorRoomNotAvailable) ||
isAccountsPage) &&
{(((!isEmptyPage || showFilterLoader) &&
!isAccountsEmptyFilter &&
!isErrorRoomNotAvailable) ||
(!isAccountsEmptyFilter && isAccountsPage)) &&
!isSettingsPage && (
<Section.SectionFilter>
{isFrame ? (
@ -560,7 +564,8 @@ export default inject(
const { usersStore, groupsStore, viewAs: accountsViewAs } = peopleStore;
const { getUsersList: fetchPeople } = usersStore;
const { getGroups: fetchGroups, fetchGroup } = groupsStore;
const { getGroups: fetchGroups, fetchGroup, groups } = groupsStore;
const isEmptyGroups = (groups && groups.length === 0) || !Boolean(groups);
if (!firstLoad) {
if (isLoading) {
@ -673,6 +678,7 @@ export default inject(
setSelectedFolder,
getFolderModel,
scrollToTop,
isEmptyGroups,
};
},
)(observer(Home));

View File

@ -112,11 +112,7 @@ const FileSelector = (props) => {
label: t(`Translations:Spreadsheets`),
},
{
key: FilterType.OFormTemplateOnly,
label: t(`Files:FormsTemplates`),
},
{
key: FilterType.OFormOnly,
key: FilterType.Pdf,
label: t(`Files:Forms`),
},
{
@ -133,7 +129,7 @@ const FileSelector = (props) => {
},
{
key: FilterType.FilesOnly,
label: t(`Files:AllFiles`),
label: t(`Translations:Files`),
},
];

View File

@ -138,7 +138,7 @@ export const FilterBlock = ({ t, config, setConfig }) => {
const filterOptions = [
{
key: "filter-type-all",
label: t("Files:AllFiles"),
label: t("Translations:Files"),
typeKey: FilterType.FilesOnly,
},
{
@ -176,15 +176,10 @@ export const FilterBlock = ({ t, config, setConfig }) => {
label: t("Files:Media"),
typeKey: FilterType.MediaOnly,
},
{
key: "filter-type-forms-templates",
label: t("Files:FormsTemplates"),
typeKey: FilterType.OFormTemplateOnly,
},
{
key: "filter-type-forms",
label: t("Files:Forms"),
typeKey: FilterType.OFormOnly,
typeKey: FilterType.Pdf,
},
];

View File

@ -27,7 +27,7 @@
import SendClockReactSvgUrl from "PUBLIC_DIR/images/send.clock.react.svg?url";
import PencilOutlineReactSvgUrl from "PUBLIC_DIR/images/pencil.outline.react.svg?url";
import DefaultUserAvatarMax from "PUBLIC_DIR/images/default_user_photo_size_200-200.png";
import React, { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { ReactSVG } from "react-svg";
import { useTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
@ -92,20 +92,43 @@ const MainProfile = (props) => {
} = props;
const [horizontalOrientation, setHorizontalOrientation] = useState(false);
const [dimension, setDimension] = useState(window.innerHeight);
const [dropDownMaxHeight, setDropDownMaxHeight] = useState(352);
const { interfaceDirection } = useTheme();
const dirTooltip = interfaceDirection === "rtl" ? "left" : "right";
const { isOwner, isAdmin, isRoomAdmin, isCollaborator } = profile;
useEffect(() => {
checkWidth();
window.addEventListener("resize", checkWidth);
return () => window.removeEventListener("resize", checkWidth);
}, []);
const comboBoxRef = useRef(null);
const checkWidth = () => {
setDimension(innerHeight);
const updateDropDownMaxHeight = () => {
const newDimension = window.innerHeight;
if (comboBoxRef.current) {
const comboBoxRect = comboBoxRef.current.getBoundingClientRect();
let availableSpaceBottom = newDimension - comboBoxRect.bottom - 20;
availableSpaceBottom = Math.max(availableSpaceBottom, 100);
const newDropDownMaxHeight = Math.min(availableSpaceBottom, 352);
setDropDownMaxHeight(newDropDownMaxHeight);
}
};
const checkScroll = () => {
updateDropDownMaxHeight();
};
useEffect(() => {
updateDropDownMaxHeight();
window.addEventListener("resize", updateDropDownMaxHeight);
window.addEventListener("scroll", checkScroll);
return () => {
window.removeEventListener("resize", updateDropDownMaxHeight);
window.removeEventListener("scroll", checkScroll);
};
}, [cultureNames]);
useEffect(() => {
if (!isMobileOnly) return;
if (!isMobile()) {
@ -113,7 +136,7 @@ const MainProfile = (props) => {
} else {
setHorizontalOrientation(false);
}
};
}, []);
const role = getUserRole(profile);
@ -356,7 +379,7 @@ const MainProfile = (props) => {
tooltipContent={tooltipLanguage}
/>
</StyledLabel>
<div className="language-combo-box-wrapper">
<div className="language-combo-box-wrapper" ref={comboBoxRef}>
<ComboBox
className="language-combo-box"
directionY={isMobileHorizontalOrientation ? "bottom" : "both"}
@ -368,7 +391,7 @@ const MainProfile = (props) => {
scaledOptions={false}
size="content"
showDisabledItems={true}
dropDownMaxHeight={dimension < 620 ? 200 : 364}
dropDownMaxHeight={dropDownMaxHeight}
manualWidth="280px"
isDefaultMode={
isMobileHorizontalOrientation
@ -552,7 +575,7 @@ const MainProfile = (props) => {
scaledOptions={false}
size="content"
showDisabledItems={true}
dropDownMaxHeight={dimension < 620 ? 200 : 364}
dropDownMaxHeight={dropDownMaxHeight}
manualWidth="280px"
isDefaultMode={
isMobileHorizontalOrientation

View File

@ -82,10 +82,7 @@ const Sdk = ({
[FilterType.FoldersOnly]: t("Common:SelectTypeFiles", {
type: t("Translations:Folders").toLowerCase(),
}),
[FilterType.OFormTemplateOnly]: t("Common:SelectTypeFiles", {
type: t("Files:FormsTemplates").toLowerCase(),
}),
[FilterType.OFormOnly]: t("Common:SelectTypeFiles", {
[FilterType.Pdf]: t("Common:SelectTypeFiles", {
type: t("Files:Forms").toLowerCase(),
}),
EditorSupportedTypes: t("Common:SelectTypeFiles", {

View File

@ -1108,7 +1108,10 @@ class FilesActionStore {
: t("RoomPinned"),
),
)
.catch((error) => console.log(error));
.catch((error) => {
console.log(error);
toastr.error(t("RoomsPinLimitMessage"));
});
case "unpin":
items.forEach((item) => {
updateRoomPin(item);

View File

@ -2072,7 +2072,8 @@ class FilesStore {
item.viewAccessibility.ImageView || item.viewAccessibility.MediaView;
const canViewFile = item.viewAccessibility.WebView;
const isMasterForm = item.fileExst === ".docxf";
const isOldForm =
item.fileExst === ".docxf" || item.fileExst === ".oform"; //TODO: Remove after change security options
const isPdf = item.fileExst === ".pdf";
let fileOptions = [
@ -2197,10 +2198,10 @@ class FilesStore {
fileOptions = this.removeOptions(fileOptions, ["move"]);
}
if (!(isMasterForm && canDuplicate))
if (!(isOldForm && canDuplicate))
fileOptions = this.removeOptions(fileOptions, ["make-form"]);
if (!canSubmitToFormGallery || isMasterForm) {
if (!canSubmitToFormGallery || isOldForm) {
fileOptions = this.removeOptions(fileOptions, [
"submit-to-gallery",
"separator-SubmitToGallery",
@ -3541,7 +3542,7 @@ class FilesStore {
case FilterType.ArchiveOnly:
return t("Archives");
case FilterType.FilesOnly:
return t("AllFiles");
return t("Translations:Files");
case `room-${RoomsType.FillingFormsRoom}`:
return t("Common:FillingFormRooms");
case `room-${RoomsType.CustomRoom}`:

View File

@ -1647,6 +1647,7 @@ class UploadDataStore {
.then(() =>
this.moveToCopyTo(destFolderId, pbData, true, fileIds, folderIds),
)
.catch((error) => toastr.error(error))
.finally(async () => {
//to update the status of trashIsEmpty filesStore
if (this.treeFoldersStore.isRecycleBinFolder)

View File

@ -1,51 +0,0 @@
{
"name": "@docspace/common",
"version": "2.0.3",
"private": true,
"scripts": {
"build": "echo 'skip it'",
"clean": "echo 'skip it'",
"deploy": "echo 'skip it'",
"start": "echo 'skip it'",
"start-prod": "echo 'skip it'"
},
"dependencies": {
"@babel/runtime": "^7.21.0",
"@loadable/component": "^5.15.3",
"axios": "^0.22.0",
"cross-fetch": "3.1.5",
"fast-deep-equal": "^3.1.3",
"global": "^4.4.0",
"i18next": "^20.6.1",
"mobx": "^6.8.0",
"mobx-react": "^7.6.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"prop-types": "^15.8.1",
"query-string": "7.1.3",
"re-resizable": "^6.9.9",
"react": "^18.2.0",
"react-autosize-textarea": "^7.1.0",
"react-content-loader": "^5.1.4",
"react-dom": "^18.2.0",
"react-hammerjs": "^1.0.1",
"react-i18next": "^13.2.1",
"react-player": "^1.15.3",
"react-router": "^6.10.0",
"react-router-dom": "^6.10.0",
"react-tooltip": "^5.23.0",
"react-viewer": "^3.2.2",
"react-virtualized-auto-sizer": "^1.0.7",
"react-window": "^1.8.8",
"react-window-infinite-loader": "^1.0.8",
"screenfull": "^5.2.0",
"sjcl": "^1.0.8",
"socket.io-client": "^4.6.1",
"styled-components": "^5.3.9",
"workbox-window": "^6.5.4"
},
"devDependencies": {
"@types/crypto-js": "^4.2.1",
"@welldone-software/why-did-you-render": "^6.2.3"
}
}

View File

@ -1,244 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import TextInput from "../text-input";
import { Icons } from "../icons";
import IconButton from "../icon-button";
import {
StyledInputGroup,
StyledChildrenBlock,
StyledIconBlock,
} from "./styled-input-block";
//const iconNames = Object.keys(Icons);
class InputBlock extends React.Component {
constructor(props) {
super(props);
}
onIconClick = (e) => {
if (
typeof this.props.onIconClick === "function" /*&& !this.props.isDisabled*/
)
this.props.onIconClick(e);
};
onChange = (e) => {
if (typeof this.props.onChange === "function") this.props.onChange(e);
};
render() {
let iconButtonSize = 0;
const {
hasError,
hasWarning,
isDisabled,
scale,
size,
className,
style,
children,
id,
name,
type,
value,
placeholder,
tabIndex,
maxLength,
onBlur,
onFocus,
onKeyDown,
isReadOnly,
isAutoFocussed,
autoComplete,
mask,
keepCharPositions,
iconName,
iconColor,
hoverColor,
isIconFill,
onIconClick,
iconSize,
theme,
forwardedRef,
onClick,
iconButtonClassName,
iconNode,
...props
} = this.props;
if (typeof iconSize == "number" && iconSize > 0) {
iconButtonSize = iconSize;
} else {
switch (size) {
case "base":
iconButtonSize = 16;
break;
case "middle":
iconButtonSize = 18;
break;
case "big":
iconButtonSize = 21;
break;
case "huge":
iconButtonSize = 24;
break;
}
}
return (
<StyledInputGroup
hasError={hasError}
hasWarning={hasWarning}
isDisabled={isDisabled}
scale={scale}
size={size}
className={className}
style={style}
color={iconColor}
hoverColor={hoverColor}
>
<div className="prepend">
<StyledChildrenBlock className="prepend-children">
{children}
</StyledChildrenBlock>
</div>
<TextInput
id={id}
className={className}
name={name}
type={type}
value={value}
onClick={onClick}
isDisabled={isDisabled}
hasError={hasError}
hasWarning={hasWarning}
placeholder={placeholder}
tabIndex={tabIndex}
maxLength={maxLength}
onBlur={onBlur}
onFocus={onFocus}
isReadOnly={isReadOnly}
isAutoFocussed={isAutoFocussed}
autoComplete={autoComplete}
size={size}
scale={scale}
onChange={this.onChange}
onKeyDown={onKeyDown}
withBorder={false}
mask={mask}
keepCharPositions={keepCharPositions}
forwardedRef={forwardedRef}
{...props}
/>
{iconName && (
<div className="append">
<StyledIconBlock
className={`input-block-icon ${iconButtonClassName}`}
//isDisabled={isDisabled}
onClick={this.onIconClick}
isClickable={typeof onIconClick === "function"}
>
<IconButton
size={iconButtonSize}
iconNode={iconNode}
iconName={iconName}
isFill={isIconFill}
//isDisabled={isDisabled}
isClickable={typeof onIconClick === "function"}
color={iconColor}
hoverColor={hoverColor}
/>
</StyledIconBlock>
</div>
)}
</StyledInputGroup>
);
}
}
InputBlock.propTypes = {
/** Used as HTML `id` property */
id: PropTypes.string,
/** Forwarded ref */
forwardedRef: PropTypes.object,
/** Used as HTML `name` property */
name: PropTypes.string,
/** Supported type of the input fields. */
type: PropTypes.oneOf(["text", "password"]),
/** Defines max length of value */
maxLength: PropTypes.number,
/** Placeholder text for the input */
placeholder: PropTypes.string,
/** Accepts css tab-index */
tabIndex: PropTypes.number,
/** input text mask */
mask: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
/** Allows to add or delete characters without changing the positions of the existing characters.*/
keepCharPositions: PropTypes.bool,
/** Supported size of the input fields. */
size: PropTypes.oneOf(["base", "middle", "big", "huge", "large"]),
/** Indicates that the input field has scale */
scale: PropTypes.bool,
/** The callback function that is required when the input is not read only. The function is called with the new value. Parent should pass it back as `value` */
onChange: PropTypes.func,
/** The callback function that is called when the field is blurred */
onBlur: PropTypes.func,
/** The callback function that is called when the field is focused */
onFocus: PropTypes.func,
/** Focuses on the input field on initial render */
isAutoFocussed: PropTypes.bool,
/** Indicates that the field cannot be used (e.g not authorised, or changes not saved) */
isDisabled: PropTypes.bool,
/** Indicates that the field is displaying read-only content */
isReadOnly: PropTypes.bool,
/** Indicates the input field has an error */
hasError: PropTypes.bool,
/** Indicates the input field has a warning */
hasWarning: PropTypes.bool,
/** Used as HTML `autocomplete` */
autoComplete: PropTypes.string,
/** Value of the input */
value: PropTypes.string,
/** Path to icon */
iconName: PropTypes.string,
/** Specifies the icon color */
iconColor: PropTypes.string,
/** Icon color on hover action */
hoverColor: PropTypes.string,
/** Size icon */
iconSize: PropTypes.number,
/** Determines if icon fill is needed */
isIconFill: PropTypes.bool,
/** The callback function that is triggered when the icon is clicked */
onIconClick: PropTypes.func,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
/** Accepts class */
className: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Sets the classNaame of the icon button */
iconButtonClassName: PropTypes.string,
};
InputBlock.defaultProps = {
type: "text",
maxLength: 255,
size: "base",
scale: false,
tabIndex: -1,
hasError: false,
hasWarning: false,
autoComplete: "off",
value: "",
iconName: "",
isIconFill: false,
isDisabled: false,
keepCharPositions: false,
iconButtonClassName: "",
};
export default InputBlock;

View File

@ -1,99 +0,0 @@
import React from "react";
import CrossReactSvgUrl from "PUBLIC_DIR/images/cross.react.svg?url";
import { StyledSelectedItem, StyledLabel } from "./styled-selected-item";
import PropTypes from "prop-types";
import IconButton from "../icon-button";
const SelectedItem = (props) => {
const {
label,
onClose,
isDisabled,
onClick,
isInline,
className,
id,
propKey,
group,
forwardedRef,
classNameCloseButton,
hideCross,
} = props;
if (!label) return <></>;
const onCloseClick = (e) => {
!isDisabled && onClose && onClose(propKey, label, group, e);
};
const handleOnClick = (e) => {
!isDisabled &&
onClick &&
!e.target.classList.contains("selected-tag-removed") &&
onClick(propKey, label, group, e);
};
return (
<StyledSelectedItem
onClick={handleOnClick}
isInline={isInline}
className={className}
isDisabled={isDisabled}
id={id}
ref={forwardedRef}
>
<StyledLabel
className="selected-item_label"
truncate={true}
noSelect
isDisabled={isDisabled}
>
{label}
</StyledLabel>
{!hideCross && (
<IconButton
className={"selected-tag-removed " + classNameCloseButton}
iconName={CrossReactSvgUrl}
size={12}
onClick={onCloseClick}
isFill
isDisabled={isDisabled}
/>
)}
</StyledSelectedItem>
);
};
SelectedItem.propTypes = {
/** Selected item text */
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
/** Sets the 'width: fit-content' property */
isInline: PropTypes.bool,
/** Hide cross icon */
hideCross: PropTypes.bool,
/** Sets a callback function that is triggered when the cross icon is clicked */
onClose: PropTypes.func.isRequired,
/** Sets a callback function that is triggered when the selected item is clicked */
onClick: PropTypes.func,
/** Sets the button to present a disabled state */
isDisabled: PropTypes.bool,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Accepts key to remove item */
propKey: PropTypes.string,
/** Accepts group key to remove item */
group: PropTypes.string,
/** Passes ref to component */
forwardedRef: PropTypes.object,
};
SelectedItem.defaultProps = {
isInline: true,
hideCross: false,
isDisabled: false,
};
export default React.memo(SelectedItem);

View File

@ -1,70 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import equal from "fast-deep-equal/react";
import Text from "../text";
import StyledSocialButton from "./styled-social-button";
import { ReactSVG } from "react-svg";
// eslint-disable-next-line no-unused-vars
class SocialButton extends React.Component {
shouldComponentUpdate(nextProps) {
return !equal(this.props, nextProps);
}
render() {
const { label, iconName, IconComponent, image, isConnect, ...otherProps } =
this.props;
return (
<StyledSocialButton isConnect={isConnect} {...otherProps}>
{IconComponent ? (
<IconComponent className="iconWrapper" />
) : (
<ReactSVG className="iconWrapper" src={iconName} />
)}
{label && (
<Text as="div" className="social_button_text">
{label}
</Text>
)}
</StyledSocialButton>
);
}
}
SocialButton.propTypes = {
/** Button text */
label: PropTypes.string,
/** Button icon */
iconName: PropTypes.string,
/** Accepts tabindex prop */
tabIndex: PropTypes.number,
/** Sets the button to present a disabled state */
isDisabled: PropTypes.bool,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Sets a callback function that is triggered when the button is clicked */
onClick: PropTypes.func,
/** Accepts the icon options */
$iconOptions: PropTypes.object,
/** Sets the image size. Contains the small and the basic size options */
size: PropTypes.oneOf(["base", "small"]),
/** Changes the button style if the user is connected to the social network account */
isConnect: PropTypes.bool,
};
SocialButton.defaultProps = {
label: "",
iconName: "SocialButtonGoogleIcon",
tabIndex: -1,
isDisabled: false,
$iconOptions: {},
size: "base",
isConnect: false,
};
export default SocialButton;

View File

@ -1,208 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { ReactSVG } from "react-svg";
import CrossIconReactSvgUrl from "PUBLIC_DIR/images/cross.react.svg?url";
import TagIconReactSvgUrl from "PUBLIC_DIR/images/tag.react.svg?url";
import DropDown from "../drop-down";
import DropDownItem from "../drop-down-item";
import IconButton from "../icon-button";
import Text from "../text";
import {
StyledTag,
StyledDropdownIcon,
StyledDropdownText,
} from "./styled-tag";
const Tag = ({
tag,
label,
isNewTag,
isDisabled,
isDefault,
isLast,
onDelete,
onClick,
advancedOptions,
tagMaxWidth,
id,
className,
style,
icon,
removeTagIcon,
}) => {
const [openDropdown, setOpenDropdown] = React.useState(false);
const tagRef = React.useRef(null);
const isMountedRef = React.useRef(true);
const onClickOutside = React.useCallback((e) => {
if (e?.target?.className?.includes("advanced-tag") || !isMountedRef.current)
return;
setOpenDropdown(false);
}, []);
React.useEffect(() => {
if (openDropdown) {
return document.addEventListener("click", onClickOutside);
}
document.removeEventListener("click", onClickOutside);
return () => {
document.removeEventListener("click", onClickOutside);
};
}, [openDropdown, onClickOutside]);
React.useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
const openDropdownAction = (e) => {
if (e?.target?.className?.includes("backdrop-active")) return;
setOpenDropdown(true);
};
const onClickAction = React.useCallback(
(e) => {
if (onClick && !isDisabled) {
onClick(e.target.dataset.tag);
}
},
[onClick, isDisabled]
);
const onDeleteAction = React.useCallback(
(e) => {
if (e.target != tagRef.current && onDelete) {
onDelete && onDelete(tag);
}
},
[onDelete, tag, tagRef]
);
return (
<>
{!!advancedOptions ? (
<>
<StyledTag
id={id}
className={`tag advanced-tag ${className ? ` ${className}` : ""}`}
style={style}
ref={tagRef}
onClick={openDropdownAction}
isDisabled={isDisabled}
isDefault={isDefault}
isLast={isLast}
tagMaxWidth={tagMaxWidth}
isClickable={!!onClick}
>
<Text className={"tag-text"} font-size={"13px"} noSelect>
...
</Text>
</StyledTag>
<DropDown
open={openDropdown}
forwardedRef={tagRef}
clickOutsideAction={onClickOutside}
// directionX={"right"}
manualY={"4px"}
>
{advancedOptions.map((tag, index) => (
<DropDownItem
className="tag__dropdown-item tag"
key={`${tag}_${index}`}
onClick={onClickAction}
data-tag={tag}
>
{!removeTagIcon && (
<StyledDropdownIcon
className="tag__dropdown-item-icon"
src={TagIconReactSvgUrl}
/>
)}
<StyledDropdownText
className="tag__dropdown-item-text"
fontWeight={600}
fontSize={"12px"}
truncate
removeTagIcon={removeTagIcon}
>
{tag}
</StyledDropdownText>
</DropDownItem>
))}
</DropDown>
</>
) : (
<StyledTag
title={label}
onClick={onClickAction}
isNewTag={isNewTag}
isDisabled={isDisabled}
isDefault={isDefault}
tagMaxWidth={tagMaxWidth}
data-tag={label}
id={id}
className={`tag${className ? ` ${className}` : ""}`}
style={style}
isLast={isLast}
isClickable={!!onClick}
>
{icon ? (
<ReactSVG className="third-party-tag" src={icon} />
) : (
<>
<Text
className={"tag-text"}
title={label}
font-size={"13px"}
noSelect
truncate
>
{label}
</Text>
{isNewTag && (
<IconButton
className={"tag-icon"}
iconName={CrossIconReactSvgUrl}
size={"10px"}
onClick={onDeleteAction}
/>
)}
</>
)}
</StyledTag>
)}
</>
);
};
Tag.propTypes = {
/** Accepts the tag id */
tag: PropTypes.string,
/** Accepts the tag label */
label: PropTypes.string,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Accepts the tag styles as new and adds the delete button */
isNewTag: PropTypes.bool,
/** Accepts the tag styles as disabled and disables clicking */
isDisabled: PropTypes.bool,
/** Accepts the function that is called when the tag is clicked */
onClick: PropTypes.func,
/** Accepts the function that ist called when the tag delete button is clicked */
onDelete: PropTypes.func,
/** Accepts the max width of the tag */
tagMaxWidth: PropTypes.string,
/** Accepts the dropdown options */
advancedOptions: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
};
export default React.memo(Tag);

View File

@ -1,147 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import Tag from "../tag";
import StyledTags from "./StyledTags";
const Tags = ({
id,
className,
style,
tags,
columnCount,
onSelectTag,
removeTagIcon,
}) => {
const [renderedTags, setRenderedTags] = React.useState(null);
const tagsRef = React.useRef(null);
const updateRenderedTags = React.useCallback(() => {
if (tags && tagsRef) {
if (!columnCount) return;
const newTags = [];
const containerWidth = tagsRef.current.offsetWidth;
if (tags.length === 1) {
if (tags[0]?.isDefault) {
const tag = { ...tags[0], maxWidth: `100%` };
newTags.push(tag);
} else if (tags[0]?.isThirdParty) {
const tag = { ...tags[0], maxWidth: `36px` };
newTags.push(tag);
} else {
const tag = { label: tags[0].label || tags[0], maxWidth: `100%` };
newTags.push(tag);
}
return setRenderedTags(newTags);
}
if (
columnCount >= tags.length ||
(tags.length === 2 && tags[0]?.isThirdParty && tags[1]?.isDefault)
) {
const thirdPartyTagCount = tags[0]?.isThirdParty ? 1 : 0;
const currentTagMaxWidth =
(containerWidth -
thirdPartyTagCount * 40 -
(tags.length - thirdPartyTagCount) * 4) /
(tags.length - thirdPartyTagCount);
const maxWidthPercent = Math.floor(
(currentTagMaxWidth / containerWidth) * 100
);
for (let i = 0; i < tags.length; i++) {
if (tags[i]?.isThirdParty) {
const tag = { ...tags[i], maxWidth: `36px` };
newTags.push(tag);
} else if (tags[i]?.isDefault) {
const tag = { ...tags[i], maxWidth: `${maxWidthPercent}%` };
newTags.push(tag);
} else {
const tag = { label: tags[i], maxWidth: `${maxWidthPercent}%` };
newTags.push(tag);
}
}
} else {
const tagWithDropdown = {
key: "selector",
advancedOptions: tags.slice(columnCount, tags.length),
};
const currentTagMaxWidth =
(containerWidth - columnCount * 4 - 35) / columnCount;
const maxWidthPercent = Math.floor(
(currentTagMaxWidth / containerWidth) * 100
);
if (columnCount !== 0) {
for (let i = 0; i < columnCount; i++) {
if (tags[i]?.isThirdParty) {
const tag = { ...tags[i], maxWidth: `36px` };
newTags.push(tag);
} else if (tags[i]?.isDefault) {
const tag = { ...tags[i], maxWidth: `${maxWidthPercent}%` };
newTags.push(tag);
} else {
const tag = { label: tags[i], maxWidth: `${maxWidthPercent}%` };
newTags.push(tag);
}
}
}
newTags.push(tagWithDropdown);
newTags[newTags.length - 1].maxWidth = `35px`;
}
setRenderedTags(newTags);
}
}, [tags, tagsRef, columnCount]);
React.useEffect(() => {
updateRenderedTags();
}, [tags, tagsRef, columnCount]);
return (
<StyledTags id={id} className={className} style={style} ref={tagsRef}>
{renderedTags?.length > 0 &&
renderedTags.map((tag, index) => (
<Tag
key={`${tag.label}_${index}`}
label={tag.label}
advancedOptions={tag.advancedOptions}
tagMaxWidth={tag.maxWidth}
tagMinWidth={tag.minWidth}
isNewTag={false}
onClick={onSelectTag}
isLast={index === renderedTags.length - 1}
removeTagIcon={removeTagIcon}
{...tag}
/>
))}
</StyledTags>
);
};
Tags.propTypes = {
/** Accepts the tags */
tags: PropTypes.array,
/** Accepts the tag column count */
columnCount: PropTypes.number,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Accepts the function that is called when the tag is selected */
onSelectTag: PropTypes.func,
};
export default Tags;

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ import { mobile, tablet } from "@docspace/shared/utils/device";
export const LoginFormWrapper = styled.div<{ bgPattern: string }>`
width: 100%;
height: 100vh;
height: 100dvh;
box-sizing: border-box;

View File

@ -306,8 +306,14 @@ const LoginForm = ({
return;
}
checkConfirmLink(confirmData);
try {
if (confirmData) await checkConfirmLink(confirmData);
} catch (e) {
console.error(e);
}
return res;
})
.then((res: string | object) => {
const isConfirm = typeof res === "string" && res.includes("confirm");
const redirectPath =
referenceUrl || sessionStorage.getItem("referenceUrl");
@ -364,6 +370,7 @@ const LoginForm = ({
router,
clientId,
referenceUrl,
loginData,
]);
const onBlurEmail = () => {

View File

@ -1,9 +0,0 @@
{
"date": "2024711_121949",
"checksums": {
"api.js": "9b7b808480a7a3b84fff3bbc086fb3c7",
"api.poly.js": "6ef0a93fc185ef373f73abeaa1c9104e",
"browserDetector.js": "fedff3ea7197068a5c86c5cf2c51ec31",
"config.json": "225114f784ac16f33c0a0cb0a6363438"
}
}

View File

@ -36,8 +36,9 @@ export const initSSR = (headers: Record<string, string>) => {
export const request = (
options: TReqOption & AxiosRequestConfig,
skipRedirect = false,
isOAuth = false,
) => {
return client.request(options, skipRedirect);
return client.request(options, skipRedirect, isOAuth);
};
export const setWithCredentialsStatus = (state: boolean) => {

View File

@ -14,10 +14,14 @@ import {
} from "../../utils/oauth/types";
export const getClient = async (clientId: string): Promise<IClientProps> => {
const client = (await request({
method: "get",
url: `/clients/${clientId}`,
})) as IClientResDTO;
const client = (await request(
{
method: "get",
url: `/clients/${clientId}`,
},
false,
true,
)) as IClientResDTO;
return transformToClientProps(client);
};
@ -26,10 +30,14 @@ export const getClientList = async (
page: number,
limit: number,
): Promise<IClientListProps> => {
const data = (await request({
method: "get",
url: `/clients?page=${page}&limit=${limit}`,
})) as IClientListDTO;
const data = (await request(
{
method: "get",
url: `/clients?page=${page}&limit=${limit}`,
},
false,
true,
)) as IClientListDTO;
const clients: IClientListProps = { ...data, data: [] };
@ -45,66 +53,94 @@ export const getClientList = async (
export const addClient = async (data: IClientReqDTO): Promise<IClientProps> => {
data.logout_redirect_uri = data.website_url;
const client = (await request({
method: "post",
url: `/clients`,
data,
})) as IClientResDTO;
const client = (await request(
{
method: "post",
url: `/clients`,
data,
},
false,
true,
)) as IClientResDTO;
return transformToClientProps(client);
};
export const updateClient = async (clientId: string, data: IClientReqDTO) => {
await request({
method: "put",
url: `/clients/${clientId}`,
data,
});
await request(
{
method: "put",
url: `/clients/${clientId}`,
data,
},
false,
true,
);
};
export const changeClientStatus = async (
clientId: string,
status: boolean,
): Promise<void> => {
await request({
method: "patch",
url: `/clients/${clientId}/activation`,
data: { status },
});
await request(
{
method: "patch",
url: `/clients/${clientId}/activation`,
data: { status },
},
false,
true,
);
};
export const regenerateSecret = async (
clientId: string,
): Promise<{ client_secret: string }> => {
const clientSecret = (await request({
method: "patch",
url: `/clients/${clientId}/regenerate`,
})) as { client_secret: string };
const clientSecret = (await request(
{
method: "patch",
url: `/clients/${clientId}/regenerate`,
},
false,
true,
)) as { client_secret: string };
return clientSecret;
};
export const deleteClient = async (clientId: string): Promise<void> => {
await request({
method: "delete",
url: `/clients/${clientId}`,
});
await request(
{
method: "delete",
url: `/clients/${clientId}`,
},
false,
true,
);
};
export const getScope = async (name: string): Promise<TScope> => {
const scope = (await request({
method: "get",
url: `/scopes/${name}`,
})) as TScope;
const scope = (await request(
{
method: "get",
url: `/scopes/${name}`,
},
false,
true,
)) as TScope;
return scope;
};
export const getScopeList = async (): Promise<TScope[]> => {
const scopeList = (await request({
method: "get",
url: `/scopes`,
})) as TScope[];
const scopeList = (await request(
{
method: "get",
url: `/scopes`,
},
false,
true,
)) as TScope[];
return scopeList;
};
@ -113,10 +149,14 @@ export const getConsentList = async (
page: number = 0,
limit: number = 100,
): Promise<TConsentList & { consents: IClientProps[] }> => {
const consentList = (await request({
method: "get",
url: `/clients/consents?page=${page}&limit=${limit}`,
})) as TConsentList;
const consentList = (await request(
{
method: "get",
url: `/clients/consents?page=${page}&limit=${limit}`,
},
false,
true,
)) as TConsentList;
const consents: IClientProps[] = [];
@ -138,10 +178,14 @@ export const getConsentList = async (
};
export const revokeUserClient = async (clientId: string): Promise<void> => {
await request({
method: "delete",
url: `/clients/${clientId}/revoke`,
});
await request(
{
method: "delete",
url: `/clients/${clientId}/revoke`,
},
false,
true,
);
};
export const onOAuthSubmit = (
@ -158,15 +202,19 @@ export const onOAuthSubmit = (
formData.append("scope", s);
});
return request({
method: "post",
url: `/oauth2/authorize`,
data: formData,
withRedirect: true,
headers: {
"X-Disable-Redirect": "true",
return request(
{
method: "post",
url: `/oauth2/authorize`,
data: formData,
withRedirect: true,
headers: {
"X-Disable-Redirect": "true",
},
},
});
false,
true,
);
};
export const onOAuthCancel = (clientId: string, clientState: string) => {
@ -175,13 +223,17 @@ export const onOAuthCancel = (clientId: string, clientState: string) => {
formData.append("client_id", clientId);
formData.append("state", clientState);
return request({
method: "post",
url: `/oauth2/authorize`,
data: formData,
withRedirect: true,
headers: {
"X-Disable-Redirect": "true",
return request(
{
method: "post",
url: `/oauth2/authorize`,
data: formData,
withRedirect: true,
headers: {
"X-Disable-Redirect": "true",
},
},
});
false,
true,
);
};

View File

@ -144,8 +144,14 @@ export const enum FilterType {
ArchiveOnly = 10,
ByExtension = 11,
MediaOnly = 12,
OFormTemplateOnly = 18,
OFormOnly = 19,
FillingFormsRooms = 13,
EditingRooms = 14,
ReviewRooms = 15,
ReadOnlyRooms = 16,
CustomRooms = 17,
PublicRooms = 20,
FormRooms = 21,
Pdf = 22,
}
/**
@ -550,7 +556,6 @@ export const enum FilesSelectorExtendedFilterTypes {
Media = "Media",
Archives = "Archives",
AllFiles = "AllFiles",
FormTemplates = "FormTemplates",
Forms = "Forms",
}

View File

@ -158,16 +158,12 @@ const useFilesHelper = ({
filter.extension = "gz,tar";
break;
case FilesSelectorFilterTypes.DOCXF:
filter.filterType = FilterType.OFormTemplateOnly;
break;
case FilesSelectorFilterTypes.XLSX:
filter.filterType = FilterType.SpreadsheetsOnly;
break;
case FilesSelectorFilterTypes.PDF:
filter.extension = FilesSelectorFilterTypes.PDF;
filter.filterType = FilterType.Pdf;
break;
case FilterType.DocumentsOnly:
@ -198,19 +194,12 @@ const useFilesHelper = ({
filter.filterType = FilterType.FoldersOnly;
break;
case FilterType.OFormTemplateOnly:
filter.filterType = FilterType.OFormTemplateOnly;
break;
case FilterType.OFormOnly:
filter.filterType = FilterType.OFormOnly;
break;
case FilterType.FilesOnly:
filter.filterType = FilterType.FilesOnly;
break;
case FilesSelectorFilterTypes.ALL:
filter.applyFilterOption = ApplyFilterOption.All;
filter.filterType = FilterType.None;
break;

View File

@ -185,6 +185,7 @@ class AxiosClient {
request = (
options: TReqOption & AxiosRequestConfig,
skipRedirect = false,
isOAuth = false,
) => {
const onSuccess = (response: TRes) => {
const error = this.getResponseError(response);
@ -216,9 +217,9 @@ class AxiosClient {
if (options.baseURL === "/apisystem" && !response.data.response)
return response.data;
return typeof response.data.response !== "undefined"
? response.data.response
: response.data;
if (isOAuth && !response.data.response) return response.data;
return response.data.response;
};
const onError = (error: TError) => {