Merge branch 'develop' into feature/templates

This commit is contained in:
Nikita Gopienko 2024-05-20 14:02:25 +03:00
commit 657e6ee609
33 changed files with 541 additions and 623 deletions

View File

@ -429,13 +429,12 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
{toast}
{/* <ReactSmartBanner t={t} ready={ready} /> */}
{withoutNavMenu ? <></> : <NavMenu />}
{currentDeviceType === DeviceType.mobile && !isFrame && <MainBar />}
<IndicatorLoader />
<ScrollToTop />
<DialogsWrapper t={t} />
<Main isDesktop={isDesktop}>
{currentDeviceType !== DeviceType.mobile && !isFrame && <MainBar />}
{!isFrame && <MainBar />}
<div className="main-container">
<Outlet />
</div>

View File

@ -1,183 +0,0 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React, { Component, createRef } from "react";
import {
isDesktop,
isTouchDevice,
getBannerAttribute,
} from "@docspace/shared/utils";
import { Scrollbar } from "@docspace/shared/components/scrollbar";
import { LayoutContextProvider } from "./context";
import PropTypes from "prop-types";
import {
isTablet,
isMobile,
isSafari,
isIOS,
isChrome,
} from "react-device-detect";
class MobileLayout extends Component {
constructor(props) {
super(props);
this.state = {
prevScrollPosition: window.pageYOffset,
visibleContent: true,
};
this.scrollRefPage = createRef();
}
componentDidMount() {
this.customScrollElm = document.querySelector(
"#customScrollBar > .scroll-wrapper > .scroller",
);
if (!isChrome) this.customScrollElm.scrollTo(0, 0);
this.customScrollElm.addEventListener(
"scroll",
this.scrolledTheVerticalAxis,
);
// this.setState({ visibleContent: true });
}
componentWillUnmount() {
this.customScrollElm.removeEventListener(
"scroll",
this.scrolledTheVerticalAxis,
);
}
scrolledTheVerticalAxis = () => {
const { prevScrollPosition, visibleContent } = this.state;
const { headerHeight } = getBannerAttribute();
const currentScrollPosition =
this.customScrollElm.scrollTop > 0 ? this.customScrollElm.scrollTop : 0;
if (
!isDesktop() &&
document.getElementsByClassName("backdrop-active").length > 0 &&
!this.props.isArticleVisibleOnUnpin
) {
const elements = document.getElementsByClassName("backdrop-active");
elements[0].click();
return;
}
if (visibleContent && isMobile && !isTouchDevice) {
return;
}
if (
(isSafari || isIOS) &&
this.customScrollElm.scrollHeight - this.customScrollElm.clientHeight <
headerHeight
) {
if (!this.state.visibleContent)
this.setState({
visibleContent: true,
});
return;
}
if (
prevScrollPosition - currentScrollPosition > 0 &&
currentScrollPosition < headerHeight
) {
if (!this.state.visibleContent)
this.setState({
visibleContent: true,
});
return;
}
if (
(isSafari || isIOS) &&
Math.abs(currentScrollPosition - prevScrollPosition) <= headerHeight &&
currentScrollPosition === 0
) {
if (!this.state.visibleContent)
this.setState({
visibleContent: true,
});
return;
}
if (Math.abs(currentScrollPosition - prevScrollPosition) <= headerHeight) {
return;
}
if (prevScrollPosition === 0 && currentScrollPosition > 100) {
if (Math.abs(currentScrollPosition - prevScrollPosition) <= 104) {
return;
}
}
let isVisible = prevScrollPosition >= currentScrollPosition;
if (
(isSafari || isIOS) &&
currentScrollPosition >=
this.customScrollElm.scrollHeight - this.customScrollElm.clientHeight &&
this.customScrollElm.scrollHeight !== this.customScrollElm.clientHeight
) {
isVisible = false;
}
this.setState({
prevScrollPosition: currentScrollPosition,
visibleContent: isVisible,
});
};
render() {
const scrollProp = { ref: this.scrollRefPage };
const { children } = this.props;
return (
<Scrollbar id="customScrollBar" {...scrollProp}>
<LayoutContextProvider
value={{
scrollRefLayout: this.scrollRefPage,
isVisible: this.state.visibleContent,
}}
>
{children}
</LayoutContextProvider>
</Scrollbar>
);
}
}
MobileLayout.propTypes = {
children: PropTypes.any,
};
export default MobileLayout;

View File

@ -1,32 +0,0 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { createContext } from "react";
const LayoutContext = createContext({});
export const LayoutContextProvider = LayoutContext.Provider;
export const LayoutContextConsumer = LayoutContext.Consumer;

View File

