Merge branch 'feature/virtual-rooms-1.2' of github.com:ONLYOFFICE/AppServer into feature/virtual-rooms-1.2

This commit is contained in:
Timofey Boyko 2022-04-20 10:47:34 +03:00
commit fd0e6db6e1
101 changed files with 3443 additions and 1263 deletions

View File

@ -1,5 +1,5 @@
{
"kafka": {
"BootstrapServers": "localhost:9092"
"BootstrapServers": ""
}
}

View File

@ -136,7 +136,11 @@ const Selector = (props) => {
const newGroupList = groupList;
newGroupList.find((group) => group.key === groupHeader.key).total = total;
if (newGroupList.length > 0) {
newGroupList.find(
(group) => group.key === groupHeader.key
).total = total;
}
setGroupList(newGroupList);
}
@ -334,7 +338,7 @@ const Selector = (props) => {
isDisabled={isDisabled}
placeholder={searchPlaceHolderLabel}
value={searchValue}
onChange={onSearchChange}
onSearchChange={onSearchChange}
onClearSearch={onSearchReset}
/>
<div style={{ width: "100%", height: "100%" }} className="body-options">

View File

@ -31,7 +31,7 @@ const Article = ({
toggleShowText,
toggleArticleOpen,
setIsMobileArticle,
isLoading,
isLoadedPage,
children,
...rest
}) => {
@ -117,7 +117,7 @@ const Article = ({
handleWrapperClass="resizable-border not-selectable"
>
<SubArticleHeader
isLoading={isLoading}
isLoadedPage={isLoadedPage}
showText={showText}
onClick={toggleShowText}
>
@ -128,7 +128,7 @@ const Article = ({
{articleMainButtonContent.props.children}
</SubArticleMainButton>
) : null}
<SubArticleBody isLoading={isLoading} showText={showText}>
<SubArticleBody showText={showText}>
{articleBodyContent ? articleBodyContent.props.children : null}
</SubArticleBody>
</Resizable>

View File

@ -1,10 +1,8 @@
import React from "react";
import Scrollbar from "@appserver/components/scrollbar";
import LoaderArticleBody from "./article-body-loader";
const ArticleBody = ({ children, isLoading = false }) => {
return isLoading ? (
<LoaderArticleBody />
) : (
const ArticleBody = ({ children }) => {
return (
<Scrollbar className="article-body__scrollbar" stype="mediumBlack">
{children}
</Scrollbar>

View File

@ -1,9 +1,9 @@
import React from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import Loaders from "@appserver/common/components/Loaders";
import { isTablet as isTabletUtils } from "@appserver/components/utils/device";
import { isTablet } from "react-device-detect";
import { inject, observer } from "mobx-react";
import {
StyledArticleHeader,
StyledHeading,
@ -15,12 +15,22 @@ const ArticleHeader = ({
showText,
children,
onClick,
isLoading = false,
isLoadedPage,
isLoaded,
tReady,
setIsLoadedArticleHeader,
...rest
}) => {
const heightLoader = isTabletUtils() || isTablet ? "20px" : "32px";
const isLoadedSetting = isLoaded;
const commonSettings = location.pathname.includes("common");
return isLoading ? (
useEffect(() => {
if (isLoadedSetting) setIsLoadedArticleHeader(isLoadedSetting);
}, [isLoadedSetting]);
const heightLoader = isTabletUtils() || isTablet ? "20px" : "32px";
const showLoader = commonSettings ? !isLoadedPage : false;
return showLoader ? (
<StyledArticleHeader>
<Loaders.ArticleHeader height={heightLoader} className="loader" />
</StyledArticleHeader>
@ -45,4 +55,11 @@ ArticleHeader.propTypes = {
ArticleHeader.displayName = "Header";
export default React.memo(ArticleHeader);
export default inject(({ common }) => {
const { isLoaded, setIsLoadedArticleHeader } = common;
return {
isLoaded,
setIsLoadedArticleHeader,
};
})(observer(ArticleHeader));

View File

@ -0,0 +1,145 @@
import styled, { css } from "styled-components";
import { Base } from "@appserver/components/themes";
const StyledContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`;
const StyledHeader = styled.div`
width: 100%;
padding: ${(props) => (props.isPersonal ? "0px 16px 12px" : "12px 16px")};
${(props) =>
props.isPersonal &&
css`
margin-left: -12px;
margin-right: 12px;
`}
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
border-bottom: ${(props) => props.theme.filesPanels.sharing.borderBottom};
`;
StyledHeader.defaultProps = { theme: Base };
const StyledExternalLink = styled.div`
width: 100%;
display: flex;
flex-direction: column;
padding: ${(props) => (props.isPersonal ? "20px 4px" : "20px 16px")};
box-sizing: border-box;
border-bottom: ${(props) =>
props.isPersonal ? "none" : props.theme.filesPanels.sharing.borderBottom};
.rectangle-loader {
margin-bottom: 16px;
}
`;
StyledExternalLink.defaultProps = { theme: Base };
const StyledInternalLink = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 16px;
box-sizing: border-box;
`;
const StyledOwner = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
box-sizing: border-box;
margin-bottom: 16px;
.owner-info {
display: flex;
align-items: center;
svg:first-child {
margin-right: 12px;
}
}
`;
const StyledBody = styled.div`
width: 100%;
display: flex;
flex-direction: column;
div:nth-child(3) {
margin-bottom: 16px;
}
`;
const StyledItem = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 8px 16px;
.item-info {
display: flex;
align-items: center;
svg:first-child {
margin-right: 12px;
}
}
`;
const StyledButtons = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 4px;
svg:first-child {
margin-right: 8px;
}
`;
export {
StyledContainer,
StyledHeader,
StyledExternalLink,
StyledInternalLink,
StyledOwner,
StyledBody,
StyledItem,
StyledButtons,
};

View File

@ -0,0 +1,138 @@
import React from "react";
import PropTypes from "prop-types";
import {
StyledContainer,
StyledHeader,
StyledExternalLink,
StyledInternalLink,
StyledOwner,
StyledBody,
StyledItem,
} from "./StyledSharingPanel";
import RectangleLoader from "../RectangleLoader/RectangleLoader";
const SharingPanelLoader = ({ id, className, style, ...rest }) => {
return (
<StyledContainer>
<StyledHeader>
<RectangleLoader width={"283px"} height={"29px"} />
<RectangleLoader width={"48px"} height={"29px"} />
</StyledHeader>
<StyledExternalLink>
<RectangleLoader
className="rectangle-loader"
width={"146px"}
height={"22px"}
/>
<RectangleLoader
className="rectangle-loader"
width={"448px"}
height={"32px"}
/>
<RectangleLoader width={"184px"} height={"20px"} />
</StyledExternalLink>
<StyledInternalLink>
<RectangleLoader width={"99px"} height={"22px"} />
<RectangleLoader width={"30px"} height={"22px"} />
</StyledInternalLink>
<StyledOwner>
<div className="owner-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"91px"} height={"16px"} />
</StyledOwner>
<StyledBody>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
<StyledItem>
<div className="item-info">
<RectangleLoader
width={"32px"}
height={"32px"}
borderRadius={"1000px"}
/>
<RectangleLoader width={"91px"} height={"16px"} />
</div>
<RectangleLoader width={"45px"} height={"32px"} />
</StyledItem>
</StyledBody>
</StyledContainer>
);
};
SharingPanelLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
SharingPanelLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
};
export default SharingPanelLoader;

View File

@ -0,0 +1,66 @@
import React from "react";
import PropTypes from "prop-types";
import {
StyledContainer,
StyledHeader,
StyledExternalLink,
StyledInternalLink,
StyledOwner,
StyledBody,
StyledItem,
StyledButtons,
} from "./StyledSharingPanel";
import RectangleLoader from "../RectangleLoader/RectangleLoader";
const SharingPanelLoaderModal = ({
id,
className,
style,
isShared,
...rest
}) => {
return (
<StyledContainer>
<StyledHeader isPersonal={true}>
<RectangleLoader width={"283px"} height={"16px"} />
</StyledHeader>
<StyledExternalLink isPersonal={true}>
<RectangleLoader
className="rectangle-loader"
width={"146px"}
height={"22px"}
/>
{isShared && (
<>
<RectangleLoader
className="rectangle-loader"
width={"368px"}
height={"32px"}
/>
<RectangleLoader width={"184px"} height={"20px"} />
</>
)}
</StyledExternalLink>
<StyledButtons>
<RectangleLoader width={"100%"} height={"40px"} />
<RectangleLoader width={"100%"} height={"40px"} />
</StyledButtons>
</StyledContainer>
);
};
SharingPanelLoaderModal.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
SharingPanelLoaderModal.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
};
export default SharingPanelLoaderModal;

View File

@ -22,6 +22,8 @@ import Tiles from "./TilesLoader";
import DialogLoader from "./DialogLoader";
import DialogAsideLoader from "./DialogAsideLoader";
import ListLoader from "./ListLoader";
import SharingPanelLoader from "./SharingPanelLoader";
import SharingPanelLoaderModal from "./SharingPanelLoader/modal";
export default {
Rectangle,
@ -48,4 +50,6 @@ export default {
ArticleGroup,
ListLoader,
NewTreeFolders,
SharingPanelLoader,
SharingPanelLoaderModal,
};

View File

@ -250,8 +250,13 @@ class SettingsStore {
this.isLoaded = isLoaded;
};
setCultures = (cultures) => {
this.cultures = cultures;
};
getPortalCultures = async () => {
this.cultures = await api.settings.getPortalCultures();
const cultures = await api.settings.getPortalCultures();
this.setCultures(cultures);
};
setIsEncryptionSupport = (isEncryptionSupport) => {

View File

@ -13,21 +13,33 @@ import {
StyledAccessRightItemContent,
StyledAccessRightItemTitleAndBadge,
} from "./styled-accessright.js";
import { ReactSVG } from "react-svg";
const AccessRightSelect = ({ options, onSelect, selectedOption, ...props }) => {
const AccessRightSelect = ({
options,
onSelect,
advancedOptions,
selectedOption,
...props
}) => {
const [currentItem, setCurrentItem] = useState(selectedOption);
function onSelectCurrentItem(e) {
const key = e.currentTarget.dataset.key;
const key = +e.currentTarget.dataset.key;
const item = options.find((el) => {
return el.key === key;
});
if (item) {
setCurrentItem(item);
onSelect && onSelect(item);
}
}
React.useEffect(() => {
setCurrentItem(selectedOption);
}, [selectedOption]);
const formatToAccessRightItem = (data) => {
return (
<>
@ -67,12 +79,15 @@ const AccessRightSelect = ({ options, onSelect, selectedOption, ...props }) => {
return (
<StyledAccessRightWrapper>
<StyledAccessRightIcon src={currentItem?.icon} />
<ReactSVG className="access-right__icon" src={currentItem?.icon} />
<ComboBox
advancedOptions={formatToAccessRightItem(options)}
advancedOptions={
!!advancedOptions ? advancedOptions : formatToAccessRightItem(options)
}
onSelect={onSelectCurrentItem}
directionX="left"
directionY="bottom"
opened
opened={false}
noBorder
options={[]}
scaled={false}
@ -92,8 +107,10 @@ AccessRightSelect.propTypes = {
options: PropTypes.arrayOf(PropTypes.object).isRequired,
/** Will be triggered whenever an AccessRightSelect is selected option */
onSelect: PropTypes.func,
/** List of advanced options */
advancedOptions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
/** The option that is selected by default */
selectedOption: PropTypes.object,
};
export default AccessRightSelect;
export default React.memo(AccessRightSelect);

View File

@ -3,14 +3,28 @@ import Base from "../themes/base";
const StyledAccessRightWrapper = styled.div`
display: flex;
align-items: center;
.access-right__icon {
display: flex;
align-items: center;
path {
fill: ${(props) => props.theme.dropDownItem.icon.color};
}
}
.combo-button {
padding-left: 4px;
}
`;
StyledAccessRightWrapper.defaultProps = { theme: Base };
const StyledAccessRightIcon = styled.img`
margin-right: 4.18px;
margin-right: 4px;
`;
const StyledAccessRightItem = styled.div`
width: 424px;
width: auto;
display: flex;
align-items: flex-start;

View File

@ -56,7 +56,7 @@ class Backdrop extends React.Component {
modifyClassName = () => {
const { className } = this.props;
let modifiedClass = "backdrop-active";
let modifiedClass = "backdrop-active not-selectable";
if (className) {
if (typeof className !== "string") {

View File

@ -223,7 +223,6 @@ const StyledButton = styled(ButtonWrapper).attrs((props) => ({
.icon {
width: auto;
height: 100%;
display: flex;
align-items: center;
}

View File

@ -114,13 +114,14 @@ class DropDown extends React.PureComponent {
const rects = this.dropDownRef.current.getBoundingClientRect();
const parentRects = forwardedRef?.current?.getBoundingClientRect();
const container = DomHelpers.getViewport();
const dimensions = parentRects
? {
toTopCorner: parentRects.top,
parentHeight: parentRects.height,
containerHeight: parentRects.top,
containerHeight: !parentRects.top,
}
: {
toTopCorner: rects.top,
@ -352,6 +353,7 @@ DropDownContainer.propTypes = {
isDefaultMode: PropTypes.bool,
/** Needed to open correctly people and group selector when the section width is small */
smallSectionWidth: PropTypes.bool,
/** It is necessary when we explicitly set the direction, disables check position */
fixedDirection: PropTypes.bool,
};

View File

@ -127,7 +127,6 @@ const MainButtonMobile = (props) => {
};
const onMainButtonClick = (e) => {
if (isOpen && ref.current.contains(e.target)) return;
toggle(!isOpen);
};
@ -191,8 +190,8 @@ const MainButtonMobile = (props) => {
/>
))}
</StyledProgressContainer>
<StyledButtonOptions isOpenButton={isOpenButton}>
{isOpenButton && buttonOptions
<StyledButtonOptions>
{buttonOptions
? buttonOptions.map((option) =>
option.isSeparator ? (
<div key={option.key} className="separator-wrapper">
@ -213,20 +212,6 @@ const MainButtonMobile = (props) => {
)
: ""}
</StyledButtonOptions>
{withButton && (
<StyledButtonWrapper
isUploading={isUploading}
isOpenButton={isOpenButton}
>
<Button
label={title}
className="action-mobile-button"
primary
size="medium"
onClick={onUploadClick}
/>
</StyledButtonWrapper>
)}
</div>
);
};
@ -250,6 +235,7 @@ const MainButtonMobile = (props) => {
directionY="top"
directionX="right"
isMobile={isMobile || isTablet}
fixedDirection={true}
heightProp={height}
sectionWidth={sectionWidth}
isDefaultMode={false}

View File

@ -134,7 +134,6 @@ const StyledDropDownItem = styled(DropDownItem)`
`;
const StyledButtonOptions = styled.div`
display: ${(props) => !props.isOpenButton && "none"};
padding: 16px 0;
background-color: ${(props) =>
props.theme.mainButtonMobile.buttonOptions.backgroundColor};

View File

@ -81,6 +81,10 @@ class ModalDialog extends React.Component {
componentDidMount() {
window.addEventListener("resize", this.throttledResize);
window.addEventListener("keyup", this.onKeyPress);
window.onpopstate = () => {
this.props.onClose();
};
}
componentWillUnmount() {
@ -163,17 +167,23 @@ class ModalDialog extends React.Component {
<Loaders.DialogLoader bodyHeight={modalLoaderBodyHeight} />
) : (
<>
<StyledHeader>
<Heading className="heading" size="medium" truncate={true}>
{header ? header.props.children : null}
</Heading>
{!withoutCloseButton && (
<CloseButton
className="modal-dialog-button_close"
onClick={onClose}
></CloseButton>
)}
</StyledHeader>
{header && (
<StyledHeader>
<Heading
className="heading"
size="medium"
truncate={true}
>
{header ? header.props.children : null}
</Heading>
{!withoutCloseButton && (
<CloseButton
className="modal-dialog-button_close"
onClick={onClose}
></CloseButton>
)}
</StyledHeader>
)}
<BodyBox paddingProp={modalBodyPadding}>
{body ? body.props.children : null}
</BodyBox>

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20597_65864)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84646 0.979492L5.22299 14.5025L7.15485 15.0201L10.7783 1.49713L8.84646 0.979492ZM2.41419 7.99916L5.20702 5.20614L3.79276 3.79197L0.292868 7.2921C-0.0976332 7.68263 -0.0976212 8.31578 0.292895 8.7063L3.79278 12.2062L5.20699 10.7919L2.41419 7.99916ZM13.5857 8.00004L10.7928 5.20714L12.207 3.79292L15.707 7.29293C15.8945 7.48047 15.9999 7.73482 15.9999 8.00004C15.9999 8.26526 15.8945 8.51961 15.707 8.70715L12.2065 12.2076L10.7923 10.7934L13.5857 8.00004Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20597_65864">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.85498 16L1.15188 16C0.518827 16 0.00382324 15.485 0.00378789 14.8519L0.00378713 6.14776C0.00378708 5.51468 0.518826 4.99964 1.15187 4.99964L4.01036 4.99964L4.01036 9.96112C4.01036 11.1721 4.75801 11.9963 5.96898 11.9963L11.0031 11.9963L11.0031 14.8519C11.0031 15.485 10.4881 16 9.85498 16ZM14.8703 11.0013L6.12976 11.0013C5.50682 11.0013 4.99997 10.4945 5 9.87155L5 1.12969C5 0.506788 5.50679 9.17346e-07 6.12973 8.62887e-07L14.8703 9.87639e-08C15.4932 4.43048e-08 16 0.506787 16 1.12973L16 9.87155C16 10.4945 15.4932 11.0013 14.8703 11.0013Z" fill="#A3A9AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.14502 -1.07443e-06L14.8481 -2.59613e-06C15.4812 -2.70681e-06 15.9962 0.515037 15.9962 1.14808L15.9962 9.85224C15.9962 10.4853 15.4812 11.0004 14.8481 11.0004L11.9896 11.0004L11.9896 6.03888C11.9896 4.82791 11.242 4.00369 10.031 4.00369L4.99694 4.00369L4.99694 1.14809C4.99693 0.515038 5.51194 -9.63738e-07 6.14502 -1.07443e-06ZM1.12973 4.99872L9.87024 4.99872C10.4932 4.99872 11 5.50551 11 6.12845L11 14.8703C11 15.4932 10.4932 16 9.87028 16L1.12973 16C0.50679 16 3.70837e-05 15.4932 2.6e-06 14.8703L1.07154e-06 6.12845C9.62618e-07 5.50551 0.506823 4.99872 1.12973 4.99872Z" fill="#A3A9AE"/>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 747 B

View File

@ -12,8 +12,9 @@ import {
StyledSubmenuItems,
StyledSubmenuItemText,
} from "./styled-submenu";
import LoaderSubmenu from "./loader";
const Submenu = ({ data, startSelect = 0, onSelect, ...rest }) => {
const Submenu = ({ data, startSelect = 0, onSelect, isLoading, ...rest }) => {
if (!data) return null;
const [currentItem, setCurrentItem] = useState(
@ -74,29 +75,40 @@ const Submenu = ({ data, startSelect = 0, onSelect, ...rest }) => {
return (
<StyledSubmenu {...rest}>
<StyledSubmenuItems ref={submenuItemsRef} role="list">
{data.map((d) => {
const isActive = d.id === currentItem.id;
{isLoading ? (
<LoaderSubmenu />
) : (
<>
<StyledSubmenuItems ref={submenuItemsRef} role="list">
{data.map((d) => {
const isActive = d.id === currentItem.id;
return (
<StyledSubmenuItem key={d.id} id={d.id} onClick={selectSubmenuItem}>
<StyledSubmenuItemText>
<Text
color={isActive ? "#316DAA" : "#657077"}
fontSize="13px"
fontWeight="600"
truncate={false}
return (
<StyledSubmenuItem
key={d.id}
id={d.id}
onClick={selectSubmenuItem}
>
{d.name}
</Text>
</StyledSubmenuItemText>
<StyledSubmenuItemLabel color={isActive ? "#316DAA" : "none"} />
</StyledSubmenuItem>
);
})}
</StyledSubmenuItems>
<StyledSubmenuBottomLine />
<StyledSubmenuItemText>
<Text
color={isActive ? "#316DAA" : "#657077"}
fontSize="13px"
fontWeight="600"
truncate={false}
>
{d.name}
</Text>
</StyledSubmenuItemText>
<StyledSubmenuItemLabel
color={isActive ? "#316DAA" : "none"}
/>
</StyledSubmenuItem>
);
})}
</StyledSubmenuItems>
<StyledSubmenuBottomLine />
</>
)}
<StyledSubmenuContentWrapper>
{currentItem.content}
</StyledSubmenuContentWrapper>
@ -108,6 +120,7 @@ Submenu.propTypes = {
data: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
startSelect: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
onSelect: PropTypes.func,
isLoading: PropTypes.bool,
};
export default Submenu;

View File

@ -1,5 +1,5 @@
import React from "react";
import styled from "styled-components";
import styled, { css } from "styled-components";
import Loaders from "@appserver/common/components/Loaders";
import { isTablet } from "react-device-detect";

View File

@ -2325,6 +2325,17 @@ const Base = {
fill: gray,
loadingFill: grayMid,
borderBottom: "1px solid #eceef1",
borderTop: "1px solid #eceef1",
externalLinkBackground: "#f8f9f9",
externalLinkSvg: "#333333",
internalLinkBorder: "1px dashed #333333",
itemBorder: "1px dashed #333333",
itemOwnerColor: "rgb(163, 169, 174)",
dropdownColor: black,
loader: {

View File

@ -2335,6 +2335,17 @@ const Dark = {
fill: grayMaxLight,
loadingFill: grayMaxLight,
borderBottom: "1px solid #474747",
borderTop: "1px solid #474747",
externalLinkBackground: "#292929",
externalLinkSvg: "#858585",
internalLinkBorder: "1px dashed #eeeeee",
itemBorder: "1px dashed #333333",
itemOwnerColor: "#858585",
dropdownColor: grayMaxLight,
loader: {

View File

@ -13,17 +13,17 @@ const ToggleIcon = ({ isChecked, isLoading }) => {
isChecked ? "checked" : "notChecked",
isLoading ? "isLoading" : "",
]}
width="30"
height="18"
viewBox="0 0 30 18"
width="28"
height="16"
viewBox="0 0 28 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.rect x="1" y="1" width="28" height="16" rx="8" />
<motion.rect width="28" height="16" rx="8" />
<motion.circle
fill-rule="evenodd"
clip-rule="evenodd"
cy="9"
cy="8"
variants={{
isLoading: {
r: [5, 6, 6],
@ -35,8 +35,8 @@ const ToggleIcon = ({ isChecked, isLoading }) => {
},
},
},
checked: { cx: 21, r: 6 },
notChecked: { cx: 9, r: 6 },
checked: { cx: 20, r: 6 },
notChecked: { cx: 8, r: 6 },
}}
/>
</motion.svg>

View File

@ -68,8 +68,6 @@ const ToggleButtonContainer = styled.label`
}
.toggle-button-text {
margin-top: 2px;
color: ${(props) =>
props.isDisabled
? props.theme.text.disableColor

View File

@ -46,6 +46,7 @@ const ViewSelector = ({
firstItem={indx === 0}
lastItem={indx === lastIndx}
key={value}
name={`view-selector-name_${value}`}
className="view-selector-icon"
data-view={value}
title={

View File

@ -42,6 +42,7 @@ const tests = isTranslation
"./tests/context-menu_tests.js",
"./tests/filter_tests.js",
"./tests/catalog_tests.js",
"./tests/duplicate-id_tests.js",
];
const reportDir = isTranslation

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20597_65864)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84646 0.979492L5.22299 14.5025L7.15485 15.0201L10.7783 1.49713L8.84646 0.979492ZM2.41419 7.99916L5.20702 5.20614L3.79276 3.79197L0.292868 7.2921C-0.0976332 7.68263 -0.0976212 8.31578 0.292895 8.7063L3.79278 12.2062L5.20699 10.7919L2.41419 7.99916ZM13.5857 8.00004L10.7928 5.20714L12.207 3.79292L15.707 7.29293C15.8945 7.48047 15.9999 7.73482 15.9999 8.00004C15.9999 8.26526 15.8945 8.51961 15.707 8.70715L12.2065 12.2076L10.7923 10.7934L13.5857 8.00004Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20597_65864">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.14502 -1.07443e-06L14.8481 -2.59613e-06C15.4812 -2.70681e-06 15.9962 0.515037 15.9962 1.14808L15.9962 9.85224C15.9962 10.4853 15.4812 11.0004 14.8481 11.0004L11.9896 11.0004L11.9896 6.03888C11.9896 4.82791 11.242 4.00369 10.031 4.00369L4.99694 4.00369L4.99694 1.14809C4.99693 0.515038 5.51194 -9.63738e-07 6.14502 -1.07443e-06ZM1.12973 4.99872L9.87024 4.99872C10.4932 4.99872 11 5.50551 11 6.12845L11 14.8703C11 15.4932 10.4932 16 9.87028 16L1.12973 16C0.50679 16 3.70837e-05 15.4932 2.6e-06 14.8703L1.07154e-06 6.12845C9.62618e-07 5.50551 0.506823 4.99872 1.12973 4.99872Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 747 B

View File

@ -21,8 +21,15 @@ export default function withFileActions(WrappedFileItem) {
id !== -1 && onSelectItem({ id, isFolder });
};
onFileContextClick = () => {
const { onSelectItem } = this.props;
const { id, isFolder } = this.props.item;
id !== -1 && onSelectItem({ id, isFolder }, true);
};
onHideContextMenu = () => {
//this.props.setBufferSelection(null);
this.props.setBufferSelection(null);
};
onDropZoneUpload = (files, uploadToFolder) => {
@ -181,7 +188,7 @@ export default function withFileActions(WrappedFileItem) {
return (
<WrappedFileItem
onContentFileSelect={this.onContentFileSelect}
fileContextClick={this.fileContextClick}
fileContextClick={this.onFileContextClick}
onDrop={this.onDrop}
onMouseDown={this.onMouseDown}
onFilesClick={this.onFilesClick}

View File

@ -11,6 +11,16 @@ export const StyledIcon = styled(IconButton)`
${commonIconsStyles}
`;
const StyledEditIcon = styled(IconButton)`
${commonIconsStyles}
svg {
path {
fill: ${(props) => props.theme.filesSection.rowView.editingIconColor};
}
}
`;
const StyledWrapper = styled.div`
display: flex;
justify-content: center;
@ -129,7 +139,7 @@ const Badges = ({
/>
)}
{isEditing && (
<StyledIcon
<StyledEditIcon
iconName={iconEdit}
className="badge icons-group is-editing tablet-badge tablet-edit"
size={sizeBadge}

View File

@ -111,7 +111,7 @@ class AddGroupsPanelComponent extends React.Component {
const selectedOptions = [];
shareDataItems.forEach((item) => {
const { sharedTo } = item;
if (!sharedTo.groups && !sharedTo.shareLink) {
if (item?.isGroup) {
selectedOptions.push({ id: sharedTo.id, key: sharedTo.id });
}
});

View File

@ -121,8 +121,11 @@ class AddUsersPanelComponent extends React.Component {
const selectedOptions = [];
shareDataItems.forEach((item) => {
const { sharedTo } = item;
if (sharedTo.groups) {
const groups = sharedTo.groups.map((group) => group.id);
if (item.isUser) {
const groups = sharedTo?.groups
? sharedTo.groups.map((group) => group.id)
: [];
selectedOptions.push({ key: sharedTo.id, id: sharedTo.id, groups });
}
});
@ -145,6 +148,7 @@ class AddUsersPanelComponent extends React.Component {
: null;
//console.log("AddUsersPanel render");
return (
<StyledAddUsersPanelPanel visible={visible}>
<Backdrop

View File

@ -8,8 +8,9 @@ import toastr from "@appserver/components/toast/toastr";
import IconButton from "@appserver/components/icon-button";
import i18n from "./i18n";
import { withTranslation, I18nextProvider } from "react-i18next";
import StyledBody from "./StyledEmbeddingPanel";
const EmbeddingBody = ({ embeddingLink, t, theme }) => {
const EmbeddingBody = ({ embeddingLink, t, theme, isPersonal }) => {
const [size, setSize] = useState("auto");
const [widthValue, setWidthValue] = useState("100%");
const [heightValue, setHeightValue] = useState("100%");
@ -61,68 +62,76 @@ const EmbeddingBody = ({ embeddingLink, t, theme }) => {
};
return (
<div className="embedding-panel_body">
<Text className="embedding-panel_text">{t("Common:Size")}:</Text>
<div className="embedding-panel_links-container">
<Link
isHovered
type="action"
className="embedding-panel_link"
onClick={onSelectSizeMiddle}
>
600 x 800 px
</Link>
<Link
isHovered
type="action"
className="embedding-panel_link"
onClick={onSelectSizeSmall}
>
400 x 600 px
</Link>
<Link
isHovered
type="action"
className="embedding-panel_link"
onClick={onSelectSizeAuto}
>
{t("Auto")}
</Link>
</div>
<div className="embedding-panel_inputs-container">
<div>
<Text className="embedding-panel_text">{t("Width")}:</Text>
<TextInput
className="embedding-panel_input"
value={widthValue}
onChange={onChangeWidth}
/>
<StyledBody isPersonal={isPersonal}>
<div className="embedding-panel_body">
<Text className="embedding-panel_text">{t("Common:Size")}:</Text>
<div className="embedding-panel_links-container">
<Link
isHovered
type="action"
className={`embedding-panel_link ${
size === "600x800" ? "embedding-panel_active-link" : ""
}`}
onClick={onSelectSizeMiddle}
>
600 x 800 px
</Link>
<Link
isHovered
type="action"
className={`embedding-panel_link ${
size === "400x600" ? "embedding-panel_active-link" : ""
}`}
onClick={onSelectSizeSmall}
>
400 x 600 px
</Link>
<Link
isHovered
type="action"
className={`embedding-panel_link ${
size === "auto" ? "embedding-panel_active-link" : ""
}`}
onClick={onSelectSizeAuto}
>
{t("Auto")}
</Link>
</div>
<div>
<Text className="embedding-panel_text">{t("Height")}:</Text>
<TextInput
className="embedding-panel_input"
value={heightValue}
onChange={onChangeHeight}
<div className="embedding-panel_inputs-container">
<div>
<Text className="embedding-panel_text">{t("Width")}:</Text>
<TextInput
className="embedding-panel_input"
value={widthValue}
onChange={onChangeWidth}
/>
</div>
<div>
<Text className="embedding-panel_text">{t("Height")}:</Text>
<TextInput
className="embedding-panel_input"
value={heightValue}
onChange={onChangeHeight}
/>
</div>
</div>
<div className="embedding-panel_code-container">
<Text className="embedding-panel_text">{t("EmbedCode")}:</Text>
<IconButton
className="embedding-panel_copy-icon"
size="16"
iconName="/static/images/copy.react.svg"
// color={theme.filesPanels.embedding.iconColor}
onClick={onCopyLink}
/>
<Textarea
color={theme.filesPanels.embedding.textAreaColor}
isReadOnly
value={link}
/>
</div>
</div>
<div className="embedding-panel_code-container">
<Text className="embedding-panel_text">{t("EmbedCode")}:</Text>
<IconButton
className="embedding-panel_copy-icon"
size="16"
iconName="/static/images/copy.react.svg"
// color={theme.filesPanels.embedding.iconColor}
onClick={onCopyLink}
/>
<Textarea
color={theme.filesPanels.embedding.textAreaColor}
isReadOnly
value={link}
/>
</div>
</div>
</StyledBody>
);
};

View File

@ -0,0 +1,57 @@
import styled, { css } from "styled-components";
const StyledBody = styled.div`
.embedding-panel_body {
padding: ${(props) => (props.isPersonal ? "0 4px 4px" : "0 16px")};
}
.embedding-panel_links-container {
display: flex;
.embedding-panel_link {
margin-right: 8px;
border: 1px solid #eceef1;
border-radius: 16px;
line-height: 20px;
padding: 3px 15px;
box-sizing: border-box;
text-decoration: none;
}
.embedding-panel_active-link {
background: #265a8f;
color: #ffffff;
}
}
.embedding-panel_inputs-container {
display: flex;
margin-top: 16px;
.embedding-panel_input {
margin-right: 8px;
width: 94px;
}
}
.embedding-panel_code-container {
margin-top: 16px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.embedding-panel_text {
padding: 0px 0 4px 0;
}
.embedding-panel_copy-icon {
position: absolute;
z-index: 1;
margin: 8px;
right: 16px;
}
`;
export default StyledBody;

View File

@ -7,12 +7,9 @@ import Aside from "@appserver/components/aside";
import IconButton from "@appserver/components/icon-button";
import { withTranslation, I18nextProvider } from "react-i18next";
import i18n from "./i18n";
import {
StyledEmbeddingPanel,
StyledContent,
StyledHeaderContent,
StyledBody,
} from "../StyledPanels";
import { StyledEmbeddingPanel, StyledContent } from "../StyledPanels";
import { StyledHeaderContent } from "../SharingPanel/StyledSharingPanel";
import EmbeddingBody from "./EmbeddingBody";
@ -25,7 +22,7 @@ class EmbeddingPanelComponent extends React.Component {
};
render() {
const { visible, t, theme } = this.props;
const { visible, t, theme, embeddingLink } = this.props;
const zIndex = 310;
//console.log("EmbeddingPanel render");
@ -39,7 +36,7 @@ class EmbeddingPanelComponent extends React.Component {
/>
<Aside className="header_aside-panel">
<StyledContent>
<StyledHeaderContent>
<StyledHeaderContent isEmbedding={true}>
<IconButton
size="16"
iconName="/static/images/arrow.path.react.svg"
@ -54,9 +51,8 @@ class EmbeddingPanelComponent extends React.Component {
{t("EmbeddingDocument")}
</Heading>
</StyledHeaderContent>
<StyledBody>
<EmbeddingBody theme={theme} />
</StyledBody>
<EmbeddingBody embeddingLink={embeddingLink} theme={theme} />
</StyledContent>
</Aside>
</StyledEmbeddingPanel>

View File

@ -2,138 +2,276 @@ import React from "react";
import ComboBox from "@appserver/components/combobox";
import { ShareAccessRights } from "@appserver/common/constants";
import DropDownItem from "@appserver/components/drop-down-item";
import AccessRightSelect from "@appserver/components/access-right-select";
import { getAccessIcon } from "../../../helpers/files-helpers";
import { ReactSVG } from "react-svg";
const {
FullAccess,
CustomFilter,
Review,
FormFilling,
Comment,
ReadOnly,
DenyAccess,
} = ShareAccessRights;
const AccessComboBox = (props) => {
const {
access,
accessOptions,
directionY,
directionX,
isDisabled,
itemId,
onAccessChange,
t,
theme,
arrowIconColor,
disableLink,
fixedDirection,
canDelete,
onRemoveUserClick,
isExternalLink,
isDefaultMode,
} = props;
const {
FullAccess,
CustomFilter,
Review,
FormFilling,
Comment,
ReadOnly,
DenyAccess,
} = ShareAccessRights;
const advancedOptions = (
<>
{accessOptions.includes("FullAccess") && (
<DropDownItem
theme={theme}
label={t("Common:FullAccess")}
icon="/static/images/access.edit.react.svg"
data-id={itemId}
data-access={FullAccess}
onClick={onAccessChange}
/>
)}
const [isLoading, setIsLoading] = React.useState(true);
const [availableOptions, setAvailableOptions] = React.useState([]);
const [selectedOption, setSelectedOption] = React.useState(null);
{accessOptions.includes("FilterEditing") && (
<DropDownItem
theme={theme}
label={t("CustomFilter")}
icon="/static/images/custom.filter.react.svg"
data-id={itemId}
data-access={CustomFilter}
onClick={onAccessChange}
/>
)}
const ref = React.useRef(null);
{accessOptions.includes("Review") && (
<DropDownItem
theme={theme}
label={t("Common:Review")}
icon="/static/images/access.review.react.svg"
data-id={itemId}
data-access={Review}
onClick={onAccessChange}
/>
)}
const onSelect = React.useCallback(
(e) => {
const access = +e.target.dataset.access;
{accessOptions.includes("FormFilling") && (
<DropDownItem
theme={theme}
label={t("FormFilling")}
icon="/static/images/access.form.react.svg"
data-id={itemId}
data-access={FormFilling}
onClick={onAccessChange}
/>
)}
if (access) {
const item = availableOptions.find((option) => {
return option.dataAccess === access;
});
{accessOptions.includes("Comment") && (
<DropDownItem
theme={theme}
label={t("Comment")}
icon="/static/images/access.comment.react.svg"
data-id={itemId}
data-access={Comment}
onClick={onAccessChange}
/>
)}
setSelectedOption(item);
{accessOptions.includes("ReadOnly") && (
<DropDownItem
theme={theme}
label={t("ReadOnly")}
icon="/static/images/eye.react.svg"
data-id={itemId}
data-access={ReadOnly}
onClick={onAccessChange}
/>
)}
{accessOptions.includes("DenyAccess") && (
<DropDownItem
theme={theme}
label={t("DenyAccess")}
icon="/static/images/access.none.react.svg"
data-id={itemId}
data-access={DenyAccess}
onClick={onAccessChange}
/>
)}
</>
onAccessChange && onAccessChange(e);
} else {
onRemoveUserClick && onRemoveUserClick(e);
}
},
[availableOptions, onAccessChange, onRemoveUserClick]
);
const accessRights = disableLink ? ReadOnly : access;
const accessIconUrl = getAccessIcon(accessRights);
const selectedOption = arrowIconColor
? { key: 0, arrowIconColor }
: { key: 0 };
React.useEffect(() => {
const accessRights = disableLink ? ReadOnly : access;
return (
const newAvailableOptions = [];
accessOptions.forEach((option) => {
switch (option) {
case "FullAccess":
const accessItem = {
key: FullAccess,
title: t("Common:FullAccess"),
label: t("Common:FullAccess"),
icon: "/static/images/access.edit.react.svg",
itemId: itemId,
dataAccess: FullAccess,
};
newAvailableOptions.push(accessItem);
if (accessRights === FullAccess) {
setSelectedOption(accessItem);
}
break;
case "FilterEditing":
const filterItem = {
key: CustomFilter,
title: t("CustomFilter"),
label: t("CustomFilter"),
icon: "/static/images/custom.filter.react.svg",
itemId: itemId,
dataAccess: CustomFilter,
};
newAvailableOptions.push(filterItem);
if (accessRights === CustomFilter) {
setSelectedOption(filterItem);
}
break;
case "Review":
const reviewItem = {
key: Review,
title: t("Common:Review"),
label: t("Common:Review"),
icon: "/static/images/access.review.react.svg",
itemId: itemId,
dataAccess: Review,
};
newAvailableOptions.push(reviewItem);
if (accessRights === Review) {
setSelectedOption(reviewItem);
}
break;
case "FormFilling":
const formItem = {
key: FormFilling,
title: t("FormFilling"),
label: t("FormFilling"),
icon: "/static/images/access.form.react.svg",
itemId: itemId,
dataAccess: FormFilling,
};
newAvailableOptions.push(formItem);
if (accessRights === FormFilling) {
setSelectedOption(formItem);
}
break;
case "Comment":
const commentItem = {
key: Comment,
title: t("Comment"),
label: t("Comment"),
icon: "/static/images/access.comment.react.svg",
itemId: itemId,
dataAccess: Comment,
};
newAvailableOptions.push(commentItem);
if (accessRights === Comment) {
setSelectedOption(commentItem);
}
break;
case "ReadOnly":
const readItem = {
key: ReadOnly,
title: t("ReadOnly"),
label: t("ReadOnly"),
icon: "/static/images/eye.react.svg",
itemId: itemId,
dataAccess: ReadOnly,
};
newAvailableOptions.push(readItem);
if (accessRights === ReadOnly) {
setSelectedOption(readItem);
}
break;
case "DenyAccess":
const denyItem = {
key: DenyAccess,
title: t("DenyAccess"),
label: t("DenyAccess"),
icon: "/static/images/access.none.react.svg",
itemId: itemId,
dataAccess: DenyAccess,
};
newAvailableOptions.push(denyItem);
if (accessRights === DenyAccess) {
setSelectedOption(denyItem);
}
break;
}
});
if (canDelete) {
newAvailableOptions.push({ key: "separator", isSeparator: true });
newAvailableOptions.push({
key: "delete",
title: t("Common:Delete"),
label: t("Common:Delete"),
icon: "/static/images/delete.react.svg",
dataFor: itemId,
onClick: onRemoveUserClick,
});
}
setAvailableOptions(newAvailableOptions);
if (newAvailableOptions.length > 0) {
setIsLoading(false);
}
}, [
access,
disableLink,
accessOptions,
onRemoveUserClick,
itemId,
canDelete,
]);
const renderAdvancedOption = React.useCallback(() => {
return (
<>
{availableOptions?.map((option) => (
<DropDownItem
key={option.key}
label={option.label}
icon={option.icon}
data-id={option.itemId}
data-access={option.dataAccess}
data-for={option.dataFor}
onClick={onSelect}
isSeparator={option.isSeparator}
/>
))}
</>
);
}, [availableOptions, onSelect]);
const advancedOptions = renderAdvancedOption();
return isLoading ? (
<> </>
) : isExternalLink ? (
<AccessRightSelect
options={[]}
selectedOption={selectedOption}
advancedOptions={advancedOptions}
/>
) : (
<ComboBox
theme={theme}
advancedOptions={advancedOptions}
options={[]}
selectedOption={selectedOption}
selectedOption={{}}
size="content"
className="panel_combo-box"
scaled={false}
directionX={directionX}
directionY={directionY}
disableIconClick={false}
isDisabled={isDisabled}
isDefaultMode={false}
isDefaultMode={isDefaultMode}
ref={ref}
forwardRef={ref}
fixedDirection={fixedDirection}
>
<ReactSVG src={accessIconUrl} className="sharing-access-combo-box-icon" />
<ReactSVG
src={selectedOption.icon}
className="sharing-access-combo-box-icon"
/>
</ComboBox>
);
};
export default AccessComboBox;
export default React.memo(AccessComboBox);

View File

@ -0,0 +1,290 @@
import React from "react";
import { VariableSizeList as List } from "react-window";
import { isMobileOnly } from "react-device-detect";
import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-scrollbars-virtual-list";
import { ShareAccessRights } from "@appserver/common/constants";
import ExternalLink from "./ExternalLink";
import InternalLink from "./InternalLink";
import Item from "./Item";
import { StyledBodyContent } from "./StyledSharingPanel";
const Row = React.memo(({ data, index, style }) => {
const {
isMyId,
externalAccessOptions,
onChangeItemAccess,
canShareOwnerChange,
onShowChangeOwnerPanel,
onRemoveUserClick,
t,
items,
selection,
onShowEmbeddingPanel,
externalLinkOpen,
onToggleExternalLinkOpen,
} = data;
if (items === undefined) return;
if (!!items[index]?.sharedTo?.shareLink) {
return (
<ExternalLink
t={t}
isPersonal={false}
selection={selection}
externalItem={items[index]}
externalAccessOptions={externalAccessOptions}
onChangeItemAccess={onChangeItemAccess}
onShowEmbeddingPanel={onShowEmbeddingPanel}
isOpen={externalLinkOpen}
onToggleLink={onToggleExternalLinkOpen}
style={style}
/>
);
}
if (!!items[index]?.internalLink) {
return (
<InternalLink
t={t}
internalLink={items[index]?.internalLink}
style={style}
/>
);
}
return (
<Item
t={t}
item={items[index]}
isMyId={isMyId}
externalAccessOptions={externalAccessOptions}
onChangeItemAccess={onChangeItemAccess}
canShareOwnerChange={canShareOwnerChange(items[index])}
onShowChangeOwnerPanel={onShowChangeOwnerPanel}
onRemoveUserClick={onRemoveUserClick}
isSeparator={items[index].isSeparator}
style={style}
/>
);
});
const Body = ({
t,
selection,
externalItem,
externalAccessOptions,
onChangeItemAccess,
onShowEmbeddingPanel,
onToggleLink,
internalLink,
owner = {},
isMyId,
onShowChangeOwnerPanel,
onRemoveUserClick,
canShareOwnerChange,
shareGroups = [],
shareUsers = [],
isPersonal,
isShared,
}) => {
const [externalLinkVisible, setExternalLinkVisible] = React.useState(false);
const [externalLinkOpen, setExternalLinkOpen] = React.useState(isShared);
const [itemList, setItemList] = React.useState([]);
const [listData, setListData] = React.useState({});
const bodyRef = React.useRef();
const listRef = React.useRef();
const onToggleExternalLinkOpen = React.useCallback(() => {
setExternalLinkOpen((oldState) => !oldState);
onToggleLink && onToggleLink(externalItem);
}, [externalItem, onToggleLink]);
React.useEffect(() => {
setExternalLinkVisible(
selection?.length === 1 && !!externalItem?.sharedTo?.shareLink
);
setExternalLinkOpen(externalItem?.access !== ShareAccessRights.DenyAccess);
}, [externalItem, selection]);
const getItemSize = React.useCallback(
(index) => {
if (itemList.length === 0) return;
if (itemList[index].isSeparator) return 16;
if (itemList[index]?.internalLink) return 62;
if (!!itemList[index]?.sharedTo.shareLink) {
return externalLinkOpen ? 145 : 63;
}
return 48;
},
[itemList, externalLinkOpen]
);
React.useEffect(() => {
if (!isPersonal) {
const items = [];
if (!!owner) {
items.push(owner);
items.push({ key: "separator-1", isSeparator: true });
}
if (shareGroups.length > 0) {
items.push(...shareGroups);
items.push({ key: "separator-2", isSeparator: true });
}
if (shareUsers.length > 0) {
items.push(...shareUsers);
}
const newListData = {
height: bodyRef?.current?.offsetHeight,
width: "auto",
data: {
items: items,
isMyId: isMyId,
externalAccessOptions: externalAccessOptions,
onChangeItemAccess: onChangeItemAccess,
canShareOwnerChange: canShareOwnerChange,
onShowChangeOwnerPanel: onShowChangeOwnerPanel,
onRemoveUserClick: onRemoveUserClick,
t: t,
},
};
if (isMobileOnly) {
if (!!internalLink) {
items.unshift({ internalLink: internalLink });
}
if (selection?.length === 1 && !!externalItem?.sharedTo?.shareLink) {
items.unshift(externalItem);
}
newListData.data.items = items;
newListData.data.selection = selection;
newListData.data.onShowEmbeddingPanel = onShowEmbeddingPanel;
newListData.data.externalLinkOpen = externalLinkOpen;
newListData.data.onToggleExternalLinkOpen = onToggleExternalLinkOpen;
}
setItemList(items);
setListData(newListData);
}
}, [
isPersonal,
bodyRef.current,
owner,
shareGroups,
shareUsers,
isMyId,
externalAccessOptions,
onChangeItemAccess,
canShareOwnerChange,
onShowChangeOwnerPanel,
onRemoveUserClick,
t,
externalItem,
internalLink,
selection,
onShowEmbeddingPanel,
externalLinkOpen,
onToggleExternalLinkOpen,
]);
React.useEffect(() => {
if (!isPersonal) {
listRef?.current?.resetAfterIndex(0);
}
}, [externalLinkOpen, isPersonal]);
return (
<>
{isPersonal ? (
<ExternalLink
t={t}
isPersonal={isPersonal}
selection={selection}
externalItem={externalItem}
externalAccessOptions={externalAccessOptions}
onChangeItemAccess={onChangeItemAccess}
onShowEmbeddingPanel={onShowEmbeddingPanel}
isOpen={externalLinkOpen}
onToggleLink={onToggleExternalLinkOpen}
/>
) : (
<>
{!isMobileOnly ? (
<StyledBodyContent
externalLinkOpen={externalLinkOpen}
externalLinkVisible={externalLinkVisible}
>
{externalLinkVisible && (
<ExternalLink
t={t}
isPersonal={isPersonal}
selection={selection}
externalItem={externalItem}
externalAccessOptions={externalAccessOptions}
onChangeItemAccess={onChangeItemAccess}
onShowEmbeddingPanel={onShowEmbeddingPanel}
isOpen={externalLinkOpen}
onToggleLink={onToggleExternalLinkOpen}
/>
)}
{!!internalLink && (
<InternalLink t={t} internalLink={internalLink} />
)}
<div className="body-scroll-content-sharing-panel" ref={bodyRef}>
{listData?.height && listData?.data?.items?.length > 0 && (
<List
height={listData.height}
width={listData.width}
itemCount={itemList.length}
itemSize={getItemSize}
itemData={listData.data}
outerElementType={CustomScrollbarsVirtualList}
>
{Row}
</List>
)}
</div>
</StyledBodyContent>
) : (
<>
<StyledBodyContent>
<div
className="body-scroll-content-sharing-panel"
ref={bodyRef}
>
{listData?.height && listData?.data?.items?.length > 0 && (
<List
height={listData.height}
width={listData.width}
itemCount={itemList.length}
itemSize={getItemSize}
itemData={listData.data}
outerElementType={CustomScrollbarsVirtualList}
ref={listRef}
>
{Row}
</List>
)}
</div>
</StyledBodyContent>
</>
)}
</>
)}
</>
);
};
export default React.memo(Body);

View File

@ -0,0 +1,202 @@
import React from "react";
import copy from "copy-to-clipboard";
import { objectToGetParams } from "@appserver/common/utils";
import { ShareAccessRights } from "@appserver/common/constants";
import toastr from "@appserver/components/toast/toastr";
import ToggleButton from "@appserver/components/toggle-button";
import InputBlock from "@appserver/components/input-block";
import Button from "@appserver/components/button";
import AccessComboBox from "./AccessComboBox";
import ShareIcon from "../../../../../../../public/images/share.react.svg";
import CodeIcon from "../../../../../../../public/images/code.react.svg";
import { StyledExternalLink } from "./StyledSharingPanel";
import Text from "@appserver/components/text";
import DropDownContainer from "@appserver/components/drop-down";
import DropDownItem from "@appserver/components/drop-down-item";
const ExternalLink = ({
t,
selection,
externalItem,
externalAccessOptions,
onToggleLink,
onShowEmbeddingPanel,
isOpen,
onChangeItemAccess,
style,
isPersonal,
}) => {
const [shareLink, setShareLink] = React.useState("");
const [shareActionOpen, setShareActionOpen] = React.useState(false);
const ref = React.useRef(null);
React.useEffect(() => {
setShareLink(externalItem.sharedTo.shareLink);
}, [externalItem]);
const onToggleShareAction = React.useCallback(() => {
setShareActionOpen((val) => !val);
}, [ref.current]);
const closeShareAction = React.useCallback(
(e) => {
if (ref.current.contains(e.target)) return;
setShareActionOpen((val) => !val);
},
[ref.current]
);
const onCopyLinkAction = React.useCallback(() => {
toastr.success(t("Translations:LinkCopySuccess"));
copy(shareLink);
}, [shareLink]);
const onShowEmbeddingPanelAction = React.useCallback(() => {
onShowEmbeddingPanel && onShowEmbeddingPanel();
}, [onShowEmbeddingPanel]);
const onShareEmail = React.useCallback(() => {
const itemName = selection.title ? selection.title : selection[0].title;
const subject = t("ShareEmailSubject", { itemName });
const body = t("ShareEmailBody", { itemName, shareLink });
const mailtoLink =
"mailto:" +
objectToGetParams({
subject,
body,
});
window.open(mailtoLink, "_self");
onToggleShareAction();
}, [onToggleShareAction, selection, t, shareLink]);
// const onShareFacebook = () => {
// const facebookLink =
// "https://www.facebook.com/sharer/sharer.php" +
// objectToGetParams({
// u: shareLink,
// t: selection.title ? selection.title : selection[0].title,
// });
// window.open(facebookLink);
// };
const onShareTwitter = React.useCallback(() => {
const twitterLink =
"https://twitter.com/intent/tweet" +
objectToGetParams({
text: shareLink,
});
window.open(twitterLink, "", "width=1000,height=670");
onToggleShareAction();
}, [onToggleShareAction, shareLink]);
const options = [
{
key: "linkItem_0",
label: `${t("ShareVia")} e-mail`,
onClick: onShareEmail,
},
// {
// key: "linkItem_1",
// label: `${t("ShareVia")} Facebook`,
// onClick: this.onShareFacebook,
// },
{
key: "linkItem_2",
label: `${t("ShareVia")} Twitter`,
onClick: onShareTwitter,
},
];
return (
<StyledExternalLink isPersonal={isPersonal} isOpen={isOpen} style={style}>
<div className="external-link__base-line">
<Text className="external-link__text" noSelect={true} truncate={true}>
{t("ExternalLink")}
</Text>
<ToggleButton
className="external-link__toggler"
isChecked={isOpen}
onChange={onToggleLink}
/>
</div>
{isOpen && (
<>
<div className="external-link__checked">
<InputBlock
className="external-link__input-link"
scale={true}
isReadOnly={true}
placeholder={shareLink}
isDisabled={true}
>
<div ref={ref} className="external-link__buttons">
<CodeIcon
className="external-link__code-icon"
onClick={onShowEmbeddingPanelAction}
/>
<ShareIcon
className="external-link__share-icon"
onClick={onToggleShareAction}
/>
<DropDownContainer
className="external-link__share-dropdown"
open={shareActionOpen}
clickOutsideAction={closeShareAction}
withBackdrop={false}
isDefaultMode={false}
>
{options.map((option) => (
<DropDownItem
key={option.key}
label={option.label}
onClick={option.onClick}
/>
))}
</DropDownContainer>
</div>
</InputBlock>
<Button
className={"external-link__copy"}
label={t("Translations:Copy")}
size={"small"}
onClick={onCopyLinkAction}
/>
</div>
<div className="external-link__access-rights">
<Text className="external-link__access-rights_text">
Access rights:
</Text>
<AccessComboBox
t={t}
access={externalItem?.access}
directionX="right"
directionY="bottom"
accessOptions={externalAccessOptions}
onAccessChange={onChangeItemAccess}
itemId={externalItem?.sharedTo?.id}
isDisabled={false}
disableLink={false}
isExternalLink={true}
isDefaultMode={false}
fixedDirection={true}
/>
</div>
</>
)}
</StyledExternalLink>
);
};
export default React.memo(ExternalLink);

View File

@ -0,0 +1,49 @@
import React from "react";
import Checkbox from "@appserver/components/checkbox";
import Button from "@appserver/components/button";
import Textarea from "@appserver/components/textarea";
import { StyledFooterContent } from "./StyledSharingPanel";
const Footer = ({
t,
isPersonal,
message,
onChangeMessage,
isNotifyUsers,
onNotifyUsersChange,
onSaveClick,
}) => {
return (
<StyledFooterContent>
{isNotifyUsers && (
<Textarea
className="sharing_panel-notification"
placeholder={t("AddShareMessage")}
onChange={onChangeMessage}
value={message}
/>
)}
{!isPersonal && (
<Checkbox
isChecked={isNotifyUsers}
label={t("Notify users")}
onChange={onNotifyUsersChange}
className="sharing_panel-checkbox"
/>
)}
<Button
className="sharing_panel-button"
label={t("Common:SaveButton")}
scale={true}
size={"normal"}
primary
onClick={onSaveClick}
/>
</StyledFooterContent>
);
};
export default React.memo(Footer);

View File

@ -0,0 +1,109 @@
import React from "react";
import IconButton from "@appserver/components/icon-button";
import Heading from "@appserver/components/heading";
import DropDown from "@appserver/components/drop-down";
import DropDownItem from "@appserver/components/drop-down-item";
import { StyledHeaderContent } from "./StyledSharingPanel";
const Header = ({
t,
isPersonal,
isEncrypted,
uploadPanelVisible,
onShowUsersPanel,
onShowGroupsPanel,
onClose,
label,
}) => {
const [showActionPanel, setShowActionPanel] = React.useState(false);
const ref = React.useRef(null);
const onPlusClick = React.useCallback(() => {
setShowActionPanel((val) => !val);
}, []);
const onCloseActionPanel = React.useCallback(
(e) => {
if (ref.current.contains(e.target)) return;
setShowActionPanel((val) => !val);
},
[ref.current]
);
const onShowUsersPanelAction = React.useCallback(() => {
setShowActionPanel(false);
onShowUsersPanel && onShowUsersPanel();
}, [onShowUsersPanel]);
const onShowGroupsPanelAction = React.useCallback(() => {
setShowActionPanel(false);
onShowGroupsPanel && onShowGroupsPanel();
}, [onShowGroupsPanel]);
return (
<StyledHeaderContent
isPersonal={isPersonal}
className="sharing_panel-header-container"
>
<div className="sharing_panel-header-info">
{uploadPanelVisible && (
<IconButton
size="15px"
iconName="/static/images/arrow.path.react.svg"
className="sharing_panel-arrow"
onClick={onClose}
/>
)}
<Heading
className="sharing_panel-header"
size="medium"
truncate={!isPersonal}
style={{ fontWeight: 700 }}
>
{uploadPanelVisible && label && isPersonal
? label
: t("SharingSettingsTitle")}
</Heading>
</div>
{!isPersonal && (
<div className="sharing_panel-icons-container">
<div ref={ref} className="sharing_panel-drop-down-wrapper">
<IconButton
size="15"
iconName="/static/images/actions.header.touch.react.svg"
className="sharing_panel-plus-icon"
onClick={onPlusClick}
/>
<DropDown
forwardedRef={ref}
directionX="right"
className="sharing_panel-drop-down"
open={showActionPanel}
manualY="30px"
clickOutsideAction={onCloseActionPanel}
>
<DropDownItem
label={t("LinkText")}
onClick={onShowUsersPanelAction}
/>
{!isEncrypted && (
<DropDownItem
label={t("AddGroupsForSharingButton")}
onClick={onShowGroupsPanelAction}
/>
)}
</DropDown>
</div>
</div>
)}
</StyledHeaderContent>
);
};
export default React.memo(Header);

View File

@ -0,0 +1,28 @@
import React from "react";
import copy from "copy-to-clipboard";
import toastr from "@appserver/components/toast/toastr";
import Text from "@appserver/components/text";
import { StyledInternalLink } from "./StyledSharingPanel";
const InternalLink = ({ t, internalLink, style }) => {
const onCopyInternalLinkAction = React.useCallback(() => {
copy(internalLink);
toastr.success(t("Translations:LinkCopySuccess"));
}, [internalLink]);
return (
<StyledInternalLink style={style}>
<Text className={"internal-link__link-text"}>{t("InternalLink")}</Text>
<Text
className={"internal-link__copy-text"}
onClick={onCopyInternalLinkAction}
>
{t("Translations:Copy")}
</Text>
</StyledInternalLink>
);
};
export default React.memo(InternalLink);

View File

@ -0,0 +1,86 @@
import React from "react";
import Avatar from "@appserver/components/avatar";
import Text from "@appserver/components/text";
import AccessComboBox from "./AccessComboBox";
import { StyledItem } from "./StyledSharingPanel";
const Item = ({
t,
item,
canShareOwnerChange,
externalAccessOptions,
onChangeItemAccess,
onShowChangeOwnerPanel,
onRemoveUserClick,
isMyId,
isSeparator,
style,
}) => {
const onShowChangeOwnerPanelAction = React.useCallback(() => {
onShowChangeOwnerPanel && onShowChangeOwnerPanel();
}, [onShowChangeOwnerPanel]);
let itemName = "";
let avatarUrl = "";
if (!isSeparator) {
itemName =
item.sharedTo.id === isMyId
? t("Common:MeLabel")
: !!item.sharedTo.displayName
? item.sharedTo.displayName
: !!item.sharedTo.name
? item.sharedTo.name
: item.sharedTo.label;
avatarUrl = !!item.avatarSmall ? item.avatarSmall : item.avatarUrl;
}
return isSeparator ? (
<StyledItem style={style} isSeparator={isSeparator} />
) : (
<StyledItem style={style}>
<div className="item__info-block">
<Avatar
className="info-block__avatar"
size={"min"}
role={"user"}
source={avatarUrl}
userName={itemName}
/>
<Text className="info-block__text">{itemName}</Text>
</div>
{item.isOwner ? (
canShareOwnerChange ? (
<Text
className="item__change-owner"
onClick={onShowChangeOwnerPanelAction}
>
{t("ChangeOwnerPanel:ChangeOwner").replace("()", "")}
</Text>
) : (
<Text className="item__owner">{t("Common:Owner")}</Text>
)
) : (
<AccessComboBox
t={t}
access={item.access}
directionX="right"
directionY="bottom"
accessOptions={externalAccessOptions}
onAccessChange={onChangeItemAccess}
itemId={item.sharedTo.id}
isDisabled={false}
disableLink={false}
canDelete={true}
onRemoveUserClick={onRemoveUserClick}
isDefaultMode={false}
fixedDirection={true}
/>
)}
</StyledItem>
);
};
export default Item;

View File

@ -1,296 +0,0 @@
import React from "react";
import IconButton from "@appserver/components/icon-button";
import Link from "@appserver/components/link";
import Row from "@appserver/components/row";
import Text from "@appserver/components/text";
import toastr from "@appserver/components/toast/toastr";
import copy from "copy-to-clipboard";
import LinkRow from "./linkRow";
import AccessComboBox from "./AccessComboBox";
import { getAccessIcon } from "../../../helpers/files-helpers";
import { ReactSVG } from "react-svg";
import { objectToGetParams } from "@appserver/common/utils";
class SharingRow extends React.Component {
constructor(props) {
super(props);
this.state = {
access: props.item.access,
};
}
componentDidUpdate() {
const { access } = this.props.item;
if (this.state.access !== access) {
this.setState({ access });
}
}
onCopyInternalLink = () => {
const { internalLink, t } = this.props;
copy(internalLink);
toastr.success(t("Translations:LinkCopySuccess"));
};
onCopyClick = () => {
const { t, item } = this.props;
const { shareLink } = item.sharedTo;
toastr.success(t("Translations:LinkCopySuccess"));
copy(shareLink);
};
onShareEmail = () => {
const { selection, item, t } = this.props;
const { shareLink } = item.sharedTo;
const itemName = selection.title ? selection.title : selection[0].title;
const subject = t("ShareEmailSubject", { itemName });
const body = t("ShareEmailBody", { itemName, shareLink });
const mailtoLink =
"mailto:" +
objectToGetParams({
subject,
body,
});
window.open(mailtoLink, "_self");
};
onShareTwitter = () => {
const { item } = this.props;
const { shareLink } = item.sharedTo;
const twitterLink =
"https://twitter.com/intent/tweet" +
objectToGetParams({
text: shareLink,
});
window.open(twitterLink, "", "width=1000,height=670");
};
// onShareFacebook = () => {
// const { item, selection } = this.props;
// const { shareLink } = item.sharedTo;
// const facebookLink =
// "https://www.facebook.com/sharer/sharer.php" +
// objectToGetParams({
// u: shareLink,
// t: selection.title ? selection.title : selection[0].title,
// });
// window.open(facebookLink);
// };
render() {
//console.log("SharingRow render");
const {
t,
selection,
item,
index,
isMyId,
accessOptions,
onChangeItemAccess,
onRemoveUserClick,
onShowEmbeddingPanel,
onToggleLink,
externalLinkData,
onShowChangeOwnerPanel,
isLoading,
internalLink,
isPersonal,
theme,
} = this.props;
const { access } = this.state;
const canShareOwnerChange = this.props.canShareOwnerChange(item);
const { isOwner, isLocked } = item;
const { label, displayName, name, shareLink, id } = item.sharedTo;
const userName = name
? name === "Everyone"
? t("ShareEveryone")
: name
: "";
const externalLinkVisible =
selection && selection.length === 1 && shareLink;
const internalLinkVisible = index === 0 && internalLink;
const internalLinkData = [
{
key: "linkItem",
label: t("CopyInternalLink"),
onClick: this.onCopyInternalLink,
},
];
const externalLinkOptions = [
{
key: "linkItem_2",
label: `${t("ShareVia")} e-mail`,
onClick: this.onShareEmail,
},
// {
// key: "linkItem_3",
// label: `${t("ShareVia")} Facebook`,
// onClick: this.onShareFacebook,
// },
{
key: "linkItem_4",
label: `${t("ShareVia")} Twitter`,
onClick: this.onShareTwitter,
},
{
key: "linkItem_5",
isSeparator: true,
},
{
key: "linkItem_6",
label: t("Embedding"),
onClick: () => onShowEmbeddingPanel(shareLink),
},
];
const onRemoveUserProp = !isLoading ? { onClick: onRemoveUserClick } : {};
const onShowChangeOwnerPanelProp = !isLoading
? { onClick: onShowChangeOwnerPanel }
: {};
const accessIconUrl = getAccessIcon(access);
return (
<>
{externalLinkVisible && (
<LinkRow
theme={theme}
linkText={t("ExternalLink")}
options={externalLinkOptions}
externalLinkData={externalLinkData}
onToggleLink={onToggleLink}
withToggle
onCopyLink={this.onCopyClick}
{...this.props}
/>
)}
{!isPersonal && (
<>
{internalLinkVisible && (
<LinkRow
theme={theme}
linkText={t("InternalLink")}
options={internalLinkData}
{...this.props}
/>
)}
{!shareLink && (
<Row
theme={theme}
className="sharing-row"
key={`internal-link-key_${id}`}
element={
isOwner || isLocked ? (
<ReactSVG
src={accessIconUrl}
className="sharing_panel-owner-icon"
beforeInjection={(svg) => {
svg
.querySelector("path")
.setAttribute(
"fill",
isLoading
? theme.filesPanels.sharing.loadingFill
: theme.filesPanels.sharing.fill
);
svg.setAttribute(
"style",
`width:16px;
min-width:16px;
height:16px;
min-height:16px;`
);
}}
/>
) : (
<AccessComboBox
t={t}
theme={theme}
access={access}
directionX="left"
onAccessChange={onChangeItemAccess}
itemId={id}
accessOptions={accessOptions}
isDisabled={isLoading}
fixedDirection={true}
/>
)
}
contextButtonSpacerWidth="0px"
>
<>
{!shareLink &&
(isOwner && canShareOwnerChange ? (
<Link
theme={theme}
isHovered
type="action"
{...onShowChangeOwnerPanelProp}
>
{label ? label : userName ? userName : displayName}
</Link>
) : (
<Text
theme={theme}
truncate
className="sharing_panel-text"
>
{label ? label : userName ? userName : displayName}
</Text>
))}
{isOwner ? (
<Text
className="sharing_panel-remove-icon"
theme={theme}
color={theme.filesPanels.sharing.color}
>
{t("Common:Owner")}
</Text>
) : id === isMyId ? (
<Text
className="sharing_panel-remove-icon"
theme={theme}
//color="#A3A9AE"
>
{t("Common:FullAccess")}
</Text>
) : (
!shareLink &&
!isLocked && (
<IconButton
iconName="/static/images/remove.react.svg"
theme={theme}
id={id}
{...onRemoveUserProp}
className="sharing_panel-remove-icon"
// color={theme.filesPanels.sharing.color}
isDisabled={isLoading}
/>
)
)}
</>
</Row>
)}
</>
)}
</>
);
}
}
export default SharingRow;

View File

@ -0,0 +1,375 @@
import { Base } from "@appserver/components/themes";
import styled, { css } from "styled-components";
import { isMobile, isMobileOnly } from "react-device-detect";
const StyledContent = styled.div`
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: ${(props) =>
props.isNotifyUsers
? "53px calc(100% - 253px) 200px"
: "53px calc(100% - 161px) 108px"};
`;
const StyledHeaderContent = styled.div`
width: 100%;
max-width: 100%;
height: ${(props) => (props.isPersonal ? "40px" : "53px")};
border-bottom: ${(props) =>
props.isPersonal ? "none" : props.theme.filesPanels.sharing.borderBottom};
padding: ${(props) => (props.isPersonal ? "0 4px" : "0 16px")};
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
@media (max-width: 500px) {
width: 100vw;
max-width: 100vw;
}
${isMobileOnly &&
css`
width: 100vw;
max-width: 100vw;
`}
.sharing_panel-header-info {
display: flex;
align-items: center;
justify-content: start;
width: calc(100% - 33px);
max-width: calc(100% - 33px);
.sharing_panel-arrow {
.icon-button_svg {
width: 15px;
}
margin-right: 16px;
}
}
${(props) =>
props.isEmbedding &&
css`
width: 100%;
display: flex;
align-items: center;
justify-content: start;
margin-bottom: 16px;
`}
.sharing_panel-icons-container {
display: flex;
margin-left: 16px;
}
`;
StyledHeaderContent.defaultProps = { theme: Base };
const StyledBodyContent = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: start;
.body-scroll-content-sharing-panel {
width: 100%;
height: ${(props) =>
props.externalLinkVisible
? !props.externalLinkOpen
? "calc(100% - 125px)"
: "calc(100% - 207px)"
: "calc(100% - 62px)"};
max-height: ${(props) =>
props.externalLinkVisible
? !props.externalLinkOpen
? "calc(100% - 125px)"
: "calc(100% - 207px)"
: "calc(100% - 62px)"};
${isMobileOnly &&
css`
height: 100% !important;
max-height: 100% !important;
`}
}
`;
const StyledExternalLink = styled.div`
width: 100%;
padding: ${(props) =>
props.isPersonal
? props.isOpen
? "8px 4px 4px"
: "8px 4px 20px"
: "20px 16px"};
border-bottom: ${(props) =>
props.isPersonal ? "none" : props.theme.filesPanels.sharing.borderBottom};
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: start;
.external-link__base-line {
display: flex;
align-items: center;
justify-content: start;
flex-direction: row;
.external-link__text {
font-weight: 700;
font-size: 16px;
line-height: 22px;
margin-right: 16px;
}
.external-link__toggler {
position: relative;
}
}
.external-link__checked {
margin-top: 16px;
width: 100%;
display: flex;
align-items: center;
justify-content: start;
.external-link__input-link {
flex-direction: row-reverse;
padding-right: 0px;
.external-link__buttons {
position: relative;
height: 100%;
display: flex;
align-items: center;
padding: 4px 16px;
.external-link__code-icon {
margin-right: 12px;
cursor: pointer;
path {
fill: ${(props) =>
props.theme.filesPanels.sharing.externalLinkSvg} !important;
}
}
.external-link__share-icon {
cursor: pointer;
path {
fill: ${(props) =>
props.theme.filesPanels.sharing.externalLinkSvg} !important;
}
}
external-link__share-dropdown {
}
}
.append {
display: none;
}
}
.external-link__copy {
margin-left: 8px;
}
.panel_combo-box {
.combo-button {
min-width: 46px;
height: 32px;
.sharing-access-combo-box-icon {
display: flex;
align-items: center;
}
}
.dropdown-container {
top: 32px;
}
}
}
.external-link__access-rights {
display: flex;
align-items: center;
justify-content: start;
flex-direction: row;
margin-top: 16px;
.external-link__access-rights_text {
color: #a3a9ae;
margin-right: 8px;
}
}
`;
StyledExternalLink.defaultProps = { theme: Base };
const StyledInternalLink = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 16px;
box-sizing: border-box;
.internal-link__link-text {
line-height: 22px !important;
font-size: 16px !important;
font-weight: 700 !important;
}
.internal-link__copy-text {
line-height: 15px !important;
font-weight: 600 !important;
border-bottom: ${(props) =>
props.theme.filesPanels.sharing.internalLinkBorder};
cursor: pointer;
}
`;
StyledInternalLink.defaultProps = { theme: Base };
const StyledItem = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
box-sizing: border-box;
.item__info-block {
display: flex;
align-items: center;
justify-content: start;
.info-block__text {
margin-left: 12px;
}
}
.item__change-owner {
line-height: 15px !important;
font-weight: 600 !important;
border-bottom: ${(props) => props.theme.filesPanels.sharing.itemBorder};
cursor: pointer;
}
.item__owner {
color: ${(props) => props.theme.filesPanels.sharing.itemOwnerColor};
}
.panel_combo-box {
.combo-button {
min-width: 46px;
height: 32px;
.sharing-access-combo-box-icon {
display: flex;
align-items: center;
}
}
}
`;
StyledItem.defaultProps = { theme: Base };
const StyledFooterContent = styled.div`
width: 100%;
min-height: 100px;
border-top: ${(props) => props.theme.filesPanels.sharing.borderTop};
position: relative;
box-sizing: border-box;
padding: 16px;
display: flex;
flex-direction: column;
align-items: start;
.sharing_panel-notification {
margin-bottom: 18px;
}
.sharing_panel-checkbox {
margin-bottom: 18px;
}
.sharing_panel-button {
min-height: 40px;
}
`;
StyledFooterContent.defaultProps = { theme: Base };
const StyledModalFooter = styled.div`
width: 100%;
padding: 16px 4px 4px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
button {
height: 40px;
box-sizing: border-box;
}
button:first-child {
margin-right: 8px;
}
`;
export {
StyledContent,
StyledHeaderContent,
StyledBodyContent,
StyledExternalLink,
StyledInternalLink,
StyledItem,
StyledFooterContent,
StyledModalFooter,
};

View File

@ -24,7 +24,14 @@ newInstance.use(Backend).init({
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SharingPanel", "Common", "Translations"],
ns: [
"SharingPanel",
"Common",
"Translations",
"EmbeddingPanel",
"Home",
"ChangeOwnerPanel",
],
defaultNS: "SharingPanel",
react: {

View File

@ -1,28 +1,14 @@
import React from "react";
import Backdrop from "@appserver/components/backdrop";
import Heading from "@appserver/components/heading";
import Aside from "@appserver/components/aside";
import IconButton from "@appserver/components/icon-button";
import Checkbox from "@appserver/components/checkbox";
import Button from "@appserver/components/button";
import DropDown from "@appserver/components/drop-down";
import DropDownItem from "@appserver/components/drop-down-item";
import Textarea from "@appserver/components/textarea";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import Aside from "@appserver/components/aside";
import { withTranslation, Trans } from "react-i18next";
import toastr from "studio/toastr";
import { ShareAccessRights } from "@appserver/common/constants";
import {
StyledAsidePanel,
StyledContent,
StyledFooter,
StyledHeaderContent,
StyledSharingBody,
StyledModalRowContainer,
} from "../StyledPanels";
import { StyledAsidePanel } from "../StyledPanels";
import { AddUsersPanel, AddGroupsPanel, EmbeddingPanel } from "../index";
import SharingRow from "./SharingRow";
import { inject, observer } from "mobx-react";
import config from "../../../../package.json";
import i18n from "./i18n";
@ -33,13 +19,20 @@ import withLoader from "../../../HOCs/withLoader";
import ModalDialog from "@appserver/components/modal-dialog";
import EmbeddingBody from "../EmbeddingPanel/EmbeddingBody";
const SharingBodyStyle = { height: `calc(100vh - 156px)` };
import { StyledContent, StyledModalFooter } from "./StyledSharingPanel";
import Header from "./Header";
import Body from "./Body";
import Footer from "./Footer";
import SharingPanelLoader from "@appserver/common/components/Loaders/SharingPanelLoader";
import SharingPanelLoaderModal from "@appserver/common/components/Loaders/SharingPanelLoader/modal";
// const SharingBodyStyle = { height: `calc(100vh - 156px)` };
class SharingPanelComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
showActionPanel: false,
isNotifyUsers: false,
shareDataItems: [],
baseShareData: [],
@ -53,24 +46,15 @@ class SharingPanelComponent extends React.Component {
showPanel: false,
accessOptions: [],
filesOwnerId: null,
showEmbeddingContent: false,
isUpdated: false,
isLoading: false,
showEmbeddingContent: false,
baseExternalAccess: null,
};
this.ref = React.createRef();
this.scrollRef = React.createRef();
}
onPlusClick = () =>
this.setState({ showActionPanel: !this.state.showActionPanel });
onCloseActionPanel = (e) => {
if (this.ref.current.contains(e.target)) return;
this.setState({ showActionPanel: !this.state.showActionPanel });
};
isUpdateAccessInfo = (selectedAccess) => {
const { baseExternalAccess, isUpdated } = this.state;
@ -251,7 +235,29 @@ class SharingPanelComponent extends React.Component {
onShowUsersPanel = () =>
this.setState({
showAddUsersPanel: !this.state.showAddUsersPanel,
showActionPanel: false,
});
onShowGroupsPanel = () =>
this.setState({
showAddGroupsPanel: !this.state.showAddGroupsPanel,
});
onShowChangeOwnerPanel = () => {
this.setState({
showChangeOwnerPanel: !this.state.showChangeOwnerPanel,
});
};
onShowEmbeddingPanel = (link) =>
this.setState({
showEmbeddingPanel: !this.state.showEmbeddingPanel,
shareLink: link,
});
onShowEmbeddingContainer = (link) =>
this.setState({
showEmbeddingContent: !this.state.showEmbeddingContent,
shareLink: link,
});
onChangeItemAccess = (e) => {
@ -360,67 +366,49 @@ class SharingPanelComponent extends React.Component {
toastr.error(err);
this.onClose();
})
.finally(() =>
this.setState({
isLoading: false,
})
);
.finally(() => {
setTimeout(() => {
return this.setState({
isLoading: false,
});
}, 500);
});
};
getInternalLink = () => {
const { homepage, selection } = this.props;
const item = selection[0];
const isFile = !!item.fileExst;
const isFile = !!item?.fileExst;
if (selection.length !== 1) return null;
return isFile
? item.canOpenPlayer
? `${window.location.href}&preview=${item.id}`
? `${window.location.href}&preview=${item?.id}`
: item.webUrl
: `${window.location.origin + homepage}/filter?folder=${item.id}`;
: `${window.location.origin + homepage}/filter?folder=${item?.id}`;
};
onShowEmbeddingPanel = (link) =>
this.setState({
showEmbeddingPanel: !this.state.showEmbeddingPanel,
shareLink: link,
});
onChangeMessage = (e) => {
this.setState({ message: e.target.value });
};
onShowEmbeddingContainer = (link) =>
this.setState({
showEmbeddingContent: !this.state.showEmbeddingContent,
shareLink: link,
});
onShowGroupsPanel = () =>
this.setState({
showAddGroupsPanel: !this.state.showAddGroupsPanel,
showActionPanel: false,
});
onShowChangeOwnerPanel = () =>
this.setState({
showChangeOwnerPanel: !this.state.showChangeOwnerPanel,
showActionPanel: false,
});
onChangeMessage = (e) => this.setState({ message: e.target.value });
setShareDataItems = (shareDataItems) => this.setState({ shareDataItems });
setShareDataItems = (shareDataItems) => {
this.setState({ shareDataItems });
};
onClose = () => {
const {
onCancel,
setSharingPanelVisible,
selectUploadedFile,
setIsFolderActions,
setSelection,
setBufferSelection,
} = this.props;
setSharingPanelVisible(false);
setSelection([]);
selectUploadedFile([]);
@ -474,8 +462,6 @@ class SharingPanelComponent extends React.Component {
//console.log("Sharing panel render");
const {
t,
theme,
tReady,
isPersonal,
isMyId,
selection,
@ -485,9 +471,10 @@ class SharingPanelComponent extends React.Component {
documentTitle,
sharingPanelVisible,
isPrivacy,
theme,
isShared,
} = this.props;
const {
showActionPanel,
isNotifyUsers,
shareDataItems,
message,
@ -500,262 +487,338 @@ class SharingPanelComponent extends React.Component {
accessOptions,
externalAccessOptions,
showEmbeddingContent,
isUpdated,
isLoading,
} = this.state;
const visible = sharingPanelVisible;
const zIndex = 310;
const onPlusClickProp = !isLoading ? { onClick: this.onPlusClick } : {};
const isEncrypted =
isPrivacy || (selection.length && selection[0].encrypted);
isPrivacy || (selection.length && selection[0]?.encrypted);
const internalLink =
selection.length === 1 && !isEncrypted && this.getInternalLink();
return isPersonal && !isMobileOnly ? (
<ModalDialog
isLoading={!tReady}
visible={visible}
displayType="modal"
onClose={this.onClose}
>
<ModalDialog.Header>{t("SharingSettingsTitle")}</ModalDialog.Header>
<ModalDialog.Body>
<StyledModalRowContainer>
{!isLoading ? (
shareDataItems.map((item, index) => (
<SharingRow
t={t}
theme={theme}
const filteredShareDataItems = [];
const externalItem = [];
const owner = [];
const shareGroups = [];
const shareUsers = [];
shareDataItems.forEach((item) => {
if (item?.sharedTo?.shareLink) {
return externalItem.push(item);
}
if (item?.isOwner) {
item.isUser = true;
return owner.push(item);
}
if (
item?.sharedTo?.userName ||
(item?.sharedTo?.label && item.sharedTo.avatarUrl)
) {
item.isUser = true;
shareUsers.push(item);
} else {
item.isGroup = true;
shareGroups.push(item);
}
});
filteredShareDataItems.push(
...externalItem,
...owner,
...shareGroups,
...shareUsers
);
return (
<>
{isPersonal ? (
<>
{isLoading ? (
isMobileOnly ? (
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={true}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
isPersonal={isPersonal}
index={index}
key={`${item.sharedTo.id}_${index}`}
selection={selection}
item={item}
isMyId={isMyId}
accessOptions={accessOptions}
externalAccessOptions={externalAccessOptions}
canShareOwnerChange={canShareOwnerChange}
onChangeItemAccess={this.onChangeItemAccess}
internalLink={internalLink}
onRemoveUserClick={this.onRemoveUserItemClick}
onShowEmbeddingPanel={this.onShowEmbeddingContainer}
onToggleLink={this.onToggleLink}
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
isLoading={isLoading}
documentTitle={documentTitle}
/>
))
) : (
<Loaders.Rectangle
height="47px"
animate={0}
foregroundColor="#f8f9f9"
backgroundColor="#f8f9f9"
backgroundOpacity={1}
foregroundOpacity={1}
/>
)}
{showEmbeddingContent && (
<EmbeddingBody embeddingLink={shareLink} />
)}
</StyledModalRowContainer>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
className="sharing_panel-button"
label={t("Common:SaveButton")}
size="normal"
primary
onClick={this.onSaveClick}
isDisabled={isLoading || !isUpdated}
/>
</ModalDialog.Footer>
</ModalDialog>
) : (
<StyledAsidePanel visible={visible}>
<Backdrop
onClick={this.onClose}
visible={visible}
zIndex={zIndex}
isAside={true}
/>
<Aside className="header_aside-panel" visible={visible}>
<StyledContent isDisabled={isLoading}>
<StyledHeaderContent className="sharing_panel-header-container">
{uploadPanelVisible && (
<IconButton
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={this.onClose}
color="A3A9AE"
/>
)}
<Heading className="sharing_panel-header" size="medium" truncate>
{t("SharingSettingsTitle")}
</Heading>
{!isPersonal && (
<div className="sharing_panel-icons-container">
<div
ref={this.ref}
className="sharing_panel-drop-down-wrapper"
modalBodyPadding="12px 0 0"
>
<ModalDialog.Body>
<SharingPanelLoaderModal isShared={isShared} />
</ModalDialog.Body>
</ModalDialog>
) : (
<>
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={true}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
width={"400px"}
isPersonal={isPersonal}
modalBodyPadding="12px 0 0"
>
<IconButton
size="17"
iconName="/static/images/actions.header.touch.react.svg"
className="sharing_panel-plus-icon"
{...onPlusClickProp}
// color="A3A9AE"
isDisabled={isLoading}
<ModalDialog.Body>
<SharingPanelLoaderModal isShared={isShared} />
</ModalDialog.Body>
</ModalDialog>
</>
)
) : isMobileOnly ? (
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
modalBodyPadding="12px 0 0"
isPersonal={isPersonal}
>
<ModalDialog.Header>
<Header
t={t}
uploadPanelVisible={showEmbeddingContent}
isPersonal={isPersonal}
isEncrypted={isEncrypted}
onClose={this.onShowEmbeddingContainer}
onShowUsersPanel={this.onShowUsersPanel}
onShowGroupsPanel={this.onShowGroupsPanel}
label={t("EmbeddingPanel:EmbeddingDocument")}
/>
</ModalDialog.Header>
<ModalDialog.Body>
{showEmbeddingContent ? (
<EmbeddingBody
isPersonal={isPersonal}
theme={theme}
embeddingLink={externalItem[0].sharedTo.shareLink}
/>
) : (
<Body
isShared={isShared}
t={t}
isPersonal={isPersonal}
selection={selection}
externalItem={externalItem[0]}
onToggleLink={this.onToggleLink}
onShowEmbeddingPanel={this.onShowEmbeddingContainer}
onChangeItemAccess={this.onChangeItemAccess}
accessOptions={accessOptions}
externalAccessOptions={externalAccessOptions}
/>
)}
</ModalDialog.Body>
<DropDown
forwardedRef={this.ref}
directionX="right"
className="sharing_panel-drop-down"
open={showActionPanel}
manualY="30px"
clickOutsideAction={this.onCloseActionPanel}
>
<DropDownItem
label={t("LinkText")}
onClick={this.onShowUsersPanel}
{!showEmbeddingContent && (
<ModalDialog.Footer>
<StyledModalFooter>
<Button
size={"normal"}
label={t("Common:SaveButton")}
primary
onClick={this.onSaveClick}
scale
/>
{!isEncrypted && (
<DropDownItem
label={t("AddGroupsForSharingButton")}
onClick={this.onShowGroupsPanel}
/>
)}
</DropDown>
</div>
<Button
size={"normal"}
label={t("Common:CancelButton")}
scale
onClick={this.onClose}
/>
</StyledModalFooter>
</ModalDialog.Footer>
)}
</ModalDialog>
) : (
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
width={"400px"}
modalBodyPadding="12px 0 0"
isPersonal={isPersonal}
>
<ModalDialog.Header>
<Header
t={t}
uploadPanelVisible={showEmbeddingContent}
isPersonal={isPersonal}
isEncrypted={isEncrypted}
onClose={this.onShowEmbeddingContainer}
onShowUsersPanel={this.onShowUsersPanel}
onShowGroupsPanel={this.onShowGroupsPanel}
label={t("EmbeddingPanel:EmbeddingDocument")}
/>
</ModalDialog.Header>
{/*<IconButton
size="16"
iconName="images/key.react.svg"
onClick={this.onKeyClick}
/>*/}
</div>
)}
</StyledHeaderContent>
<StyledSharingBody
ref={this.scrollRef}
stype="mediumBlack"
style={SharingBodyStyle}
<ModalDialog.Body>
{showEmbeddingContent ? (
<EmbeddingBody
isPersonal={isPersonal}
theme={theme}
embeddingLink={externalItem[0].sharedTo.shareLink}
/>
) : (
<Body
isShared={isShared}
t={t}
isPersonal={isPersonal}
selection={selection}
externalItem={externalItem[0]}
onToggleLink={this.onToggleLink}
onShowEmbeddingPanel={this.onShowEmbeddingContainer}
onChangeItemAccess={this.onChangeItemAccess}
accessOptions={accessOptions}
externalAccessOptions={externalAccessOptions}
/>
)}
</ModalDialog.Body>
{!showEmbeddingContent && (
<ModalDialog.Footer>
<StyledModalFooter>
<Button
size={"normal"}
label={t("Common:SaveButton")}
primary
onClick={this.onSaveClick}
scale
/>
<Button
size={"normal"}
label={t("Common:CancelButton")}
scale
onClick={this.onClose}
/>
</StyledModalFooter>
</ModalDialog.Footer>
)}
</ModalDialog>
)}
</>
) : (
<StyledAsidePanel visible={visible}>
<Backdrop
onClick={this.onClose}
visible={visible}
zIndex={zIndex}
isAside={true}
/>
<Aside
className="header_aside-panel"
visible={visible}
withoutBodyScroll={true}
>
{!isLoading ? (
shareDataItems.map((item, index) => (
<SharingRow
<StyledContent isNotifyUsers={isNotifyUsers}>
<Header
t={t}
uploadPanelVisible={uploadPanelVisible}
isPersonal={isPersonal}
isEncrypted={isEncrypted}
onClose={this.onClose}
onShowUsersPanel={this.onShowUsersPanel}
onShowGroupsPanel={this.onShowGroupsPanel}
/>
<Body
t={t}
theme={theme}
isPersonal={isPersonal}
index={index}
key={`${item.sharedTo.id}_${index}`}
selection={selection}
item={item}
externalItem={externalItem[0]}
owner={owner[0]}
shareGroups={shareGroups}
shareUsers={shareUsers}
isMyId={isMyId}
onToggleLink={this.onToggleLink}
onShowEmbeddingPanel={this.onShowEmbeddingPanel}
onChangeItemAccess={this.onChangeItemAccess}
accessOptions={accessOptions}
externalAccessOptions={externalAccessOptions}
canShareOwnerChange={canShareOwnerChange}
onChangeItemAccess={this.onChangeItemAccess}
internalLink={internalLink}
onRemoveUserClick={this.onRemoveUserItemClick}
onShowEmbeddingPanel={this.onShowEmbeddingPanel}
onToggleLink={this.onToggleLink}
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
isLoading={isLoading}
documentTitle={documentTitle}
/>
))
) : (
<div key="loader" className="panel-loader-wrapper">
<Loader type="oval" size="16px" className="panel-loader" />
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
</div>
)}
{isNotifyUsers && (
<div className="sharing_panel-text-area">
<Textarea
placeholder={t("AddShareMessage")}
onChange={this.onChangeMessage}
value={message}
isDisabled={isLoading}
<Footer
t={t}
isPersonal={isPersonal}
message={message}
onChangeMessage={this.onChangeMessage}
isNotifyUsers={isNotifyUsers}
onNotifyUsersChange={this.onNotifyUsersChange}
onSaveClick={this.onSaveClick}
/>
</div>
</StyledContent>
) : (
<SharingPanelLoader />
)}
</StyledSharingBody>
<StyledFooter>
{!isPersonal && (
<Checkbox
isChecked={isNotifyUsers}
label={t("Notify users")}
onChange={this.onNotifyUsersChange}
className="sharing_panel-checkbox"
isDisabled={isLoading}
/>
)}
<Button
className="sharing_panel-button"
label={t("Common:SaveButton")}
size={isMobile ? "normal" : "small"}
minwidth="100px"
primary
onClick={this.onSaveClick}
isDisabled={isLoading}
</Aside>
{showAddUsersPanel && (
<AddUsersPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowUsersPanel}
visible={showAddUsersPanel}
shareDataItems={filteredShareDataItems}
setShareDataItems={this.setShareDataItems}
groupsCaption={groupsCaption}
accessOptions={accessOptions}
isMultiSelect
isEncrypted={isEncrypted}
/>
</StyledFooter>
</StyledContent>
</Aside>
)}
{showAddUsersPanel && (
<AddUsersPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowUsersPanel}
visible={showAddUsersPanel}
shareDataItems={shareDataItems}
setShareDataItems={this.setShareDataItems}
groupsCaption={groupsCaption}
accessOptions={accessOptions}
isMultiSelect
isEncrypted={isEncrypted}
/>
)}
{showAddGroupsPanel && (
<AddGroupsPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowGroupsPanel}
visible={showAddGroupsPanel}
shareDataItems={filteredShareDataItems}
setShareDataItems={this.setShareDataItems}
accessOptions={accessOptions}
isMultiSelect
/>
)}
{showAddGroupsPanel && (
<AddGroupsPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowGroupsPanel}
visible={showAddGroupsPanel}
shareDataItems={shareDataItems}
setShareDataItems={this.setShareDataItems}
accessOptions={accessOptions}
isMultiSelect
/>
)}
{showChangeOwnerPanel && (
<AddUsersPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowChangeOwnerPanel}
visible={showChangeOwnerPanel}
shareDataItems={filteredShareDataItems}
setShareDataItems={this.setShareDataItems}
/>
)}
{showChangeOwnerPanel && (
<AddUsersPanel
onSharingPanelClose={this.onClose}
onClose={this.onShowChangeOwnerPanel}
visible={showChangeOwnerPanel}
shareDataItems={shareDataItems}
setShareDataItems={this.setShareDataItems}
/>
{showEmbeddingPanel && (
<EmbeddingPanel
visible={showEmbeddingPanel}
onSharingPanelClose={this.onClose}
onClose={this.onShowEmbeddingPanel}
embeddingLink={externalItem[0].sharedTo.shareLink}
/>
)}
</StyledAsidePanel>
)}
{showEmbeddingPanel && (
<EmbeddingPanel
visible={showEmbeddingPanel}
onSharingPanelClose={this.onClose}
onClose={this.onShowEmbeddingPanel}
embeddingLink={shareLink}
/>
)}
</StyledAsidePanel>
</>
);
}
}
@ -774,6 +837,7 @@ const SharingPanel = inject(
) => {
const { replaceFileStream, setEncryptionAccess } = auth;
const { personal, customNames, isDesktopClient } = auth.settingsStore;
const { user } = auth.userStore;
const { id, access } = selectedFolderStore;
@ -841,19 +905,32 @@ const SharingPanel = inject(
id,
setBufferSelection,
access,
isShared: selection[0].shared,
};
}
)(
observer(
withTranslation(["SharingPanel", "Common", "Translations"])(
withLoader(SharingPanelComponent)(<Loaders.DialogAsideLoader isPanel />)
)
withTranslation([
"SharingPanel",
"Common",
"Translations",
"Home",
"ChangeOwnerPanel",
"EmbeddingPanel",
])(withLoader(SharingPanelComponent)(<Loaders.DialogAsideLoader isPanel />))
)
);
class Panel extends React.Component {
static convertSharingUsers = (shareDataItems) => {
const t = i18n.getFixedT(null, ["SharingPanel", "Common"]);
const t = i18n.getFixedT(null, [
"SharingPanel",
"Common",
"Translations",
"Home",
"ChangeOwnerPanel",
"EmbeddingPanel",
]);
const {
FullAccess,
DenyAccess,

View File

@ -1,7 +1,7 @@
import styled, { css } from "styled-components";
import Scrollbar from "@appserver/components/scrollbar";
import { desktop, tablet } from "@appserver/components/utils/device";
import { isMobile } from "react-device-detect";
import { desktop, mobile, tablet } from "@appserver/components/utils/device";
import { isMobile, isMobileOnly } from "react-device-detect";
import { Base } from "@appserver/components/themes";
const PanelStyles = css`
@ -37,8 +37,8 @@ const PanelStyles = css`
}
.footer {
padding: 16px 0;
width: calc(100% - 32px);
padding: 16px;
width: 100%;
margin: auto;
left: 0;
right: 0;
@ -80,6 +80,7 @@ const StyledAsidePanel = styled.div`
padding-top: ${isMobile ? "55px" : "48px"};
height: ${isMobile ? "calc(100vh - 55px)" : "calc(100vh - 48px)"};
}
.modal-dialog-aside {
padding: 0;
transform: translateX(${(props) => (props.visible ? "0" : "500px")});
@ -93,18 +94,31 @@ const StyledAsidePanel = styled.div`
.header_aside-panel {
transition: unset;
transform: translateX(${(props) => (props.visible ? "0" : "500px")});
width: 500px;
transform: translateX(${(props) => (props.visible ? "0" : "480px")});
width: 480px;
max-width: 480px;
overflow-y: hidden;
@media (max-width: 550px) {
width: 320px;
transform: translateX(${(props) => (props.visible ? "0" : "320px")});
@media (max-width: 500px) {
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
}
.sharing_panel-header-container {
padding-right: 0;
}
${isMobileOnly &&
css`
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
`}
}
${PanelStyles}
`;
@ -152,13 +166,32 @@ StyledVersionHistoryPanel.defaultProps = { theme: Base };
const StyledAddUsersPanelPanel = styled.div`
.header_aside-panel {
transform: translateX(${(props) => (props.visible ? "0" : "500px")});
width: 500px;
transition: unset;
transform: translateX(${(props) => (props.visible ? "0" : "480px")});
width: 480px;
max-width: 480px;
overflow-y: hidden;
@media (max-width: 550px) {
width: 320px;
transform: translateX(${(props) => (props.visible ? "0" : "320px")});
@media (max-width: 500px) {
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
}
${isMobileOnly &&
css`
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
`}
}
${PanelStyles}
.combo-button-label {
@ -168,13 +201,32 @@ const StyledAddUsersPanelPanel = styled.div`
const StyledAddGroupsPanel = styled.div`
.header_aside-panel {
transform: translateX(${(props) => (props.visible ? "0" : "500px")});
width: 500px;
transition: unset;
transform: translateX(${(props) => (props.visible ? "0" : "480px")});
width: 480px;
max-width: 480px;
overflow-y: hidden;
@media (max-width: 550px) {
width: 320px;
transform: translateX(${(props) => (props.visible ? "0" : "320px")});
@media (max-width: 500px) {
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
}
${isMobileOnly &&
css`
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
`}
}
${PanelStyles}
.combo-button-label {
@ -184,13 +236,32 @@ const StyledAddGroupsPanel = styled.div`
const StyledEmbeddingPanel = styled.div`
.header_aside-panel {
transform: translateX(${(props) => (props.visible ? "0" : "500px")});
width: 500px;
transition: unset;
transform: translateX(${(props) => (props.visible ? "0" : "480px")});
width: 480px;
max-width: 480px;
overflow-y: hidden;
@media (max-width: 550px) {
width: 320px;
transform: translateX(${(props) => (props.visible ? "0" : "320px")});
@media (max-width: 500px) {
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
}
${isMobileOnly &&
css`
position: fixed;
top: 64px;
bottom: 0;
left: 0;
width: 100vw;
height: calc(100vh - 64px);
`}
}
${PanelStyles}
`;
@ -258,21 +329,6 @@ const StyledHeaderContent = styled.div`
align-items: center;
padding: 0 16px;
.sharing_panel-icons-container {
display: flex;
margin-left: auto;
.sharing_panel-drop-down-wrapper {
position: relative;
.sharing_panel-drop-down {
padding: 4px 0;
}
.sharing_panel-plus-icon {
//margin-right: 12px;
}
}
}
.upload_panel-icons-container {
display: flex;
margin-left: auto;
@ -307,10 +363,6 @@ const StyledBody = styled.div`
}
}
.embedding-panel_body {
padding: 0 16px;
}
.change-owner_body {
padding: 0 16px;
display: flex;
@ -355,42 +407,6 @@ const StyledBody = styled.div`
}
}
.embedding-panel_links-container {
display: flex;
.embedding-panel_link {
margin-right: 8px;
height: 32px;
background-color: ${(props) =>
props.theme.filesPanels.body.backgroundColor};
line-height: 30px;
padding: 0px 8px;
}
}
.embedding-panel_inputs-container {
display: flex;
.embedding-panel_input {
margin-right: 8px;
width: 94px;
}
}
.embedding-panel_code-container {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.embedding-panel_text {
padding: 8px 0 4px 0;
}
.embedding-panel_copy-icon {
position: absolute;
z-index: 1;
margin: 8px;
right: 16px;
}
.sharing-access-combo-box-icon {
path {
fill: ${(props) => props.theme.filesPanels.body.fill};
@ -542,8 +558,8 @@ const StyledFooter = styled.div`
display: flex;
position: fixed;
bottom: 0;
padding: 16px 0;
width: calc(100% - 32px);
padding: 16px;
width: 100%;
margin: auto;
left: 0;
right: 0;
@ -571,7 +587,7 @@ const StyledFooter = styled.div`
}
@media ${desktop} {
padding: 10px 0;
padding: 16px;
min-height: 57px;
.sharing_panel-checkbox {
@ -723,13 +739,14 @@ const StyledModalRowContainer = styled.div`
.embedding-panel_links-container {
display: flex;
.embedding-panel_link {
margin-right: 8px;
height: 32px;
background-color: ${(props) =>
props.theme.filesPanels.modalRow.backgroundColor};
border: 1px solid #eceef1;
border-radius: 16px;
line-height: 30px;
padding: 0px 8px;
padding: 4px 15px;
}
}

View File

@ -41,11 +41,6 @@ const SimpleFilesRowContent = styled(RowContent)`
width: max-content;
}
.is-editing {
path {
fill: ${(props) => props.theme.filesSection.rowView.editingIconColor};
}
}
${(props) =>
((props.sectionWidth <= 1024 && props.sectionWidth > 500) || isTablet) &&
`

View File

@ -311,6 +311,8 @@ const FilesTableRow = (props) => {
if (showHotkeyBorder) {
setHeaderBorder(true);
} else {
const elem = document.getElementById("table-container_caption-header");
if (elem) elem.style.borderColor = "#ECEEF1";
setHeaderBorder(false);
}
}

View File

@ -55,7 +55,7 @@ const StyledTile = styled.div`
border: ${(props) => props.theme.filesSection.tilesView.tile.border};
border-radius: 6px;
${(props) => props.showHotkeyBorder && "border-color: #2DA7DB"};
${(props) => props.isFolder && "border-top-left-radius: 0px;"}
${(props) => props.isFolder && "border-top-left-radius: 6px;"}
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
${(props) => props.isFolder && FlexBoxStyles}

View File

@ -472,7 +472,10 @@ class HotkeyStore {
get caretIndex() {
const { filesList, hotkeyCaret, selection } = this.filesStore;
const id = selection.length ? selection[0].id : hotkeyCaret?.id;
const id =
selection.length && selection.length === 1 && !hotkeyCaret
? selection[0].id
: hotkeyCaret?.id;
const caretIndex = filesList.findIndex((f) => f.id === id);
if (caretIndex !== -1) return caretIndex;
@ -522,7 +525,7 @@ class HotkeyStore {
}
get prevFile() {
const { filesList, selection, hotkeyCaret } = this.filesStore;
const { filesList } = this.filesStore;
if (this.caretIndex !== -1) {
const prevCaretIndex = this.caretIndex - 1;

View File

@ -0,0 +1,48 @@
const Endpoints = require("./mocking/endpoints.js");
const browser = process.env.profile || "chromium";
const isModel = !!process.env.MODEL;
const deviceType = process.env.DEVICE_TYPE || "desktop";
const featureName = isModel
? `Render test on '${browser}' with '${deviceType}' dimension (model)`
: `Render test on '${browser}' with '${deviceType}' dimension`;
Feature(featureName);
Before(async ({ I }) => {
I.mockEndpoint(Endpoints.self, "self");
I.mockEndpoint(Endpoints.settings, "settings");
I.mockEndpoint(Endpoints.build, "build");
I.mockEndpoint(Endpoints.info, "info");
I.mockEndpoint(Endpoints.common, "common");
I.mockEndpoint(Endpoints.cultures, "cultures");
I.mockEndpoint(Endpoints.root, "one");
I.mockEndpoint(Endpoints.my, "duplicate");
I.mockEndpoint(Endpoints.fileSettings, "default");
I.mockEndpoint(Endpoints.getFolder(1), "1");
I.mockEndpoint(Endpoints.capabilities, "capabilities");
I.mockEndpoint(Endpoints.thirdparty, "thirdparty");
I.mockEndpoint(Endpoints.thumbnails, "thumbnails");
});
Scenario("Duplicate id test", async ({ I }) => {
I.amOnPage("/products/files");
I.wait(3);
if (deviceType !== "desktop") {
I.click({ react: "SortButton" });
I.click({ name: "view-selector-name_tile" });
} else {
I.click({ name: "view-selector-name_tile" });
}
I.wait(3);
I.saveScreenshot("duplicate-id-tile-view.png");
if (!isModel) {
I.seeVisualDiff("duplicate-id-tile-view.png", {
tolerance: 0.16,
prepareBaseImage: false,
});
}
});

View File

@ -0,0 +1,112 @@
{
"count": 1,
"response": {
"files": [
{
"folderId": 1,
"version": 1,
"versionGroup": 1,
"contentLength": "19.01 KB",
"pureContentLength": 19464,
"fileStatus": 0,
"viewUrl": "http://192.168.31.38:8092/products/files/httphandlers/filehandler.ashx?action=download&fileid=20",
"webUrl": "http://192.168.31.38:8092/products/files/doceditor?fileid=20&version=1",
"fileType": 7,
"fileExst": ".docx",
"comment": "Uploaded",
"thumbnailUrl": "http://192.168.31.38:8092/products/files/httphandlers/filehandler.ashx?action=thumb&fileid=20&version=1",
"thumbnailStatus": 1,
"canWebRestrictedEditing": false,
"canFillForms": true,
"id": 20,
"rootFolderId": 1,
"canShare": true,
"canEdit": true,
"title": "New document.docx",
"access": 0,
"shared": false,
"created": "2022-04-06T17:34:30.0000000Z",
"createdBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
},
"updated": "2022-04-06T17:34:30.0000000Z",
"rootFolderType": 5,
"updatedBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
}
}
],
"folders": [
{
"parentId": 1,
"filesCount": 3,
"foldersCount": 0,
"new": 0,
"id": 20,
"rootFolderId": 1,
"canShare": true,
"canEdit": true,
"title": "New folder",
"access": 0,
"shared": false,
"created": "2022-04-13T08:54:10.0000000Z",
"createdBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
},
"updated": "2022-04-13T08:56:18.0000000Z",
"rootFolderType": 5,
"updatedBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
}
}
],
"current": {
"parentId": 0,
"filesCount": 28,
"foldersCount": 3,
"isShareable": true,
"new": 0,
"id": 1,
"rootFolderId": 1,
"canShare": true,
"canEdit": true,
"title": "My documents",
"access": 0,
"shared": false,
"created": "2022-04-01T12:20:35.0000000Z",
"createdBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
},
"updated": "2022-04-14T08:22:42.0000000Z",
"rootFolderType": 5,
"updatedBy": {
"id": "6047b48f-7540-11ec-af2b-d45d64a7a0a5",
"displayName": "Administrator",
"avatarSmall": "/storage/userPhotos/root/6047b48f-7540-11ec-af2b-d45d64a7a0a5_size_32-32.jpeg?_=-1380565972",
"profileUrl": "http://192.168.31.38:8092/products/people/view/administrator"
}
},
"pathParts": [1],
"startIndex": 0,
"count": 25,
"total": 28,
"new": 0
},
"status": 0,
"statusCode": 200
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,3 +1,10 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 2.30269C16 1.58472 15.418 1.00269 14.7 1.00269H1.3C0.58203 1.00269 0 1.58472 0 2.30269L0 14.9735C0 16.2368 1.61969 16.7578 2.35619 15.7315L3.20892 14.5432C3.45311 14.2029 3.84628 14.0011 4.26511 14.0011H14.7C15.418 14.0011 16 13.4191 16 12.7011V2.30269Z" fill="#A3A9AE"/>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20763_43822)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 2.99976C0 1.89519 0.89543 0.999756 2 0.999756H14C15.1046 0.999756 16 1.89519 16 2.99976V10.9998C16 12.1043 15.1046 12.9998 14 12.9998H5.41421L3.41421 14.9998C2.15428 16.2597 0 15.3673 0 13.5855V2.99976ZM14 2.99976L2 2.99976V13.5855L4 11.5855C4.37507 11.2105 4.88378 10.9998 5.41421 10.9998H14V2.99976Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20763_43822">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 625 B

View File

@ -1,3 +1,10 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.00504 1.99976L10.0042 5.00699L4.00172 11.0111L1.00062 8.00828L7.00504 1.99976ZM11.7793 1.40022L10.684 0.298922C10.2607 -0.126691 9.11354 -0.126691 8.50125 0.48686L7.99954 0.988305L11.0224 4.00377L11.519 3.5032C12.1076 2.9113 12.1076 1.73033 11.7793 1.40022ZM0 11.6677C0.00679371 11.8812 0.120601 12 0.342874 12H3.00156L0.00679371 9.00152L0 11.6677Z" fill="#A3A9AE"/>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20768_40080)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2929 1.29312C11.5119 0.074169 13.4882 0.0741679 14.7071 1.29312C15.9261 2.51207 15.9261 4.48838 14.7071 5.70733L5.70713 14.7073C5.57897 14.8355 5.41839 14.9264 5.24256 14.9704L1.24256 15.9704C0.901782 16.0556 0.541295 15.9557 0.292914 15.7073C0.0445341 15.459 -0.055315 15.0985 0.0298787 14.7577L1.02988 10.7577C1.07384 10.5819 1.16476 10.4213 1.29291 10.2931L10.2929 1.29312ZM13.2929 2.70733C12.855 2.26943 12.145 2.26943 11.7071 2.70733L10.9142 3.50023L12.5 5.08601L13.2929 4.29312C13.7308 3.85522 13.7308 3.14524 13.2929 2.70733ZM11.0858 6.50023L9.50002 4.91444L2.90299 11.5115L2.37439 13.6259L4.48877 13.0973L11.0858 6.50023Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20768_40080">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 954 B

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 0C0.895431 0 0 0.895431 0 2V14C0 15.1046 0.895431 16 2 16H14C15.1046 16 16 15.1046 16 14V2C16 0.895431 15.1046 0 14 0H2ZM14 2H2V5H14V2ZM2 7H14V10H2V7ZM9 12C8.44772 12 8 12.4477 8 13C8 13.5523 8.44772 14 9 14H13C13.5523 14 14 13.5523 14 13C14 12.4477 13.5523 12 13 12H9Z" fill="#A3A9AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 1C1.89543 1 1 1.89543 1 3V13C1 14.1046 1.89543 15 3 15H13C14.1046 15 15 14.1046 15 13V3C15 1.89543 14.1046 1 13 1H3ZM3 3L13 3V13H3V3ZM4 6H12V4H4V6ZM4 9H12V7H4V9ZM8 12H12V10H8V12Z" fill="#333333"/>
</svg>

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 352 B

View File

@ -1,3 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8ZM5.29071 12.5949C6.08473 13.0641 7.01092 13.3333 8 13.3333C10.9455 13.3333 13.3333 10.9455 13.3333 8C13.3333 7.01092 13.0641 6.08473 12.5949 5.29071L5.29071 12.5949ZM3.40509 10.7093C2.93591 9.91527 2.66667 8.98908 2.66667 8C2.66667 5.05448 5.05448 2.66667 8 2.66667C8.98908 2.66667 9.91527 2.93591 10.7093 3.40509L3.40509 10.7093Z" fill="#A3A9AE"/>
<g clip-path="url(#clip0_20763_37010)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4769 3.10916C9.13367 1.43923 5.85958 1.65538 3.75736 3.7576C1.65513 5.85983 1.43898 9.13391 3.10891 11.4771L11.4769 3.10916ZM12.8911 4.52337L4.52313 12.8913C6.86633 14.5613 10.1404 14.3451 12.2426 12.2429C14.3449 10.1407 14.561 6.86657 12.8911 4.52337ZM2.34315 2.34339C5.46734 -0.780804 10.5327 -0.780804 13.6569 2.34339C16.781 5.46758 16.781 10.5329 13.6569 13.6571C10.5327 16.7813 5.46734 16.7813 2.34315 13.6571C-0.781049 10.5329 -0.781049 5.46758 2.34315 2.34339Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20763_37010">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 792 B

View File

@ -1,3 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7 1.00269C15.418 1.00269 16 1.58472 16 2.30269V11.7011C16 12.4191 15.418 13.0011 14.7 13.0011H4.26511C3.84628 13.0011 3.45311 13.2029 3.20892 13.5432L2.35619 14.7315C1.61969 15.7578 0 15.2368 0 13.9735V2.30269C0 1.58472 0.58203 1.00269 1.3 1.00269H14.7ZM6.28642 9.68714C6.69054 10.1041 7.34657 10.1041 7.75089 9.68714L11.7259 5.82285C11.92 5.6226 12.0291 5.35091 12.0291 5.06775C12.0291 4.78458 11.92 4.51289 11.7259 4.31264C11.3216 3.89564 10.6655 3.89564 10.2614 4.31264L7.20374 7.42846C7.10149 7.53352 6.93582 7.53352 6.83377 7.42846L5.79681 6.34682C5.2987 5.99655 4.73646 5.92982 4.33234 6.34682C3.92803 6.76362 3.92803 7.44023 4.33234 7.85703L6.28642 9.68714Z" fill="#A3A9AE"/>
<g clip-path="url(#clip0_20763_43934)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2.99988L8.75736 2.99988L10.7574 0.999878H2C0.89543 0.999878 0 1.89531 0 2.99988V13.5857C0 15.3675 2.15428 16.2598 3.41421 14.9999L5.41421 12.9999H14C15.1046 12.9999 16 12.1044 16 10.9999V4.24252L14 6.24252V10.9999H5.41421C4.88378 10.9999 4.37507 11.2106 4 11.5857L2 13.5857V2.99988ZM14.7071 2.70698L8.20711 9.20698C7.81658 9.59751 7.18342 9.59751 6.79289 9.20699L3.79289 6.20698L5.2071 4.79277L7.5 7.08566L13.2929 1.29277L14.7071 2.70698Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20763_43934">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 762 B

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20597_65864)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84646 0.979492L5.22299 14.5025L7.15485 15.0201L10.7783 1.49713L8.84646 0.979492ZM2.41419 7.99916L5.20702 5.20614L3.79276 3.79197L0.292868 7.2921C-0.0976332 7.68263 -0.0976212 8.31578 0.292895 8.7063L3.79278 12.2062L5.20699 10.7919L2.41419 7.99916ZM13.5857 8.00004L10.7928 5.20714L12.207 3.79292L15.707 7.29293C15.8945 7.48047 15.9999 7.73482 15.9999 8.00004C15.9999 8.26526 15.8945 8.51961 15.707 8.70715L12.2065 12.2076L10.7923 10.7934L13.5857 8.00004Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20597_65864">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -1,3 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.14502 -1.07443e-06L14.8481 -2.59613e-06C15.4812 -2.70681e-06 15.9962 0.515037 15.9962 1.14808L15.9962 9.85224C15.9962 10.4853 15.4812 11.0004 14.8481 11.0004L11.9896 11.0004L11.9896 6.03888C11.9896 4.82791 11.242 4.00369 10.031 4.00369L4.99694 4.00369L4.99694 1.14809C4.99693 0.515038 5.51194 -9.63738e-07 6.14502 -1.07443e-06ZM1.12973 4.99872L9.87024 4.99872C10.4932 4.99872 11 5.50551 11 6.12845L11 14.8703C11 15.4932 10.4932 16 9.87028 16L1.12973 16C0.50679 16 3.70837e-05 15.4932 2.6e-06 14.8703L1.07154e-06 6.12845C9.62618e-07 5.50551 0.506823 4.99872 1.12973 4.99872Z" fill="#A3A9AE"/>
<g clip-path="url(#clip0_20707_66689)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.00001 -0.000241343C4.89544 -0.000241343 4.00001 0.895189 4.00001 1.99976L6.00001 1.99976L14 1.99976L14 9.99976L14 11.9998C15.1046 11.9998 16 11.1043 16 9.99976L16 1.99976C16 0.895189 15.1046 -0.000241343 14 -0.000241343L6.00001 -0.000241343ZM2.00001 3.99976C0.895436 3.99976 5.59506e-06 4.89519 5.59506e-06 5.99976L5.59506e-06 13.9998C5.59506e-06 15.1043 0.895437 15.9998 2.00001 15.9998L10 15.9998C11.1046 15.9998 12 15.1043 12 13.9998L12 5.99976C12 4.89519 11.1046 3.99976 10 3.99976L2.00001 3.99976ZM2.00001 13.9998L2.00001 5.99976L10 5.99976L10 13.9998L2.00001 13.9998Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_20707_66689">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 897 B

View File

@ -1,9 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_17158:39464)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49896 0L8.49896 0C9.32739 0 9.99896 0.671573 9.99896 1.5V2H10L13 2C14.1049 2 15 2.89593 15 4.00022L13 4.00022V4L10 4H8.49896L7.49896 4H6L3 4V4.00022H1C1 2.89593 1.89515 2 3 2L5.99896 2V1.5C5.99896 0.671573 6.67054 0 7.49896 0ZM3 5.00023L3 13.0002C3 14.6571 4.34315 16.0002 6 16.0002H10C11.6569 16.0002 13 14.6571 13 13.0002L13 5.00023H11L11 13.0002C11 13.5525 10.5523 14.0002 10 14.0002L6 14.0002C5.44771 14.0002 5 13.5525 5 13.0002L5 5.00023L3 5.00023ZM7 6L7 14H9V6H7Z" fill="#333333"/>
<g clip-path="url(#clip0_20763_37019)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49896 0H8.49896C9.32739 0 9.99896 0.671573 9.99896 1.5V2H10H13C14.1049 2 15 2.89593 15 4.00022H13V4H10H8.49896H7.49896H6H3V4.00022H1C1 2.89593 1.89515 2 3 2H5.99896V1.5C5.99896 0.671573 6.67054 0 7.49896 0ZM3 5.00023V13.0002C3 14.6571 4.34315 16.0002 6 16.0002H10C11.6569 16.0002 13 14.6571 13 13.0002V5.00023H11V13.0002C11 13.5525 10.5523 14.0002 10 14.0002H6C5.44771 14.0002 5 13.5525 5 13.0002V5.00023H3ZM7 6V14H9V6H7Z" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_17158:39464">
<clipPath id="clip0_20763_37019">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 745 B

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.49952 10.4663C3.47193 9.6138 2.66403 8.61284 2.21523 8C2.66402 7.38716 3.47193 6.3862 4.49952 5.53367C5.58351 4.63435 6.79128 4 8.00001 4C9.20874 4 10.4165 4.63435 11.5005 5.53367C12.5281 6.3862 13.336 7.38716 13.7848 8C13.336 8.61284 12.5281 9.6138 11.5005 10.4663C10.4165 11.3656 9.20874 12 8.00001 12C6.79128 12 5.58351 11.3656 4.49952 10.4663ZM8.00001 2C6.1116 2 4.45712 2.97015 3.22251 3.99443C1.97234 5.03163 1.02832 6.22746 0.542514 6.8996C0.0652547 7.55992 0.0652569 8.44008 0.542514 9.1004C1.02832 9.77254 1.97234 10.9684 3.22251 12.0056C4.45713 13.0298 6.1116 14 8.00001 14C9.88842 14 11.5429 13.0298 12.7775 12.0056C14.0277 10.9684 14.9717 9.77254 15.4575 9.1004C15.9348 8.44008 15.9348 7.55992 15.4575 6.8996C14.9717 6.22746 14.0277 5.03163 12.7775 3.99443C11.5429 2.97015 9.88842 2 8.00001 2ZM8.00001 6C6.89544 6 6.00001 6.89543 6.00001 8C6.00001 9.10457 6.89544 10 8.00001 10C9.10458 10 10 9.10457 10 8C10 6.89543 9.10458 6 8.00001 6Z" fill="#A3A9AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5005 10.4663C3.47291 9.6138 2.665 8.61284 2.21621 8C2.665 7.38716 3.47291 6.3862 4.5005 5.53367C5.58449 4.63435 6.79226 4 8.00099 4C9.20972 4 10.4175 4.63435 11.5015 5.53367C12.5291 6.3862 13.337 7.38716 13.7858 8C13.337 8.61284 12.5291 9.6138 11.5015 10.4663C10.4175 11.3656 9.20972 12 8.00099 12C6.79226 12 5.58449 11.3656 4.5005 10.4663ZM8.00099 2C6.11258 2 4.4581 2.97015 3.22349 3.99443C1.97331 5.03163 1.0293 6.22746 0.54349 6.8996C0.0662313 7.55992 0.0662334 8.44008 0.543491 9.1004C1.0293 9.77254 1.97331 10.9684 3.22349 12.0056C4.4581 13.0298 6.11258 14 8.00099 14C9.8894 14 11.5439 13.0298 12.7785 12.0056C14.0287 10.9684 14.9727 9.77254 15.4585 9.1004C15.9357 8.44008 15.9357 7.55992 15.4585 6.8996C14.9727 6.22746 14.0287 5.03163 12.7785 3.99443C11.5439 2.97015 9.8894 2 8.00099 2ZM8.00099 6C6.89642 6 6.00099 6.89543 6.00099 8C6.00099 9.10457 6.89642 10 8.00099 10C9.10556 10 10.001 9.10457 10.001 8C10.001 6.89543 9.10556 6 8.00099 6Z" fill="#333333"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 15C0 15.5523 0.447715 16 1 16H15C15.5523 16 16 15.5523 16 15V14.7143C16 14.162 15.5523 13.7143 15 13.7143H1C0.447715 13.7143 0 14.162 0 14.7143V15ZM0 8.14286C0 8.69514 0.447715 9.14286 1 9.14286H15C15.5523 9.14286 16 8.69514 16 8.14286V7.85714C16 7.30486 15.5523 6.85714 15 6.85714H1C0.447715 6.85714 0 7.30486 0 7.85714V8.14286ZM1 2.28571C0.447715 2.28571 0 1.838 0 1.28571V1C0 0.447716 0.447715 0 1 0H15C15.5523 0 16 0.447716 16 1V1.28571C16 1.838 15.5523 2.28571 15 2.28571H1Z" fill="#A3A9AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2.5C2 2.22386 1.77614 2 1.5 2H0.5C0.223858 2 0 2.22386 0 2.5V3.5C0 3.77614 0.223858 4 0.5 4H1.5C1.77614 4 2 3.77614 2 3.5V2.5ZM16 2.5C16 2.22386 15.7761 2 15.5 2H4.5C4.22386 2 4 2.22386 4 2.5V3.5C4 3.77614 4.22386 4 4.5 4L15.5 4C15.7761 4 16 3.77614 16 3.5V2.5ZM2 7.5C2 7.22386 1.77614 7 1.5 7H0.5C0.223858 7 0 7.22386 0 7.5V8.5C0 8.77614 0.223858 9 0.5 9H1.5C1.77614 9 2 8.77614 2 8.5V7.5ZM16 7.5C16 7.22386 15.7761 7 15.5 7H4.5C4.22386 7 4 7.22386 4 7.5V8.5C4 8.77614 4.22386 9 4.5 9H15.5C15.7761 9 16 8.77614 16 8.5V7.5ZM0 12.5C0 12.2239 0.223858 12 0.5 12H1.5C1.77614 12 2 12.2239 2 12.5V13.5C2 13.7761 1.77614 14 1.5 14H0.5C0.223858 14 0 13.7761 0 13.5V12.5ZM4 12.5C4 12.2239 4.22386 12 4.5 12L15.5 12C15.7761 12 16 12.2239 16 12.5V13.5C16 13.7761 15.7761 14 15.5 14L4.5 14C4.22386 14 4 13.7761 4 13.5V12.5Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 653 B

After

Width:  |  Height:  |  Size: 984 B

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 0C0.447715 0 0 0.447715 0 0.999999V5.85714C0 6.40943 0.447715 6.85714 1 6.85714H5.85714C6.40943 6.85714 6.85714 6.40943 6.85714 5.85714V1C6.85714 0.447715 6.40943 0 5.85714 0H1ZM1 9.14286C0.447715 9.14286 0 9.59057 0 10.1429V15C0 15.5523 0.447715 16 1 16H5.85714C6.40943 16 6.85714 15.5523 6.85714 15V10.1429C6.85714 9.59057 6.40943 9.14286 5.85714 9.14286H1ZM9.14286 0.999999C9.14286 0.447715 9.59057 0 10.1429 0H15C15.5523 0 16 0.447715 16 1V5.85714C16 6.40943 15.5523 6.85714 15 6.85714H10.1429C9.59057 6.85714 9.14286 6.40943 9.14286 5.85714V0.999999ZM10.1429 9.14286C9.59057 9.14286 9.14286 9.59057 9.14286 10.1429V15C9.14286 15.5523 9.59057 16 10.1429 16H15C15.5523 16 16 15.5523 16 15V10.1429C16 9.59057 15.5523 9.14286 15 9.14286H10.1429Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 1C1.44772 1 1 1.44772 1 2V6C1 6.55228 1.44772 7 2 7H6C6.55228 7 7 6.55228 7 6V2C7 1.44772 6.55228 1 6 1H2ZM3.5 5C3.22386 5 3 4.77614 3 4.5V3.5C3 3.22386 3.22386 3 3.5 3H4.5C4.77614 3 5 3.22386 5 3.5V4.5C5 4.77614 4.77614 5 4.5 5H3.5ZM2 9C1.44772 9 1 9.44771 1 10V14C1 14.5523 1.44772 15 2 15H6C6.55228 15 7 14.5523 7 14V10C7 9.44771 6.55228 9 6 9H2ZM3.5 13C3.22386 13 3 12.7761 3 12.5V11.5C3 11.2239 3.22386 11 3.5 11H4.5C4.77614 11 5 11.2239 5 11.5V12.5C5 12.7761 4.77614 13 4.5 13H3.5ZM9 2C9 1.44772 9.44771 1 10 1H14C14.5523 1 15 1.44772 15 2V6C15 6.55228 14.5523 7 14 7H10C9.44771 7 9 6.55228 9 6V2ZM11.5 3C11.2239 3 11 3.22386 11 3.5V4.5C11 4.77614 11.2239 5 11.5 5H12.5C12.7761 5 13 4.77614 13 4.5V3.5C13 3.22386 12.7761 3 12.5 3H11.5ZM10 9C9.44771 9 9 9.44771 9 10V14C9 14.5523 9.44771 15 10 15H14C14.5523 15 15 14.5523 15 14V10C15 9.44771 14.5523 9 14 9H10ZM11.5 13C11.2239 13 11 12.7761 11 12.5V11.5C11 11.2239 11.2239 11 11.5 11H12.5C12.7761 11 13 11.2239 13 11.5V12.5C13 12.7761 12.7761 13 12.5 13H11.5Z" fill="#A3A9AE"/>
</svg>

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,101 @@
import React from "react";
import { observer, inject } from "mobx-react";
import { isMobileOnly } from "react-device-detect";
import { isSmallTablet } from "@appserver/components/utils/device";
const withLoading = (WrappedComponent) => {
const withLoading = (props) => {
const {
isLoadedArticleBody,
isLoadedArticleHeader,
isLoadedSectionHeader,
isLoadedSubmenu,
isLoadedLngTZSettings,
isLoadedPortalRenaming,
isLoadedCustomization,
isLoadedCustomizationNavbar,
isLoadedWelcomePageSettings,
} = props;
const pathname = location.pathname;
const index = pathname.lastIndexOf("/");
const setting = pathname.slice(index + 1);
const viewMobile = isSmallTablet() || isMobileOnly;
const isLoadedCustomizationSettings =
isLoadedCustomization &&
isLoadedLngTZSettings &&
isLoadedWelcomePageSettings &&
isLoadedPortalRenaming &&
isLoadedArticleBody &&
isLoadedArticleHeader &&
isLoadedSectionHeader &&
isLoadedSubmenu;
const isLoadedCustomizationNavbarSettings =
isLoadedCustomizationNavbar &&
isLoadedArticleBody &&
isLoadedArticleHeader &&
isLoadedSectionHeader &&
isLoadedSubmenu;
const isLoadedCustomizationSettingLngTZSettings =
isLoadedArticleBody &&
isLoadedArticleHeader &&
isLoadedSectionHeader &&
isLoadedLngTZSettings;
const isLoadedCustomizationSettingWelcomePageSettings =
isLoadedArticleBody &&
isLoadedArticleHeader &&
isLoadedSectionHeader &&
isLoadedWelcomePageSettings;
const isLoadedCustomizationSettingPortalRenaming =
isLoadedArticleBody &&
isLoadedArticleHeader &&
isLoadedSectionHeader &&
isLoadedPortalRenaming;
const isLoadedPage =
setting === "language-and-time-zone"
? isLoadedCustomizationSettingLngTZSettings
: setting === "welcome-page-settings"
? isLoadedCustomizationSettingWelcomePageSettings
: setting === "portal-renaming"
? isLoadedCustomizationSettingPortalRenaming
: viewMobile
? isLoadedCustomizationNavbarSettings
: isLoadedCustomizationSettings;
return <WrappedComponent {...props} isLoadedPage={isLoadedPage} />;
};
return inject(({ common }) => {
const {
isLoadedArticleBody,
isLoadedArticleHeader,
isLoadedSectionHeader,
isLoadedSubmenu,
isLoadedLngTZSettings,
isLoadedPortalRenaming,
isLoadedCustomization,
isLoadedCustomizationNavbar,
isLoadedWelcomePageSettings,
} = common;
return {
isLoadedArticleBody,
isLoadedArticleHeader,
isLoadedSectionHeader,
isLoadedSubmenu,
isLoadedLngTZSettings,
isLoadedPortalRenaming,
isLoadedCustomization,
isLoadedCustomizationNavbar,
isLoadedWelcomePageSettings,
};
})(observer(withLoading));
};
export default withLoading;

View File

@ -535,11 +535,13 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
component={ComingSoonRoute}
/>
<PrivateRoute path={PAYMENTS_URL} component={PaymentsRoute} />
<PrivateRoute
restricted
path={SETTINGS_URL}
component={SettingsRoute}
/>
{!personal && (
<PrivateRoute
restricted
path={SETTINGS_URL}
component={SettingsRoute}
/>
)}
<PrivateRoute
exact
allowForMe

View File

@ -121,14 +121,15 @@ const HeaderNav = ({
settingsModule && useCallback(() => history.push(settingsUrl), []);
const getCurrentUserActions = useCallback(() => {
const settings = settingsModule
? {
key: "SettingsBtn",
label: t("Common:Settings"),
onClick: onSettingsClick,
url: settingsUrl,
}
: null;
const settings =
settingsModule && !isPersonal
? {
key: "SettingsBtn",
label: t("Common:Settings"),
onClick: onSettingsClick,
url: settingsUrl,
}
: null;
const actions = [
{

View File

@ -243,32 +243,35 @@ const HeaderComponent = ({
/>
)}
</LinkWithoutRedirect>
{isNavAvailable && isDesktopView && !isPersonal && (
<StyledNavigationIconsWrapper>
{mainModules.map((item) => {
return (
<React.Fragment key={item.id}>
{item.iconUrl &&
!item.separator &&
item.id !== "settings" && (
<HeaderNavigationIcon
key={item.id}
id={item.id}
data-id={item.id}
data-link={item.link}
active={item.id == currentProductId}
iconUrl={item.iconUrl}
badgeNumber={item.notifications}
onItemClick={onItemClick}
onBadgeClick={onBadgeClick}
url={item.link}
/>
)}
</React.Fragment>
);
})}
</StyledNavigationIconsWrapper>
)}
{isNavAvailable &&
isDesktopView &&
!isPersonal &&
currentProductId !== "home" && (
<StyledNavigationIconsWrapper>
{mainModules.map((item) => {
return (
<React.Fragment key={item.id}>
{item.iconUrl &&
!item.separator &&
item.id !== "settings" && (
<HeaderNavigationIcon
key={item.id}
id={item.id}
data-id={item.id}
data-link={item.link}
active={item.id == currentProductId}
iconUrl={item.iconUrl}
badgeNumber={item.notifications}
onItemClick={onItemClick}
onBadgeClick={onBadgeClick}
url={item.link}
/>
)}
</React.Fragment>
);
})}
</StyledNavigationIconsWrapper>
)}
</Header>
{isNavAvailable && !isDesktopView && (

View File

@ -96,8 +96,6 @@ const commonStyle = css`
font-family: "Open Sans", sans-serif, Arial;
font-style: normal;
color: ${(props) => props.theme.menuContainer.color};
margin-left: 60px;
margin-top: -3px;
max-width: 300px;
white-space: nowrap;
overflow: hidden;
@ -114,6 +112,10 @@ export const StyledProfileMenu = styled(DropDownItem)`
`;
export const MenuContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
position: relative;
height: 76px;
background: ${(props) => props.theme.menuContainer.background};
@ -121,15 +123,15 @@ export const MenuContainer = styled.div`
padding: 16px;
cursor: default;
box-sizing: border-box;
.avatar {
height: 40px;
width: 40px;
}
`;
MenuContainer.defaultProps = { theme: Base };
export const AvatarContainer = styled.div`
display: inline-block;
float: left;
`;
export const MainLabelContainer = styled.div`
font-size: 16px;
line-height: 28px;
@ -178,19 +180,20 @@ class ProfileMenu extends React.Component {
>
<StyledProfileMenu>
<MenuContainer>
<AvatarContainer>
<Avatar
size="medium"
role={avatarRole}
source={avatarSource}
userName={displayName}
/>
</AvatarContainer>
<MainLabelContainer>{displayName}</MainLabelContainer>
<LabelContainer>{email}</LabelContainer>
<StyledControlContainer onClick={clickOutsideAction}>
<StyledCrossIcon />
</StyledControlContainer>
<Avatar
className="avatar"
size="medium"
role={avatarRole}
source={avatarSource}
userName={displayName}
/>
<div>
<MainLabelContainer>{displayName}</MainLabelContainer>
<LabelContainer>{email}</LabelContainer>
<StyledControlContainer onClick={clickOutsideAction}>
<StyledCrossIcon />
</StyledControlContainer>
</div>
</MenuContainer>
</StyledProfileMenu>
{children}

View File

@ -28,26 +28,13 @@ const HomeContainer = styled.div`
align-items: center;
.home-modules {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 26px ${isMobileOnly ? "31px" : "45px"};
${(isTablet || isMobile) &&
css`
display: flex;
justify-content: center;
flex-wrap: wrap;
`}
display: flex;
justify-content: center;
gap: ${isMobileOnly ? "31px" : "45px"};
.home-module {
z-index: 42;
}
@media (max-width: 400px) {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
}
.home-error-text {

View File

@ -9,6 +9,7 @@ import { isArrayEqual } from "@appserver/components/utils/array";
import { isMobileOnly } from "react-device-detect";
import { isMobile } from "@appserver/components/utils/device";
import withLoading from "../../../../../../HOCs/withLoading";
import {
//getKeyByLink,
@ -19,6 +20,7 @@ import {
} from "../../../utils";
import CatalogItem from "@appserver/components/catalog-item";
import LoaderArticleBody from "./loaderArticleBody";
class ArticleBodyContent extends React.Component {
constructor(props) {
@ -57,6 +59,12 @@ class ArticleBodyContent extends React.Component {
}
componentDidUpdate(prevProps, prevState) {
const { isLoaded, tReady, setIsLoadedArticleBody } = this.props;
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) setIsLoadedArticleBody(isLoadedSetting);
if (!isArrayEqual(prevState.selectedKeys, this.state.selectedKeys)) {
const { selectedKeys } = this.state;
@ -145,14 +153,26 @@ class ArticleBodyContent extends React.Component {
render() {
const items = this.catalogItems();
const { isLoadedPage } = this.props;
const commonSettings = location.pathname.includes("common");
return <>{items}</>;
const showLoader = commonSettings ? !isLoadedPage : false;
return showLoader ? <LoaderArticleBody /> : <>{items}</>;
}
}
export default inject(({ auth }) => {
export default inject(({ auth, common }) => {
const { isLoaded, setIsLoadedArticleBody } = common;
return {
showText: auth.settingsStore.showText,
toggleArticleOpen: auth.settingsStore.toggleArticleOpen,
isLoaded,
setIsLoadedArticleBody,
};
})(withRouter(withTranslation("Settings")(observer(ArticleBodyContent))));
})(
withLoading(
withRouter(withTranslation("Settings")(observer(ArticleBodyContent)))
)
);

View File

@ -15,7 +15,6 @@ const StyledLoader = styled.div`
@media (min-width: 1024px) {
padding: 0px 20px 0px;
margin-top: -7px;
.loader {
width: 216px;

View File

@ -7,8 +7,9 @@ import Headline from "@appserver/common/components/Headline";
import IconButton from "@appserver/components/icon-button";
import GroupButtonsMenu from "@appserver/components/group-buttons-menu";
import DropDownItem from "@appserver/components/drop-down-item";
import LoaderSectionHeader from "../loaderSectionHeader";
import { tablet, desktop } from "@appserver/components/utils/device";
import withLoading from "../../../../../../HOCs/withLoading";
import {
getKeyByLink,
@ -132,6 +133,12 @@ class SectionHeaderContent extends React.Component {
}
componentDidUpdate() {
const { isLoaded, tReady, setIsLoadedSectionHeader } = this.props;
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) setIsLoadedSectionHeader(isLoadedSetting);
const arrayOfParams = this.getArrayOfParams();
const key = getKeyByLink(arrayOfParams, settingsTree);
@ -214,6 +221,7 @@ class SectionHeaderContent extends React.Component {
isHeaderChecked,
isHeaderVisible,
selection,
isLoadedPage,
} = this.props;
const { header, isCategoryOrHeader } = this.state;
const arrayOfParams = this.getArrayOfParams();
@ -241,6 +249,8 @@ class SectionHeaderContent extends React.Component {
},
];
const commonSettings = location.pathname.includes("common");
const showLoader = commonSettings ? !isLoadedPage : false;
return (
<StyledContainer isHeaderVisible={isHeaderVisible}>
{isHeaderVisible ? (
@ -257,6 +267,8 @@ class SectionHeaderContent extends React.Component {
selected={menuItems[0].label}
/>
</div>
) : showLoader ? (
<LoaderSectionHeader />
) : (
<HeaderContainer>
{!isCategoryOrHeader && arrayOfParams[0] && (
@ -289,7 +301,7 @@ class SectionHeaderContent extends React.Component {
}
}
export default inject(({ auth, setup }) => {
export default inject(({ auth, setup, common }) => {
const { customNames } = auth.settingsStore;
const { addUsers, removeAdmins } = setup.headerAction;
const { toggleSelector } = setup;
@ -304,7 +316,7 @@ export default inject(({ auth, setup }) => {
selection,
} = setup.selectionStore;
const { admins, selectorIsOpen } = setup.security.accessRight;
const { isLoaded, setIsLoadedSectionHeader } = common;
return {
addUsers,
removeAdmins,
@ -320,9 +332,13 @@ export default inject(({ auth, setup }) => {
toggleSelector,
selectorIsOpen,
selection,
isLoaded,
setIsLoadedSectionHeader,
};
})(
withRouter(
withTranslation(["Settings", "Common"])(observer(SectionHeaderContent))
withLoading(
withRouter(
withTranslation(["Settings", "Common"])(observer(SectionHeaderContent))
)
)
);

View File

@ -4,11 +4,11 @@ import { ArticleHeaderContent, ArticleBodyContent } from "./Article";
import { SectionHeaderContent, SectionPagingContent } from "./Section";
import { inject, observer } from "mobx-react";
import Section from "@appserver/common/components/Section";
import LoaderSectionHeader from "./Section/loaderSectionHeader";
const ArticleSettings = React.memo(() => {
import withLoading from "../../../../HOCs/withLoading";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
const ArticleSettings = React.memo(({ isLoadedPage }) => {
return (
<Article>
<Article isLoadedPage={isLoadedPage}>
<Article.Header>
<ArticleHeaderContent />
</Article.Header>
@ -26,6 +26,7 @@ const Layout = ({
language,
children,
addUsers,
isLoadedPage,
}) => {
useEffect(() => {
currentProductId !== "settings" && setCurrentProductId("settings");
@ -33,7 +34,7 @@ const Layout = ({
return (
<>
<ArticleSettings />
<ArticleSettings isLoadedPage={isLoadedPage} />
<Section withBodyScroll={true}>
<Section.SectionHeader>
<SectionHeaderContent />
@ -53,9 +54,10 @@ const Layout = ({
export default inject(({ auth, setup }) => {
const { language, settingsStore } = auth;
const { addUsers } = setup.headerAction;
return {
language,
setCurrentProductId: settingsStore.setCurrentProductId,
addUsers,
};
})(observer(Layout));
})(withLoading(observer(Layout)));

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import Text from "@appserver/components/text";
@ -11,9 +11,8 @@ import withCultureNames from "@appserver/common/hoc/withCultureNames";
import history from "@appserver/common/history";
import { Base } from "@appserver/components/themes";
import LoaderCustomizationNavbar from "./sub-components/loaderCustomizationNavbar";
import { StyledArrowRightIcon } from "../common/settingsCustomization/StyledSettings";
import { withRouter } from "react-router";
const StyledComponent = styled.div`
padding-top: 13px;
@ -47,15 +46,28 @@ const StyledComponent = styled.div`
StyledComponent.defaultProps = { theme: Base };
const CustomizationNavbar = ({ t, theme, helpUrlCommonSettings }) => {
const CustomizationNavbar = ({
t,
theme,
helpUrlCommonSettings,
isLoaded,
tReady,
setIsLoadedCustomizationNavbar,
isLoadedPage,
}) => {
const isLoadedSetting = isLoaded && tReady;
useEffect(() => {
if (isLoadedSetting) setIsLoadedCustomizationNavbar(isLoadedSetting);
}, [isLoadedSetting]);
const onClickLink = (e) => {
e.preventDefault();
history.push(e.target.pathname);
};
//return <LoaderCustomizationNavbar />;
return (
return !isLoadedPage ? (
<LoaderCustomizationNavbar />
) : (
<StyledComponent>
<div className="category-item-wrapper">
<div className="category-item-heading">
@ -128,14 +140,19 @@ const CustomizationNavbar = ({ t, theme, helpUrlCommonSettings }) => {
);
};
export default inject(({ auth }) => {
export default inject(({ auth, common }) => {
const { helpUrlCommonSettings, theme } = auth.settingsStore;
const { isLoaded, setIsLoadedCustomizationNavbar } = common;
return {
theme,
helpUrlCommonSettings,
isLoaded,
setIsLoadedCustomizationNavbar,
};
})(
withCultureNames(
observer(withTranslation(["Settings", "Common"])(CustomizationNavbar))
withRouter(
withCultureNames(
observer(withTranslation(["Settings", "Common"])(CustomizationNavbar))
)
)
);

View File

@ -11,6 +11,8 @@ import CustomizationNavbar from "./customization-navbar";
import { Base } from "@appserver/components/themes";
import { setDocumentTitle } from "../../../../../helpers/utils";
import LoaderDescriptionCustomization from "./sub-components/loaderDescriptionCustomization";
import { withRouter } from "react-router";
import withLoading from "../../../../../HOCs/withLoading";
const StyledComponent = styled.div`
width: 100%;
@ -65,9 +67,24 @@ const StyledComponent = styled.div`
StyledComponent.defaultProps = { theme: Base };
const Customization = ({ t, setIsLoadingArticleSettings }) => {
const Customization = (props) => {
const { t, isLoaded, tReady, setIsLoadedCustomization, isLoadedPage } = props;
const [mobileView, setMobileView] = useState(true);
const [isLoadingCustomization, setIsLoadingCustomization] = useState(false);
const isLoadedSetting = isLoaded && tReady;
useEffect(() => {
setDocumentTitle(t("Customization"));
window.addEventListener("resize", checkInnerWidth);
return () => window.removeEventListener("resize", checkInnerWidth);
}, []);
useEffect(() => {
if (isLoadedSetting) {
setIsLoadedCustomization(isLoadedSetting);
}
}, [isLoadedSetting]);
const checkInnerWidth = () => {
if (isSmallTablet()) {
@ -77,47 +94,35 @@ const Customization = ({ t, setIsLoadingArticleSettings }) => {
}
};
useEffect(() => {
setDocumentTitle(t("Customization"));
//TODO: Add method to get the portal name
setIsLoadingArticleSettings(true);
setTimeout(() => {
setIsLoadingCustomization(false);
setIsLoadingArticleSettings(isLoadingCustomization);
}, 3000);
window.addEventListener("resize", checkInnerWidth);
return () => window.removeEventListener("resize", checkInnerWidth);
}, []);
const isMobile = !!(isSmallTablet() && mobileView);
return isMobile ? (
<CustomizationNavbar />
<CustomizationNavbar isLoadedPage={isLoadedPage} />
) : (
<StyledComponent>
<div className="category-description">{`${t(
"Settings:CustomizationDescription"
)}`}</div>
{/* <LoaderDescriptionCustomization /> */}
<LanguageAndTimeZone
isLoadingCustomization={isLoadingCustomization}
isMobileView={isMobile}
/>
{!isLoadedPage ? (
<LoaderDescriptionCustomization />
) : (
<div className="category-description">{`${t(
"Settings:CustomizationDescription"
)}`}</div>
)}
<LanguageAndTimeZone isMobileView={isMobile} />
<WelcomePageSettings isMobileView={isMobile} />
<PortalRenaming isMobileView={isMobile} />
</StyledComponent>
);
};
export default inject(({ setup }) => {
const { setIsLoadingArticleSettings } = setup;
export default inject(({ common }) => {
const { isLoaded, setIsLoadedCustomization } = common;
return {
setIsLoadingArticleSettings,
isLoaded,
setIsLoadedCustomization,
};
})(
withCultureNames(
withTranslation(["Settings", "Common"])(observer(Customization))
withLoading(
withRouter(withTranslation(["Settings", "Common"])(observer(Customization)))
)
);

View File

@ -5,15 +5,35 @@ import { withTranslation } from "react-i18next";
import { AppServerConfig } from "@appserver/common/constants";
import { combineUrl } from "@appserver/common/utils";
import config from "../../../../../../package.json";
import { inject, observer } from "mobx-react";
import Customization from "./customization";
import WhiteLabel from "./whitelabel";
import LoaderSubmenu from "./sub-components/loaderSubmenu";
import withLoading from "../../../../../HOCs/withLoading";
const SubmenuCommon = (props) => {
const { t, history } = props;
const {
t,
history,
isLoaded,
tReady,
setIsLoadedSubmenu,
isLoadedPage,
} = props;
const [currentTab, setCurrentTab] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const isLoadedSetting = isLoaded && tReady;
useEffect(() => {
const path = location.pathname;
const currentTab = data.findIndex((item) => path.includes(item.id));
if (currentTab !== -1) {
setCurrentTab(currentTab);
}
}, []);
useEffect(() => {
if (isLoadedSetting) setIsLoadedSubmenu(isLoadedSetting);
}, [isLoadedSetting]);
const data = [
{
@ -28,15 +48,6 @@ const SubmenuCommon = (props) => {
},
];
useEffect(() => {
const path = location.pathname;
const currentTab = data.findIndex((item) => path.includes(item.id));
if (currentTab !== -1) {
setCurrentTab(currentTab);
}
//setIsLoading(true);
}, []);
const onSelect = (e) => {
history.push(
combineUrl(
@ -46,16 +57,23 @@ const SubmenuCommon = (props) => {
)
);
};
//TODO: isLoading
return isLoading ? (
<LoaderSubmenu />
) : (
return (
<Submenu
data={data}
startSelect={currentTab}
onSelect={(e) => onSelect(e)}
isLoading={!isLoadedPage}
/>
);
};
export default withTranslation("Settings")(withRouter(SubmenuCommon));
export default inject(({ common }) => {
const { isLoaded, setIsLoadedSubmenu } = common;
return {
isLoaded,
setIsLoadedSubmenu,
};
})(
withLoading(withRouter(withTranslation("Settings")(observer(SubmenuCommon))))
);

View File

@ -37,23 +37,11 @@ const StyledSettingsComponent = styled.div`
font-weight: 400;
}
.field-container-flex {
display: flex;
justify-content: space-between;
margin-top: 8px;
margin-bottom: 12px;
}
.toggle {
position: inherit;
grid-gap: inherit;
}
.field-title {
font-weight: 600;
line-height: 20px;
}
.errorText {
position: absolute;
font-size: 10px;

View File

@ -1,9 +1,7 @@
import React from "react";
import { withTranslation } from "react-i18next";
import FieldContainer from "@appserver/components/field-container";
import ToggleButton from "@appserver/components/toggle-button";
import ComboBox from "@appserver/components/combobox";
import Loader from "@appserver/components/loader";
import toastr from "@appserver/components/toast/toastr";
import HelpButton from "@appserver/components/help-button";
import SaveCancelButtons from "@appserver/components/save-cancel-buttons";
@ -11,8 +9,6 @@ import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import { setDocumentTitle } from "../../../../../../helpers/utils";
import { inject, observer } from "mobx-react";
import { LANGUAGE } from "@appserver/common/constants";
import { convertLanguage } from "@appserver/common/utils";
import withCultureNames from "@appserver/common/hoc/withCultureNames";
import { LanguageTimeSettingsTooltip } from "../sub-components/common-tooltips";
import { combineUrl } from "@appserver/common/utils";
import { AppServerConfig } from "@appserver/common/constants";
@ -23,12 +19,21 @@ import { isSmallTablet } from "@appserver/components/utils/device";
import checkScrollSettingsBlock from "../utils";
import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import LoaderCustomization from "../sub-components/loaderCustomization";
import withLoading from "../../../../../../HOCs/withLoading";
const mapTimezonesToArray = (timezones) => {
return timezones.map((timezone) => {
return { key: timezone.id, label: timezone.displayName };
});
};
const mapCulturesToArray = (cultures, i18n) => {
const t = i18n.getFixedT(null, "Common");
return cultures.map((culture) => {
return { key: culture, label: t(`Culture_${culture}`) };
});
};
const findSelectedItemByKey = (items, selectedItemKey) => {
return items.find((item) => item.key === selectedItemKey);
};
@ -44,25 +49,7 @@ class LanguageAndTimeZone extends React.Component {
constructor(props) {
super(props);
const {
portalLanguage,
portalTimeZoneId,
rawTimezones,
cultureNames,
/*organizationName,*/
t,
//i18n,
} = props;
const timezones = mapTimezonesToArray(rawTimezones);
const language = findSelectedItemByKey(
cultureNames,
convertLanguage(portalLanguage || cultureNames[0])
);
const timezone = findSelectedItemByKey(
timezones,
portalTimeZoneId || timezones[0]
);
const { t } = props;
languageFromSessionStorage = getFromSessionStorage("language");
languageDefaultFromSessionStorage = getFromSessionStorage(
@ -76,13 +63,11 @@ class LanguageAndTimeZone extends React.Component {
setDocumentTitle(t("StudioTimeLanguageSettings"));
this.state = {
isLoadedData: false,
isLoading: false,
timezones,
timezone: timezoneFromSessionStorage || timezone,
timezoneDefault: timezoneDefaultFromSessionStorage || timezone,
language: languageFromSessionStorage || language,
languageDefault: languageDefaultFromSessionStorage || language,
timezone: timezoneFromSessionStorage || "",
timezoneDefault: timezoneDefaultFromSessionStorage || "",
language: languageFromSessionStorage || "",
languageDefault: languageDefaultFromSessionStorage || "",
hasChanged: false,
showReminder: false,
hasScroll: false,
@ -90,67 +75,85 @@ class LanguageAndTimeZone extends React.Component {
}
componentDidMount() {
const { languageDefault, timezoneDefault } = this.state;
const {
cultureNames,
portalLanguage,
i18n,
language,
rawTimezones,
portalTimeZoneId,
getPortalTimezones,
isLoaded,
cultures,
portalLanguage,
tReady,
setIsLoadedLngTZSettings,
} = this.props;
const { timezones, isLoadedData } = this.state;
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) {
setIsLoadedLngTZSettings(isLoadedSetting);
}
window.addEventListener("resize", this.checkInnerWidth);
if (!timezones.length) {
getPortalTimezones().then(() => {
const timezones = mapTimezonesToArray(this.props.rawTimezones);
if (
rawTimezones.length > 0 &&
isLoaded === true &&
tReady === true &&
this.state.timezone === ""
) {
const timezones = mapTimezonesToArray(rawTimezones);
const language =
languageFromSessionStorage ||
findSelectedItemByKey(cultureNames, portalLanguage) ||
cultureNames[0];
const timezone =
timezoneFromSessionStorage ||
findSelectedItemByKey(timezones, portalTimeZoneId) ||
timezones[0];
const timezone =
timezoneFromSessionStorage ||
findSelectedItemByKey(timezones, portalTimeZoneId) ||
rawTimezones[0];
const languageDefault =
findSelectedItemByKey(cultureNames, portalLanguage) ||
cultureNames[0];
const timezoneDefault =
findSelectedItemByKey(timezones, portalTimeZoneId) || timezones[0];
const timezoneDefault =
findSelectedItemByKey(timezones, portalTimeZoneId) || timezones[0];
this.setState({
language,
timezones,
timezone,
languageDefault,
timezoneDefault,
});
if (!timezoneDefault) {
this.setState({
timezoneDefault: timezone,
});
}
if (!languageDefault) {
this.setState({
languageDefault: language,
});
}
this.setState({
timezone,
timezoneDefault,
});
}
if (timezones.length && !isLoadedData) {
this.setState({ isLoadedData: true });
if (
cultures.length > 0 &&
isLoaded === true &&
tReady === true &&
this.state.language === ""
) {
const cultureNames = mapCulturesToArray(cultures, i18n);
const language =
languageFromSessionStorage ||
findSelectedItemByKey(cultureNames, portalLanguage) ||
cultureNames[0];
const languageDefault =
findSelectedItemByKey(cultureNames, portalLanguage) || cultureNames[0];
this.setState({
language,
languageDefault,
});
}
if (!languageDefault) {
this.setState({
languageDefault: language,
});
}
if (timezoneDefault && languageDefault) {
this.checkChanges();
}
}
componentDidUpdate(prevProps, prevState) {
const {
timezones,
timezoneDefault,
languageDefault,
hasScroll,
} = this.state;
const { timezoneDefault, languageDefault, hasScroll } = this.state;
const {
i18n,
@ -158,8 +161,66 @@ class LanguageAndTimeZone extends React.Component {
nameSchemaId,
getCurrentCustomSchema,
cultureNames,
rawTimezones,
portalTimeZoneId,
isLoaded,
cultures,
portalLanguage,
tReady,
setIsLoadedLngTZSettings,
} = this.props;
if (isLoaded !== prevProps.isLoaded || tReady !== prevProps.tReady) {
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) {
setIsLoadedLngTZSettings(isLoadedSetting);
}
}
if (
rawTimezones.length > 0 &&
isLoaded === true &&
tReady === true &&
this.state.timezone === ""
) {
const timezones = mapTimezonesToArray(rawTimezones);
const timezone =
timezoneFromSessionStorage ||
findSelectedItemByKey(timezones, portalTimeZoneId) ||
rawTimezones[0];
const timezoneDefault =
findSelectedItemByKey(timezones, portalTimeZoneId) || timezones[0];
this.setState({
timezone,
timezoneDefault,
});
}
if (
cultures.length > 0 &&
isLoaded === true &&
tReady === true &&
this.state.language === ""
) {
const cultureNames = mapCulturesToArray(cultures, i18n);
const language =
languageFromSessionStorage ||
findSelectedItemByKey(cultureNames, portalLanguage) ||
cultureNames[0];
const languageDefault =
findSelectedItemByKey(cultureNames, portalLanguage) || cultureNames[0];
this.setState({
language,
languageDefault,
});
}
const checkScroll = checkScrollSettingsBlock();
window.addEventListener("resize", checkScroll);
@ -180,9 +241,6 @@ class LanguageAndTimeZone extends React.Component {
settingsMobile.style.display = "none";
}
if (timezones.length && !prevState.isLoadedData) {
this.setState({ isLoadedData: true });
}
if (language !== prevProps.language) {
i18n
.changeLanguage(language)
@ -332,21 +390,24 @@ class LanguageAndTimeZone extends React.Component {
const {
t,
theme,
cultureNames,
isMobileView,
isLoadingCustomization,
rawTimezones,
cultures,
i18n,
isLoadedPage,
} = this.props;
const {
isLoadedData,
language,
isLoading,
timezones,
timezone,
showReminder,
hasScroll,
} = this.state;
const timezones = mapTimezonesToArray(rawTimezones);
const cultureNames = mapCulturesToArray(cultures, i18n);
const tooltipLanguageTimeSettings = (
<LanguageTimeSettingsTooltip theme={theme} t={t} />
);
@ -371,13 +432,6 @@ class LanguageAndTimeZone extends React.Component {
className="dropdown-item-width"
/>
</FieldContainer>
<div className="field-container-flex">
<div className="field-title">{`${t("Automatic time zone")}`}</div>
<ToggleButton
className="toggle"
onChange={() => toastr.info(<>Not implemented</>)}
/>
</div>
<FieldContainer
id="fieldContainerTimezone"
labelText={`${t("TimeZone")}:`}
@ -399,9 +453,8 @@ class LanguageAndTimeZone extends React.Component {
</div>
);
//return <LoaderCustomization lngTZSettings={true} />;
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
return !isLoadedPage ? (
<LoaderCustomization lngTZSettings={true} />
) : (
<StyledSettingsComponent
hasScroll={hasScroll}
@ -440,24 +493,23 @@ class LanguageAndTimeZone extends React.Component {
}
}
export default inject(({ auth, setup }) => {
export default inject(({ auth, setup, common }) => {
const {
culture,
timezone,
timezones,
//cultures,
nameSchemaId,
organizationName,
greetingSettings,
//getPortalCultures,
getPortalTimezones,
getCurrentCustomSchema,
cultures,
} = auth.settingsStore;
const { user } = auth.userStore;
const { setLanguageAndTime } = setup;
const { isLoaded, setIsLoadedLngTZSettings } = common;
return {
theme: auth.settingsStore.theme,
user,
@ -465,17 +517,18 @@ export default inject(({ auth, setup }) => {
portalTimeZoneId: timezone,
language: culture,
rawTimezones: timezones,
//rawCultures: cultures,
greetingSettings,
nameSchemaId,
organizationName,
//getPortalCultures,
setLanguageAndTime,
getCurrentCustomSchema,
getPortalTimezones,
isLoaded,
setIsLoadedLngTZSettings,
cultures,
};
})(
withCultureNames(
withLoading(
withTranslation(["Settings", "Common"])(observer(LanguageAndTimeZone))
)
);

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import Loader from "@appserver/components/loader";
import toastr from "@appserver/components/toast/toastr";
import HelpButton from "@appserver/components/help-button";
import FieldContainer from "@appserver/components/field-container";
@ -19,10 +18,18 @@ import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import { setDocumentTitle } from "../../../../../../helpers/utils";
import LoaderCustomization from "../sub-components/loaderCustomization";
import withLoading from "../../../../../../HOCs/withLoading";
const PortalRenaming = (props) => {
const {
t,
setPortalRename,
isMobileView,
tReady,
isLoaded,
setIsLoadedPortalRenaming,
isLoadedPage,
} = props;
const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
// TODO: Change false
const [isLoadedData, setIsLoadedData] = useState(true);
const [isLoadingPortalNameSave, setIsLoadingPortalNameSave] = useState(false);
const portalNameFromSessionStorage = getFromSessionStorage("portalName");
@ -46,6 +53,8 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
const lengthNameError =
"The account name must be between 6 and 50 characters long";
const isLoadedSetting = isLoaded && tReady;
useEffect(() => {
setDocumentTitle(t("PortalRenaming"));
@ -77,6 +86,10 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
);
}, []);
useEffect(() => {
if (isLoadedSetting) setIsLoadedPortalRenaming(isLoadedSetting);
}, [isLoadedSetting]);
//TODO: Need a method to get the portal name
const onSavePortalRename = () => {
setIsLoadingPortalNameSave(true);
@ -187,9 +200,8 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
</div>
);
//return <LoaderCustomization portalRenaming={true} />;
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
return !isLoadedPage ? (
<LoaderCustomization portalRenaming={true} />
) : (
<StyledSettingsComponent
hasScroll={hasScroll}
@ -225,12 +237,16 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
);
};
export default inject(({ auth, setup }) => {
export default inject(({ auth, setup, common }) => {
const { theme } = auth.settingsStore;
const { setPortalRename } = setup;
const { isLoaded, setIsLoadedPortalRenaming } = common;
return {
theme,
setPortalRename,
isLoaded,
setIsLoadedPortalRenaming,
};
})(withTranslation(["Settings", "Common"])(observer(PortalRenaming)));
})(
withLoading(withTranslation(["Settings", "Common"])(observer(PortalRenaming)))
);

View File

@ -20,6 +20,7 @@ import { isSmallTablet } from "@appserver/components/utils/device";
import checkScrollSettingsBlock from "../utils";
import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import LoaderCustomization from "../sub-components/loaderCustomization";
import withLoading from "../../../../../../HOCs/withLoading";
let greetingTitleFromSessionStorage = "";
let greetingTitleDefaultFromSessionStorage = "";
@ -45,7 +46,6 @@ class WelcomePageSettings extends React.Component {
setDocumentTitle(t("CustomTitlesWelcome"));
this.state = {
isLoadedData: false,
isLoading: false,
greetingTitle: greetingTitleFromSessionStorage || greetingSettings,
greetingTitleDefault:
@ -60,16 +60,27 @@ class WelcomePageSettings extends React.Component {
}
componentDidMount() {
const { isLoaded, setIsLoadedWelcomePageSettings, tReady } = this.props;
window.addEventListener("resize", this.checkInnerWidth);
showLoader();
this.setState({
isLoadedData: true,
});
hideLoader();
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) setIsLoadedWelcomePageSettings(isLoadedSetting);
}
componentDidUpdate(prevProps, prevState) {
const { isLoaded, setIsLoadedWelcomePageSettings, tReady } = this.props;
const { hasScroll } = this.state;
if (isLoaded !== prevProps.isLoaded || tReady !== prevProps.tReady) {
const isLoadedSetting = isLoaded && tReady;
if (isLoadedSetting) {
setIsLoadedWelcomePageSettings(isLoadedSetting);
}
}
const checkScroll = checkScrollSettingsBlock();
window.addEventListener("resize", checkScroll);
@ -90,12 +101,6 @@ class WelcomePageSettings extends React.Component {
settingsMobile.style.display = "none";
}
if (prevState.isLoadedData !== true) {
this.setState({
isLoadedData: true,
});
}
if (this.state.greetingTitleDefault) {
this.checkChanges();
}
@ -217,9 +222,8 @@ class WelcomePageSettings extends React.Component {
};
render() {
const { t, isMobileView } = this.props;
const { t, isMobileView, isLoadedPage } = this.props;
const {
isLoadedData,
greetingTitle,
isLoadingGreetingSave,
isLoadingGreetingRestore,
@ -250,9 +254,8 @@ class WelcomePageSettings extends React.Component {
</div>
);
// return <LoaderCustomization welcomePage={true} />;
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
return !isLoadedPage ? (
<LoaderCustomization welcomePage={true} />
) : (
<StyledSettingsComponent
hasScroll={hasScroll}
@ -293,14 +296,21 @@ class WelcomePageSettings extends React.Component {
}
}
export default inject(({ auth, setup }) => {
export default inject(({ auth, setup, common }) => {
const { greetingSettings, organizationName, theme } = auth.settingsStore;
const { setGreetingTitle, restoreGreetingTitle } = setup;
const { isLoaded, setIsLoadedWelcomePageSettings } = common;
return {
theme,
greetingSettings,
organizationName,
setGreetingTitle,
restoreGreetingTitle,
isLoaded,
setIsLoadedWelcomePageSettings,
};
})(withTranslation(["Settings", "Common"])(observer(WelcomePageSettings)));
})(
withLoading(
withTranslation(["Settings", "Common"])(observer(WelcomePageSettings))
)
);

View File

@ -127,7 +127,6 @@ const LoaderCustomization = ({
<Loaders.Rectangle height="32px" className="combo-box" />
{lngTZSettings && (
<>
<Loaders.Rectangle height="20px" className="field-container" />
<Loaders.Rectangle height="20px" className="title-long" />
<Loaders.Rectangle height="32px" className="combo-box" />
</>

View File

@ -1,10 +1,10 @@
import React, { lazy, Suspense } from "react";
import React, { lazy, Suspense, useEffect } from "react";
import { Route, Switch, Redirect } from "react-router-dom";
import { withRouter } from "react-router";
import Layout from "./Layout";
import { combineUrl } from "@appserver/common/utils";
import AppServerConfig from "@appserver/common/constants/AppServerConfig";
import { inject, observer } from "mobx-react";
const SecuritySettings = lazy(() => import("./categories/security/index.js"));
const Admins = lazy(() => import("./categories/security/access-rights/admins"));
const TfaPage = lazy(() => import("./categories/security/access-portal/tfa"));
@ -105,7 +105,13 @@ const DATA_MANAGEMENT_URL = combineUrl(
const ERROR_404_URL = combineUrl(AppServerConfig.proxyURL, "/error/404");
const Settings = () => {
const Settings = (props) => {
const { loadBaseInfo } = props;
useEffect(() => {
loadBaseInfo();
}, []);
return (
<Layout key="1">
<Suspense fallback={null}>
@ -159,4 +165,10 @@ const Settings = () => {
);
};
export default withRouter(Settings);
export default inject(({ common }) => {
return {
loadBaseInfo: async () => {
await common.initSettings();
},
};
})(withRouter(observer(Settings)));

View File

@ -0,0 +1,95 @@
import { makeAutoObservable, runInAction } from "mobx";
import authStore from "@appserver/common/store/AuthStore";
class CommonStore {
isInit = false;
isLoaded = false;
isLoadedArticleBody = false;
isLoadedArticleHeader = false;
isLoadedSectionHeader = false;
isLoadedSubmenu = false;
isLoadedLngTZSettings = false;
isLoadedPortalRenaming = false;
isLoadedCustomization = false;
isLoadedCustomizationNavbar = false;
isLoadedWelcomePageSettings = false;
constructor() {
this.authStore = authStore;
makeAutoObservable(this);
}
initSettings = async () => {
if (this.isInit) return;
this.isInit = true;
const requests = [];
requests.push(
authStore.settingsStore.getPortalTimezones(),
authStore.settingsStore.getPortalCultures()
);
return Promise.all(requests).finally(() => this.setIsLoaded(true));
};
setIsLoadedArticleBody = (isLoadedArticleBody) => {
runInAction(() => {
this.isLoadedArticleBody = isLoadedArticleBody;
});
};
setIsLoadedArticleHeader = (isLoadedArticleHeader) => {
runInAction(() => {
this.isLoadedArticleHeader = isLoadedArticleHeader;
});
};
setIsLoadedSectionHeader = (isLoadedSectionHeader) => {
runInAction(() => {
this.isLoadedSectionHeader = isLoadedSectionHeader;
});
};
setIsLoadedSubmenu = (isLoadedSubmenu) => {
runInAction(() => {
this.isLoadedSubmenu = isLoadedSubmenu;
});
};
setIsLoadedLngTZSettings = (isLoadedLngTZSettings) => {
runInAction(() => {
this.isLoadedLngTZSettings = isLoadedLngTZSettings;
});
};
setIsLoadedWelcomePageSettings = (isLoadedWelcomePageSettings) => {
runInAction(() => {
this.isLoadedWelcomePageSettings = isLoadedWelcomePageSettings;
});
};
setIsLoadedPortalRenaming = (isLoadedPortalRenaming) => {
runInAction(() => {
this.isLoadedPortalRenaming = isLoadedPortalRenaming;
});
};
setIsLoadedCustomization = (isLoadedCustomization) => {
runInAction(() => {
this.isLoadedCustomization = isLoadedCustomization;
});
};
setIsLoadedCustomizationNavbar = (isLoadedCustomizationNavbar) => {
runInAction(() => {
this.isLoadedCustomizationNavbar = isLoadedCustomizationNavbar;
});
};
setIsLoaded = (isLoaded) => {
this.isLoaded = isLoaded;
};
}
export default CommonStore;

View File

@ -10,9 +10,6 @@ import config from "../../package.json";
class SettingsSetupStore {
selectionStore = null;
authStore = null;
isLoadingArticleSettings = false;
isInit = false;
common = {
@ -334,8 +331,10 @@ class SettingsSetupStore {
return api.settings.sendOwnerChange(id);
};
setIsLoadingArticleSettings = (isLoading) => {
this.isLoadingArticleSettings = isLoading;
getCommonThirdPartyList = async () => {
const res = await api.settings.getCommonThirdPartyList();
this.setCommonThirdPartyList(res);
};
}

View File

@ -4,12 +4,14 @@ import WizardStore from "./WizardStore";
import SettingsSetupStore from "./SettingsSetupStore";
import ConfirmStore from "./ConfirmStore";
import BackupStore from "./BackupStore";
import CommonStore from "./CommonStore";
const paymentStore = new PaymentStore();
const wizardStore = new WizardStore();
const setupStore = new SettingsSetupStore();
const confirmStore = new ConfirmStore();
const backupStore = new BackupStore();
const commonStore = new CommonStore();
const store = {
auth: authStore,
@ -18,6 +20,7 @@ const store = {
setup: setupStore,
confirm: confirmStore,
backup: backupStore,
common: commonStore,
};
export default store;

Some files were not shown because too many files have changed in this diff Show More