Web: added catalog layout and document catalog

This commit is contained in:
Timofey Boyko 2021-09-27 20:59:37 +08:00
parent b324f5fbf4
commit a5c57fea52
31 changed files with 1897 additions and 420 deletions

View File

@ -0,0 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
import RectangleLoader from '../RectangleLoader';
import { StyledContainer, StyledBlock } from './StyledDocumentCatalogFolderLoader';
const DocumentCatalogFolderLoader = ({ id, className, style, ...rest }) => {
return (
<StyledContainer>
<StyledBlock id={id} className={className} style={style}>
<RectangleLoader width="100%" height="36px" {...rest} />
<RectangleLoader width="100%" height="36px" {...rest} />
<RectangleLoader width="100%" height="36px" {...rest} />
<RectangleLoader width="100%" height="36px" {...rest} />
</StyledBlock>
<StyledBlock>
<RectangleLoader width="100%" height="36px" {...rest} />
<RectangleLoader width="100%" height="36px" {...rest} />
</StyledBlock>
<StyledBlock>
<RectangleLoader width="100%" height="36px" {...rest} />
</StyledBlock>
<StyledBlock>
<RectangleLoader width="100%" height="36px" {...rest} />
</StyledBlock>
</StyledContainer>
);
};
DocumentCatalogFolderLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
DocumentCatalogFolderLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
};
export default DocumentCatalogFolderLoader;

View File

@ -0,0 +1,30 @@
import styled from 'styled-components';
import { tablet, mobile } from '@appserver/components/utils/device';
const StyledContainer = styled.div`
width: 256px;
padding: 0 20px;
@media ${tablet} {
width: 52px;
padding: 0;
}
@media ${mobile} {
width: 100%;
padding: 0 16px;
}
`;
const StyledBlock = styled.div`
width: 100%;
height: auto;
margin-bottom: 20px;
@media ${tablet} {
margin-bottom: 24px;
}
`;
export { StyledBlock, StyledContainer };

View File

@ -0,0 +1 @@
export default from './DocumentCatalogFolderLoader';

View File