@ -24,18 +24,19 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React, { useEffect, useState } from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import MobileLayout from "./MobileLayout";
import { inject, observer } from "mobx-react";
import styled, { css } from "styled-components";
import React, { useEffect, useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { isMobile, isMobileOnly } from "react-device-detect";
import { Scrollbar } from "@docspace/shared/components/scrollbar";
import {
isTablet as isTabletUtils,
isMobile as isMobileUtils,
mobileMore,
tablet,
} from "@docspace/shared/utils";
import { isMobile, isMobileOnly } from "react-device-detect";
import { inject, observer } from "mobx-react";
const StyledContainer = styled.div`
user-select: none;
@ -58,6 +59,14 @@ const StyledContainer = styled.div`
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
`}
@media ${mobileMore} {
#customScrollBar {
> .scroll-wrapper > .scroller > .scroll-body {
padding-inline: 0px !important;
}
}
}
`;
const Layout = (props) => {
@ -157,7 +166,7 @@ const Layout = (props) => {
contentHeight={contentHeight}
isPortrait={isPortrait}
>
{isMobileUtils() ? <MobileLayout {...props} /> : children}
<Scrollbar id="customScrollBar">{children}</Scrollbar>
</StyledContainer>
);
};

View File

@ -33,7 +33,7 @@ import { isMobile as isMobileUtils } from "@docspace/shared/utils";
const StyledMain = styled.main`
height: ${(props) => props.mainHeight && `${props.mainHeight}px`};
width: 100vw;
width: 100%;
z-index: 0;
display: flex;
flex-direction: column;

View File

@ -39,7 +39,6 @@ import { withTranslation } from "react-i18next";
import { useNavigate, useLocation } from "react-router-dom";
import { NavMenuHeaderLoader } from "@docspace/shared/skeletons/nav-menu";
import { LayoutContextConsumer } from "../Layout/context";
import { inject, observer } from "mobx-react";
import PreparationPortalDialog from "../dialogs/PreparationPortalDialog";
@ -167,47 +166,43 @@ const NavMenu = (props) => {
const isPreparationPortal = location.pathname === "/preparation-portal";
return (
<LayoutContextConsumer>
{(value) => (
<StyledContainer isLoaded={isLoaded} isVisible={value.isVisible}>
<Backdrop
visible={isBackdropVisible}
onClick={backdropClick}
withBackground={true}
withBlur={true}
/>
<StyledContainer isLoaded={isLoaded}>
<Backdrop
visible={isBackdropVisible}
onClick={backdropClick}
withBackground={true}
withBlur={true}
/>
{!hideHeader &&
(isLoaded && isAuthenticated ? (
<>
{!isPreparationPortal && (
<HeaderNav hideProfileMenu={hideProfileMenu} />
)}
<Header
customHeader={customHeader}
isPreparationPortal={isPreparationPortal}
isNavOpened={isNavOpened}
onClick={showNav}
onNavMouseEnter={handleNavMouseEnter}
onNavMouseLeave={handleNavMouseLeave}
toggleAside={toggleAside}
backdropClick={backdropClick}
/>
</>
) : !isLoaded && isAuthenticated ? (
<NavMenuHeaderLoader />
) : (
<HeaderUnAuth />
))}
{!hideHeader &&
(isLoaded && isAuthenticated ? (
<>
{!isPreparationPortal && (
<HeaderNav hideProfileMenu={hideProfileMenu} />
)}
<Header
customHeader={customHeader}
isPreparationPortal={isPreparationPortal}
isNavOpened={isNavOpened}
onClick={showNav}
onNavMouseEnter={handleNavMouseEnter}
onNavMouseLeave={handleNavMouseLeave}
toggleAside={toggleAside}
backdropClick={backdropClick}
/>
</>
) : !isLoaded && isAuthenticated ? (
<NavMenuHeaderLoader />
) : (
<HeaderUnAuth />
))}
{isAsideAvailable && (
<Aside visible={isAsideVisible} onClick={backdropClick}>
{asideContent}
</Aside>
)}
</StyledContainer>
{isAsideAvailable && (
<Aside visible={isAsideVisible} onClick={backdropClick}>
{asideContent}
</Aside>
)}
</LayoutContextConsumer>
</StyledContainer>
);
};

View File

@ -44,12 +44,18 @@ export const GroupMemberRow = styled.div<{}>`
align-items: flex-start;
justify-content: center;
padding: 9px 0;
width: 100%;
overflow: hidden;
.name {
color: ${({ theme }) => theme.sideBarRow.titleColor};
font-size: 14px;
font-weight: 600;
line-height: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.email {
@ -57,6 +63,10 @@ export const GroupMemberRow = styled.div<{}>`
font-size: 10px;
font-weight: 400;
line-height: normal;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
}

View File

@ -425,6 +425,7 @@ const StyledCrossIcon = styled(CrossIcon)`
StyledCrossIcon.defaultProps = { theme: Base };
const StyledDeleteIcon = styled(DeleteIcon)`
cursor: pointer;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`

View File

@ -50,6 +50,7 @@ import AccessSelector from "./AccessSelector";
const Item = ({
t,
item,
theme,
setInviteItems,
inviteItems,
changeInviteItem,
@ -216,7 +217,7 @@ const Item = ({
tooltipContent={t("EmailErrorMessage")}
openOnClick={false}
size={16}
color="#F21C0E"
color={theme.infoPanel.errorColor}
/>
<StyledDeleteIcon
className="delete-icon"

View File

@ -66,6 +66,7 @@ const Row = memo(({ data, index, style }) => {
<Item
t={t}
item={item}
theme={theme}
setInviteItems={setInviteItems}
changeInviteItem={changeInviteItem}
inviteItems={inviteItems}
@ -196,7 +197,7 @@ const ItemsList = ({
export default inject(({ userStore, dialogsStore, settingsStore }) => {
const { setInviteItems, inviteItems, changeInviteItem } = dialogsStore;
const { isOwner } = userStore.user;
const { standalone } = settingsStore;
const { theme, standalone } = settingsStore;
return {
setInviteItems,
@ -204,5 +205,6 @@ export default inject(({ userStore, dialogsStore, settingsStore }) => {
changeInviteItem,
isOwner,
standalone,
theme,
};
})(observer(ItemsList));

View File

@ -34,6 +34,7 @@ import { PluginFileType } from "SRC_DIR/helpers/plugins/enums";
import { UrlActionType } from "@docspace/shared/enums";
import MediaViewer from "@docspace/shared/components/media-viewer/MediaViewer";
import { Portal } from "@docspace/shared/components/portal";
const FilesMediaViewer = (props) => {
const {
@ -275,41 +276,46 @@ const FilesMediaViewer = (props) => {
return (
visible && (
<MediaViewer
t={t}
files={files}
getIcon={getIcon}
visible={visible}
playlist={playlist}
prevMedia={prevMedia}
nextMedia={nextMedia}
onCopyLink={onCopyLink}
userAccess={userAccess}
onChangeUrl={onChangeUrl}
isPreviewFile={firstLoad}
onDuplicate={onDuplicate}
onMoveAction={onMoveAction}
onCopyAction={onCopyAction}
onClose={onMediaViewerClose}
onDelete={onDeleteMediaFile}
onClickRename={onClickRename}
onClickDelete={onClickDelete}
setActiveFiles={setActiveFiles}
archiveRoomsId={archiveRoomsId}
onPreviewClick={onPreviewClick}
onDownload={onDownloadMediaFile}
onClickLinkEdit={onClickLinkEdit}
onClickDownload={onClickDownload}
onShowInfoPanel={onShowInfoPanel}
playlistPos={currentPostionIndex}
currentFileId={currentMediaFileId}
onClickDownloadAs={onClickDownloadAs}
currentDeviceType={currentDeviceType}
extsImagePreviewed={extsImagePreviewed}
setBufferSelection={setBufferSelection}
onEmptyPlaylistError={onMediaViewerClose}
deleteDialogVisible={deleteDialogVisible}
pluginContextMenuItems={pluginContextMenuItems}
<Portal
visible
element={
<MediaViewer
t={t}
files={files}
getIcon={getIcon}
visible={visible}
playlist={playlist}
prevMedia={prevMedia}
nextMedia={nextMedia}
onCopyLink={onCopyLink}
userAccess={userAccess}
onChangeUrl={onChangeUrl}
isPreviewFile={firstLoad}
onDuplicate={onDuplicate}
onMoveAction={onMoveAction}
onCopyAction={onCopyAction}
onClose={onMediaViewerClose}
onDelete={onDeleteMediaFile}
onClickRename={onClickRename}
onClickDelete={onClickDelete}
setActiveFiles={setActiveFiles}
archiveRoomsId={archiveRoomsId}
onPreviewClick={onPreviewClick}
onDownload={onDownloadMediaFile}
onClickLinkEdit={onClickLinkEdit}
onClickDownload={onClickDownload}
onShowInfoPanel={onShowInfoPanel}
playlistPos={currentPostionIndex}
currentFileId={currentMediaFileId}
onClickDownloadAs={onClickDownloadAs}
currentDeviceType={currentDeviceType}
extsImagePreviewed={extsImagePreviewed}
setBufferSelection={setBufferSelection}
onEmptyPlaylistError={onMediaViewerClose}
deleteDialogVisible={deleteDialogVisible}
pluginContextMenuItems={pluginContextMenuItems}
/>
}
/>
)
);

View File

@ -898,7 +898,7 @@ const Manager = (props) => {
scale={true}
onChange={onChangePage}
placeholder={t("EnterPage")}
value={config.filter.page || 1}
value={config.filter.page}
isDisabled={!config.filter.count}
tabIndex={7}
/>

View File

@ -65,7 +65,7 @@ const Certificates = (props) => {
spShowAdditionalParameters,
idpVerifyAlgorithm,
spEncryptAlgorithm,
spDecryptAlgorithm,
spSigningAlgorithm,
isLoadingXml,
isDisabledSpSigning,
isDisabledSpEncrypt,
@ -186,7 +186,7 @@ const Certificates = (props) => {
name="spSigningAlgorithm"
options={verifyAlgorithmsOptions}
tabIndex={14}
value={spEncryptAlgorithm}
value={spSigningAlgorithm}
/>
<SsoComboBox
@ -195,7 +195,7 @@ const Certificates = (props) => {
name={"spEncryptAlgorithm"}
options={decryptAlgorithmsOptions}
tabIndex={15}
value={spDecryptAlgorithm}
value={spEncryptAlgorithm}
/>
</>
)}
@ -220,7 +220,7 @@ export default inject(({ ssoStore }) => {
spShowAdditionalParameters,
idpVerifyAlgorithm,
spEncryptAlgorithm,
spDecryptAlgorithm,
spSigningAlgorithm,
isLoadingXml,
isDisabledSpSigning,
isDisabledSpEncrypt,
@ -237,7 +237,7 @@ export default inject(({ ssoStore }) => {
spShowAdditionalParameters,
idpVerifyAlgorithm,
spEncryptAlgorithm,
spDecryptAlgorithm,
spSigningAlgorithm,
isLoadingXml,
isDisabledSpSigning,
isDisabledSpEncrypt,

View File

@ -58,7 +58,7 @@ const SingleSignOn = (props) => {
const isMobileView = currentDeviceType === DeviceType.mobile;
useEffect(() => {
isSSOAvailable && !isMobileView && init();
isSSOAvailable && init();
setDocumentTitle(t("Settings:SingleSignOn"));
}, []);

View File

@ -53,7 +53,7 @@ const SsoComboBox = (props) => {
};
return (
<FieldContainer isVertical labelText={labelText}>
<FieldContainer isVertical labelVisible labelText={labelText}>
<StyledInputWrapper>
<ComboBox
id={name}

View File

@ -28,241 +28,330 @@ import React from "react";
import { Navigate } from "react-router-dom";
import loadable from "@loadable/component";
import Error404 from "@docspace/shared/components/errors/Error404";
import componentLoader from "@docspace/shared/utils/component-loader";
import PrivateRoute from "../components/PrivateRouteWrapper";
import ErrorBoundary from "../components/ErrorBoundaryWrapper";
import Error404 from "@docspace/shared/components/errors/Error404";
import { generalRoutes } from "./general";
const PortalSettings = loadable(() => import("../pages/PortalSettings"));
const CustomizationSettings = loadable(
() => import("../pages/PortalSettings/categories/common/index.js"),
);
const LanguageAndTimeZoneSettings = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/language-and-time-zone"
),
);
const WelcomePageSettings = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/welcome-page-settings"
),
);
const DNSSettings = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/dns-settings"
),
);
const PortalRenaming = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/portal-renaming"
),
);
const WhiteLabel = loadable(
() => import("../pages/PortalSettings/categories/common/Branding/whitelabel"),
);
const CompanyInfoSettings = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Branding/companyInfoSettings"
),
);
const AdditionalResources = loadable(
() =>
import(
"../pages/PortalSettings/categories/common/Branding/additionalResources"
),
);
const SecuritySettings = loadable(
() => import("../pages/PortalSettings/categories/security/index.js"),
);
const TfaPage = loadable(
() => import("../pages/PortalSettings/categories/security/access-portal/tfa"),
);
const PasswordStrengthPage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/passwordStrength"
),
);
const TrustedMailPage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/trustedMail"
),
);
const IpSecurityPage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/ipSecurity"
),
);
const BruteForceProtectionPage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/bruteForceProtection"
),
);
const AdminMessagePage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/adminMessage"
),
);
const SessionLifetimePage = loadable(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/sessionLifetime"
),
);
const Integration = loadable(
() => import("../pages/PortalSettings/categories/integration"),
);
const Payments = loadable(
() => import("../pages/PortalSettings/categories/payments"),
);
const Statistics = loadable(
() => import("../pages/PortalSettings/categories/storage-management"),
);
const QuotaPerRoom = loadable(
() =>
import(
"../pages/PortalSettings/categories/storage-management/sub-components/QuotaPerRoom.js"
),
);
const QuotaPerUser = loadable(
() =>
import(
"../pages/PortalSettings/categories/storage-management/sub-components/QuotaPerUser.js"
),
);
const ThirdParty = loadable(
() =>
import(
"../pages/PortalSettings/categories/integration/ThirdPartyServicesSettings"
),
const PortalSettings = loadable(() =>
componentLoader(() => import("../pages/PortalSettings")),
);
const DocumentService = loadable(
() =>
import("../pages/PortalSettings/categories/integration/DocumentService"),
const CustomizationSettings = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/common/index.js"),
),
);
const LanguageAndTimeZoneSettings = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/language-and-time-zone"
),
),
);
const WelcomePageSettings = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/welcome-page-settings"
),
),
);
const DNSSettings = loadable(() =>
componentLoader(
() => () =>
import(
"../pages/PortalSettings/categories/common/Customization/dns-settings"
),
),
);
const PortalRenaming = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/common/Customization/portal-renaming"
),
),
);
const WhiteLabel = loadable(() =>
componentLoader(
() =>
import("../pages/PortalSettings/categories/common/Branding/whitelabel"),
),
);
const CompanyInfoSettings = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/common/Branding/companyInfoSettings"
),
),
);
const AdditionalResources = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/common/Branding/additionalResources"
),
),
);
const SecuritySettings = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/security/index.js"),
),
);
const TfaPage = loadable(() =>
componentLoader(
() =>
import("../pages/PortalSettings/categories/security/access-portal/tfa"),
),
);
const PasswordStrengthPage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/passwordStrength"
),
),
);
const TrustedMailPage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/trustedMail"
),
),
);
const IpSecurityPage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/ipSecurity"
),
),
);
const BruteForceProtectionPage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/bruteForceProtection"
),
),
);
const AdminMessagePage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/adminMessage"
),
),
);
const SessionLifetimePage = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/security/access-portal/sessionLifetime"
),
),
);
const Integration = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/integration"),
),
);
const Payments = loadable(() =>
componentLoader(() => import("../pages/PortalSettings/categories/payments")),
);
const Statistics = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/storage-management"),
),
);
const QuotaPerRoom = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/storage-management/sub-components/QuotaPerRoom.js"
),
),
);
const QuotaPerUser = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/storage-management/sub-components/QuotaPerUser.js"
),
),
);
const ThirdParty = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/integration/ThirdPartyServicesSettings"
),
),
);
const SingleSignOn = loadable(
() => import("../pages/PortalSettings/categories/integration/SingleSignOn"),
);
const SPSettings = loadable(
() =>
import(
"../pages/PortalSettings/categories/integration/SingleSignOn/SPSettings"
),
);
const SPMetadata = loadable(
() =>
import(
"../pages/PortalSettings/categories/integration/SingleSignOn/ProviderMetadata"
),
const DocumentService = loadable(() =>
componentLoader(
() =>
import("../pages/PortalSettings/categories/integration/DocumentService"),
),
);
const DeveloperTools = loadable(
() => import("../pages/PortalSettings/categories/developer-tools/index.js"),
const SingleSignOn = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/integration/SingleSignOn"),
),
);
const SPSettings = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/integration/SingleSignOn/SPSettings"
),
),
);
const SPMetadata = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/integration/SingleSignOn/ProviderMetadata"
),
),
);
const DataImport = loadable(
() => import("../pages/PortalSettings/categories/data-import/index.js"),
);
const GoogleDataImport = loadable(
() =>
import(
"../pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js"
),
);
const NextcloudDataImport = loadable(
() =>
import(
"../pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js"
),
);
const OnlyofficeDataImport = loadable(
() =>
import(
"../pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js"
),
const DeveloperTools = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/developer-tools/index.js"),
),
);
const WebhookHistory = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookHistory"
),
const DataImport = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/data-import/index.js"),
),
);
const WebhookDetails = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookEventDetails"
),
const GoogleDataImport = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js"
),
),
);
const Backup = loadable(
() => import("../pages/PortalSettings/categories/data-management/index"),
const NextcloudDataImport = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js"
),
),
);
const DeleteDataPage = loadable(
() => import("../pages/PortalSettings/categories/delete-data"),
const OnlyofficeDataImport = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js"
),
),
);
const RestoreBackup = loadable(
() =>
import(
"../pages/PortalSettings/categories/data-management/backup/restore-backup/index"
),
);
const Bonus = loadable(() => import("../pages/Bonus"));
const DocSpace = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/DocSpace"
),
const WebhookHistory = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookHistory"
),
),
);
const SimpleRoom = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/SimpleRoom"
),
const WebhookDetails = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookEventDetails"
),
),
);
const Manager = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager"
),
const Backup = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/data-management/index"),
),
);
const RoomSelector = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/RoomSelector"
),
const DeleteDataPage = loadable(() =>
componentLoader(
() => import("../pages/PortalSettings/categories/delete-data"),
),
);
const FileSelector = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/FileSelector"
),
const RestoreBackup = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/data-management/backup/restore-backup/index"
),
),
);
const Editor = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Editor"
),
const Bonus = loadable(() => componentLoader(() => import("../pages/Bonus")));
const DocSpace = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/DocSpace"
),
),
);
const Viewer = loadable(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Viewer"
),
const SimpleRoom = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/SimpleRoom"
),
),
);
const Manager = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Manager"
),
),
);
const RoomSelector = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/RoomSelector"
),
),
);
const FileSelector = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/FileSelector"
),
),
);
const Editor = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Editor"
),
),
);
const Viewer = loadable(() =>
componentLoader(
() =>
import(
"../pages/PortalSettings/categories/developer-tools/JavascriptSDK/presets/Viewer"
),
),
);
const PortalSettingsRoutes = {

View File

@ -651,6 +651,8 @@ class GroupsStore {
};
changeGroupContextSelection = (group: TGroup, isSingleMenu: boolean) => {
this.peopleStore.selectionStore.setBufferSelection(null);
if (isSingleMenu) {
this.singleContextMenuAction(group);
} else {

View File

@ -338,7 +338,6 @@ module.exports = (env, argv) => {
"./shell": "./src/Shell",
"./store": "./src/store",
"./Layout": "./src/components/Layout",
"./Layout/context": "./src/components/Layout/context.js",
"./Main": "./src/components/Main",
"./NavMenu": "./src/components/NavMenu",
"./PreparationPortalDialog":

View File

@ -120,7 +120,7 @@ async function Page({ searchParams }: { searchParams: TSearchParams }) {
searchParams.append("action", action);
}
const redirectURL = `${baseURL}/doceditor?${searchParams.toString()}`;
const redirectURL = `/doceditor?${searchParams.toString()}`;
return permanentRedirect(redirectURL);
}

View File

@ -28,10 +28,7 @@
import { headers } from "next/headers";
import {
createRequest,
getBaseUrl,
} from "@docspace/shared/utils/next-ssr-helper";
import { createRequest } from "@docspace/shared/utils/next-ssr-helper";
import { TenantStatus, EditorConfigErrorType } from "@docspace/shared/enums";
import type {
TDocServiceLocation,
@ -80,6 +77,7 @@ export async function fileCopyAs(
enableExternalExt,
password,
}),
false,
);
const file = await (await fetch(createFile)).json();
@ -141,6 +139,7 @@ export async function createFile(
[["Content-Type", "application/json;charset=utf-8"]],
"POST",
JSON.stringify({ title, templateId, formId }),
false,
);
const file = await (await fetch(createFile)).json();
@ -311,6 +310,8 @@ export async function getUser(share?: string) {
[`/people/@self`],
[share ? ["Request-Token", share] : ["", ""]],
"GET",
undefined,
false,
);
if (!cookie?.includes("asc_auth_key")) return undefined;
@ -335,6 +336,8 @@ export async function getSettings(share?: string) {
],
[share ? ["Request-Token", share] : ["", ""]],
"GET",
undefined,
false,
);
const settingsRes = await fetch(getSettings);
@ -360,6 +363,7 @@ export async function checkFillFromDraft(
],
"POST",
JSON.stringify({ fileId: templateFileId }),
false,
);
const response = await fetch(checkFillFormDraft);
@ -383,6 +387,8 @@ export async function openEdit(
[`/files/file/${fileId}/openedit?${searchParams}`],
[share ? ["Request-Token", share] : ["", ""]],
"GET",
undefined,
false,
);
const res = await fetch(getConfig);
@ -442,6 +448,8 @@ export async function getEditorUrl(
[`/files/docservice?${editorSearchParams ? editorSearchParams : ""}`],
[share ? ["Request-Token", share] : ["", ""]],
"GET",
undefined,
false,
);
const res = await fetch(request);

