Merge pull request #840 from ONLYOFFICE/feature/redesign-alert-bar

Feature/redesign alert bar
This commit is contained in:
Alexey Safronov 2022-09-20 13:51:32 +03:00 committed by GitHub
commit d9353c91fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 230 additions and 352 deletions

View File

@ -29,6 +29,7 @@ import { useThemeDetector } from "SRC_DIR/helpers/utils";
import { isMobileOnly } from "react-device-detect";
import IndicatorLoader from "./components/IndicatorLoader";
import DialogsWrapper from "./components/dialogs/DialogsWrapper";
import MainBar from "./components/MainBar";
// const { proxyURL } = AppServerConfig;
// const homepage = config.homepage;
@ -404,73 +405,77 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
<Toast />
<ReactSmartBanner t={t} ready={ready} />
{isEditor || !isMobileOnly ? <></> : <NavMenu />}
{isMobileOnly && <MainBar />}
<IndicatorLoader />
<ScrollToTop />
<DialogsWrapper t={t} />
<Main isDesktop={isDesktop}>
<Switch>
<PrivateRoute
exact
path={[
"/",
{!isMobileOnly && <MainBar />}
<div className="main-container">
<Switch>
<PrivateRoute
exact
path={[
"/",
"/rooms/personal",
"/rooms/personal/filter",
"/rooms/personal",
"/rooms/personal/filter",
"/rooms/shared",
"/rooms/shared/filter",
"/rooms/shared/:room",
"/rooms/shared/:room/filter",
"/rooms/shared",
"/rooms/shared/filter",
"/rooms/shared/:room",
"/rooms/shared/:room/filter",
"/rooms/archived",
"/rooms/archived/filter",
"/rooms/archived/:room",
"/rooms/archived/:room/filter",
"/rooms/archived",
"/rooms/archived/filter",
"/rooms/archived/:room",
"/rooms/archived/:room/filter",
"/files/favorite",
"/files/favorite/filter",
"/files/favorite",
"/files/favorite/filter",
"/files/recent",
"/files/recent/filter",
"/files/recent",
"/files/recent/filter",
"/files/trash",
"/files/trash/filter",
"/files/trash",
"/files/trash/filter",
"/accounts",
"/accounts/filter",
"/accounts",
"/accounts/filter",
"/accounts/create/:type",
"/accounts/edit/:userId",
"/accounts/view/:userId",
"/accounts/view/@self",
"/accounts/create/:type",
"/accounts/edit/:userId",
"/accounts/view/:userId",
"/accounts/view/@self",
"/settings",
"/settings/common",
"/settings/admin",
//"/settings/connected-clouds",
]}
component={FilesRoute}
/>
<PrivateRoute
path={"/form-gallery/:folderId"}
component={FormGalleryRoute}
/>
<PublicRoute exact path={"/wizard"} component={WizardRoute} />
<PrivateRoute path={"/about"} component={AboutRoute} />
<Route path={"/confirm"} component={ConfirmRoute} />
<PrivateRoute path={"/payments"} component={PaymentsRoute} />
<PrivateRoute
restricted
path={"/portal-settings"}
component={PortalSettingsRoute}
/>
<PrivateRoute
path={"/preparation-portal"}
component={PreparationPortalRoute}
/>
<PrivateRoute path={"/error401"} component={Error401Route} />
<PrivateRoute component={Error404Route} />
</Switch>
"/settings",
"/settings/common",
"/settings/admin",
//"/settings/connected-clouds",
]}
component={FilesRoute}
/>
<PrivateRoute
path={"/form-gallery/:folderId"}
component={FormGalleryRoute}
/>
<PublicRoute exact path={"/wizard"} component={WizardRoute} />
<PrivateRoute path={"/about"} component={AboutRoute} />
<Route path={"/confirm"} component={ConfirmRoute} />
<PrivateRoute path={"/payments"} component={PaymentsRoute} />
<PrivateRoute
restricted
path={"/portal-settings"}
component={PortalSettingsRoute}
/>
<PrivateRoute
path={"/preparation-portal"}
component={PreparationPortalRoute}
/>
<PrivateRoute path={"/error401"} component={Error401Route} />
<PrivateRoute component={Error404Route} />
</Switch>
</div>
</Main>
</Router>
</Layout>

