Merge branch 'develop' into feature/open-location

This commit is contained in:
Vlada Gazizova 2023-02-16 18:23:43 +03:00
commit b5712eaba8
110 changed files with 3942 additions and 2631 deletions

View File

@ -244,17 +244,17 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
if (_context.Check("avatarMax"))
{
result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("avatarMedium"))
{
result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("avatar"))
{
result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("listAdminModules"))

View File

@ -5,7 +5,7 @@
"DeleteFolder": "You are about to delete this folder. Are you sure you want to continue?",
"DeleteFile": "You are about to delete this file. Are you sure you want to continue?",
"MoveToTrashButton": "Move to Trash",
"MoveToTrashFile": "You are about to delete this file. Please note that if you have shared it with someone, it will become unavailable. Are you sure you want to continue?",
"MoveToTrashFile": "You are about to delete this file. Please note that if you have shared it with someone, it will become unavailable. The file will be permanently deleted in 30 days. Are you sure you want to continue?",
"MoveToTrashFileFromPersonal": "You are about to delete this file. Are you sure you want to continue?",
"MoveToTrashFolder": "You are about to delete this folder. Please note that if you have shared it with someone, it will become unavailable. Are you sure you want to continue?",
"MoveToTrashFolderFromPersonal": "You are about to delete this folder. Are you sure you want to continue?",

View File

@ -13,6 +13,7 @@
"BackToParentFolderButton": "Back to parent folder",
"ByAuthor": "Author",
"ByCreation": "Created",
"ByErasure": "Erasure",
"ByLastModified": "Modified",
"ByOwner": "Owner",
"CollaborationRooms": "Collaboration",
@ -22,6 +23,7 @@
"CopyItems": "<strong>{{qty}}</strong> elements copied",
"CreateRoom": "Create room",
"CustomRooms": "Custom",
"DaysRemaining": "Days remaining: {{daysRemaining}}",
"Document": "Document",
"EditRoom": "Edit room",
"EmptyFile": "Empty file",
@ -100,7 +102,8 @@
"TooltipElementCopyMessage": "Copy {{element}}",
"TooltipElementsCopyMessage": "Copy {{element}} elements",
"TooltipElementsMoveMessage": "Move {{element}} elements",
"TrashEmptyDescription": "All deleted files are moved to 'Trash'. Restore files deleted by mistake or delete them permanently. Please note, that the files deleted from the 'Trash' cannot be restored any longer.",
"TrashErasureWarning": "Items in Trash are automatically deleted after 30 days",
"TrashEmptyDescription": "All deleted files are moved to 'Trash'. Restore files deleted by mistake or delete them permanently. Files in 'Trash' are automatically deleted after 30 days. Please note, that the files deleted from the 'Trash' cannot be restored any longer.",
"UnarchivedRoomAction": "The room '{{name}}' is unarchived",
"UnarchivedRoomsAction": "The rooms are unarchived",
"UnblockVersion": "Unblock/Check-in",

View File

@ -73,7 +73,7 @@
"DeleteTheme": "Delete theme",
"DeleteThemeForever": "Delete theme forever?",
"DeleteThemeNotice": "The theme will be deleted permanently. You will not be able to undo this action.",
"DeveloperTools": "Developer",
"DeveloperTools": "Developer Tools",
"Disabled": "Disabled",
"DownloadCopy": "Download copy",
"DownloadReportBtnText": "Download report",

View File

@ -73,7 +73,7 @@
"DeleteTheme": "Удалить тему",
"DeleteThemeForever": "Удалить тему навсегда?",
"DeleteThemeNotice": "Тема будет удалена навсегда. Вы не сможете отменить это действие.",
"DeveloperTools": "Разработчик",
"DeveloperTools": "Инструменты разработчика",
"Disabled": "Отключено",
"DownloadCopy": "Скачать копию",
"DownloadReportBtn": "Скачать и открыть отчет",

View File

