Merge branch 'feature/workspaces' of github.com:ONLYOFFICE/AppServer into feature/workspaces
This commit is contained in:
commit
1675e77330
@ -56,69 +56,84 @@ class ModuleStore {
|
||||
iconName = null,
|
||||
iconUrl = null
|
||||
) => {
|
||||
switch (item.id) {
|
||||
case "6743007c-6f95-4d20-8c88-a8601ce5e76d":
|
||||
item.iconName = "CrmIcon";
|
||||
item.iconUrl = "/static/images/crm.react.svg";
|
||||
item.imageUrl = "/images/crm.svg";
|
||||
item.helpUrl = "https://helpcenter.onlyoffice.com/userguides/crm.aspx";
|
||||
break;
|
||||
case "1e044602-43b5-4d79-82f3-fd6208a11960":
|
||||
item.iconName = "ProjectsIcon";
|
||||
item.iconUrl = "/static/images/projects.react.svg";
|
||||
item.imageUrl = "/images/projects.svg";
|
||||
item.helpUrl =
|
||||
"https://helpcenter.onlyoffice.com/userguides/projects.aspx";
|
||||
break;
|
||||
case "2A923037-8B2D-487b-9A22-5AC0918ACF3F":
|
||||
item.iconName = "MailIcon";
|
||||
item.iconUrl = "/static/images/mail.react.svg";
|
||||
item.imageUrl = "/images/mail.svg";
|
||||
break;
|
||||
case "32D24CB5-7ECE-4606-9C94-19216BA42086":
|
||||
item.iconName = "CalendarCheckedIcon";
|
||||
item.iconUrl = "/static/images/calendar.checked.react.svg";
|
||||
item.imageUrl = "/images/calendar.svg";
|
||||
break;
|
||||
case "BF88953E-3C43-4850-A3FB-B1E43AD53A3E":
|
||||
item.iconName = "ChatIcon";
|
||||
item.iconUrl = "/static/images/chat.react.svg";
|
||||
item.imageUrl = "/images/talk.svg";
|
||||
item.isolateMode = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const id =
|
||||
item.id && typeof item.id === "string" ? item.id.toLowerCase() : null;
|
||||
|
||||
const actions = noAction
|
||||
? null
|
||||
: {
|
||||
onClick: (e) => {
|
||||
if (e) {
|
||||
window.open(item.link, "_self");
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onBadgeClick: (e) => console.log(iconName + " Badge Clicked", e),
|
||||
};
|
||||
|
||||
const description = noAction ? { description: item.description } : null;
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
const result = {
|
||||
id,
|
||||
appName: "none",
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
originUrl: item.originUrl,
|
||||
helpUrl: item.helpUrl,
|
||||
notifications: 0,
|
||||
iconName: item.iconName || iconName || "/static/images/people.react.svg", //TODO: Change to URL
|
||||
iconUrl: item.iconUrl || iconUrl,
|
||||
imageUrl: item.imageUrl,
|
||||
notifications: 0,
|
||||
isolateMode: item.isolateMode,
|
||||
isPrimary: item.isPrimary,
|
||||
...description,
|
||||
...actions,
|
||||
};
|
||||
|
||||
switch (id) {
|
||||
case "6743007c-6f95-4d20-8c88-a8601ce5e76d":
|
||||
result.appName = "crm";
|
||||
result.iconName = "CrmIcon";
|
||||
result.iconUrl = "/static/images/crm.react.svg";
|
||||
result.imageUrl = "/images/crm.svg";
|
||||
result.helpUrl =
|
||||
"https://helpcenter.onlyoffice.com/userguides/crm.aspx";
|
||||
break;
|
||||
case "1e044602-43b5-4d79-82f3-fd6208a11960":
|
||||
result.appName = "projects";
|
||||
result.iconName = "ProjectsIcon";
|
||||
result.iconUrl = "/static/images/projects.react.svg";
|
||||
result.imageUrl = "/images/projects.svg";
|
||||
result.helpUrl =
|
||||
"https://helpcenter.onlyoffice.com/userguides/projects.aspx";
|
||||
break;
|
||||
case "2a923037-8b2d-487b-9a22-5ac0918acf3f":
|
||||
result.appName = "mail";
|
||||
result.iconName = "MailIcon";
|
||||
result.iconUrl = "/static/images/mail.react.svg";
|
||||
result.imageUrl = "/images/mail.svg";
|
||||
break;
|
||||
case "32d24cb5-7ece-4606-9c94-19216ba42086":
|
||||
result.appName = "calendar";
|
||||
result.iconName = "CalendarCheckedIcon";
|
||||
result.iconUrl = "/static/images/calendar.checked.react.svg";
|
||||
result.imageUrl = "/images/calendar.svg";
|
||||
break;
|
||||
case "bf88953e-3c43-4850-a3fb-b1e43ad53a3e":
|
||||
result.appName = "chat";
|
||||
result.iconName = "ChatIcon";
|
||||
result.iconUrl = "/static/images/chat.react.svg";
|
||||
result.imageUrl = "/images/talk.svg";
|
||||
result.isolateMode = true;
|
||||
break;
|
||||
case "e67be73d-f9ae-4ce1-8fec-1880cb518cb4":
|
||||
result.appName = "files";
|
||||
break;
|
||||
case "f4d98afd-d336-4332-8778-3c6945c81ea0":
|
||||
result.appName = "people";
|
||||
break;
|
||||
default:
|
||||
result.appName = "none";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!noAction) {
|
||||
result.onClick = (e) => {
|
||||
if (e) {
|
||||
window.open(item.link, "_self");
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
result.onBadgeClick = (e) => console.log(iconName + " Badge Clicked", e);
|
||||
} else {
|
||||
result.description = item.description;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
get totalNotificationsCount() {
|
||||
|
@ -239,7 +239,7 @@ DropDownContainer.propTypes = {
|
||||
DropDownContainer.defaultProps = {
|
||||
directionX: "left",
|
||||
directionY: "bottom",
|
||||
withBackdrop: false,
|
||||
withBackdrop: true,
|
||||
showDisabledItems: false,
|
||||
};
|
||||
|
||||
|
@ -17,6 +17,33 @@ describe("<RowContainer />", () => {
|
||||
expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
it("stop event on context click", () => {
|
||||
const wrapper = shallow(
|
||||
<RowContainer>
|
||||
<span>Demo</span>
|
||||
</RowContainer>
|
||||
);
|
||||
|
||||
const event = { preventDefault: () => {} };
|
||||
|
||||
jest.spyOn(event, "preventDefault");
|
||||
|
||||
wrapper.simulate("contextmenu", event);
|
||||
|
||||
expect(event.preventDefault).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("renders like list", () => {
|
||||
const wrapper = mount(
|
||||
<RowContainer useReactWindow={false}>
|
||||
<span>Demo</span>
|
||||
</RowContainer>
|
||||
);
|
||||
|
||||
expect(wrapper).toExist();
|
||||
expect(wrapper.getDOMNode().style).toHaveProperty("height", "");
|
||||
});
|
||||
|
||||
it("renders without manualHeight", () => {
|
||||
const wrapper = mount(
|
||||
<RowContainer>
|
||||
@ -27,45 +54,6 @@ describe("<RowContainer />", () => {
|
||||
expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
it("call onRowContextClick() with normal options", () => {
|
||||
const options = [
|
||||
{
|
||||
key: "1",
|
||||
label: "test",
|
||||
},
|
||||
];
|
||||
|
||||
const wrapper = mount(
|
||||
<RowContainer>
|
||||
<span>Demo</span>
|
||||
</RowContainer>
|
||||
);
|
||||
|
||||
const instance = wrapper.instance();
|
||||
|
||||
instance.onRowContextClick(options);
|
||||
|
||||
expect(wrapper.state("contextOptions")).toEqual(options);
|
||||
});
|
||||
|
||||
it("call onRowContextClick() with wrong options", () => {
|
||||
const options = {
|
||||
key: "1",
|
||||
label: "test",
|
||||
};
|
||||
const wrapper = mount(
|
||||
<RowContainer>
|
||||
<span>Demo</span>
|
||||
</RowContainer>
|
||||
);
|
||||
|
||||
const instance = wrapper.instance();
|
||||
|
||||
instance.onRowContextClick(options);
|
||||
|
||||
expect(wrapper.state("contextOptions")).toEqual([]);
|
||||
});
|
||||
|
||||
it("componentWillUnmount() props lifecycle test", () => {
|
||||
const wrapper = shallow(
|
||||
<RowContainer>
|
||||
|
@ -27,7 +27,8 @@ class Row extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.container.removeEventListener("contextmenu", this.onContextMenu);
|
||||
this.container &&
|
||||
this.container.removeEventListener("contextmenu", this.onContextMenu);
|
||||
}
|
||||
|
||||
onContextMenu = (e) => {
|
||||
@ -141,12 +142,10 @@ Row.propTypes = {
|
||||
element: PropTypes.element,
|
||||
/** Accepts id */
|
||||
id: PropTypes.string,
|
||||
/** If true, this state is shown as a rectangle in the checkbox */
|
||||
indeterminate: PropTypes.bool,
|
||||
/** shouldComponentUpdate function */
|
||||
needForUpdate: PropTypes.func,
|
||||
/** when selecting row element. Returns data value. */
|
||||
onSelect: PropTypes.func,
|
||||
selectItem: PropTypes.func,
|
||||
/** Accepts css style */
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
sectionWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
|
@ -32,6 +32,44 @@ describe("<Row />", () => {
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders with children", () => {
|
||||
const wrapper = mount(<Row {...baseProps} />);
|
||||
|
||||
expect(wrapper).toHaveProp("children", baseProps.children);
|
||||
});
|
||||
|
||||
it("renders with contentElement and sectionWidth", () => {
|
||||
const element = <div>content</div>;
|
||||
const wrapper = mount(
|
||||
<Row {...baseProps} contentElement={element} sectionWidth={600} />
|
||||
);
|
||||
|
||||
expect(wrapper).toHaveProp("contentElement", element);
|
||||
});
|
||||
|
||||
it("can apply contextButtonSpacerWidth", () => {
|
||||
const test = "10px";
|
||||
const wrapper = mount(
|
||||
<Row {...baseProps} contextButtonSpacerWidth={test} />
|
||||
);
|
||||
|
||||
expect(wrapper).toHaveProp("contextButtonSpacerWidth", test);
|
||||
});
|
||||
|
||||
it("can apply data property", () => {
|
||||
const test = { test: "test" };
|
||||
const wrapper = mount(<Row {...baseProps} data={test} />);
|
||||
|
||||
expect(wrapper).toHaveProp("data", test);
|
||||
});
|
||||
|
||||
it("can apply indeterminate", () => {
|
||||
const test = true;
|
||||
const wrapper = mount(<Row {...baseProps} indeterminate={test} />);
|
||||
|
||||
expect(wrapper).toHaveProp("indeterminate", test);
|
||||
});
|
||||
|
||||
it("accepts id", () => {
|
||||
const wrapper = mount(<Row {...baseProps} id="testId" />);
|
||||
|
||||
@ -49,4 +87,13 @@ 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);
|
||||
});
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ const EmptyFilterContainer = ({
|
||||
const subheadingText = t("EmptyFilterSubheadingText");
|
||||
const descriptionText = t("EmptyFilterDescriptionText");
|
||||
|
||||
onResetFilter = () => {
|
||||
const onResetFilter = () => {
|
||||
setIsLoading(true);
|
||||
const newFilter = FilesFilter.getDefault();
|
||||
fetchFiles(selectedFolderId, newFilter)
|
||||
|
@ -121,7 +121,7 @@ class AddGroupsPanelComponent extends React.Component {
|
||||
<StyledHeaderContent>
|
||||
<IconButton
|
||||
size="16"
|
||||
iconName="ArrowPathIcon"
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
onClick={this.onArrowClick}
|
||||
color="A3A9AE"
|
||||
/>
|
||||
|
@ -154,7 +154,7 @@ class AddUsersPanelComponent extends React.Component {
|
||||
<StyledHeaderContent>
|
||||
<IconButton
|
||||
size="16"
|
||||
iconName="ArrowPathIcon"
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
onClick={this.onArrowClick}
|
||||
color="#A3A9AE"
|
||||
/>
|
||||
|
@ -37,39 +37,71 @@ export const getUserRole = (user) => {
|
||||
export const getUserContactsPattern = () => {
|
||||
return {
|
||||
contact: [
|
||||
{ type: "mail", icon: "MailIcon", link: "mailto:{0}" },
|
||||
{ type: "phone", icon: "PhoneIcon", link: "tel:{0}" },
|
||||
{ type: "mobphone", icon: "MobileIcon", link: "tel:{0}" },
|
||||
{ type: "gmail", icon: "GmailIcon", link: "mailto:{0}" },
|
||||
{ type: "skype", icon: "SkypeIcon", link: "skype:{0}?userinfo" },
|
||||
{ type: "msn", icon: "WindowsMsnIcon" },
|
||||
{ type: "icq", icon: "IcqIcon", link: "https://www.icq.com/people/{0}" },
|
||||
{ type: "jabber", icon: "JabberIcon" },
|
||||
{ type: "aim", icon: "AimIcon" },
|
||||
{
|
||||
type: "mail",
|
||||
icon: "/static/images/mail.react.svg",
|
||||
link: "mailto:{0}",
|
||||
},
|
||||
{
|
||||
type: "phone",
|
||||
icon: "/products/people/images/phone.react.svg",
|
||||
link: "tel:{0}",
|
||||
},
|
||||
{
|
||||
type: "mobphone",
|
||||
icon: "/products/people/images/mobile.react.svg",
|
||||
link: "tel:{0}",
|
||||
},
|
||||
{
|
||||
type: "gmail",
|
||||
icon: "/products/people/images/gmail.react.svg",
|
||||
link: "mailto:{0}",
|
||||
},
|
||||
{
|
||||
type: "skype",
|
||||
icon: "/products/people/images/skype.react.svg",
|
||||
link: "skype:{0}?userinfo",
|
||||
},
|
||||
{ type: "msn", icon: "/products/people/images/windows.msn.react.svg" },
|
||||
{
|
||||
type: "icq",
|
||||
icon: "/products/people/images/icq.react.svg",
|
||||
link: "https://www.icq.com/people/{0}",
|
||||
},
|
||||
{ type: "jabber", icon: "/products/people/images/jabber.react.svg" },
|
||||
{ type: "aim", icon: "/products/people/images/aim.react.svg" },
|
||||
],
|
||||
social: [
|
||||
{
|
||||
type: "facebook",
|
||||
icon: "ShareFacebookIcon",
|
||||
icon: "/products/people/images/share.facebook.react.svg",
|
||||
link: "https://facebook.com/{0}",
|
||||
},
|
||||
{
|
||||
type: "livejournal",
|
||||
icon: "LivejournalIcon",
|
||||
icon: "/products/people/images/livejournal.react.svg",
|
||||
link: "https://{0}.livejournal.com",
|
||||
},
|
||||
{ type: "myspace", icon: "MyspaceIcon", link: "https://myspace.com/{0}" },
|
||||
{
|
||||
type: "myspace",
|
||||
icon: "/products/people/images/myspace.react.svg",
|
||||
link: "https://myspace.com/{0}",
|
||||
},
|
||||
{
|
||||
type: "twitter",
|
||||
icon: "ShareTwitterIcon",
|
||||
icon: "/products/people/images/share.twitter.react.svg",
|
||||
link: "https://twitter.com/{0}",
|
||||
},
|
||||
{
|
||||
type: "blogger",
|
||||
icon: "BloggerIcon",
|
||||
icon: "/products/people/images/blogger.react.svg",
|
||||
link: "https://{0}.blogspot.com",
|
||||
},
|
||||
{ type: "yahoo", icon: "YahooIcon", link: "mailto:{0}@yahoo.com" },
|
||||
{
|
||||
type: "yahoo",
|
||||
icon: "/products/people/images/yahoo.react.svg",
|
||||
link: "mailto:{0}@yahoo.com",
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Router, Switch } from "react-router-dom";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import NavMenu from "./components/NavMenu";
|
||||
@ -12,9 +11,6 @@ import Layout from "./components/Layout";
|
||||
import ScrollToTop from "./components/Layout/ScrollToTop";
|
||||
import history from "@appserver/common/history";
|
||||
import toastr from "studio/toastr";
|
||||
import Loader from "@appserver/components/loader";
|
||||
import Grid from "@appserver/components/grid";
|
||||
import PageLayout from "@appserver/common/components/PageLayout";
|
||||
import { updateTempContent } from "@appserver/common/utils";
|
||||
import { Provider as MobxProvider } from "mobx-react";
|
||||
import ThemeProvider from "@appserver/components/theme-provider";
|
||||
@ -24,35 +20,27 @@ import config from "../package.json";
|
||||
import "./custom.scss";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import AppLoader from "./components/AppLoader";
|
||||
import System from "./components/System";
|
||||
|
||||
const Payments = React.lazy(() => import("./components/pages/Payments"));
|
||||
const Error404 = React.lazy(() => import("studio/Error404"));
|
||||
const Error401 = React.lazy(() => import("studio/Error401"));
|
||||
const Home = React.lazy(() => import("./components/pages/Home"));
|
||||
const Login = React.lazy(() => import("login/app"));
|
||||
const People = React.lazy(() => import("people/app"));
|
||||
const Files = React.lazy(() => import("files/app"));
|
||||
const About = React.lazy(() => import("./components/pages/About"));
|
||||
const Settings = React.lazy(() => import("./components/pages/Settings"));
|
||||
const ComingSoon = React.lazy(() => import("./components/pages/ComingSoon"));
|
||||
|
||||
const LoadingShell = () => (
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Loader className="pageLoader" type="rombs" size="40px" />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
const SettingsRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Settings {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
const PaymentsRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Payments {...props} />
|
||||
</ErrorBoundary>
|
||||
@ -60,7 +48,7 @@ const PaymentsRoute = (props) => (
|
||||
);
|
||||
|
||||
const Error404Route = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Error404 {...props} />
|
||||
</ErrorBoundary>
|
||||
@ -68,14 +56,14 @@ const Error404Route = (props) => (
|
||||
);
|
||||
|
||||
const Error401Route = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Error401 {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
const HomeRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Home {...props} />
|
||||
</ErrorBoundary>
|
||||
@ -83,35 +71,15 @@ const HomeRoute = (props) => (
|
||||
);
|
||||
|
||||
const LoginRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Login {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
const PeopleRoute = (props) => {
|
||||
return (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<ErrorBoundary>
|
||||
<People {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const FilesRoute = (props) => {
|
||||
return (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<ErrorBoundary>
|
||||
<Files {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const AboutRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<About {...props} />
|
||||
</ErrorBoundary>
|
||||
@ -119,12 +87,28 @@ const AboutRoute = (props) => (
|
||||
);
|
||||
|
||||
const ComingSoonRoute = (props) => (
|
||||
<React.Suspense fallback={<LoadingShell />}>
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<ComingSoon {...props} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
const DynamicAppRoute = ({ link, appName, ...rest }) => {
|
||||
const system = {
|
||||
url: `${window.location.origin}${link}remoteEntry.js`,
|
||||
scope: appName,
|
||||
module: "./app",
|
||||
};
|
||||
return (
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<System system={system} {...rest} />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
// useEffect(() => {
|
||||
// //utils.removeTempContent();
|
||||
@ -160,7 +144,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
// .catch((err) => toastr.error(err.message));
|
||||
// }, []);
|
||||
|
||||
const { isLoaded, loadBaseInfo, isThirdPartyResponse } = rest;
|
||||
const { isLoaded, loadBaseInfo, isThirdPartyResponse, modules } = rest;
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
@ -168,7 +152,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
} catch (err) {
|
||||
toastr.error(err);
|
||||
}
|
||||
}, [loadBaseInfo]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoaded) updateTempContent();
|
||||
@ -182,6 +166,16 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
const isEditor = pathname.indexOf("doceditor") !== -1;
|
||||
|
||||
const dynamicRoutes = modules.map((m) => (
|
||||
<PrivateRoute
|
||||
key={m.id}
|
||||
path={m.link}
|
||||
component={DynamicAppRoute}
|
||||
link={m.link}
|
||||
appName={m.appName}
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Router history={history}>
|
||||
@ -195,14 +189,6 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
path={["/", "/error=:error"]}
|
||||
component={HomeRoute}
|
||||
/>
|
||||
<PrivateRoute
|
||||
path={["/products/people", "/products/people/filter"]}
|
||||
component={PeopleRoute}
|
||||
/>
|
||||
<PrivateRoute
|
||||
path={["/products/files", "/products/files/filter"]}
|
||||
component={FilesRoute}
|
||||
/>
|
||||
<PrivateRoute path={["/about"]} component={AboutRoute} />
|
||||
<PublicRoute
|
||||
exact
|
||||
@ -231,6 +217,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
path="/settings"
|
||||
component={SettingsRoute}
|
||||
/>
|
||||
{dynamicRoutes}
|
||||
<PrivateRoute path="/error401" component={Error401Route} />
|
||||
<PrivateRoute component={Error404Route} />
|
||||
</Switch>
|
||||
@ -243,7 +230,6 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
|
||||
const ShellWrapper = inject(({ auth }) => {
|
||||
const { init, isLoaded } = auth;
|
||||
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
const isThirdPartyResponse = pathname.indexOf("thirdparty") !== -1;
|
||||
|
||||
@ -254,6 +240,7 @@ const ShellWrapper = inject(({ auth }) => {
|
||||
},
|
||||
isThirdPartyResponse,
|
||||
isLoaded,
|
||||
modules: auth.moduleStore.modules,
|
||||
};
|
||||
})(observer(Shell));
|
||||
|
||||
@ -261,7 +248,7 @@ export default () => (
|
||||
<ThemeProvider theme={Base}>
|
||||
<MobxProvider {...store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<ShellWrapper />
|
||||
<ShellWrapper />
|
||||
</I18nextProvider>
|
||||
</MobxProvider>
|
||||
</ThemeProvider>
|
||||
|
13
web/ASC.Web.Client/src/components/AppLoader/index.js
Normal file
13
web/ASC.Web.Client/src/components/AppLoader/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import PageLayout from "@appserver/common/components/PageLayout";
|
||||
import Loader from "@appserver/components/loader";
|
||||
|
||||
const AppLoader = () => (
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Loader className="pageLoader" type="rombs" size="40px" />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
export default AppLoader;
|
107
web/ASC.Web.Client/src/components/System/index.js
Normal file
107
web/ASC.Web.Client/src/components/System/index.js
Normal file
@ -0,0 +1,107 @@
|
||||
import React from "react";
|
||||
import AppLoader from "../AppLoader";
|
||||
import ErrorBoundary from "@appserver/common/components/ErrorBoundary";
|
||||
import Error520 from "studio/Error520";
|
||||
import Error404 from "studio/Error404";
|
||||
|
||||
function loadComponent(scope, module) {
|
||||
return async () => {
|
||||
// Initializes the share scope. This fills it with known provided modules from this build and all remotes
|
||||
await __webpack_init_sharing__("default");
|
||||
const container = window[scope]; // or get the container somewhere else
|
||||
// Initialize the container, it may provide shared modules
|
||||
await container.init(__webpack_share_scopes__.default);
|
||||
const factory = await window[scope].get(module);
|
||||
const Module = factory();
|
||||
return Module;
|
||||
};
|
||||
}
|
||||
|
||||
const useDynamicScript = (args) => {
|
||||
const [ready, setReady] = React.useState(false);
|
||||
const [failed, setFailed] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!args.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const exists = document.getElementById(args.id);
|
||||
|
||||
if (exists) {
|
||||
setReady(true);
|
||||
setFailed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const element = document.createElement("script");
|
||||
|
||||
element.id = args.id;
|
||||
element.src = args.url;
|
||||
element.type = "text/javascript";
|
||||
element.async = true;
|
||||
|
||||
setReady(false);
|
||||
setFailed(false);
|
||||
|
||||
element.onload = () => {
|
||||
console.log(`Dynamic Script Loaded: ${args.url}`);
|
||||
setReady(true);
|
||||
};
|
||||
|
||||
element.onerror = () => {
|
||||
console.error(`Dynamic Script Error: ${args.url}`);
|
||||
setReady(false);
|
||||
setFailed(true);
|
||||
};
|
||||
|
||||
document.head.appendChild(element);
|
||||
|
||||
//TODO: Uncomment if you need to remove loaded remoteEntry
|
||||
// return () => {
|
||||
// console.log(`Dynamic Script Removed: ${args.url}`);
|
||||
// document.head.removeChild(element);
|
||||
// };
|
||||
}, [args.url]);
|
||||
|
||||
return {
|
||||
ready,
|
||||
failed,
|
||||
};
|
||||
};
|
||||
|
||||
const System = (props) => {
|
||||
const { ready, failed } = useDynamicScript({
|
||||
url: props.system && props.system.url,
|
||||
id: props.system && props.system.scope,
|
||||
});
|
||||
|
||||
if (!props.system) {
|
||||
console.log(`Not system specified`);
|
||||
return <Error404 />;
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
console.log(`Loading dynamic script: ${props.system.url}`);
|
||||
return <AppLoader />;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
console.log(`Failed to load dynamic script: ${props.system.url}`);
|
||||
return <Error520 />;
|
||||
}
|
||||
|
||||
const Component = React.lazy(
|
||||
loadComponent(props.system.scope, props.system.module)
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Suspense fallback={<AppLoader />}>
|
||||
<ErrorBoundary>
|
||||
<Component />
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default System;
|
@ -126,8 +126,6 @@ const config = {
|
||||
filename: "remoteEntry.js",
|
||||
remotes: {
|
||||
studio: `studio@${homepage}/remoteEntry.js`,
|
||||
people: `people@${homepage}/products/people/remoteEntry.js`,
|
||||
files: `files@${homepage}/products/files/remoteEntry.js`,
|
||||
login: `login@${homepage}/login/remoteEntry.js`,
|
||||
},
|
||||
exposes: {
|
||||
|
Loading…
Reference in New Issue
Block a user