View File

@ -7,9 +7,18 @@ const StyledMain = styled.main`
width: 100vw;
z-index: 0;
display: flex;
flex-direction: row;
flex-direction: column;
box-sizing: border-box;
.main-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
box-sizing: border-box;
}
${isMobileOnly &&
css`
height: auto;

View File

@ -1,10 +1,12 @@
import React, { useEffect, useState } from "react";
import { ADS_TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import SnackBar from "@docspace/components/snackbar";
import { Consumer } from "@docspace/components/utils/context";
import difference from "lodash/difference";
import { ADS_TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import { getBannerAttribute } from "@docspace/components/utils/banner";
import SnackBar from "@docspace/components/snackbar";
const bannerHOC = (props) => {
const { firstLoad, setMaintenanceExist } = props;
@ -63,17 +65,12 @@ const bannerHOC = (props) => {
};
return htmlLink && !firstLoad ? (
<Consumer>
{(context) => (
<SnackBar
sectionWidth={context.sectionWidth}
onLoad={onLoad}
clickAction={onClose}
isCampaigns={true}
htmlContent={htmlLink}
/>
)}
</Consumer>
<SnackBar
onLoad={onLoad}
clickAction={onClose}
isCampaigns={true}
htmlContent={htmlLink}
/>
) : null;
};

View File

@ -0,0 +1,78 @@
import React from "react";
import { inject, observer } from "mobx-react";
import styled, { css } from "styled-components";
import { isMobileOnly } from "react-device-detect";
import Bar from "./Bar";
import SnackBar from "@docspace/components/snackbar";
const StyledContainer = styled.div`
width: 100%;
max-width: 100%;
${isMobileOnly &&
css`
width: calc(100% + 16px);
max-width: calc(100% + 16px);
margin-right: -16px;
margin-top: 48px;
`}
#bar-banner {
margin-bottom: -3px;
}
#bar-frame {
min-width: 100%;
max-width: 100%;
}
`;
//TODO: remove commented code after update bar logic
const MainBar = ({
firstLoad,
checkedMaintenance,
snackbarExist,
setMaintenanceExist,
}) => {
// const [isVisible, setIsVisible] = React.useState(false);
React.useEffect(() => {
// setTimeout(() => setIsVisible(true), 9000);
return () => setMaintenanceExist && setMaintenanceExist(false);
}, []);
return (
<StyledContainer id={"main-bar"} className={"main-bar"}>
{checkedMaintenance && !snackbarExist && (
<Bar firstLoad={firstLoad} setMaintenanceExist={setMaintenanceExist} />
)}
{/* {isVisible && (
<SnackBar
headerText={"Rooms is about to be exceeded: 10 / 12"}
text={
"You can archived the unnecessary rooms or click here to find a better pricing plan for your portal."
}
isCampaigns={false}
opacity={1}
onLoad={() => console.log("load snackbar")}
/>
)} */}
</StyledContainer>
);
};
export default inject(({ auth, filesStore }) => {
const {
checkedMaintenance,
setMaintenanceExist,
snackbarExist,
} = auth.settingsStore;
const { firstLoad } = filesStore;
return { firstLoad, checkedMaintenance, snackbarExist, setMaintenanceExist };
})(observer(MainBar));

View File

@ -1,83 +0,0 @@
import React, { useEffect, useState } from "react";
import difference from "lodash/difference";
import SnackBar from "@docspace/components/snackbar";
import { Consumer } from "@docspace/components/utils/context";
import { getBannerAttribute } from "@docspace/components/utils/banner";
import { ADS_TIMEOUT } from "SRC_DIR/helpers/constants";
const bannerHOC = (props) => {
const { firstLoad, setMaintenanceExist } = props;
const [htmlLink, setHtmlLink] = useState();
const [campaigns, setCampaigns] = useState();
const { loadLanguagePath } = getBannerAttribute();
const bar = (localStorage.getItem("bar") || "")
.split(",")
.filter((bar) => bar.length > 0);
const updateBanner = async () => {
const closed = JSON.parse(localStorage.getItem("barClose"));
const banner = difference(bar, closed);
let index = Number(localStorage.getItem("barIndex") || 0);
if (banner.length < 1 || index + 1 >= banner.length) {
index = 0;
} else {
index++;
}
try {
const [htmlUrl, campaigns] = await loadLanguagePath();
setHtmlLink(htmlUrl);
setCampaigns(campaigns);
} catch (e) {
updateBanner();
}
localStorage.setItem("barIndex", index);
return;
};
useEffect(() => {
const updateTimeout = setTimeout(() => updateBanner(), 1000);
const updateInterval = setInterval(updateBanner, ADS_TIMEOUT);
return () => {
clearTimeout(updateTimeout);
clearInterval(updateInterval);
};
}, []);
const onClose = () => {
setMaintenanceExist(false);
const closeItems = JSON.parse(localStorage.getItem("barClose")) || [];
const closed =
closeItems.length > 0 ? [...closeItems, campaigns] : [campaigns];
localStorage.setItem("barClose", JSON.stringify(closed));
setHtmlLink(null);
};
const onLoad = () => {
setMaintenanceExist(true);
};
return htmlLink && !firstLoad ? (
<Consumer>
{(context) => (
<SnackBar
sectionWidth={context.sectionWidth}
onLoad={onLoad}
clickAction={onClose}
isCampaigns={true}
htmlContent={htmlLink}
/>
)}
</Consumer>
) : null;
};
export default bannerHOC;

View File

@ -2,4 +2,3 @@ export { default as SectionHeaderContent } from "./Header";
export { default as SectionBodyContent } from "./Body";
export { default as SectionFilterContent } from "./Filter";
export { default as SectionPagingContent } from "./Paging";
export { default as Bar } from "./Bar";

View File

@ -18,7 +18,6 @@ import {
SectionBodyContent,
SectionFilterContent,
SectionPagingContent,
Bar,
} from "./Section";
import Dialogs from "./Section/Body/Dialogs"; //TODO: Move dialogs to another folder
@ -87,11 +86,7 @@ const PureHome = ({
<Section.SectionHeader>
<SectionHeaderContent />
</Section.SectionHeader>
<Section.SectionBar>
{checkedMaintenance && !snackbarExist && (
<Bar setMaintenanceExist={setMaintenanceExist} />
)}
</Section.SectionBar>
<Section.SectionFilter>
<SectionFilterContent />
</Section.SectionFilter>

View File

@ -2,4 +2,3 @@ export { default as SectionHeaderContent } from "./Header";
export { default as SectionBodyContent } from "./Body";
export { default as SectionFilterContent } from "./Filter";
export { default as SectionPagingContent } from "./Paging";
export { default as Bar } from "./Bar";

View File

@ -21,7 +21,6 @@ import {
SectionFilterContent,
SectionHeaderContent,
SectionPagingContent,
Bar,
} from "./Section";
import { InfoPanelBodyContent, InfoPanelHeaderContent } from "./InfoPanel";
import MediaViewer from "./MediaViewer";
@ -524,16 +523,6 @@ class PureHome extends React.Component {
)}
</Section.SectionHeader>
<Section.SectionBar>
{checkedMaintenance && !snackbarExist && (
<Bar
firstLoad={firstLoad}
personal={personal}
setMaintenanceExist={setMaintenanceExist}
/>
)}
</Section.SectionBar>
<Section.SectionFilter>
{isFrame ? (
showFilter && <SectionFilterContent />

View File

@ -68,8 +68,7 @@ const StyledArticle = styled.article`
position: fixed;
margin: 0;
padding: 0;
margin-top: ${(props) =>
props.isBannerVisible ? "-16px" : "64px"} !important;
top: ${(props) => (props.isBannerVisible ? "-16px" : "64px")} !important;
height: calc(100% - 64px) !important;
`}

View File

@ -16,7 +16,7 @@ import SubSectionHeader from "./sub-components/section-header";
import SubSectionFilter from "./sub-components/section-filter";
import SubSectionBody from "./sub-components/section-body";
import SubSectionBodyContent from "./sub-components/section-body-content";
import SubSectionBar from "./sub-components/section-bar";
import SubSectionPaging from "./sub-components/section-paging";
//import SectionToggler from "./sub-components/section-toggler";
import InfoPanel from "./sub-components/info-panel";
@ -35,78 +35,11 @@ const StyledSelectoWrapper = styled.div`
}
`;
const StyledMainBar = styled.div`
box-sizing: border-box;
${NoUserSelect}
margin-left: -20px;
width: calc(100% + 20px);
#bar-banner {
margin-bottom: -3px;
}
#bar-frame {
min-width: 100%;
max-width: 100%;
}
@media ${tablet} {
width: calc(100% + 16px);
margin-left: -16px;
}
${isMobile &&
css`
width: calc(100% + 32px) !important;
margin-left: -16px;
`}
@media ${mobile} {
width: 100vw !important;
max-width: 100vw !important;
}
${isMobileOnly &&
css`
width: 100vw !important;
max-width: 100vw !important;
#bar-frame {
min-width: 100vw;
}
`}
${(props) =>
!props.isSectionHeaderAvailable &&
css`
width: 100vw !important;
max-width: 100vw !important;
${isMobile &&
css`
position: fixed;
top: 48px;
left: 0;
margin-left: 0 !important;
box-sizing: border-box;
`}
`}
`;
function SectionHeader() {
return null;
}
SectionHeader.displayName = "SectionHeader";
function SectionBar() {
return null;
}
SectionBar.displayName = "SectionBar";
function SectionFilter() {
return null;
}
@ -136,7 +69,7 @@ class Section extends React.Component {
static SectionHeader = SectionHeader;
static SectionFilter = SectionFilter;
static SectionBody = SectionBody;
static SectionBar = SectionBar;
static SectionPaging = SectionPaging;
static InfoPanelBody = InfoPanelBody;
static InfoPanelHeader = InfoPanelHeader;
@ -223,7 +156,7 @@ class Section extends React.Component {
} = this.props;
let sectionHeaderContent = null;
let sectionBarContent = null;
let sectionFilterContent = null;
let sectionPagingContent = null;
let sectionBodyContent = null;
@ -241,9 +174,7 @@ class Section extends React.Component {
case SectionFilter.displayName:
sectionFilterContent = child;
break;
case SectionBar.displayName:
sectionBarContent = child;
break;
case SectionPaging.displayName:
sectionPagingContent = child;
break;
@ -268,7 +199,6 @@ class Section extends React.Component {
!!sectionBodyContent ||
isSectionFilterAvailable ||
isSectionPagingAvailable,
isSectionBarAvailable = !!sectionBarContent,
isSectionAvailable =
isSectionHeaderAvailable ||
isSectionFilterAvailable ||
@ -296,30 +226,10 @@ class Section extends React.Component {
showText={showText}
viewAs={viewAs}
maintenanceExist={maintenanceExist}
isSectionBarAvailable={isSectionBarAvailable}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
settingsStudio={settingsStudio}
>
{!isMobile && (
<StyledMainBar
width={width}
id="main-bar"
className={"main-bar"}
showText={showText}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
>
{sectionBarContent
? sectionBarContent.props.children
: null}
</SubSectionBar>
</StyledMainBar>
)}
{isSectionHeaderAvailable && !isMobile && (
<SubSectionHeader
maintenanceExist={maintenanceExist}
@ -360,27 +270,6 @@ class Section extends React.Component {
settingsStudio={settingsStudio}
selectoRef={this.selectoRef}
>
{isMobile && (
<StyledMainBar
width={width}
id="main-bar"
className={"main-bar"}
showText={showText}
isSectionHeaderAvailable={
isSectionHeaderAvailable
}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
>
{sectionBarContent
? sectionBarContent.props.children
: null}
</SubSectionBar>
</StyledMainBar>
)}
{isSectionHeaderAvailable && isMobile && (
<SubSectionHeader
className="section-body_header"

View File

@ -1,17 +0,0 @@
import React from "react";
import equal from "fast-deep-equal/react";
class SectionBar extends React.Component {
componentWillUnmount() {
this.props.setMaintenanceExist && this.props.setMaintenanceExist(false);
}
render() {
const { children } = this.props;
return <>{children}</>;
}
}
SectionBar.displayName = "SectionBar";
export default SectionBar;

View File

@ -71,7 +71,6 @@ const StyledSectionContainer = styled.section`
css`
width: 100vw !important;
max-width: 100vw !important;
margin-top: 48px !important;
`}
.layout-progress-bar {

View File

@ -124,13 +124,14 @@ class SnackBar extends React.Component {
}}
/>
) : (
<>
{showIcon && (
<Box className="logo">
<StyledLogoIcon size="medium" color={textColor} />
</Box>
)}
<Box className="text-container" textalign={textAlign}>
<div className="text-container">
<div className="header-body" textalign={textAlign}>
{showIcon && (
<Box className="logo">
<StyledLogoIcon size="medium" color={textColor} />
</Box>
)}
<Heading
size="xsmall"
isInline={true}
@ -140,40 +141,42 @@ class SnackBar extends React.Component {
>
{headerText}
</Heading>
<div className="text-body" textalign={textAlign}>
</div>
<div className="text-body">
<Text
as="p"
className={"text"}
color={textColor}
fontSize={fontSize}
fontWeight={fontWeight}
noSelect
>
{text}
</Text>
{btnText && (
<Text
as="p"
color={textColor}
fontSize={fontSize}
fontWeight={fontWeight}
className="button"
onClick={this.onActionClick}
>
{text}
{btnText}
</Text>
)}
{btnText && (
<Text
color={textColor}
className="button"
onClick={this.onActionClick}
>
{btnText}
</Text>
)}
{countDownTime > -1 && (
<Countdown
date={Date.now() + countDownTime}
renderer={this.countDownRenderer}
onComplete={this.onActionClick}
/>
)}
</div>
</Box>
</>
{countDownTime > -1 && (
<Countdown
date={Date.now() + countDownTime}
renderer={this.countDownRenderer}
onComplete={this.onActionClick}
/>
)}
</div>
</div>
)}
{!btnText && (
<button className="action" onClick={this.onActionClick}>
<StyledCrossIcon size="medium" />
<StyledCrossIcon size="small" />
</button>
)}
</StyledSnackBar>

View File

@ -1,5 +1,5 @@
import styled from "styled-components";
import InfoIcon from "../../../public/images/info.react.svg";
import InfoIcon from "../../../public/images/danger.toast.react.svg";
import commonIconsStyles from "../utils/common-icons-style";
const StyledLogoIcon = styled(InfoIcon)`

View File

@ -31,10 +31,6 @@ const StyledSnackBar = styled(Box)`
background-color: ${(props) => props.backgroundColor};
background-image: url(${(props) => props.backgroundImg || ""});
.logo {
padding-right: 10px;
}
.text-container {
width: 100%;
display: flex;
@ -42,8 +38,24 @@ const StyledSnackBar = styled(Box)`
gap: 5px;
text-align: ${(props) => props.textalign};
.text-header {
margin: 0;
.header-body {
width: 100%;
height: 16px;
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
justify-content: start;
.text-header {
font-size: 12px;
line-height: 16px;
font-weight: 600;
text-align: center;
margin: 0;
}
}
.text-body {
@ -52,6 +64,12 @@ const StyledSnackBar = styled(Box)`
flex-direction: row;
gap: 10px;
justify-content: ${(props) => props.textalign};
.text {
font-size: 12px;
line-height: 16px;
font-weight: 400;
}
}
}
@ -61,12 +79,12 @@ const StyledSnackBar = styled(Box)`
border: none;
font-size: inherit;
color: "#333";
margin: 0 0 0 24px;
margin: -2px 4px 4px 24px;
padding: 0;
min-width: min-content;
cursor: pointer;
margin-left: auto;
padding-left: 8px;
text-decoration: underline;
}

View File

@ -1,3 +0,0 @@
export { default as CheckToastIcon } from "./check.toast.react.svg";
export { default as DangerToastIcon } from "./danger.toast.react.svg";
export { default as InfoToastIcon } from "./info.toast.react.svg";

View File

@ -2,7 +2,9 @@ import React from "react";
import { toast } from "react-toastify";
import styled from "styled-components";
import { CheckToastIcon, DangerToastIcon, InfoToastIcon } from "./svg";
import CheckToastIcon from "../../../public/images/check.toast.react.svg";
import DangerToastIcon from "../../../public/images/danger.toast.react.svg";
import InfoToastIcon from "../../../public/images/info.toast.react.svg";
import Text from "../text";
import {

View File

Before

Width:  |  Height:  |  Size: 667 B

After

Width:  |  Height:  |  Size: 667 B

View File

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 684 B

View File

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 558 B