@ -49,20 +49,17 @@ export default function withContent(WrappedContent) {
render() {
const {
element,
isDesktop,
isTrashFolder,
isArchiveFolder,
item,
onFilesClick,
t,
viewer,
titleWithoutExt,
} = this.props;
const { access, createdBy, fileExst, fileStatus, href, icon, id } = item;
const { access, createdBy, fileStatus, href } = item;
const updatedDate = this.getStatusByDate(false);
const createdDate = this.getStatusByDate(true);

View File

@ -11,6 +11,7 @@ import {
isMobileOnly,
isChrome,
isTablet,
isAndroid,
} from "react-device-detect";
import { inject, observer } from "mobx-react";
@ -130,6 +131,10 @@ const Layout = (props) => {
}
}
if (isMobileOnly && isAndroid && isChrome) {
height = `calc(100vh - ${correctorMobileChrome}px)`;
}
// if (isTablet && isIOS && isSafari) {
// if (
// window.innerHeight < window.innerWidth &&

View File

@ -58,9 +58,10 @@ export const CategoryType = Object.freeze({
* @readonly
*/
export const TableVersions = Object.freeze({
Files: "2",
Rooms: "1",
Files: "2",
Accounts: "3",
Trash: "4",
});
export const BINDING_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";

View File

@ -13,6 +13,7 @@ import config from "PACKAGE_FILE";
import { combineUrl, toUrlParams } from "@docspace/common/utils";
import { addFileToRecentlyViewed } from "@docspace/common/api/files";
import i18n from "./i18n";
import moment from "moment";
import { request } from "@docspace/common/api/client";
@ -265,3 +266,12 @@ export const connectedCloudsTypeIcon = (key) => {
default:
}
};
export const getDaysRemaining = (autoDelete) => {
let daysRemaining = moment(autoDelete)
.startOf("day")
.diff(moment().startOf("day"), "days");
if (daysRemaining <= 0) return "<1";
return "" + daysRemaining;
};

View File

@ -126,7 +126,7 @@ const AboutContent = (props) => {
</div>
<div className="row">
<Text className="row-el" fontSize="13px" noSelect>
<Text className="row-el" fontSize="13px">
{t("DocumentManagement")}:
</Text>
<ColorTheme
@ -138,6 +138,7 @@ const AboutContent = (props) => {
fontWeight="600"
href={linkRepo}
target="_blank"
enableUserSelect
>
&nbsp;ONLYOFFICE DocSpace&nbsp;
</ColorTheme>
@ -148,7 +149,7 @@ const AboutContent = (props) => {
</div>
<div className="row">
<Text className="row-el" fontSize="13px" noSelect>
<Text className="row-el" fontSize="13px">
{t("OnlineEditors")}:
</Text>
<ColorTheme
@ -160,6 +161,7 @@ const AboutContent = (props) => {
fontWeight="600"
href={linkDocs}
target="_blank"
enableUserSelect
>
&nbsp;ONLYOFFICE Docs&nbsp;
</ColorTheme>
@ -169,20 +171,20 @@ const AboutContent = (props) => {
</div>
<div className="row">
<Text className="row-el" fontSize="13px" noSelect>
<Text className="row-el" fontSize="13px">
{t("SoftwareLicense")}:{" "}
</Text>
<Text className="row-el" fontSize="13px" fontWeight="600" noSelect>
<Text className="row-el" fontSize="13px" fontWeight="600">
&nbsp;{license}
</Text>
</div>
<Text className="copyright" fontSize="14px" fontWeight="600" noSelect>
<Text className="copyright" fontSize="14px" fontWeight="600">
© {companyName}
</Text>
<div className="row">
<Text className="address-title" fontSize="13px" noSelect>
<Text className="address-title" fontSize="13px">
{t("AboutCompanyAddressTitle")}:{" "}
</Text>
<Text className="address-title select-el" fontSize="13px">
@ -191,7 +193,7 @@ const AboutContent = (props) => {
</div>
<div className="row">
<Text className="tel-title" fontSize="13px" noSelect>
<Text className="tel-title" fontSize="13px">
{t("Common:Phone")}:{" "}
</Text>
<Text className="tel-title select-el" fontSize="13px">
@ -200,7 +202,7 @@ const AboutContent = (props) => {
</div>
<div className="row">
<Text className="row-el" fontSize="13px" noSelect>
<Text className="row-el" fontSize="13px">
{t("AboutCompanyEmailTitle")}:
</Text>
@ -212,13 +214,14 @@ const AboutContent = (props) => {
fontSize="13px"
fontWeight="600"
href={`mailto:${companyInfoSettingsData.email}`}
enableUserSelect
>
&nbsp;{email}
</ColorTheme>
</div>
<div className="row">
<Text className="row-el" fontSize="13px" noSelect>
<Text className="row-el" fontSize="13px">
{t("Site")}:
</Text>
@ -231,6 +234,7 @@ const AboutContent = (props) => {
fontWeight="600"
target="_blank"
href={site}
enableUserSelect
>
&nbsp;{site.replace(/^https?\:\/\//i, "")}
</ColorTheme>

View File

@ -32,7 +32,6 @@ import { resendInvitesAgain } from "@docspace/common/api/people";
const StyledContainer = styled.div`
width: 100%;
min-height: 33px;
height: 69px;
.group-button-menu-container {
margin: 0 0 0 -20px;
@ -81,6 +80,7 @@ const StyledContainer = styled.div`
@media ${tablet} {
grid-template-columns: 1fr auto;
height: 60px;
}
${isMobile &&

View File

@ -3,6 +3,7 @@ import styled, { css } from "styled-components";
import { isIOS, isFirefox, isMobileOnly } from "react-device-detect";
import { inject, observer } from "mobx-react";
import { getBgPattern } from "@docspace/common/utils";
import { hugeMobile } from "@docspace/components/utils/device";
const StyledWrapper = styled.div`
height: ${isIOS && !isFirefox ? "calc(var(--vh, 1vh) * 100)" : "100vh"};
@ -18,17 +19,21 @@ const StyledWrapper = styled.div`
min-height: 100%;
width: 100%;
`}
`;
const BgBlock = styled.div`
background-image: ${(props) => props.bgPattern};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: 100% 100%;
background-size: cover;
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: -1;
@media (max-width: 1024px) {
background-size: cover;
}
@media (max-width: 428px) {
@media ${hugeMobile} {
background-image: none;
}
`;
@ -37,7 +42,12 @@ const ConfirmWrapper = (props) => {
const { children, currentColorScheme } = props;
const bgPattern = getBgPattern(currentColorScheme?.id);
return <StyledWrapper bgPattern={bgPattern}>{children}</StyledWrapper>;
return (
<StyledWrapper>
<BgBlock bgPattern={bgPattern} />
{children}
</StyledWrapper>
);
};
export default inject(({ auth }) => {

View File

@ -1,12 +1,13 @@
import styled from "styled-components";
import { mobile, tablet } from "@docspace/components/utils/device";
import { hugeMobile, mobile, tablet } from "@docspace/components/utils/device";
export const StyledPage = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin: 56px auto 0 auto;
margin: 0 auto;
max-width: 960px;
box-sizing: border-box;
@media ${tablet} {
padding: 0 16px;
@ -30,6 +31,23 @@ export const StyledPage = styled.div`
}
`;
export const StyledContent = styled.div`
min-height: 100vh;
flex: 1 0 auto;
flex-direction: column;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
@media ${hugeMobile} {
justify-content: start;
min-height: 100%;
}
`;
export const StyledHeader = styled.div`
.title {
margin-bottom: 32px;
@ -44,7 +62,11 @@ export const StyledHeader = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 64px;
padding-bottom: 40px;
}
@media ${hugeMobile} {
margin-top: 0;
}
`;
@ -52,9 +74,27 @@ export const StyledBody = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin: 56px auto;
@media ${mobile} {
@media ${hugeMobile} {
width: 100%;
margin: 0 auto;
}
.title {
margin-bottom: 32px;
text-align: center;
}
.subtitle {
margin-bottom: 32px;
}
.docspace-logo {
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 40px;
}
.password-field-wrapper {

View File

@ -5,7 +5,6 @@ import Text from "@docspace/components/text";
import TextInput from "@docspace/components/text-input";
import PasswordInput from "@docspace/components/password-input";
import Button from "@docspace/components/button";
import Section from "@docspace/common/components/Section";
import FieldContainer from "@docspace/components/field-container";
import { inject, observer } from "mobx-react";
import { EmployeeActivationStatus } from "@docspace/common/constants";
@ -16,7 +15,12 @@ import {
} from "@docspace/common/api/people";
import { createPasswordHash } from "@docspace/common/utils";
import toastr from "@docspace/components/toast/toastr";
import { StyledPage, StyledBody, StyledHeader } from "./StyledConfirm";
import {
StyledPage,
StyledContent,
StyledBody,
StyledHeader,
} from "./StyledConfirm";
import withLoader from "../withLoader";
import { getPasswordErrorMessage } from "SRC_DIR/helpers/utils";
@ -135,123 +139,115 @@ const ActivateUserForm = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
<StyledContent>
<StyledBody>
<StyledHeader>
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
<Text className="subtitle">{t("InviteTitle")}</Text>
</StyledHeader>
<Text className="subtitle">{t("InviteTitle")}</Text>
</StyledHeader>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!nameValid}
errorMessage={t("Common:RequiredField")}
>
<TextInput
id="name"
name="name"
value={name}
placeholder={t("Common:FirstName")}
size="large"
scale={true}
tabIndex={1}
isAutoFocussed={true}
autoComplete="given-name"
onChange={onChangeName}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!nameValid}
errorMessage={t("Common:RequiredField")}
>
<TextInput
id="name"
name="name"
value={name}
placeholder={t("Common:FirstName")}
size="large"
scale={true}
tabIndex={1}
isAutoFocussed={true}
autoComplete="given-name"
onChange={onChangeName}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!surNameValid}
errorMessage={t("Common:RequiredField")}
>
<TextInput
id="surname"
name="surname"
value={surName}
placeholder={t("Common:LastName")}
size="large"
scale={true}
tabIndex={2}
autoComplete="family-name"
onChange={onChangeSurName}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!surNameValid}
errorMessage={t("Common:RequiredField")}
>
<TextInput
id="surname"
name="surname"
value={surName}
placeholder={t("Common:LastName")}
size="large"
scale={true}
tabIndex={2}
autoComplete="family-name"
onChange={onChangeSurName}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
className="confirm-input"
simpleView={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
inputValue={password}
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
size="large"
scale={true}
tabIndex={1}
autoComplete="current-password"
onChange={onChangePassword}
onValidateInput={onValidatePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t("Common:PasswordMinimumLength")}: ${
settings ? settings.minLength : 8
}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t("Common:PasswordLimitUpperCase")}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
// If need copy credentials use t("EmailAndPasswordCopiedToClipboard")
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
className="confirm-input"
simpleView={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
inputValue={password}
hasError={isPasswordErrorShow && !passwordValid}
size="large"
scale={true}
tabIndex={1}
autoComplete="current-password"
onChange={onChangePassword}
onValidateInput={onValidatePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t("Common:PasswordMinimumLength")}: ${
settings ? settings.minLength : 8
}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t("Common:PasswordLimitUpperCase")}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
// If need copy credentials use t("EmailAndPasswordCopiedToClipboard")
/>
</FieldContainer>
<Button
className="confirm-button"
primary
size="normal"
label={t("LoginRegistryButton")}
tabIndex={5}
onClick={onSubmit}
isDisabled={isLoading}
/>
</FieldContainer>
<Button
className="confirm-button"
primary
size="normal"
label={t("LoginRegistryButton")}
tabIndex={5}
onClick={onSubmit}
isDisabled={isLoading}
/>
</StyledBody>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ActivateUserFormWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ActivateUserForm {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => {
const {
greetingSettings,
@ -272,7 +268,7 @@ export default inject(({ auth }) => {
})(
withRouter(
withTranslation(["Confirm", "Common", "Wizard"])(
withLoader(observer(ActivateUserFormWrapper))
withLoader(observer(ActivateUserForm))
)
)
);

View File

@ -3,13 +3,12 @@ import { withRouter } from "react-router";
import { withTranslation } from "react-i18next";
import Text from "@docspace/components/text";
import Button from "@docspace/components/button";
import Section from "@docspace/common/components/Section";
import { inject, observer } from "mobx-react";
import {
StyledPage,
StyledBody,
StyledHeader,
ButtonsWrapper,
StyledContent,
} from "./StyledConfirm";
import withLoader from "../withLoader";
import FormWrapper from "@docspace/components/form-wrapper";
@ -20,60 +19,50 @@ const ChangeOwnerForm = (props) => {
console.log(props.linkData);
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
<Text className="subtitle">
{t("ConfirmOwnerPortalTitle", { newOwner: "NEW OWNER" })}
</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Common:SaveButton")}
tabIndex={2}
isDisabled={false}
//onClick={this.onAcceptClick} // call toast with t("ConfirmOwnerPortalSuccessMessage")
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={2}
isDisabled={false}
//onClick={this.onCancelClick}
/>
</ButtonsWrapper>
</FormWrapper>
</StyledBody>
<FormWrapper>
<Text className="subtitle">
{t("ConfirmOwnerPortalTitle", { newOwner: "NEW OWNER" })}
</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Common:SaveButton")}
tabIndex={2}
isDisabled={false}
//onClick={this.onAcceptClick} // call toast with t("ConfirmOwnerPortalSuccessMessage")
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={2}
isDisabled={false}
//onClick={this.onCancelClick}
/>
</ButtonsWrapper>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ChangeOwnerFormWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ChangeOwnerForm {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
defaultPage: auth.settingsStore.defaultPage,
}))(
withRouter(
withTranslation(["Confirm", "Common"])(
withLoader(observer(ChangeOwnerFormWrapper))
withLoader(observer(ChangeOwnerForm))
)
)
);

View File

@ -1,13 +1,12 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { withRouter } from "react-router";
import { withTranslation } from "react-i18next";
import Text from "@docspace/components/text";
import PasswordInput from "@docspace/components/password-input";
import Button from "@docspace/components/button";
import Section from "@docspace/common/components/Section";
import FieldContainer from "@docspace/components/field-container";
import { inject, observer } from "mobx-react";
import { StyledPage, StyledBody, StyledHeader } from "./StyledConfirm";
import { StyledPage, StyledBody, StyledContent } from "./StyledConfirm";
import withLoader from "../withLoader";
import { getPasswordErrorMessage } from "../../../helpers/utils";
import { createPasswordHash } from "@docspace/common/utils";
@ -26,6 +25,7 @@ const ChangePasswordForm = (props) => {
logout,
changePassword,
linkData,
getSettings,
} = props;
const [password, setPassword] = useState("");
@ -33,6 +33,10 @@ const ChangePasswordForm = (props) => {
const [isPasswordErrorShow, setIsPasswordErrorShow] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (!hashSettings) getSettings(true);
}, []);
const onChangePassword = (e) => {
setPassword(e.target.value);
};
@ -45,7 +49,7 @@ const ChangePasswordForm = (props) => {
setIsPasswordErrorShow(true);
};
const onSubmit = () => {
const onSubmit = async () => {
setIsLoading(true);
if (!password.trim()) {
@ -57,30 +61,34 @@ const ChangePasswordForm = (props) => {
return;
}
const hash = createPasswordHash(password, hashSettings);
const { uid, confirmHeader } = linkData;
changePassword(uid, hash, confirmHeader)
.then(() => logout())
.then(() => {
setIsLoading(false);
toastr.success(t("ChangePasswordSuccess"));
tryRedirectTo(defaultPage);
})
.catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
try {
const hash = createPasswordHash(password, hashSettings);
const { uid, confirmHeader } = linkData;
await changePassword(uid, hash, confirmHeader);
logout();
setIsLoading(false);
toastr.success(t("ChangePasswordSuccess"));
tryRedirectTo(defaultPage);
} catch (error) {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error(errorMessage);
if (errorMessage === "Invalid params") {
toastr.error(t("Common:SomethingWentWrong"));
} else {
toastr.error(t(`${errorMessage}`));
setIsLoading(false);
});
}
setIsLoading(false);
}
};
const onKeyPress = (event) => {
@ -91,83 +99,75 @@ const ChangePasswordForm = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
<div className="password-form">
<Text fontSize="16px" fontWeight="600" className="subtitle">
{t("PassworResetTitle")}
</Text>
<FieldContainer
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
simpleView={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
inputValue={password}
<FormWrapper>
<div className="password-form">
<Text fontSize="16px" fontWeight="600" className="subtitle">
{t("PassworResetTitle")}
</Text>
<FieldContainer
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
size="large"
scale
tabIndex={1}
autoComplete="current-password"
onChange={onChangePassword}
onValidateInput={onValidatePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t("Common:PasswordMinimumLength")}: ${
settings ? settings.minLength : 8
}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t("Common:PasswordLimitUpperCase")}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
/>
</FieldContainer>
</div>
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
simpleView={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
inputValue={password}
hasError={isPasswordErrorShow && !passwordValid}
size="large"
scale
tabIndex={1}
autoComplete="current-password"
onChange={onChangePassword}
onValidateInput={onValidatePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t(
"Common:PasswordMinimumLength"
)}: ${settings ? settings.minLength : 8}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t(
"Common:PasswordLimitUpperCase"
)}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
/>
</FieldContainer>
</div>
<Button
primary
size="medium"
scale
label={t("Common:Create")}
tabIndex={5}
onClick={onSubmit}
isDisabled={isLoading}
/>
</FormWrapper>
</StyledBody>
<Button
primary
size="medium"
scale
label={t("Common:Create")}
tabIndex={5}
onClick={onSubmit}
isDisabled={isLoading}
/>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ChangePasswordFormWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ChangePasswordForm {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth, setup }) => {
const {
greetingSettings,
@ -175,6 +175,7 @@ export default inject(({ auth, setup }) => {
defaultPage,
passwordSettings,
theme,
getSettings,
} = auth.settingsStore;
const { changePassword } = setup;
@ -187,11 +188,12 @@ export default inject(({ auth, setup }) => {
logout: auth.logout,
changePassword,
isAuthenticated: auth.isAuthenticated,
getSettings,
};
})(
withRouter(
withTranslation(["Confirm", "Common", "Wizard"])(
withLoader(observer(ChangePasswordFormWrapper))
withLoader(observer(ChangePasswordForm))
)
)
);

View File

@ -4,9 +4,8 @@ import { withTranslation } from "react-i18next";
import Text from "@docspace/components/text";
import TextInput from "@docspace/components/text-input";
import Button from "@docspace/components/button";
import Section from "@docspace/common/components/Section";
import { inject, observer } from "mobx-react";
import { StyledPage, StyledBody, StyledHeader } from "./StyledConfirm";
import { StyledPage, StyledBody, StyledContent } from "./StyledConfirm";
import withLoader from "../withLoader";
import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
@ -17,66 +16,54 @@ const ChangePhoneForm = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
<div className="subtitle">
<Text fontSize="16px" fontWeight="600" className="phone-title">
{t("EnterPhone")}
</Text>
<Text>
{t("CurrentNumber")}: {currentNumber}
</Text>
<Text>{t("PhoneSubtitle")}</Text>
</div>
<FormWrapper>
<div className="subtitle">
<Text fontSize="16px" fontWeight="600" className="phone-title">
{t("EnterPhone")}
</Text>
<Text>
{t("CurrentNumber")}: {currentNumber}
</Text>
<Text>{t("PhoneSubtitle")}</Text>
</div>
<TextInput
className="phone-input"
id="phone"
name="phone"
type="phone"
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
hasError={false}
guide={false}
/>
<TextInput
className="phone-input"
id="phone"
name="phone"
type="phone"
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
hasError={false}
guide={false}
/>
<Button
primary
scale
size="medium"
label={t("GetCode")}
tabIndex={2}
isDisabled={false}
/>
</FormWrapper>
</StyledBody>
<Button
primary
scale
size="medium"
label={t("GetCode")}
tabIndex={2}
isDisabled={false}
/>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ChangePhoneFormWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ChangePhoneForm {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
}))(
withRouter(
withTranslation("Confirm")(withLoader(observer(ChangePhoneFormWrapper)))
)
withRouter(withTranslation("Confirm")(withLoader(observer(ChangePhoneForm))))
);

View File

@ -2,7 +2,6 @@ import React, { useState } from "react";
import { withRouter } from "react-router";
import { Trans, withTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import Section from "@docspace/common/components/Section";
import Text from "@docspace/components/text";
import Button from "@docspace/components/button";
import Link from "@docspace/components/link";
@ -11,7 +10,7 @@ import { continuePortal } from "@docspace/common/api/portal";
import {
StyledPage,
StyledBody,
StyledHeader,
StyledContent,
ButtonsWrapper,
} from "./StyledConfirm";
@ -40,68 +39,56 @@ const ContinuePortal = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
{isReactivate ? (
<Trans t={t} i18nKey="SuccessReactivate" ns="Confirm">
Your account has been successfully reactivated. In 10 seconds you
will be redirected to the
<Link isHovered href="/">
portal
</Link>
</Trans>
) : (
<>
<Text className="subtitle">{t("PortalContinueTitle")}</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Reactivate")}
tabIndex={1}
onClick={onRestoreClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</>
)}
</FormWrapper>
</StyledBody>
<FormWrapper>
{isReactivate ? (
<Trans t={t} i18nKey="SuccessReactivate" ns="Confirm">
Your account has been successfully reactivated. In 10 seconds
you will be redirected to the
<Link isHovered href="/">
portal
</Link>
</Trans>
) : (
<>
<Text className="subtitle">{t("PortalContinueTitle")}</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Reactivate")}
tabIndex={1}
onClick={onRestoreClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</>
)}
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ContinuePortalWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ContinuePortal {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
theme: auth.settingsStore.theme,
}))(
withRouter(
withTranslation(["Confirm", "Common"])(
withLoader(observer(ContinuePortalWrapper))
)
withTranslation(["Confirm", "Common"])(withLoader(observer(ContinuePortal)))
)
);

View File

@ -16,7 +16,6 @@ import FieldContainer from "@docspace/components/field-container";
import toastr from "@docspace/components/toast/toastr";
import SocialButton from "@docspace/components/social-button";
import { getUserFromConfirm } from "@docspace/common/api/people";
import Section from "@docspace/common/components/Section";
import {
createPasswordHash,
getProviderTranslation,
@ -33,6 +32,7 @@ import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
import Box from "@docspace/components/box";
import DefaultUserPhoto from "PUBLIC_DIR/images/default_user_photo_size_82-82.png";
import { StyledPage, StyledContent } from "./StyledConfirm";
export const ButtonsWrapper = styled.div`
display: flex;
@ -46,7 +46,7 @@ export const ButtonsWrapper = styled.div`
`;
const ConfirmContainer = styled(Box)`
margin-top: 80px;
margin: 56px auto;
display: flex;
flex: 1fr 1fr;
gap: 80px;
@ -54,7 +54,6 @@ const ConfirmContainer = styled(Box)`
justify-content: center;
@media ${tablet} {
margin: 100px auto 0 auto;
display: flex;
flex: 1fr;
flex-direction: column;
@ -63,7 +62,7 @@ const ConfirmContainer = styled(Box)`
}
@media ${hugeMobile} {
margin-top: 32px;
margin: 0 auto;
width: 100%;
flex: 1fr;
flex-direction: column;
@ -132,6 +131,7 @@ const GreetingContainer = styled.div`
}
.docspace-logo {
width: 100%;
padding-bottom: 32px;
.injected-svg {
@ -142,7 +142,7 @@ const GreetingContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 64px;
padding-bottom: 40px;
}
}
`;
@ -187,12 +187,12 @@ const RegisterContainer = styled.div`
//margin-top: 32px;
width: 100%;
@media (max-width: 768px) {
margin: 32px 0 0 0;
@media ${tablet} {
//margin: 32px 0 0 0;
width: 100%;
}
@media (max-width: 375px) {
margin: 32px 0 0 0;
@media ${hugeMobile} {
//margin: 32px 0 0 0;
width: 100%;
}
}
@ -211,10 +211,9 @@ const RegisterContainer = styled.div`
.password-field-wrapper {
width: 100%;
}
}
`;
}`;
const Confirm = (props) => {
const CreateUserForm = (props) => {
const { settings, t, greetingTitle, providers, isDesktop, linkData } = props;
const inputRef = React.useRef(null);
@ -553,233 +552,238 @@ const Confirm = (props) => {
const userAvatar = user.hasAvatar ? user.avatar : DefaultUserPhoto;
return (
<ConfirmContainer>
<GreetingContainer isGreetingMode={isGreetingMode}>
<DocspaceLogo className="docspace-logo" />
<Text
fontSize="23px"
fontWeight={700}
textAlign="left"
className="greeting-title"
>
{greetingTitle}
</Text>
<div className="greeting-block">
<Avatar className="avatar" role="user" source={user.avatar} />
<div className="user-info">
<Text fontSize="15px" fontWeight={600}>
{user.firstName} {user.lastName}
<StyledPage>
<StyledContent>
<ConfirmContainer>
<GreetingContainer isGreetingMode={isGreetingMode}>
<DocspaceLogo className="docspace-logo" />
<Text
fontSize="23px"
fontWeight={700}
textAlign="left"
className="greeting-title"
>
{greetingTitle}
</Text>
<Text fontSize="12px" fontWeight={600} color="#A3A9AE">
{user.department}
</Text>
</div>
</div>
<div className="tooltip">
<span className="tooltiptext">{t("WelcomeUser")}</span>
</div>
</GreetingContainer>
<div className="greeting-block">
<Avatar className="avatar" role="user" source={user.avatar} />
<div className="user-info">
<Text fontSize="15px" fontWeight={600}>
{user.firstName} {user.lastName}
</Text>
<Text fontSize="12px" fontWeight={600} color="#A3A9AE">
{user.department}
</Text>
</div>
</div>
<FormWrapper>
<RegisterContainer isGreetingMode={isGreetingMode}>
{ssoExists() && <ButtonsWrapper>{ssoButton()}</ButtonsWrapper>}
<div className="tooltip">
<span className="tooltiptext">{t("WelcomeUser")}</span>
</div>
</GreetingContainer>
{oauthDataExists() && (
<>
<ButtonsWrapper>{providerButtons()}</ButtonsWrapper>
{providers && providers.length > 2 && (
<Link
isHovered
type="action"
fontSize="13px"
fontWeight="600"
color="#3B72A7"
className="more-label"
onClick={moreAuthOpen}
>
{t("Common:ShowMore")}
</Link>
<FormWrapper>
<RegisterContainer isGreetingMode={isGreetingMode}>
{ssoExists() && <ButtonsWrapper>{ssoButton()}</ButtonsWrapper>}
{oauthDataExists() && (
<>
<ButtonsWrapper>{providerButtons()}</ButtonsWrapper>
{providers && providers.length > 2 && (
<Link
isHovered
type="action"
fontSize="13px"
fontWeight="600"
color="#3B72A7"
className="more-label"
onClick={moreAuthOpen}
>
{t("Common:ShowMore")}
</Link>
)}
</>
)}
</>
)}
{(oauthDataExists() || ssoExists()) && (
<div className="line">
<Text color="#A3A9AE" className="or-label">
{t("Common:Or")}
</Text>
</div>
)}
{(oauthDataExists() || ssoExists()) && (
<div className="line">
<Text color="#A3A9AE" className="or-label">
{t("Common:Or")}
</Text>
</div>
)}
<form className="auth-form-container">
<div className="auth-form-fields">
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isEmailErrorShow && !emailValid}
errorMessage={
emailErrorText
? t(`Common:${emailErrorText}`)
: t("Common:RequiredField")
}
>
<EmailInput
id="login"
name="login"
type="email"
hasError={isEmailErrorShow && !emailValid}
value={email}
placeholder={t("Common:Email")}
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
isDisabled={isLoading || !!emailFromLink}
autoComplete="username"
onChange={onChangeEmail}
onBlur={onBlurEmail}
onValidateInput={onValidateEmail}
forwardedRef={inputRef}
/>
</FieldContainer>
<form className="auth-form-container">
<div className="auth-form-fields">
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isEmailErrorShow && !emailValid}
errorMessage={
emailErrorText
? t(`Common:${emailErrorText}`)
: t("Common:RequiredField")
}
>
<EmailInput
id="login"
name="login"
type="email"
hasError={isEmailErrorShow && !emailValid}
value={email}
placeholder={t("Common:Email")}
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
isDisabled={isLoading || !!emailFromLink}
autoComplete="username"
onChange={onChangeEmail}
onBlur={onBlurEmail}
onValidateInput={onValidateEmail}
forwardedRef={inputRef}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!fnameValid}
errorMessage={errorText ? errorText : t("Common:RequiredField")}
>
<TextInput
id="first-name"
name="first-name"
type="text"
hasError={!fnameValid}
value={fname}
placeholder={t("Common:FirstName")}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
onChange={onChangeFname}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!fnameValid}
errorMessage={
errorText ? errorText : t("Common:RequiredField")
}
>
<TextInput
id="first-name"
name="first-name"
type="text"
hasError={!fnameValid}
value={fname}
placeholder={t("Common:FirstName")}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
onChange={onChangeFname}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!snameValid}
errorMessage={errorText ? errorText : t("Common:RequiredField")}
>
<TextInput
id="last-name"
name="last-name"
type="text"
hasError={!snameValid}
value={sname}
placeholder={t("Common:LastName")}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
onChange={onChangeSname}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={!snameValid}
errorMessage={
errorText ? errorText : t("Common:RequiredField")
}
>
<TextInput
id="last-name"
name="last-name"
type="text"
hasError={!snameValid}
value={sname}
placeholder={t("Common:LastName")}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
onChange={onChangeSname}
onKeyDown={onKeyPress}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
simpleView={false}
hideNewPasswordButton
showCopyLink={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
hasError={isPasswordErrorShow && !passwordValid}
inputValue={password}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="current-password"
onChange={onChangePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
onValidateInput={onValidatePassword}
tooltipPasswordTitle={`${t("Common:PasswordLimitMessage")}:`}
tooltipPasswordLength={`${t(
"Common:PasswordMinimumLength"
)}: ${settings ? settings.minLength : 8}`}
tooltipPasswordDigits={`${t("Common:PasswordLimitDigits")}`}
tooltipPasswordCapital={`${t(
"Common:PasswordLimitUpperCase"
)}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
/>
</FieldContainer>
<FieldContainer
className="form-field"
isVertical={true}
labelVisible={false}
hasError={isPasswordErrorShow && !passwordValid}
errorMessage={`${t(
"Common:PasswordLimitMessage"
)}: ${getPasswordErrorMessage(t, settings)}`}
>
<PasswordInput
simpleView={false}
hideNewPasswordButton
showCopyLink={false}
passwordSettings={settings}
id="password"
inputName="password"
placeholder={t("Common:Password")}
type="password"
hasError={isPasswordErrorShow && !passwordValid}
inputValue={password}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="current-password"
onChange={onChangePassword}
onBlur={onBlurPassword}
onKeyDown={onKeyPress}
onValidateInput={onValidatePassword}
tooltipPasswordTitle={`${t(
"Common:PasswordLimitMessage"
)}:`}
tooltipPasswordLength={`${t(
"Common:PasswordMinimumLength"
)}: ${settings ? settings.minLength : 8}`}
tooltipPasswordDigits={`${t(
"Common:PasswordLimitDigits"
)}`}
tooltipPasswordCapital={`${t(
"Common:PasswordLimitUpperCase"
)}`}
tooltipPasswordSpecial={`${t(
"Common:PasswordLimitSpecialSymbols"
)}`}
generatePasswordTitle={t("Wizard:GeneratePassword")}
/>
</FieldContainer>
<Button
className="login-button"
primary
size="medium"
scale={true}
label={
isLoading
? t("Common:LoadingProcessing")
: t("LoginRegistryButton")
}
tabIndex={1}
isDisabled={isLoading}
isLoading={isLoading}
onClick={onSubmit}
<Button
className="login-button"
primary
size="medium"
scale={true}
label={
isLoading
? t("Common:LoadingProcessing")
: t("LoginRegistryButton")
}
tabIndex={1}
isDisabled={isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</div>
</form>
<MoreLoginModal
t={t}
visible={moreAuthVisible}
onClose={moreAuthClose}
providers={providers}
onSocialLoginClick={onSocialButtonClick}
ssoLabel={ssoLabel}
ssoUrl={ssoUrl}
/>
</div>
</form>
<MoreLoginModal
t={t}
visible={moreAuthVisible}
onClose={moreAuthClose}
providers={providers}
onSocialLoginClick={onSocialButtonClick}
ssoLabel={ssoLabel}
ssoUrl={ssoUrl}
/>
</RegisterContainer>
</FormWrapper>
</ConfirmContainer>
</RegisterContainer>
</FormWrapper>
</ConfirmContainer>
</StyledContent>
</StyledPage>
);
};
Confirm.propTypes = {
CreateUserForm.propTypes = {
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
const CreateUserForm = (props) => (
<Section>
<Section.SectionBody>
<Confirm {...props} />
</Section.SectionBody>
</Section>
);
export default inject(({ auth }) => {
const {

View File

@ -2,7 +2,6 @@ import React, { useState } from "react";
import { withRouter } from "react-router";
import { Trans, withTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import Section from "@docspace/common/components/Section";
import Text from "@docspace/components/text";
import Button from "@docspace/components/button";
import Link from "@docspace/components/link";
@ -11,7 +10,7 @@ import { suspendPortal } from "@docspace/common/api/portal";
import {
StyledPage,
StyledBody,
StyledHeader,
StyledContent,
ButtonsWrapper,
} from "./StyledConfirm";
@ -50,61 +49,51 @@ const DeactivatePortal = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
{isDeactivate ? (
<Trans t={t} i18nKey="SuccessDeactivate" ns="Confirm">
Your account has been successfully deactivated. In 10 seconds you
will be redirected to the
<Link isHovered href={url}>
site
</Link>
</Trans>
) : (
<>
<Text className="subtitle">{t("PortalDeactivateTitle")}</Text>
<ButtonsWrapper>
<Button
scale
primary
size="medium"
label={t("Settings:Deactivate")}
tabIndex={1}
onClick={onDeactivateClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</>
)}
</FormWrapper>
</StyledBody>
<FormWrapper>
{isDeactivate ? (
<Trans t={t} i18nKey="SuccessDeactivate" ns="Confirm">
Your account has been successfully deactivated. In 10 seconds
you will be redirected to the
<Link isHovered href={url}>
site
</Link>
</Trans>
) : (
<>
<Text className="subtitle">{t("PortalDeactivateTitle")}</Text>
<ButtonsWrapper>
<Button
scale
primary
size="medium"
label={t("Settings:Deactivate")}
tabIndex={1}
onClick={onDeactivateClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</>
)}
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const DeactivatePortalWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<DeactivatePortal {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
theme: auth.settingsStore.theme,
@ -112,7 +101,7 @@ export default inject(({ auth }) => ({
}))(
withRouter(
withTranslation(["Confirm", "Settings", "Common"])(
withLoader(observer(DeactivatePortalWrapper))
withLoader(observer(DeactivatePortal))
)
)
);

View File

@ -7,7 +7,7 @@ import Section from "@docspace/common/components/Section";
import { inject, observer } from "mobx-react";
import { deleteSelf } from "@docspace/common/api/people";
import toastr from "@docspace/components/toast/toastr";
import { StyledPage, StyledBody, StyledHeader } from "./StyledConfirm";
import { StyledPage, StyledBody, StyledContent } from "./StyledConfirm";
import withLoader from "../withLoader";
import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
@ -35,8 +35,8 @@ const ProfileRemoveForm = (props) => {
if (isProfileDeleted) {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{t("DeleteProfileSuccessMessage")}
@ -44,65 +44,55 @@ const ProfileRemoveForm = (props) => {
<Text fontSize="16px" fontWeight="600" className="confirm-subtitle">
{t("DeleteProfileSuccessMessageInfo")}
</Text>
</StyledHeader>
</StyledBody>
</StyledBody>
</StyledContent>
</StyledPage>
);
}
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
<div className="subtitle">
<Text
fontSize="16px"
fontWeight="600"
className="delete-profile-confirm"
>
{t("DeleteProfileConfirmation")}
</Text>
<Text>{t("DeleteProfileConfirmationInfo")}</Text>
</div>
<FormWrapper>
<div className="subtitle">
<Text
fontSize="16px"
fontWeight="600"
className="delete-profile-confirm"
>
{t("DeleteProfileConfirmation")}
</Text>
<Text>{t("DeleteProfileConfirmationInfo")}</Text>
</div>
<Button
primary
scale
size="medium"
label={t("DeleteProfileBtn")}
tabIndex={1}
isDisabled={isLoading}
onClick={onDeleteProfile}
/>
</FormWrapper>
</StyledBody>
<Button
primary
scale
size="medium"
label={t("DeleteProfileBtn")}
tabIndex={1}
isDisabled={isLoading}
onClick={onDeleteProfile}
/>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const ProfileRemoveFormWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<ProfileRemoveForm {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
theme: auth.settingsStore.theme,
logout: auth.logout,
}))(
withRouter(
withTranslation("Confirm")(withLoader(observer(ProfileRemoveFormWrapper)))
withTranslation("Confirm")(withLoader(observer(ProfileRemoveForm)))
)
);

View File

@ -2,7 +2,6 @@ import React, { useState } from "react";
import { withRouter } from "react-router";
import { withTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import Section from "@docspace/common/components/Section";
import Text from "@docspace/components/text";
import Button from "@docspace/components/button";
import toastr from "@docspace/components/toast/toastr";
@ -10,7 +9,7 @@ import { deletePortal } from "@docspace/common/api/portal";
import {
StyledPage,
StyledBody,
StyledHeader,
StyledContent,
ButtonsWrapper,
} from "./StyledConfirm";
@ -36,56 +35,44 @@ const RemovePortal = (props) => {
return (
<StyledPage>
<StyledBody>
<StyledHeader>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
</StyledHeader>
<FormWrapper>
<Text className="subtitle">{t("PortalRemoveTitle")}</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Common:Delete")}
tabIndex={1}
onClick={onDeleteClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</FormWrapper>
</StyledBody>
<FormWrapper>
<Text className="subtitle">{t("PortalRemoveTitle")}</Text>
<ButtonsWrapper>
<Button
primary
scale
size="medium"
label={t("Common:Delete")}
tabIndex={1}
onClick={onDeleteClick}
/>
<Button
scale
size="medium"
label={t("Common:CancelButton")}
tabIndex={1}
onClick={onCancelClick}
/>
</ButtonsWrapper>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
const RemovePortalWrapper = (props) => {
return (
<Section>
<Section.SectionBody>
<RemovePortal {...props} />
</Section.SectionBody>
</Section>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
theme: auth.settingsStore.theme,
}))(
withRouter(
withTranslation(["Confirm", "Common"])(
withLoader(observer(RemovePortalWrapper))
)
withTranslation(["Confirm", "Common"])(withLoader(observer(RemovePortal)))
)
);

View File

@ -6,7 +6,6 @@ import Button from "@docspace/components/button";
import TextInput from "@docspace/components/text-input";
import FieldContainer from "@docspace/components/field-container";
import Text from "@docspace/components/text";
import Section from "@docspace/common/components/Section";
import { inject, observer } from "mobx-react";
import Box from "@docspace/components/box";
import withLoader from "../withLoader";
@ -16,9 +15,10 @@ import { hugeMobile, tablet } from "@docspace/components/utils/device";
import Link from "@docspace/components/link";
import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
import { StyledPage, StyledContent } from "./StyledConfirm";
const StyledForm = styled(Box)`
margin-top: 63px;
margin: 56px auto;
display: flex;
flex: 1fr 1fr;
gap: 80px;
@ -26,18 +26,14 @@ const StyledForm = styled(Box)`
justify-content: center;
@media ${tablet} {
margin: 100px auto 0 auto;
display: flex;
flex: 1fr;
flex-direction: column;
align-items: center;
gap: 32px;
}
@media ${hugeMobile} {
margin-top: 32px;
width: 100%;
flex: 1fr;
margin: 0 auto;
flex-direction: column;
gap: 0px;
padding-right: 8px;
@ -52,7 +48,7 @@ const StyledForm = styled(Box)`
}
.docspace-logo {
padding-bottom: 64px;
padding-bottom: 40px;
@media ${tablet} {
display: flex;
@ -142,97 +138,109 @@ const TfaActivationForm = withLoader((props) => {
};
return (
<StyledForm className="set-app-container">
<Box className="set-app-description" marginProp="0 0 32px 0">
<DocspaceLogo className="docspace-logo" />
<Text isBold fontSize="14px" className="set-app-title">
{t("SetAppTitle")}
</Text>
<StyledPage>
<StyledContent>
<StyledForm className="set-app-container">
<Box className="set-app-description" marginProp="0 0 32px 0">
<DocspaceLogo className="docspace-logo" />
<Text isBold fontSize="14px" className="set-app-title">
{t("SetAppTitle")}
</Text>
<Trans t={t} i18nKey="SetAppDescription" ns="Confirm">
The two-factor authentication is enabled to provide additional portal
security. Configure your authenticator application to continue work on
the portal. For example you could use Google Authenticator for
<Link isHovered href={props.tfaAndroidAppUrl} target="_blank">
Android
</Link>
and{" "}
<Link isHovered href={props.tfaIosAppUrl} target="_blank">
iOS
</Link>{" "}
or Authenticator for{" "}
<Link isHovered href={props.tfaWinAppUrl} target="_blank">
Windows Phone
</Link>{" "}
.
</Trans>
<Trans t={t} i18nKey="SetAppDescription" ns="Confirm">
The two-factor authentication is enabled to provide additional
portal security. Configure your authenticator application to
continue work on the portal. For example you could use Google
Authenticator for
<Link isHovered href={props.tfaAndroidAppUrl} target="_blank">
Android
</Link>
and{" "}
<Link isHovered href={props.tfaIosAppUrl} target="_blank">
iOS
</Link>{" "}
or Authenticator for{" "}
<Link isHovered href={props.tfaWinAppUrl} target="_blank">
Windows Phone
</Link>{" "}
.
</Trans>
<Text className="set-app-text">
<Trans
t={t}
i18nKey="SetAppInstallDescription"
ns="Confirm"
key={secretKey}
>
To connect your apllication scan the QR code or manually enter your
secret key <strong>{{ secretKey }}</strong> then enter 6-digit code
from your application in the field below.
</Trans>
</Text>
</Box>
<FormWrapper>
<Box
displayProp="flex"
flexDirection="column"
className="app-code-wrapper"
>
<div className="qrcode-wrapper">
<img src={qrCode} height="180px" width="180px" alt="QR-code"></img>
</div>
<Box className="app-code-input">
<FieldContainer
labelVisible={false}
hasError={error ? true : false}
errorMessage={error}
<Text className="set-app-text">
<Trans
t={t}
i18nKey="SetAppInstallDescription"
ns="Confirm"
key={secretKey}
>
To connect your apllication scan the QR code or manually enter
your secret key <strong>{{ secretKey }}</strong> then enter
6-digit code from your application in the field below.
</Trans>
</Text>
</Box>
<FormWrapper>
<Box
displayProp="flex"
flexDirection="column"
className="app-code-wrapper"
>
<TextInput
id="code"
name="code"
type="text"
size="large"
scale
isAutoFocussed
tabIndex={1}
placeholder={t("EnterCodePlaceholder")}
isDisabled={isLoading}
maxLength={6}
onChange={(e) => {
setCode(e.target.value);
setError("");
}}
value={code}
hasError={error ? true : false}
onKeyDown={onKeyPress}
/>
</FieldContainer>
</Box>
<Box className="app-code-continue-btn">
<Button
scale
primary
size="medium"
tabIndex={3}
label={
isLoading ? t("Common:LoadingProcessing") : t("SetAppButton")
}
isDisabled={!code.length || isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</Box>
</Box>
</FormWrapper>
</StyledForm>
<div className="qrcode-wrapper">
<img
src={qrCode}
height="180px"
width="180px"
alt="QR-code"
></img>
</div>
<Box className="app-code-input">
<FieldContainer
labelVisible={false}
hasError={error ? true : false}
errorMessage={error}
>
<TextInput
id="code"
name="code"
type="text"
size="large"
scale
isAutoFocussed
tabIndex={1}
placeholder={t("EnterCodePlaceholder")}
isDisabled={isLoading}
maxLength={6}
onChange={(e) => {
setCode(e.target.value);
setError("");
}}
value={code}
hasError={error ? true : false}
onKeyDown={onKeyPress}
/>
</FieldContainer>
</Box>
<Box className="app-code-continue-btn">
<Button
scale
primary
size="medium"
tabIndex={3}
label={
isLoading
? t("Common:LoadingProcessing")
: t("SetAppButton")
}
isDisabled={!code.length || isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</Box>
</Box>
</FormWrapper>
</StyledForm>
</StyledContent>
</StyledPage>
);
});
@ -264,11 +272,7 @@ const TfaActivationWrapper = (props) => {
return error ? (
<ErrorContainer bodyText={error} />
) : (
<Section>
<Section.SectionBody>
<TfaActivationForm secretKey={secretKey} qrCode={qrCode} {...props} />
</Section.SectionBody>
</Section>
<TfaActivationForm secretKey={secretKey} qrCode={qrCode} {...props} />
);
};

View File

@ -6,46 +6,32 @@ import Button from "@docspace/components/button";
import TextInput from "@docspace/components/text-input";
import FieldContainer from "@docspace/components/field-container";
import Text from "@docspace/components/text";
import Section from "@docspace/common/components/Section";
import { inject, observer } from "mobx-react";
import Box from "@docspace/components/box";
import toastr from "@docspace/components/toast/toastr";
import withLoader from "../withLoader";
import {
hugeMobile,
smallTablet,
tablet,
} from "@docspace/components/utils/device";
import { hugeMobile } from "@docspace/components/utils/device";
import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
import { StyledPage, StyledContent } from "./StyledConfirm";
const StyledForm = styled(Box)`
margin: 63px auto;
width: 320px;
margin: 56px auto;
display: flex;
flex-direction: column;
flex: 1fr;
@media ${tablet} {
margin: 120px auto;
width: 480px;
}
@media ${smallTablet} {
width: 400px;
}
@media ${hugeMobile} {
margin: 32px 8px auto 8px;
padding-left: 8px;
margin: 0 auto;
width: 100%;
}
.docspace-logo {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 64px;
padding-bottom: 40px;
}
.app-code-wrapper {
@ -94,66 +80,70 @@ const TfaAuthForm = withLoader((props) => {
};
return (
<StyledForm className="app-code-container">
<DocspaceLogo className="docspace-logo" />
<FormWrapper>
<Box className="app-code-description" marginProp="0 0 32px 0">
<Text isBold fontSize="14px" className="app-code-text">
{t("EnterAppCodeTitle")}
</Text>
<Text>{t("EnterAppCodeDescription")}</Text>
</Box>
<Box
displayProp="flex"
flexDirection="column"
className="app-code-wrapper"
>
<Box className="app-code-input">
<FieldContainer
labelVisible={false}
hasError={error ? true : false}
errorMessage={error}
<StyledPage>
<StyledContent>
<StyledForm className="app-code-container">
<DocspaceLogo className="docspace-logo" />
<FormWrapper>
<Box className="app-code-description" marginProp="0 0 32px 0">
<Text isBold fontSize="14px" className="app-code-text">
{t("EnterAppCodeTitle")}
</Text>
<Text>{t("EnterAppCodeDescription")}</Text>
</Box>
<Box
displayProp="flex"
flexDirection="column"
className="app-code-wrapper"
>
<TextInput
id="code"
name="code"
type="text"
size="huge"
scale
isAutoFocussed
tabIndex={1}
placeholder={t("EnterCodePlaceholder")}
isDisabled={isLoading}
maxLength={6}
onChange={(e) => {
setCode(e.target.value);
setError("");
}}
value={code}
hasError={error ? true : false}
onKeyDown={onKeyPress}
/>
</FieldContainer>
</Box>
<Box className="app-code-continue-btn">
<Button
scale
primary
size="medium"
tabIndex={3}
label={
isLoading
? t("Common:LoadingProcessing")
: t("Common:ContinueButton")
}
isDisabled={!code.length || isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</Box>
</Box>
</FormWrapper>
</StyledForm>
<Box className="app-code-input">
<FieldContainer
labelVisible={false}
hasError={error ? true : false}
errorMessage={error}
>
<TextInput
id="code"
name="code"
type="text"
size="huge"
scale
isAutoFocussed
tabIndex={1}
placeholder={t("EnterCodePlaceholder")}
isDisabled={isLoading}
maxLength={6}
onChange={(e) => {
setCode(e.target.value);
setError("");
}}
value={code}
hasError={error ? true : false}
onKeyDown={onKeyPress}
/>
</FieldContainer>
</Box>
<Box className="app-code-continue-btn">
<Button
scale
primary
size="medium"
tabIndex={3}
label={
isLoading
? t("Common:LoadingProcessing")
: t("Common:ContinueButton")
}
isDisabled={!code.length || isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</Box>
</Box>
</FormWrapper>
</StyledForm>
</StyledContent>
</StyledPage>
);
});
@ -165,13 +155,7 @@ const TfaAuthFormWrapper = (props) => {
setIsLoading(false);
}, []);
return (
<Section>
<Section.SectionBody>
<TfaAuthForm {...props} />
</Section.SectionBody>
</Section>
);
return <TfaAuthForm {...props} />;
};
export default inject(({ auth, confirm }) => ({

View File

@ -212,10 +212,6 @@ const paddingCss = css`
@media ${desktop} {
padding-right: 3px;
}
@media ${tablet} {
margin-left: -1px;
}
`;
const StyledGridWrapper = styled.div`

View File

@ -15,7 +15,7 @@ const StyledInfoPanelBody = styled.div`
: css`
padding: 80px 3px 0 20px;
@media ${hugeMobile} {
padding: 80px 8px 0 16px;
padding: 80px 0 0 16px;
}
`}

View File

@ -1,5 +1,5 @@
import styled from "styled-components";
import { isMobileOnly } from "react-device-detect";
import { Base } from "@docspace/components/themes";
const StyledThumbnail = styled.div`
@ -7,13 +7,14 @@ const StyledThumbnail = styled.div`
justify-content: center;
align-items: center;
width: 100%;
height: auto;
height: ${isMobileOnly ? "188" : "240"}px;
img {
border: ${(props) => `solid 1px ${props.theme.infoPanel.borderColor}`};
border-radius: 6px;
width: auto;
max-width: 100%;
height: auto;
width: 100%;
height: 100%;
object-fit: none;
object-position: top;
}
`;
@ -30,7 +31,8 @@ const StyledNoThumbnail = styled.div`
border-radius: 16px;
}
.custom-logo {
outline: 1px solid ${(props) => props.theme.infoPanel.details.customLogoBorderColor};
outline: 1px solid ${(props) =>
props.theme.infoPanel.details.customLogoBorderColor};
`;
const StyledAccess = styled.div`

View File

@ -1,11 +1,11 @@
import styled from "styled-components";
import { isMobileOnly } from "react-device-detect";
import { Base } from "@docspace/components/themes";
const StyledGalleryThumbnail = styled.div`
box-sizing: border-box;
width: 100%;
height: 346px;
height: ${isMobileOnly ? "335" : "346"}px;
overflow: hidden;
border: ${(props) =>
`solid 1px ${props.theme.infoPanel.gallery.borderColor}`};

View File

@ -27,6 +27,7 @@ const FilesMediaViewer = (props) => {
setToPreviewFile,
setScrollToItem,
setCurrentId,
setAlreadyFetchingRooms,
setBufferSelection,
isFavoritesFolder,
archiveRoomsId,
@ -72,6 +73,7 @@ const FilesMediaViewer = (props) => {
fetchFiles(previewFile.folderId).finally(() => {
setIsLoading(false);
setFirstLoad(false);
setAlreadyFetchingRooms(false);
});
}
}, [previewFile]);
@ -230,6 +232,7 @@ export default inject(
isPreview,
resetUrl,
setSelection,
setAlreadyFetchingRooms,
} = filesStore;
const {
visible,
@ -286,6 +289,7 @@ export default inject(
setScrollToItem,
setCurrentId,
setBufferSelection,
setAlreadyFetchingRooms,
isFavoritesFolder,
onClickFavorite,
onClickDownloadAs,

View File

@ -81,6 +81,7 @@ const FilesRowContainer = ({
fetchMoreFiles,
hasMoreFiles,
isRooms,
isTrashFolder,
withPaging,
highlightFileId,
}) => {
@ -110,6 +111,7 @@ const FilesRowContainer = ({
itemIndex={index}
sectionWidth={sectionWidth}
isRooms={isRooms}
isTrashFolder={isTrashFolder}
isHighlight={highlightFileId === item.id}
/>
));
@ -143,7 +145,7 @@ export default inject(({ filesStore, auth, treeFoldersStore }) => {
highlightFileId,
} = filesStore;
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
const { isRoomsFolder, isArchiveFolder, isTrashFolder } = treeFoldersStore;
const { withPaging } = auth.settingsStore;
const isRooms = isRoomsFolder || isArchiveFolder;
@ -157,6 +159,7 @@ export default inject(({ filesStore, auth, treeFoldersStore }) => {
fetchMoreFiles,
hasMoreFiles,
isRooms,
isTrashFolder,
withPaging,
highlightFileId,
};

View File

@ -4,6 +4,7 @@ import { withRouter } from "react-router";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import { isMobile, isTablet, isMobileOnly } from "react-device-detect";
import moment from "moment";
import Link from "@docspace/components/link";
import Text from "@docspace/components/text";
@ -94,15 +95,18 @@ const FilesRowContent = ({
quickButtons,
theme,
isRooms,
isTrashFolder,
}) => {
const {
contentLength,
fileExst,
filesCount,
foldersCount,
autoDelete,
providerKey,
title,
isRoom,
daysRemaining,
} = item;
return (
@ -138,7 +142,11 @@ const FilesRowContent = ({
// color={sideColor}
className="row_update-text"
>
{updatedDate && updatedDate}
{isTrashFolder
? t("Files:DaysRemaining", {
daysRemaining,
})
: updatedDate && updatedDate}
</Text>
<Text
@ -166,8 +174,9 @@ const FilesRowContent = ({
);
};
export default inject(({ auth }) => {
return { theme: auth.settingsStore.theme };
export default inject(({ auth, treeFoldersStore }) => {
const { isRecycleBinFolder } = treeFoldersStore;
return { theme: auth.settingsStore.theme, isTrashFolder: isRecycleBinFolder };
})(
observer(
withRouter(

View File

@ -116,6 +116,7 @@ const Table = ({
hasMoreFiles,
filterTotal,
isRooms,
isTrashFolder,
withPaging,
columnStorageName,
columnInfoPanelStorageName,
@ -189,6 +190,7 @@ const Table = ({
theme={theme}
tagCount={tagCount}
isRooms={isRooms}
isTrashFolder={isTrashFolder}
hideColumns={hideColumns}
isHighlight={highlightFileId === item.id}
/>
@ -233,7 +235,7 @@ const Table = ({
export default inject(({ filesStore, treeFoldersStore, auth, tableStore }) => {
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
const { isRoomsFolder, isArchiveFolder, isTrashFolder } = treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const { columnStorageName, columnInfoPanelStorageName } = tableStore;
@ -266,6 +268,7 @@ export default inject(({ filesStore, treeFoldersStore, auth, tableStore }) => {
hasMoreFiles,
filterTotal: isRooms ? roomsFilterTotal : filterTotal,
isRooms,
isTrashFolder,
withPaging,
columnStorageName,
columnInfoPanelStorageName,

View File

@ -14,7 +14,7 @@ class FilesTableHeader extends React.Component {
}
getTableColumns = (fromUpdate = false) => {
const { t, isRooms, getColumns } = this.props;
const { t, isRooms, isTrashFolder, getColumns } = this.props;
const defaultColumns = [];
@ -68,7 +68,81 @@ class FilesTableHeader extends React.Component {
onClick: this.onRoomsFilter,
},
];
defaultColumns.push(...columns);
} else if (isTrashFolder) {
const columns = [
{
key: "Name",
title: t("Common:Name"),
resizable: true,
enable: this.props.nameColumnIsEnabled,
default: true,
sortBy: "AZ",
minWidth: 210,
onClick: this.onFilter,
},
{
key: "Room",
title: t("Common:Room"),
enable: this.props.roomColumnIsEnabled,
resizable: true,
sortBy: "Room",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "AuthorTrash",
title: t("ByAuthor"),
enable: this.props.authorTrashColumnIsEnabled,
resizable: true,
sortBy: "Author",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "CreatedTrash",
title: t("ByCreation"),
enable: this.props.createdTrashColumnIsEnabled,
resizable: true,
sortBy: "DateAndTimeCreation",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "Erasure",
title: t("ByErasure"),
enable: this.props.erasureColumnIsEnabled,
resizable: true,
sortBy: "DateAndTime",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "SizeTrash",
title: t("Common:Size"),
enable: this.props.sizeTrashColumnIsEnabled,
resizable: true,
sortBy: "Size",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "TypeTrash",
title: t("Common:Type"),
enable: this.props.typeTrashColumnIsEnabled,
resizable: true,
sortBy: "Type",
onClick: this.onFilter,
onChange: this.onColumnChange,
},
{
key: "QuickButtons",
title: "",
enable: this.props.quickButtonsColumnIsEnabled,
defaultSize: 75,
resizable: false,
},
];
defaultColumns.push(...columns);
} else {
const columns = [
@ -135,7 +209,6 @@ class FilesTableHeader extends React.Component {
resizable: false,
},
];
defaultColumns.push(...columns);
}
@ -194,7 +267,10 @@ class FilesTableHeader extends React.Component {
};
componentDidUpdate(prevProps) {
if (this.props.isRooms !== prevProps.isRooms) {
if (
this.props.isRooms !== prevProps.isRooms ||
this.props.isTrashFolder !== prevProps.isTrashFolder
) {
return this.getTableColumns(true);
}
@ -352,7 +428,12 @@ export default inject(
roomsFilter,
fetchRooms,
} = filesStore;
const { isRecentFolder, isRoomsFolder, isArchiveFolder } = treeFoldersStore;
const {
isRecentFolder,
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const withContent = canShare;
const sortingVisible = !isRecentFolder;
@ -369,10 +450,16 @@ export default inject(
nameColumnIsEnabled,
authorColumnIsEnabled,
authorTrashColumnIsEnabled,
createdColumnIsEnabled,
createdTrashColumnIsEnabled,
modifiedColumnIsEnabled,
roomColumnIsEnabled,
erasureColumnIsEnabled,
sizeColumnIsEnabled,
sizeTrashColumnIsEnabled,
typeColumnIsEnabled,
typeTrashColumnIsEnabled,
quickButtonsColumnIsEnabled,
roomColumnNameIsEnabled,
@ -414,10 +501,16 @@ export default inject(
nameColumnIsEnabled,
authorColumnIsEnabled,
authorTrashColumnIsEnabled,
createdColumnIsEnabled,
createdTrashColumnIsEnabled,
modifiedColumnIsEnabled,
roomColumnIsEnabled,
erasureColumnIsEnabled,
sizeColumnIsEnabled,
sizeTrashColumnIsEnabled,
typeColumnIsEnabled,
typeTrashColumnIsEnabled,
quickButtonsColumnIsEnabled,
roomColumnNameIsEnabled,
@ -429,6 +522,7 @@ export default inject(
getColumns,
setColumnEnable,
isRooms,
isTrashFolder,
};
}
)(

View File

@ -8,6 +8,7 @@ import ItemIcon from "../../../../../components/ItemIcon";
import { withTranslation } from "react-i18next";
import { classNames } from "@docspace/components/utils/classNames";
import RoomsRowDataComponent from "./sub-components/RoomsRowData";
import TrashRowDataComponent from "./sub-components/TrashRowData";
import RowDataComponent from "./sub-components/RowData";
import { StyledTableRow, StyledDragAndDrop } from "./StyledTable";
@ -37,6 +38,7 @@ const FilesTableRow = (props) => {
showHotkeyBorder,
id,
isRooms,
isTrashFolder,
isHighlight,
} = props;
const { acceptBackground, background } = theme.dragAndDrop;
@ -146,6 +148,12 @@ const FilesTableRow = (props) => {
dragStyles={dragStyles}
{...props}
/>
) : isTrashFolder ? (
<TrashRowDataComponent
element={element}
dragStyles={dragStyles}
{...props}
/>
) : (
<RowDataComponent
element={element}

View File

@ -0,0 +1,22 @@
import React from "react";
import { StyledText } from "./CellStyles";
const ErasureCell = ({ t, sideColor, item }) => {
const { daysRemaining } = item;
const title = t("Files:DaysRemaining", { daysRemaining });
return (
<StyledText
title={title}
fontSize="12px"
fontWeight={600}
color={sideColor}
className="row_update-text"
truncate
>
{title}
</StyledText>
);
};
export default ErasureCell;

View File

@ -0,0 +1,66 @@
import { Loader, Tooltip } from "@docspace/components";
import Text from "@docspace/components/text";
import React, { useState } from "react";
import { StyledText } from "./CellStyles";
import { getFolderPath } from "@docspace/common/api/files";
import { CategoryType } from "@docspace/client/src/helpers/constants";
const RoomCell = ({ sideColor, item }) => {
const { originRoomTitle, originId, originTitle } = item;
const [path, setPath] = useState([]);
const [isTooltipLoading, setIsTooltipLoading] = useState(false);
const getPath = async () => {
if (path.length) return;
setIsTooltipLoading(true);
try {
const folderPath = await getFolderPath(originId);
if (folderPath[0].id === CategoryType.Shared) folderPath.shift();
setPath(folderPath);
} catch (e) {
console.error(e);
setPath([{ id: 0, title: originRoomTitle || originTitle }]);
}
setIsTooltipLoading(false);
};
return [
<StyledText
key="cell"
fontSize="12px"
fontWeight={600}
color={sideColor}
className="row_update-text"
truncate
data-for={"" + item.id}
data-tip={""}
data-place={"bottom"}
>
{originRoomTitle || originTitle}
</StyledText>,
<Tooltip
id={"" + item.id}
key={"tooltip"}
effect={"float"}
afterShow={getPath}
getContent={() => (
<span>
{isTooltipLoading ? (
<Loader color="#333333" size="12px" type="track" />
) : (
path.map((pathPart, i) => (
<Text key={pathPart.id} isBold={i === 0} isInline fontSize="12px">
{i === 0 ? pathPart.title : `/${pathPart.title}`}
</Text>
))
)}
</span>
)}
/>,
];
};
export default RoomCell;

View File

@ -0,0 +1,211 @@
import React from "react";
import { inject, observer } from "mobx-react";
import TableCell from "@docspace/components/table-container/TableCell";
import FileNameCell from "./FileNameCell";
import TypeCell from "./TypeCell";
import AuthorCell from "./AuthorCell";
import DateCell from "./DateCell";
import SizeCell from "./SizeCell";
import { classNames } from "@docspace/components/utils/classNames";
import {
StyledBadgesContainer,
StyledQuickButtonsContainer,
} from "../StyledTable";
import ErasureCell from "./ErasureCell";
import RoomCell from "./RoomCell";
const TrashRowDataComponent = (props) => {
const {
authorTrashColumnIsEnabled,
createdTrashColumnIsEnabled,
roomColumnIsEnabled,
erasureColumnIsEnabled,
sizeTrashColumnIsEnabled,
typeTrashColumnIsEnabled,
quickButtonsColumnIsEnabled,
dragStyles,
selectionProp,
value,
theme,
onContentFileSelect,
checkedProps,
element,
inProgress,
showHotkeyBorder,
badgesComponent,
quickButtonsComponent,
} = props;
return (
<>
<TableCell
{...dragStyles}
className={classNames(
selectionProp?.className,
"table-container_file-name-cell"
)}
value={value}
>
<FileNameCell
theme={theme}
onContentSelect={onContentFileSelect}
checked={checkedProps}
element={element}
inProgress={inProgress}
{...props}
/>
<StyledBadgesContainer showHotkeyBorder={showHotkeyBorder}>
{badgesComponent}
</StyledBadgesContainer>
</TableCell>
{roomColumnIsEnabled ? (
<TableCell
style={
!roomColumnIsEnabled ? { background: "none" } : dragStyles.style
}
{...selectionProp}
>
<RoomCell
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{authorTrashColumnIsEnabled ? (
<TableCell
style={
!authorTrashColumnIsEnabled
? { background: "none" }
: dragStyles.style
}
{...selectionProp}
>
<AuthorCell
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{createdTrashColumnIsEnabled ? (
<TableCell
style={
!createdTrashColumnIsEnabled
? { background: "none !important" }
: dragStyles.style
}
{...selectionProp}
>
<DateCell
create
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{erasureColumnIsEnabled ? (
<TableCell
style={
!erasureColumnIsEnabled ? { background: "none" } : dragStyles.style
}
{...selectionProp}
>
<ErasureCell
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{sizeTrashColumnIsEnabled ? (
<TableCell
style={
!sizeTrashColumnIsEnabled
? { background: "none" }
: dragStyles.style
}
{...selectionProp}
>
<SizeCell
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{typeTrashColumnIsEnabled ? (
<TableCell
style={
!typeTrashColumnIsEnabled
? { background: "none !important" }
: dragStyles.style
}
{...selectionProp}
>
<TypeCell
sideColor={theme.filesSection.tableView.row.sideColor}
{...props}
/>
</TableCell>
) : (
<div />
)}
{quickButtonsColumnIsEnabled ? (
<TableCell
style={
!quickButtonsColumnIsEnabled
? { background: "none" }
: dragStyles.style
}
{...selectionProp}
className={classNames(
selectionProp?.className,
"table-container_quick-buttons-wrapper"
)}
>
<StyledQuickButtonsContainer>
{quickButtonsComponent}
</StyledQuickButtonsContainer>
</TableCell>
) : (
<div />
)}
</>
);
};
export default inject(({ tableStore }) => {
const {
authorTrashColumnIsEnabled,
createdTrashColumnIsEnabled,
roomColumnIsEnabled,
erasureColumnIsEnabled,
sizeTrashColumnIsEnabled,
typeTrashColumnIsEnabled,
quickButtonsColumnIsEnabled,
} = tableStore;
return {
authorTrashColumnIsEnabled,
createdTrashColumnIsEnabled,
roomColumnIsEnabled,
erasureColumnIsEnabled,
sizeTrashColumnIsEnabled,
typeTrashColumnIsEnabled,
quickButtonsColumnIsEnabled,
};
})(observer(TrashRowDataComponent));

View File

@ -267,7 +267,7 @@ const StyledFileTileTop = styled.div`
position: absolute;
height: 100%;
width: 100%;
object-fit: ${(props) => (props.isMedia ? "cover" : "none")};
object-fit: none;
object-position: top;
z-index: 0;
border-radius: 6px 6px 0 0;

View File

@ -147,6 +147,10 @@ const TABLE_ROOMS_COLUMNS = `roomsTableColumns_ver-${TableVersions.Rooms}`;
const COLUMNS_ROOMS_SIZE_INFO_PANEL = `roomsColumnsSizeInfoPanel_ver-${TableVersions.Rooms}`;
const TABLE_TRASH_COLUMNS = `trashTableColumns_ver-${TableVersions.Trash}`;
const COLUMNS_TRASH_SIZE_INFO_PANEL = `trashColumnsSizeInfoPanel_ver-${TableVersions.Trash}`;
const SectionFilterContent = ({
t,
filter,
@ -165,6 +169,7 @@ const SectionFilterContent = ({
fetchTags,
infoPanelVisible,
isRooms,
isTrash,
userId,
isPersonalRoom,
setCurrentRoomsFilter,
@ -1020,13 +1025,13 @@ const SectionFilterContent = ({
return filterOptions;
}, [
isFavoritesFolder,
isRecentFolder,
isRooms,
t,
personal,
isPersonalRoom,
providers,
isPersonalRoom,
isRooms,
isFavoritesFolder,
isRecentFolder,
]);
const getViewSettingsData = React.useCallback(() => {
@ -1064,22 +1069,10 @@ const SectionFilterContent = ({
label: t("ByLastModified"),
default: true,
};
const type = {
id: "sort-by_type",
key: "Type",
label: t("Common:Type"),
default: true,
};
const size = {
id: "sort-by_size",
key: "Size",
label: t("Common:Size"),
default: true,
};
const creationDate = {
id: "sort-by_created",
key: "DateAndTimeCreation",
label: t("ByCreation"),
const room = {
id: "sort-by_room",
key: "Room",
label: t("Common:Room"),
default: true,
};
const authorOption = {
@ -1088,18 +1081,42 @@ const SectionFilterContent = ({
label: t("ByAuthor"),
default: true,
};
const creationDate = {
id: "sort-by_created",
key: "DateAndTimeCreation",
label: t("ByCreation"),
default: true,
};
const owner = {
id: "sort-by_owner",
key: "Author",
label: t("Common:Owner"),
default: true,
};
const erasure = {
id: "sort-by_erasure",
key: "Erasure",
label: t("ByErasure"),
default: true,
};
const tags = {
id: "sort-by_tags",
key: "Tags",
label: t("Common:Tags"),
default: true,
};
const size = {
id: "sort-by_size",
key: "Size",
label: t("Common:Size"),
default: true,
};
const type = {
id: "sort-by_type",
key: "Type",
label: t("Common:Type"),
default: true,
};
const roomType = {
id: "sort-by_room-type",
key: "roomType",
@ -1158,6 +1175,69 @@ const SectionFilterContent = ({
!hide && commonOptions.push(modifiedDate);
}
} else if (isTrash) {
const availableSort = localStorage
?.getItem(`${TABLE_TRASH_COLUMNS}=${userId}`)
?.split(",");
const infoPanelColumnsSize = localStorage
?.getItem(`${COLUMNS_TRASH_SIZE_INFO_PANEL}=${userId}`)
?.split(" ");
if (availableSort?.includes("Author")) {
const idx = availableSort.findIndex((x) => x === "Author");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(authorOption);
}
if (availableSort?.includes("Created")) {
const idx = availableSort.findIndex((x) => x === "Created");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(creationDate);
}
if (availableSort?.includes("Room")) {
const idx = availableSort.findIndex((x) => x === "Room");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(room);
}
if (availableSort?.includes("Erasure")) {
const idx = availableSort.findIndex((x) => x === "Erasure");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(erasure);
}
if (availableSort?.includes("Size")) {
const idx = availableSort.findIndex((x) => x === "Size");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(size);
}
if (availableSort?.includes("Type")) {
const idx = availableSort.findIndex((x) => x === "Type");
const hide =
infoPanelVisible &&
infoPanelColumnsSize &&
infoPanelColumnsSize[idx] === "0px";
!hide && commonOptions.push(type);
}
} else {
const availableSort = localStorage
?.getItem(`${TABLE_COLUMNS}=${userId}`)
@ -1219,6 +1299,12 @@ const SectionFilterContent = ({
commonOptions.push(tags);
commonOptions.push(owner);
commonOptions.push(modifiedDate);
} else if (isTrash) {
commonOptions.push(authorOption);
commonOptions.push(creationDate);
commonOptions.push(erasure);
commonOptions.push(size);
commonOptions.push(type);
} else {
commonOptions.push(authorOption);
commonOptions.push(creationDate);
@ -1415,6 +1501,7 @@ export default inject(
isRoomsFolder,
isArchiveFolder,
isPersonalRoom,
isTrashFolder: isTrash,
} = treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
@ -1441,6 +1528,7 @@ export default inject(
isFavoritesFolder,
isRecentFolder,
isRooms,
isTrash,
setIsLoading,
fetchFiles,

View File

@ -36,6 +36,7 @@ import { Consumer } from "@docspace/components/utils/context";
import { inject, observer } from "mobx-react";
import TableGroupMenu from "@docspace/components/table-container/TableGroupMenu";
import Navigation from "@docspace/common/components/Navigation";
import TrashWarning from "@docspace/common/components/Navigation/sub-components/trash-warning";
import { Events } from "@docspace/common/constants";
import config from "PACKAGE_FILE";
import { combineUrl } from "@docspace/common/utils";
@ -685,11 +686,15 @@ class SectionHeaderContent extends React.Component {
const menuItems = this.getMenuItems();
const isLoading = !title || !tReady;
const headerMenu = getHeaderMenu(t);
const isEmptyTrash = !![
...this.props.activeFiles,
...this.props.activeFolders,
].length;
return (
<Consumer>
return [
<Consumer key="header">
{(context) => (
<StyledContainer>
<StyledContainer isRecycleBinFolder={isRecycleBinFolder}>
{isHeaderVisible && headerMenu.length ? (
<TableGroupMenu
checkboxOptions={menuItems}
@ -727,6 +732,7 @@ class SectionHeaderContent extends React.Component {
getContextOptionsFolder={this.getContextOptionsFolder}
onClose={this.onClose}
onClickFolder={this.onClickFolder}
isTrashFolder={isRecycleBinFolder}
isRecycleBinFolder={isRecycleBinFolder || isArchiveFolder}
isEmptyFilesList={
isArchiveFolder ? isEmptyArchive : isEmptyFilesList
@ -737,6 +743,7 @@ class SectionHeaderContent extends React.Component {
isInfoPanelVisible={isInfoPanelVisible}
titles={{
trash: t("EmptyRecycleBin"),
trashWarning: t("TrashErasureWarning"),
}}
withMenu={!isRoomsFolder}
onPlusClick={this.onCreateRoom}
@ -748,8 +755,15 @@ class SectionHeaderContent extends React.Component {
)}
</StyledContainer>
)}
</Consumer>
);
</Consumer>,
isRecycleBinFolder && !isEmptyPage && (
<TrashWarning
key="trash-warning"
title={t("Files:TrashErasureWarning")}
isTabletView
/>
),
];
}
}

View File

@ -528,6 +528,7 @@ class PureHome extends React.Component {
isHeaderVisible={isHeaderVisible}
onOpenUploadPanel={this.showUploadPanel}
firstLoad={firstLoad}
isEmptyPage={isEmptyPage}
>
{!isErrorRoomNotAvailable && (
<Section.SectionHeader>

View File

@ -55,6 +55,7 @@ let timeout = null,
CancelToken,
source;
const backUrl = window.location.origin;
const PriceCalculation = ({
t,
user,
@ -71,13 +72,19 @@ const PriceCalculation = ({
currencySymbol,
isAlreadyPaid,
isFreeAfterPaidPeriod,
setStartPaymentLink,
managersCount,
}) => {
useEffect(() => {
useEffect(async () => {
initializeInfo();
!isAlreadyPaid && setStartPaymentLink(t);
if (isAlreadyPaid) return;
try {
const link = await getPaymentLink(managersCount, source?.token, backUrl);
setPaymentLink(link);
} catch (e) {
toastr.error(t("ErrorNotification"));
}
return () => {
timeout && clearTimeout(timeout);
@ -103,7 +110,7 @@ const PriceCalculation = ({
CancelToken = axios.CancelToken;
source = CancelToken.source();
await getPaymentLink(value, source.token)
await getPaymentLink(value, source.token, backUrl)
.then((link) => {
setPaymentLink(link);
setIsLoading(false);
@ -207,7 +214,6 @@ export default inject(({ auth, payments }) => {
setManagersCount,
maxAvailableManagersCount,
initializeInfo,
setStartPaymentLink,
managersCount,
} = payments;
const { theme } = auth.settingsStore;
@ -224,7 +230,7 @@ export default inject(({ auth, payments }) => {
return {
managersCount,
setStartPaymentLink,
isFreeTariff,
setManagersCount,
tariffsInfo,

View File

@ -30,6 +30,8 @@ const PersonalSettings = ({
showTitle,
createWithoutDialog,
setCreateWithoutDialog,
showAdminSettings,
}) => {
const [isLoadingFavorites, setIsLoadingFavorites] = React.useState(false);
const [isLoadingRecent, setIsLoadingRecent] = React.useState(false);
@ -75,7 +77,10 @@ const PersonalSettings = ({
};
return (
<StyledSettings showTitle={showTitle}>
<StyledSettings
showTitle={showTitle}
hideAdminSettings={!showAdminSettings}
>
<Box className="settings-section">
{showTitle && (
<Heading className="heading" level={2} size="xsmall">

View File

@ -3,10 +3,22 @@ import { tablet } from "@docspace/components/utils/device";
import { isMobile } from "react-device-detect";
const StyledSettings = styled.div`
margin-top: ${(props) => (props.showTitle ? 24 : 34)}px;
margin-top: ${(props) =>
props.hideAdminSettings ? 22 : props.showTitle ? 24 : 34}px;
${(props) =>
props.hideAdminSettings &&
css`
padding-top: 2px;
`}
@media ${tablet} {
margin-top: 8px;
margin-top: ${(props) => (props.hideAdminSettings ? 0 : 8)}px;
${(props) =>
props.hideAdminSettings &&
css`
padding-top: 8px;
`}
}
${isMobile &&

View File

@ -70,7 +70,11 @@ const SectionBodyContent = ({ isErrorSettings, history, user }) => {
) : (
<StyledContainer>
{!showAdminSettings ? (
<PersonalSettings t={t} showTitle={true} />
<PersonalSettings
t={t}
showTitle={true}
showAdminSettings={showAdminSettings}
/>
) : (
<Submenu
data={data}

View File

@ -16,7 +16,10 @@ import { isMobile, isMobileOnly } from "react-device-detect";
import toastr from "@docspace/components/toast/toastr";
import config from "PACKAGE_FILE";
import { thumbnailStatuses } from "@docspace/client/src/helpers/filesConstants";
import { openDocEditor as openEditor } from "@docspace/client/src/helpers/filesUtils";
import {
getDaysRemaining,
openDocEditor as openEditor,
} from "@docspace/client/src/helpers/filesUtils";
import { getCategoryUrl } from "SRC_DIR/helpers/utils";
import {
getCategoryType,
@ -2394,6 +2397,8 @@ class FilesStore {
const newItem = items.map((item) => {
const {
access,
autoDelete,
originTitle,
comment,
contentLength,
created,
@ -2408,6 +2413,10 @@ class FilesStore {
id,
logo,
locked,
originId,
originFolderId,
originRoomId,
originRoomTitle,
parentId,
pureContentLength,
rootFolderType,
@ -2505,6 +2514,8 @@ class FilesStore {
return {
access,
daysRemaining: autoDelete && getDaysRemaining(autoDelete),
originTitle,
//checked,
comment,
contentLength,
@ -2545,6 +2556,10 @@ class FilesStore {
canEdit,
thumbnailUrl,
thumbnailStatus,
originId,
originFolderId,
originRoomId,
originRoomTitle,
previewUrl,
folderUrl,
href,

View File

@ -140,15 +140,6 @@ class PaymentStore {
}
};
setStartPaymentLink = async (t) => {
try {
const link = await api.portal.getPaymentLink(this.managersCount);
this.setPaymentLink(link);
} catch (e) {
toastr.error(t("ErrorNotification"));
}
};
setTotalPrice = (value) => {
const price = this.getTotalCostByFormula(value);
if (price !== this.totalPrice) this.totalPrice = price;

View File

@ -3,12 +3,15 @@ import { TableVersions } from "SRC_DIR/helpers/constants";
const TABLE_COLUMNS = `filesTableColumns_ver-${TableVersions.Files}`;
const TABLE_ROOMS_COLUMNS = `roomsTableColumns_ver-${TableVersions.Rooms}`;
const TABLE_TRASH_COLUMNS = `trashTableColumns_ver-${TableVersions.Trash}`;
const COLUMNS_SIZE = `filesColumnsSize_ver-${TableVersions.Files}`;
const COLUMNS_ROOMS_SIZE = `roomsColumnsSize_ver-${TableVersions.Rooms}`;
const COLUMNS_TRASH_SIZE = `trashColumnsSize_ver-${TableVersions.Trash}`;
const COLUMNS_SIZE_INFO_PANEL = `filesColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
const COLUMNS_ROOMS_SIZE_INFO_PANEL = `roomsColumnsSizeInfoPanel_ver-${TableVersions.Rooms}`;
const COLUMNS_TRASH_SIZE_INFO_PANEL = `trashColumnsSizeInfoPanel_ver-${TableVersions.Trash}`;
class TableStore {
authStore;
@ -22,12 +25,19 @@ class TableStore {
nameColumnIsEnabled = true; // always true
authorColumnIsEnabled = false;
roomColumnIsEnabled = true;
erasureColumnIsEnabled = true;
createdColumnIsEnabled = true;
modifiedColumnIsEnabled = true;
sizeColumnIsEnabled = true;
typeColumnIsEnabled = true;
quickButtonsColumnIsEnabled = true;
authorTrashColumnIsEnabled = true;
createdTrashColumnIsEnabled = false;
sizeTrashColumnIsEnabled = false;
typeTrashColumnIsEnabled = false;
constructor(authStore, treeFoldersStore) {
makeAutoObservable(this);
@ -54,28 +64,51 @@ class TableStore {
setAuthorColumn = (enable) => {
this.authorColumnIsEnabled = enable;
};
setCreatedColumn = (enable) => {
this.createdColumnIsEnabled = enable;
};
setModifiedColumn = (enable) => {
this.modifiedColumnIsEnabled = enable;
};
setRoomColumn = (enable) => {
this.roomColumnIsEnabled = enable;
};
setErasureColumn = (enable) => {
this.erasureColumnIsEnabled = enable;
};
setSizeColumn = (enable) => {
this.sizeColumnIsEnabled = enable;
};
setTypeColumn = (enable) => {
this.typeColumnIsEnabled = enable;
};
setQuickButtonsColumn = (enable) => {
this.quickButtonsColumnIsEnabled = enable;
};
setAuthorTrashColumn = (enable) => (this.authorTrashColumnIsEnabled = enable);
setCreatedTrashColumn = (enable) =>
(this.createdTrashColumnIsEnabled = enable);
setSizeTrashColumn = (enable) => (this.sizeTrashColumnIsEnabled = enable);
setTypeTrashColumn = (enable) => (this.typeTrashColumnIsEnabled = enable);
setColumnsEnable = () => {
const storageColumns = localStorage.getItem(this.tableStorageName);
const splitColumns = storageColumns && storageColumns.split(",");
if (splitColumns) {
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const {
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
if (isRooms) {
@ -83,51 +116,96 @@ class TableStore {
this.setRoomColumnTags(splitColumns.includes("Tags"));
this.setRoomColumnOwner(splitColumns.includes("Owner"));
this.setRoomColumnActivity(splitColumns.includes("Activity"));
} else {
this.setAuthorColumn(splitColumns.includes("Author"));
this.setCreatedColumn(splitColumns.includes("Created"));
this.setModifiedColumn(splitColumns.includes("Modified"));
this.setSizeColumn(splitColumns.includes("Size"));
this.setTypeColumn(splitColumns.includes("Type"));
this.setQuickButtonsColumn(splitColumns.includes("QuickButtons"));
return;
}
if (isTrashFolder) {
this.setRoomColumn(splitColumns.includes("Room"));
this.setAuthorTrashColumn(splitColumns.includes("AuthorTrash"));
this.setCreatedTrashColumn(splitColumns.includes("CreatedTrash"));
this.setErasureColumn(splitColumns.includes("Erasure"));
this.setSizeTrashColumn(splitColumns.includes("SizeTrash"));
this.setTypeTrashColumn(splitColumns.includes("TypeTrash"));
this.setQuickButtonsColumn(splitColumns.includes("QuickButtons"));
return;
}
this.setModifiedColumn(splitColumns.includes("Modified"));
this.setAuthorColumn(splitColumns.includes("Author"));
this.setCreatedColumn(splitColumns.includes("Created"));
this.setSizeColumn(splitColumns.includes("Size"));
this.setTypeColumn(splitColumns.includes("Type"));
this.setQuickButtonsColumn(splitColumns.includes("QuickButtons"));
}
};
setColumnEnable = (key) => {
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const {
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
switch (key) {
case "Room":
this.setRoomColumn(!this.roomColumnIsEnabled);
return;
case "Author":
this.setAuthorColumn(!this.authorColumnIsEnabled);
return;
case "AuthorTrash":
this.setAuthorTrashColumn(!this.authorTrashColumnIsEnabled);
return;
case "Created":
this.setCreatedColumn(!this.createdColumnIsEnabled);
return;
case "CreatedTrash":
this.setCreatedTrashColumn(!this.createdTrashColumnIsEnabled);
return;
case "Modified":
this.setModifiedColumn(!this.modifiedColumnIsEnabled);
return;
case "Erasure":
this.setErasureColumn(!this.erasureColumnIsEnabled);
return;
case "Size":
this.setSizeColumn(!this.sizeColumnIsEnabled);
return;
case "SizeTrash":
this.setSizeTrashColumn(!this.sizeTrashColumnIsEnabled);
return;
case "Type":
isRooms
? this.setRoomColumnType(!this.roomColumnTypeIsEnabled)
: this.setTypeColumn(!this.typeColumnIsEnabled);
return;
case "TypeTrash":
this.setTypeTrashColumn(!this.typeTrashColumnIsEnabled);
return;
case "QuickButtons":
this.setQuickButtonsColumn(!this.quickButtonsColumnIsEnabled);
return;
case "Owner":
this.setRoomColumnOwner(!this.roomColumnOwnerIsEnabled);
return;
case "Tags":
this.setRoomColumnTags(!this.roomColumnTagsIsEnabled);
return;
case "Activity":
this.setRoomColumnActivity(!this.roomColumnActivityIsEnabled);
return;
default:
return;
}
@ -155,32 +233,50 @@ class TableStore {
};
get tableStorageName() {
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const {
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const userId = this.authStore.userStore.user.id;
return isRooms
? `${TABLE_ROOMS_COLUMNS}=${userId}`
: isTrashFolder
? `${TABLE_TRASH_COLUMNS}=${userId}`
: `${TABLE_COLUMNS}=${userId}`;
}
get columnStorageName() {
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const {
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const userId = this.authStore.userStore.user.id;
return isRooms
? `${COLUMNS_ROOMS_SIZE}=${userId}`
: isTrashFolder
? `${COLUMNS_TRASH_SIZE}=${userId}`
: `${COLUMNS_SIZE}=${userId}`;
}
get columnInfoPanelStorageName() {
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const {
isRoomsFolder,
isArchiveFolder,
isTrashFolder,
} = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const userId = this.authStore.userStore.user.id;
return isRooms
? `${COLUMNS_ROOMS_SIZE_INFO_PANEL}=${userId}`
: isTrashFolder
? `${COLUMNS_TRASH_SIZE_INFO_PANEL}=${userId}`
: `${COLUMNS_SIZE_INFO_PANEL}=${userId}`;
}
@ -192,6 +288,11 @@ class TableStore {
const userId = this.authStore.userStore.user.id;
return `${COLUMNS_ROOMS_SIZE}=${userId}`;
}
get trashColumnStorageName() {
const userId = this.authStore.userStore.user.id;
return `${COLUMNS_TRASH_SIZE}=${userId}`;
}
get filesColumnInfoPanelStorageName() {
const userId = this.authStore.userStore.user.id;
return `${COLUMNS_SIZE_INFO_PANEL}=${userId}`;
@ -200,6 +301,10 @@ class TableStore {
const userId = this.authStore.userStore.user.id;
return `${COLUMNS_ROOMS_SIZE_INFO_PANEL}=${userId}`;
}
get trashColumnInfoPanelStorageName() {
const userId = this.authStore.userStore.user.id;
return `${COLUMNS_TRASH_SIZE_INFO_PANEL}=${userId}`;
}
}
export default TableStore;

View File

@ -239,12 +239,13 @@ export function getPaymentAccount() {
return request({ method: "get", url: "/portal/payment/account" });
}
export function getPaymentLink(adminCount, cancelToken) {
export function getPaymentLink(adminCount, cancelToken, backUrl) {
return request({
method: "put",
url: `/portal/payment/url`,
data: {
quantity: { admin: adminCount },
backUrl,
},
cancelToken,
});

View File

@ -1,10 +1,10 @@
import { request } from "../client";
import axios from "axios";
export function getSettings() {
export function getSettings(withPassword = false) {
return request({
method: "get",
url: "/settings.json",
url: `/settings.json?withPassword=${withPassword}`,
});
}

View File

@ -6,8 +6,9 @@ const LoginContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin: 56px auto 0 auto;
margin: 56px auto;
max-width: 960px;
z-index: 0;
.remember-wrapper {
max-width: 142px;
@ -236,7 +237,7 @@ const LoginContainer = styled.div`
justify-content: center;
width: 100%;
height: 46px;
padding-bottom: 64px;
padding-bottom: 40px;
svg {
path:last-child {

View File

@ -23,7 +23,7 @@ export interface MediaViewerProps {
playlistPos: number;
getIcon: (size: number, ext: string, ...arg: any) => any;
getIcon: (size: number, ext: string, ...arg: any) => string;
onClose: VoidFunction;
onError?: VoidFunction;

View File

@ -0,0 +1,112 @@
import React from "react";
import MediaZoomInIcon from "PUBLIC_DIR/images/media.zoomin.react.svg";
import MediaZoomOutIcon from "PUBLIC_DIR/images/media.zoomout.react.svg";
import MediaRotateLeftIcon from "PUBLIC_DIR/images/media.rotateleft.react.svg";
import MediaRotateRightIcon from "PUBLIC_DIR/images/media.rotateright.react.svg";
import MediaDeleteIcon from "PUBLIC_DIR/images/media.delete.react.svg";
import MediaDownloadIcon from "PUBLIC_DIR/images/download.react.svg";
import MediaFavoriteIcon from "PUBLIC_DIR/images/favorite.react.svg";
import ViewerSeparator from "PUBLIC_DIR/images/viewer.separator.react.svg";
export const getCustomToolbar = (
onDeleteClick: VoidFunction,
onDownloadClick: VoidFunction
) => {
return [
{
key: "zoomOut",
percent: true,
actionType: 2,
render: (
<div className="iconContainer zoomOut">
<MediaZoomOutIcon size="scale" />
</div>
),
},
{
key: "percent",
actionType: 999,
},
{
key: "zoomIn",
actionType: 1,
render: (
<div className="iconContainer zoomIn">
<MediaZoomInIcon size="scale" />
</div>
),
},
{
key: "rotateLeft",
actionType: 5,
render: (
<div className="iconContainer rotateLeft">
<MediaRotateLeftIcon size="scale" />
</div>
),
},
{
key: "rotateRight",
actionType: 6,
render: (
<div className="iconContainer rotateRight">
<MediaRotateRightIcon size="scale" />
</div>
),
},
{
key: "separator download-separator",
actionType: -1,
noHover: true,
render: (
<div className="separator" style={{ height: "16px" }}>
<ViewerSeparator size="scale" />
</div>
),
},
{
key: "download",
actionType: 102,
render: (
<div className="iconContainer download" style={{ height: "16px" }}>
<MediaDownloadIcon size="scale" />
</div>
),
onClick: onDownloadClick,
},
{
key: "context-separator",
actionType: -1,
noHover: true,
render: (
<div className="separator" style={{ height: "16px" }}>
<ViewerSeparator size="scale" />
</div>
),
},
{
key: "context-menu",
actionType: -1,
},
{
key: "delete",
actionType: 103,
render: (
<div className="iconContainer viewer-delete">
<MediaDeleteIcon size="scale" />
</div>
),
onClick: onDeleteClick,
},
{
key: "favorite",
actionType: 104,
render: (
<div className="iconContainer viewer-favorite">
<MediaFavoriteIcon size="scale" />
</div>
),
},
];
};

View File

@ -1,4 +1,9 @@
import { NullOrUndefined, PlaylistType } from "../types";
import {
ContextMenuModel,
NullOrUndefined,
PlaylistType,
SeparatorType,
} from "../types";
export const mediaTypes = Object.freeze({
audio: 1,
@ -57,3 +62,7 @@ export const findNearestIndex = (
}
return found;
};
export const isSeparator = (arg: ContextMenuModel): arg is SeparatorType => {
return arg?.isSeparator;
};

View File

@ -1,7 +1,7 @@
import { isMobileOnly } from "react-device-detect";
import React, { useState, useCallback, useMemo, useEffect } from "react";
import ImageViewer from "./sub-components/image-viewer";
import ViewerWrapper from "./sub-components/ViewerWrapper";
import { MediaViewerProps } from "./MediaViewer.props";
import { FileStatus } from "@docspace/common/constants";
@ -267,17 +267,24 @@ function MediaViewer({
return 0 <= posExt ? fileTitle.substring(posExt).trim().toLowerCase() : "";
}, []);
let lastRemovedFileId: null | number = null;
const onDelete = () => {
const { playlist, onDelete } = props;
let currentFileId = playlist.find((file) => file.id === playlistPos)
?.fileId;
if (currentFileId === lastRemovedFileId) return;
const canDelete = targetFile?.security?.Delete;
if (!canDelete) return;
if (!isNullOrUndefined(currentFileId)) onDelete(currentFileId);
if (!isNullOrUndefined(currentFileId)) {
onDelete(currentFileId);
lastRemovedFileId = currentFileId;
}
};
const onDownload = () => {
@ -425,7 +432,7 @@ function MediaViewer({
return (
<>
{canOpen && (
<ImageViewer
<ViewerWrapper
userAccess={props.userAccess}
visible={props.visible}
title={title}

View File

@ -0,0 +1,6 @@
import styled from "styled-components";
import DropDown from "@docspace/components/drop-down";
export const StyledDropDown = styled(DropDown)`
background: #333;
`;

View File

@ -0,0 +1,16 @@
import styled from "styled-components";
import DropDownItem from "@docspace/components/drop-down-item";
export const StyledDropDownItem = styled(DropDownItem)`
color: #fff;
.drop-down-item_icon svg {
path {
fill: #fff !important;
}
}
&:hover {
background: #444;
}
`;

View File

@ -0,0 +1,33 @@
import { ContextMenuModel, PlaylistType } from "../../types";
interface ViewerWrapperProps {
userAccess: boolean;
visible: boolean;
title: string;
images: { src: string; alt: string }[];
inactive: boolean;
playlist: PlaylistType[];
playlistPos: number;
isFavorite: boolean;
isImage: boolean;
isAudio: boolean;
isVideo: boolean;
isPreviewFile: boolean;
archiveRoom: boolean;
errorTitle: string;
headerIcon: string;
audioIcon: string;
onClose: VoidFunction;
onPrevClick: VoidFunction;
onNextClick: VoidFunction;
onDeleteClick: VoidFunction;
onDownloadClick: VoidFunction;
onSetSelectionFile: VoidFunction;
contextModel: () => ContextMenuModel[];
}
export default ViewerWrapperProps;

View File

@ -0,0 +1,120 @@
import React, { useMemo, memo, useCallback } from "react";
import equal from "fast-deep-equal/react";
import { Viewer } from "@docspace/components/viewer";
import { isSeparator } from "../../helpers";
import { getCustomToolbar } from "../../helpers/getCustomToolbar";
import { ContextMenuModel } from "../../types";
import { StyledDropDown } from "../StyledDropDown";
import { StyledDropDownItem } from "../StyledDropDownItem";
import ViewerWrapperProps from "./ViewerWrapper.props";
const DefaultSpeedZoom = 0.25;
function ViewerWrapper(props: ViewerWrapperProps) {
const onClickContextItem = useCallback(
(item: ContextMenuModel) => {
if (isSeparator(item)) return;
item.onClick();
props.onClose();
},
[props.onClose]
);
const generateContextMenu = (
isOpen: boolean,
right: string,
bottom: string
) => {
const model = props.contextModel();
return (
<StyledDropDown
open={isOpen}
isDefaultMode={false}
directionY="top"
directionX="right"
fixedDirection={true}
withBackdrop={false}
manualY={(bottom || "63") + "px"}
manualX={(right || "-31") + "px"}
>
{model.map((item) => {
if (item.disabled) return;
const isItemSeparator = isSeparator(item);
return (
<StyledDropDownItem
className={`${item.isSeparator ? "is-separator" : ""}`}
key={item.key}
label={isItemSeparator ? undefined : item.label}
icon={!isItemSeparator && item.icon ? item.icon : ""}
onClick={() => onClickContextItem(item)}
/>
);
})}
</StyledDropDown>
);
};
const toolbars = useMemo(() => {
const {
onDeleteClick,
onDownloadClick,
playlist,
playlistPos,
userAccess,
} = props;
const customToolbar = getCustomToolbar(onDeleteClick, onDownloadClick);
const canShare = playlist[playlistPos].canShare;
const toolbars =
!canShare && userAccess
? customToolbar.filter(
(x) => x.key !== "share" && x.key !== "share-separator"
)
: customToolbar.filter((x) => x.key !== "delete");
return toolbars;
}, [
props.onDeleteClick,
props.onDownloadClick,
props.playlist,
props.playlistPos,
props.userAccess,
]);
return (
<Viewer
title={props.title}
images={props.images}
isAudio={props.isAudio}
isVideo={props.isVideo}
visible={props.visible}
isImage={props.isImage}
playlist={props.playlist}
inactive={props.inactive}
audioIcon={props.audioIcon}
zoomSpeed={DefaultSpeedZoom}
errorTitle={props.errorTitle}
headerIcon={props.headerIcon}
customToolbar={() => toolbars}
playlistPos={props.playlistPos}
archiveRoom={props.archiveRoom}
isPreviewFile={props.isPreviewFile}
onMaskClick={props.onClose}
onNextClick={props.onNextClick}
onPrevClick={props.onPrevClick}
contextModel={props.contextModel}
onDownloadClick={props.onDownloadClick}
generateContextMenu={generateContextMenu}
onSetSelectionFile={props.onSetSelectionFile}
/>
);
}
export default memo(ViewerWrapper, (prevProps, nextProps) =>
equal(prevProps, nextProps)
);

View File

@ -87,3 +87,21 @@ export interface IFile {
viewUrl: string;
webUrl: string;
}
export type ContextMenuType = {
id?: string;
key: string;
label: string;
icon: string;
disabled: boolean;
onClick: VoidFunction;
isSeparator?: undefined;
};
export type SeparatorType = {
key: string;
isSeparator: boolean;
disabled: boolean;
};
export type ContextMenuModel = ContextMenuType | SeparatorType;

View File

@ -19,6 +19,7 @@ import {
isDesktop as isDesktopUtils,
} from "@docspace/components/utils/device";
import ToggleInfoPanelButton from "./sub-components/toggle-infopanel-btn";
import TrashWarning from "./sub-components/trash-warning";
const Navigation = ({
tReady,
@ -33,6 +34,7 @@ const Navigation = ({
getContextOptionsPlus,
getContextOptionsFolder,
onBackToParentFolder,
isTrashFolder,
isRecycleBinFolder,
isEmptyFilesList,
clearTrash,
@ -168,6 +170,7 @@ const Navigation = ({
isRootFolder={isRootFolder}
canCreate={canCreate}
isTabletView={isTabletView}
isTrashFolder={isTrashFolder}
isRecycleBinFolder={isRecycleBinFolder}
isDesktop={isDesktop}
isDesktopClient={isDesktopClient}
@ -200,6 +203,9 @@ const Navigation = ({
onPlusClick={onPlusClick}
/>
</StyledContainer>
{isTrashFolder && !isEmptyPage && (
<TrashWarning title={titles.trashWarning} />
)}
{infoPanelIsVisible && (
<ToggleInfoPanelButton
id="info-panel-toggle--open"

View File

@ -9,7 +9,7 @@ import {
const StyledContainer = styled.div`
${(props) =>
!props.isDropBox &&
!props.isDropBoxComponent &&
props.isDesktop &&
css`
width: fit-content;
@ -26,6 +26,7 @@ const StyledContainer = styled.div`
height: 100%;
${(props) =>
props.isDesktopClient &&
props.isDropBoxComponent &&
css`
max-height: 32px;
`}

View File

@ -90,7 +90,7 @@ StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const ControlButtons = ({
personal,
isDropBox,
isDropBoxComponent,
isRootFolder,
canCreate,
getContextOptionsFolder,
@ -112,7 +112,7 @@ const ControlButtons = ({
};
return (
<StyledContainer isDropBox={isDropBox}>
<StyledContainer isDropBoxComponent={isDropBoxComponent}>
{(!isRootFolder && canCreate) ||
(isRecycleBinFolder && !isEmptyFilesList) ? (
<>

View File

@ -156,7 +156,7 @@ const DropBox = React.forwardRef(
>
<StyledContainer
canCreate={canCreate}
isDropBox={true}
isDropBoxComponent={true}
isInfoPanelVisible={isInfoPanelVisible}
isDesktopClient={isDesktopClient}
>
@ -169,7 +169,7 @@ const DropBox = React.forwardRef(
isDesktop={isDesktop}
personal={personal}
isRootFolder={isRootFolder}
isDropBox={true}
isDropBoxComponent={true}
canCreate={canCreate}
getContextOptionsFolder={getContextOptionsFolder}
getContextOptionsPlus={getContextOptionsPlus}

View File

@ -0,0 +1,47 @@
import React from "react";
import styled, { css } from "styled-components";
import { tablet } from "@docspace/components/utils/device";
const StyledTrashWarning = styled.div`
box-sizing: border-box;
height: 32px;
padding: 8px 12px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: left;
font-weight: 400;
font-size: 12px;
line-height: 16px;
color: ${({ theme }) => theme.section.header.trashErasureLabelText};
background: ${({ theme }) =>
theme.section.header.trashErasureLabelBackground};
${({ isTabletView }) =>
!isTabletView
? css`
@media ${tablet} {
display: none;
}
`
: css`
margin-bottom: 16px;
display: none;
@media ${tablet} {
display: flex;
}
`}
`;
const TrashWarning = ({ title, isTabletView }) => {
return (
<StyledTrashWarning className="trash-warning" isTabletView={isTabletView}>
{title}
</StyledTrashWarning>
);
};
export default TrashWarning;

View File

@ -225,6 +225,7 @@ class Section extends React.Component {
isHeaderVisible={isHeaderVisible}
viewAs={viewAs}
showText={showText}
isEmptyPage={this.props.isEmptyPage}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
@ -261,6 +262,7 @@ class Section extends React.Component {
viewAs={viewAs}
showText={showText}
settingsStudio={settingsStudio}
isEmptyPage={this.props.isEmptyPage}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
@ -387,6 +389,7 @@ Section.propTypes = {
isHeaderVisible: PropTypes.bool,
isInfoPanelAvailable: PropTypes.bool,
settingsStudio: PropTypes.bool,
isEmptyPage: PropTypes.bool,
};
Section.defaultProps = {

View File

@ -76,15 +76,22 @@ const StyledSectionContainer = styled.section`
.layout-progress-bar {
position: fixed;
right: ${(props) =>
props.isInfoPanelVisible && !isMobile ? "416px" : "15px"};
bottom: 21px;
props.isInfoPanelVisible && !isMobile ? "424px" : "24px"};
bottom: 24px;
}
.layout-progress-bar_close-icon {
position: fixed;
right: ${(props) =>
props.isInfoPanelVisible && !isMobile ? "480px" : "80px"};
bottom: 36px;
}
.layout-progress-second-bar {
position: fixed;
right: ${(props) =>
props.isInfoPanelVisible && !isMobile ? "416px" : "15px"};
bottom: 83px;
props.isInfoPanelVisible && !isMobile ? "424px" : "24px"};
bottom: 96px;
}
${(props) =>

View File

@ -41,6 +41,16 @@ const StyledSectionHeader = styled.div`
min-height: 53px;
`}
${({ isTrashFolder, isEmptyPage }) =>
isTrashFolder &&
!isEmptyPage &&
css`
@media ${tablet} {
height: 109px;
min-height: 109px;
}
`}
padding-right: 20px;
box-sizing: border-box;
@ -87,13 +97,24 @@ const StyledSectionHeader = styled.div`
StyledSectionHeader.defaultProps = { theme: Base };
const SectionHeader = (props) => {
const { viewAs, settingsStudio = false, className, ...rest } = props;
const {
viewAs,
settingsStudio = false,
className,
isEmptyPage,
...rest
} = props;
const pathname = window.location.pathname.toLowerCase();
const isTrashFolder = pathname.indexOf("trash") !== -1;
return (
<StyledSectionHeader
className={`section-header ${className}`}
isEmptyPage={isEmptyPage}
viewAs={viewAs}
settingsStudio={settingsStudio}
isTrashFolder={isTrashFolder}
{...rest}
/>
);

View File

@ -188,12 +188,12 @@ class SettingsStore {
this.greetingSettings = greetingSettings;
};
getSettings = async () => {
getSettings = async (withPassword) => {
let newSettings = null;
if (window?.__ASC_INITIAL_EDITOR_STATE__?.portalSettings)
newSettings = window.__ASC_INITIAL_EDITOR_STATE__.portalSettings;
else newSettings = await api.settings.getSettings();
else newSettings = await api.settings.getSettings(withPassword);
if (window["AscDesktopEditor"] !== undefined || this.personal) {
const dp = combineUrl(

View File

@ -2049,6 +2049,8 @@ const Base = {
header: {
backgroundColor: white,
background: `linear-gradient(180deg,#ffffff 2.81%,rgba(255, 255, 255, 0.91) 63.03%,rgba(255, 255, 255, 0) 100%)`,
trashErasureLabelBackground: "#f8f9f9",
trashErasureLabelText: "#555f65",
},
},

View File

@ -2040,6 +2040,8 @@ const Dark = {
header: {
backgroundColor: black,
background: `linear-gradient(180deg, #333333 2.81%, rgba(51, 51, 51, 0.9) 63.03%, rgba(51, 51, 51, 0) 100%);`,
trashErasureLabelBackground: "#292929",
trashErasureLabelText: "#ADADAD",
},
},

View File

@ -80,12 +80,12 @@ export const Viewer = (props) => {
};
}
return () => document.removeEventListener("touchstart", onTouch);
}, [isPlay, isOpenContextMenu]);
}, [isPlay, isOpenContextMenu, isImage]);
function resetTimer() {
setPanelVisible(true);
clearTimeout(timer);
timer = setTimeout(() => setPanelVisible(false), 5000);
timer = setTimeout(() => setPanelVisible(false), 2500);
setImageTimer(timer);
}

View File

@ -240,6 +240,10 @@ const StyledMobileDetails = styled.div`
.title {
font-weight: 600;
margin-top: 6px;
width: calc(100% - 100px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;

View File

@ -907,7 +907,7 @@ export default function ViewerPlayer(props) {
}
if (isMobileOnly && videoRef.current && displayUI) {
clearTimeout(globalTimer);
setGlobalTimer(setTimeout(() => setPanelVisible(false), 5000));
setGlobalTimer(setTimeout(() => setPanelVisible(false), 2500));
}
}, [displayUI, isOpenContextMenu, state.isControlTouch, props.isPlay]);

View File

@ -32,11 +32,17 @@ export default function template(
});
}
const initialEditorStateStringify = JSON.stringify(initialEditorState);
const initialEditorStateString = initialEditorStateStringify.includes(
"</script>"
)
? initialEditorStateStringify.replaceAll("</script>", "<\\/script>")
: initialEditorStateStringify;
const scripts = `
<script id="__ASC_INITIAL_EDITOR_STATE__">
window.__ASC_INITIAL_EDITOR_STATE__ = ${JSON.stringify(
initialEditorState
)}
window.__ASC_INITIAL_EDITOR_STATE__ = ${initialEditorStateString}
</script>
<script id="__ASC_INITIAL_EDITOR_I18N__">
window.initialI18nStoreASC = ${JSON.stringify(initialI18nStoreASC)}

View File

@ -1,7 +1,7 @@
import React, { useState, useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import { ButtonsWrapper, LoginFormWrapper } from "./StyledLogin";
import { ButtonsWrapper, LoginFormWrapper, LoginContent } from "./StyledLogin";
import Text from "@docspace/components/text";
import SocialButton from "@docspace/components/social-button";
import {
@ -210,77 +210,80 @@ const Login: React.FC<ILoginProps> = ({
isDesktop={isDesktopEditor}
bgPattern={bgPattern}
>
<ColorTheme themeId={ThemeType.LinkForgotPassword} theme={theme}>
<img src={logoUrl} className="logo-wrapper" />
<Text
fontSize="23px"
fontWeight={700}
textAlign="center"
className="greeting-title"
>
{greetingSettings}
</Text>
<FormWrapper id="login-form" theme={theme}>
{ssoExists() && <ButtonsWrapper>{ssoButton()}</ButtonsWrapper>}
{oauthDataExists() && (
<>
<ButtonsWrapper>{providerButtons()}</ButtonsWrapper>
{providers && providers.length > 2 && (
<Link
isHovered
type="action"
fontSize="13px"
fontWeight="600"
color="#3B72A7"
className="more-label"
onClick={moreAuthOpen}
>
{t("Common:ShowMore")}
</Link>
)}
</>
)}
{(oauthDataExists() || ssoExists()) && (
<div className="line">
<Text className="or-label">{t("Or")}</Text>
</div>
)}
<LoginForm
isDesktop={!!isDesktopEditor}
isLoading={isLoading}
hashSettings={portalSettings?.passwordHash}
setIsLoading={setIsLoading}
onRecoverDialogVisible={onRecoverDialogVisible}
match={match}
enableAdmMess={enableAdmMess}
<div className="bg-cover"></div>
<LoginContent>
<ColorTheme themeId={ThemeType.LinkForgotPassword} theme={theme}>
<img src={logoUrl} className="logo-wrapper" />
<Text
fontSize="23px"
fontWeight={700}
textAlign="center"
className="greeting-title"
>
{greetingSettings}
</Text>
<FormWrapper id="login-form" theme={theme}>
{ssoExists() && <ButtonsWrapper>{ssoButton()}</ButtonsWrapper>}
{oauthDataExists() && (
<>
<ButtonsWrapper>{providerButtons()}</ButtonsWrapper>
{providers && providers.length > 2 && (
<Link
isHovered
type="action"
fontSize="13px"
fontWeight="600"
color="#3B72A7"
className="more-label"
onClick={moreAuthOpen}
>
{t("Common:ShowMore")}
</Link>
)}
</>
)}
{(oauthDataExists() || ssoExists()) && (
<div className="line">
<Text className="or-label">{t("Or")}</Text>
</div>
)}
<LoginForm
isDesktop={!!isDesktopEditor}
isLoading={isLoading}
hashSettings={portalSettings?.passwordHash}
setIsLoading={setIsLoading}
onRecoverDialogVisible={onRecoverDialogVisible}
match={match}
enableAdmMess={enableAdmMess}
/>
</FormWrapper>
<Toast />
<MoreLoginModal
visible={moreAuthVisible}
onClose={moreAuthClose}
providers={providers}
onSocialLoginClick={onSocialButtonClick}
ssoLabel={ssoLabel}
ssoUrl={ssoUrl}
t={t}
/>
</FormWrapper>
<Toast />
<MoreLoginModal
visible={moreAuthVisible}
onClose={moreAuthClose}
providers={providers}
onSocialLoginClick={onSocialButtonClick}
ssoLabel={ssoLabel}
ssoUrl={ssoUrl}
t={t}
/>
<RecoverAccessModalDialog
visible={recoverDialogVisible}
onClose={onRecoverDialogVisible}
textBody={t("RecoverTextBody")}
emailPlaceholderText={t("RecoverContactEmailPlaceholder")}
id="recover-access-modal"
/>
</ColorTheme>
{!checkIsSSR() && enabledJoin && (
<Register
id="login_register"
enabledJoin={enabledJoin}
currentColorScheme={currentColorScheme}
/>
)}
<RecoverAccessModalDialog
visible={recoverDialogVisible}
onClose={onRecoverDialogVisible}
textBody={t("RecoverTextBody")}
emailPlaceholderText={t("RecoverContactEmailPlaceholder")}
id="recover-access-modal"
/>
</ColorTheme>
{!checkIsSSR() && enabledJoin && (
<Register
id="login_register"
enabledJoin={enabledJoin}
currentColorScheme={currentColorScheme}
/>
)}
</LoginContent>{" "}
</LoginFormWrapper>
);
};

View File

@ -1,5 +1,5 @@
import styled, { css } from "styled-components";
import { tablet } from "@docspace/components/utils/device";
import { tablet, hugeMobile } from "@docspace/components/utils/device";
export const ButtonsWrapper = styled.div`
display: flex;
@ -12,8 +12,6 @@ export const ButtonsWrapper = styled.div`
}
`;
interface ILoginFormWrapperProps {
enabledJoin?: boolean;
isDesktop?: boolean;
@ -29,20 +27,44 @@ export const LoginFormWrapper = styled.div`
: css`1fr 68px`
: css`1fr`};
width: 100%;
height: 100vh;
box-sizing: border-box;
background-image: ${props => props.bgPattern};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: 100% 100%;
@media (max-width: 1024px) {
background-size: cover;
}
@media (max-width: 428px) {
background-image: none;
@media ${hugeMobile} {
height: calc(100vh - 48px);
}
.bg-cover {
background-image: ${props => props.bgPattern};
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
@media ${hugeMobile} {
background-image: none;
}
}
`;
export const LoginContent = styled.div`
min-height: 100vh;
flex: 1 0 auto;
flex-direction: column;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
@media ${hugeMobile} {
min-height: 100%;
justify-content: start;
}
`;

View File

@ -170,9 +170,11 @@ public class FileDtoHelper : FileEntryDtoHelper
result.ThumbnailStatus = file.ThumbnailStatus;
var cacheKey = Math.Abs(result.Updated.GetHashCode());
if (file.ThumbnailStatus == Thumbnail.Created)
{
result.ThumbnailUrl = _commonLinkUtility.GetFullAbsolutePath(_filesLinkUtility.GetFileThumbnailUrl(file.Id, file.Version));
result.ThumbnailUrl = _commonLinkUtility.GetFullAbsolutePath(_filesLinkUtility.GetFileThumbnailUrl(file.Id, file.Version)) + $"&hash={cacheKey}";
}
}
catch (Exception)

View File

@ -27,29 +27,29 @@
using static ASC.Files.Core.Security.FileSecurity;
namespace ASC.Files.Core.ApiModels.ResponseDto;
public abstract class FileEntryDto
{
protected internal abstract FileEntryType EntryType { get; }
public string Title { get; set; }
public FileShare Access { get; set; }
public bool Shared { get; set; }
public ApiDateTime Created { get; set; }
public string Title { get; set; }
public FileShare Access { get; set; }
public bool Shared { get; set; }
public ApiDateTime Created { get; set; }
public EmployeeDto CreatedBy { get; set; }
private ApiDateTime _updated;
private ApiDateTime _updated;
public ApiDateTime Updated
{
get => _updated < Created ? Created : _updated;
set => _updated = value;
}
public FolderType RootFolderType { get; set; }
public EmployeeDto UpdatedBy { get; set; }
public bool? ProviderItem { get; set; }
public string ProviderKey { get; set; }
public ApiDateTime AutoDelete { get; set; }
public FolderType RootFolderType { get; set; }
public EmployeeDto UpdatedBy { get; set; }
public bool? ProviderItem { get; set; }
public string ProviderKey { get; set; }
public int? ProviderId { get; set; }
protected FileEntryDto(FileEntry entry)
{
Title = entry.Title;
@ -60,24 +60,32 @@ public abstract class FileEntryDto
ProviderKey = entry.ProviderKey;
ProviderId = entry.ProviderId.NullIfDefault();
}
protected FileEntryDto() { }
}
public abstract class FileEntryDto<T> : FileEntryDto
{
{
public T Id { get; set; }
public T RootFolderId { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public T OriginId { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public T OriginRoomId { get; set; }
public string OriginTitle { get; set; }
public string OriginRoomTitle { get; set; }
public bool CanShare { get; set; }
public IDictionary<FilesSecurityActions, bool> Security { get; set; }
protected FileEntryDto(FileEntry<T> entry)
: base(entry)
{
Id = entry.Id;
RootFolderId = entry.RootId;
}
protected FileEntryDto() { }
}
@ -129,7 +137,12 @@ public class FileEntryDtoHelper
ProviderKey = entry.ProviderKey,
ProviderId = entry.ProviderId.NullIfDefault(),
CanShare = await _fileSharingHelper.CanSetAccessAsync(entry),
Security = entry.Security
Security = entry.Security,
OriginId = entry.OriginId,
OriginTitle = entry.OriginTitle,
OriginRoomId = entry.OriginRoomId,
OriginRoomTitle = entry.OriginRoomTitle,
AutoDelete = entry.DeletedPermanentlyOn != default ? _apiDateTimeHelper.Get(entry.DeletedPermanentlyOn) : null
};
}
}
}

View File

@ -105,7 +105,7 @@ public class FilesSettings : ISettings<FilesSettings>
HideFavoritesSetting = false,
HideTemplatesSetting = false,
DownloadTarGzSetting = false,
AutomaticallyCleanUpSetting = null,
AutomaticallyCleanUpSetting = new AutoCleanUpData { IsAutoCleanUp = true, Gap = DateToAutoCleanUp.ThirtyDays },
DefaultSharingAccessRightsSetting = null
};
}
@ -457,4 +457,4 @@ public class FilesSettingsHelper
{
_settingsManager.SaveForCurrentUser(settings);
}
}
}

View File

@ -29,250 +29,250 @@ namespace ASC.Files.Core;
[Scope]
public interface IFileDao<T>
{
/// <summary>
/// Clear the application cache for the specific file
/// <summary>
/// Clear the application cache for the specific file
/// </summary>
Task InvalidateCacheAsync(T fileId);
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId);
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId, int fileVersion);
/// <summary>
/// Receive file
/// </summary>
/// <param name="parentId">folder id</param>
/// <param name="title">file name</param>
/// <returns>
/// file
/// <summary>
/// Receive file
/// </summary>
/// <param name="parentId">folder id</param>
/// <param name="title">file name</param>
/// <returns>
/// file
/// </returns>
Task<File<T>> GetFileAsync(T parentId, string title);
/// <summary>
/// Receive last file without forcesave
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion"></param>
/// <returns></returns>
/// <summary>
/// Receive last file without forcesave
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion"></param>
/// <returns></returns>
Task<File<T>> GetFileStableAsync(T fileId, int fileVersion = -1);
/// <summary>
/// Returns all versions of the file
/// </summary>
/// <param name="fileId"></param>
/// <summary>
/// Returns all versions of the file
/// </summary>
/// <param name="fileId"></param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFileHistoryAsync(T fileId);
/// <summary>
/// Gets the file (s) by ID (s)
/// </summary>
/// <param name="fileIds">id file</param>
/// <summary>
/// Gets the file (s) by ID (s)
/// </summary>
/// <param name="fileIds">id file</param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> fileIds);
/// <summary>
/// Gets the file (s) by ID (s) for share
/// </summary>
/// <param name="fileIds">id file</param>
/// <param name="filterType"></param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <returns></returns>
/// <summary>
/// Gets the file (s) by ID (s) for share
/// </summary>
/// <param name="fileIds">id file</param>
/// <param name="filterType"></param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesFilteredAsync(IEnumerable<T> fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool checkShared = false);
/// <summary>
///
/// </summary>
/// <param name="parentId"></param>
/// <summary>
///
/// </summary>
/// <param name="parentId"></param>
/// <returns></returns>
IAsyncEnumerable<T> GetFilesAsync(T parentId);
/// <summary>
/// Get files in folder
/// </summary>
/// <param name="parentId">folder id</param>
/// <param name="orderBy"></param>
/// <param name="filterType">filterType type</param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"> </param>
/// <param name="searchInContent"></param>
/// <param name="withSubfolders"> </param>
/// <returns>list of files</returns>
/// <remarks>
/// Return only the latest versions of files of a folder
/// <summary>
/// Get files in folder
/// </summary>
/// <param name="parentId">folder id</param>
/// <param name="orderBy"></param>
/// <param name="filterType">filterType type</param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"> </param>
/// <param name="searchInContent"></param>
/// <param name="withSubfolders"> </param>
/// <returns>list of files</returns>
/// <remarks>
/// Return only the latest versions of files of a folder
/// </remarks>
IAsyncEnumerable<File<T>> GetFilesAsync(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false, bool exludeSubject = false);
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file);
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="offset"></param>
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="offset"></param>
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file, long offset);
/// <summary>
/// Get presigned uri
/// </summary>
/// <param name="file"></param>
/// <param name="expires"></param>
/// <summary>
/// Get presigned uri
/// </summary>
/// <param name="file"></param>
/// <param name="expires"></param>
/// <returns>Stream uri</returns>
Task<Uri> GetPreSignedUriAsync(File<T> file, TimeSpan expires);
/// <summary>
/// Check is supported PreSignedUri
/// </summary>
/// <param name="file"></param>
/// <summary>
/// Check is supported PreSignedUri
/// </summary>
/// <param name="file"></param>
/// <returns>Stream uri</returns>
Task<bool> IsSupportedPreSignedUriAsync(File<T> file);
/// <summary>
/// Saves / updates the version of the file
/// and save stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="fileStream"> </param>
/// <returns></returns>
/// <remarks>
/// Updates the file if:
/// - The file comes with the given id
/// - The file with that name in the folder / container exists
///
/// Save in all other cases
/// </remarks>
/// <summary>
/// Saves / updates the version of the file
/// and save stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="fileStream"> </param>
/// <returns></returns>
/// <remarks>
/// Updates the file if:
/// - The file comes with the given id
/// - The file with that name in the folder / container exists
///
/// Save in all other cases
/// </remarks>
Task<File<T>> SaveFileAsync(File<T> file, Stream fileStream);
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <param name="fileStream"></param>
/// <returns></returns>
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <param name="fileStream"></param>
/// <returns></returns>
Task<File<T>> ReplaceFileVersionAsync(File<T> file, Stream fileStream);
/// <summary>
/// Deletes a file including all previous versions
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Deletes a file including all previous versions
/// </summary>
/// <param name="fileId">file id</param>
Task DeleteFileAsync(T fileId);
/// <summary>
/// Checks whether or not file
/// </summary>
/// <param name="title">file name</param>
/// <param name="folderId">folder id</param>
/// <returns>Returns true if the file exists, otherwise false</returns>
/// <summary>
/// Checks whether or not file
/// </summary>
/// <param name="title">file name</param>
/// <param name="folderId">folder id</param>
/// <returns>Returns true if the file exists, otherwise false</returns>
Task<bool> IsExistAsync(string title, object folderId);
/// <summary>
/// Moves a file or set of files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Moves a file or set of files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param>
Task<T> MoveFileAsync(T fileId, T toFolderId);
Task<TTo> MoveFileAsync<TTo>(T fileId, TTo toFolderId);
Task<string> MoveFileAsync(T fileId, string toFolderId);
Task<int> MoveFileAsync(T fileId, int toFolderId);
/// <summary>
/// Copy the files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Copy the files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param>
Task<File<T>> CopyFileAsync(T fileId, T toFolderId);
Task<File<TTo>> CopyFileAsync<TTo>(T fileId, TTo toFolderId);
Task<File<string>> CopyFileAsync(T fileId, string toFolderId);
Task<File<int>> CopyFileAsync(T fileId, int toFolderId);
/// <summary>
/// Rename file
/// </summary>
/// <param name="file"></param>
/// <summary>
/// Rename file
/// </summary>
/// <param name="file"></param>
/// <param name="newTitle">new name</param>
Task<T> FileRenameAsync(File<T> file, string newTitle);
/// <summary>
/// Update comment file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
/// <param name="comment">new comment</param>
/// <summary>
/// Update comment file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
/// <param name="comment">new comment</param>
Task<string> UpdateCommentAsync(T fileId, int fileVersion, string comment);
/// <summary>
/// Complete file version
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Complete file version
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
Task CompleteVersionAsync(T fileId, int fileVersion);
/// <summary>
/// Continue file version
/// </summary>
/// <param name="fileId">file id</param>
/// <summary>
/// Continue file version
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
Task ContinueVersionAsync(T fileId, int fileVersion);
/// <summary>
/// Check the need to use the trash before removing
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
/// <summary>
/// Check the need to use the trash before removing
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
bool UseTrashForRemove(File<T> file);
string GetUniqFilePath(File<T> file, string fileTitle);
#region chunking
#region chunking
Task<ChunkedUploadSession<T>> CreateUploadSessionAsync(File<T> file, long contentLength);
Task<File<T>> UploadChunkAsync(ChunkedUploadSession<T> uploadSession, Stream chunkStream, long chunkLength);
Task<File<T>> FinalizeUploadSessionAsync(ChunkedUploadSession<T> uploadSession);
Task AbortUploadSessionAsync(ChunkedUploadSession<T> uploadSession);
#endregion
#endregion
#region Only in TMFileDao
#region Only in TMFileDao
/// <summary>
/// Set created by
/// </summary>
/// <param name="fileIds"></param>
/// <summary>
/// Set created by
/// </summary>
/// <param name="fileIds"></param>
/// <param name="newOwnerId"></param>
Task ReassignFilesAsync(T[] fileIds, Guid newOwnerId);
/// <summary>
/// Search files in SharedWithMe & Projects
/// </summary>
/// <param name="parentIds"></param>
/// <param name="filterType"></param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <summary>
/// Search files in SharedWithMe & Projects
/// </summary>
/// <param name="parentIds"></param>
/// <param name="filterType"></param>
/// <param name="subjectGroup"></param>
/// <param name="subjectID"></param>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent);
/// <summary>
/// Search the list of files containing text
/// Only in TMFileDao
/// </summary>
/// <param name="text">search text</param>
/// <param name="bunch"></param>
/// <returns>list of files</returns>
/// <summary>
/// Search the list of files containing text
/// Only in TMFileDao
/// </summary>
/// <param name="text">search text</param>
/// <param name="bunch"></param>
/// <returns>list of files</returns>
IAsyncEnumerable<File<T>> SearchAsync(string text, bool bunch = false);
/// <summary>
/// Checks whether file exists on storage
/// </summary>
/// <param name="file">file</param>
/// <returns></returns>
/// <summary>
/// Checks whether file exists on storage
/// </summary>
/// <param name="file">file</param>
/// <returns></returns>
Task<bool> IsExistOnStorageAsync(File<T> file);
@ -290,15 +290,15 @@ public interface IFileDao<T>
Task<Stream> GetThumbnailAsync(File<T> file, int width, int height);
Task<Stream> GetThumbnailAsync(T fileId, int width, int height);
Task<Stream> GetThumbnailAsync(T fileId, int width, int height);
IAsyncEnumerable<FileWithShare> GetFeedsAsync(int tenant, DateTime from, DateTime to);
IAsyncEnumerable<int> GetTenantsWithFeedsAsync(DateTime fromTime, bool includeSecurity);
Task<EntryProperties> GetProperties(T fileId);
Task SaveProperties(T fileId, EntryProperties entryProperties);
#endregion
Task<EntryProperties> GetProperties(T fileId);
Task SaveProperties(T fileId, EntryProperties entryProperties);
#endregion
}

View File

@ -359,6 +359,7 @@ public interface IFolderDao<T>
IAsyncEnumerable<int> GetTenantsWithFoldersFeedsAsync(DateTime fromTime);
IAsyncEnumerable<int> GetTenantsWithRoomsFeedsAsync(DateTime fromTime);
IAsyncEnumerable<OriginData> GetOriginsDataAsync(IEnumerable<T> entriesIds);
#endregion
}

View File

@ -50,4 +50,5 @@ public interface ITagDao<T>
Task RemoveTagsAsync(FileEntry<T> entry, IEnumerable<int> tagsIds);
Task RemoveTags(IEnumerable<Tag> tag);
Task RemoveTags(Tag tag);
}
Task<int> RemoveTagLinksAsync(T entryId, FileEntryType entryType, TagType tagType);
}

View File

@ -94,4 +94,4 @@ public static class DaoFactoryExtension
services.TryAdd<EditHistory>();
}
}
}

View File

@ -24,8 +24,6 @@
// 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
using Microsoft.OneDrive.Sdk;
using Document = ASC.ElasticSearch.Document;
namespace ASC.Files.Core.Data;
@ -359,7 +357,7 @@ internal class FileDao : AbstractDao, IFileDao<int>
}
break;
}
await foreach (var e in FromQuery(filesDbContext, q).AsAsyncEnumerable())
{
yield return _mapper.Map<DbFileQuery, File<int>>(e);
@ -892,11 +890,13 @@ internal class FileDao : AbstractDao, IFileDao<int>
using (var tx = await filesDbContext.Database.BeginTransactionAsync())
{
var trashId = await trashIdTask;
var oldParentId = toUpdate.FirstOrDefault()?.ParentId;
foreach (var f in toUpdate)
{
f.ParentId = toFolderId;
var trashId = await trashIdTask;
if (trashId.Equals(toFolderId))
{
f.ModifiedBy = _authContext.CurrentAccount.ID;
@ -904,6 +904,18 @@ internal class FileDao : AbstractDao, IFileDao<int>
}
}
var tagDao = _daoFactory.GetTagDao<int>();
if (toFolderId == trashId && oldParentId.HasValue)
{
var origin = Tag.Origin(fileId, FileEntryType.File, oldParentId.Value, _authContext.CurrentAccount.ID);
await tagDao.SaveTags(origin);
}
else if (oldParentId == trashId)
{
await tagDao.RemoveTagLinksAsync(fileId, FileEntryType.File, TagType.Origin);
}
await filesDbContext.SaveChangesAsync();
await tx.CommitAsync();

View File

@ -47,6 +47,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
private readonly ProviderFolderDao _providerFolderDao;
private readonly CrossDao _crossDao;
private readonly IMapper _mapper;
private readonly GlobalFolder _globalFolder;
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private readonly GlobalStore _globalStore;
@ -69,7 +70,8 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
ProviderFolderDao providerFolderDao,
CrossDao crossDao,
IMapper mapper,
GlobalStore globalStore)
GlobalStore globalStore,
GlobalFolder globalFolder)
: base(
dbContextManager,
userManager,
@ -91,6 +93,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
_crossDao = crossDao;
_mapper = mapper;
_globalStore = globalStore;
_globalFolder = globalFolder;
}
public async Task<Folder<int>> GetFolderAsync(int folderId)
@ -282,7 +285,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
q = excludeSubject ? q.Where(r => r.CreateBy != subjectID) : q.Where(r => r.CreateBy == subjectID);
}
}
await foreach (var e in FromQueryWithShared(filesDbContext, q).AsAsyncEnumerable())
{
yield return _mapper.Map<DbFolderQuery, Folder<int>>(e);
@ -591,6 +594,16 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
.ToListAsync();
filesDbContext.Tag.RemoveRange(tagsToRemove);
var originToDelete = await Query(filesDbContext.Tag)
.Where(t => t.Name == id.ToString() || subfoldersStrings.Contains(t.Name))
.ToListAsync();
filesDbContext.Tag.RemoveRange(originToDelete);
await Query(filesDbContext.TagLink)
.Where(l => originToDelete.Select(t => t.Id).Contains(l.TagId))
.ExecuteDeleteAsync();
var securityToDelete = await Query(filesDbContext.Security)
.Where(r => subfoldersStrings.Contains(r.EntryId))
@ -633,6 +646,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
{
using var filesDbContext = _dbContextFactory.CreateDbContext();
var strategy = filesDbContext.Database.CreateExecutionStrategy();
var trashIdTask = _globalFolder.GetFolderTrashAsync<int>(_daoFactory);
await strategy.ExecuteAsync(async () =>
{
@ -640,6 +654,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
using (var tx = await filesDbContext.Database.BeginTransactionAsync())
{
var folder = await GetFolderAsync(folderId);
var oldParentId = folder.ParentId;
if (folder.FolderType != FolderType.DEFAULT && !DocSpaceHelper.IsRoom(folder.FolderType))
{
@ -699,6 +714,19 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
}
}
var trashId = await trashIdTask;
var tagDao = _daoFactory.GetTagDao<int>();
if (toFolderId == trashId)
{
var origin = Tag.Origin(folderId, FileEntryType.Folder, oldParentId, _authContext.CurrentAccount.ID);
await tagDao.SaveTags(origin);
}
else if (oldParentId == trashId)
{
await tagDao.RemoveTagLinksAsync(folderId, FileEntryType.Folder, TagType.Origin);
}
await filesDbContext.SaveChangesAsync().ConfigureAwait(false);
await tx.CommitAsync().ConfigureAwait(false);
@ -1311,6 +1339,13 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
return (this as IFolderDao<int>).GetFolderIDAsync(FileConstant.ModuleId, Archive, null, createIfNotExists);
}
public IAsyncEnumerable<OriginData> GetOriginsDataAsync(IEnumerable<int> entriesIds)
{
var filesDbContext = _dbContextFactory.CreateDbContext();
return _getOriginsDataQuery(filesDbContext, entriesIds, TenantID);
}
#endregion
protected internal IQueryable<DbFolder> GetFolderQuery(FilesDbContext filesDbContext, Expression<Func<DbFolder, bool>> where = null)
@ -1645,6 +1680,27 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
return _globalStore.GetStore().CreateDataWriteOperator(chunkedUploadSession, sessionHolder);
}
private static readonly Func<FilesDbContext, IEnumerable<int>, int, IAsyncEnumerable<OriginData>> _getOriginsDataQuery =
Microsoft.EntityFrameworkCore.EF.CompileAsyncQuery((FilesDbContext filesDbContext, IEnumerable<int> entriesIds, int tenantId) =>
filesDbContext.TagLink.AsNoTracking()
.Where(l => l.TenantId == tenantId)
.Where(l => entriesIds.Contains(Convert.ToInt32(l.EntryId)))
.Join(filesDbContext.Tag.AsNoTracking()
.Where(t => t.Type == TagType.Origin), l => l.TagId, t => t.Id, (l, t) => new { t.Name, t.Type, l.EntryType, l.EntryId })
.GroupBy(r => r.Name, r => new { r.EntryId, r.EntryType })
.Select(r => new OriginData
{
OriginRoom = filesDbContext.Folders.AsNoTracking().FirstOrDefault(f => f.TenantId == tenantId &&
f.Id == filesDbContext.Tree.AsNoTracking()
.Where(t => t.FolderId == Convert.ToInt32(r.Key))
.OrderByDescending(t => t.Level)
.Select(t => t.ParentId)
.Skip(1)
.FirstOrDefault()),
OriginFolder = filesDbContext.Folders.AsNoTracking().FirstOrDefault(f => f.TenantId == tenantId && f.Id == Convert.ToInt32(r.Key)),
Entries = r.Select(e => new KeyValuePair<string, FileEntryType>(e.EntryId, e.EntryType)).ToHashSet()
}));
private string GetProjectTitle(object folderID)
{
return "";
@ -1716,4 +1772,11 @@ public class ParentRoomPair
{
public int FolderId { get; set; }
public int ParentRoomId { get; set; }
}
public class OriginData
{
public DbFolder OriginRoom { get; set; }
public DbFolder OriginFolder { get; set; }
public HashSet<KeyValuePair<string, FileEntryType>> Entries { get; set; }
}

View File

@ -642,6 +642,33 @@ internal class TagDao<T> : AbstractDao, ITagDao<T>
});
}
public async Task<int> RemoveTagLinksAsync(T entryId, FileEntryType entryType, TagType tagType)
{
var count = 0;
await using var filesDbContext = await _dbContextFactory.CreateDbContextAsync();
var strategy = filesDbContext.Database.CreateExecutionStrategy();
var mappedId = (await MappingIDAsync(entryId)).ToString();
await strategy.ExecuteAsync(async () =>
{
await using var tx = await filesDbContext.Database.BeginTransactionAsync();
count = await Query(filesDbContext.TagLink)
.Where(l => l.EntryId == mappedId && l.EntryType == entryType)
.Join(filesDbContext.Tag, l => l.TagId, t => t.Id, (l, t) => new { l, t.Type })
.Where(r => r.Type == tagType)
.Select(r => r.l)
.ExecuteDeleteAsync();
await filesDbContext.SaveChangesAsync();
await tx.CommitAsync();
});
return count;
}
private Task RemoveTagInDbAsync(Tag tag)
{
if (tag == null)

View File

@ -24,26 +24,27 @@
// 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
namespace ASC.Files.Core;
namespace ASC.Files.Core;
public enum DateToAutoCleanUp
{
OneWeek = 1,
TwoWeeks,
OneMonth,
TwoMonths,
ThreeMonths
}
public class AutoCleanUpData
{
public bool IsAutoCleanUp { get; set; }
public DateToAutoCleanUp Gap { get; set; }
}
public enum DateToAutoCleanUp
{
OneWeek = 1,
TwoWeeks,
OneMonth,
ThirtyDays,
TwoMonths,
ThreeMonths
}
[Scope]
public class FileDateTime
public class AutoCleanUpData
{
public bool IsAutoCleanUp { get; set; }
public DateToAutoCleanUp Gap { get; set; }
}
[Scope]
public class FileDateTime
{
private readonly TenantUtil _tenantUtil;
@ -51,19 +52,32 @@ public class FileDateTime
{
_tenantUtil = tenantUtil;
}
public DateTime GetModifiedOnWithAutoCleanUp(DateTime modifiedOn, DateToAutoCleanUp date, bool utc = false)
{
var dateTime = modifiedOn;
switch (date)
{
case DateToAutoCleanUp.OneWeek: dateTime = dateTime.AddDays(7); break;
case DateToAutoCleanUp.TwoWeeks: dateTime = dateTime.AddDays(14); break;
case DateToAutoCleanUp.OneMonth: dateTime = dateTime.AddMonths(1); break;
case DateToAutoCleanUp.TwoMonths: dateTime = dateTime.AddMonths(2); break;
case DateToAutoCleanUp.ThreeMonths: dateTime = dateTime.AddMonths(3); break;
default: break;
}
return utc ? _tenantUtil.DateTimeToUtc(dateTime) : dateTime;
}
public DateTime GetModifiedOnWithAutoCleanUp(DateTime modifiedOn, DateToAutoCleanUp date, bool utc = false)
{
var dateTime = modifiedOn;
switch (date)
{
case DateToAutoCleanUp.OneWeek:
dateTime = dateTime.AddDays(7);
break;
case DateToAutoCleanUp.TwoWeeks:
dateTime = dateTime.AddDays(14);
break;
case DateToAutoCleanUp.OneMonth:
dateTime = dateTime.AddMonths(1);
break;
case DateToAutoCleanUp.ThirtyDays:
dateTime = dateTime.AddDays(30);
break;
case DateToAutoCleanUp.TwoMonths:
dateTime = dateTime.AddMonths(2);
break;
case DateToAutoCleanUp.ThreeMonths:
dateTime = dateTime.AddMonths(3);
break;
}
return utc ? _tenantUtil.DateTimeToUtc(dateTime) : dateTime;
}
}

View File

@ -86,6 +86,8 @@ public abstract class FileEntry : ICloneable
public abstract bool IsNew { get; set; }
public FileEntryType FileEntryType { get; set; }
public IEnumerable<Tag> Tags { get; set; }
public string OriginTitle { get; set; }
public string OriginRoomTitle { get; set; }
private string _modifiedByString;
private string _createByString;
@ -112,6 +114,8 @@ public abstract class FileEntry<T> : FileEntry, ICloneable, IFileEntry<T>
{
public T Id { get; set; }
public T ParentId { get; set; }
public T OriginId { get; set; }
public T OriginRoomId { get; set; }
public IDictionary<FilesSecurityActions, bool> Security { get; set; }
@ -137,34 +141,25 @@ public abstract class FileEntry<T> : FileEntry, ICloneable, IFileEntry<T>
public T FolderIdDisplay
{
get
{
if (_folderIdDisplay != null)
{
return _folderIdDisplay;
}
return ParentId;
}
get => !EqualityComparer<T>.Default.Equals(_folderIdDisplay, default) ? _folderIdDisplay : ParentId;
set => _folderIdDisplay = value;
}
public string DeletedPermanentlyOnString
public DateTime DeletedPermanentlyOn
{
get
{
if (!ModifiedOn.Equals(default(DateTime)) && Equals(FolderIdDisplay, _globalFolderHelper.FolderTrash) && _filesSettingsHelper.AutomaticallyCleanUp.IsAutoCleanUp)
if (!ModifiedOn.Equals(default) && Equals(FolderIdDisplay, _globalFolderHelper.FolderTrash) && _filesSettingsHelper.AutomaticallyCleanUp.IsAutoCleanUp)
{
var deletedPermanentlyOn = _fileDateTime.GetModifiedOnWithAutoCleanUp(ModifiedOn, _filesSettingsHelper.AutomaticallyCleanUp.Gap);
return deletedPermanentlyOn.ToString("g");
}
else
{
return null;
}
return _fileDateTime.GetModifiedOnWithAutoCleanUp(ModifiedOn, _filesSettingsHelper.AutomaticallyCleanUp.Gap);
}
return default;
}
}
public string DeletedPermanentlyOnString => DeletedPermanentlyOn != default ? DeletedPermanentlyOn.ToString("g") : null;
public bool DenyDownload { get; set; }
public bool DenySharing { get; set; }
@ -193,4 +188,4 @@ public abstract class FileEntry<T> : FileEntry, ICloneable, IFileEntry<T>
{
return Title;
}
}
}

View File

@ -37,7 +37,8 @@ public enum SortedByType
New,
DateAndTimeCreation,
RoomType,
Tags
Tags,
Room
}
[DebuggerDisplay("{SortedBy} {IsAsc}")]

View File

@ -23,9 +23,9 @@
// 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
using Profile = AutoMapper.Profile;
namespace ASC.Files.Core;
[Flags]
@ -39,6 +39,7 @@ public enum TagType
Template = 32,
Custom = 64,
Pin = 128,
Origin = 256
}
[Serializable]
@ -114,6 +115,15 @@ public sealed class Tag : IMapFrom<DbFilesTag>
return new Tag("pin", TagType.Pin, owner, 0).AddEntry(entry);
}
public static Tag Origin<T>(T entryId, FileEntryType type, T originId, Guid owner)
{
return new Tag(originId.ToString(), TagType.Origin, owner, 0)
{
EntryId = entryId,
EntryType = type
};
}
public override bool Equals(object obj)
{
return obj is Tag f && Equals(f);
@ -134,4 +144,4 @@ public sealed class Tag : IMapFrom<DbFilesTag>
profile.CreateMap<DbFilesTag, Tag>();
profile.CreateMap<DbFilesTagLink, Tag>();
}
}
}

View File

@ -276,6 +276,7 @@ public class FileStorageService<T> //: IFileStorageService
bool withSubfolders,
OrderBy orderBy,
SearchArea searchArea = SearchArea.Active,
T roomId = default,
bool withoutTags = false,
IEnumerable<string> tagNames = null,
bool excludeSubject = false,
@ -329,7 +330,7 @@ public class FileStorageService<T> //: IFileStorageService
IEnumerable<FileEntry> entries;
try
{
(entries, total) = await _entryManager.GetEntriesAsync(parent, from, count, filterType, subjectGroup, subjectId, searchText, searchInContent, withSubfolders, orderBy, searchArea,
(entries, total) = await _entryManager.GetEntriesAsync(parent, from, count, filterType, subjectGroup, subjectId, searchText, searchInContent, withSubfolders, orderBy, roomId, searchArea,
withoutTags, tagNames, excludeSubject, provider, subjectFilter);
}
catch (Exception e)

View File

@ -713,9 +713,8 @@ public class FileSecurity : IFileSecurity
{
return false;
}
var folderDao = _daoFactory.GetFolderDao<T>();
var mytrashId = await folderDao.GetFolderIDTrashAsync(false, userId);
var mytrashId = await _globalFolder.GetFolderTrashAsync(_daoFactory);
if (!Equals(mytrashId, 0) && Equals(e.RootId, mytrashId))
{
return folder == null || action != FilesSecurityActions.Delete || !Equals(e.Id, mytrashId);

View File

@ -575,7 +575,7 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
return ProviderInfo.GetThumbnailAsync(boxFileId, width, height);
}
#region chunking
#region chunking
private File<string> RestoreIds(File<string> file)
{
@ -664,5 +664,5 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
return Task.CompletedTask;
}
#endregion
}
#endregion
}

View File

@ -574,7 +574,7 @@ internal class DropboxFileDao : DropboxDaoBase, IFileDao<string>
return ProviderInfo.GetThumbnailsAsync(_dropboxDaoSelector.ConvertId(fileId), width, height);
}
#region chunking
#region chunking
private File<string> RestoreIds(File<string> file)
{
@ -707,5 +707,5 @@ internal class DropboxFileDao : DropboxDaoBase, IFileDao<string>
return Task.CompletedTask;
}
#endregion
}
#endregion
}

View File

@ -24,7 +24,7 @@
// 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
using DriveFile = Google.Apis.Drive.v3.Data.File;
using DriveFile = Google.Apis.Drive.v3.Data.File;
using File = System.IO.File;
namespace ASC.Files.Thirdparty.GoogleDrive;
@ -578,7 +578,7 @@ internal class GoogleDriveFileDao : GoogleDriveDaoBase, IFileDao<string>
return ProviderInfo.GetThumbnail(MakeDriveId(_googleDriveDaoSelector.ConvertId(fileId)), width, height);
}
#region chunking
#region chunking
private File<string> RestoreIds(File<string> file)
{
@ -733,5 +733,5 @@ internal class GoogleDriveFileDao : GoogleDriveDaoBase, IFileDao<string>
return Task.CompletedTask;
}
#endregion
}
#endregion
}

View File

@ -244,6 +244,11 @@ internal abstract class ThirdPartyProviderDao
throw new NotImplementedException();
}
public IAsyncEnumerable<OriginData> GetOriginsDataAsync(IEnumerable<string> entriesId)
{
throw new NotImplementedException();
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText,
bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
@ -719,6 +724,11 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
return Task.CompletedTask;
}
public Task<int> RemoveTagLinksAsync(string entryId, FileEntryType entryType, TagType tagType)
{
return Task.FromResult(default(int));
}
public IAsyncEnumerable<Tag> GetTagsAsync(string entryID, FileEntryType entryType, TagType tagType)
{
return AsyncEnumerable.Empty<Tag>();

View File

@ -585,7 +585,7 @@ internal class OneDriveFileDao : OneDriveDaoBase, IFileDao<string>
return ProviderInfo.GetThumbnailAsync(oneDriveId, width, height);
}
#region chunking
#region chunking
private File<string> RestoreIds(File<string> file)
{
@ -730,5 +730,5 @@ internal class OneDriveFileDao : OneDriveDaoBase, IFileDao<string>
System.IO.File.Delete(uploadSession.GetItemOrDefault<string>("TempPath"));
}
}
#endregion
}
#endregion
}

View File

@ -181,12 +181,12 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
return GetFileStreamAsync(file, 0);
}
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="offset"></param>
/// <returns>Stream</returns>
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="offset"></param>
/// <returns>Stream</returns>
public Task<Stream> GetFileStreamAsync(File<string> file, long offset)
{
ArgumentNullException.ThrowIfNull(file);
@ -442,10 +442,10 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
var selector = GetSelector(file.Id);
var fileDao = selector.GetFileDao(file.Id);
return fileDao.UseTrashForRemove(file);
return fileDao.UseTrashForRemove(file);
}
#region chunking
#region chunking
public Task<ChunkedUploadSession<string>> CreateUploadSessionAsync(File<string> file, long contentLength)
{
@ -519,5 +519,5 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
return fileDao.GetThumbnailAsync(file, width, height);
}
#endregion
}
#endregion
}

View File

@ -56,7 +56,7 @@ internal class ProviderTagDao : ProviderDaoBase, ITagDao<string>
.GetNewTagsAsync(subject, parentFolder, deepSearch);
}
#region Only for Teamlab Documents
#region Only for Teamlab Documents
public IAsyncEnumerable<Tag> GetNewTagsAsync(Guid subject, IEnumerable<FileEntry<string>> fileEntries)
{
@ -118,6 +118,11 @@ internal class ProviderTagDao : ProviderDaoBase, ITagDao<string>
await _tagDao.RemoveTags(tag);
}
public async Task<int> RemoveTagLinksAsync(string entryId, FileEntryType entryType, TagType tagType)
{
return await _tagDao.RemoveTagLinksAsync(entryId, entryType, tagType);
}
public IAsyncEnumerable<Tag> GetTagsAsync(string entryID, FileEntryType entryType, TagType tagType)
{
return _tagDao.GetTagsAsync(entryID, entryType, tagType);
@ -148,5 +153,5 @@ internal class ProviderTagDao : ProviderDaoBase, ITagDao<string>
return _tagDao.RemoveTagsAsync(tagsIds);
}
#endregion
}
#endregion
}

Some files were not shown because too many files have changed in this diff Show More