View File

@ -32,7 +32,7 @@ export function middleware(request: NextRequest) {
const host = request.headers.get("x-forwarded-host");
const proto = request.headers.get("x-forwarded-proto");
const redirectUrl = `${proto}:${host}`;
const redirectUrl = `${proto}://${host}`;
if (request.nextUrl.pathname === "/health") {
console.log("Get login health check for portal: ", redirectUrl);

View File

@ -54,7 +54,7 @@ export const checkIsAuthenticated = async () => {
export async function getSettings() {
const [getSettings] = createRequest(
[`/settings?withPassword=false`],
[`/settings?withPassword=true`],
[["", ""]],
"GET",
);

View File

@ -348,7 +348,7 @@ ContextMenuButtonPure.defaultProps = {
size: 16,
isDisabled: false,
directionX: "left",
isFill: false,
isFill: true,
usePortal: true,
displayIconBorder: false,

View File

@ -52,6 +52,7 @@ const PureText = ({
const StyledText = styled(PureText)`
text-decoration: ${(props) => props.theme.link.textDecoration};
text-underline-offset: 2px;
${(props) =>
props.enableUserSelect

View File

@ -140,6 +140,7 @@ export const StyledSwitchToolbar = styled.div<StyledSwitchToolbarProps>`
display: block;
opacity: 0;
transition: all 0.3s;
top: 0;
${(props) =>
props.left ? "left: 0" : props.isPDFFile ? "right: 20px" : "right: 0"};
@ -154,7 +155,7 @@ export const StyledViewerContainer = styled.div<StyledViewerContainerProps>`
color: ${(props) => props.theme.mediaViewer.color};
display: ${(props) => (props.visible ? "block" : "none")};
overflow: hidden;
span {
> span {
position: fixed;
${(props) =>
props.theme.interfaceDirection === "rtl"

View File

@ -39,6 +39,7 @@ interface ImageViewerProps {
isFistImage: boolean;
isLastImage: boolean;
panelVisible: boolean;
isPublicFile?: boolean;
mobileDetails: JSX.Element;
toolbar: ReturnType<typeof getCustomToolbar>;
devices: DevicesType;

View File

@ -93,6 +93,7 @@ export const ImageViewer = ({
contextModel,
errorTitle,
devices,
isPublicFile,
}: ImageViewerProps) => {
const imgRef = useRef<HTMLImageElement>(null);
const imgWrapperRef = useRef<HTMLDivElement>(null);
@ -523,7 +524,7 @@ export const ImageViewer = ({
: dx,
y: dy,
opacity:
style.scale.get() === 1 && !isDesktop && mdy > 0
style.scale.get() === 1 && !isDesktop && mdy > 0 && !isPublicFile
? imgRef.current.height / 10 / mdy
: style.opacity.get(),
immediate: true,
@ -541,7 +542,7 @@ export const ImageViewer = ({
cancel();
}
if (style.scale.get() === 1 && !isDesktop) {
if (style.scale.get() === 1 && !isDesktop && !isPublicFile) {
if (mdx < -imgRef.current.width / 4) {
return onNext?.();
}

View File

@ -33,7 +33,6 @@ import React, {
} from "react";
import { DeviceType } from "@docspace/shared/enums";
import { Portal } from "@docspace/shared/components/portal";
import { includesMethod } from "@docspace/shared/utils/typeGuards";
import type { TContextMenuRef } from "@docspace/shared/components/context-menu";
@ -251,91 +250,78 @@ export const Viewer = (props: ViewerProps) => {
)}
{isImage ? (
<Portal
visible
element={
<ImageViewer
isTiff={isTiff}
devices={devices}
toolbar={toolbar}
errorTitle={errorTitle}
panelVisible={panelVisible}
mobileDetails={mobileDetails}
imageId={playlistFile.fileId}
version={playlistFile.version}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
thumbnailSrc={playlistFile.thumbnailUrl}
src={fileUrl}
onMask={onMaskClick}
onPrev={onPrevClick}
onNext={onNextClick}
contextModel={contextModel}
generateContextMenu={generateContextMenu}
setIsOpenContextMenu={setIsOpenContextMenu}
resetToolbarVisibleTimer={resetToolbarVisibleTimer}
/>
}
<ImageViewer
key={fileUrl}
isTiff={isTiff}
devices={devices}
toolbar={toolbar}
errorTitle={errorTitle}
panelVisible={panelVisible}
mobileDetails={mobileDetails}
imageId={playlistFile.fileId}
version={playlistFile.version}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
thumbnailSrc={playlistFile.thumbnailUrl}
src={fileUrl}
onMask={onMaskClick}
onPrev={onPrevClick}
onNext={onNextClick}
contextModel={contextModel}
generateContextMenu={generateContextMenu}
setIsOpenContextMenu={setIsOpenContextMenu}
resetToolbarVisibleTimer={resetToolbarVisibleTimer}
isPublicFile={isPublicFile}
/>
) : isVideo || isAudio ? (
<Portal
visible
element={
<ViewerPlayer
isError={isError}
src={fileUrl}
devices={devices}
isAudio={isAudio}
isVideo={isVideo}
audioIcon={audioIcon}
errorTitle={errorTitle}
panelVisible={panelVisible}
isFullScreen={isFullscreen}
isPreviewFile={isPreviewFile}
mobileDetails={mobileDetails}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
isOpenContextMenu={isOpenContextMenu}
thumbnailSrc={playlistFile.thumbnailUrl}
canDownload={!!targetFile?.security.Download}
onPrev={onPrevClick}
onNext={onNextClick}
setIsError={setIsError}
onMask={handleMaskClick}
contextModel={contextModel}
setPanelVisible={setPanelVisible}
setIsFullScreen={setIsFullScreen}
onDownloadClick={onDownloadClick}
generateContextMenu={generateContextMenu}
removeToolbarVisibleTimer={removeToolbarVisibleTimer}
removePanelVisibleTimeout={removePanelVisibleTimeout}
restartToolbarVisibleTimer={restartToolbarVisibleTimer}
isThirdParty={targetFile?.providerItem}
/>
}
<ViewerPlayer
isError={isError}
src={fileUrl}
devices={devices}
isAudio={isAudio}
isVideo={isVideo}
audioIcon={audioIcon}
errorTitle={errorTitle}
panelVisible={panelVisible}
isFullScreen={isFullscreen}
isPreviewFile={isPreviewFile}
mobileDetails={mobileDetails}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
isOpenContextMenu={isOpenContextMenu}
thumbnailSrc={playlistFile.thumbnailUrl}
canDownload={!!targetFile?.security.Download}
onPrev={onPrevClick}
onNext={onNextClick}
setIsError={setIsError}
onMask={handleMaskClick}
contextModel={contextModel}
setPanelVisible={setPanelVisible}
setIsFullScreen={setIsFullScreen}
onDownloadClick={onDownloadClick}
generateContextMenu={generateContextMenu}
removeToolbarVisibleTimer={removeToolbarVisibleTimer}
removePanelVisibleTimeout={removePanelVisibleTimeout}
restartToolbarVisibleTimer={restartToolbarVisibleTimer}
isThirdParty={targetFile?.providerItem}
/>
) : (
isPdf && (
<Portal
visible
element={
<PDFViewer
title={title}
toolbar={toolbar}
devices={devices}
src={fileUrl ?? ""}
mobileDetails={mobileDetails}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
isPDFSidebarOpen={isPDFSidebarOpen}
onNext={onNextClick}
onPrev={onPrevClick}
onMask={handleMaskClick}
generateContextMenu={generateContextMenu}
setIsOpenContextMenu={setIsOpenContextMenu}
setIsPDFSidebarOpen={setIsPDFSidebarOpen}
/>
}
<PDFViewer
title={title}
toolbar={toolbar}
devices={devices}
src={fileUrl ?? ""}
mobileDetails={mobileDetails}
isLastImage={!isNotLastElement}
isFistImage={!isNotFirstElement}
isPDFSidebarOpen={isPDFSidebarOpen}
onNext={onNextClick}
onPrev={onPrevClick}
onMask={handleMaskClick}
generateContextMenu={generateContextMenu}
setIsOpenContextMenu={setIsOpenContextMenu}
setIsPDFSidebarOpen={setIsPDFSidebarOpen}
/>
)
)}

View File

@ -24,9 +24,17 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React, { forwardRef, useImperativeHandle, useState } from "react";
import React, {
forwardRef,
useImperativeHandle,
useRef,
useState,
} from "react";
import MediaContextMenu from "PUBLIC_DIR/images/vertical-dots.react.svg";
import { useClickOutside } from "../../../../utils/useClickOutside";
import ImageViewerToolbarProps, {
ImperativeHandle,
ToolbarItemType,
@ -50,11 +58,17 @@ const ViewerToolbar = forwardRef<ImperativeHandle, ImageViewerToolbarProps>(
},
ref,
) => {
const contextMenuRef = useRef<HTMLLIElement>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [percent, setPercent] = useState<number>(() =>
Math.round(percentValue * 100),
);
useClickOutside(contextMenuRef, () => {
setIsOpen(false);
});
useImperativeHandle(
ref,
() => {
@ -71,6 +85,7 @@ const ViewerToolbar = forwardRef<ImperativeHandle, ImageViewerToolbarProps>(
const contextMenu = generateContextMenu(isOpen);
return (
<ToolbarItem
ref={contextMenuRef}
style={{ position: "relative" }}
key={item.key}
onClick={() => {

View File

@ -2094,6 +2094,7 @@ export const getBaseTheme = () => {
borderColor: grayLightMid,
thumbnailBorderColor: grayLightMid,
textColor: black,
errorColor: "#F21C0E",
closeButtonWrapperPadding: "0px",
closeButtonIcon: white,

View File

@ -2067,6 +2067,7 @@ const Dark: TTheme = {
borderColor: "#474747",
thumbnailBorderColor: grayLightMid,
textColor: white,
errorColor: "#E06451",
closeButtonWrapperPadding: "6px",
closeButtonIcon: black,

View File

@ -34,13 +34,17 @@ export const getBaseUrl = () => {
const host = hdrs.get("x-forwarded-host");
const proto = hdrs.get("x-forwarded-proto");
const baseURL = `${proto}:${host}`;
const baseURL = `${proto}://${host}`;
return baseURL;
};
export const getAPIUrl = () => {
const baseUrl = process.env.API_HOST?.trim() ?? getBaseUrl();
export const getAPIUrl = (internalRequest: boolean) => {
const baseUrl = internalRequest
? process.env.API_HOST?.trim() ?? getBaseUrl()
: getBaseUrl();
// const baseUrl = getBaseUrl();
const baseAPIUrl = `${baseUrl}/${API_PREFIX}`;
@ -52,18 +56,19 @@ export const createRequest = (
newHeaders: [string, string][],
method: string,
body?: string,
internalRequest: boolean = true,
) => {
const hdrs = new Headers(headers());
const apiURL = getAPIUrl();
const apiURL = getAPIUrl(internalRequest);
newHeaders.forEach((hdr) => {
if (hdr[0]) hdrs.set(hdr[0], hdr[1]);
});
const host = hdrs.get("x-forwarded-host");
const baseURL = getBaseUrl();
if (host && process.env.API_HOST?.trim()) hdrs.set("origin", host);
if (baseURL && process.env.API_HOST?.trim()) hdrs.set("origin", baseURL);
const urls = paths.map((path) => `${apiURL}${path}`);

View File

@ -28,12 +28,12 @@
"use client";
import { useEffect } from "react";
import { DependencyList, RefObject, useEffect } from "react";
export const useClickOutside = (
ref: { current: HTMLElement },
handler: () => void,
...deps: unknown[]
export const useClickOutside = <T extends HTMLElement>(
ref: RefObject<T>,
handler: VoidFunction,
...deps: DependencyList
) => {
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {