Merge branch 'feature/workspaces' of github.com:ONLYOFFICE/AppServer into feature/workspaces
This commit is contained in:
commit
9a791c61ad
@ -4,10 +4,7 @@ import Backdrop from "@appserver/components/backdrop";
|
||||
//import ProgressBar from "@appserver/components/progress-bar";
|
||||
import { size } from "@appserver/components/utils/device";
|
||||
import { Provider } from "@appserver/components/utils/context";
|
||||
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import i18n from "./i18n";
|
||||
import Article from "./sub-components/article";
|
||||
import SubArticleHeader from "./sub-components/article-header";
|
||||
import SubArticleMainButton from "./sub-components/article-main-button";
|
||||
@ -20,7 +17,6 @@ import SubSectionBody from "./sub-components/section-body";
|
||||
import SubSectionBodyContent from "./sub-components/section-body-content";
|
||||
import SubSectionPaging from "./sub-components/section-paging";
|
||||
import SectionToggler from "./sub-components/section-toggler";
|
||||
import { changeLanguage } from "../../utils";
|
||||
import ReactResizeDetector from "react-resize-detector";
|
||||
import FloatingButton from "../FloatingButton";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -60,7 +56,7 @@ function SectionPaging() {
|
||||
}
|
||||
SectionPaging.displayName = "SectionPaging";
|
||||
|
||||
class PageLayoutComponent extends React.Component {
|
||||
class PageLayout extends React.Component {
|
||||
static ArticleHeader = ArticleHeader;
|
||||
static ArticleMainButton = ArticleMainButton;
|
||||
static ArticleBody = ArticleBody;
|
||||
@ -282,9 +278,7 @@ class PageLayoutComponent extends React.Component {
|
||||
{isArticleBodyAvailable && (
|
||||
<ArticlePinPanel
|
||||
pinned={this.state.isArticlePinned}
|
||||
pinText={this.props.t("Pin")}
|
||||
onPin={this.pinArticle}
|
||||
unpinText={this.props.t("Unpin")}
|
||||
onUnpin={this.unpinArticle}
|
||||
/>
|
||||
)}
|
||||
@ -410,11 +404,10 @@ class PageLayoutComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
PageLayoutComponent.propTypes = {
|
||||
PageLayout.propTypes = {
|
||||
children: PropTypes.any,
|
||||
withBodyScroll: PropTypes.bool,
|
||||
withBodyAutoFocus: PropTypes.bool,
|
||||
t: PropTypes.func,
|
||||
showPrimaryProgressBar: PropTypes.bool,
|
||||
primaryProgressBarValue: PropTypes.number,
|
||||
showPrimaryButtonAlert: PropTypes.bool,
|
||||
@ -437,21 +430,11 @@ PageLayoutComponent.propTypes = {
|
||||
firstLoad: PropTypes.bool,
|
||||
};
|
||||
|
||||
PageLayoutComponent.defaultProps = {
|
||||
PageLayout.defaultProps = {
|
||||
withBodyScroll: true,
|
||||
withBodyAutoFocus: false,
|
||||
};
|
||||
|
||||
const PageLayoutTranslated = withTranslation()(PageLayoutComponent);
|
||||
|
||||
const PageLayout = ({ language, ...rest }) => {
|
||||
useEffect(() => {
|
||||
changeLanguage(i18n, language);
|
||||
}, [language]);
|
||||
|
||||
return <PageLayoutTranslated i18n={i18n} {...rest} />;
|
||||
};
|
||||
|
||||
PageLayout.ArticleHeader = ArticleHeader;
|
||||
PageLayout.ArticleMainButton = ArticleMainButton;
|
||||
PageLayout.ArticleBody = ArticleBody;
|
||||
@ -460,13 +443,8 @@ PageLayout.SectionFilter = SectionFilter;
|
||||
PageLayout.SectionBody = SectionBody;
|
||||
PageLayout.SectionPaging = SectionPaging;
|
||||
|
||||
PageLayout.propTypes = {
|
||||
language: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const { isLoaded, language, settingsStore } = auth;
|
||||
const { isLoaded, settingsStore } = auth;
|
||||
const {
|
||||
isHeaderVisible,
|
||||
isTabletView,
|
||||
@ -475,7 +453,6 @@ export default inject(({ auth }) => {
|
||||
} = settingsStore;
|
||||
return {
|
||||
isLoaded,
|
||||
language,
|
||||
isTabletView,
|
||||
isHeaderVisible,
|
||||
isArticlePinned,
|
||||
|
@ -7,6 +7,7 @@ import { tablet, smallTablet } from "@appserver/components/utils/device";
|
||||
import CatalogPinIcon from "../../../../../public/images/catalog.pin.react.svg";
|
||||
import CatalogUnpinIcon from "../../../../../public/images/catalog.unpin.react.svg";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import i18n from "../i18n";
|
||||
|
||||
const StyledCatalogPinIcon = styled(CatalogPinIcon)`
|
||||
${commonIconsStyles}
|
||||
@ -60,7 +61,8 @@ const StyledArticlePinPanel = styled.div`
|
||||
|
||||
const ArticlePinPanel = React.memo((props) => {
|
||||
//console.log("PageLayout ArticlePinPanel render");
|
||||
const { pinned, pinText, onPin, unpinText, onUnpin } = props;
|
||||
|
||||
const { pinned, onPin, onUnpin } = props;
|
||||
const textStyles = {
|
||||
as: "span",
|
||||
color: "#555F65",
|
||||
@ -75,14 +77,14 @@ const ArticlePinPanel = React.memo((props) => {
|
||||
<div className="icon-wrapper">
|
||||
<StyledCatalogUnpinIcon size="scale" />
|
||||
</div>
|
||||
<Text {...textStyles}>{unpinText}</Text>
|
||||
<Text {...textStyles}>{i18n.t("Unpin")}</Text>
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={onPin}>
|
||||
<div className="icon-wrapper">
|
||||
<StyledCatalogPinIcon size="scale" />
|
||||
</div>
|
||||
<Text {...textStyles}>{pinText}</Text>
|
||||
<Text {...textStyles}>{i18n.t("Pin")}</Text>
|
||||
</div>
|
||||
)}
|
||||
</StyledArticlePinPanel>
|
||||
|
@ -158,7 +158,7 @@ class AuthStore {
|
||||
logout = async (withoutRedirect) => {
|
||||
const response = await api.user.logout();
|
||||
|
||||
console.log("Logout response ", response);
|
||||
//console.log("Logout response ", response);
|
||||
|
||||
setWithCredentialsStatus(false);
|
||||
|
||||
|
@ -10,7 +10,7 @@ const Icon = ({ size, primary, icon, isHovered }) => (
|
||||
<div className="btnIcon">
|
||||
{icon &&
|
||||
React.cloneElement(icon, {
|
||||
isfill: true,
|
||||
//isfill: true,
|
||||
size: size === "large" ? "large" : size === "big" ? "medium" : "small",
|
||||
color: icon.props.color
|
||||
? isHovered
|
||||
|
@ -22,7 +22,8 @@ import treeFoldersStore from "./store/TreeFoldersStore";
|
||||
import selectedFolderStore from "./store/SelectedFolderStore";
|
||||
import filesActionsStore from "./store/FilesActionsStore";
|
||||
import "./custom.scss";
|
||||
import "./i18n";
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
//import { regDesktop } from "@appserver/common/src/desktop";
|
||||
|
||||
const Error404 = React.lazy(() => import("studio/Error404"));
|
||||
@ -135,6 +136,8 @@ export default () => (
|
||||
selectedFolderStore={selectedFolderStore}
|
||||
filesActionsStore={filesActionsStore}
|
||||
>
|
||||
<Files />
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Files />
|
||||
</I18nextProvider>
|
||||
</FilesProvider>
|
||||
);
|
||||
|
@ -2,8 +2,7 @@ import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import Link from "@appserver/components/link";
|
||||
import history from "@appserver/common/history";
|
||||
import { changeLanguage } from "@appserver/common/utils";
|
||||
import { withTranslation, I18nextProvider } from "react-i18next";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
@ -115,8 +115,8 @@ class FilesRowContent extends React.PureComponent {
|
||||
super(props);
|
||||
let titleWithoutExt = getTitleWithoutExst(props.item);
|
||||
|
||||
if (props.fileAction.id === -1) {
|
||||
titleWithoutExt = this.getDefaultName(props.fileAction.extension);
|
||||
if (props.fileActionId === -1) {
|
||||
titleWithoutExt = this.getDefaultName(props.fileActionExt);
|
||||
}
|
||||
|
||||
this.state = {
|
||||
@ -129,70 +129,18 @@ class FilesRowContent extends React.PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
onSelectItem = (item) => {
|
||||
const { selected, setSelected, setSelection } = this.props;
|
||||
selected === "close" && setSelected("none");
|
||||
setSelection([item]);
|
||||
};
|
||||
|
||||
//TODO: move to actions, used in files row content and tile
|
||||
onEditComplete = (id, isFolder) => {
|
||||
const {
|
||||
selectedFolderId,
|
||||
fileAction,
|
||||
filter,
|
||||
folders,
|
||||
files,
|
||||
treeFolders,
|
||||
setTreeFolders,
|
||||
setIsLoading,
|
||||
fetchFiles,
|
||||
setAction,
|
||||
} = this.props;
|
||||
const selectedItem = this.props.item;
|
||||
const items = [...folders, ...files];
|
||||
const item = items.find((o) => o.id === id && !o.fileExst); //TODO maybe need files find and folders find, not at one function?
|
||||
if (
|
||||
fileAction.type === FileAction.Create ||
|
||||
fileAction.type === FileAction.Rename
|
||||
) {
|
||||
setIsLoading(true);
|
||||
fetchFiles(selectedFolderId, filter)
|
||||
.then((data) => {
|
||||
const newItem = (item && item.id) === -1 ? null : item; //TODO not add new folders?
|
||||
if (isFolder) {
|
||||
const path = data.selectedFolder.pathParts;
|
||||
const newTreeFolders = treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
loopTreeFolders(path, newTreeFolders, folders, null, newItem);
|
||||
setTreeFolders(newTreeFolders);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setAction({ type: null, id: null, extension: null });
|
||||
setIsLoading(false);
|
||||
|
||||
fileAction.type === FileAction.Rename &&
|
||||
this.onSelectItem(selectedItem);
|
||||
});
|
||||
}
|
||||
|
||||
//this.setState({ editingId: null }, () => {
|
||||
// setAction({type: null});
|
||||
//});
|
||||
};
|
||||
|
||||
completeAction = (id) => {
|
||||
this.onEditComplete(id, !this.props.item.fileExst);
|
||||
const { item } = this.props;
|
||||
this.props.editCompleteAction(id, !item.fileExst, /* item */);
|
||||
};
|
||||
|
||||
updateItem = (e) => {
|
||||
updateItem = () => {
|
||||
const {
|
||||
fileAction,
|
||||
updateFile,
|
||||
renameFolder,
|
||||
item,
|
||||
setIsLoading,
|
||||
fileActionId,
|
||||
} = this.props;
|
||||
|
||||
const { itemTitle } = this.state;
|
||||
@ -203,15 +151,15 @@ class FilesRowContent extends React.PureComponent {
|
||||
this.setState({
|
||||
itemTitle: originalTitle,
|
||||
});
|
||||
return this.completeAction(fileAction.id);
|
||||
return this.completeAction(fileActionId);
|
||||
}
|
||||
|
||||
item.fileExst
|
||||
? updateFile(fileAction.id, itemTitle)
|
||||
.then(() => this.completeAction(fileAction.id))
|
||||
? updateFile(fileActionId, itemTitle)
|
||||
.then(() => this.completeAction(fileActionId))
|
||||
.finally(() => setIsLoading(false))
|
||||
: renameFolder(fileAction.id, itemTitle)
|
||||
.then(() => this.completeAction(fileAction.id))
|
||||
: renameFolder(fileActionId, itemTitle)
|
||||
.then(() => this.completeAction(fileActionId))
|
||||
.finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
@ -292,7 +240,7 @@ class FilesRowContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
// componentDidUpdate(prevProps) {
|
||||
// const { fileAction, item, newRowItems, setNewRowItems } = this.props;
|
||||
// const { item, newRowItems, setNewRowItems } = this.props;
|
||||
// const itemId = item.id.toString();
|
||||
|
||||
// if (newRowItems.length && newRowItems.includes(itemId)) {
|
||||
@ -303,8 +251,8 @@ class FilesRowContent extends React.PureComponent {
|
||||
// }
|
||||
|
||||
// if (fileAction) {
|
||||
// if (fileAction.id !== prevProps.fileAction.id) {
|
||||
// this.setState({ editingId: fileAction.id });
|
||||
// if (fileActionId !== prevProps.fileActionId) {
|
||||
// this.setState({ editingId: fileActionId });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@ -330,7 +278,7 @@ class FilesRowContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
onClickUpdateItem = (e) => {
|
||||
this.props.fileAction.type === FileAction.Create
|
||||
this.props.fileActionType === FileAction.Create
|
||||
? this.createItem(e)
|
||||
: this.updateItem(e);
|
||||
};
|
||||
@ -546,14 +494,14 @@ class FilesRowContent extends React.PureComponent {
|
||||
const {
|
||||
t,
|
||||
item,
|
||||
fileAction,
|
||||
isTrashFolder,
|
||||
folders,
|
||||
isLoading,
|
||||
isMobile,
|
||||
canWebEdit,
|
||||
/* canConvert,*/
|
||||
sectionWidth,
|
||||
fileActionId,
|
||||
fileActionExt,
|
||||
} = this.props;
|
||||
const {
|
||||
itemTitle,
|
||||
@ -585,7 +533,7 @@ class FilesRowContent extends React.PureComponent {
|
||||
const accessToEdit =
|
||||
item.access === ShareAccessRights.FullAccess ||
|
||||
item.access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
|
||||
const isEdit = id === fileAction.id && fileExst === fileAction.extension;
|
||||
const isEdit = id === fileActionId && fileExst === fileActionExt;
|
||||
|
||||
const linkStyles =
|
||||
isTrashFolder || window.innerWidth <= 1024
|
||||
@ -618,7 +566,6 @@ class FilesRowContent extends React.PureComponent {
|
||||
visible={showNewFilesPanel}
|
||||
onClose={this.onShowNewFilesPanel}
|
||||
folderId={newFolderId}
|
||||
folders={folders}
|
||||
/>
|
||||
)}
|
||||
<SimpleFilesRowContent
|
||||
@ -818,14 +765,11 @@ export default inject(
|
||||
uploadDataStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
filesActionsStore,
|
||||
},
|
||||
{ item }
|
||||
) => {
|
||||
const {
|
||||
replaceFileStream,
|
||||
getEncryptionAccess,
|
||||
setEncryptionAccess,
|
||||
} = auth;
|
||||
const { replaceFileStream, setEncryptionAccess } = auth;
|
||||
const { homepage, culture, isDesktopClient } = auth.settingsStore;
|
||||
const { setIsLoading, isLoading } = initFilesStore;
|
||||
const { secondaryProgressDataStore } = uploadDataStore;
|
||||
@ -836,8 +780,6 @@ export default inject(
|
||||
} = formatsStore;
|
||||
|
||||
const {
|
||||
files,
|
||||
folders,
|
||||
fetchFiles,
|
||||
filter,
|
||||
setNewRowItems,
|
||||
@ -847,9 +789,6 @@ export default inject(
|
||||
renameFolder,
|
||||
createFolder,
|
||||
openDocEditor,
|
||||
selected,
|
||||
setSelected,
|
||||
setSelection
|
||||
} = filesStore;
|
||||
|
||||
const {
|
||||
@ -861,9 +800,12 @@ export default inject(
|
||||
addExpandedKeys,
|
||||
} = treeFoldersStore;
|
||||
|
||||
const { type, extension, id, setAction } = filesStore.fileActionStore;
|
||||
const {
|
||||
type: fileActionType,
|
||||
extension: fileActionExt,
|
||||
id: fileActionId,
|
||||
} = filesStore.fileActionStore;
|
||||
|
||||
const fileAction = { type, extension, id };
|
||||
const {
|
||||
setSecondaryProgressBarData,
|
||||
clearSecondaryProgressData,
|
||||
@ -880,9 +822,9 @@ export default inject(
|
||||
homepage,
|
||||
viewer: auth.userStore.user,
|
||||
culture,
|
||||
fileAction,
|
||||
files,
|
||||
folders,
|
||||
fileActionId,
|
||||
fileActionType,
|
||||
fileActionExt,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
selectedFolderPathParts: selectedFolderStore.pathParts,
|
||||
newItems: selectedFolderStore.new,
|
||||
@ -899,7 +841,6 @@ export default inject(
|
||||
isSound,
|
||||
newRowItems,
|
||||
expandedKeys,
|
||||
selected,
|
||||
|
||||
setIsLoading,
|
||||
fetchFiles,
|
||||
@ -912,13 +853,10 @@ export default inject(
|
||||
updateFile,
|
||||
renameFolder,
|
||||
replaceFileStream,
|
||||
getEncryptionAccess,
|
||||
setEncryptionAccess,
|
||||
addExpandedKeys,
|
||||
openDocEditor,
|
||||
setAction,
|
||||
setSelected,
|
||||
setSelection
|
||||
editCompleteAction: filesActionsStore.editCompleteAction,
|
||||
};
|
||||
}
|
||||
)(withRouter(withTranslation("Home")(observer(FilesRowContent))));
|
||||
|
@ -10,6 +10,7 @@ import Row from "@appserver/components/row";
|
||||
import FilesRowContent from "./FilesRowContent";
|
||||
import history from "@appserver/common/history";
|
||||
import toastr from "@appserver/components/toast";
|
||||
import { FileAction } from "@appserver/common/constants";
|
||||
|
||||
import { lockFile, finalizeVersion } from "@appserver/common/api/files"; //TODO: move to actions
|
||||
|
||||
@ -88,6 +89,7 @@ const SimpleFilesRow = (props) => {
|
||||
isTabletView,
|
||||
filter,
|
||||
selectedFolderId,
|
||||
actionId,
|
||||
|
||||
fetchFiles,
|
||||
setSharingPanelVisible,
|
||||
@ -108,7 +110,9 @@ const SimpleFilesRow = (props) => {
|
||||
removeItemFromFavorite,
|
||||
getFileInfo,
|
||||
fetchFavoritesFolder,
|
||||
actionId,
|
||||
copyToAction,
|
||||
deleteFileAction,
|
||||
deleteFolderAction,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
@ -289,14 +293,13 @@ const SimpleFilesRow = (props) => {
|
||||
alert: false,
|
||||
});
|
||||
|
||||
//TODO: need add to action
|
||||
// this.copyTo(
|
||||
// selectedFolderId,
|
||||
// folderIds,
|
||||
// fileIds,
|
||||
// conflictResolveType,
|
||||
// deleteAfter
|
||||
// );
|
||||
copyToAction(
|
||||
selectedFolderId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
conflictResolveType,
|
||||
deleteAfter
|
||||
);
|
||||
};
|
||||
|
||||
const onClickRename = () => {
|
||||
@ -320,18 +323,22 @@ const SimpleFilesRow = (props) => {
|
||||
};
|
||||
|
||||
const onClickDelete = () => {
|
||||
const splitItem = id.split("-");
|
||||
|
||||
if (isThirdPartyFolder) {
|
||||
const splitItem = id.split("-");
|
||||
setRemoveItem({ id: splitItem[splitItem.length - 1], title });
|
||||
showDeleteThirdPartyDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = this.props.selection[0];
|
||||
const translations = {
|
||||
deleteOperation: t("DeleteOperation"),
|
||||
folderRemoved: t("FolderRemoved"),
|
||||
fileRemoved: t("FileRemoved"),
|
||||
};
|
||||
|
||||
item.fileExst
|
||||
? this.onDeleteFile(item.id, item.folderId)
|
||||
: this.onDeleteFolder(item.id, item.parentId);
|
||||
? deleteFileAction(item.id, item.folderId, translations)
|
||||
: deleteFolderAction(item.id, item.parentId, translations);
|
||||
};
|
||||
|
||||
const getFilesContextOptions = (options, item) => {
|
||||
@ -594,6 +601,7 @@ export default inject(
|
||||
settingsStore,
|
||||
versionHistoryStore,
|
||||
uploadDataStore,
|
||||
filesActionsStore,
|
||||
},
|
||||
{ item }
|
||||
) => {
|
||||
@ -695,6 +703,19 @@ export default inject(
|
||||
removeItemFromFavorite,
|
||||
getFileInfo,
|
||||
fetchFavoritesFolder,
|
||||
copyToAction: filesActionsStore.copyToAction,
|
||||
deleteFileAction: filesActionsStore.deleteFileAction,
|
||||
deleteFolderAction: filesActionsStore.deleteFolderAction,
|
||||
};
|
||||
}
|
||||
)(withTranslation()(observer(SimpleFilesRow)));
|
||||
|
||||
// onDrop = (item, items, e) => {
|
||||
// const { onDropZoneUpload, selectedFolderId } = this.props;
|
||||
|
||||
// if (!item.fileExst) {
|
||||
// onDropZoneUpload(items, item.id);
|
||||
// } else {
|
||||
// onDropZoneUpload(items, selectedFolderId);
|
||||
// }
|
||||
// };
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,59 +1,39 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import Backend from "i18next-http-backend";
|
||||
import config from "../package.json";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
//const { LANGUAGE /*i18nBaseSettings*/ } = constants;
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
i18n
|
||||
/*
|
||||
load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
|
||||
learn more: https://github.com/i18next/i18next-http-backend
|
||||
*/
|
||||
.use(Backend)
|
||||
/*
|
||||
detect user language
|
||||
learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
*/
|
||||
//.use(LanguageDetector)
|
||||
/*
|
||||
pass the i18n instance to react-i18next.
|
||||
*/
|
||||
.use(initReactI18next)
|
||||
/*
|
||||
init i18next
|
||||
for all options read: https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
.init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
const lng = localStorage.getItem(LANGUAGE) || "en";
|
||||
|
||||
newInstance.use(Backend).init({
|
||||
lng: lng,
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: false,
|
||||
load: "languageOnly",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/{{lng}}/{{ns}}.json`,
|
||||
allowMultiLoading: false,
|
||||
crossDomain: false,
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
react: {
|
||||
useSuspense: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
export default newInstance;
|
||||
|
@ -1,16 +1,32 @@
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import store from "studio/store";
|
||||
import dialogsStore from "./DialogsStore";
|
||||
import uploadDataStore from "./UploadDataStore";
|
||||
import treeFoldersStore from "./TreeFoldersStore";
|
||||
import filesStore from "./FilesStore";
|
||||
import selectedFolderStore from "./SelectedFolderStore";
|
||||
import { removeFiles, getProgress } from "@appserver/common/api/files";
|
||||
import initFilesStore from "./InitFilesStore";
|
||||
|
||||
import {
|
||||
removeFiles,
|
||||
getProgress,
|
||||
copyToFolder,
|
||||
deleteFile,
|
||||
deleteFolder,
|
||||
moveToFolder,
|
||||
} from "@appserver/common/api/files";
|
||||
import { FileAction } from "@appserver/common/constants";
|
||||
import { TIMEOUT } from "../helpers/constants";
|
||||
import { loopTreeFolders } from "../helpers/files-helpers";
|
||||
|
||||
const { confirmDelete } = store.auth.settingsStore;
|
||||
const { secondaryProgressDataStore } = uploadDataStore;
|
||||
//import toastr from "@appserver/components/toast";
|
||||
|
||||
const { fetchFiles } = filesStore;
|
||||
const { setTreeFolders } = treeFoldersStore;
|
||||
const { setIsLoading } = initFilesStore;
|
||||
const { secondaryProgressDataStore, loopFilesOperations } = uploadDataStore;
|
||||
const {
|
||||
setSecondaryProgressBarData,
|
||||
clearSecondaryProgressData,
|
||||
} = secondaryProgressDataStore;
|
||||
|
||||
class FilesActionStore {
|
||||
constructor() {
|
||||
@ -18,73 +34,6 @@ class FilesActionStore {
|
||||
}
|
||||
|
||||
deleteAction = (translations) => {
|
||||
if (confirmDelete) {
|
||||
dialogsStore.setDeleteDialogVisible(false);
|
||||
} else {
|
||||
return this.onDelete(translations);
|
||||
}
|
||||
};
|
||||
|
||||
loopDeleteOperation = (id, translations) => {
|
||||
const { filter, fetchFiles } = filesStore;
|
||||
const isRecycleBin = treeFoldersStore.isRecycleBinFolder;
|
||||
const {
|
||||
setSecondaryProgressBarData,
|
||||
clearSecondaryProgressData,
|
||||
} = secondaryProgressDataStore;
|
||||
|
||||
const successMessage = isRecycleBin
|
||||
? translations.deleteFromTrash
|
||||
: translations.deleteSelectedElem;
|
||||
getProgress()
|
||||
.then((res) => {
|
||||
const currentProcess = res.find((x) => x.id === id);
|
||||
if (currentProcess && currentProcess.progress !== 100) {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
percent: currentProcess.progress,
|
||||
label: translations.deleteOperation,
|
||||
visible: true,
|
||||
alert: false,
|
||||
});
|
||||
setTimeout(() => this.loopDeleteOperation(id, translations), 1000);
|
||||
} else {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
percent: 100,
|
||||
label: translations.deleteOperation,
|
||||
visible: true,
|
||||
alert: false,
|
||||
});
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
fetchFiles(selectedFolderStore.id, filter).then((data) => {
|
||||
if (!isRecycleBin) {
|
||||
const path = data.selectedFolder.pathParts.slice(0);
|
||||
const newTreeFolders = treeFoldersStore.treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
const foldersCount = data.selectedFolder.foldersCount;
|
||||
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
|
||||
treeFoldersStore.setTreeFolders(newTreeFolders);
|
||||
}
|
||||
//toastr.success(successMessage);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
onDelete = (translations) => {
|
||||
const {
|
||||
setSecondaryProgressBarData,
|
||||
clearSecondaryProgressData,
|
||||
} = secondaryProgressDataStore;
|
||||
const { isRecycleBinFolder, isPrivacyFolder } = treeFoldersStore;
|
||||
const { selection } = filesStore;
|
||||
|
||||
@ -129,14 +78,60 @@ class FilesActionStore {
|
||||
}
|
||||
};
|
||||
|
||||
loopDeleteOperation = (id, translations) => {
|
||||
const { filter } = filesStore;
|
||||
const { isRecycleBinFolder } = treeFoldersStore;
|
||||
const successMessage = isRecycleBinFolder
|
||||
? translations.deleteFromTrash
|
||||
: translations.deleteSelectedElem;
|
||||
|
||||
getProgress()
|
||||
.then((res) => {
|
||||
const currentProcess = res.find((x) => x.id === id);
|
||||
if (currentProcess && currentProcess.progress !== 100) {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
percent: currentProcess.progress,
|
||||
label: translations.deleteOperation,
|
||||
visible: true,
|
||||
alert: false,
|
||||
});
|
||||
setTimeout(() => this.loopDeleteOperation(id, translations), 1000);
|
||||
} else {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
percent: 100,
|
||||
label: translations.deleteOperation,
|
||||
visible: true,
|
||||
alert: false,
|
||||
});
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
fetchFiles(selectedFolderStore.id, filter).then((data) => {
|
||||
if (!isRecycleBinFolder) {
|
||||
const path = data.selectedFolder.pathParts.slice(0);
|
||||
const newTreeFolders = treeFoldersStore.treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
const foldersCount = data.selectedFolder.foldersCount;
|
||||
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
|
||||
setTreeFolders(newTreeFolders);
|
||||
}
|
||||
//toastr.success(successMessage);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
getDownloadProgress = (data, label) => {
|
||||
const url = data.url;
|
||||
|
||||
const {
|
||||
setSecondaryProgressBarData,
|
||||
clearSecondaryProgressData,
|
||||
} = secondaryProgressDataStore;
|
||||
|
||||
getProgress()
|
||||
.then((res) => {
|
||||
const currentItem = res.find((x) => x.id === data.id);
|
||||
@ -163,6 +158,203 @@ class FilesActionStore {
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
editCompleteAction = (id, isFolder /* selectedItem */) => {
|
||||
const { filter, folders, files, fileActionStore } = filesStore;
|
||||
const { type, setAction } = fileActionStore;
|
||||
const { treeFolders } = treeFoldersStore;
|
||||
|
||||
const items = [...folders, ...files];
|
||||
const item = items.find((o) => o.id === id && !o.fileExst); //TODO: maybe need files find and folders find, not at one function?
|
||||
if (type === FileAction.Create || type === FileAction.Rename) {
|
||||
setIsLoading(true);
|
||||
fetchFiles(selectedFolderStore.id, filter)
|
||||
.then((data) => {
|
||||
const newItem = (item && item.id) === -1 ? null : item; //TODO: not add new folders?
|
||||
if (isFolder) {
|
||||
const path = data.selectedFolder.pathParts;
|
||||
const newTreeFolders = treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
loopTreeFolders(path, newTreeFolders, folders, null, newItem);
|
||||
setTreeFolders(newTreeFolders);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setAction({ type: null, id: null, extension: null });
|
||||
setIsLoading(false);
|
||||
|
||||
//uncomment if need to select item
|
||||
//type === FileAction.Rename && this.onSelectItem(selectedItem);
|
||||
|
||||
// onSelectItem = (item) => {
|
||||
// const { selected, setSelected, setSelection } = this.props;
|
||||
// selected === "close" && setSelected("none");
|
||||
// setSelection([item]);
|
||||
// };
|
||||
});
|
||||
}
|
||||
|
||||
//this.setState({ editingId: null }, () => {
|
||||
// setAction({type: null});
|
||||
//});
|
||||
};
|
||||
|
||||
copyToAction = (
|
||||
destFolderId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
conflictResolveType,
|
||||
deleteAfter
|
||||
) => {
|
||||
copyToFolder(
|
||||
destFolderId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
conflictResolveType,
|
||||
deleteAfter
|
||||
)
|
||||
.then((res) => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
loopFilesOperations(id, destFolderId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
moveToAction = (
|
||||
destFolderId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
conflictResolveType,
|
||||
deleteAfter
|
||||
) => {
|
||||
moveToFolder(
|
||||
destFolderId,
|
||||
folderIds,
|
||||
fileIds,
|
||||
conflictResolveType,
|
||||
deleteAfter
|
||||
)
|
||||
.then((res) => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
loopFilesOperations(id, destFolderId, false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
deleteFileAction = (fileId, currentFolderId, translations) => {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
visible: true,
|
||||
percent: 0,
|
||||
label: translations.deleteOperation,
|
||||
alert: false,
|
||||
});
|
||||
deleteFile(fileId)
|
||||
.then((res) => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
this.loopDeleteProgress(id, currentFolderId, false, translations);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
deleteFolderAction = (folderId, currentFolderId, translations) => {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
visible: true,
|
||||
percent: 0,
|
||||
label: translations.deleteOperation,
|
||||
alert: false,
|
||||
});
|
||||
deleteFolder(folderId, currentFolderId)
|
||||
.then((res) => {
|
||||
const id = res[0] && res[0].id ? res[0].id : null;
|
||||
this.loopDeleteProgress(id, currentFolderId, true, translations);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
});
|
||||
};
|
||||
|
||||
loopDeleteProgress = (id, folderId, isFolder, translations) => {
|
||||
const { filter } = filesStore;
|
||||
const { treeFolders, isRecycleBinFolder } = treeFoldersStore;
|
||||
|
||||
getProgress().then((res) => {
|
||||
const deleteProgress = res.find((x) => x.id === id);
|
||||
if (deleteProgress && deleteProgress.progress !== 100) {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
visible: true,
|
||||
percent: deleteProgress.progress,
|
||||
label: translations.deleteOperation,
|
||||
alert: false,
|
||||
});
|
||||
setTimeout(
|
||||
() => this.loopDeleteProgress(id, folderId, isFolder, translations),
|
||||
1000
|
||||
);
|
||||
} else {
|
||||
setSecondaryProgressBarData({
|
||||
icon: "trash",
|
||||
visible: true,
|
||||
percent: 100,
|
||||
label: translations.deleteOperation,
|
||||
alert: false,
|
||||
});
|
||||
fetchFiles(folderId, filter)
|
||||
.then((data) => {
|
||||
if (!isRecycleBinFolder && isFolder) {
|
||||
const path = data.selectedFolder.pathParts.slice(0);
|
||||
const newTreeFolders = treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
const foldersCount = data.selectedFolder.foldersCount;
|
||||
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
|
||||
setTreeFolders(newTreeFolders);
|
||||
}
|
||||
//isFolder
|
||||
// ? toastr.success(translations.folderRemoved)
|
||||
// : toastr.success(translations.fileRemoved);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSecondaryProgressBarData({
|
||||
visible: true,
|
||||
alert: true,
|
||||
});
|
||||
//toastr.error(err);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
})
|
||||
.finally(() =>
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default new FilesActionStore();
|
||||
|
@ -15,7 +15,8 @@ const GroupAction = React.lazy(() => import("./components/pages/GroupAction"));
|
||||
const Reassign = React.lazy(() => import("./components/pages/Reassign"));
|
||||
import config from "../package.json";
|
||||
import "./custom.scss";
|
||||
import "./i18n";
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
|
||||
const Error404 = React.lazy(() => import("studio/Error404"));
|
||||
|
||||
@ -86,8 +87,10 @@ const People = inject(({ auth, peopleStore }) => ({
|
||||
|
||||
const peopleStore = new PeopleStore();
|
||||
|
||||
export default () => (
|
||||
export default (props) => (
|
||||
<PeopleProvider peopleStore={peopleStore}>
|
||||
<People />
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<People {...props}/>
|
||||
</I18nextProvider>
|
||||
</PeopleProvider>
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
//import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import MainButton from "@appserver/components/main-button";
|
||||
import DropDownItem from "@appserver/components/drop-down-item";
|
||||
import InviteDialog from "./../../dialogs/InviteDialog/index";
|
||||
@ -67,17 +66,18 @@ class PureArticleMainButtonContent extends React.Component {
|
||||
<>
|
||||
<MainButton isDisabled={false} isDropdown={true} text={t("Actions")}>
|
||||
<DropDownItem
|
||||
icon="images/add.employee.react.svg"
|
||||
icon={`${homepage}/images/add.employee.react.svg`}
|
||||
label={userCaption}
|
||||
onClick={this.goToEmployeeCreate}
|
||||
/>
|
||||
|
||||
<DropDownItem
|
||||
icon="images/add.guest.react.svg"
|
||||
icon={`${homepage}/images/add.guest.react.svg`}
|
||||
label={guestCaption}
|
||||
onClick={this.goToGuestCreate}
|
||||
/>
|
||||
<DropDownItem
|
||||
icon="images/add.department.react.svg"
|
||||
icon={`${homepage}/images/add.department.react.svg`}
|
||||
label={groupCaption}
|
||||
onClick={this.goToGroupCreate}
|
||||
/>
|
||||
@ -94,7 +94,7 @@ class PureArticleMainButtonContent extends React.Component {
|
||||
/> */}
|
||||
{false && (
|
||||
<DropDownItem
|
||||
icon="images/import.react.svg"
|
||||
icon={`${homepage}/images/import.react.svg`}
|
||||
label={t("ImportPeople")}
|
||||
onClick={this.onDropDownItemClick.bind(
|
||||
this,
|
||||
|
@ -16,7 +16,7 @@ import RectangleLoader from "@appserver/common/components/Loaders/RectangleLoade
|
||||
import { updateTempContent } from "@appserver/common/utils";
|
||||
import { Provider as MobxProvider } from "mobx-react";
|
||||
import ThemeProvider from "@appserver/components/theme-provider";
|
||||
import { Base, Dark } from "@appserver/components/themes";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import store from "studio/store";
|
||||
import config from "../package.json";
|
||||
import "./custom.scss";
|
||||
@ -237,28 +237,6 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// const mapStateToProps = (state) => {
|
||||
// const { modules, isLoaded, settings } = state.auth;
|
||||
// const { organizationName } = settings;
|
||||
// return {
|
||||
// modules,
|
||||
// isLoaded,
|
||||
// organizationName,
|
||||
// };
|
||||
// };
|
||||
|
||||
// const mapDispatchToProps = (dispatch) => {
|
||||
// return {
|
||||
// getIsAuthenticated: () => getIsAuthenticated(dispatch),
|
||||
// getPortalSettings: () => getPortalSettings(dispatch),
|
||||
// getUser: () => getUser(dispatch),
|
||||
// getModules: () => getModules(dispatch),
|
||||
// setIsLoaded: () => dispatch(setIsLoaded(true)),
|
||||
// };
|
||||
// };
|
||||
|
||||
// export default connect(mapStateToProps, mapDispatchToProps)(Shell);
|
||||
|
||||
const ShellWrapper = inject(({ auth }) => {
|
||||
const { init, isLoaded } = auth;
|
||||
|
||||
|
39
web/ASC.Web.Client/src/components/pages/About/i18n.js
Normal file
39
web/ASC.Web.Client/src/components/pages/About/i18n.js
Normal file
@ -0,0 +1,39 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-http-backend";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
newInstance.use(Backend).init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: `/locales/{{lng}}/About.json`,
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default newInstance;
|
@ -2,11 +2,12 @@
|
||||
import Text from "@appserver/components/text";
|
||||
import Link from "@appserver/components/link";
|
||||
import PageLayout from "@appserver/common/components/PageLayout";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { I18nextProvider, useTranslation, Trans } from "react-i18next";
|
||||
import version from "../../../../package.json";
|
||||
import styled from "styled-components";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { setDocumentTitle } from "../../../helpers/utils";
|
||||
import i18n from "./i18n";
|
||||
|
||||
const BodyStyle = styled.div`
|
||||
margin-top: ${isMobile ? "80px" : "24px"};
|
||||
@ -191,11 +192,13 @@ const Body = () => {
|
||||
};
|
||||
|
||||
const About = ({ language }) => (
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Body language={language} />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Body language={language} />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
</I18nextProvider>
|
||||
);
|
||||
|
||||
export default About;
|
||||
|
@ -17,6 +17,8 @@ import { isMobile, isIOS } from "react-device-detect";
|
||||
|
||||
import { setDocumentTitle } from "../../../helpers/utils";
|
||||
import { inject } from "mobx-react";
|
||||
import i18n from "../../../i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
|
||||
const commonStyles = `
|
||||
.link-box {
|
||||
@ -228,8 +230,14 @@ ComingSoon.propTypes = {
|
||||
isLoaded: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => ({
|
||||
const ComingSoonWrapper = inject(({ auth }) => ({
|
||||
modules: auth.moduleStore.modules,
|
||||
isLoaded: auth.isLoaded,
|
||||
setCurrentProductId: auth.settingsStore.setCurrentProductId,
|
||||
}))(withRouter(ComingSoon));
|
||||
|
||||
export default (props) => (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<ComingSoonWrapper {...props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
|
@ -53,7 +53,7 @@ const Tiles = ({ modules, isPrimary }) => {
|
||||
(m) => m.isPrimary === isPrimary && m.isolateMode !== true
|
||||
);
|
||||
|
||||
console.log("Tiles", mapped, isPrimary);
|
||||
//console.log("Tiles", mapped, isPrimary);
|
||||
|
||||
return mapped.length > 0 ? (
|
||||
<div className="home-modules">
|
||||
|
@ -4,55 +4,33 @@ import Backend from "i18next-http-backend";
|
||||
import config from "../package.json";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
i18n
|
||||
/*
|
||||
load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
|
||||
learn more: https://github.com/i18next/i18next-http-backend
|
||||
*/
|
||||
.use(Backend)
|
||||
/*
|
||||
detect user language
|
||||
learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
*/
|
||||
//.use(LanguageDetector)
|
||||
/*
|
||||
pass the i18n instance to react-i18next.
|
||||
*/
|
||||
.use(initReactI18next)
|
||||
/*
|
||||
init i18next
|
||||
for all options read: https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
.init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
newInstance.use(Backend).init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
export default newInstance;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { Component, useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import Box from "@appserver/components/box";
|
||||
@ -20,7 +19,8 @@ import { checkPwd } from "@appserver/common/desktop";
|
||||
import { sendInstructionsToChangePassword } from "@appserver/common/api/people";
|
||||
import { createPasswordHash, tryRedirectTo } from "@appserver/common/utils";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import "./i18n";
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider, useTranslation } from "react-i18next";
|
||||
|
||||
const LoginContainer = styled.div`
|
||||
display: flex;
|
||||
@ -115,108 +115,107 @@ const LoginFormWrapper = styled.div`
|
||||
height: calc(100vh-56px);
|
||||
`;
|
||||
|
||||
class Form extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const Form = (props) => {
|
||||
const [identifierValid, setIdentifierValid] = useState(true);
|
||||
const [identifier, setIdentifier] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isDisabled, setIsDisabled] = useState(false);
|
||||
|
||||
this.state = {
|
||||
identifierValid: true,
|
||||
identifier: "",
|
||||
isLoading: false,
|
||||
isDisabled: false,
|
||||
passwordValid: true,
|
||||
password: "",
|
||||
isChecked: false,
|
||||
openDialog: false,
|
||||
email: "",
|
||||
emailError: false,
|
||||
errorText: "",
|
||||
socialButtons: [],
|
||||
};
|
||||
}
|
||||
const [passwordValid, setPasswordValid] = useState(true);
|
||||
const [password, setPassword] = useState("");
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [email, setEmail] = useState("");
|
||||
const [emailError, setEmailError] = useState(false);
|
||||
const [errorText, setErrorText] = useState("");
|
||||
const [socialButtons, setSocialButtons] = useState([]);
|
||||
|
||||
onChangeLogin = (event) => {
|
||||
this.setState({ identifier: event.target.value });
|
||||
!this.state.identifierValid && this.setState({ identifierValid: true });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
const {
|
||||
login,
|
||||
hashSettings,
|
||||
isDesktop,
|
||||
defaultPage,
|
||||
match,
|
||||
organizationName,
|
||||
greetingTitle,
|
||||
} = props;
|
||||
const { error, confirmedEmail } = match.params;
|
||||
const { t } = useTranslation("Login");
|
||||
|
||||
const onChangeLogin = (event) => {
|
||||
setIdentifier(event.target.value);
|
||||
!identifierValid && setIdentifierValid(true);
|
||||
errorText && setErrorText("");
|
||||
};
|
||||
|
||||
onChangePassword = (event) => {
|
||||
this.setState({ password: event.target.value });
|
||||
!this.state.passwordValid && this.setState({ passwordValid: true });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
const onChangePassword = (event) => {
|
||||
setPassword(event.target.value);
|
||||
!passwordValid && setPasswordValid(true);
|
||||
errorText && setErrorText("");
|
||||
};
|
||||
|
||||
onChangeEmail = (event) => {
|
||||
this.setState({ email: event.target.value, emailError: false });
|
||||
const onChangeEmail = (event) => {
|
||||
setEmail(event.target.value);
|
||||
setEmailError(false);
|
||||
};
|
||||
|
||||
onChangeCheckbox = () => this.setState({ isChecked: !this.state.isChecked });
|
||||
const onChangeCheckbox = () => setIsChecked(!isChecked);
|
||||
|
||||
onClick = () => {
|
||||
this.setState({
|
||||
openDialog: true,
|
||||
isDisabled: true,
|
||||
email: this.state.identifier,
|
||||
});
|
||||
const onClick = () => {
|
||||
setOpenDialog(true);
|
||||
setIsDisabled(true);
|
||||
setEmail(identifier);
|
||||
};
|
||||
|
||||
onKeyPress = (event) => {
|
||||
const onKeyPress = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
!this.state.isDisabled
|
||||
? this.onSubmit()
|
||||
: this.onSendPasswordInstructions();
|
||||
!isDisabled ? onSubmit() : onSendPasswordInstructions();
|
||||
}
|
||||
};
|
||||
|
||||
onSendPasswordInstructions = () => {
|
||||
if (!this.state.email.trim()) {
|
||||
this.setState({ emailError: true });
|
||||
const onSendPasswordInstructions = () => {
|
||||
if (!email.trim()) {
|
||||
setEmailError(true);
|
||||
} else {
|
||||
this.setState({ isLoading: true });
|
||||
sendInstructionsToChangePassword(this.state.email)
|
||||
setIsLoading(true);
|
||||
sendInstructionsToChangePassword(email)
|
||||
.then(
|
||||
(res) => toastr.success(res),
|
||||
(message) => toastr.error(message)
|
||||
)
|
||||
.finally(this.onDialogClose());
|
||||
.finally(onDialogClose());
|
||||
}
|
||||
};
|
||||
|
||||
onDialogClose = () => {
|
||||
this.setState({
|
||||
openDialog: false,
|
||||
isDisabled: false,
|
||||
isLoading: false,
|
||||
email: "",
|
||||
emailError: false,
|
||||
});
|
||||
const onDialogClose = () => {
|
||||
setOpenDialog(false);
|
||||
setIsDisabled(false);
|
||||
setIsLoading(false);
|
||||
setEmail("");
|
||||
setEmailError(false);
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
const { errorText, identifier, password } = this.state;
|
||||
const { login, hashSettings, isDesktop, defaultPage } = this.props;
|
||||
|
||||
errorText && this.setState({ errorText: "" });
|
||||
const onSubmit = () => {
|
||||
errorText && setErrorText("");
|
||||
let hasError = false;
|
||||
|
||||
const userName = identifier.trim();
|
||||
|
||||
if (!userName) {
|
||||
hasError = true;
|
||||
this.setState({ identifierValid: !hasError });
|
||||
setIdentifierValid(!hasError);
|
||||
}
|
||||
|
||||
const pass = password.trim();
|
||||
|
||||
if (!pass) {
|
||||
hasError = true;
|
||||
this.setState({ passwordValid: !hasError });
|
||||
setPasswordValid(!hasError);
|
||||
}
|
||||
|
||||
if (hasError) return false;
|
||||
|
||||
this.setState({ isLoading: true });
|
||||
setIsLoading(true);
|
||||
const hash = createPasswordHash(pass, hashSettings);
|
||||
|
||||
isDesktop && checkPwd();
|
||||
@ -224,203 +223,178 @@ class Form extends Component {
|
||||
login(userName, hash)
|
||||
.then(() => tryRedirectTo(defaultPage))
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
errorText: error,
|
||||
identifierValid: !error,
|
||||
passwordValid: !error,
|
||||
isLoading: false,
|
||||
});
|
||||
setErrorText(error);
|
||||
setIdentifierValid(!error);
|
||||
setPasswordValid(!error);
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { match, t, organizationName } = this.props;
|
||||
const { error, confirmedEmail } = match.params;
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${t("Authorization")} – ${organizationName}`; //TODO: implement the setDocumentTitle() utility in ASC.Web.Common
|
||||
|
||||
error && this.setState({ errorText: error });
|
||||
confirmedEmail && this.setState({ identifier: confirmedEmail });
|
||||
window.addEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
error && setErrorText(error);
|
||||
confirmedEmail && setIdentifier(confirmedEmail);
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener("keyup", onKeyPress);
|
||||
};
|
||||
}, []);
|
||||
|
||||
settings = {
|
||||
const settings = {
|
||||
minLength: 6,
|
||||
upperCase: false,
|
||||
digits: false,
|
||||
specSymbols: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { greetingTitle, match, t } = this.props;
|
||||
//console.log("Login render");
|
||||
|
||||
const {
|
||||
identifierValid,
|
||||
identifier,
|
||||
isLoading,
|
||||
passwordValid,
|
||||
password,
|
||||
isChecked,
|
||||
openDialog,
|
||||
email,
|
||||
emailError,
|
||||
errorText,
|
||||
socialButtons,
|
||||
} = this.state;
|
||||
const { confirmedEmail } = match.params;
|
||||
return (
|
||||
<>
|
||||
<LoginContainer>
|
||||
<Text
|
||||
fontSize="32px"
|
||||
fontWeight={600}
|
||||
textAlign="center"
|
||||
className="greeting-title"
|
||||
>
|
||||
{greetingTitle}
|
||||
</Text>
|
||||
|
||||
//console.log("Login render");
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoginContainer>
|
||||
<Text
|
||||
fontSize="32px"
|
||||
fontWeight={600}
|
||||
textAlign="center"
|
||||
className="greeting-title"
|
||||
<form className="auth-form-container">
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
hasError={!identifierValid}
|
||||
errorMessage={errorText ? errorText : t("RequiredFieldMessage")} //TODO: Add wrong login server error
|
||||
>
|
||||
{greetingTitle}
|
||||
</Text>
|
||||
|
||||
<form className="auth-form-container">
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
<TextInput
|
||||
id="login"
|
||||
name="login"
|
||||
hasError={!identifierValid}
|
||||
errorMessage={errorText ? errorText : t("RequiredFieldMessage")} //TODO: Add wrong login server error
|
||||
>
|
||||
<TextInput
|
||||
id="login"
|
||||
name="login"
|
||||
hasError={!identifierValid}
|
||||
value={identifier}
|
||||
placeholder={t("RegistrationEmailWatermark")}
|
||||
size="large"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="username"
|
||||
onChange={this.onChangeLogin}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
value={identifier}
|
||||
placeholder={t("RegistrationEmailWatermark")}
|
||||
size="large"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="username"
|
||||
onChange={onChangeLogin}
|
||||
onKeyDown={onKeyPress}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
hasError={!passwordValid}
|
||||
errorMessage={errorText ? "" : t("RequiredFieldMessage")} //TODO: Add wrong password server error
|
||||
>
|
||||
<PasswordInput
|
||||
simpleView={true}
|
||||
passwordSettings={settings}
|
||||
id="password"
|
||||
inputName="password"
|
||||
placeholder={t("Password")}
|
||||
type="password"
|
||||
hasError={!passwordValid}
|
||||
errorMessage={errorText ? "" : t("RequiredFieldMessage")} //TODO: Add wrong password server error
|
||||
>
|
||||
<PasswordInput
|
||||
simpleView={true}
|
||||
passwordSettings={this.settings}
|
||||
id="password"
|
||||
inputName="password"
|
||||
placeholder={t("Password")}
|
||||
type="password"
|
||||
hasError={!passwordValid}
|
||||
inputValue={password}
|
||||
size="large"
|
||||
scale={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="current-password"
|
||||
onChange={this.onChangePassword}
|
||||
onKeyDown={this.onKeyPress}
|
||||
inputValue={password}
|
||||
size="large"
|
||||
scale={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="current-password"
|
||||
onChange={onChangePassword}
|
||||
onKeyDown={onKeyPress}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<div className="login-forgot-wrapper">
|
||||
<div className="login-checkbox-wrapper">
|
||||
<Checkbox
|
||||
className="login-checkbox"
|
||||
isChecked={isChecked}
|
||||
onChange={onChangeCheckbox}
|
||||
label={<Text fontSize="13px">{t("Remember")}</Text>}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<div className="login-forgot-wrapper">
|
||||
<div className="login-checkbox-wrapper">
|
||||
<Checkbox
|
||||
className="login-checkbox"
|
||||
isChecked={isChecked}
|
||||
onChange={this.onChangeCheckbox}
|
||||
label={<Text fontSize="13px">{t("Remember")}</Text>}
|
||||
/>
|
||||
{/*<HelpButton
|
||||
{/*<HelpButton
|
||||
className="login-tooltip"
|
||||
helpButtonHeaderContent={t("CookieSettingsTitle")}
|
||||
tooltipContent={
|
||||
<Text fontSize="12px">{t("RememberHelper")}</Text>
|
||||
}
|
||||
/>*/}
|
||||
<Link
|
||||
fontSize="13px"
|
||||
color="#316DAA"
|
||||
className="login-link"
|
||||
type="page"
|
||||
isHovered={false}
|
||||
onClick={this.onClick}
|
||||
>
|
||||
{t("ForgotPassword")}
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
fontSize="13px"
|
||||
color="#316DAA"
|
||||
className="login-link"
|
||||
type="page"
|
||||
isHovered={false}
|
||||
onClick={onClick}
|
||||
>
|
||||
{t("ForgotPassword")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{openDialog && (
|
||||
<ForgotPasswordModalDialog
|
||||
openDialog={openDialog}
|
||||
isLoading={isLoading}
|
||||
email={email}
|
||||
emailError={emailError}
|
||||
onChangeEmail={this.onChangeEmail}
|
||||
onSendPasswordInstructions={this.onSendPasswordInstructions}
|
||||
onDialogClose={this.onDialogClose}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
id="button"
|
||||
className="login-button"
|
||||
primary
|
||||
size="large"
|
||||
scale={true}
|
||||
label={isLoading ? t("LoadingProcessing") : t("LoginButton")}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
{openDialog && (
|
||||
<ForgotPasswordModalDialog
|
||||
openDialog={openDialog}
|
||||
isLoading={isLoading}
|
||||
onClick={this.onSubmit}
|
||||
email={email}
|
||||
emailError={emailError}
|
||||
onChangeEmail={onChangeEmail}
|
||||
onSendPasswordInstructions={onSendPasswordInstructions}
|
||||
onDialogClose={onDialogClose}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
|
||||
{confirmedEmail && (
|
||||
<Text isBold={true} fontSize="16px">
|
||||
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
|
||||
</Text>
|
||||
)}
|
||||
{/* TODO: old error indication
|
||||
<Button
|
||||
id="button"
|
||||
className="login-button"
|
||||
primary
|
||||
size="large"
|
||||
scale={true}
|
||||
label={isLoading ? t("LoadingProcessing") : t("LoginButton")}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
|
||||
{confirmedEmail && (
|
||||
<Text isBold={true} fontSize="16px">
|
||||
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
|
||||
</Text>
|
||||
)}
|
||||
{/* TODO: old error indication
|
||||
|
||||
<Text fontSize="14px" color="#c30">
|
||||
{errorText}
|
||||
</Text> */}
|
||||
|
||||
{socialButtons.length ? (
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<div className="login-bottom-border"></div>
|
||||
<Text className="login-bottom-text" color="#A3A9AE">
|
||||
{t("Or")}
|
||||
</Text>
|
||||
<div className="login-bottom-border"></div>
|
||||
</Box>
|
||||
) : null}
|
||||
</form>
|
||||
</LoginContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
{socialButtons.length ? (
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<div className="login-bottom-border"></div>
|
||||
<Text className="login-bottom-text" color="#A3A9AE">
|
||||
{t("Or")}
|
||||
</Text>
|
||||
<div className="login-bottom-border"></div>
|
||||
</Box>
|
||||
) : null}
|
||||
</form>
|
||||
</LoginContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Form.propTypes = {
|
||||
login: PropTypes.func.isRequired,
|
||||
match: PropTypes.object.isRequired,
|
||||
hashSettings: PropTypes.object,
|
||||
greetingTitle: PropTypes.string.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
socialButtons: PropTypes.array,
|
||||
organizationName: PropTypes.string,
|
||||
homepage: PropTypes.string,
|
||||
@ -434,10 +408,6 @@ Form.defaultProps = {
|
||||
email: "",
|
||||
};
|
||||
|
||||
const FormWrapper = withTranslation()(Form);
|
||||
|
||||
//const RegisterWrapper = withTranslation()(Register);
|
||||
|
||||
const LoginForm = (props) => {
|
||||
const { enabledJoin, isDesktop } = props;
|
||||
|
||||
@ -445,7 +415,7 @@ const LoginForm = (props) => {
|
||||
<LoginFormWrapper enabledJoin={enabledJoin} isDesktop={isDesktop}>
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<FormWrapper {...props} />
|
||||
<Form {...props} />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
<Register />
|
||||
@ -459,7 +429,7 @@ LoginForm.propTypes = {
|
||||
isDesktop: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const Login = inject(({ auth }) => {
|
||||
const { settingsStore, isAuthenticated, isLoaded, login } = auth;
|
||||
const {
|
||||
greetingSettings: greetingTitle,
|
||||
@ -482,3 +452,9 @@ export default inject(({ auth }) => {
|
||||
login,
|
||||
};
|
||||
})(withRouter(observer(LoginForm)));
|
||||
|
||||
export default (props) => (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Login {...props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
|
25
web/ASC.Web.Login/src/custom.scss
Normal file
25
web/ASC.Web.Login/src/custom.scss
Normal file
@ -0,0 +1,25 @@
|
||||
// Override default variables before the import
|
||||
//$font-family-base: "Open Sans", sans-serif;
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100%;
|
||||
|
||||
.pageLoader {
|
||||
position: fixed;
|
||||
left: calc(50% - 20px);
|
||||
top: 35%;
|
||||
}
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body.loading * {
|
||||
cursor: wait !important;
|
||||
}
|
@ -4,39 +4,22 @@ import Backend from "i18next-http-backend";
|
||||
import config from "../package.json";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
i18n
|
||||
/*
|
||||
load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
|
||||
learn more: https://github.com/i18next/i18next-http-backend
|
||||
*/
|
||||
.use(Backend)
|
||||
/*
|
||||
detect user language
|
||||
learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
*/
|
||||
//.use(LanguageDetector)
|
||||
/*
|
||||
pass the i18n instance to react-i18next.
|
||||
*/
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
const lng = localStorage.getItem(LANGUAGE) || "en";
|
||||
|
||||
newInstance
|
||||
.use(initReactI18next)
|
||||
/*
|
||||
init i18next
|
||||
for all options read: https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
lng: "ru",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
//whitelist: languages,
|
||||
fallbackLng: "ru",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
@ -48,11 +31,16 @@ i18n
|
||||
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/{{lng}}/{{ns}}.json`,
|
||||
allowMultiLoading: true,
|
||||
crossDomain: false,
|
||||
},
|
||||
|
||||
ns: ["Login"],
|
||||
defaultNS: "Login",
|
||||
|
||||
react: {
|
||||
useSuspense: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
export default newInstance;
|
||||
|
@ -1,3 +0,0 @@
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
@ -8,7 +8,7 @@ import RegisterModalDialog from "./register-modal-dialog";
|
||||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { sendRegisterRequest } from "@appserver/common/api/settings";
|
||||
import { I18nextProvider, withTranslation } from "react-i18next";
|
||||
import { I18nextProvider, useTranslation } from "react-i18next";
|
||||
import i18n from "../i18n";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
@ -26,13 +26,16 @@ const StyledRegister = styled(Box)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const Register = ({ t }) => {
|
||||
const Register = (props) => {
|
||||
const { enabledJoin, isAuthenticated } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [emailErr, setEmailErr] = useState(false);
|
||||
|
||||
const { t } = useTranslation("Login");
|
||||
|
||||
const onRegisterClick = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
@ -64,50 +67,31 @@ const Register = ({ t }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledRegister onClick={onRegisterClick}>
|
||||
<Text color="#316DAA">{t("Register")}</Text>
|
||||
</StyledRegister>
|
||||
enabledJoin &&
|
||||
!isAuthenticated && (
|
||||
<>
|
||||
<StyledRegister onClick={onRegisterClick}>
|
||||
<Text color="#316DAA">{t("Register")}</Text>
|
||||
</StyledRegister>
|
||||
|
||||
{visible && (
|
||||
<RegisterModalDialog
|
||||
visible={visible}
|
||||
loading={loading}
|
||||
email={email}
|
||||
emailErr={emailErr}
|
||||
t={t}
|
||||
onChangeEmail={onChangeEmail}
|
||||
onRegisterModalClose={onRegisterModalClose}
|
||||
onSendRegisterRequest={onSendRegisterRequest}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
{visible && (
|
||||
<RegisterModalDialog
|
||||
visible={visible}
|
||||
loading={loading}
|
||||
email={email}
|
||||
emailErr={emailErr}
|
||||
t={t}
|
||||
onChangeEmail={onChangeEmail}
|
||||
onRegisterModalClose={onRegisterModalClose}
|
||||
onSendRegisterRequest={onSendRegisterRequest}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
Register.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const RegisterTranslationWrapper = withTranslation()(Register);
|
||||
|
||||
const RegisterWrapper = (props) => {
|
||||
const { language, isAuthenticated, enabledJoin } = props;
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language);
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
{enabledJoin && !isAuthenticated && (
|
||||
<RegisterTranslationWrapper {...props} />
|
||||
)}
|
||||
</I18nextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
RegisterWrapper.propTypes = {
|
||||
language: PropTypes.string,
|
||||
isAuthenticated: PropTypes.bool,
|
||||
enabledJoin: PropTypes.bool,
|
||||
@ -121,4 +105,4 @@ export default inject(({ auth }) => {
|
||||
isAuthenticated,
|
||||
language,
|
||||
};
|
||||
})(observer(RegisterWrapper));
|
||||
})(observer(Register));
|
||||
|
@ -77,8 +77,15 @@ var config = {
|
||||
},
|
||||
{ test: /\.json$/, loader: "json-loader" },
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ["style-loader", "css-loader"],
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: [
|
||||
// Creates `style` nodes from JS strings
|
||||
"style-loader",
|
||||
// Translates CSS into CommonJS
|
||||
"css-loader",
|
||||
// Compiles Sass to CSS
|
||||
"sass-loader",
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
|
53
yarn.lock
53
yarn.lock
@ -5818,9 +5818,9 @@ caniuse-api@^3.0.0:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001181:
|
||||
version "1.0.30001194"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001194.tgz#3d16ff3d734a5a7d9818402c28b1f636c5be5bed"
|
||||
integrity sha512-iDUOH+oFeBYk5XawYsPtsx/8fFpndAPUQJC7gBTfxHM8xw5nOZv7ceAD4frS1MKCLUac7QL5wdAJiFQlDRjXlA==
|
||||
version "1.0.30001196"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001196.tgz#00518a2044b1abf3e0df31fadbe5ed90b63f4e64"
|
||||
integrity sha512-CPvObjD3ovWrNBaXlAIGWmg2gQQuJ5YhuciUOjPRox6hIQttu8O+b51dx6VIpIY9ESd2d0Vac1RKpICdG4rGUg==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
@ -7584,9 +7584,9 @@ ejs@^3.1.2:
|
||||
jake "^10.6.1"
|
||||
|
||||
electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.649:
|
||||
version "1.3.678"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.678.tgz#c7c6960463167126b7ed076fade14cac6223bfff"
|
||||
integrity sha512-E5ha1pE9+aWWrT2fUD5wdPBWUnYtKnEnloewbtVyrkAs79HvodOiNO4rMR94+hKbxgMFQG4fnPQACOc1cfMfBg==
|
||||
version "1.3.680"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.680.tgz#88cc44bd2a85b46cf7521f714db57dd74d0cd488"
|
||||
integrity sha512-XBACJT9RdpdWtoMXQPR8Be3ZtmizWWbxfw8cY2b5feUwiDO3FUl8qo4W2jXoq/WnnA3xBRqafu1XbpczqyUvlA==
|
||||
|
||||
element-resize-detector@^1.2.1:
|
||||
version "1.2.2"
|
||||
@ -7826,27 +7826,10 @@ error-stack-parser@^2.0.6:
|
||||
dependencies:
|
||||
stackframe "^1.1.1"
|
||||
|
||||
es-abstract@^1.17.0-next.0, es-abstract@^1.17.2, es-abstract@^1.17.4:
|
||||
version "1.17.7"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
|
||||
integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.2.2"
|
||||
is-regex "^1.1.1"
|
||||
object-inspect "^1.8.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.1"
|
||||
string.prototype.trimend "^1.0.1"
|
||||
string.prototype.trimstart "^1.0.1"
|
||||
|
||||
es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2:
|
||||
version "1.18.0-next.3"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.3.tgz#56bc8b5cc36b2cca25a13be07f3c02c2343db6b7"
|
||||
integrity sha512-VMzHx/Bczjg59E6jZOQjHeN3DEoptdhejpARgflAViidlqSpjdq9zA6lKwlhRRs/lOw1gHJv2xkkSFRgvEwbQg==
|
||||
es-abstract@^1.17.0-next.0, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2:
|
||||
version "1.18.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4"
|
||||
integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
es-to-primitive "^1.2.1"
|
||||
@ -10117,7 +10100,7 @@ is-buffer@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
|
||||
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
|
||||
|
||||
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.2, is-callable@^1.2.3:
|
||||
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
|
||||
integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
|
||||
@ -12713,7 +12696,7 @@ object-copy@^0.1.0:
|
||||
define-property "^0.2.5"
|
||||
kind-of "^3.0.3"
|
||||
|
||||
object-inspect@^1.7.0, object-inspect@^1.8.0, object-inspect@^1.9.0:
|
||||
object-inspect@^1.7.0, object-inspect@^1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
||||
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
||||
@ -12738,7 +12721,7 @@ object-visit@^1.0.0:
|
||||
dependencies:
|
||||
isobject "^3.0.0"
|
||||
|
||||
object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2:
|
||||
object.assign@^4.1.0, object.assign@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
|
||||
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
|
||||
@ -14506,9 +14489,9 @@ react-hammerjs@^1.0.1:
|
||||
hammerjs "^2.0.8"
|
||||
|
||||
react-helmet-async@^1.0.2:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.8.tgz#28178a85ca4d669dff295d5c119d575af4451855"
|
||||
integrity sha512-FSOjIzbHdJYTQ0An0yZlqd4mmTzQxPLyZC2LF5zmobo6Af+tXDKnLmmAE7s+vZ7JuRt/L8baK3VnULSB1usY0g==
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.9.tgz#5b9ed2059de6b4aab47f769532f9fbcbce16c5ca"
|
||||
integrity sha512-N+iUlo9WR3/u9qGMmP4jiYfaD6pe9IvDTapZLFJz2D3xlTlCM1Bzy4Ab3g72Nbajo/0ZyW+W9hdz8Hbe4l97pQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
invariant "^2.2.4"
|
||||
@ -16511,7 +16494,7 @@ string.prototype.trim@^1.2.1:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.18.0-next.2"
|
||||
|
||||
string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.4:
|
||||
string.prototype.trimend@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
|
||||
integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
|
||||
@ -16519,7 +16502,7 @@ string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.4:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.4:
|
||||
string.prototype.trimstart@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
|
||||
integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
|
||||
|
Loading…
Reference in New Issue
Block a user