Merge branch 'develop' into feature/thumbnails

# Conflicts:
#	products/ASC.Files/Client/src/Files.jsx
#	products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesContent.js
#	products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContainer.js
#	products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/SimpleFilesRow.js
#	products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTileContent.js
#	products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js
#	products/ASC.Files/Client/src/store/InitFilesStore.js
This commit is contained in:
Artem Tarasov 2021-04-05 10:54:27 +03:00
commit 43491f42a9
83 changed files with 3628 additions and 1984 deletions

View File

@ -1,4 +1,4 @@
import { action, computed, makeObservable, observable } from "mobx";
import { makeAutoObservable } from "mobx";
import api from "../api";
import { ARTICLE_PINNED_KEY, LANGUAGE } from "../constants";
import { combineUrl } from "../utils";
@ -69,68 +69,7 @@ class SettingsStore {
hasShortenService = false;
constructor() {
makeObservable(this, {
currentProductId: observable,
culture: observable,
cultures: observable,
trustedDomains: observable,
trustedDomainsType: observable,
timezone: observable,
timezones: observable,
utcOffset: observable,
utcHoursOffset: observable,
defaultPage: observable,
homepage: observable,
datePattern: observable,
datePatternJQ: observable,
dateTimePattern: observable,
datepicker: observable,
organizationName: observable,
greetingSettings: observable,
enableAdmMess: observable,
urlLicense: observable,
urlSupport: observable,
urlAuthKeys: computed,
logoUrl: observable,
customNames: observable,
isDesktopClient: observable,
isEncryptionSupport: observable,
encryptionKeys: observable,
isHeaderVisible: observable,
isTabletView: observable,
isArticlePinned: observable,
hashSettings: observable,
ownerId: observable,
nameSchemaId: observable,
wizardToken: observable,
wizardCompleted: computed,
passwordSettings: observable,
hasShortenService: observable,
getSettings: action,
getCurrentCustomSchema: action,
getPortalSettings: action,
init: action,
isLoaded: observable,
isLoading: observable,
setIsLoading: action,
setIsLoaded: action,
getPortalCultures: action,
getIsEncryptionSupport: action,
updateEncryptionKeys: action,
setEncryptionKeys: action,
getEncryptionKeys: action,
setModuleInfo: action,
setCurrentProductId: action,
setWizardComplete: action,
setPasswordSettings: action,
getPortalPasswordSettings: action,
setTimezones: action,
getPortalTimezones: action,
setHeaderVisible: action,
setIsTabletView: action,
setValue: action,
setArticlePinned: action,
});
makeAutoObservable(this);
}
get urlAuthKeys() {
@ -203,8 +142,13 @@ class SettingsStore {
this.cultures = await api.settings.getPortalCultures();
};
setIsEncryptionSupport = (isEncryptionSupport) => {
this.isEncryptionSupport = isEncryptionSupport;
};
getIsEncryptionSupport = async () => {
this.isEncryptionSupport = await api.files.getIsEncryptionSupport();
const isEncryptionSupport = await api.files.getIsEncryptionSupport();
this.setIsEncryptionSupport(isEncryptionSupport);
};
updateEncryptionKeys = (encryptionKeys) => {

View File

@ -30,8 +30,6 @@ class ContextMenuButton extends React.Component {
isOpen: props.opened,
data: props.data,
displayType,
offsetX: props.manualX,
offsetY: props.manualY,
};
this.throttledResize = throttle(this.resize, 300);
}
@ -73,10 +71,6 @@ class ContextMenuButton extends React.Component {
this.toggle(this.props.opened);
}
if (this.props.manualX !== prevProps.manualX) {
this.onContextClick();
}
if (this.props.opened && this.state.displayType === "aside") {
window.addEventListener("popstate", this.popstate, false);
}
@ -86,20 +80,6 @@ class ContextMenuButton extends React.Component {
}
}
onContextClick = () => {
if (this.props.isDisabled) {
this.stopAction;
return;
}
this.setState({
data: this.props.getData(),
isOpen: !this.state.isOpen,
offsetX: this.props.manualX,
offsetY: this.props.manualY,
});
};
onIconButtonClick = () => {
if (this.props.isDisabled) {
this.stopAction;
@ -110,8 +90,6 @@ class ContextMenuButton extends React.Component {
{
data: this.props.getData(),
isOpen: !this.state.isOpen,
offsetX: "0px",
offsetY: "100%",
},
() =>
!this.props.isDisabled &&
@ -194,15 +172,12 @@ class ContextMenuButton extends React.Component {
/>
{displayType === "dropdown" ? (
<DropDown
id="contextMenu"
manualX={offsetX}
manualY={offsetY}
directionX={directionX}
directionY={directionY}
open={isOpen}
clickOutsideAction={this.clickOutsideAction}
columnCount={columnCount}
withBackdrop={isMobile}
withBackdrop={!!isMobile}
>
{this.state.data.map(
(item, index) =>
@ -299,10 +274,6 @@ ContextMenuButton.propTypes = {
directionX: PropTypes.string,
/** Direction Y */
directionY: PropTypes.string,
/** Manual X padding */
manualX: PropTypes.string,
/** Manual Y padding */
manualY: PropTypes.string,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */

View File

@ -1,14 +1,10 @@
import React from "react";
import React, { useRef } from "react";
import RowContainer from "../row-container";
import RowContent from "../row-content";
import Row from "../row";
import ContextMenu from "./index";
export default {
title: "Components/ContextMenu",
component: ContextMenu,
subcomponents: { RowContainer, Row, RowContent },
parameters: {
docs: {
description: {
@ -24,37 +20,112 @@ In particular case, state is created containing options for particular Row eleme
},
};
const getRndString = (n) =>
Math.random()
.toString(36)
.substring(2, n + 2);
const Template = (args) => {
const cm = useRef(null);
const items = [
{
label: "Edit",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Preview",
icon: "/static/images/catalog.folder.react.svg",
},
{
separator: true,
},
{
label: "Sharing settings",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Link for portal users",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Copy external link",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Send by e-mail",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Version history",
icon: "/static/images/catalog.folder.react.svg",
items: [
{
label: "Show version history",
},
{
label: "Finalize version",
},
{
label: "Unblock / Check-in",
},
],
},
{
separator: true,
},
{
label: "Make as favorite",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Download",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Download as",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Move or copy",
icon: "/static/images/catalog.folder.react.svg",
items: [
{
label: "Move to",
},
{
label: "Copy",
},
{
label: "Duplicate",
},
],
},
{
label: "Rename",
icon: "/static/images/catalog.folder.react.svg",
disabled: true,
},
{
separator: true,
},
{
label: "Quit",
icon: "/static/images/catalog.folder.react.svg",
},
];
const array = Array.from(Array(10).keys());
const Template = (args) => (
<RowContainer {...args} manualHeight="300px">
{array.map((item, index) => {
return (
<Row
key={`${item + 1}`}
contextOptions={
index !== 3
? [
{ key: 1, label: getRndString(5) },
{ key: 2, label: getRndString(5) },
{ key: 3, label: getRndString(5) },
{ key: 4, label: getRndString(5) },
]
: []
}
>
<RowContent>
<span>{getRndString(5)}</span>
<></>
</RowContent>
</Row>
);
})}
</RowContainer>
);
return (
<div>
<ContextMenu model={items} ref={cm}></ContextMenu>
<div
style={{
width: "200px",
height: "200px",
backgroundColor: "red",
display: "inline-block",
}}
onContextMenu={(e) => cm.current.show(e)}
>
{""}
</div>
</div>
);
};
export const Default = Template.bind({});

View File

@ -13,7 +13,7 @@ describe("<ContextMenu />", () => {
expect(wrapper).toExist();
});
it("componentWillUnmount() test unmount", () => {
/* it("componentWillUnmount() test unmount", () => {
const wrapper = mount(<ContextMenu {...baseProps} />);
wrapper.unmount();
@ -82,5 +82,5 @@ describe("<ContextMenu />", () => {
wrapper.setState({ visible: true });
expect(wrapper.getDOMNode().style).toHaveProperty("color", "red");
});
}); */
});

View File

@ -1,134 +1,589 @@
import React from "react";
import React, { Component } from "react";
import PropTypes from "prop-types";
import DropDownItem from "../drop-down-item";
import DropDown from "../drop-down";
import DomHelpers from "../utils/domHelpers";
import ObjectUtils from "../utils/objectUtils";
import { classNames } from "../utils/classNames";
import { CSSTransition } from "react-transition-group";
import { ReactSVG } from "react-svg";
import Portal from "../portal";
import StyledContextMenu from "./styled-context-menu";
import ArrowIcon from "./svg/arrow.right.react.svg";
class ContextMenu extends React.PureComponent {
class ContextMenuSub extends Component {
constructor(props) {
super(props);
this.state = {
activeItem: null,
};
this.onEnter = this.onEnter.bind(this);
this.submenuRef = React.createRef();
}
onItemMouseEnter(e, item) {
if (item.disabled) {
e.preventDefault();
return;
}
this.setState({
activeItem: item,
});
}
onItemClick(e, item) {
if (item.disabled) {
e.preventDefault();
return;
}
if (!item.url) {
e.preventDefault();
}
if (item.onClick) {
item.onClick({
originalEvent: e,
});
}
if (!item.items) {
this.props.onLeafClick(e);
}
}
position() {
const parentItem = this.submenuRef.current.parentElement;
const containerOffset = DomHelpers.getOffset(
this.submenuRef.current.parentElement
);
const viewport = DomHelpers.getViewport();
const sublistWidth = this.submenuRef.current.offsetParent
? this.submenuRef.current.offsetWidth
: DomHelpers.getHiddenElementOuterWidth(this.submenuRef.current);
const itemOuterWidth = DomHelpers.getOuterWidth(parentItem.children[0]);
this.submenuRef.current.style.top = "0px";
if (
parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth >
viewport.width - DomHelpers.calculateScrollbarWidth()
) {
this.submenuRef.current.style.left = -1 * sublistWidth + "px";
} else {
this.submenuRef.current.style.left = itemOuterWidth + "px";
}
}
onEnter() {
this.position();
}
isActive() {
return this.props.root || !this.props.resetMenu;
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.resetMenu === true) {
return {
activeItem: null,
};
}
return null;
}
componentDidUpdate() {
if (this.isActive()) {
this.position();
}
}
renderSeparator(index) {
return (
<li
key={"separator_" + index}
className="p-menu-separator"
role="separator"
></li>
);
}
renderSubmenu(item) {
if (item.items) {
return (
<ContextMenuSub
model={item.items}
resetMenu={item !== this.state.activeItem}
onLeafClick={this.props.onLeafClick}
/>
);
}
return null;
}
renderMenuitem(item, index) {
if (item.disabled) return; //TODO: Not render disabled items
const active = this.state.activeItem === item;
const className = classNames(
"p-menuitem",
{ "p-menuitem-active": active },
item.className
);
const linkClassName = classNames("p-menuitem-link", {
"p-disabled": item.disabled,
});
const iconClassName = classNames("p-menuitem-icon", {
"p-disabled": item.disabled,
});
const submenuIconClassName = "p-submenu-icon";
const icon = item.icon && (
<ReactSVG
wrapper="span"
className={iconClassName}
src={item.icon}
></ReactSVG>
);
const label = item.label && (
<span className="p-menuitem-text">{item.label}</span>
);
const submenuIcon = item.items && (
<ArrowIcon className={submenuIconClassName} />
);
const submenu = this.renderSubmenu(item);
let content = (
<a
href={item.url || "#"}
className={linkClassName}
target={item.target}
onClick={(event) => this.onItemClick(event, item, index)}
role="menuitem"
>
{icon}
{label}
{submenuIcon}
</a>
);
if (item.template) {
const defaultContentOptions = {
onClick: (event) => this.onItemClick(event, item, index),
className: linkClassName,
labelClassName: "p-menuitem-text",
iconClassName,
submenuIconClassName,
element: content,
props: this.props,
active,
};
content = ObjectUtils.getJSXElement(
item.template,
item,
defaultContentOptions
);
}
return (
<li
key={item.label + "_" + index}
role="none"
className={className}
style={item.style}
onMouseEnter={(event) => this.onItemMouseEnter(event, item)}
>
{content}
{submenu}
</li>
);
}
renderItem(item, index) {
if (item.isSeparator) return this.renderSeparator(index);
else return this.renderMenuitem(item, index);
}
renderMenu() {
if (this.props.model) {
return this.props.model.map((item, index) => {
return this.renderItem(item, index);
});
}
return null;
}
render() {
const className = classNames({ "p-submenu-list": !this.props.root });
const submenu = this.renderMenu();
const isActive = this.isActive();
return (
<CSSTransition
nodeRef={this.submenuRef}
classNames="p-contextmenusub"
in={isActive}
timeout={{ enter: 0, exit: 0 }}
unmountOnExit={true}
onEnter={this.onEnter}
>
<ul ref={this.submenuRef} className={className}>
{submenu}
</ul>
</CSSTransition>
);
}
}
ContextMenuSub.propTypes = {
model: PropTypes.any,
root: PropTypes.bool,
className: PropTypes.string,
resetMenu: PropTypes.bool,
onLeafClick: PropTypes.func,
};
ContextMenuSub.defaultProps = {
model: null,
root: false,
className: null,
resetMenu: false,
onLeafClick: null,
};
class ContextMenu extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
reshow: false,
resetMenu: false,
};
this.onMenuClick = this.onMenuClick.bind(this);
this.onLeafClick = this.onLeafClick.bind(this);
this.onMenuMouseEnter = this.onMenuMouseEnter.bind(this);
this.onEnter = this.onEnter.bind(this);
this.onEntered = this.onEntered.bind(this);
this.onExit = this.onExit.bind(this);
this.onExited = this.onExited.bind(this);
this.menuRef = React.createRef();
}
onMenuClick() {
this.setState({
resetMenu: false,
});
}
onMenuMouseEnter() {
this.setState({
resetMenu: false,
});
}
show(e) {
if (!(e instanceof Event)) {
e.persist();
}
e.stopPropagation();
e.preventDefault();
this.currentEvent = e;
if (this.state.visible) {
this.setState({ reshow: true });
} else {
this.setState({ visible: true }, () => {
if (this.props.onShow) {
this.props.onShow(this.currentEvent);
}
});
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.visible && prevState.reshow !== this.state.reshow) {
let event = this.currentEvent;
this.setState(
{
visible: false,
reshow: false,
rePosition: false,
resetMenu: true,
},
() => this.show(event)
);
}
}
hide(e) {
if (!(e instanceof Event)) {
e.persist();
}
this.currentEvent = e;
this.setState({ visible: false, reshow: false }, () => {
if (this.props.onHide) {
this.props.onHide(this.currentEvent);
}
});
}
onEnter() {
if (this.props.autoZIndex) {
this.menuRef.current.style.zIndex = String(
this.props.baseZIndex + DomHelpers.generateZIndex()
);
}
this.position(this.currentEvent);
}
onEntered() {
this.bindDocumentListeners();
}
onExit() {
this.currentEvent = null;
this.unbindDocumentListeners();
}
onExited() {
DomHelpers.revertZIndex();
}
position(event) {
if (event) {
let left = event.pageX + 1;
let top = event.pageY + 1;
let width = this.menuRef.current.offsetParent
? this.menuRef.current.offsetWidth
: DomHelpers.getHiddenElementOuterWidth(this.menuRef.current);
let height = this.menuRef.current.offsetParent
? this.menuRef.current.offsetHeight
: DomHelpers.getHiddenElementOuterHeight(this.menuRef.current);
let viewport = DomHelpers.getViewport();
//flip
if (left + width - document.body.scrollLeft > viewport.width) {
left -= width;
}
//flip
if (top + height - document.body.scrollTop > viewport.height) {
top -= height;
}
//fit
if (left < document.body.scrollLeft) {
left = document.body.scrollLeft;
}
//fit
if (top < document.body.scrollTop) {
top = document.body.scrollTop;
}
this.menuRef.current.style.left = left + "px";
this.menuRef.current.style.top = top + "px";
}
}
onLeafClick(e) {
this.setState({
resetMenu: true,
});
this.hide(e);
e.stopPropagation();
}
isOutsideClicked(e) {
return (
this.menuRef &&
this.menuRef.current &&
!(
this.menuRef.current.isSameNode(e.target) ||
this.menuRef.current.contains(e.target)
)
);
}
bindDocumentListeners() {
this.bindDocumentResizeListener();
this.bindDocumentClickListener();
}
unbindDocumentListeners() {
this.unbindDocumentResizeListener();
this.unbindDocumentClickListener();
}
bindDocumentClickListener() {
if (!this.documentClickListener) {
this.documentClickListener = (e) => {
if (this.isOutsideClicked(e) && e.button !== 2) {
this.hide(e);
this.setState({
resetMenu: true,
});
}
};
document.addEventListener("click", this.documentClickListener);
}
}
bindDocumentContextMenuListener() {
if (!this.documentContextMenuListener) {
this.documentContextMenuListener = (e) => {
this.show(e);
};
document.addEventListener(
"contextmenu",
this.documentContextMenuListener
);
}
}
bindDocumentResizeListener() {
if (!this.documentResizeListener) {
this.documentResizeListener = (e) => {
if (this.state.visible) {
this.hide(e);
}
};
window.addEventListener("resize", this.documentResizeListener);
}
}
unbindDocumentClickListener() {
if (this.documentClickListener) {
document.removeEventListener("click", this.documentClickListener);
this.documentClickListener = null;
}
}
unbindDocumentContextMenuListener() {
if (this.documentContextMenuListener) {
document.removeEventListener(
"contextmenu",
this.documentContextMenuListener
);
this.documentContextMenuListener = null;
}
}
unbindDocumentResizeListener() {
if (this.documentResizeListener) {
window.removeEventListener("resize", this.documentResizeListener);
this.documentResizeListener = null;
}
}
componentDidMount() {
this.container =
document.getElementById(this.props.targetAreaId) || document;
this.container.addEventListener("contextmenu", this.handleContextMenu);
if (this.props.global) {
this.bindDocumentContextMenuListener();
}
}
componentWillUnmount() {
this.container.removeEventListener("contextmenu", this.handleContextMenu);
this.unbindDocumentListeners();
this.unbindDocumentContextMenuListener();
DomHelpers.revertZIndex();
}
moveMenu = (e) => {
const menu = document.getElementById(this.props.id);
const bounds =
this.container !== document && this.container.getBoundingClientRect();
const clickX = e.clientX - bounds.left;
const clickY = e.clientY - bounds.top;
const containerWidth = this.container.offsetWidth;
const containerHeight = this.container.offsetHeight;
const menuWidth = (menu && menu.offsetWidth) || 180;
const menuHeight = menu && menu.offsetHeight;
const right = containerWidth - clickX < menuWidth && clickX > menuWidth;
const bottom = containerHeight - clickY < menuHeight && clickY > menuHeight;
let newTop = `0px`;
let newLeft = `0px`;
newLeft = right ? `${clickX - menuWidth - 8}px` : `${clickX + 8}px`;
newTop = bottom ? `${clickY - menuHeight}px` : `${clickY}px`;
if (menu) {
menu.style.top = newTop;
menu.style.left = newLeft;
}
};
handleContextMenu = (e) => {
if (e) {
e.preventDefault();
this.handleClick(e);
}
this.setState(
{
visible: true,
},
() => this.moveMenu(e)
renderContextMenu() {
const className = classNames(
"p-contextmenu p-component",
this.props.className
);
};
handleClick = (e) => {
const { visible } = this.state;
const menu = document.getElementById(this.props.id);
const wasOutside = e.target ? !(e.target.contains === menu) : true;
if (wasOutside && visible) this.setState({ visible: false });
};
itemClick = (action, e) => {
action && action(e);
this.setState({ visible: false });
};
render() {
//console.log('ContextMenu render', this.props);
const { visible } = this.state;
const { options, id, className, style, withBackdrop } = this.props;
return (
((visible && options) || null) && (
<DropDown
id={id}
className={className}
style={style}
open={visible}
clickOutsideAction={this.handleClick}
withBackdrop={withBackdrop}
<StyledContextMenu>
<CSSTransition
nodeRef={this.menuRef}
classNames="p-contextmenu"
in={this.state.visible}
timeout={{ enter: 250, exit: 0 }}
unmountOnExit
onEnter={this.onEnter}
onEntered={this.onEntered}
onExit={this.onExit}
onExited={this.onExited}
>
{options.map((item) => {
if (item && item.key !== undefined) {
return (
<DropDownItem
key={item.key}
{...item}
onClick={this.itemClick.bind(this, item.onClick)}
/>
);
}
})}
</DropDown>
)
<div
ref={this.menuRef}
id={this.props.id}
className={className}
style={this.props.style}
onClick={this.onMenuClick}
onMouseEnter={this.onMenuMouseEnter}
>
<ContextMenuSub
model={this.props.model}
root
resetMenu={this.state.resetMenu}
onLeafClick={this.onLeafClick}
/>
</div>
</CSSTransition>
</StyledContextMenu>
);
}
render() {
const element = this.renderContextMenu();
return <Portal element={element} appendTo={this.props.appendTo} />;
}
}
ContextMenu.propTypes = {
/** DropDownItems collection */
options: PropTypes.array,
/** Id of container apply to */
targetAreaId: PropTypes.string,
/** Accepts class */
className: PropTypes.string,
/** Accepts id */
/** Unique identifier of the element */
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Used to display backdrop */
withBackdrop: PropTypes.bool,
/** An array of menuitems */
model: PropTypes.array,
/** Inline style of the component */
style: PropTypes.object,
/** Style class of the component */
className: PropTypes.string,
/** Attaches the menu to document instead of a particular item */
global: PropTypes.bool,
/** Base zIndex value to use in layering */
autoZIndex: PropTypes.bool,
/** Whether to automatically manage layering */
baseZIndex: PropTypes.number,
/** DOM element instance where the menu should be mounted */
appendTo: PropTypes.any,
/** Callback to invoke when a popup menu is shown */
onShow: PropTypes.func,
/** Callback to invoke when a popup menu is hidden */
onHide: PropTypes.func,
};
ContextMenu.defaultProps = {
options: [],
id: "contextMenu",
withBackdrop: true,
id: null,
model: null,
style: null,
className: null,
global: false,
autoZIndex: true,
baseZIndex: 0,
appendTo: null,
onShow: null,
onHide: null,
};
export default ContextMenu;

View File

@ -0,0 +1,136 @@
import styled from "styled-components";
import Base from "../themes/base";
const StyledContextMenu = styled.div`
.p-contextmenu {
position: absolute;
background: ${(props) => props.theme.dropDown.background};
border-radius: ${(props) => props.theme.dropDown.borderRadius};
-moz-border-radius: ${(props) => props.theme.dropDown.borderRadius};
-webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius};
box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
padding: 4px 0px;
}
.p-contextmenu ul {
margin: 0;
padding: 0;
list-style: none;
}
.p-contextmenu .p-submenu-list {
position: absolute;
background: ${(props) => props.theme.dropDown.background};
border-radius: ${(props) => props.theme.dropDown.borderRadius};
-moz-border-radius: ${(props) => props.theme.dropDown.borderRadius};
-webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius};
box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
padding: 4px 0px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-left: 8px;
margin-top: -4px;
}
.p-contextmenu .p-menuitem-link {
cursor: pointer;
display: flex;
align-items: center;
text-decoration: none;
overflow: hidden;
position: relative;
border: ${(props) => props.theme.dropDownItem.border};
margin: ${(props) => props.theme.dropDownItem.margin};
padding: ${(props) => props.theme.dropDownItem.padding};
font-family: ${(props) => props.theme.fontFamily};
font-style: normal;
background: none;
user-select: none;
outline: 0 !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-weight: ${(props) => props.theme.dropDownItem.fontWeight};
font-size: ${(props) => props.theme.dropDownItem.fontSize};
color: ${(props) => props.theme.dropDownItem.color};
text-transform: none;
&:hover {
background-color: ${(props) =>
props.noHover
? props.theme.dropDownItem.backgroundColor
: props.theme.dropDownItem.hoverBackgroundColor};
}
&.p-disabled {
color: ${(props) => props.theme.dropDownItem.disableColor};
&:hover {
cursor: default;
background-color: ${(props) =>
props.theme.dropDownItem.hoverDisabledBackgroundColor};
}
}
}
.p-contextmenu .p-menuitem-text {
line-height: ${(props) => props.theme.dropDownItem.lineHeight};
}
.p-contextmenu .p-menu-separator {
cursor: default;
padding: 0px 16px;
margin: 4px 16px 4px;
border-bottom: 1px solid #eceef1;
width: calc(90%-32px);
&:hover {
cursor: default;
}
}
.p-contextmenu .p-menuitem {
position: relative;
margin: ${(props) => props.theme.dropDownItem.margin};
}
.p-menuitem-icon {
max-height: ${(props) => props.theme.dropDownItem.lineHeight};
path {
fill: ${(props) => props.theme.dropDownItem.icon.color};
}
&.p-disabled {
path {
fill: ${(props) => props.theme.dropDownItem.icon.disableColor};
}
}
margin-right: 8px;
}
.p-submenu-icon {
margin-left: auto;
}
.p-contextmenu-enter {
opacity: 0;
}
.p-contextmenu-enter-active {
opacity: 1;
transition: opacity 250ms;
}
`;
StyledContextMenu.defaultProps = {
theme: Base,
};
export default StyledContextMenu;

View File

@ -0,0 +1,3 @@
<svg width="5" height="9" viewBox="0 0 5 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.07786 4.50079L0.468338 1.45635C0.216742 1.16282 0.250735 0.72091 0.544263 0.469314C0.837792 0.217719 1.2797 0.251712 1.5313 0.54524L4.5313 4.04524C4.75599 4.30738 4.75599 4.69421 4.5313 4.95635L1.5313 8.45635C1.2797 8.74988 0.837792 8.78387 0.544263 8.53227C0.250735 8.28068 0.216742 7.83877 0.468338 7.54524L3.07786 4.50079Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 455 B

View File

@ -60,3 +60,4 @@ export { Icons } from "./icons";
export { default as SaveCancelButtons } from "./save-cancel-buttons";
export { default as DragAndDrop } from "./drag-and-drop";
export * as Themes from "./themes";
export { default as Portal } from "./portal";

View File

@ -84,6 +84,7 @@
"react-text-mask": "^5.4.3",
"react-toastify": "^5.5.0",
"react-tooltip": "^3.11.6",
"react-transition-group": "^4.4.1",
"react-virtualized-auto-sizer": "^1.0.3",
"react-window": "^1.8.6",
"react-window-infinite-loader": "^1.0.5",

View File

@ -0,0 +1,50 @@
import { Component } from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
class Portal extends Component {
constructor(props) {
super(props);
this.state = {
mounted: props.visible,
};
}
domExist() {
return !!(
typeof window !== undefined &&
window.document &&
window.document.createElement
);
}
componentDidMount() {
if (this.domExist() && !this.state.mounted) {
this.setState({ mounted: true });
}
}
render() {
return this.props.element && this.state.mounted
? ReactDOM.createPortal(
this.props.element,
this.props.appendTo || document.body
)
: null;
}
}
Portal.propTypes = {
element: PropTypes.any,
appendTo: PropTypes.any,
visible: PropTypes.bool,
};
Portal.defaultProps = {
element: null,
appendTo: null,
visible: false,
};
export default Portal;

View File

@ -3,6 +3,7 @@ import React from "react";
import Checkbox from "../checkbox";
import ContextMenuButton from "../context-menu-button";
import ContextMenu from "../context-menu";
import {
StyledOptionButton,
StyledContentElement,
@ -16,58 +17,11 @@ class Row extends React.Component {
constructor(props) {
super(props);
this.state = {
contextX: "0px",
contextY: "100%",
contextOpened: false,
};
this.rowRef = React.createRef();
this.cm = React.createRef();
this.row = React.createRef();
}
componentDidMount() {
this.container = this.rowRef.current;
this.container.addEventListener("contextmenu", this.onContextMenu);
}
componentWillUnmount() {
this.container &&
this.container.removeEventListener("contextmenu", this.onContextMenu);
}
onContextMenu = (e) => {
e.preventDefault();
const menu = document.getElementById("contextMenu");
const containerBounds =
this.container !== document && this.container.getBoundingClientRect();
const clickX = containerBounds.right - e.clientX;
const clickY = e.clientY - containerBounds.top;
const containerWidth = this.container.offsetWidth;
const containerHeight = this.container.offsetHeight;
const menuWidth = (menu && menu.offsetWidth) || 180;
const menuHeight = menu && menu.offsetHeight;
const left = containerWidth - clickX > menuWidth && clickX < menuWidth;
const bottom = containerHeight - clickY < menuHeight && clickY > menuHeight;
let newTop = `0px`;
let newRight = `0px`;
newRight = !left ? `${clickX - menuWidth - 8}px` : `${clickX + 8}px`;
newTop = bottom ? `${clickY - menuHeight}px` : `${clickY}px`;
this.setState({
contextOpened: !this.state.contextOpened,
contextX: newRight,
contextY: newTop,
});
};
render() {
//console.log("Row render");
const {
checked,
children,
@ -82,8 +36,6 @@ class Row extends React.Component {
sectionWidth,
} = this.props;
const { contextOpened, contextX, contextY } = this.state;
const renderCheckbox = Object.prototype.hasOwnProperty.call(
this.props,
"checked"
@ -111,8 +63,16 @@ class Row extends React.Component {
return contextOptions;
};
const onContextMenu = (e) => {
rowContextClick && rowContextClick();
if (!this.cm.current.menuRef.current) {
this.row.current.click(e); //TODO: need fix context menu to global
}
this.cm.current.show(e);
};
return (
<StyledRow ref={this.rowRef} {...this.props}>
<StyledRow ref={this.row} {...this.props} onContextMenu={onContextMenu}>
{renderCheckbox && (
<StyledCheckbox>
<Checkbox
@ -135,9 +95,6 @@ class Row extends React.Component {
)}
{renderContext ? (
<ContextMenuButton
manualX={contextX}
manualY={contextY}
opened={contextOpened}
color="#A3A9AE"
hoverColor="#657077"
className="expandButton"
@ -147,6 +104,7 @@ class Row extends React.Component {
) : (
<div className="expandButton"> </div>
)}
<ContextMenu model={contextOptions} ref={this.cm}></ContextMenu>
</StyledOptionButton>
</StyledRow>
);

View File

@ -87,13 +87,4 @@ describe("<Row />", () => {
expect(wrapper.getDOMNode().style).toHaveProperty("color", "red");
});
it("componentWillUnmount() props lifecycle test", () => {
const wrapper = shallow(<Row {...baseProps} />);
const instance = wrapper.instance();
instance.componentWillUnmount();
expect(wrapper).toExist(false);
});
});

View File

@ -0,0 +1,31 @@
export function classNames(...args) {
if (args) {
let classes = [];
for (let i = 0; i < args.length; i++) {
let className = args[i];
if (!className) continue;
const type = typeof className;
if (type === "string" || type === "number") {
classes.push(className);
} else if (type === "object") {
const _classes = Array.isArray(className)
? className
: Object.entries(className).map(([key, value]) =>
!!value ? key : null
);
classes = _classes.length
? classes.concat(_classes.filter((c) => !!c))
: classes;
}
}
return classes.join(" ");
}
return null;
}

View File

@ -0,0 +1,117 @@
export default class DomHelpers {
static getViewport() {
let win = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName("body")[0],
w = win.innerWidth || e.clientWidth || g.clientWidth,
h = win.innerHeight || e.clientHeight || g.clientHeight;
return { width: w, height: h };
}
static getOffset(el) {
if (el) {
let rect = el.getBoundingClientRect();
return {
top:
rect.top +
(window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop ||
0),
left:
rect.left +
(window.pageXOffset ||
document.documentElement.scrollLeft ||
document.body.scrollLeft ||
0),
};
}
return {
top: "auto",
left: "auto",
};
}
static getOuterWidth(el, margin) {
if (el) {
let width = el.offsetWidth;
if (margin) {
let style = getComputedStyle(el);
width += parseFloat(style.marginLeft) + parseFloat(style.marginRight);
}
return width;
}
return 0;
}
static getHiddenElementOuterWidth(element) {
if (element) {
element.style.visibility = "hidden";
element.style.display = "block";
let elementWidth = element.offsetWidth;
element.style.display = "none";
element.style.visibility = "visible";
return elementWidth;
}
return 0;
}
static getHiddenElementOuterHeight(element) {
if (element) {
element.style.visibility = "hidden";
element.style.display = "block";
let elementHeight = element.offsetHeight;
element.style.display = "none";
element.style.visibility = "visible";
return elementHeight;
}
return 0;
}
static calculateScrollbarWidth(el) {
if (el) {
let style = getComputedStyle(el);
return (
el.offsetWidth -
el.clientWidth -
parseFloat(style.borderLeftWidth) -
parseFloat(style.borderRightWidth)
);
} else {
if (this.calculatedScrollbarWidth != null)
return this.calculatedScrollbarWidth;
let scrollDiv = document.createElement("div");
scrollDiv.className = "p-scrollbar-measure";
document.body.appendChild(scrollDiv);
let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);
this.calculatedScrollbarWidth = scrollbarWidth;
return scrollbarWidth;
}
}
static generateZIndex() {
this.zIndex = this.zIndex || 1000;
return ++this.zIndex;
}
static revertZIndex() {
this.zIndex = 1000 < this.zIndex ? --this.zIndex : 1000;
}
static getCurrentZIndex() {
return this.zIndex;
}
}

View File

@ -0,0 +1,5 @@
export default class ObjectUtils {
static getJSXElement(obj, ...params) {
return this.isFunction(obj) ? obj(...params) : obj;
}
}

View File

@ -1,3 +0,0 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 0C7.67157 0 7 0.671573 7 1.5V7H1.5C0.671573 7 0 7.67157 0 8.5C0 9.32843 0.671573 10 1.5 10H7V15.5C7 16.3284 7.67157 17 8.5 17C9.32843 17 10 16.3284 10 15.5V10H15.5C16.3284 10 17 9.32843 17 8.5C17 7.67157 16.3284 7 15.5 7H10V1.5C10 0.671573 9.32843 0 8.5 0Z" fill="#A3A9AE"/>
</svg>

Before

Width:  |  Height:  |  Size: 432 B

View File

@ -29,5 +29,7 @@
"ShareEveryone": "Everyone",
"ShareEmailSubject": "You have been granted access to the {{itemName}} document",
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}"
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}",
"LoadingLabel": "Loading... Please wait..."
}

View File

@ -29,5 +29,7 @@
"ShareEveryone": "Все",
"ShareEmailSubject": "Вам предоставлен доступ к документу {{itemName}}",
"ShareEmailBody": "Вам предоставлен доступ к документу {{itemName}}. Нажмите на ссылку ниже, чтобы открыть документ прямо сейчас: {{shareLink}}"
"ShareEmailBody": "Вам предоставлен доступ к документу {{itemName}}. Нажмите на ссылку ниже, чтобы открыть документ прямо сейчас: {{shareLink}}",
"LoadingLabel": "Загрузка... Пожалуйста подождите..."
}

View File

@ -7,17 +7,7 @@ import PrivateRoute from "@appserver/common/components/PrivateRoute";
import AppLoader from "@appserver/common/components/AppLoader";
import toastr from "studio/toastr";
import { combineUrl, updateTempContent } from "@appserver/common/utils";
import initFilesStore from "./store/InitFilesStore";
import filesStore from "./store/FilesStore";
import settingsStore from "./store/SettingsStore";
import mediaViewerDataStore from "./store/MediaViewerDataStore";
import formatsStore from "./store/FormatsStore";
import versionHistoryStore from "./store/VersionHistoryStore";
import uploadDataStore from "./store/UploadDataStore";
import dialogsStore from "./store/DialogsStore";
import treeFoldersStore from "./store/TreeFoldersStore";
import selectedFolderStore from "./store/SelectedFolderStore";
import filesActionsStore from "./store/FilesActionsStore";
import stores from "./store/index";
import contextOptionsStore from "./store/ContextOptionsStore";
import "./custom.scss";
import i18n from "./i18n";
@ -126,39 +116,26 @@ class FilesContent extends React.Component {
}
}
const Files = inject(({ auth, initFilesStore }) => {
const Files = inject(({ auth, filesStore }) => {
return {
isDesktop: auth.settingsStore.isDesktopClient,
user: auth.userStore.user,
isAuthenticated: auth.isAuthenticated,
encryptionKeys: auth.settingsStore.encryptionKeys,
isEncryption: auth.settingsStore.isEncryptionSupport,
isLoaded: auth.isLoaded && initFilesStore.isLoaded,
setIsLoaded: initFilesStore.setIsLoaded,
isLoaded: auth.isLoaded && filesStore.isLoaded,
setIsLoaded: filesStore.setIsLoaded,
setEncryptionKeys: auth.settingsStore.setEncryptionKeys,
loadFilesInfo: async () => {
//await auth.init();
await initFilesStore.initFiles();
await filesStore.initFiles();
auth.setProductVersion(config.version);
},
};
})(observer(FilesContent));
export default () => (
<FilesProvider
initFilesStore={initFilesStore}
filesStore={filesStore}
settingsStore={settingsStore}
mediaViewerDataStore={mediaViewerDataStore}
formatsStore={formatsStore}
versionHistoryStore={versionHistoryStore}
uploadDataStore={uploadDataStore}
dialogsStore={dialogsStore}
treeFoldersStore={treeFoldersStore}
selectedFolderStore={selectedFolderStore}
filesActionsStore={filesActionsStore}
contextOptionsStore={contextOptionsStore}
>
<FilesProvider {...stores}>
<I18nextProvider i18n={i18n}>
<Files />
</I18nextProvider>

View File

@ -213,13 +213,13 @@ const ThirdPartyList = withTranslation("Article")(
export default inject(
({
initFilesStore,
filesStore,
settingsStore,
treeFoldersStore,
selectedFolderStore,
dialogsStore,
}) => {
const { setIsLoading } = initFilesStore;
const { setIsLoading } = filesStore;
const { setSelectedFolder } = selectedFolderStore;
const { setSelectedNode } = treeFoldersStore;
const {

View File

@ -470,15 +470,15 @@ TreeFolders.defaultProps = {
};
export default inject(
({
auth,
initFilesStore,
filesStore,
treeFoldersStore,
selectedFolderStore,
}) => {
const { setIsLoading, dragging, setDragging } = initFilesStore;
const { filter, setFilter, selection } = filesStore;
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
const {
filter,
setFilter,
selection,
setIsLoading,
dragging,
setDragging,
} = filesStore;
const {
treeFolders,

View File

@ -209,12 +209,12 @@ const TreeSettings = withTranslation("Settings")(withRouter(PureTreeSettings));
export default inject(
({
auth,
initFilesStore,
filesStore,
settingsStore,
treeFoldersStore,
selectedFolderStore,
}) => {
const { setIsLoading, isLoading } = initFilesStore;
const { setIsLoading, isLoading } = filesStore;
const { setSelectedFolder } = selectedFolderStore;
const { selectedTreeNode, setSelectedNode } = treeFoldersStore;
const {

View File

@ -128,9 +128,8 @@ class ArticleBodyContent extends React.Component {
}
export default inject(
({ initFilesStore, filesStore, treeFoldersStore, selectedFolderStore }) => {
const { setIsLoading } = initFilesStore;
const { fetchFiles, filter } = filesStore;
({ filesStore, treeFoldersStore, selectedFolderStore }) => {
const { fetchFiles, filter, setIsLoading } = filesStore;
const { treeFolders, setSelectedNode, setTreeFolders } = treeFoldersStore;
const selectedTreeNode =
treeFoldersStore.selectedTreeNode.length > 0

View File

@ -94,9 +94,14 @@ const DragTooltip = (props) => {
return <StyledTooltip ref={tooltipRef}>{tooltipLabel}</StyledTooltip>;
};
export default inject(({ initFilesStore, filesStore }) => {
const { tooltipValue, tooltipPageX, tooltipPageY } = initFilesStore;
const { selection, iconOfDraggedFile } = filesStore;
export default inject(({ filesStore }) => {
const {
selection,
iconOfDraggedFile,
tooltipValue,
tooltipPageX,
tooltipPageY,
} = filesStore;
const isSingleItem = selection.length === 1;

View File

@ -4,6 +4,6 @@ import DragTooltip from "./DragTooltip";
const Tooltip = ({ dragging }) => (dragging ? <DragTooltip /> : <></>);
export default inject(({ initFilesStore, filesStore }) => ({
dragging: initFilesStore.dragging && filesStore.selection[0],
export default inject(({ filesStore }) => ({
dragging: filesStore.dragging && filesStore.selection[0],
}))(observer(Tooltip));

View File

@ -52,8 +52,7 @@ class DeleteDialogComponent extends React.Component {
deleteSelectedElem: t("DeleteSelectedElem"),
};
deleteAction(translations)
.catch((err) => toastr.error(err));
deleteAction(translations).catch((err) => toastr.error(err));
};
onChange = (event) => {
@ -174,7 +173,6 @@ const DeleteDialog = withTranslation("DeleteDialog")(DeleteDialogComponent);
export default inject(
({
initFilesStore,
filesStore,
uploadDataStore,
treeFoldersStore,
@ -182,9 +180,8 @@ export default inject(
dialogsStore,
filesActionsStore,
}) => {
const { isLoading } = initFilesStore;
const { secondaryProgressDataStore } = uploadDataStore;
const { fetchFiles, selection, filter } = filesStore;
const { fetchFiles, selection, filter, isLoading } = filesStore;
const { deleteAction } = filesActionsStore;
const {

View File

@ -150,16 +150,9 @@ const EmptyTrashDialog = withTranslation("EmptyTrashDialog")(
);
export default inject(
({
initFilesStore,
filesStore,
uploadDataStore,
selectedFolderStore,
dialogsStore,
}) => {
const { isLoading } = initFilesStore;
({ filesStore, uploadDataStore, selectedFolderStore, dialogsStore }) => {
const { secondaryProgressDataStore } = uploadDataStore;
const { fetchFiles, filter } = filesStore;
const { fetchFiles, filter, isLoading } = filesStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,

View File

@ -52,10 +52,8 @@ const EmptyFilterContainer = ({
);
};
export default inject(
({ initFilesStore, filesStore, selectedFolderStore }) => ({
fetchFiles: filesStore.fetchFiles,
selectedFolderId: selectedFolderStore.id,
setIsLoading: initFilesStore.setIsLoading,
})
)(withTranslation("Home")(observer(EmptyFilterContainer)));
export default inject(({ filesStore, selectedFolderStore }) => ({
fetchFiles: filesStore.fetchFiles,
selectedFolderId: selectedFolderStore.id,
setIsLoading: filesStore.setIsLoading,
}))(withTranslation("Home")(observer(EmptyFilterContainer)));

View File

@ -79,13 +79,13 @@ const EmptyFolderContainer = ({
);
};
export default inject(({ initFilesStore, filesStore, selectedFolderStore }) => {
export default inject(({ filesStore, selectedFolderStore }) => {
const { filter, fetchFiles } = filesStore;
return {
filter,
fetchFiles,
setIsLoading: initFilesStore.setIsLoading,
setIsLoading: filesStore.setIsLoading,
parentId: selectedFolderStore.parentId,
};
})(withTranslation("Home")(observer(EmptyFolderContainer)));

View File

@ -188,21 +188,19 @@ const RootFolderContainer = (props) => {
};
export default inject(
({
auth,
initFilesStore,
filesStore,
treeFoldersStore,
selectedFolderStore,
}) => {
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
const {
isDesktopClient,
isEncryptionSupport,
organizationName,
} = auth.settingsStore;
const { privacyInstructions, setIsLoading } = initFilesStore;
const { filter, fetchFiles } = filesStore;
const {
filter,
fetchFiles,
privacyInstructions,
setIsLoading,
} = filesStore;
const { title, rootFolderType } = selectedFolderStore;
const { isPrivacyFolder, myFolderId } = treeFoldersStore;

View File

@ -593,7 +593,7 @@ class FilesContent extends React.Component {
{canWebEdit && !isTrashFolder && accessToEdit && (
<IconButton
onClick={this.onFilesClick}
iconName="images/access.edit.react.svg"
iconName="/static/images/access.edit.react.svg"
className="badge"
size="small"
isfill={true}
@ -732,7 +732,6 @@ export default inject(
(
{
auth,
initFilesStore,
filesStore,
formatsStore,
uploadDataStore,
@ -750,7 +749,6 @@ export default inject(
isDesktopClient: isDesktop,
isTabletView,
} = auth.settingsStore;
const { setIsLoading, isLoading } = initFilesStore;
const { secondaryProgressDataStore } = uploadDataStore;
const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
@ -784,6 +782,8 @@ export default inject(
renameFolder,
createFolder,
openDocEditor,
setIsLoading,
isLoading,
} = filesStore;
const {

View File

@ -0,0 +1,44 @@
import React from "react";
import { inject, observer } from "mobx-react";
import RowContainer from "@appserver/components/row-container";
import { Consumer } from "@appserver/components/utils/context";
import SimpleFilesRow from "./SimpleFilesRow";
import Loaders from "@appserver/common/components/Loaders";
import { isMobile } from "react-device-detect";
const FilesRowContainer = (props) => {
const { isLoaded, isLoading } = props;
return !isLoaded || (isMobile && isLoading) ? (
<Loaders.Rows />
) : (
<Consumer>
{(context) => (
<RowContainer
className="files-row-container"
draggable
useReactWindow={false}
>
{props.filesList.map((item) => {
return (
<SimpleFilesRow
key={item.id}
item={item}
sectionWidth={context.sectionWidth}
/>
);
})}
</RowContainer>
)}
</Consumer>
);
};
export default inject(({ auth, filesStore }) => {
const { filesList, isLoading } = filesStore;
return {
filesList,
isLoading,
isLoaded: auth.isLoaded,
};
})(observer(FilesRowContainer));

View File

@ -0,0 +1,500 @@
import React from "react";
import { withRouter } from "react-router";
import { Trans, withTranslation } from "react-i18next";
import styled from "styled-components";
import Badge from "@appserver/components/badge";
import Link from "@appserver/components/link";
import Text from "@appserver/components/text";
import { markAsRead } from "@appserver/common/api/files";
import { FileAction, AppServerConfig } from "@appserver/common/constants";
import toastr from "studio/toastr";
import { getTitleWithoutExst } from "../../../../../helpers/files-helpers";
import { NewFilesPanel } from "../../../../panels";
import EditingWrapperComponent from "./EditingWrapperComponent";
import TileContent from "./TileContent";
import { isMobile } from "react-device-detect";
import { inject, observer } from "mobx-react";
import CheckIcon from "../../../../../../public/images/check.react.svg";
import CrossIcon from "../../../../../../../../../public/images/cross.react.svg";
import config from "../../../../../../package.json";
import { combineUrl } from "@appserver/common/utils";
const SimpleFilesTileContent = styled(TileContent)`
.rowMainContainer {
height: auto;
max-width: 100%;
align-self: flex-end;
a {
word-break: break-word;
}
}
.mainIcons {
align-self: flex-end;
}
.badge-ext {
margin-left: -8px;
margin-right: 8px;
}
.badge {
margin-right: 8px;
}
.badges {
display: flex;
align-items: center;
}
.share-icon {
margin-top: -4px;
padding-right: 8px;
}
@media (max-width: 1024px) {
display: inline-flex;
height: auto;
& > div {
margin-top: 0;
}
}
`;
const okIcon = (
<CheckIcon
className="edit-ok-icon"
size="scale"
isfill={true}
color="#A3A9AE"
/>
);
const cancelIcon = (
<CrossIcon
className="edit-cancel-icon"
size="scale"
isfill={true}
color="#A3A9AE"
/>
);
class FilesTileContent extends React.PureComponent {
constructor(props) {
super(props);
let titleWithoutExt = getTitleWithoutExst(props.item);
if (props.fileAction.id === -1) {
titleWithoutExt = this.getDefaultName(props.fileAction.extension);
}
this.state = {
itemTitle: titleWithoutExt,
editingId: props.fileAction.id,
showNewFilesPanel: false,
newFolderId: [],
newItems: props.item.new,
//loading: false
};
}
completeAction = (e) => {
//this.setState({ loading: false }, () =>)
this.props.onEditComplete(e);
};
updateItem = (e) => {
const {
fileAction,
updateFile,
renameFolder,
item,
setIsLoading,
} = this.props;
const { itemTitle } = this.state;
const originalTitle = getTitleWithoutExst(item);
setIsLoading(true);
if (originalTitle === itemTitle) return this.completeAction(e);
item.fileExst
? updateFile(fileAction.id, itemTitle)
.then(() => this.completeAction(e))
.finally(() => setIsLoading(false))
: renameFolder(fileAction.id, itemTitle)
.then(() => this.completeAction(e))
.finally(() => setIsLoading(false));
};
createItem = (e) => {
const { createFile, item, setIsLoading, createFolder } = this.props;
const { itemTitle } = this.state;
setIsLoading(true);
if (itemTitle.trim() === "") return this.completeAction();
!item.fileExst
? createFolder(item.parentId, itemTitle)
.then(() => this.completeAction(e))
.finally(() => {
toastr.success(
<Trans i18nKey="FolderCreated" ns="Home">
New folder {{ itemTitle }} is created
</Trans>
);
return setIsLoading(false);
})
: createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
.then(() => this.completeAction(e))
.finally(() => {
const exst = item.fileExst;
toastr.success(
<Trans i18nKey="FileCreated" ns="Home">
New file {{ itemTitle }}.{{ exst }} is created
</Trans>
);
return setIsLoading(false);
});
};
componentDidUpdate(prevProps) {
const { fileAction } = this.props;
if (fileAction) {
if (fileAction.id !== prevProps.fileAction.id) {
this.setState({ editingId: fileAction.id });
}
}
}
renameTitle = (e) => {
this.setState({ itemTitle: e.target.value });
};
cancelUpdateItem = (e) => {
//this.setState({ loading: false });
this.completeAction(e);
};
onClickUpdateItem = () => {
this.props.fileAction.type === FileAction.Create
? this.createItem()
: this.updateItem();
};
onKeyUpUpdateItem = (e) => {
if (e.keyCode === 13) {
this.props.fileAction.type === FileAction.Create
? this.createItem()
: this.updateItem();
}
if (e.keyCode === 27) return this.cancelUpdateItem();
};
onFilesClick = () => {
const { id, fileExst, viewUrl, providerKey } = this.props.item;
const {
filter,
parentFolder,
setIsLoading,
onMediaFileClick,
fetchFiles,
canWebEdit,
openDocEditor,
isVideo,
isImage,
isSound,
expandedKeys,
addExpandedKeys,
} = this.props;
if (!fileExst) {
setIsLoading(true);
if (!expandedKeys.includes(parentFolder + "")) {
addExpandedKeys(parentFolder + "");
}
fetchFiles(id, filter)
.catch((err) => {
toastr.error(err);
setIsLoading(false);
})
.finally(() => setIsLoading(false));
} else {
if (canWebEdit) {
return openDocEditor(id, providerKey);
}
const isOpenMedia = isImage || isSound || isVideo;
if (isOpenMedia) {
onMediaFileClick(id);
return;
}
return window.open(viewUrl, "_blank");
}
};
onMobileRowClick = (e) => {
if (!isMobile) return;
this.onFilesClick();
};
getStatusByDate = () => {
const { culture, t, item, sectionWidth } = this.props;
const { created, updated, version, fileExst } = item;
const title =
version > 1
? t("TitleModified")
: fileExst
? t("TitleUploaded")
: t("TitleCreated");
const date = fileExst ? updated : created;
const dateLabel = new Date(date).toLocaleString(culture);
const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
return mobile ? dateLabel : `${title}: ${dateLabel}`;
};
getDefaultName = (format) => {
const { t } = this.props;
switch (format) {
case "docx":
return t("NewDocument");
case "xlsx":
return t("NewSpreadsheet");
case "pptx":
return t("NewPresentation");
default:
return t("NewFolder");
}
};
onShowVersionHistory = (e) => {
const { homepage, history } = this.props;
const fileId = e.currentTarget.dataset.id;
history.push(
combineUrl(AppServerConfig.proxyURL, homepage, `/${fileId}/history`)
);
};
onBadgeClick = () => {
const { showNewFilesPanel } = this.state;
const {
item,
treeFolders,
setTreeFolders,
rootFolderId,
newItems,
filter,
fetchFiles,
} = this.props;
if (item.fileExst) {
markAsRead([], [item.id])
.then(() => {
const data = treeFolders;
const dataItem = data.find((x) => x.id === rootFolderId);
dataItem.newItems = newItems ? dataItem.newItems - 1 : 0; //////newItems
setTreeFolders(data);
fetchFiles(this.props.selectedFolderId, filter.clone());
})
.catch((err) => toastr.error(err));
} else {
const newFolderId = this.props.selectedFolderPathParts;
newFolderId.push(item.id);
this.setState({
showNewFilesPanel: !showNewFilesPanel,
newFolderId,
});
}
};
onShowNewFilesPanel = () => {
const { showNewFilesPanel } = this.state;
this.setState({ showNewFilesPanel: !showNewFilesPanel });
};
render() {
const { item, fileAction, isTrashFolder, folders } = this.props;
const {
itemTitle,
editingId,
showNewFilesPanel,
newItems,
newFolderId,
} = this.state;
const { fileExst, id } = item;
const titleWithoutExt = getTitleWithoutExst(item);
const isEdit = id === editingId && fileExst === fileAction.extension;
const linkStyles = isTrashFolder
? { noHover: true }
: { onClick: this.onFilesClick };
const showNew = item.new && item.new > 0;
return isEdit ? (
<EditingWrapperComponent
itemTitle={itemTitle}
okIcon={okIcon}
cancelIcon={cancelIcon}
renameTitle={this.renameTitle}
onKeyUpUpdateItem={this.onKeyUpUpdateItem}
onClickUpdateItem={this.onClickUpdateItem}
cancelUpdateItem={this.cancelUpdateItem}
itemId={id}
/>
) : (
<>
{showNewFilesPanel && (
<NewFilesPanel
visible={showNewFilesPanel}
onClose={this.onShowNewFilesPanel}
folderId={newFolderId}
folders={folders}
/>
)}
<SimpleFilesTileContent
sideColor="#333"
isFile={fileExst}
onClick={this.onMobileRowClick}
disableSideInfo
>
<Link
containerWidth="100%"
type="page"
title={titleWithoutExt}
fontWeight="bold"
fontSize="15px"
{...linkStyles}
color="#333"
isTextOverflow
>
{titleWithoutExt}
</Link>
<>
{fileExst ? (
<div className="badges">
<Text
className="badge-ext"
as="span"
color="#A3A9AE"
fontSize="15px"
fontWeight={600}
title={fileExst}
truncate={true}
>
{fileExst}
</Text>
</div>
) : (
<div className="badges">
{!!showNew && (
<Badge
className="badge-version"
backgroundColor="#ED7309"
borderRadius="11px"
color="#FFFFFF"
fontSize="10px"
fontWeight={800}
label={newItems}
maxWidth="50px"
onClick={this.onBadgeClick}
padding="0 5px"
data-id={id}
/>
)}
</div>
)}
</>
</SimpleFilesTileContent>
</>
);
}
}
export default inject(
(
{ auth, filesStore, formatsStore, treeFoldersStore, selectedFolderStore },
{ item }
) => {
const { culture } = auth.settingsStore;
const {
iconFormatsStore,
mediaViewersFormatsStore,
docserviceStore,
} = formatsStore;
const {
folders,
fetchFiles,
filter,
newRowItems,
createFile,
updateFile,
renameFolder,
createFolder,
setIsLoading,
isLoading,
dragging,
} = filesStore;
const {
treeFolders,
setTreeFolders,
isRecycleBinFolder,
expandedKeys,
addExpandedKeys,
} = treeFoldersStore;
const { type, extension, id } = filesStore.fileActionStore;
const fileAction = { type, extension, id };
const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
const isImage = iconFormatsStore.isImage(item.fileExst);
const isSound = iconFormatsStore.isSound(item.fileExst);
return {
culture,
homepage: config.homepage,
fileAction,
folders,
rootFolderId: selectedFolderStore.pathParts,
selectedFolderId: selectedFolderStore.id,
selectedFolderPathParts: selectedFolderStore.pathParts,
newItems: selectedFolderStore.new,
parentFolder: selectedFolderStore.parentId,
isLoading,
treeFolders,
isTrashFolder: isRecycleBinFolder,
filter,
dragging,
canWebEdit,
isVideo,
isImage,
isSound,
newRowItems,
expandedKeys,
setIsLoading,
fetchFiles,
setTreeFolders,
createFile,
createFolder,
updateFile,
renameFolder,
addExpandedKeys,
};
}
)(withRouter(withTranslation("Home")(observer(FilesTileContent))));

View File

@ -22,6 +22,8 @@ const SectionBodyContent = (props) => {
startDrag,
setStartDrag,
setDragging,
startDrag,
setStartDrag,
setTooltipPosition,
isRecycleBinFolder,
moveDragItems,
@ -54,8 +56,8 @@ const SectionBodyContent = (props) => {
const onMouseMove = (e) => {
if (!dragging) {
setDragging(true);
document.body.classList.add("drag-cursor");
setDragging(true);
}
setTooltipPosition(e.pageX, e.pageY);
@ -106,6 +108,7 @@ const SectionBodyContent = (props) => {
const folderId = value ? value.split("_")[1] : treeValue;
setStartDrag(false);
setDragging(false);
setStartDrag(false);
onMoveTo(folderId);
@ -150,15 +153,19 @@ const SectionBodyContent = (props) => {
export default inject(
({
initFilesStore,
filesStore,
selectedFolderStore,
treeFoldersStore,
filesActionsStore,
}) => {
const {
firstLoad,
fileActionStore,
filesList,
dragging,
setDragging,
startDrag,
setStartDrag,
isLoading,
viewAs,
setTooltipPosition,
@ -177,6 +184,8 @@ export default inject(
isLoading,
isEmptyFilesList: filesList.length <= 0,
setDragging,
startDrag,
setStartDrag,
folderId: selectedFolderStore.id,
setTooltipPosition,
isRecycleBinFolder: treeFoldersStore.isRecycleBinFolder,

View File

@ -307,29 +307,33 @@ class SectionFilterContent extends React.Component {
}
}
export default inject(
({ auth, initFilesStore, filesStore, selectedFolderStore }) => {
const { setIsLoading, setViewAs, viewAs } = initFilesStore;
const { firstLoad, fetchFiles, filter } = filesStore;
export default inject(({ auth, filesStore, selectedFolderStore }) => {
const {
firstLoad,
fetchFiles,
filter,
setIsLoading,
setViewAs,
viewAs,
} = filesStore;
const { user } = auth.userStore;
const { customNames, culture } = auth.settingsStore;
const { user } = auth.userStore;
const { customNames, culture } = auth.settingsStore;
return {
customNames,
user,
firstLoad,
selectedFolderId: selectedFolderStore.id,
selectedItem: filter.selectedItem,
filter,
viewAs,
return {
customNames,
user,
firstLoad,
selectedFolderId: selectedFolderStore.id,
selectedItem: filter.selectedItem,
filter,
viewAs,
setIsLoading,
fetchFiles,
setViewAs,
};
}
)(
setIsLoading,
fetchFiles,
setViewAs,
};
})(
withRouter(
withLayoutSize(withTranslation("Home")(observer(SectionFilterContent)))
)

View File

@ -93,12 +93,10 @@ const StyledContainer = styled.div`
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding-bottom: 56px;
${
isMobile &&
css`
position: sticky;
`
}
${isMobile &&
css`
position: sticky;
`}
${(props) =>
!props.isTabletView
@ -115,12 +113,10 @@ const StyledContainer = styled.div`
@media ${tablet} {
padding-bottom: 0;
${
!isMobile &&
css`
height: 56px;
`
}
${!isMobile &&
css`
height: 56px;
`}
& > div:first-child {
${(props) =>
!isMobile &&
@ -557,7 +553,6 @@ class SectionHeaderContent extends React.Component {
export default inject(
({
auth,
initFilesStore,
filesStore,
dialogsStore,
treeFoldersStore,
@ -565,7 +560,6 @@ export default inject(
filesActionsStore,
settingsStore,
}) => {
const { setIsLoading } = initFilesStore;
const {
setSelected,
fileActionStore,
@ -581,6 +575,7 @@ export default inject(
isOnlyFoldersSelected,
isThirdPartySelection,
isWebEditSelected,
setIsLoading,
} = filesStore;
const { isRecycleBinFolder, isPrivacyFolder } = treeFoldersStore;
const { setAction } = fileActionStore;

View File

@ -162,9 +162,8 @@ const SectionPagingContent = ({
);
};
export default inject(({ initFilesStore, filesStore, selectedFolderStore }) => {
const { setIsLoading } = initFilesStore;
const { files, folders, fetchFiles, filter } = filesStore;
export default inject(({ filesStore, selectedFolderStore }) => {
const { files, folders, fetchFiles, filter, setIsLoading } = filesStore;
return {
files,

View File

@ -294,19 +294,11 @@ const Home = withTranslation("Home")(PureHome);
export default inject(
({
auth,
initFilesStore,
filesStore,
uploadDataStore,
dialogsStore,
selectedFolderStore,
}) => {
const {
dragging,
setDragging,
setIsLoading,
isLoading,
viewAs,
} = initFilesStore;
const {
secondaryProgressDataStore,
primaryProgressDataStore,
@ -319,6 +311,11 @@ export default inject(
fileActionStore,
selection,
setSelections,
dragging,
setDragging,
setIsLoading,
isLoading,
viewAs,
} = filesStore;
const { id } = fileActionStore;

View File

@ -162,8 +162,8 @@ const SectionBodyContent = ({
};
export default inject(
({ auth, initFilesStore, settingsStore, treeFoldersStore }) => {
const { isLoading } = initFilesStore;
({ auth, filesStore, settingsStore, treeFoldersStore }) => {
const { isLoading } = filesStore;
const { selectedTreeNode } = treeFoldersStore;
const {
isLoadedSettingsTree,

View File

@ -99,20 +99,17 @@ const PureSettings = ({
const Settings = withTranslation("Settings")(PureSettings);
export default inject(
({ initFilesStore, filesStore, settingsStore, treeFoldersStore }) => {
const { isLoading } = initFilesStore;
const { setFirstLoad } = filesStore;
const { setSelectedNode } = treeFoldersStore;
const { getFilesSettings, isLoadedSettingsTree } = settingsStore;
export default inject(({ filesStore, settingsStore, treeFoldersStore }) => {
const { setFirstLoad, isLoading } = filesStore;
const { setSelectedNode } = treeFoldersStore;
const { getFilesSettings, isLoadedSettingsTree } = settingsStore;
return {
isLoading,
isLoadedSettingsTree,
return {
isLoading,
isLoadedSettingsTree,
setFirstLoad,
setSelectedNode,
getFilesSettings,
};
}
)(withRouter(observer(Settings)));
setFirstLoad,
setSelectedNode,
getFilesSettings,
};
})(withRouter(observer(Settings)));

View File

@ -55,27 +55,24 @@ class SectionBodyContent extends React.Component {
}
}
export default inject(
({ auth, initFilesStore, filesStore, versionHistoryStore }) => {
const { setIsLoading, isLoading } = initFilesStore;
const { setFirstLoad } = filesStore;
const {
versions,
fetchFileVersions,
fileId,
setVerHistoryFileId,
} = versionHistoryStore;
export default inject(({ auth, filesStore, versionHistoryStore }) => {
const { setFirstLoad, setIsLoading, isLoading } = filesStore;
const {
versions,
fetchFileVersions,
fileId,
setVerHistoryFileId,
} = versionHistoryStore;
return {
culture: auth.settingsStore.culture,
isLoading,
versions,
fileId,
return {
culture: auth.settingsStore.culture,
isLoading,
versions,
fileId,
setFirstLoad,
setIsLoading,
fetchFileVersions,
setVerHistoryFileId,
};
}
)(withRouter(observer(SectionBodyContent)));
setFirstLoad,
setIsLoading,
fetchFileVersions,
setVerHistoryFileId,
};
})(withRouter(observer(SectionBodyContent)));

View File

@ -86,20 +86,17 @@ VersionHistory.propTypes = {
history: PropTypes.object.isRequired,
};
export default inject(
({ auth, initFilesStore, filesStore, versionHistoryStore }) => {
const { isLoading } = initFilesStore;
const { filter, setFilesFilter } = filesStore;
const { setIsVerHistoryPanel, versions } = versionHistoryStore;
export default inject(({ auth, filesStore, versionHistoryStore }) => {
const { filter, setFilesFilter, isLoading } = filesStore;
const { setIsVerHistoryPanel, versions } = versionHistoryStore;
return {
isTabletView: auth.settingsStore.isTabletView,
isLoading,
filter,
versions,
return {
isTabletView: auth.settingsStore.isTabletView,
isLoading,
filter,
versions,
setFilesFilter,
setIsVerHistoryPanel,
};
}
)(withRouter(observer(VersionHistory)));
setFilesFilter,
setIsVerHistoryPanel,
};
})(withRouter(observer(VersionHistory)));

View File

@ -140,9 +140,15 @@ const ChangeOwnerPanel = withTranslation("ChangeOwnerPanel")(
ChangeOwnerComponent
);
export default inject(({ auth, initFilesStore, filesStore, dialogsStore }) => {
const { setIsLoading, isLoading } = initFilesStore;
const { selection, setFile, setFolder, setFilesOwner } = filesStore;
export default inject(({ auth, filesStore, dialogsStore }) => {
const {
selection,
setFile,
setFolder,
setFilesOwner,
setIsLoading,
isLoading,
} = filesStore;
const { ownerPanelVisible, setChangeOwnerPanelVisible } = dialogsStore;
return {

View File

@ -0,0 +1,44 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
const homepage = config.homepage;
//import LanguageDetector from "i18next-browser-languagedetector";
// not like to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
const languages = ["en", "ru"];
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
supportedLngs: languages,
whitelist: languages,
fallbackLng: "en",
load: "languageOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: `${homepage}/locales/{{lng}}/EmbeddingPanel.json`,
},
ns: ["EmbeddingPanel"],
defaultNS: "EmbeddingPanel",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -9,7 +9,7 @@ import Link from "@appserver/components/link";
import TextInput from "@appserver/components/text-input";
import Textarea from "@appserver/components/textarea";
import toastr from "studio/toastr";
import { withTranslation } from "react-i18next";
import { withTranslation, I18nextProvider } from "react-i18next";
import {
StyledEmbeddingPanel,
StyledContent,
@ -17,6 +17,7 @@ import {
StyledBody,
} from "../StyledPanels";
import copy from "copy-to-clipboard";
import i18n from "./i18n";
class EmbeddingPanelComponent extends React.Component {
constructor(props) {
@ -128,7 +129,7 @@ class EmbeddingPanelComponent extends React.Component {
<StyledHeaderContent>
<IconButton
size="16"
iconName="ArrowPathIcon"
iconName="/static/images/arrow.path.react.svg"
onClick={this.onArrowClick}
color="#A3A9AE"
/>
@ -215,4 +216,12 @@ EmbeddingPanelComponent.propTypes = {
onClose: PropTypes.func,
};
export default withTranslation("EmbeddingPanel")(EmbeddingPanelComponent);
const EmbeddingPanel = withTranslation("EmbeddingPanel")(
EmbeddingPanelComponent
);
export default (props) => (
<I18nextProvider i18n={i18n}>
<EmbeddingPanel {...props} />
</I18nextProvider>
);

View File

@ -282,14 +282,12 @@ const NewFilesPanel = withTranslation("NewFilesPanel")(NewFilesPanelComponent);
export default inject(
({
initFilesStore,
filesStore,
mediaViewerDataStore,
treeFoldersStore,
formatsStore,
filesActionsStore,
}) => {
const { setIsLoading } = initFilesStore;
const {
files,
folders,
@ -297,6 +295,7 @@ export default inject(
filter,
addFileToRecentlyViewed,
setNewRowItems,
setIsLoading,
} = filesStore;
const { treeFolders, setTreeFolders } = treeFoldersStore;
const { setMediaViewerData } = mediaViewerDataStore;

View File

@ -0,0 +1,69 @@
import React, { useEffect } from "react";
import { Provider as MobxProvider } from "mobx-react";
import { inject, observer } from "mobx-react";
import { getShareFiles } from "@appserver/common/api/files";
import SharingPanel from "../SharingPanel";
import stores from "../../../store/index";
import store from "studio/store";
const { auth: authStore } = store;
const SharingDialog = ({
sharingObject,
onSuccess,
isVisible,
setSharingPanelVisible,
onCancel,
setSelection,
}) => {
useEffect(() => {
setSharingPanelVisible(isVisible);
}, [isVisible]);
useEffect(() => {
setSelection([sharingObject]);
}, []);
return (
<>
{isVisible && (
<SharingPanel
key="sharing-panel"
uploadPanelVisible={false}
onSuccess={onSuccess}
onCancel={onCancel}
/>
)}
</>
);
};
const SharingDialogWrapper = inject(({ dialogsStore, filesStore }) => {
const { getShareUsers, setSelection, selection } = filesStore;
const { setSharingPanelVisible } = dialogsStore;
return {
setSharingPanelVisible,
getShareUsers,
setSelection,
selection,
};
})(observer(SharingDialog));
class SharingModal extends React.Component {
static getSharingSettings = (fileId) => {
return getShareFiles([+fileId], []).then((users) =>
SharingPanel.convertSharingUsers(users)
);
};
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<SharingDialogWrapper {...this.props} />
</MobxProvider>
);
}
}
export default SharingModal;

View File

@ -31,7 +31,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("FullAccess") && (
<DropDownItem
label={t("FullAccess")}
icon="images/access.edit.react.svg"
icon="/static/images/access.edit.react.svg"
data-id={itemId}
data-access={FullAccess}
onClick={onAccessChange}
@ -41,7 +41,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("FilterEditing") && (
<DropDownItem
label={t("CustomFilter")}
icon="images/custom.filter.react.svg"
icon="/static/images/custom.filter.react.svg"
data-id={itemId}
data-access={CustomFilter}
onClick={onAccessChange}
@ -51,7 +51,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("Review") && (
<DropDownItem
label={t("Review")}
icon="images/access.review.react.svg"
icon="/static/images/access.review.react.svg"
data-id={itemId}
data-access={Review}
onClick={onAccessChange}
@ -61,7 +61,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("FormFilling") && (
<DropDownItem
label={t("FormFilling")}
icon="images/access.form.react.svg"
icon="/static/images/access.form.react.svg"
data-id={itemId}
data-access={FormFilling}
onClick={onAccessChange}
@ -71,7 +71,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("Comment") && (
<DropDownItem
label={t("Comment")}
icon="images/access.comment.react.svg"
icon="/static/images/access.comment.react.svg"
data-id={itemId}
data-access={Comment}
onClick={onAccessChange}
@ -91,7 +91,7 @@ const AccessComboBox = (props) => {
{accessOptions.includes("DenyAccess") && (
<DropDownItem
label={t("DenyAccess")}
icon="images/access.none.react.svg"
icon="/static/images/access.none.react.svg"
data-id={itemId}
data-access={DenyAccess}
onClick={onAccessChange}

View File

@ -44,6 +44,7 @@ class SharingRow extends React.Component {
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 });
@ -228,7 +229,7 @@ class SharingRow extends React.Component {
!shareLink &&
!isLocked && (
<IconButton
iconName="images/remove.react.svg"
iconName="/static/images/remove.react.svg"
id={id}
{...onRemoveUserProp}
className="sharing_panel-remove-icon"

View File

@ -0,0 +1,44 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
const homepage = config.homepage;
//import LanguageDetector from "i18next-browser-languagedetector";
// not like to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
const languages = ["en", "ru"];
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
supportedLngs: languages,
whitelist: languages,
fallbackLng: "en",
load: "languageOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: `${homepage}/locales/{{lng}}/SharingPanel.json`,
},
ns: ["SharingPanel"],
defaultNS: "SharingPanel",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -8,6 +8,8 @@ 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 { withRouter } from "react-router";
import { withTranslation, Trans } from "react-i18next";
import toastr from "studio/toastr";
@ -23,13 +25,14 @@ 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";
import { I18nextProvider } from "react-i18next";
const SharingBodyStyle = { height: `calc(100vh - 156px)` };
class SharingPanelComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
showActionPanel: false,
isNotifyUsers: false,
@ -104,10 +107,11 @@ class SharingPanelComponent extends React.Component {
isDesktop,
setEncryptionAccess,
setShareFiles,
onSuccess,
} = this.props;
const folderIds = [];
const fileIds = [];
let folderIds = [];
let fileIds = [];
const share = [];
let externalAccess = null;
@ -150,6 +154,7 @@ class SharingPanelComponent extends React.Component {
folderIds.push(item.id);
}
}
const owner = shareDataItems.find((x) => x.isOwner);
const ownerId =
filesOwnerId !== owner.sharedTo.id ? owner.sharedTo.id : null;
@ -196,10 +201,10 @@ class SharingPanelComponent extends React.Component {
}
return Promise.resolve();
})
.then(() => onSuccess && onSuccess())
.catch((err) => toastr.error(err))
.finally(() => setIsLoading(false));
};
onNotifyUsersChange = () =>
this.setState({ isNotifyUsers: !this.state.isNotifyUsers });
@ -251,9 +256,6 @@ class SharingPanelComponent extends React.Component {
};
getShareData = () => {
const returnValue = this.getData();
const folderId = returnValue[0];
const fileId = returnValue[1];
const {
getAccessOption,
getExternalAccessOption,
@ -262,12 +264,17 @@ class SharingPanelComponent extends React.Component {
getShareUsers,
} = this.props;
const returnValue = this.getData();
const folderId = returnValue[0];
const fileId = returnValue[1];
if (folderId.length !== 0 || fileId.length !== 0) {
setIsLoading(true);
getShareUsers(folderId, fileId)
.then((shareDataItems) => {
const baseShareData = JSON.parse(JSON.stringify(shareDataItems));
const accessOptions = getAccessOption(selection);
const externalAccessOptions = getExternalAccessOption(selection);
const filesOwner = shareDataItems.find((x) => x.isOwner);
const filesOwnerId = filesOwner ? filesOwner.sharedTo.id : null;
@ -277,10 +284,11 @@ class SharingPanelComponent extends React.Component {
shareDataItems,
accessOptions,
externalAccessOptions,
showPanel: true,
//showPanel: true,
filesOwnerId,
});
})
.catch((err) => {
toastr.error(err);
this.onClose();
@ -291,6 +299,7 @@ class SharingPanelComponent extends React.Component {
getInternalLink = () => {
const { homepage, selection } = this.props;
const item = selection[0];
const isFile = !!item.fileExst;
@ -326,8 +335,11 @@ class SharingPanelComponent extends React.Component {
setShareDataItems = (shareDataItems) => this.setState({ shareDataItems });
onClose = () => {
this.props.setSharingPanelVisible(false);
this.props.selectUploadedFile([]);
const { onCancel, setSharingPanelVisible, selectUploadedFile } = this.props;
setSharingPanelVisible(false);
selectUploadedFile([]);
onCancel && onCancel();
};
componentDidMount() {
@ -382,6 +394,8 @@ class SharingPanelComponent extends React.Component {
canShareOwnerChange,
isLoading,
uploadPanelVisible,
documentTitle,
sharingPanelVisible,
} = this.props;
const {
showActionPanel,
@ -393,12 +407,12 @@ class SharingPanelComponent extends React.Component {
showEmbeddingPanel,
showChangeOwnerPanel,
shareLink,
showPanel,
//showPanel,
accessOptions,
externalAccessOptions,
} = this.state;
const visible = showPanel;
const visible = sharingPanelVisible;
const zIndex = 310;
const onPlusClickProp = !isLoading ? { onClick: this.onPlusClick } : {};
const internalLink = selection.length === 1 && this.getInternalLink();
@ -417,7 +431,7 @@ class SharingPanelComponent extends React.Component {
{uploadPanelVisible && (
<IconButton
size="16"
iconName="ArrowPathIcon"
iconName="/static/images/arrow.path.react.svg"
onClick={this.onClose}
color="A3A9AE"
/>
@ -429,7 +443,7 @@ class SharingPanelComponent extends React.Component {
<div ref={this.ref} className="sharing_panel-drop-down-wrapper">
<IconButton
size="17"
iconName="images/actions.header.touch.react.svg"
iconName="/static/images/actions.header.touch.react.svg"
className="sharing_panel-plus-icon"
{...onPlusClickProp}
color="A3A9AE"
@ -466,26 +480,38 @@ class SharingPanelComponent extends React.Component {
stype="mediumBlack"
style={SharingBodyStyle}
>
{shareDataItems.map((item, index) => (
<SharingRow
t={t}
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.onShowEmbeddingPanel}
onToggleLink={this.onToggleLink}
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
isLoading={isLoading}
/>
))}
{!isLoading ? (
shareDataItems.map((item, index) => (
<SharingRow
t={t}
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.onShowEmbeddingPanel}
onToggleLink={this.onToggleLink}
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
isLoading={isLoading}
documentTitle={documentTitle}
/>
))
) : (
<div key="loader" className="sharing-panel-loader-wrapper">
<Loader
type="oval"
size="16px"
className="sharing-panel-loader"
/>
<Text as="span">{t("LoadingLabel")}</Text>
</div>
)}
{isNotifyUsers && (
<div className="sharing_panel-text-area">
<Textarea
@ -565,23 +591,14 @@ class SharingPanelComponent extends React.Component {
}
}
const SharingPanel = withTranslation("SharingPanel")(SharingPanelComponent);
export default inject(
const SharingPanel = inject(
(
{
auth,
initFilesStore,
filesStore,
uploadDataStore,
dialogsStore,
treeFoldersStore,
},
{ auth, filesStore, uploadDataStore, dialogsStore, treeFoldersStore },
{ uploadPanelVisible }
) => {
const { replaceFileStream, setEncryptionAccess } = auth;
const { customNames, isDesktopClient } = auth.settingsStore;
const { setIsLoading, isLoading } = initFilesStore;
const {
selection,
canShareOwnerChange,
@ -591,28 +608,31 @@ export default inject(
setFolder,
getShareUsers,
setShareFiles,
setIsLoading,
isLoading,
} = filesStore;
const { isPrivacyFolder } = treeFoldersStore;
const { setSharingPanelVisible } = dialogsStore;
const { setSharingPanelVisible, sharingPanelVisible } = dialogsStore;
const {
uploadSelection,
selectedUploadFile,
selectUploadedFile,
updateUploadedItem,
} = uploadDataStore;
return {
isMyId: auth.userStore.user.id,
isMyId: auth.userStore.user && auth.userStore.user.id,
groupsCaption: customNames.groupsCaption,
isDesktop: isDesktopClient,
homepage: config.homepage,
selection: uploadPanelVisible ? uploadSelection : selection,
selection: uploadPanelVisible ? selectedUploadFile : selection,
isLoading,
isPrivacy: isPrivacyFolder,
uploadSelection,
selectedUploadFile,
canShareOwnerChange,
setIsLoading,
setSharingPanelVisible,
sharingPanelVisible,
selectUploadedFile,
updateUploadedItem,
replaceFileStream,
@ -625,4 +645,48 @@ export default inject(
setShareFiles,
};
}
)(withRouter(observer(SharingPanel)));
)(observer(withTranslation("SharingPanel")(SharingPanelComponent)));
class Panel extends React.Component {
static convertSharingUsers = (shareDataItems) => {
const t = i18n.getFixedT(null, "SharingPanel");
let sharingSettings = [];
for (let i = 1; i < shareDataItems.length; i++) {
let resultAccess =
shareDataItems[i].access === 1
? t("FullAccess")
: shareDataItems[i].access === 2
? t("ReadOnly")
: shareDataItems[i].access === 3
? t("DenyAccess")
: shareDataItems[i].access === 5
? t("Review")
: shareDataItems[i].access === 6
? t("Comment")
: shareDataItems[i].access === 7
? t("FormFilling")
: shareDataItems[i].access === 8
? t("CustomFilter")
: "";
let obj = {
user:
shareDataItems[i].sharedTo.displayName ||
shareDataItems[i].sharedTo.name,
permissions: resultAccess,
};
sharingSettings.push(obj);
}
return sharingSettings;
};
render() {
return (
<I18nextProvider i18n={i18n}>
<SharingPanel {...this.props} />
</I18nextProvider>
);
}
}
export default Panel;

View File

@ -6,7 +6,7 @@ import ToggleButton from "@appserver/components/toggle-button";
import { StyledLinkRow } from "../StyledPanels";
import AccessComboBox from "./AccessComboBox";
import { ShareAccessRights } from "@appserver/common/constants";
import AccessEditIcon from "../../../../public/images/access.edit.react.svg";
import AccessEditIcon from "../../../../../../../public/images/access.edit.react.svg";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
const StyledAccessEditIcon = styled(AccessEditIcon)`

View File

@ -195,6 +195,15 @@ const StyledContent = styled.div`
min-height: 16px;
}
}
.sharing-panel-loader-wrapper {
margin-top: 8px;
padding-left: 32px;
}
.sharing-panel-loader {
display: inline;
margin-right: 10px;
}
`;
const StyledHeaderContent = styled.div`

View File

@ -89,9 +89,9 @@ VersionHistoryPanel.propTypes = {
fileId: PropTypes.string,
};
export default inject(({ auth, initFilesStore, versionHistoryStore }) => {
export default inject(({ auth, filesStore, versionHistoryStore }) => {
const { isTabletView } = auth.settingsStore;
const { isLoading } = initFilesStore;
const { isLoading } = filesStore;
const {
fileId,
versions,

View File

@ -7,21 +7,21 @@ export const presentInArray = (array, search, caseInsensitive = false) => {
export const getAccessIcon = (access) => {
switch (access) {
case 1:
return "images/access.edit.react.svg";
return "/static/images/access.edit.react.svg";
case 2:
return "/static/images/eye.react.svg";
case 3:
return "images/access.none.react.svg";
return "/static/images/access.none.react.svg";
case 4:
return "images/catalog.question.react.svg";
case 5:
return "images/access.review.react.svg";
return "/static/images/access.review.react.svg";
case 6:
return "images/access.comment.react.svg";
return "/static/images/access.comment.react.svg";
case 7:
return "images/access.form.react.svg";
return "/static/images/access.form.react.svg";
case 8:
return "images/custom.filter.react.svg";
return "/static/images/custom.filter.react.svg";
default:
return;
}

View File

@ -10,7 +10,6 @@ import("./bootstrap");
// import * as serviceWorker from "./serviceWorker";
// import { ErrorBoundary, store as commonStore } from "asc-web-common";
// import { Provider as MobxProvider } from "mobx-react";
// import initFilesStore from "./store/InitFilesStore";
// import filesStore from "./store/FilesStore";
// import settingsStore from "./store/SettingsStore";
// import mediaViewerDataStore from "./store/MediaViewerDataStore";
@ -26,7 +25,6 @@ import("./bootstrap");
// ReactDOM.render(
// <MobxProvider
// auth={authStore}
// initFilesStore={initFilesStore}
// filesStore={filesStore}
// settingsStore={settingsStore}
// mediaViewerDataStore={mediaViewerDataStore}

View File

@ -139,4 +139,4 @@ class DocserviceStore {
canWebFilterEditing = (extension) =>
presentInArray(this.customfilterDocs, extension);
}
export default DocserviceStore;
export default new DocserviceStore();

View File

@ -25,4 +25,4 @@ class FileActionStore {
};
}
export default FileActionStore;
export default new FileActionStore();

View File

@ -1,12 +1,4 @@
import { makeAutoObservable } from "mobx";
import store from "studio/store";
import uploadDataStore from "./UploadDataStore";
import treeFoldersStore from "./TreeFoldersStore";
import filesStore from "./FilesStore";
import selectedFolderStore from "./SelectedFolderStore";
import initFilesStore from "./InitFilesStore";
import settingsStore from "./SettingsStore";
import dialogsStore from "./DialogsStore";
import {
removeFiles,
@ -24,42 +16,41 @@ import { FileAction } from "@appserver/common/constants";
import { TIMEOUT } from "../helpers/constants";
import { loopTreeFolders } from "../helpers/files-helpers";
const { auth } = store;
const {
fetchFiles,
markItemAsFavorite,
removeItemFromFavorite,
fetchFavoritesFolder,
getFileInfo,
setSelected,
selectFile,
deselectFile,
setSelection,
setFile,
} = filesStore;
const { setTreeFolders } = treeFoldersStore;
const { setIsLoading } = initFilesStore;
const { secondaryProgressDataStore, loopFilesOperations } = uploadDataStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const {
setConnectDialogVisible,
setConnectItem,
setThirdPartyMoveDialogVisible,
setDestFolderId,
} = dialogsStore;
class FilesActionStore {
constructor() {
authStore;
uploadDataStore;
treeFoldersStore;
filesStore;
selectedFolderStore;
settingsStore;
dialogsStore;
constructor(
authStore,
uploadDataStore,
treeFoldersStore,
filesStore,
selectedFolderStore,
settingsStore,
dialogsStore
) {
makeAutoObservable(this);
this.authStore = authStore;
this.uploadDataStore = uploadDataStore;
this.treeFoldersStore = treeFoldersStore;
this.filesStore = filesStore;
this.selectedFolderStore = selectedFolderStore;
this.settingsStore = settingsStore;
this.dialogsStore = dialogsStore;
}
deleteAction = (translations) => {
const { isRecycleBinFolder, isPrivacyFolder } = treeFoldersStore;
const { selection } = filesStore;
const { isRecycleBinFolder, isPrivacyFolder } = this.treeFoldersStore;
const { selection } = this.filesStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const deleteAfter = true; //Delete after finished TODO: get from settings
const immediately = isRecycleBinFolder || isPrivacyFolder ? true : false; //Don't move to the Recycle Bin
@ -102,8 +93,13 @@ class FilesActionStore {
};
loopDeleteOperation = (id, translations) => {
const { filter } = filesStore;
const { isRecycleBinFolder } = treeFoldersStore;
const { filter, fetchFiles } = this.filesStore;
const { isRecycleBinFolder, setTreeFolders } = this.treeFoldersStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const successMessage = isRecycleBinFolder
? translations.deleteFromTrash
: translations.deleteSelectedElem;
@ -129,10 +125,10 @@ class FilesActionStore {
alert: false,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
fetchFiles(selectedFolderStore.id, filter).then((data) => {
fetchFiles(this.selectedFolderStore.id, filter).then((data) => {
if (!isRecycleBinFolder) {
const path = data.selectedFolder.pathParts.slice(0);
const newTreeFolders = treeFoldersStore.treeFolders;
const newTreeFolders = this.treeFoldersStore.treeFolders;
const folders = data.selectedFolder.folders;
const foldersCount = data.selectedFolder.foldersCount;
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
@ -151,6 +147,10 @@ class FilesActionStore {
};
getDownloadProgress = (data, label) => {
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const url = data.url;
return getProgress()
@ -180,7 +180,11 @@ class FilesActionStore {
};
downloadAction = (label) => {
const { selection } = filesStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const { selection } = this.filesStore;
const fileIds = [];
const folderIds = [];
const items = [];
@ -222,15 +226,22 @@ class FilesActionStore {
};
editCompleteAction = (id, selectedItem) => {
const { filter, folders, files, fileActionStore } = filesStore;
const {
filter,
folders,
files,
fileActionStore,
fetchFiles,
setIsLoading,
} = this.filesStore;
const { type, setAction } = fileActionStore;
const { treeFolders } = treeFoldersStore;
const { treeFolders, setTreeFolders } = this.treeFoldersStore;
const items = [...folders, ...files];
const item = items.find((o) => o.id === id && !o.fileExst); //TODO: maybe need files find and folders find, not at one function?
if (type === FileAction.Create || type === FileAction.Rename) {
setIsLoading(true);
fetchFiles(selectedFolderStore.id, filter)
fetchFiles(this.selectedFolderStore.id, filter)
.then((data) => {
const newItem = (item && item.id) === -1 ? null : item; //TODO: not add new folders?
if (!selectedItem.fileExst) {
@ -250,7 +261,8 @@ class FilesActionStore {
};
onSelectItem = (item) => {
filesStore.selected === "close" && setSelected("none");
const { setSelection, selected, setSelected } = this.filesStore;
selected === "close" && setSelected("none");
setSelection([item]);
};
@ -261,6 +273,12 @@ class FilesActionStore {
conflictResolveType,
deleteAfter
) => {
const { loopFilesOperations } = this.uploadDataStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
return copyToFolder(
destFolderId,
folderIds,
@ -289,7 +307,13 @@ class FilesActionStore {
conflictResolveType,
deleteAfter
) => {
moveToFolder(
const { loopFilesOperations } = this.uploadDataStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
return moveToFolder(
destFolderId,
folderIds,
fileIds,
@ -311,6 +335,11 @@ class FilesActionStore {
};
deleteFileAction = (fileId, currentFolderId, translations) => {
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
setSecondaryProgressBarData({
icon: "trash",
visible: true,
@ -333,6 +362,11 @@ class FilesActionStore {
};
deleteFolderAction = (folderId, currentFolderId, translations) => {
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
setSecondaryProgressBarData({
icon: "trash",
visible: true,
@ -355,8 +389,16 @@ class FilesActionStore {
};
loopDeleteProgress = (id, folderId, isFolder, translations) => {
const { filter } = filesStore;
const { treeFolders, isRecycleBinFolder } = treeFoldersStore;
const { filter, fetchFiles } = this.filesStore;
const {
treeFolders,
isRecycleBinFolder,
setTreeFolders,
} = this.treeFoldersStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
getProgress().then((res) => {
const deleteProgress = res.find((x) => x.id === id);
@ -406,20 +448,28 @@ class FilesActionStore {
};
lockFileAction = (id, locked) => {
const { setFile } = this.filesStore;
return lockFile(id, locked).then((res) => setFile(res));
};
finalizeVersionAction = (id) => {
const { fetchFiles, setIsLoading } = this.filesStore;
setIsLoading(true);
return finalizeVersion(id, 0, false)
.then(() => {
fetchFiles(selectedFolderStore.id, filesStore.filter);
fetchFiles(this.selectedFolderStore.id, this.filesStore.filter);
})
.finally(() => setIsLoading(false));
};
duplicateAction = (item, label) => {
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const folderIds = [];
const fileIds = [];
item.fileExst ? fileIds.push(item.id) : folderIds.push(item.id);
@ -435,7 +485,7 @@ class FilesActionStore {
});
return this.copyToAction(
selectedFolderStore.id,
this.selectedFolderStore.id,
folderIds,
fileIds,
conflictResolveType,
@ -444,6 +494,13 @@ class FilesActionStore {
};
setFavoriteAction = (action, id) => {
const {
markItemAsFavorite,
removeItemFromFavorite,
fetchFavoritesFolder,
getFileInfo,
setSelected,
} = this.filesStore;
//let data = selection.map(item => item.id)
switch (action) {
case "mark":
@ -452,8 +509,8 @@ class FilesActionStore {
case "remove":
return removeItemFromFavorite([id])
.then(() => {
return treeFoldersStore.isFavoritesFolder
? fetchFavoritesFolder(selectedFolderStore.id)
return this.treeFoldersStore.isFavoritesFolder
? fetchFavoritesFolder(this.selectedFolderStore.id)
: getFileInfo(id);
})
.then(() => setSelected("close"));
@ -463,7 +520,8 @@ class FilesActionStore {
};
selectRowAction = (checked, file) => {
filesStore.selected === "close" && setSelected("none");
const { selected, setSelected, selectFile, deselectFile } = this.filesStore;
selected === "close" && setSelected("none");
if (checked) {
selectFile(file);
} else {
@ -472,16 +530,17 @@ class FilesActionStore {
};
openLocationAction = (locationId, isFolder) => {
const locationFilter = isFolder ? filesStore.filter : null;
const locationFilter = isFolder ? this.filesStore.filter : null;
return fetchFiles(locationId, locationFilter).then(() =>
return this.filesStore.fetchFiles(locationId, locationFilter).then(() =>
//isFolder ? null : this.selectRowAction(!checked, item)
isFolder ? null : this.selectRowAction(false, item)
);
};
setThirdpartyInfo = (providerKey) => {
const { providers, capabilities } = settingsStore.thirdPartyStore;
const { setConnectDialogVisible, setConnectItem } = this.dialogsStore;
const { providers, capabilities } = this.settingsStore.thirdPartyStore;
const provider = providers.find((x) => x.provider_key === providerKey);
const capabilityItem = capabilities.find((x) => x[0] === providerKey);
const capability = {
@ -503,9 +562,18 @@ class FilesActionStore {
const conflictResolveType = 0; //Skip = 0, Overwrite = 1, Duplicate = 2 TODO: get from settings
const deleteAfter = true;
const { selection } = filesStore;
const { isRootFolder } = selectedFolderStore;
const { isShareFolder, isCommonFolder } = treeFoldersStore;
const { selection } = this.filesStore;
const { isRootFolder } = this.selectedFolderStore;
const { isShareFolder, isCommonFolder } = this.treeFoldersStore;
const {
setThirdPartyMoveDialogVisible,
setDestFolderId,
} = this.dialogsStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
for (let item of selection) {
if (item.providerKey && !isRootFolder) {
@ -531,7 +599,7 @@ class FilesActionStore {
alert: false,
});
if (auth.isAdmin) {
if (this.authStore.isAdmin) {
if (isShareFolder) {
this.copyToAction(
destFolderId,
@ -571,4 +639,4 @@ class FilesActionStore {
};
}
export default new FilesActionStore();
export default FilesActionStore;

View File

@ -1,5 +1,4 @@
import { makeObservable, action, observable, computed } from "mobx";
import store from "studio/store";
import { makeAutoObservable } from "mobx";
import api from "@appserver/common/api";
import {
FolderType,
@ -9,41 +8,32 @@ import {
AppServerConfig,
} from "@appserver/common/constants";
import history from "@appserver/common/history";
import FileActionStore from "./FileActionStore";
import selectedFolderStore from "./SelectedFolderStore";
import formatsStore from "./FormatsStore";
import treeFoldersStore from "./TreeFoldersStore";
import { createTreeFolders } from "../helpers/files-helpers";
import config from "../../package.json";
import { combineUrl } from "@appserver/common/utils";
import { updateTempContent } from "@appserver/common/utils";
const { FilesFilter } = api;
const { settingsStore, userStore, isAdmin } = store.auth;
const {
iconFormatsStore,
mediaViewersFormatsStore,
docserviceStore,
} = formatsStore;
const {
isSpreadsheet,
isPresentation,
getFileIcon,
getFolderIcon,
getIcon,
} = iconFormatsStore;
const {
canWebEdit,
canWebComment,
canWebReview,
canFormFillingDocs,
canWebFilterEditing,
} = docserviceStore;
const { setExpandedKeys, setSelectedNode } = treeFoldersStore;
class FilesStore {
fileActionStore = null;
authStore;
settingsStore;
userStore;
fileActionStore;
selectedFolderStore;
treeFoldersStore;
formatsStore;
isLoaded = false;
isLoading = false;
viewAs = "row";
dragging = false;
privacyInstructions = "https://www.onlyoffice.com/private-rooms.aspx";
isInit = false;
tooltipPageX = 0;
tooltipPageY = 0;
startDrag = false;
firstLoad = true;
files = [];
@ -53,65 +43,119 @@ class FilesStore {
filter = FilesFilter.getDefault(); //TODO: FILTER
newRowItems = [];
constructor() {
makeObservable(this, {
fileActionStore: observable,
constructor(
authStore,
settingsStore,
userStore,
fileActionStore,
selectedFolderStore,
treeFoldersStore,
formatsStore
) {
const pathname = window.location.pathname.toLowerCase();
this.isEditor = pathname.indexOf("doceditor") !== -1;
firstLoad: observable,
files: observable,
folders: observable,
selected: observable,
filter: observable, //TODO: FILTER
selection: observable,
newRowItems: observable,
filesList: computed,
sortedFiles: computed,
canCreate: computed,
isHeaderVisible: computed,
isHeaderIndeterminate: computed,
isHeaderChecked: computed,
userAccess: computed,
isAccessedSelected: computed,
isOnlyFoldersSelected: computed,
isThirdPartySelection: computed,
isWebEditSelected: computed,
selectionTitle: computed,
currentFilesCount: computed,
canShare: computed,
setFirstLoad: action,
setFiles: action,
setFolders: action,
setSelected: action,
setFilesFilter: action, //TODO: FILTER
setSelection: action,
setNewRowItems: action,
setFilesOwner: action,
fetchFiles: action,
selectFile: action,
deselectFile: action,
addFileToRecentlyViewed: action,
createFile: action,
createFolder: action,
updateFile: action,
getAccessOption: action,
getExternalAccessOption: action,
setSelections: action,
getShareUsers: action,
setShareFiles: action,
markItemAsFavorite: action,
removeItemFromFavorite: action,
fetchFavoritesFolder: action,
getFileInfo: action,
setFolder: action,
setFile: action,
});
this.fileActionStore = new FileActionStore();
makeAutoObservable(this);
this.authStore = authStore;
this.settingsStore = settingsStore;
this.userStore = userStore;
this.fileActionStore = fileActionStore;
this.selectedFolderStore = selectedFolderStore;
this.treeFoldersStore = treeFoldersStore;
this.formatsStore = formatsStore;
}
setIsLoaded = (isLoaded) => {
this.isLoaded = isLoaded;
};
setViewAs = (viewAs) => {
this.viewAs = viewAs;
};
setDragging = (dragging) => {
this.dragging = dragging;
};
setIsLoading = (isLoading) => {
this.isLoading = isLoading;
};
setTooltipPosition = (tooltipPageX, tooltipPageY) => {
this.tooltipPageX = tooltipPageX;
this.tooltipPageY = tooltipPageY;
};
setStartDrag = (startDrag) => {
this.startDrag = startDrag;
};
get tooltipValue() {
if (!this.dragging) return null;
const selectionLength = this.selection.length;
const elementTitle = selectionLength && this.selection[0].title;
const singleElement = selectionLength === 1;
const filesCount = singleElement ? elementTitle : selectionLength;
const { isShareFolder, isCommonFolder } = this.treeFoldersStore;
let operationName;
if (this.authStore.isAdmin && isShareFolder) {
operationName = "copy";
} else if (!this.authStore.isAdmin && (isShareFolder || isCommonFolder)) {
operationName = "copy";
} else {
operationName = "move";
}
return operationName === "copy"
? singleElement
? { label: "TooltipElementCopyMessage", filesCount }
: { label: "TooltipElementsCopyMessage", filesCount }
: singleElement
? { label: "TooltipElementMoveMessage", filesCount }
: { label: "TooltipElementsMoveMessage", filesCount };
}
initFiles = () => {
if (this.isInit) return;
this.isInit = true;
const { isAuthenticated } = this.authStore;
const {
getPortalCultures,
isDesktopClient,
getIsEncryptionSupport,
getEncryptionKeys,
setModuleInfo,
} = this.settingsStore;
setModuleInfo(config.homepage, config.id);
const requests = [];
updateTempContent();
if (!isAuthenticated) {
return this.setIsLoaded(true);
} else {
updateTempContent(isAuthenticated);
}
if (!this.isEditor) {
requests.push(
getPortalCultures(),
this.treeFoldersStore.fetchTreeFolders()
);
if (isDesktopClient) {
requests.push(getIsEncryptionSupport(), getEncryptionKeys());
}
}
return Promise.all(requests);
};
setFirstLoad = (firstLoad) => {
this.firstLoad = firstLoad;
};
@ -214,11 +258,16 @@ class FilesStore {
fetchFiles = (folderId, filter, clearFilter = true) => {
const filterData = filter ? filter.clone() : FilesFilter.getDefault();
filterData.folder = folderId;
const { privacyFolder, expandedKeys } = treeFoldersStore;
const {
privacyFolder,
expandedKeys,
setExpandedKeys,
setSelectedNode,
} = this.treeFoldersStore;
setSelectedNode([folderId + ""]);
if (privacyFolder && privacyFolder.id === +folderId) {
if (!store.auth.settingsStore.isEncryptionSupport) {
if (!this.settingsStore.isEncryptionSupport) {
const newExpandedKeys = createTreeFolders(
privacyFolder.pathParts,
expandedKeys
@ -232,7 +281,7 @@ class FilesStore {
this.fileActionStore.setAction({ type: null });
this.setSelected("close");
selectedFolderStore.setSelectedFolder({
this.selectedFolderStore.setSelectedFolder({
folders: [],
...privacyFolder,
pathParts: privacyFolder.pathParts,
@ -252,12 +301,12 @@ class FilesStore {
filterData.total = data.total;
this.setFilesFilter(filterData); //TODO: FILTER
this.setFolders(
isPrivacyFolder && !store.auth.settingsStore.isEncryptionSupport
isPrivacyFolder && !this.settingsStore.isEncryptionSupport
? []
: data.folders
);
this.setFiles(
isPrivacyFolder && !store.auth.settingsStore.isEncryptionSupport
isPrivacyFolder && !this.settingsStore.isEncryptionSupport
? []
: data.files
);
@ -266,7 +315,7 @@ class FilesStore {
this.setSelected("close");
}
selectedFolderStore.setSelectedFolder({
this.selectedFolderStore.setSelectedFolder({
folders: data.folders,
...data.current,
pathParts: data.pathParts,
@ -274,7 +323,7 @@ class FilesStore {
});
const selectedFolder = {
selectedFolder: { ...selectedFolderStore },
selectedFolder: { ...this.selectedFolderStore },
};
return Promise.resolve(selectedFolder);
});
@ -303,13 +352,14 @@ class FilesStore {
getFilesContextOptions = (item, canOpenPlayer) => {
const options = [];
const isVisitor = (userStore.user && userStore.user.isVisitor) || false;
const isVisitor =
(this.userStore.user && this.userStore.user.isVisitor) || false;
const isFile = !!item.fileExst;
const isFavorite = item.fileStatus === 32;
const isFullAccess = item.access < 2;
const isThirdPartyFolder =
item.providerKey && selectedFolderStore.isRootFolder;
item.providerKey && this.selectedFolderStore.isRootFolder;
if (item.id <= 0) return [];
@ -317,7 +367,7 @@ class FilesStore {
isRecycleBinFolder,
isPrivacyFolder,
isRecentFolder,
} = treeFoldersStore;
} = this.treeFoldersStore;
if (isRecycleBinFolder) {
options.push("download");
@ -418,7 +468,7 @@ class FilesStore {
};
addFileToRecentlyViewed = (fileId) => {
if (treeFoldersStore.isPrivacyFolder) return Promise.resolve();
if (this.treeFoldersStore.isPrivacyFolder) return Promise.resolve();
return api.files.addFileToRecentlyViewed(fileId);
};
@ -455,7 +505,7 @@ class FilesStore {
};
getFilesCount = () => {
const { filesCount, foldersCount } = selectedFolderStore;
const { filesCount, foldersCount } = this.selectedFolderStore;
return filesCount + this.folders ? this.folders.length : foldersCount;
};
@ -466,15 +516,16 @@ class FilesStore {
};
canShareOwnerChange = (item) => {
const userId = userStore.user.id;
const userId = this.userStore.user.id;
const isCommonFolder =
treeFoldersStore.commonFolder &&
selectedFolderStore.pathParts &&
treeFoldersStore.commonFolder.id === selectedFolderStore.pathParts[0];
this.treeFoldersStore.commonFolder &&
this.selectedFolderStore.pathParts &&
this.treeFoldersStore.commonFolder.id ===
this.selectedFolderStore.pathParts[0];
if (item.providerKey || !isCommonFolder) {
return false;
} else if (isAdmin) {
} else if (this.authStore.isAdmin) {
return true;
} else if (item.createdBy.id === userId) {
return true;
@ -484,8 +535,9 @@ class FilesStore {
};
get canShare() {
const folderType = selectedFolderStore.rootFolderType;
const isVisitor = (userStore.user && userStore.user.isVisitor) || false;
const folderType = this.selectedFolderStore.rootFolderType;
const isVisitor =
(this.userStore.user && this.userStore.user.isVisitor) || false;
if (isVisitor) {
return false;
@ -497,7 +549,7 @@ class FilesStore {
case FolderType.SHARE:
return false;
case FolderType.COMMON:
return isAdmin;
return this.authStore.isAdmin;
case FolderType.TRASH:
return false;
case FolderType.Favorites:
@ -514,10 +566,14 @@ class FilesStore {
get currentFilesCount() {
const serviceFilesCount = this.getServiceFilesCount();
const filesCount = this.getFilesCount();
return selectedFolderStore.providerItem ? serviceFilesCount : filesCount;
return this.selectedFolderStore.providerItem
? serviceFilesCount
: filesCount;
}
get iconOfDraggedFile() {
const { getIcon } = this.formatsStore.iconFormatsStore;
if (this.selection.length === 1) {
const icon = getIcon(
24,
@ -547,19 +603,21 @@ class FilesStore {
}
get canCreate() {
switch (selectedFolderStore.rootFolderType) {
switch (this.selectedFolderStore.rootFolderType) {
case FolderType.USER:
return true;
case FolderType.SHARE:
const canCreateInSharedFolder = selectedFolderStore.access === 1;
return !selectedFolderStore.isRootFolder && canCreateInSharedFolder;
const canCreateInSharedFolder = this.selectedFolderStore.access === 1;
return (
!this.selectedFolderStore.isRootFolder && canCreateInSharedFolder
);
case FolderType.Privacy:
return (
store.auth.settingsStore.isDesktopClient &&
store.auth.settingsStore.isEncryptionSupport
this.settingsStore.isDesktopClient &&
this.settingsStore.isEncryptionSupport
);
case FolderType.COMMON:
return isAdmin;
return this.authStore.isAdmin;
case FolderType.TRASH:
default:
return false;
@ -567,6 +625,8 @@ class FilesStore {
}
onCreateAddTempItem = (items) => {
const { getFileIcon, getFolderIcon } = this.formatsStore.iconFormatsStore;
if (items.length && items[0].id === -1) return; //TODO: if change media collection from state remove this;
const icon = this.fileActionStore.extension
? getFileIcon(`.${this.fileActionStore.extension}`, 24)
@ -575,13 +635,15 @@ class FilesStore {
items.unshift({
id: -1,
title: "",
parentId: selectedFolderStore.id,
parentId: this.selectedFolderStore.id,
fileExst: this.fileActionStore.extension,
icon,
});
};
get filesList() {
const { mediaViewersFormatsStore, iconFormatsStore } = this.formatsStore;
const { getIcon } = iconFormatsStore;
//return [...this.folders, ...this.files];
const items = [...this.folders, ...this.files];
@ -674,6 +736,12 @@ class FilesStore {
}
get sortedFiles() {
const {
isSpreadsheet,
isPresentation,
} = this.formatsStore.iconFormatsStore;
const { canWebEdit } = this.formatsStore.docserviceStore;
const formatKeys = Object.freeze({
OriginalFormat: 0,
});
@ -708,14 +776,14 @@ class FilesStore {
}
get userAccess() {
switch (selectedFolderStore.rootFolderType) {
switch (this.selectedFolderStore.rootFolderType) {
case FolderType.USER:
return true;
case FolderType.SHARE:
return false;
case FolderType.COMMON:
return (
isAdmin ||
this.authStore.isAdmin ||
this.selection.some((x) => x.access === 0 || x.access === 1)
);
case FolderType.Privacy:
@ -731,7 +799,7 @@ class FilesStore {
return (
(this.selection.length &&
this.selection.every((x) => x.access === 1 || x.access === 0)) ||
(isAdmin && this.selection.length)
(this.authStore.isAdmin && this.selection.length)
);
}
@ -741,15 +809,15 @@ class FilesStore {
get isThirdPartySelection() {
const withProvider = this.selection.find((x) => !x.providerKey);
return !withProvider && selectedFolderStore.isRootFolder;
return !withProvider && this.selectedFolderStore.isRootFolder;
}
get isWebEditSelected() {
const { editedDocs } = this.formatsStore.docserviceStore;
return this.selection.some((selected) => {
if (selected.isFolder === true || !selected.fileExst) return false;
return docserviceStore.editedDocs.find(
(format) => selected.fileExst === format
);
return editedDocs.find((format) => selected.fileExst === format);
});
}
@ -759,24 +827,37 @@ class FilesStore {
}
getOptions = (selection, externalAccess = false) => {
const webEdit = selection.find((x) => canWebEdit(x.fileExst));
const webComment = selection.find((x) => canWebComment(x.fileExst));
const webReview = selection.find((x) => canWebReview(x.fileExst));
const formFillingDocs = selection.find((x) =>
canFormFillingDocs(x.fileExst)
);
const webFilter = selection.find((x) => canWebFilterEditing(x.fileExst));
const {
canWebEdit,
canWebComment,
canWebReview,
canFormFillingDocs,
canWebFilterEditing,
} = this.formatsStore.docserviceStore;
let AccessOptions = [];
if (webEdit || !externalAccess) AccessOptions.push("FullAccess");
AccessOptions.push("ReadOnly", "DenyAccess");
const webEdit = selection.find((x) => canWebEdit(x.fileExst));
const webComment = selection.find((x) => canWebComment(x.fileExst));
const webReview = selection.find((x) => canWebReview(x.fileExst));
const formFillingDocs = selection.find((x) =>
canFormFillingDocs(x.fileExst)
);
const webFilter = selection.find((x) => canWebFilterEditing(x.fileExst));
if (webEdit || !externalAccess) AccessOptions.push("FullAccess");
if (webComment) AccessOptions.push("Comment");
if (webReview) AccessOptions.push("Review");
if (formFillingDocs) AccessOptions.push("FormFilling");
if (webFilter) AccessOptions.push("FilterEditing");
return AccessOptions;
};
@ -958,7 +1039,7 @@ class FilesStore {
this.setFolders(favoritesFolder.folders);
this.setFiles(favoritesFolder.files);
selectedFolderStore.setSelectedFolder({
this.selectedFolderStore.setSelectedFolder({
folders: favoritesFolder.folders,
...favoritesFolder.current,
pathParts: favoritesFolder.pathParts,
@ -1002,4 +1083,4 @@ class FilesStore {
};
}
export default new FilesStore();
export default FilesStore;

View File

@ -1,24 +1,16 @@
import { makeObservable, observable } from "mobx";
import IconFormatsStore from "./IconFormatsStore";
import MediaViewersFormatsStore from "./MediaViewersFormatsStore";
import DocserviceStore from "./DocserviceStore";
import { makeAutoObservable } from "mobx";
class FormatsStore {
iconFormatsStore = null;
mediaViewersFormatsStore = null;
docserviceStore = null;
iconFormatsStore;
mediaViewersFormatsStore;
docserviceStore;
constructor() {
makeObservable(this, {
iconFormatsStore: observable,
mediaViewersFormatsStore: observable,
docserviceStore: observable,
});
this.iconFormatsStore = new IconFormatsStore();
this.mediaViewersFormatsStore = new MediaViewersFormatsStore();
this.docserviceStore = new DocserviceStore();
constructor(iconFormatsStore, mediaViewersFormatsStore, docserviceStore) {
makeAutoObservable(this);
this.iconFormatsStore = iconFormatsStore;
this.mediaViewersFormatsStore = mediaViewersFormatsStore;
this.docserviceStore = docserviceStore;
}
}
export default new FormatsStore();
export default FormatsStore;

View File

@ -395,4 +395,4 @@ class IconFormatsStore {
};
}
export default IconFormatsStore;
export default new IconFormatsStore();

View File

@ -1,12 +1,14 @@
import { makeAutoObservable } from "mobx";
import filesStore from "./FilesStore";
class MediaViewerDataStore {
filesStore;
id = null;
visible = false;
constructor() {
constructor(filesStore) {
makeAutoObservable(this);
this.filesStore = filesStore;
}
setMediaViewerData = (mediaData) => {
@ -18,8 +20,8 @@ class MediaViewerDataStore {
const playlist = [];
let id = 0;
if (filesStore.filesList) {
filesStore.filesList.forEach((file) => {
if (this.filesStore.filesList) {
this.filesStore.filesList.forEach((file) => {
if (file.canOpenPlayer) {
playlist.push({
id: id,
@ -35,4 +37,4 @@ class MediaViewerDataStore {
}
}
export default new MediaViewerDataStore();
export default MediaViewerDataStore;

View File

@ -52,4 +52,4 @@ class MediaViewersFormatsStore {
};
}
export default MediaViewersFormatsStore;
export default new MediaViewersFormatsStore();

View File

@ -1,10 +1,9 @@
import { makeAutoObservable } from "mobx";
import api from "@appserver/common/api";
import axios from "axios";
import ThirdPartyStore from "./ThirdPartyStore";
class SettingsStore {
thirdPartyStore = null;
thirdPartyStore;
isErrorSettings = null;
expandedSetting = null;
@ -18,10 +17,10 @@ class SettingsStore {
settingsIsLoaded = false;
constructor() {
constructor(thirdPartyStore) {
makeAutoObservable(this);
this.thirdPartyStore = new ThirdPartyStore();
this.thirdPartyStore = thirdPartyStore;
}
setIsLoaded = (isLoaded) => {
@ -116,4 +115,4 @@ class SettingsStore {
api.files.forceSave(data).then((res) => this.setFilesSetting(setting, res));
}
export default new SettingsStore();
export default SettingsStore;

View File

@ -167,4 +167,4 @@ class ThirdPartyStore {
}
}
export default ThirdPartyStore;
export default new ThirdPartyStore();

View File

@ -1,43 +1,17 @@
import { makeObservable, observable, computed, action } from "mobx";
import { makeAutoObservable } from "mobx";
import { getFoldersTree } from "@appserver/common/api/files";
import { FolderType } from "@appserver/common/constants";
import selectedFolderStore from "./SelectedFolderStore";
class TreeFoldersStore {
selectedFolderStore;
treeFolders = [];
selectedTreeNode = [];
expandedKeys = [];
constructor() {
makeObservable(this, {
treeFolders: observable,
selectedTreeNode: observable,
expandedKeys: observable,
myFolderId: computed,
commonFolderId: computed,
myFolder: computed,
shareFolder: computed,
favoritesFolder: computed,
recentFolder: computed,
privacyFolder: computed,
commonFolder: computed,
recycleBinFolder: computed,
isMyFolder: computed,
isShareFolder: computed,
isFavoritesFolder: computed,
isRecentFolder: computed,
isPrivacyFolder: computed,
isCommonFolder: computed,
isRecycleBinFolder: computed,
fetchTreeFolders: action,
setTreeFolders: action,
setExpandedKeys: action,
setSelectedNode: action,
});
constructor(selectedFolderStore) {
makeAutoObservable(this);
this.selectedFolderStore = selectedFolderStore;
}
fetchTreeFolders = async () => {
@ -103,38 +77,46 @@ class TreeFoldersStore {
}
get isMyFolder() {
return this.myFolder && this.myFolder.id === selectedFolderStore.id;
return this.myFolder && this.myFolder.id === this.selectedFolderStore.id;
}
get isShareFolder() {
return this.shareFolder && this.shareFolder.id === selectedFolderStore.id;
return (
this.shareFolder && this.shareFolder.id === this.selectedFolderStore.id
);
}
get isFavoritesFolder() {
return (
this.favoritesFolder && selectedFolderStore.id === this.favoritesFolder.id
this.favoritesFolder &&
this.selectedFolderStore.id === this.favoritesFolder.id
);
}
get isRecentFolder() {
return this.recentFolder && selectedFolderStore.id === this.recentFolder.id;
return (
this.recentFolder && this.selectedFolderStore.id === this.recentFolder.id
);
}
get isPrivacyFolder() {
return (
this.privacyFolder &&
this.privacyFolder.rootFolderType === selectedFolderStore.rootFolderType
this.privacyFolder.rootFolderType ===
this.selectedFolderStore.rootFolderType
);
}
get isCommonFolder() {
return this.commonFolder && this.commonFolder.id === selectedFolderStore.id;
return (
this.commonFolder && this.commonFolder.id === this.selectedFolderStore.id
);
}
get isRecycleBinFolder() {
return (
this.recycleBinFolder &&
selectedFolderStore.id === this.recycleBinFolder.id
this.selectedFolderStore.id === this.recycleBinFolder.id
);
}
@ -155,4 +137,4 @@ class TreeFoldersStore {
}
}
export default new TreeFoldersStore();
export default TreeFoldersStore;

View File

@ -1,27 +1,20 @@
import { makeObservable, action, observable } from "mobx";
import { makeAutoObservable } from "mobx";
import api from "@appserver/common/api";
import { TIMEOUT } from "../helpers/constants";
import { loopTreeFolders } from "../helpers/files-helpers";
import SecondaryProgressDataStore from "./SecondaryProgressDataStore";
import PrimaryProgressDataStore from "./PrimaryProgressDataStore";
import formatsStore from "./FormatsStore";
import treeFoldersStore from "./TreeFoldersStore";
import selectedFolderStore from "./SelectedFolderStore";
import filesStore from "./FilesStore";
import uniqueid from "lodash/uniqueId";
import throttle from "lodash/throttle";
import sumBy from "lodash/sumBy";
const { docserviceStore } = formatsStore;
const { canConvert } = docserviceStore;
const { setTreeFolders } = treeFoldersStore;
const { fetchFiles } = filesStore;
const chunkSize = 1024 * 1023; //~0.999mb
class UploadDataStore {
secondaryProgressDataStore = null;
primaryProgressDataStore = null;
formatsStore;
treeFoldersStore;
selectedFolderStore;
filesStore;
secondaryProgressDataStore;
primaryProgressDataStore;
files = [];
filesSize = 0;
@ -38,37 +31,21 @@ class UploadDataStore {
selectedUploadFile = [];
constructor() {
makeObservable(this, {
secondaryProgressDataStore: observable,
primaryProgressDataStore: observable,
files: observable,
filesSize: observable,
convertFiles: observable,
convertFilesSize: observable,
uploadStatus: observable,
uploadToFolder: observable,
uploadedFiles: observable,
percent: observable,
uploaded: observable,
selectedUploadFile: observable,
uploadPanelVisible: observable,
convertDialogVisible: observable,
selectUploadedFile: action,
setUploadPanelVisible: action,
clearUploadData: action,
cancelUpload: action,
setUploadData: action,
updateUploadedItem: action,
setConvertDialogVisible: action,
startUpload: action,
itemOperationToFolder: action,
});
this.secondaryProgressDataStore = new SecondaryProgressDataStore();
this.primaryProgressDataStore = new PrimaryProgressDataStore();
constructor(
formatsStore,
treeFoldersStore,
selectedFolderStore,
filesStore,
secondaryProgressDataStore,
primaryProgressDataStore
) {
makeAutoObservable(this);
this.formatsStore = formatsStore;
this.treeFoldersStore = treeFoldersStore;
this.selectedFolderStore = selectedFolderStore;
this.filesStore = filesStore;
this.secondaryProgressDataStore = secondaryProgressDataStore;
this.primaryProgressDataStore = primaryProgressDataStore;
}
selectUploadedFile = (file) => {
@ -306,6 +283,8 @@ class UploadDataStore {
};
startUpload = (uploadFiles, folderId, t) => {
const { canConvert } = this.formatsStore.docserviceStore;
let newFiles = this.files;
let filesSize = 0;
@ -347,28 +326,31 @@ class UploadDataStore {
};
refreshFiles = (folderId) => {
const { setTreeFolders } = this.treeFoldersStore;
if (
selectedFolderStore.id === folderId &&
this.selectedFolderStore.id === folderId &&
window.location.pathname.indexOf("/history") === -1
) {
return fetchFiles(
selectedFolderStore.id,
filesStore.filter.clone(),
false
).then((data) => {
const path = data.selectedFolder.pathParts;
const newTreeFolders = treeFoldersStore.treeFolders;
const folders = data.selectedFolder.folders;
const foldersCount = data.selectedFolder.foldersCount;
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
setTreeFolders(newTreeFolders);
});
return this.filesStore
.fetchFiles(
this.selectedFolderStore.id,
this.filesStore.filter.clone(),
false
)
.then((data) => {
const path = data.selectedFolder.pathParts;
const newTreeFolders = this.treeFoldersStore.treeFolders;
const folders = data.selectedFolder.folders;
const foldersCount = data.selectedFolder.foldersCount;
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
setTreeFolders(newTreeFolders);
});
} else {
return api.files
.getFolder(folderId, filesStore.filter.clone())
.getFolder(folderId, this.filesStore.filter.clone())
.then((data) => {
const path = data.pathParts;
const newTreeFolders = treeFoldersStore.treeFolders;
const newTreeFolders = this.treeFoldersStore.treeFolders;
const folders = data.folders;
const foldersCount = data.count;
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
@ -675,7 +657,7 @@ class UploadDataStore {
loopFilesOperations = (id, destFolderId, isCopy) => {
const label = this.secondaryProgressDataStore.label;
const treeFolders = treeFoldersStore.treeFolders;
const treeFolders = this.treeFoldersStore.treeFolders;
const {
clearSecondaryProgressData,
@ -713,10 +695,14 @@ class UploadDataStore {
let foldersCount = data.current.foldersCount;
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
if (!isCopy || destFolderId === selectedFolderStore.id) {
fetchFiles(selectedFolderStore.id, filesStore.filter)
if (!isCopy || destFolderId === this.selectedFolderStore.id) {
this.filesStore
.fetchFiles(
this.selectedFolderStore.id,
this.filesStore.filter
)
.then((data) => {
if (!treeFoldersStore.isRecycleBinFolder) {
if (!this.treeFoldersStore.isRecycleBinFolder) {
newTreeFolders = treeFolders;
path = data.selectedFolder.pathParts.slice(0);
folders = data.selectedFolder.folders;
@ -727,7 +713,7 @@ class UploadDataStore {
folders,
foldersCount
);
setTreeFolders(newTreeFolders);
this.treeFoldersStore.setTreeFolders(newTreeFolders);
}
})
.finally(() => {
@ -751,7 +737,7 @@ class UploadDataStore {
this.secondaryProgressDataStore.clearSecondaryProgressData(),
TIMEOUT
);
setTreeFolders(newTreeFolders);
this.treeFoldersStore.setTreeFolders(newTreeFolders);
}
});
}
@ -770,4 +756,4 @@ class UploadDataStore {
};
}
export default new UploadDataStore();
export default UploadDataStore;

View File

@ -1,4 +1,73 @@
import store from "@appserver/common/store";
const { moduleStore } = store.moduleStore;
import FilesStore from "./FilesStore";
import fileActionStore from "./FileActionStore";
import selectedFolderStore from "./SelectedFolderStore";
import TreeFoldersStore from "./TreeFoldersStore";
import thirdPartyStore from "./ThirdPartyStore";
import SettingsStore from "./SettingsStore";
import FilesActionsStore from "./FilesActionsStore";
import FormatsStore from "./FormatsStore";
import iconFormatsStore from "./IconFormatsStore";
import mediaViewersFormatsStore from "./MediaViewersFormatsStore";
import docserviceStore from "./DocserviceStore";
import MediaViewerDataStore from "./MediaViewerDataStore";
import UploadDataStore from "./UploadDataStore";
import SecondaryProgressDataStore from "./SecondaryProgressDataStore";
import PrimaryProgressDataStore from "./PrimaryProgressDataStore";
export default moduleStore;
import versionHistoryStore from "./VersionHistoryStore";
import dialogsStore from "./DialogsStore";
import store from "studio/store";
const formatsStore = new FormatsStore(
iconFormatsStore,
mediaViewersFormatsStore,
docserviceStore
);
const treeFoldersStore = new TreeFoldersStore(selectedFolderStore);
const filesStore = new FilesStore(
store.auth,
store.auth.settingsStore,
store.auth.userStore,
fileActionStore,
selectedFolderStore,
treeFoldersStore,
formatsStore
);
const settingsStore = new SettingsStore(thirdPartyStore);
const secondaryProgressDataStore = new SecondaryProgressDataStore();
const primaryProgressDataStore = new PrimaryProgressDataStore();
const uploadDataStore = new UploadDataStore(
formatsStore,
treeFoldersStore,
selectedFolderStore,
filesStore,
secondaryProgressDataStore,
primaryProgressDataStore
);
const filesActionsStore = new FilesActionsStore(
store.auth,
uploadDataStore,
treeFoldersStore,
filesStore,
selectedFolderStore,
settingsStore,
dialogsStore
);
const mediaViewerDataStore = new MediaViewerDataStore(filesStore);
const stores = {
filesStore,
settingsStore,
mediaViewerDataStore,
formatsStore,
versionHistoryStore,
uploadDataStore,
dialogsStore,
treeFoldersStore,
selectedFolderStore,
filesActionsStore,
};
export default stores;

View File

@ -144,6 +144,7 @@ var config = {
},
exposes: {
"./app": "./src/Files.jsx",
"./SharingDialog": "./src/components/panels/SharingDialog",
},
shared: {
...deps,

View File

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 428 B

View File

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 520 B

View File

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 443 B

View File

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 608 B

View File

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 839 B

View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

View File

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 723 B

View File

@ -0,0 +1,3 @@
{
"FileLocation": "Open file location"
}

View File

@ -0,0 +1,3 @@
{
"FileLocation": "Открыть расположение файла"
}

View File

@ -1,6 +1,9 @@
import React, { useEffect, useState } from "react";
import Toast from "@appserver/components/toast";
import toastr from "@appserver/components/toast/toastr";
import toastr from "studio/toastr";
import { toast } from "react-toastify";
import Box from "@appserver/components/box";
import { regDesktop } from "@appserver/common/desktop";
import Loaders from "@appserver/common/components/Loaders";
@ -16,21 +19,34 @@ import {
openEdit,
setEncryptionKeys,
getEncryptionAccess,
getFileInfo,
} from "@appserver/common/api/files";
import { checkIsAuthenticated } from "@appserver/common/api/user";
import { getUser } from "@appserver/common/api/people";
import FilesFilter from "@appserver/common/api/files/filter";
import throttle from "lodash/throttle";
import { isIOS, deviceType } from "react-device-detect";
import { homepage } from "../package.json";
import "./custom.scss";
import { AppServerConfig } from "@appserver/common/constants";
import SharingDialog from "files/SharingDialog";
import i18n from "./i18n";
let documentIsReady = false;
let docTitle = null;
let fileType = null;
let config;
let docSaved = null;
let docEditor;
let fileInfo;
const url = window.location.href;
const filesUrl = url.substring(0, url.indexOf("/doceditor"));
toast.configure();
const Editor = () => {
const urlParams = getObjectByLocation(window.location);
@ -48,6 +64,18 @@ const Editor = () => {
500
);
useEffect(() => {
init();
}, []);
const updateUsersRightsList = () => {
SharingDialog.getSharingSettings(fileId).then((sharingSettings) => {
docEditor.setSharingSettings({
sharingSettings,
});
});
};
const init = async () => {
try {
if (!fileId) return;
@ -72,8 +100,9 @@ const Editor = () => {
setIsAuthenticated(success);
}
}
fileInfo = await getFileInfo(fileId);
const config = await openEdit(fileId, doc);
config = await openEdit(fileId, doc);
if (isDesktop) {
const isEncryption =
@ -110,6 +139,18 @@ const Editor = () => {
);
}
if (
config &&
config.document.permissions.edit &&
config.document.permissions.modifyFilter
) {
const sharingSettings = await SharingDialog.getSharingSettings(fileId);
config.document.info = {
...config.document.info,
sharingSettings,
};
}
setIsLoading(false);
loadDocApi(docApiUrl, () => onLoad(config));
@ -124,10 +165,6 @@ const Editor = () => {
}
};
useEffect(() => {
init();
}, []);
const isIPad = () => {
return isIOS && deviceType === "tablet";
};
@ -195,6 +232,7 @@ const Editor = () => {
const onLoad = (config) => {
console.log("Editor config: ", config);
try {
console.log(config);
@ -208,6 +246,20 @@ const Editor = () => {
config.type = "mobile";
}
const filterObj = FilesFilter.getDefault();
filterObj.folder = fileInfo.folderId;
const urlFilter = filterObj.toUrlParams();
config.editorConfig.customization = {
...config.editorConfig.customization,
goback: {
blank: true,
requestClose: false,
text: i18n.t("FileLocation"),
url: `${combineUrl(filesUrl, `/filter?${urlFilter}`)}`,
},
};
const events = {
events: {
onAppReady: onSDKAppReady,
@ -217,6 +269,10 @@ const Editor = () => {
onInfo: onSDKInfo,
onWarning: onSDKWarning,
onError: onSDKError,
...(config.document.permissions.edit &&
config.document.permissions.modifyFilter && {
onRequestSharingSettings: onSDKRequestSharingSettings,
}),
},
};
@ -224,9 +280,7 @@ const Editor = () => {
if (!window.DocsAPI) throw new Error("DocsAPI is not defined");
//hideLoader();
window.DocsAPI.DocEditor("editor", newConfig);
docEditor = window.DocsAPI.DocEditor("editor", newConfig);
} catch (error) {
console.log(error);
toastr.error(error.message, null, 0, true);
@ -243,6 +297,16 @@ const Editor = () => {
);
};
const [isVisible, setIsVisible] = useState(false);
const onSDKRequestSharingSettings = () => {
setIsVisible(true);
};
const onCancel = () => {
setIsVisible(false);
};
const onSDKWarning = (event) => {
console.log(
"ONLYOFFICE Document Editor reports a warning: code " +
@ -288,7 +352,16 @@ const Editor = () => {
<Toast />
{!isLoading ? (
<div id="editor"></div>
<>
<div id="editor"></div>
<SharingDialog
isVisible={isVisible}
sharingObject={fileInfo}
onCancel={onCancel}
onSuccess={updateUsersRightsList}
/>
</>
) : (
<Box paddingProp="16px">
<Loaders.Rectangle height="96vh" />

View File

@ -14,10 +14,10 @@ newInstance
.use(initReactI18next)
.use(Backend)
.init({
lng: "ru",
lng: lng,
supportedLngs: languages,
//whitelist: languages,
fallbackLng: "ru",
fallbackLng: "en",
load: "languageOnly",
debug: true,
@ -35,8 +35,8 @@ newInstance
crossDomain: false,
},
ns: ["Login"],
defaultNS: "Login",
ns: ["Editor"],
defaultNS: "Editor",
react: {
useSuspense: true,

View File

@ -5,8 +5,8 @@ const ModuleFederationPlugin = require("webpack").container
.ModuleFederationPlugin;
const TerserPlugin = require("terser-webpack-plugin");
const { InjectManifest } = require("workbox-webpack-plugin");
//const combineUrl = require("@appserver/common/utils/combineUrl");
//const AppServerConfig = require("@appserver/common/constants/AppServerConfig");
const combineUrl = require("@appserver/common/utils/combineUrl");
const AppServerConfig = require("@appserver/common/constants/AppServerConfig");
const path = require("path");
const pkg = require("./package.json");
@ -133,7 +133,16 @@ const config = {
new ModuleFederationPlugin({
name: "editor",
filename: "remoteEntry.js",
remotes: {},
remotes: {
studio: `studio@${combineUrl(
AppServerConfig.proxyURL,
"/remoteEntry.js"
)}`,
files: `files@${combineUrl(
AppServerConfig.proxyURL,
"/products/files/remoteEntry.js"
)}`,
},
exposes: {
"./app": "./src/Editor.jsx",
},

2012
yarn.lock

File diff suppressed because it is too large Load Diff