@ -1,7 +1,7 @@
import React from "react";
import PropTypes from "prop-types";
import StyledTreeSettingsLoader from "./StyledTreeSettingsLoader";
import TreeNodeLoader from "../TreeNodeLoader";
import React from 'react';
import PropTypes from 'prop-types';
import StyledTreeSettingsLoader from './StyledTreeSettingsLoader';
import TreeNodeLoader from '../TreeNodeLoader';
const TreeSettingsLoader = ({ id, className, style, ...rest }) => {
return (

View File

@ -1,23 +1,24 @@
import Rectangle from "./RectangleLoader";
import Circle from "./CircleLoader";
import Header from "./HeaderLoader";
import SectionHeader from "./SectionHeaderLoader";
import ArticleHeader from "./ArticleHeaderLoader";
import TreeFolders from "./TreeFolderLoader";
import TreeSettingsLoader from "./TreeSettingsLoader";
import Row from "./RowLoader";
import Rows from "./RowsLoader";
import Text from "./TextLoader";
import Filter from "./FilterLoader";
import ProfileView from "./ProfileViewLoader";
import SettingsFiles from "./SettingsFilesLoader";
import Group from "./GroupLoader";
import HistoryRows from "./HistoryRowsLoader";
import Tile from "./TileLoader";
import Tiles from "./TilesLoader";
import DialogLoader from "./DialogLoader";
import DialogAsideLoader from "./DialogAsideLoader";
import MainButton from "./MainButtonLoader";
import Rectangle from './RectangleLoader';
import Circle from './CircleLoader';
import Header from './HeaderLoader';
import SectionHeader from './SectionHeaderLoader';
import ArticleHeader from './ArticleHeaderLoader';
import TreeFolders from './TreeFolderLoader';
import TreeSettingsLoader from './TreeSettingsLoader';
import Row from './RowLoader';
import Rows from './RowsLoader';
import Text from './TextLoader';
import Filter from './FilterLoader';
import ProfileView from './ProfileViewLoader';
import SettingsFiles from './SettingsFilesLoader';
import Group from './GroupLoader';
import HistoryRows from './HistoryRowsLoader';
import Tile from './TileLoader';
import Tiles from './TilesLoader';
import DialogLoader from './DialogLoader';
import DialogAsideLoader from './DialogAsideLoader';
import MainButton from './MainButtonLoader';
import DocumentCatalogFolderLoader from './DocumentCatalogFolderLoader';
export default {
Rectangle,
@ -40,4 +41,5 @@ export default {
DialogLoader,
DialogAsideLoader,
MainButton,
DocumentCatalogFolderLoader,
};

View File

@ -1,26 +1,31 @@
import React from "react";
import PropTypes from "prop-types";
import Backdrop from "@appserver/components/backdrop";
import { size } from "@appserver/components/utils/device";
import { Provider } from "@appserver/components/utils/context";
import { isMobile } from "react-device-detect";
import Article from "./sub-components/article";
import SubArticleHeader from "./sub-components/article-header";
import SubArticleMainButton from "./sub-components/article-main-button";
import SubArticleBody from "./sub-components/article-body";
import ArticlePinPanel from "./sub-components/article-pin-panel";
import Section from "./sub-components/section";
import SubSectionHeader from "./sub-components/section-header";
import SubSectionFilter from "./sub-components/section-filter";
import SubSectionBody from "./sub-components/section-body";
import SubSectionBodyContent from "./sub-components/section-body-content";
import SubSectionPaging from "./sub-components/section-paging";
import SectionToggler from "./sub-components/section-toggler";
import ReactResizeDetector from "react-resize-detector";
import FloatingButton from "../FloatingButton";
import { inject, observer } from "mobx-react";
import Selecto from "react-selecto";
import styled from "styled-components";
import React from 'react';
import PropTypes from 'prop-types';
import Backdrop from '@appserver/components/backdrop';
import { isTablet, isDesktop, size } from '@appserver/components/utils/device';
import { Provider } from '@appserver/components/utils/context';
import { isMobile } from 'react-device-detect';
import Article from './sub-components/article';
import SubArticleHeader from './sub-components/article-header';
import SubArticleMainButton from './sub-components/article-main-button';
import SubArticleBody from './sub-components/article-body';
import ArticlePinPanel from './sub-components/article-pin-panel';
import Section from './sub-components/section';
import SubSectionHeader from './sub-components/section-header';
import SubSectionFilter from './sub-components/section-filter';
import SubSectionBody from './sub-components/section-body';
import SubSectionBodyContent from './sub-components/section-body-content';
import SubSectionPaging from './sub-components/section-paging';
import SectionToggler from './sub-components/section-toggler';
import ReactResizeDetector from 'react-resize-detector';
import FloatingButton from '../FloatingButton';
import { inject, observer } from 'mobx-react';
import Selecto from 'react-selecto';
import styled from 'styled-components';
import Catalog from './sub-components/catalog';
import SubCatalogBackdrop from './sub-components/catalog-backdrop';
import SubCatalogHeader from './sub-components/catalog-header';
import SubCatalogMainButton from './sub-components/catalog-main-button';
import SubCatalogBody from './sub-components/catalog-body';
const StyledSelectoWrapper = styled.div`
.selecto-selection {
@ -28,42 +33,60 @@ const StyledSelectoWrapper = styled.div`
}
`;
function CatalogHeader() {
return null;
}
CatalogHeader.displayName = 'CatalogHeader';
function CatalogMainButton() {
return null;
}
CatalogMainButton.displayName = 'CatalogMainButton';
function CatalogBody() {
return null;
}
CatalogBody.displayName = 'CatalogBody';
function ArticleHeader() {
return null;
}
ArticleHeader.displayName = "ArticleHeader";
ArticleHeader.displayName = 'ArticleHeader';
function ArticleMainButton() {
return null;
}
ArticleMainButton.displayName = "ArticleMainButton";
ArticleMainButton.displayName = 'ArticleMainButton';
function ArticleBody() {
return null;
}
ArticleBody.displayName = "ArticleBody";
ArticleBody.displayName = 'ArticleBody';
function SectionHeader() {
return null;
}
SectionHeader.displayName = "SectionHeader";
SectionHeader.displayName = 'SectionHeader';
function SectionFilter() {
return null;
}
SectionFilter.displayName = "SectionFilter";
SectionFilter.displayName = 'SectionFilter';
function SectionBody() {
return null;
}
SectionBody.displayName = "SectionBody";
SectionBody.displayName = 'SectionBody';
function SectionPaging() {
return null;
}
SectionPaging.displayName = "SectionPaging";
SectionPaging.displayName = 'SectionPaging';
class PageLayout extends React.Component {
static CatalogHeader = CatalogHeader;
static CatalogMainButton = CatalogMainButton;
static CatalogBody = CatalogBody;
static ArticleHeader = ArticleHeader;
static ArticleMainButton = ArticleMainButton;
static ArticleBody = ArticleBody;
@ -83,7 +106,7 @@ class PageLayout extends React.Component {
componentDidUpdate(prevProps) {
if (!this.scroll) {
this.scroll = document.getElementsByClassName("section-scroll")[0];
this.scroll = document.getElementsByClassName('section-scroll')[0];
}
if (
@ -93,19 +116,25 @@ class PageLayout extends React.Component {
) {
this.backdropClick();
}
if (isDesktop()) return this.props.setShowText(true);
if (isTablet() && !this.props.userShowText) return this.props.setShowText(false);
if (this.props.showText && isTablet() && !this.props.userShowText)
return this.props.setShowText(false);
if (this.props.showText && isMobile && !this.props.userShowText)
return this.props.setShowText(false);
if (!isTablet() && !isMobile && !this.props.showText && !this.props.userShowText)
return this.props.setShowText(true);
}
componentDidMount() {
window.addEventListener("orientationchange", this.orientationChangeHandler);
window.addEventListener('orientationchange', this.orientationChangeHandler);
this.orientationChangeHandler();
}
componentWillUnmount() {
window.removeEventListener(
"orientationchange",
this.orientationChangeHandler
);
window.removeEventListener('orientationchange', this.orientationChangeHandler);
if (this.intervalHandler) clearInterval(this.intervalHandler);
if (this.timeoutHandler) clearTimeout(this.timeoutHandler);
@ -159,16 +188,12 @@ class PageLayout extends React.Component {
dragCondition = (e) => {
const path = e.inputEvent.composedPath();
const isBackdrop = path.some(
(x) => x.classList && x.classList.contains("backdrop-active")
);
const isBackdrop = path.some((x) => x.classList && x.classList.contains('backdrop-active'));
const notSelectablePath = path.some(
(x) => x.classList && x.classList.contains("not-selectable")
(x) => x.classList && x.classList.contains('not-selectable'),
);
const isDraggable = path.some(
(x) => x.classList && x.classList.contains("draggable")
);
const isDraggable = path.some((x) => x.classList && x.classList.contains('draggable'));
if (notSelectablePath || isBackdrop || isDraggable) {
return false;
@ -205,7 +230,13 @@ class PageLayout extends React.Component {
isBackdropVisible,
isArticlePinned,
isDesktop,
showText,
toggleShowText,
showCatalog,
} = this.props;
let catalogHeaderContent = null;
let catalogMainButtonContent = null;
let catalogBodyContent = null;
let articleHeaderContent = null;
let articleMainButtonContent = null;
let articleBodyContent = null;
@ -213,12 +244,19 @@ class PageLayout extends React.Component {
let sectionFilterContent = null;
let sectionPagingContent = null;
let sectionBodyContent = null;
React.Children.forEach(children, (child) => {
const childType =
child && child.type && (child.type.displayName || child.type.name);
const childType = child && child.type && (child.type.displayName || child.type.name);
switch (childType) {
case CatalogHeader.displayName:
catalogHeaderContent = child;
break;
case CatalogMainButton.displayName:
catalogMainButtonContent = child;
break;
case CatalogBody.displayName:
catalogBodyContent = child;
break;
case ArticleHeader.displayName:
articleHeaderContent = child;
break;
@ -249,59 +287,70 @@ class PageLayout extends React.Component {
isArticleMainButtonAvailable = !!articleMainButtonContent,
isArticleBodyAvailable = !!articleBodyContent,
isArticleAvailable =
isArticleHeaderAvailable ||
isArticleMainButtonAvailable ||
isArticleBodyAvailable,
isArticleHeaderAvailable || isArticleMainButtonAvailable || isArticleBodyAvailable,
isSectionHeaderAvailable = !!sectionHeaderContent,
isSectionFilterAvailable = !!sectionFilterContent,
isSectionPagingAvailable = !!sectionPagingContent,
isSectionBodyAvailable =
!!sectionBodyContent ||
isSectionFilterAvailable ||
isSectionPagingAvailable,
!!sectionBodyContent || isSectionFilterAvailable || isSectionPagingAvailable,
isSectionAvailable =
isSectionHeaderAvailable ||
isSectionFilterAvailable ||
isSectionBodyAvailable ||
isSectionPagingAvailable ||
isArticleAvailable,
isBackdropAvailable = isArticleAvailable;
isBackdropAvailable = isArticleAvailable,
isCatalogHeaderAvailable = !!catalogHeaderContent,
isCatalogMainButtonAvailable = !!catalogMainButtonContent,
isCatalogBodyAvailable = !!catalogBodyContent,
isCatalogAvailable =
isCatalogHeaderAvailable || isCatalogMainButtonAvailable || isCatalogBodyAvailable;
const renderPageLayout = () => {
return (
<>
{isBackdropAvailable && (
<Backdrop
zIndex={400}
visible={isBackdropVisible}
onClick={this.backdropClick}
/>
{showCatalog && isCatalogAvailable && (
<Catalog showText={showText}>
{isCatalogHeaderAvailable && (
<>
<SubCatalogBackdrop showText={showText} onClick={toggleShowText} />
<SubCatalogHeader showText={showText} onClick={toggleShowText}>
{catalogHeaderContent ? catalogHeaderContent.props.children : null}
</SubCatalogHeader>
</>
)}
{isCatalogMainButtonAvailable && (
<SubCatalogMainButton showText={showText}>
{catalogMainButtonContent ? catalogMainButtonContent.props.children : null}
</SubCatalogMainButton>
)}
{isCatalogBodyAvailable && (
<SubCatalogBody showText={showText}>
{catalogBodyContent ? catalogBodyContent.props.children : null}
</SubCatalogBody>
)}
</Catalog>
)}
{isArticleAvailable && (
<Article
visible={isArticleVisible}
pinned={isArticlePinned}
firstLoad={firstLoad}
>
{!showCatalog && isBackdropAvailable && (
<Backdrop zIndex={400} visible={isBackdropVisible} onClick={this.backdropClick} />
)}
{!showCatalog && isArticleAvailable && (
<Article visible={isArticleVisible} pinned={isArticlePinned} firstLoad={firstLoad}>
{isArticleHeaderAvailable && (
<SubArticleHeader>
{articleHeaderContent
? articleHeaderContent.props.children
: null}
{articleHeaderContent ? articleHeaderContent.props.children : null}
</SubArticleHeader>
)}
{isArticleMainButtonAvailable && (
<SubArticleMainButton>
{articleMainButtonContent
? articleMainButtonContent.props.children
: null}
{articleMainButtonContent ? articleMainButtonContent.props.children : null}
</SubArticleMainButton>
)}
{isArticleBodyAvailable && (
<SubArticleBody pinned={isArticlePinned} isDesktop={isDesktop}>
{articleBodyContent
? articleBodyContent.props.children
: null}
{articleBodyContent ? articleBodyContent.props.children : null}
</SubArticleBody>
)}
{isArticleBodyAvailable && (
@ -317,28 +366,23 @@ class PageLayout extends React.Component {
<ReactResizeDetector
refreshRate={100}
refreshMode="debounce"
refreshOptions={{ trailing: true }}
>
refreshOptions={{ trailing: true }}>
{({ width, height }) => (
<Provider
value={{
sectionWidth: width,
sectionHeight: height,
}}
>
}}>
<Section
showText={showText}
widthProp={width}
unpinArticle={this.unpinArticle}
pinned={isArticlePinned}
>
pinned={isArticlePinned}>
{isSectionHeaderAvailable && (
<SubSectionHeader
isHeaderVisible={isHeaderVisible}
isArticlePinned={isArticlePinned}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
: null}
isArticlePinned={isArticlePinned}>
{sectionHeaderContent ? sectionHeaderContent.props.children : null}
</SubSectionHeader>
)}
@ -347,14 +391,11 @@ class PageLayout extends React.Component {
<div
id="main-bar"
style={{
display: "grid",
paddingRight: "20px",
}}
></div>
display: 'grid',
paddingRight: '20px',
}}></div>
<SubSectionFilter className="section-header_filter">
{sectionFilterContent
? sectionFilterContent.props.children
: null}
{sectionFilterContent ? sectionFilterContent.props.children : null}
</SubSectionFilter>
</>
)}
@ -366,25 +407,18 @@ class PageLayout extends React.Component {
withScroll={withBodyScroll}
autoFocus={isMobile || isTabletView ? false : true}
pinned={isArticlePinned}
viewAs={viewAs}
>
viewAs={viewAs}>
{isSectionFilterAvailable && (
<SubSectionFilter className="section-body_filter">
{sectionFilterContent
? sectionFilterContent.props.children
: null}
{sectionFilterContent ? sectionFilterContent.props.children : null}
</SubSectionFilter>
)}
<SubSectionBodyContent>
{sectionBodyContent
? sectionBodyContent.props.children
: null}
{sectionBodyContent ? sectionBodyContent.props.children : null}
</SubSectionBodyContent>
{isSectionPagingAvailable && (
<SubSectionPaging>
{sectionPagingContent
? sectionPagingContent.props.children
: null}
{sectionPagingContent ? sectionPagingContent.props.children : null}
</SubSectionPaging>
)}
</SubSectionBody>
@ -426,11 +460,8 @@ class PageLayout extends React.Component {
<></>
)}
{isArticleAvailable && (
<SectionToggler
visible={!isArticleVisible}
onClick={this.showArticle}
/>
{!showCatalog && isArticleAvailable && (
<SectionToggler visible={!isArticleVisible} onClick={this.showArticle} />
)}
</Section>
</Provider>
@ -455,9 +486,9 @@ class PageLayout extends React.Component {
{!isMobile && uploadFiles && !dragging && (
<StyledSelectoWrapper>
<Selecto
boundContainer={".section-body"}
dragContainer={".section-body"}
selectableTargets={[".files-item"]}
boundContainer={'.section-body'}
dragContainer={'.section-body'}
selectableTargets={['.files-item']}
hitRate={0}
selectByClick={false}
selectFromInside={true}
@ -498,6 +529,11 @@ PageLayout.propTypes = {
isTabletView: PropTypes.bool,
isHeaderVisible: PropTypes.bool,
firstLoad: PropTypes.bool,
showText: PropTypes.bool,
userShowText: PropTypes.bool,
setShowText: PropTypes.func,
toggleShowText: PropTypes.func,
showCatalog: PropTypes.bool,
};
PageLayout.defaultProps = {
@ -505,6 +541,8 @@ PageLayout.defaultProps = {
withBodyAutoFocus: false,
};
PageLayout.CatalogHeader = CatalogHeader;
PageLayout.CatalogMainButton = CatalogMainButton;
PageLayout.ArticleHeader = ArticleHeader;
PageLayout.ArticleMainButton = ArticleMainButton;
PageLayout.ArticleBody = ArticleBody;
@ -526,6 +564,11 @@ export default inject(({ auth }) => {
setIsArticleVisible,
setIsBackdropVisible,
isDesktopClient,
showText,
userShowText,
setShowText,
toggleShowText,
showCatalog,
} = settingsStore;
return {
@ -540,5 +583,10 @@ export default inject(({ auth }) => {
isBackdropVisible,
setIsBackdropVisible,
isDesktop: isDesktopClient,
showText,
userShowText,
setShowText,
toggleShowText,
showCatalog,
};
})(observer(PageLayout));

View File

@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import Backdrop from '@appserver/components/backdrop';
import { mobile } from '@appserver/components/utils/device';
import CrossIcon from '@appserver/components/public/static/images/cross.react.svg';
const StyledBackdrop = styled(Backdrop)`
display: none;
width: 100vw;
height: 64px;
position: fixed;
top: 0;
left: 0;
background: rgba(6, 22, 38, 0.15);
backdrop-filter: blur(18px);
cursor: initial;
@media ${mobile} {
display: block;
}
`;
const StyledControlContainer = styled.div`
background: #9a9ea3;
width: 24px;
height: 24px;
position: absolute;
top: 30px;
right: 10px;
border-radius: 100px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
`;
const StyledCrossIcon = styled(CrossIcon)`
width: 11.31px;
height: 11.31px;
path {
fill: #ffffff;
}
`;
const CatalogBackdrop = (props) => {
const { showText, onClick, ...rest } = props;
return (
<StyledBackdrop visible={showText} {...rest}>
<StyledControlContainer onClick={onClick}>
<StyledCrossIcon />
</StyledControlContainer>
</StyledBackdrop>
);
};
CatalogBackdrop.propTypes = {
showText: PropTypes.bool,
onClick: PropTypes.func,
};
export default React.memo(CatalogBackdrop);

View File

@ -0,0 +1,11 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
const CatalogBody = (props) => {
return <> {props.children}</>;
};
CatalogBody.displayName = 'CatalogBody';
export default React.memo(CatalogBody);

View File

@ -0,0 +1,85 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import Heading from '@appserver/components/heading';
import Backdrop from '@appserver/components/backdrop';
import { mobile, tablet } from '@appserver/components/utils/device';
import MenuIcon from '@appserver/components/public/static/images/menu.react.svg';
const StyledCatalogHeader = styled.div`
padding: 12px 20px 13px;
display: flex;
justify-content: flex-start;
align-items: center;
@media ${tablet} {
padding: 16px 20px 17px;
justify-content: ${(props) => (props.showText ? 'flex-start' : 'center')};
}
@media ${mobile} {
border-bottom: 1px solid #eceef1;
padding: 28px 20px 12px;
}
`;
const StyledHeading = styled(Heading)`
margin: 0;
font-weight: bold;
line-height: 28px;
@media ${tablet} {
display: ${(props) => (props.showText ? 'block' : 'none')};
margin-left: ${(props) => props.showText && '12px'};
}
@media ${mobile} {
margin-left: 0;
}
`;
const StyledIconBox = styled.div`
display: none;
align-items: center;
height: 28px;
@media ${tablet} {
display: flex;
}
@media ${mobile} {
display: none;
}
`;
const StyledMenuIcon = styled(MenuIcon)`
display: block;
width: 20px;
height: 20px;
fill: #657077;
cursor: pointer;
`;
const CatalogHeader = (props) => {
const { showText, children, onClick, ...rest } = props;
return (
<StyledCatalogHeader showText={showText} {...rest}>
<StyledIconBox>
<StyledMenuIcon onClick={onClick} />
</StyledIconBox>
<StyledHeading showText={showText} color="#333333" size="large">
{children}
</StyledHeading>
</StyledCatalogHeader>
);
};
CatalogHeader.propTypes = {
children: PropTypes.any,
showText: PropTypes.bool,
onClick: PropTypes.func,
};
CatalogHeader.displayName = 'CatalogHeader';
export default React.memo(CatalogHeader);

View File

@ -0,0 +1,24 @@
import React from 'react';
import styled from 'styled-components';
import { tablet, mobile } from '@appserver/components/utils/device';
const StyledCatalogMainButton = styled.div`
padding: 0px 20px 16px;
@media ${tablet} {
display: ${(props) => (props.showText ? 'block' : 'none')};
padding: 0 20px 16px;
}
@media ${mobile} {
padding: 16px 20px 16px;
}
`;
const CatalogMainButton = (props) => {
return <StyledCatalogMainButton {...props} />;
};
CatalogMainButton.displayName = 'CatalogMainButton';
export default CatalogMainButton;

View File

@ -0,0 +1,68 @@
import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import { Resizable } from 're-resizable';
import { isMobile } from 'react-device-detect';
import { mobile, tablet } from '@appserver/components/utils/device';
const StyledCatalog = styled.div`
.resizable-block {
display: flex;
flex-direction: column;
min-width: 256px;
width: 256px;
height: 100% !important;
background: #f8f9f9;
overflow: hidden;
.resizable-border {
div {
cursor: ew-resize !important;
}
}
@media ${tablet} {
min-width: ${(props) => (props.showText ? '240px' : '52px')};
max-width: ${(props) => (props.showText ? '240px' : '52px')};
.resizable-border {
display: none;
}
}
@media ${mobile} {
display: ${(props) => (props.showText ? 'flex' : 'none')};
min-width: 100vw;
width: 100vw;
margin: 0;
padding: 0;
}
}
`;
const Catalog = (props) => {
const { showText, children, ...rest } = props;
const enable = {
top: false,
right: !isMobile,
bottom: false,
left: false,
};
return (
<StyledCatalog showText={showText} {...rest}>
<Resizable
enable={enable}
className="resizable-block"
handleWrapperClass="resizable-border not-selectable">
{children}
</Resizable>
</StyledCatalog>
);
};
Catalog.propTypes = {
showText: PropTypes.bool,
children: PropTypes.any,
};
export default Catalog;

View File

@ -1,13 +1,7 @@
import React from "react";
import styled, { css } from "styled-components";
import { tablet, size } from "@appserver/components/utils/device";
import {
isIOS,
isTablet,
isSafari,
isChrome,
isMobile,
} from "react-device-detect";
import React from 'react';
import styled, { css } from 'styled-components';
import { tablet, size, mobile } from '@appserver/components/utils/device';
import { isIOS, isTablet, isSafari, isChrome, isMobile } from 'react-device-detect';
const tabletProps = css`
.section-header_filter {
@ -25,6 +19,9 @@ const StyledSection = styled.section`
flex-grow: 1;
display: flex;
flex-direction: column;
@media ${mobile} {
display: ${(props) => (!props.showText ? 'flex' : 'none')};
}
//width: ${(props) => `${props.widthProp}px`};
.layout-progress-bar {
position: fixed;
@ -57,13 +54,11 @@ const StyledSection = styled.section`
padding: 0 0 0 16px;
${tabletProps};
}
${
isMobile &&
css`
${tabletProps};
min-width: 100px;
`
}
${isMobile &&
css`
${tabletProps};
min-width: 100px;
`}
`;
class Section extends React.Component {

View File

@ -1,55 +1,55 @@
import { makeAutoObservable } from "mobx";
import api from "../api";
import { ARTICLE_PINNED_KEY, LANGUAGE } from "../constants";
import { combineUrl } from "../utils";
import FirebaseHelper from "../utils/firebase";
import { AppServerConfig } from "../constants";
import { makeAutoObservable } from 'mobx';
import api from '../api';
import { ARTICLE_PINNED_KEY, LANGUAGE } from '../constants';
import { combineUrl } from '../utils';
import FirebaseHelper from '../utils/firebase';
import { AppServerConfig } from '../constants';
const { proxyURL } = AppServerConfig;
class SettingsStore {
isLoading = false;
isLoaded = false;
currentProductId = "";
culture = "en-US";
currentProductId = '';
culture = 'en-US';
cultures = [];
trustedDomains = [];
trustedDomainsType = 0;
trustedDomains = [];
timezone = "UTC";
timezone = 'UTC';
timezones = [];
utcOffset = "00:00:00";
utcOffset = '00:00:00';
utcHoursOffset = 0;
defaultPage = "/";
homepage = "";
datePattern = "M/d/yyyy";
datePatternJQ = "00/00/0000";
dateTimePattern = "dddd, MMMM d, yyyy h:mm:ss tt";
defaultPage = '/';
homepage = '';
datePattern = 'M/d/yyyy';
datePatternJQ = '00/00/0000';
dateTimePattern = 'dddd, MMMM d, yyyy h:mm:ss tt';
datepicker = {
datePattern: "mm/dd/yy",
dateTimePattern: "DD, mm dd, yy h:mm:ss tt",
timePattern: "h:mm tt",
datePattern: 'mm/dd/yy',
dateTimePattern: 'DD, mm dd, yy h:mm:ss tt',
timePattern: 'h:mm tt',
};
organizationName = "ONLYOFFICE";
greetingSettings = "Web Office Applications";
organizationName = 'ONLYOFFICE';
greetingSettings = 'Web Office Applications';
enableAdmMess = false;
enabledJoin = false;
urlLicense = "https://gnu.org/licenses/gpl-3.0.html";
urlSupport = "https://helpdesk.onlyoffice.com/";
logoUrl = combineUrl(proxyURL, "/static/images/nav.logo.opened.react.svg");
urlLicense = 'https://gnu.org/licenses/gpl-3.0.html';
urlSupport = 'https://helpdesk.onlyoffice.com/';
logoUrl = combineUrl(proxyURL, '/static/images/nav.logo.opened.react.svg');
customNames = {
id: "Common",
userCaption: "User",
usersCaption: "Users",
groupCaption: "Group",
groupsCaption: "Groups",
userPostCaption: "Title",
regDateCaption: "Registration Date",
groupHeadCaption: "Head",
guestCaption: "Guest",
guestsCaption: "Guests",
id: 'Common',
userCaption: 'User',
usersCaption: 'Users',
groupCaption: 'Group',
groupsCaption: 'Groups',
userPostCaption: 'Title',
regDateCaption: 'Registration Date',
groupHeadCaption: 'Head',
guestCaption: 'Guest',
guestsCaption: 'Guests',
};
isDesktopClient = window["AscDesktopEditor"] !== undefined;
isDesktopClient = window['AscDesktopEditor'] !== undefined;
//isDesktopEncryption: desktopEncryption;
isEncryptionSupport = false;
encryptionKeys = null;
@ -58,15 +58,17 @@ class SettingsStore {
isHeaderVisible = false;
isTabletView = false;
isArticlePinned =
localStorage.getItem(ARTICLE_PINNED_KEY) === "true" || false;
isArticlePinned = localStorage.getItem(ARTICLE_PINNED_KEY) === 'true' || false;
isArticleVisible = false;
isBackdropVisible = false;
isArticleVisibleOnUnpin = false;
showText = true;
userShowText = false;
showCatalog = true;
hashSettings = null;
title = "";
title = '';
ownerId = null;
nameSchemaId = null;
owner = {};
@ -76,23 +78,23 @@ class SettingsStore {
customSchemaList = [];
firebase = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
apiKey: '',
authDomain: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: '',
measurementId: '',
};
version = "";
version = '';
constructor() {
makeAutoObservable(this);
}
get urlAuthKeys() {
const splitted = this.culture.split("-");
const lang = splitted.length > 0 ? splitted[0] : "en";
const splitted = this.culture.split('-');
const lang = splitted.length > 0 ? splitted[0] : 'en';
return `https://helpcenter.onlyoffice.com/${lang}/installation/groups-authorization-keys.aspx`;
}
@ -101,8 +103,8 @@ class SettingsStore {
}
get helpUrlCommonSettings() {
const substring = this.culture.substring(0, this.culture.indexOf("-"));
const lang = substring.length > 0 ? substring : "en";
const substring = this.culture.substring(0, this.culture.indexOf('-'));
const lang = substring.length > 0 ? substring : 'en';
return `https://helpcenter.onlyoffice.com/${lang}/administration/configuration.aspx#CustomizingPortal_block`;
}
@ -131,8 +133,8 @@ class SettingsStore {
getSettings = async () => {
const newSettings = await api.settings.getSettings();
if (window["AscDesktopEditor"] !== undefined || this.personal) {
const dp = combineUrl(proxyURL, "/products/files/");
if (window['AscDesktopEditor'] !== undefined || this.personal) {
const dp = combineUrl(proxyURL, '/products/files/');
this.setDefaultPage(dp);
}
@ -140,24 +142,22 @@ class SettingsStore {
if (key in this) {
this.setValue(
key,
key === "defaultPage"
? combineUrl(proxyURL, newSettings[key])
: newSettings[key]
key === 'defaultPage' ? combineUrl(proxyURL, newSettings[key]) : newSettings[key],
);
if (key === "culture") {
if (key === 'culture') {
const language = localStorage.getItem(LANGUAGE);
if (!language || language == "undefined") {
if (!language || language == 'undefined') {
localStorage.setItem(LANGUAGE, newSettings[key]);
}
}
if (key === "personal") {
if (key === 'personal') {
window.AppServer = {
...window.AppServer,
personal: newSettings[key],
};
}
} else if (key === "passwordHash") {
this.setValue("hashSettings", newSettings[key]);
} else if (key === 'passwordHash') {
this.setValue('hashSettings', newSettings[key]);
}
});
@ -226,14 +226,14 @@ class SettingsStore {
getOAuthToken = (tokenGetterWin) => {
return new Promise((resolve, reject) => {
localStorage.removeItem("code");
localStorage.removeItem('code');
let interval = null;
interval = setInterval(() => {
try {
const code = localStorage.getItem("code");
const code = localStorage.getItem('code');
if (code) {
localStorage.removeItem("code");
localStorage.removeItem('code');
clearInterval(interval);
resolve(code);
} else if (tokenGetterWin && tokenGetterWin.closed) {
@ -252,25 +252,24 @@ class SettingsStore {
};
setModuleInfo = (homepage, productId) => {
if (this.homepage === homepage || this.currentProductId === productId)
return;
if (this.homepage === homepage || this.currentProductId === productId) return;
console.log(`setModuleInfo('${homepage}', '${productId}')`);
this.homepage = homepage;
this.setCurrentProductId(productId);
const baseElm = document.getElementsByTagName("base");
const baseElm = document.getElementsByTagName('base');
if (baseElm && baseElm.length === 1) {
const baseUrl = homepage
? homepage[homepage.length - 1] === "/"
? homepage[homepage.length - 1] === '/'
? homepage
: `${homepage}/`
: "/";
: '/';
console.log("SET base URL", baseUrl);
console.log('SET base URL', baseUrl);
baseElm[0].setAttribute("href", baseUrl);
baseElm[0].setAttribute('href', baseUrl);
}
};
@ -325,6 +324,19 @@ class SettingsStore {
this.isArticleVisibleOnUnpin = visible;
};
setShowText = (showText) => {
this.showText = showText;
};
setUserShowText = (userShowText) => {
this.userShowText = userShowText;
};
toggleShowText = () => {
this.showText = !this.showText;
this.userShowText = true;
};
get firebaseHelper() {
window.firebaseHelper = new FirebaseHelper(this.firebase);
return window.firebaseHelper;

View File

@ -27,6 +27,7 @@ Display catalog item. Can show only icon (showText property). If is it end of bl
| `onClick` | `func` | - | - | - | What the catalog item will trigger when clicked |
| `showInitial` | `bool` | - | - | `false` | Tells when the catalog item should display initial text(first symbol of text) |
| `isEndOfBlock` | `bool` | - | - | `false` | Tells when the catalog item should be end of block (adding margin-bottom) |
| `isActive` | `bool` | - | - | `false` | Tells when the catalog item should be active (adding background color) |
| `showBadge` | `bool` | - | - | `false` | Tells when the catalog item should display badge |
| `labelBadge` | `string` | - | - | - | Label for badge |
| `iconBadge` | `string` | - | - | - | Icon for badge |

View File

@ -28,6 +28,7 @@ const CatalogItem = (props) => {
showText,
onClick,
isEndOfBlock,
isActive,
showInitial,
showBadge,
labelBadge,
@ -35,12 +36,13 @@ const CatalogItem = (props) => {
onClickBadge,
} = props;
const onClickAction = (e) => {
onClick && onClick(e);
const onClickAction = () => {
onClick && onClick(id);
};
const onClickBadgeAction = (e) => {
onClickBadge && onClickBadge(e);
e.stopPropagation();
onClickBadge && onClickBadge(id);
};
return (
@ -52,6 +54,7 @@ const CatalogItem = (props) => {
isEndOfBlock={isEndOfBlock}
>
<StyledCatalogItemSibling
isActive={isActive}
onClick={onClickAction}
></StyledCatalogItemSibling>
@ -96,7 +99,7 @@ CatalogItem.propTypes = {
/** Accepts className */
className: PropTypes.string,
/** Accepts id */
id: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Catalog item icon */
@ -111,6 +114,8 @@ CatalogItem.propTypes = {
showInitial: PropTypes.bool,
/** Tells when the catalog item should be end of block */
isEndOfBlock: PropTypes.bool,
/** Tells when the catalog item should be active */
isActive: PropTypes.bool,
/** Tells when the catalog item should display badge */
showBadge: PropTypes.bool,
/** Label in catalog item badge */
@ -124,6 +129,7 @@ CatalogItem.propTypes = {
CatalogItem.defaultProps = {
showText: false,
showBadge: false,
isActive: false,
showInitial: false,
isEndOfBlock: false,
};

View File

@ -103,6 +103,9 @@ const StyledCatalogItemImg = styled.div`
svg {
width: ${(props) => props.theme.catalogItem.img.svg.width};
height: ${(props) => props.theme.catalogItem.img.svg.height};
path {
fill: #657077 !important;
}
}
@media ${tablet} {
@ -122,6 +125,9 @@ const StyledCatalogItemSibling = styled.div`
width: 100%;
height: 100%;
background-color: ${(props) =>
props.isActive && props.theme.catalogItem.sibling.hover.backgroundColor};
&:hover {
background-color: ${(props) =>
props.theme.catalogItem.sibling.hover.backgroundColor};
@ -131,8 +137,6 @@ const StyledCatalogItemSibling = styled.div`
StyledCatalogItemSibling.defaultProps = { theme: Base };
const StyledCatalogItemContainer = styled.div`
background-color: ${(props) =>
props.theme.catalogItem.container.backgroundColor};
display: flex;
justify-content: ${(props) => (props.showText ? "space-between" : "center")};
align-items: center;

View File

@ -1630,7 +1630,6 @@ const Base = {
height: "36px",
padding: "0 20px",
marginBottom: "20px",
backgroundColor: grayLight,
tablet: {
height: "44px",
padding: "0 16px",

View File

@ -0,0 +1,130 @@
import React, { useEffect, useState } from "react";
import { withTranslation } from "react-i18next";
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { getLanguage } from "@appserver/common/utils";
import CampaignsBanner from "@appserver/components/campaigns-banner";
import { ADS_TIMEOUT } from "../../../helpers/constants";
import { LANGUAGE } from "@appserver/common/constants";
const i18nConfig = i18n.createInstance();
let translationUrl;
const loadLanguagePath = async () => {
if (!window.firebaseHelper) return;
const lng = localStorage.getItem(LANGUAGE) || "en";
const language = getLanguage(lng instanceof Array ? lng[0] : lng);
const campaigns = (localStorage.getItem("campaigns") || "")
.split(",")
.filter((campaign) => campaign.length > 0);
const index = Number(localStorage.getItem("bannerIndex") || 0);
const campaign = campaigns[index];
try {
translationUrl = await window.firebaseHelper.getCampaignsTranslations(
campaign,
language
);
} catch (e) {
translationUrl = await window.firebaseHelper.getCampaignsTranslations(
campaign,
"en"
);
//console.error(e);
}
return translationUrl;
};
const bannerHOC = (WrappedComponent) => (props) => {
const { FirebaseHelper } = props;
const campaigns = (localStorage.getItem("campaigns") || "")
.split(",")
.filter((campaign) => campaign.length > 0);
const [bannerImage, setBannerImage] = useState("");
const [bannerTranslation, setBannerTranslation] = useState();
const updateBanner = async () => {
let index = Number(localStorage.getItem("bannerIndex") || 0);
const campaign = campaigns[index];
if (campaigns.length < 1 || index + 1 >= campaigns.length) {
index = 0;
} else {
index++;
}
try {
const translationUrl = await loadLanguagePath();
setBannerTranslation(translationUrl);
i18nConfig.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "all",
debug: false,
defaultNS: "",
backend: {
loadPath: function () {
return translationUrl;
},
},
});
const image = await FirebaseHelper.getCampaignsImages(
campaign.toLowerCase()
);
setBannerImage(image);
} catch (e) {
updateBanner();
//console.error(e);
}
localStorage.setItem("bannerIndex", index);
};
useEffect(() => {
updateBanner();
setInterval(updateBanner, ADS_TIMEOUT);
}, []);
if (!bannerTranslation || !bannerImage) return <></>;
return <WrappedComponent bannerImage={bannerImage} {...props} />;
};
const Banner = (props) => {
//console.log("Banner render", props);
const { t, tReady, bannerImage } = props;
const campaigns = (localStorage.getItem("campaigns") || "")
.split(",")
.filter((campaign) => campaign.length > 0);
if (!campaigns.length || !tReady) {
return <></>;
}
return (
<CampaignsBanner
headerLabel={t("Header")}
subHeaderLabel={t("SubHeader")}
img={bannerImage}
btnLabel={t("ButtonLabel")}
link={t("Link")}
/>
);
};
const BannerWithTranslation = withTranslation()(Banner);
const WrapperBanner = (props) => (
<BannerWithTranslation i18n={i18nConfig} useSuspense={false} {...props} />
);
export default bannerHOC(WrapperBanner);

View File

@ -0,0 +1,91 @@
import React from "react";
import styled from "styled-components";
import { withTranslation } from "react-i18next";
import Text from "@appserver/components/text";
import IconButton from "@appserver/components/icon-button";
import withLoader from "../../../HOCs/withLoader";
const StyledDownloadAppList = styled.div`
margin-top: 20px;
.download-app-list {
padding-top: 3px;
display: flex;
max-width: inherit;
}
.icon-button {
padding: 5px;
}
`;
const DownloadAppListContainer = ({ t }) => {
const windowsLink =
"https://www.onlyoffice.com/download-desktop.aspx#windows";
const macLink = "https://www.onlyoffice.com/download-desktop.aspx#mac";
const linuxLink = "https://www.onlyoffice.com/download-desktop.aspx#linux";
const androidLink = "https://www.onlyoffice.com/office-for-android.aspx";
const iosLink = "https://www.onlyoffice.com/office-for-ios.aspx";
return (
<StyledDownloadAppList>
<Text color="#83888d" fontSize="14px">
{t("Translations:DownloadApps")}
</Text>
<div className="download-app-list">
<IconButton
onClick={() => window.open(windowsLink)}
className="icon-button"
iconName="/static/images/windows.react.svg"
size="25"
isfill={true}
color="#A3A9AE"
hoverColor="#3785D3"
/>
<IconButton
onClick={() => window.open(macLink)}
className="icon-button"
iconName="/static/images/macOS.react.svg"
size="25"
isfill={true}
color="#A3A9AE"
hoverColor="#000000"
/>
<IconButton
onClick={() => window.open(linuxLink)}
className="icon-button"
iconName="/static/images/linux.react.svg"
size="25"
isfill={true}
color="#A3A9AE"
hoverColor="#FFB800"
/>
<IconButton
onClick={() => window.open(androidLink)}
className="icon-button"
iconName="/static/images/android.react.svg"
size="25"
isfill={true}
color="#A3A9AE"
hoverColor="#9BD71C"
/>
<IconButton
onClick={() => window.open(iosLink)}
className="icon-button"
iconName="/static/images/iOS.react.svg"
size="25"
isfill={true}
color="#A3A9AE"
hoverColor="#000000"
/>
</div>
</StyledDownloadAppList>
);
};
const DownloadAppList = withTranslation(["Translations"])(
withLoader(DownloadAppListContainer)(<></>)
);
export default DownloadAppList;

View File

@ -0,0 +1,138 @@
import React from 'react';
import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import CatalogItem from '@appserver/components/catalog-item';
import { FolderType } from '@appserver/common/constants';
import { withTranslation } from 'react-i18next';
import withLoader from '../../../HOCs/withLoader';
import Loaders from '@appserver/common/components/Loaders';
const Items = ({ data, showText, selectedTreeNode, onClick, onBadgeClick }) => {
const isActive = (item) => {
return `${item.id}` === selectedTreeNode[0];
};
const getEndOfBlock = (item) => {
switch (item.key) {
case '0-3':
case '0-5':
case '0-6':
return true;
default:
return false;
}
};
const getFolderIcon = (item) => {
let iconUrl = 'images/catalog.folder.react.svg';
switch (item.rootFolderType) {
case FolderType.USER:
iconUrl = '/static/images/catalog.user.react.svg';
break;
case FolderType.SHARE:
iconUrl = '/static/images/catalog.shared.react.svg';
break;
case FolderType.COMMON:
iconUrl = '/static/images/catalog.portfolio.react.svg';
break;
case FolderType.Favorites:
iconUrl = '/static/images/catalog.favorites.react.svg';
break;
case FolderType.Recent:
iconUrl = '/static/images/catalog.recent.react.svg';
break;
case FolderType.Privacy:
iconUrl = '/static/images/catalog.private.react.svg';
break;
case FolderType.TRASH:
iconUrl = '/static/images/catalog.trash.react.svg';
break;
default:
break;
}
switch (item.providerKey) {
case 'GoogleDrive':
iconUrl = '/static/images/cloud.services.google.drive.react.svg';
break;
case 'Box':
iconUrl = '/static/images/cloud.services.box.react.svg';
break;
case 'DropboxV2':
iconUrl = '/static/images/cloud.services.dropbox.react.svg';
break;
case 'OneDrive':
iconUrl = '/static/images/cloud.services.onedrive.react.svg';
break;
case 'SharePoint':
iconUrl = '/static/images/cloud.services.onedrive.react.svg';
break;
case 'kDrive':
iconUrl = '/static/images/catalog.folder.react.svg';
break;
case 'Yandex':
iconUrl = '/static/images/catalog.folder.react.svg';
break;
case 'NextCloud':
iconUrl = '/static/images/cloud.services.nextcloud.react.svg';
break;
case 'OwnCloud':
iconUrl = '/static/images/catalog.folder.react.svg';
break;
case 'WebDav':
iconUrl = '/static/images/catalog.folder.react.svg';
break;
default:
break;
}
return iconUrl;
};
const getItem = (data) => {
const items = data.map((item) => {
const showBadge = item.newItems ? item.newItems > 0 && true : false;
return (
<CatalogItem
key={item.id}
id={item.id}
icon={getFolderIcon(item)}
showText={showText}
text={item.title}
isActive={isActive(item)}
onClick={onClick}
isEndOfBlock={getEndOfBlock(item)}
showBadge={showBadge}
labelBadge={showBadge ? item.newItems : null}
onClickBadge={onBadgeClick}
/>
);
});
return items;
};
return <>{getItem(data)}</>;
};
Items.propTypes = {
data: PropTypes.array,
showText: PropTypes.bool,
selectedTreeNode: PropTypes.array,
onClick: PropTypes.func,
onClickBadge: PropTypes.func,
};
export default inject(({ auth, treeFoldersStore }) => {
const { treeFolders, selectedTreeNode } = treeFoldersStore;
return {
showText: auth.settingsStore.showText,
data: treeFolders,
selectedTreeNode,
};
})(
withTranslation(['Home', 'Common'])(
withLoader(observer(Items))(<Loaders.DocumentCatalogFolderLoader />),
),
);

View File

@ -0,0 +1,95 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import CatalogItem from '@appserver/components/catalog-item';
import { inject, observer } from 'mobx-react';
import { withTranslation } from 'react-i18next';
import { combineUrl } from '@appserver/common/utils';
import config from '../../../../package.json';
import { AppServerConfig } from '@appserver/common/constants';
import withLoader from '../../../HOCs/withLoader';
import { isTablet } from '@appserver/components/utils/device';
const PureSettingsItems = ({
match,
expandedSetting,
setSelectedNode,
setExpandSettingsTree,
setSelectedFolder,
history,
setIsLoading,
t,
showText,
homepage,
setShowText,
}) => {
const { setting } = match.params;
const iconUrl = '/static/images/settings.react.svg';
React.useEffect(() => {
setIsLoading(true);
setSelectedNode([setting]);
setIsLoading(false);
}, [setting, setIsLoading, setSelectedNode]);
React.useEffect(() => {
const { setting } = match.params;
if (setting && !expandedSetting) setExpandSettingsTree(['settings']);
}, [match, expandedSetting, setExpandSettingsTree]);
const onClick = () => {
setSelectedFolder(null);
setSelectedNode(['common']);
if (!expandedSetting || expandedSetting[0] !== 'settings') setExpandSettingsTree(`settings`);
if (isTablet()) setShowText(false);
return history.push(combineUrl(AppServerConfig.proxyURL, homepage, '/settings/common'));
// if (selectedTreeNode[0] !== path) {
// setSelectedNode(section);
// return history.push(
// combineUrl(AppServerConfig.proxyURL, config.homepage, `/settings/${path}`),
// );
// }
};
const isActive = () => {
return window.location.pathname.indexOf('/settings') > 0;
};
return (
<CatalogItem
id="settings"
key="settings"
text={t('Common:Settings')}
icon={iconUrl}
showText={showText}
onClick={onClick}
isActive={isActive()}
/>
);
};
const SettingsItems = withTranslation(['Settings', 'Common'])(
withRouter(withLoader(PureSettingsItems)(<></>)),
);
export default inject(
({ auth, filesStore, settingsStore, treeFoldersStore, selectedFolderStore }) => {
const { setIsLoading } = filesStore;
const { setSelectedFolder } = selectedFolderStore;
const { selectedTreeNode, setSelectedNode } = treeFoldersStore;
const { expandedSetting, setExpandSettingsTree } = settingsStore;
return {
selectedTreeNode,
expandedSetting,
setIsLoading,
setSelectedFolder,
setSelectedNode,
setExpandSettingsTree,
showText: auth.settingsStore.showText,
setShowText: auth.settingsStore.setShowText,
homepage: config.homepage,
};
},
)(observer(SettingsItems));

View File

@ -0,0 +1,276 @@
import React from "react";
import styled from "styled-components";
import Link from "@appserver/components/link";
import { withTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
import { combineUrl } from "@appserver/common/utils";
import { AppServerConfig } from "@appserver/common/constants";
import config from "../../../../package.json";
import withLoader from "../../../HOCs/withLoader";
import { useCallback } from "react";
import IconButton from "@appserver/components/icon-button";
import { connectedCloudsTitleTranslation } from "../../../helpers/utils";
const StyledThirdParty = styled.div`
margin-top: 42px;
.tree-thirdparty-list {
padding-top: 3px;
display: flex;
max-width: inherit;
.icon {
padding: 5px;
}
div {
height: 25px;
width: 25px;
//background: #eceef1;
//text-align: center;
margin-right: 10px;
color: #818b91;
:first-of-type {
border-radius: 3px 0 0 3px;
}
:last-of-type {
border-radius: 0 3px 3px 0;
}
@media (max-width: 1024px) {
height: 32px;
:first-of-type {
border-radius: 3px 0 0 3px;
}
:last-of-type {
border-radius: 0 3px 3px 0;
padding-right: 5px;
img {
margin-top: 7px;
}
}
img {
padding: 7px 4px 0 4px;
}
}
&:hover {
cursor: pointer;
}
}
}
`;
const iconButtonProps = {
color: "#A3A9AE",
hoverColor: "#818b91",
size: 25,
className: "icon",
};
const ServiceItem = (props) => {
const { capability, src, ...rest } = props;
const capabilityName = capability[0];
const capabilityLink = capability.length > 1 ? capability[1] : "";
const dataProps = {
"data-link": capabilityLink,
"data-title": capabilityName,
"data-key": capabilityName,
};
return (
<div {...dataProps} {...rest}>
<IconButton iconName={src} {...iconButtonProps} />
</div>
);
};
const PureThirdPartyListContainer = ({
t,
googleConnectItem,
boxConnectItem,
dropboxConnectItem,
oneDriveConnectItem,
nextCloudConnectItem,
//webDavConnectItem,
setConnectItem,
setConnectDialogVisible,
setSelectedNode,
setSelectedFolder,
getOAuthToken,
openConnectWindow,
setThirdPartyDialogVisible,
history,
}) => {
const redirectAction = () => {
const thirdPartyUrl = "/settings/thirdParty";
if (history.location.pathname.indexOf(thirdPartyUrl) === -1) {
setSelectedNode(["thirdParty"]);
setSelectedFolder(null);
return history.push(
combineUrl(AppServerConfig.proxyURL, config.homepage, thirdPartyUrl)
);
}
};
const onConnect = (e) => {
const data = e.currentTarget.dataset;
if (data.link) {
let authModal = window.open(
"",
"Authorization",
"height=600, width=1020"
);
openConnectWindow(data.title, authModal)
.then(() => redirectAction())
.then((modal) =>
getOAuthToken(modal).then((token) => {
authModal.close();
const serviceData = {
title: connectedCloudsTitleTranslation(data.title, t),
provider_key: data.title,
link: data.link,
token,
};
setConnectItem(serviceData);
setConnectDialogVisible(true);
})
)
.catch((e) => console.error(e));
} else {
data.title = connectedCloudsTitleTranslation(data.title, t);
setConnectItem(data);
setConnectDialogVisible(true);
redirectAction();
}
};
const onShowConnectPanel = useCallback(() => {
setThirdPartyDialogVisible(true);
redirectAction();
}, []);
return (
<StyledThirdParty>
<Link
color="#555F65"
fontSize="14px"
fontWeight={600}
onClick={onShowConnectPanel}
>
{t("Translations:AddAccount")}
</Link>
<div className="tree-thirdparty-list">
{googleConnectItem && (
<ServiceItem
capability={googleConnectItem}
src="images/services/google_drive.svg"
onClick={onConnect}
/>
)}
{boxConnectItem && (
<ServiceItem
capability={boxConnectItem}
src="images/services/box.svg"
onClick={onConnect}
/>
)}
{dropboxConnectItem && (
<ServiceItem
capability={dropboxConnectItem}
src="images/services/dropbox.svg"
onClick={onConnect}
/>
)}
{oneDriveConnectItem && (
<ServiceItem
capability={oneDriveConnectItem}
src="images/services/onedrive.svg"
onClick={onConnect}
/>
)}
{nextCloudConnectItem && (
<ServiceItem
capability={nextCloudConnectItem}
src="images/services/nextcloud.svg"
onClick={onConnect}
/>
)}
{/* {webDavConnectItem && (
<ServiceItem
capability={webDavConnectItem}
src="images/services/more.svg"
onClick={onConnect}
/>
)} */}
<IconButton
iconName="images/services/more.svg"
onClick={onShowConnectPanel}
{...iconButtonProps}
/>
</div>
</StyledThirdParty>
);
};
const ThirdPartyList = withTranslation(["Article", "Translations"])(
withRouter(withLoader(PureThirdPartyListContainer)(<></>))
);
export default inject(
({
filesStore,
auth,
settingsStore,
treeFoldersStore,
selectedFolderStore,
dialogsStore,
}) => {
const { setIsLoading } = filesStore;
const { setSelectedFolder } = selectedFolderStore;
const { setSelectedNode } = treeFoldersStore;
const {
googleConnectItem,
boxConnectItem,
dropboxConnectItem,
oneDriveConnectItem,
nextCloudConnectItem,
webDavConnectItem,
openConnectWindow,
} = settingsStore.thirdPartyStore;
const { getOAuthToken } = auth.settingsStore;
const {
setConnectItem,
setConnectDialogVisible,
setThirdPartyDialogVisible,
} = dialogsStore;
return {
googleConnectItem,
boxConnectItem,
dropboxConnectItem,
oneDriveConnectItem,
nextCloudConnectItem,
webDavConnectItem,
setIsLoading,
setSelectedFolder,
setSelectedNode,
setConnectItem,
setConnectDialogVisible,
getOAuthToken,
openConnectWindow,
setThirdPartyDialogVisible,
};
}
)(observer(ThirdPartyList));

View File

@ -0,0 +1,130 @@
import React from 'react';
import styled from 'styled-components';
import { inject, observer } from 'mobx-react';
import { withRouter } from 'react-router';
import { setDocumentTitle } from '../../../helpers/utils';
import config from '../../../../package.json';
import { AppServerConfig } from '@appserver/common/constants';
import Items from './Items';
import { tablet } from '@appserver/components/utils/device';
import FilesFilter from '@appserver/common/api/files/filter';
import SettingsItems from './SettingsItems';
import { combineUrl } from '@appserver/common/utils';
import { isDesktop, isTablet } from 'react-device-detect';
import ThirdPartyList from './ThirdPartyList';
import DownloadAppList from './DownloadAppList';
import Banner from './Banner';
const StyledBlock = styled.div`
padding: 0 20px;
@media ${tablet} {
padding: ${(props) => (props.showText ? '0 16px' : 0)};
}
`;
const CatalogBodyContent = (props) => {
const {
personal,
firstLoad,
showText,
isDesktopClient,
enableThirdParty,
isVisitor,
campaigns,
FirebaseHelper,
} = props;
const onClick = React.useCallback((data) => {
const {
setShowText,
setIsLoading,
setSelectedNode,
fetchFiles,
homepage,
history,
setFirstLoad,
} = props;
setSelectedNode(data);
setIsLoading(true);
if (window.location.pathname.indexOf('/filter') > 0) {
fetchFiles(data, null, true, false)
.then(() => !isDesktop && setShowText(false))
.catch((err) => toastr.error(err))
.finally(() => setIsLoading(false));
} else {
setFirstLoad(true);
const filter = FilesFilter.getDefault();
filter.folder = data;
const urlFilter = filter.toUrlParams();
history.push(combineUrl(AppServerConfig.proxyURL, homepage, `/filter?${urlFilter}`));
}
}, []);
const onShowNewFilesPanel = React.useCallback((folderId) => {
props.setNewFilesPanelVisible(true, [`${folderId}`]);
}, []);
return (
<>
<Items onClick={onClick} onBadgeClick={onShowNewFilesPanel} />
{!personal && !firstLoad && <SettingsItems />}
{!isDesktopClient && showText && (
<StyledBlock showText={showText}>
{enableThirdParty && !isVisitor && <ThirdPartyList />}
<DownloadAppList />
{(isDesktop || isTablet) && personal && !firstLoad && campaigns.length > 0 && (
<Banner FirebaseHelper={FirebaseHelper} />
)}
</StyledBlock>
)}
</>
);
};
export default inject(
({ auth, filesStore, treeFoldersStore, selectedFolderStore, dialogsStore, settingsStore }) => {
const { fetchFiles, setIsLoading, setFirstLoad, firstLoad } = filesStore;
const { treeFolders, setSelectedNode, setTreeFolders } = treeFoldersStore;
const { setNewFilesPanelVisible } = dialogsStore;
const {
showText,
setShowText,
personal,
hideArticle,
isDesktopClient,
FirebaseHelper,
} = auth.settingsStore;
const selectedFolderTitle = selectedFolderStore.title;
selectedFolderTitle ? setDocumentTitle(selectedFolderTitle) : setDocumentTitle();
return {
treeFolders,
showText,
setShowText,
enableThirdParty: settingsStore.enableThirdParty,
isVisitor: auth.userStore.user.isVisitor,
homepage: config.homepage,
personal,
setIsLoading,
setFirstLoad,
fetchFiles,
setSelectedNode,
setTreeFolders,
setNewFilesPanelVisible,
hideArticle,
firstLoad,
isDesktopClient,
FirebaseHelper,
};
},
)(observer(withRouter(CatalogBodyContent)));

View File

@ -0,0 +1,13 @@
import React from 'react';
import Loaders from '@appserver/common/components/Loaders';
import { inject, observer } from 'mobx-react';
const CatalogHeaderContent = ({ currentModuleName }) => {
return currentModuleName ? <>{currentModuleName}</> : <Loaders.ArticleHeader />;
};
export default inject(({ auth }) => {
return {
currentModuleName: (auth.product && auth.product.title) || '',
};
})(observer(CatalogHeaderContent));

View File

@ -0,0 +1,167 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import MainButton from '@appserver/components/main-button';
import DropDownItem from '@appserver/components/drop-down-item';
import { withTranslation } from 'react-i18next';
import { isMobile } from 'react-device-detect';
import Loaders from '@appserver/common/components/Loaders';
import { FileAction, AppServerConfig } from '@appserver/common/constants';
import { encryptionUploadDialog } from '../../../helpers/desktop';
import { inject, observer } from 'mobx-react';
import config from '../../../../package.json';
import { combineUrl } from '@appserver/common/utils';
import withLoader from '../../../HOCs/withLoader';
class CatalogMainButtonContent extends React.Component {
onCreate = (e) => {
// this.goToHomePage();
const format = e.currentTarget.dataset.format || null;
this.props.setAction({
type: FileAction.Create,
extension: format,
id: -1,
});
};
onUploadFileClick = () => {
if (this.props.isPrivacy) {
encryptionUploadDialog((encryptedFile, encrypted) => {
const { startUpload, t } = this.props;
encryptedFile.encrypted = encrypted;
this.goToHomePage();
startUpload([encryptedFile], null, t);
});
} else {
this.inputFilesElement.click();
}
};
onUploadFolderClick = () => this.inputFolderElement.click();
goToHomePage = () => {
const { homepage, history, filter } = this.props;
const urlFilter = filter.toUrlParams();
history.push(combineUrl(AppServerConfig.proxyURL, homepage, `/filter?${urlFilter}`));
};
onFileChange = (e) => {
const { startUpload, t } = this.props;
//this.goToHomePage();
startUpload(e.target.files, null, t);
};
onInputClick = (e) => (e.target.value = null);
// shouldComponentUpdate(nextProps, nextState) {
// return (
// nextProps.canCreate !== this.props.canCreate ||
// nextProps.firstLoad !== this.props.firstLoad ||
// nextProps.isPrivacy !== this.props.isPrivacy
// );
// }
render() {
//console.log("Files ArticleMainButtonContent render");
const { t, tReady, canCreate, isDisabled, firstLoad, isPrivacy } = this.props;
return (
<MainButton
isDisabled={isDisabled ? isDisabled : !canCreate}
isDropdown={true}
text={t('Common:Actions')}>
<DropDownItem
className="main-button_drop-down"
icon="images/actions.documents.react.svg"
label={t('NewDocument')}
onClick={this.onCreate}
data-format="docx"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/spreadsheet.react.svg"
label={t('NewSpreadsheet')}
onClick={this.onCreate}
data-format="xlsx"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/actions.presentation.react.svg"
label={t('NewPresentation')}
onClick={this.onCreate}
data-format="pptx"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/catalog.folder.react.svg"
label={t('NewFolder')}
onClick={this.onCreate}
/>
<DropDownItem isSeparator />
<DropDownItem
className="main-button_drop-down"
icon="images/actions.upload.react.svg"
label={t('UploadFiles')}
onClick={this.onUploadFileClick}
/>
{!isMobile && (
<DropDownItem
className="main-button_drop-down"
icon="images/actions.upload.react.svg"
label={t('UploadFolder')}
disabled={isPrivacy}
onClick={this.onUploadFolderClick}
/>
)}
<input
id="customFileInput"
className="custom-file-input"
multiple
type="file"
onChange={this.onFileChange}
onClick={this.onInputClick}
ref={(input) => (this.inputFilesElement = input)}
style={{ display: 'none' }}
/>
<input
id="customFolderInput"
className="custom-file-input"
webkitdirectory=""
mozdirectory=""
type="file"
onChange={this.onFileChange}
onClick={this.onInputClick}
ref={(input) => (this.inputFolderElement = input)}
style={{ display: 'none' }}
/>
</MainButton>
);
}
}
CatalogMainButtonContent.propTypes = {
history: PropTypes.object.isRequired,
};
export default inject(({ filesStore, uploadDataStore, treeFoldersStore }) => {
const { firstLoad, fileActionStore, filter, canCreate } = filesStore;
const { isPrivacyFolder } = treeFoldersStore;
const { startUpload } = uploadDataStore;
return {
homepage: config.homepage,
firstLoad,
isPrivacy: isPrivacyFolder,
filter,
canCreate,
setAction: fileActionStore.setAction,
startUpload,
};
})(
withRouter(
withTranslation(['Article', 'Common'])(
withLoader(observer(CatalogMainButtonContent))(<Loaders.MainButton />),
),
),
);

View File

@ -0,0 +1,3 @@
export { default as CatalogHeaderContent } from './Header';
export { default as CatalogBodyContent } from './Body';
export { default as CatalogMainButtonContent } from './MainButton';

View File

@ -1,32 +1,38 @@
import React from "react";
import React from 'react';
//import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { isMobile } from "react-device-detect";
import axios from "axios";
import toastr from "studio/toastr";
import PageLayout from "@appserver/common/components/PageLayout";
import { showLoader, hideLoader } from "@appserver/common/utils";
import FilesFilter from "@appserver/common/api/files/filter";
import { getGroup } from "@appserver/common/api/groups";
import { getUserById } from "@appserver/common/api/people";
import { withTranslation, Trans } from "react-i18next";
import { withRouter } from 'react-router';
import { isMobile } from 'react-device-detect';
import axios from 'axios';
import toastr from 'studio/toastr';
import PageLayout from '@appserver/common/components/PageLayout';
import { showLoader, hideLoader } from '@appserver/common/utils';
import FilesFilter from '@appserver/common/api/files/filter';
import { getGroup } from '@appserver/common/api/groups';
import { getUserById } from '@appserver/common/api/people';
import { withTranslation, Trans } from 'react-i18next';
import {
ArticleBodyContent,
ArticleHeaderContent,
ArticleMainButtonContent,
} from "../../components/Article";
} from '../../components/Article';
import {
CatalogBodyContent,
CatalogHeaderContent,
CatalogMainButtonContent,
} from '../../components/Catalog';
import {
SectionBodyContent,
SectionFilterContent,
SectionHeaderContent,
SectionPagingContent,
} from "./Section";
} from './Section';
import { createTreeFolders } from "../../helpers/files-helpers";
import MediaViewer from "./MediaViewer";
import DragTooltip from "../../components/DragTooltip";
import { observer, inject } from "mobx-react";
import config from "../../../package.json";
import { createTreeFolders } from '../../helpers/files-helpers';
import MediaViewer from './MediaViewer';
import DragTooltip from '../../components/DragTooltip';
import { observer, inject } from 'mobx-react';
import config from '../../../package.json';
class PureHome extends React.Component {
componentDidMount() {
@ -42,19 +48,17 @@ class PureHome extends React.Component {
getFileInfo,
} = this.props;
const reg = new RegExp(`${homepage}((/?)$|/filter)`, "gm"); //TODO: Always find?
const reg = new RegExp(`${homepage}((/?)$|/filter)`, 'gm'); //TODO: Always find?
const match = window.location.pathname.match(reg);
let filterObj = null;
if (window.location.href.indexOf("/files/#preview") > 1) {
if (window.location.href.indexOf('/files/#preview') > 1) {
const pathname = window.location.href;
const fileId = pathname.slice(pathname.indexOf("#preview") + 9);
const fileId = pathname.slice(pathname.indexOf('#preview') + 9);
getFileInfo(fileId)
.then((data) => {
const canOpenPlayer = mediaViewersFormatsStore.isMediaOrImage(
data.fileExst
);
const canOpenPlayer = mediaViewersFormatsStore.isMediaOrImage(data.fileExst);
const file = { ...data, canOpenPlayer };
setToPreviewFile(file, true);
})
@ -83,7 +87,7 @@ class PureHome extends React.Component {
if (filterObj && filterObj.authorType) {
const authorType = filterObj.authorType;
const indexOfUnderscore = authorType.indexOf("_");
const indexOfUnderscore = authorType.indexOf('_');
const type = authorType.slice(0, indexOfUnderscore);
const itemId = authorType.slice(indexOfUnderscore + 1);
@ -105,9 +109,9 @@ class PureHome extends React.Component {
const newFilter = filter ? filter.clone() : FilesFilter.getDefault();
const requests = [Promise.resolve(newFilter)];
if (type === "group") {
if (type === 'group') {
requests.push(getGroup(itemId));
} else if (type === "user") {
} else if (type === 'user') {
requests.push(getUserById(itemId));
}
@ -117,16 +121,16 @@ class PureHome extends React.Component {
.all(requests)
.catch((err) => {
Promise.resolve(FilesFilter.getDefault());
console.warn("Filter restored by default", err);
console.warn('Filter restored by default', err);
})
.then((data) => {
const filter = data[0];
const result = data[1];
if (result) {
const type = result.displayName ? "user" : "group";
const type = result.displayName ? 'user' : 'group';
const selectedItem = {
key: result.id,
label: type === "user" ? result.displayName : result.name,
label: type === 'user' ? result.displayName : result.name,
type,
};
filter.selectedItem = selectedItem;
@ -154,7 +158,7 @@ class PureHome extends React.Component {
fetchDefaultFiles = () => {
const { isVisitor, fetchFiles, setIsLoading, setFirstLoad } = this.props;
const filterObj = FilesFilter.getDefault();
const folderId = isVisitor ? "@common" : filterObj.folder;
const folderId = isVisitor ? '@common' : filterObj.folder;
fetchFiles(folderId).finally(() => {
setIsLoading(false);
@ -171,31 +175,31 @@ class PureHome extends React.Component {
showOperationToast = (type, qty, title) => {
const { t } = this.props;
switch (type) {
case "move":
case 'move':
if (qty > 1) {
return toastr.success(
<Trans t={t} i18nKey="MoveItems" ns="Home">
{{ qty }} elements has been moved
</Trans>
</Trans>,
);
}
return toastr.success(
<Trans t={t} i18nKey="MoveItem" ns="Home">
{{ title }} moved
</Trans>
</Trans>,
);
case "duplicate":
case 'duplicate':
if (qty > 1) {
return toastr.success(
<Trans t={t} i18nKey="CopyItems" ns="Home">
{{ qty }} elements copied
</Trans>
</Trans>,
);
}
return toastr.success(
<Trans t={t} i18nKey="CopyItem" ns="Home">
{{ title }} copied
</Trans>
</Trans>,
);
default:
break;
@ -213,8 +217,7 @@ class PureHome extends React.Component {
} = this.props;
setUploadPanelVisible(!uploadPanelVisible);
if (primaryProgressDataVisible && uploaded && converted)
clearPrimaryProgressData();
if (primaryProgressDataVisible && uploaded && converted) clearPrimaryProgressData();
};
componentDidUpdate(prevProps) {
const {
@ -227,15 +230,8 @@ class PureHome extends React.Component {
if (this.props.isHeaderVisible !== prevProps.isHeaderVisible) {
this.props.setHeaderVisible(this.props.isHeaderVisible);
}
if (
isProgressFinished &&
isProgressFinished !== prevProps.isProgressFinished
) {
this.showOperationToast(
secondaryProgressDataStoreIcon,
selectionLength,
selectionTitle
);
if (isProgressFinished && isProgressFinished !== prevProps.isProgressFinished) {
this.showOperationToast(secondaryProgressDataStoreIcon, selectionLength, selectionTitle);
}
}
@ -261,6 +257,8 @@ class PureHome extends React.Component {
dragging,
tReady,
showCatalog,
} = this.props;
return (
<>
@ -283,27 +281,33 @@ class PureHome extends React.Component {
showSecondaryButtonAlert={secondaryProgressDataStoreAlert}
viewAs={viewAs}
hideAside={
!!fileActionId ||
primaryProgressDataVisible ||
secondaryProgressDataStoreVisible
!!fileActionId || primaryProgressDataVisible || secondaryProgressDataStoreVisible
}
isLoaded={!firstLoad}
isHeaderVisible={isHeaderVisible}
onOpenUploadPanel={this.showUploadPanel}
firstLoad={firstLoad}
dragging={dragging}
>
dragging={dragging}>
<PageLayout.ArticleHeader>
<ArticleHeaderContent />
</PageLayout.ArticleHeader>
<PageLayout.ArticleMainButton>
<ArticleMainButtonContent />
</PageLayout.ArticleMainButton>
<PageLayout.ArticleBody>
<ArticleBodyContent onTreeDrop={this.onDrop} />
</PageLayout.ArticleBody>
<PageLayout.CatalogHeader>
<CatalogHeaderContent />
</PageLayout.CatalogHeader>
<PageLayout.CatalogMainButton>
<CatalogMainButtonContent />
</PageLayout.CatalogMainButton>
<PageLayout.CatalogBody>
<CatalogBodyContent />
</PageLayout.CatalogBody>
<PageLayout.SectionHeader>
<SectionHeaderContent />
</PageLayout.SectionHeader>
@ -325,21 +329,11 @@ class PureHome extends React.Component {
}
}
const Home = withTranslation("Home")(PureHome);
const Home = withTranslation('Home')(PureHome);
export default inject(
({
auth,
filesStore,
uploadDataStore,
treeFoldersStore,
mediaViewerDataStore,
formatsStore,
}) => {
const {
secondaryProgressDataStore,
primaryProgressDataStore,
} = uploadDataStore;
({ auth, filesStore, uploadDataStore, treeFoldersStore, mediaViewerDataStore, formatsStore }) => {
const { secondaryProgressDataStore, primaryProgressDataStore } = uploadDataStore;
const {
firstLoad,
setFirstLoad,
@ -358,12 +352,7 @@ export default inject(
const { mediaViewersFormatsStore } = formatsStore;
const { id } = fileActionStore;
const {
isRecycleBinFolder,
isPrivacyFolder,
expandedKeys,
setExpandedKeys,
} = treeFoldersStore;
const { isRecycleBinFolder, isPrivacyFolder, expandedKeys, setExpandedKeys } = treeFoldersStore;
const {
visible: primaryProgressDataVisible,
@ -381,17 +370,10 @@ export default inject(
isSecondaryProgressFinished: isProgressFinished,
} = secondaryProgressDataStore;
const {
setUploadPanelVisible,
startUpload,
uploaded,
converted,
} = uploadDataStore;
const { setUploadPanelVisible, startUpload, uploaded, converted } = uploadDataStore;
const selectionLength = isProgressFinished ? selection.length : null;
const selectionTitle = isProgressFinished
? filesStore.selectionTitle
: null;
const selectionTitle = isProgressFinished ? filesStore.selectionTitle : null;
const { setToPreviewFile } = mediaViewerDataStore;
if (!firstLoad) {
@ -443,6 +425,7 @@ export default inject(
setToPreviewFile,
mediaViewersFormatsStore,
getFileInfo,
showCatalog: auth.settingsStore.showCatalog,
};
}
},
)(withRouter(observer(Home)));

View File

@ -1,27 +1,25 @@
import React, { useEffect, useState } from "react";
import { withRouter } from "react-router";
import PageLayout from "@appserver/common/components/PageLayout";
import Loaders from "@appserver/common/components/Loaders";
import { showLoader, hideLoader } from "@appserver/common/utils";
import React, { useEffect, useState } from 'react';
import { withRouter } from 'react-router';
import PageLayout from '@appserver/common/components/PageLayout';
import Loaders from '@appserver/common/components/Loaders';
import { showLoader, hideLoader } from '@appserver/common/utils';
import {
ArticleHeaderContent,
ArticleBodyContent,
ArticleMainButtonContent,
} from "../../components/Article";
import { SectionHeaderContent, SectionBodyContent } from "./Section";
import { withTranslation } from "react-i18next";
import { setDocumentTitle } from "../../helpers/utils";
import { inject, observer } from "mobx-react";
} from '../../components/Article';
import {
CatalogBodyContent,
CatalogHeaderContent,
CatalogMainButtonContent,
} from '../../components/Catalog';
import { SectionHeaderContent, SectionBodyContent } from './Section';
import { withTranslation } from 'react-i18next';
import { setDocumentTitle } from '../../helpers/utils';
import { inject, observer } from 'mobx-react';
const PureSettings = ({
match,
t,
isLoading,
isLoadedSettingsTree,
setFirstLoad,
tReady,
}) => {
const [title, setTitle] = useState("");
const PureSettings = ({ match, t, isLoading, isLoadedSettingsTree, setFirstLoad, tReady }) => {
const [title, setTitle] = useState('');
const { setting } = match.params;
useEffect(() => {
@ -30,17 +28,17 @@ const PureSettings = ({
useEffect(() => {
switch (setting) {
case "common":
setTitle(t("CommonSettings"));
case 'common':
setTitle(t('CommonSettings'));
break;
case "admin":
setTitle(t("Common:AdminSettings"));
case 'admin':
setTitle(t('Common:AdminSettings'));
break;
case "thirdParty":
setTitle(t("ThirdPartySettings"));
case 'thirdParty':
setTitle(t('ThirdPartySettings'));
break;
default:
setTitle(t("CommonSettings"));
setTitle(t('CommonSettings'));
break;
}
}, [setting, t, tReady]);
@ -74,6 +72,16 @@ const PureSettings = ({
<ArticleBodyContent />
</PageLayout.ArticleBody>
<PageLayout.CatalogHeader>
<CatalogHeaderContent />
</PageLayout.CatalogHeader>
<PageLayout.CatalogMainButton>
<CatalogMainButtonContent />
</PageLayout.CatalogMainButton>
<PageLayout.CatalogBody>
<CatalogBodyContent />
</PageLayout.CatalogBody>
<PageLayout.SectionHeader>
{(!isLoadedSettingsTree && isLoading) || isLoading || !tReady ? (
<Loaders.SectionHeader />
@ -84,7 +92,7 @@ const PureSettings = ({
<PageLayout.SectionBody>
{(!isLoadedSettingsTree && isLoading) || isLoading || !tReady ? (
setting === "thirdParty" ? (
setting === 'thirdParty' ? (
<Loaders.Rows />
) : (
<Loaders.SettingsFiles />
@ -98,7 +106,7 @@ const PureSettings = ({
);
};
const Settings = withTranslation(["Settings", "Common"])(PureSettings);
const Settings = withTranslation(['Settings', 'Common'])(PureSettings);
export default inject(({ filesStore, settingsStore, treeFoldersStore }) => {
const { setFirstLoad, isLoading } = filesStore;

View File

@ -0,0 +1,39 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { mobile } from '@appserver/components/utils/device';
import MenuIcon from '@appserver/components/public/static/images/menu.react.svg';
const StyledIconBox = styled.div`
display: none;
@media ${mobile} {
display: ${(props) => (props.isProduct && props.showCatalog ? 'flex' : 'none')};
align-items: center;
}
`;
const StyledMenuIcon = styled(MenuIcon)`
width: 20px;
height: 20px;
fill: #657077;
cursor: pointer;
`;
const HeaderCatalogBurger = (props) => {
const { isProduct, showCatalog, onClick, ...rest } = props;
return (
<StyledIconBox isProduct={isProduct} showCatalog={showCatalog} onClick={onClick} {...rest}>
<StyledMenuIcon />
</StyledIconBox>
);
};
HeaderCatalogBurger.propTypes = {
isProduct: PropTypes.bool,
onClick: PropTypes.func,
showCatalog: PropTypes.bool,
};
export default React.memo(HeaderCatalogBurger);

View File

@ -1,28 +1,26 @@
import React, { useCallback, useState } from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import NavItem from "./nav-item";
import ProfileActions from "./profile-actions";
import { useTranslation } from "react-i18next";
import { tablet } from "@appserver/components/utils/device";
import { combineUrl, deleteCookie } from "@appserver/common/utils";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
import { AppServerConfig } from "@appserver/common/constants";
import config from "../../../../package.json";
import { isDesktop } from "react-device-detect";
import AboutDialog from "../../pages/About/AboutDialog";
import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import NavItem from './nav-item';
import ProfileActions from './profile-actions';
import { useTranslation } from 'react-i18next';
import { tablet } from '@appserver/components/utils/device';
import { combineUrl, deleteCookie } from '@appserver/common/utils';
import { inject, observer } from 'mobx-react';
import { withRouter } from 'react-router';
import { AppServerConfig } from '@appserver/common/constants';
import config from '../../../../package.json';
import { isDesktop, isMobile } from 'react-device-detect';
import AboutDialog from '../../pages/About/AboutDialog';
import HeaderCatalogBurger from './header-catalog-burger';
const { proxyURL } = AppServerConfig;
const homepage = config.homepage;
const PROXY_HOMEPAGE_URL = combineUrl(proxyURL, homepage);
const ABOUT_URL = combineUrl(PROXY_HOMEPAGE_URL, "/about");
const PROFILE_SELF_URL = combineUrl(
PROXY_HOMEPAGE_URL,
"/products/people/view/@self"
);
const PROFILE_MY_URL = combineUrl(PROXY_HOMEPAGE_URL, "/my");
const ABOUT_URL = combineUrl(PROXY_HOMEPAGE_URL, '/about');
const PROFILE_SELF_URL = combineUrl(PROXY_HOMEPAGE_URL, '/products/people/view/@self');
const PROFILE_MY_URL = combineUrl(PROXY_HOMEPAGE_URL, '/my');
const StyledNav = styled.nav`
display: flex;
@ -66,14 +64,15 @@ const HeaderNav = ({
versionAppServer,
userIsUpdate,
setUserIsUpdate,
currentProductId,
toggleShowText,
showCatalog,
}) => {
const { t } = useTranslation(["NavMenu", "Common", "About"]);
const { t } = useTranslation(['NavMenu', 'Common', 'About']);
const [visibleDialog, setVisibleDialog] = useState(false);
const onProfileClick = useCallback(() => {
peopleAvailable
? history.push(PROFILE_SELF_URL)
: history.push(PROFILE_MY_URL);
peopleAvailable ? history.push(PROFILE_SELF_URL) : history.push(PROFILE_MY_URL);
}, []);
const onAboutClick = useCallback(() => {
@ -87,13 +86,8 @@ const HeaderNav = ({
const onCloseDialog = () => setVisibleDialog(false);
const onSwitchToDesktopClick = useCallback(() => {
deleteCookie("desktop_view");
window.open(
`${window.location.origin}?desktop_view=true`,
"_self",
"",
true
);
deleteCookie('desktop_view');
window.open(`${window.location.origin}?desktop_view=true`, '_self', '', true);
}, []);
const onLogoutClick = useCallback(() => logout && logout(), [logout]);
@ -101,34 +95,33 @@ const HeaderNav = ({
const getCurrentUserActions = useCallback(() => {
return [
{
key: "ProfileBtn",
label: t("Common:Profile"),
key: 'ProfileBtn',
label: t('Common:Profile'),
onClick: onProfileClick,
url: peopleAvailable ? PROFILE_SELF_URL : PROFILE_MY_URL,
},
{
key: "SwitchToBtn",
key: 'SwitchToBtn',
...(!isPersonal && {
label: t("TurnOnDesktopVersion"),
label: t('TurnOnDesktopVersion'),
onClick: onSwitchToDesktopClick,
url: `${window.location.origin}?desktop_view=true`,
target: "_self",
target: '_self',
}),
},
{
key: "AboutBtn",
label: t("AboutCompanyTitle"),
key: 'AboutBtn',
label: t('AboutCompanyTitle'),
onClick: onAboutClick,
url: ABOUT_URL,
},
{
key: "LogoutBtn",
label: t("LogoutButton"),
key: 'LogoutBtn',
label: t('LogoutButton'),
onClick: onLogoutClick,
},
];
}, [onProfileClick, onAboutClick, onLogoutClick]);
//console.log("HeaderNav render");
return (
<StyledNav className="profileMenuIcon hidingHeader">
@ -145,17 +138,26 @@ const HeaderNav = ({
history.push(m.link);
e.preventDefault();
}}
onBadgeClick={(e) => console.log(m.iconName + "Badge Clicked", e)}
onBadgeClick={(e) => console.log(m.iconName + 'Badge Clicked', e)}
noHover={true}
/>
))}
{isAuthenticated && user ? (
<ProfileActions
userActions={getCurrentUserActions()}
user={user}
userIsUpdate={userIsUpdate}
setUserIsUpdate={setUserIsUpdate}
/>
<>
<ProfileActions
userActions={getCurrentUserActions()}
user={user}
userIsUpdate={userIsUpdate}
setUserIsUpdate={setUserIsUpdate}
isProduct={currentProductId !== 'home'}
showCatalog={showCatalog}
/>
<HeaderCatalogBurger
isProduct={currentProductId !== 'home'}
showCatalog={showCatalog}
onClick={toggleShowText}
/>
</>
) : (
<></>
)}
@ -171,7 +173,7 @@ const HeaderNav = ({
);
};
HeaderNav.displayName = "HeaderNav";
HeaderNav.displayName = 'HeaderNav';
HeaderNav.propTypes = {
history: PropTypes.object,
@ -180,22 +182,20 @@ HeaderNav.propTypes = {
logout: PropTypes.func,
isAuthenticated: PropTypes.bool,
isLoaded: PropTypes.bool,
currentProductId: PropTypes.string,
toggleShowText: PropTypes.func,
};
export default withRouter(
inject(({ auth }) => {
const {
settingsStore,
userStore,
isAuthenticated,
isLoaded,
language,
logout,
} = auth;
const { settingsStore, userStore, isAuthenticated, isLoaded, language, logout } = auth;
const {
defaultPage,
personal: isPersonal,
version: versionAppServer,
currentProductId,
toggleShowText,
showCatalog,
} = settingsStore;
const { user, userIsUpdate, setUserIsUpdate } = userStore;
const modules = auth.availableModules;
@ -205,13 +205,16 @@ export default withRouter(
isAuthenticated,
isLoaded,
language,
defaultPage: defaultPage || "/",
defaultPage: defaultPage || '/',
modules,
logout,
peopleAvailable: modules.some((m) => m.appName === "people"),
peopleAvailable: modules.some((m) => m.appName === 'people'),
versionAppServer,
userIsUpdate,
setUserIsUpdate,
currentProductId,
toggleShowText,
showCatalog,
};
})(observer(HeaderNav))
})(observer(HeaderNav)),
);

View File

@ -1,10 +1,18 @@
import React from "react";
import PropTypes from "prop-types";
import Avatar from "@appserver/components/avatar";
import DropDownItem from "@appserver/components/drop-down-item";
import Link from "@appserver/components/link";
import ProfileMenu from "./profile-menu";
import api from "@appserver/common/api";
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import Avatar from '@appserver/components/avatar';
import DropDownItem from '@appserver/components/drop-down-item';
import Link from '@appserver/components/link';
import ProfileMenu from './profile-menu';
import api from '@appserver/common/api';
import { mobile } from '@appserver/components/utils/device';
const StyledDiv = styled.div`
@media ${mobile} {
display: ${(props) => (props.isProduct && props.showCatalog ? 'none' : 'block')};
}
`;
class ProfileActions extends React.PureComponent {
constructor(props) {
super(props);
@ -14,7 +22,7 @@ class ProfileActions extends React.PureComponent {
this.state = {
opened: props.opened,
user: props.user,
avatar: "",
avatar: '',
};
}
@ -44,10 +52,10 @@ class ProfileActions extends React.PureComponent {
getUserRole = (user) => {
let isModuleAdmin = user.listAdminModules && user.listAdminModules.length;
if (user.isOwner) return "owner";
if (user.isAdmin || isModuleAdmin) return "admin";
if (user.isVisitor) return "guest";
return "user";
if (user.isOwner) return 'owner';
if (user.isAdmin || isModuleAdmin) return 'admin';
if (user.isVisitor) return 'guest';
return 'user';
};
onClose = (e) => {
@ -82,7 +90,10 @@ class ProfileActions extends React.PureComponent {
const userRole = this.getUserRole(user);
return (
<div ref={this.ref}>
<StyledDiv
isProduct={this.props.isProduct}
showCatalog={this.props.showCatalog}
ref={this.ref}>
<Avatar
onClick={this.onClick}
role={userRole}
@ -98,22 +109,20 @@ class ProfileActions extends React.PureComponent {
displayName={user.displayName}
email={user.email}
open={opened}
clickOutsideAction={this.onClose}
>
<div style={{ paddingTop: "8px" }}>
clickOutsideAction={this.onClose}>
<div style={{ paddingTop: '8px' }}>
{this.props.userActions.map((action) => (
<Link
noHover={true}
key={action.key}
href={action.url}
onClick={this.onClickItemLink}
>
onClick={this.onClickItemLink}>
<DropDownItem {...action} />
</Link>
))}
</div>
</ProfileMenu>
</div>
</StyledDiv>
);
}
}
@ -124,6 +133,8 @@ ProfileActions.propTypes = {
userActions: PropTypes.array,
userIsUpdate: PropTypes.bool,
setUserIsUpdate: PropTypes.func,
isProduct: PropTypes.bool,
showCatalog: PropTypes.bool,
};
ProfileActions.defaultProps = {
@ -131,6 +142,7 @@ ProfileActions.defaultProps = {
user: {},
userActions: [],
userIsUpdate: false,
isProduct: false,
};
export default ProfileActions;