Merge branch 'feature/ssr-confirm' into feature/ssr-confirm-test
This commit is contained in:
commit
8f6235424c
@ -146,7 +146,7 @@ const AccessSelector: React.FC<AccessSelectorProps> = ({
|
||||
directionX="right"
|
||||
directionY="top"
|
||||
fixedDirection
|
||||
manualWidth="fit-content"
|
||||
manualWidth="auto"
|
||||
isDefaultMode
|
||||
isAside={isMobileView}
|
||||
setIsOpenItemAccess={setIsOpenItemAccess}
|
||||
|
@ -38,6 +38,7 @@ import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import RoomsFilter from "@docspace/shared/api/rooms/filter";
|
||||
|
||||
import { RoomSearchArea } from "@docspace/shared/enums";
|
||||
import { frameCallEvent } from "@docspace/shared/utils/common";
|
||||
import { getCategoryUrl } from "SRC_DIR/helpers/utils";
|
||||
import { CategoryType } from "SRC_DIR/helpers/constants";
|
||||
|
||||
@ -60,6 +61,8 @@ const RoomNoAccessContainer = (props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
React.useEffect(() => {
|
||||
frameCallEvent({ event: "onNoAccess" });
|
||||
|
||||
const timer = setTimeout(onGoToShared, 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
@ -218,7 +218,7 @@ const QuotaForm = ({
|
||||
size="content"
|
||||
onSelect={onSelectComboBox}
|
||||
showDisabledItems
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
directionY="both"
|
||||
/>
|
||||
</div>
|
||||
|
@ -196,7 +196,7 @@ const SpaceQuota = (props) => {
|
||||
size="content"
|
||||
modernView
|
||||
isLoading={isLoading}
|
||||
manualWidth="fit-content"
|
||||
manualWidth="auto"
|
||||
directionY="both"
|
||||
/>
|
||||
</StyledBody>
|
||||
|
@ -239,7 +239,7 @@ const ThirdPartyComboBox = ({
|
||||
scaled
|
||||
withBackdrop={isMobile}
|
||||
size="content"
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth={"auto"}
|
||||
isMobileView={isMobileOnly}
|
||||
directionY="both"
|
||||
displaySelectedOption
|
||||
|
@ -174,7 +174,7 @@ const GroupMember = ({ member, infoPanelSelection }: GroupMemberProps) => {
|
||||
size="content"
|
||||
modernView
|
||||
title={t("Common:Role")}
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
isMobileView={isMobileOnly}
|
||||
directionY="both"
|
||||
displaySelectedOption
|
||||
|
@ -155,7 +155,7 @@ const EmbeddingPanelComponent = (props: EmbeddingPanelProps) => {
|
||||
);
|
||||
|
||||
const fileConfig = {
|
||||
mode: "viewer",
|
||||
mode: "editor",
|
||||
width: `${widthValue}${dataDimensions[0].label}`,
|
||||
height: `${heightValue}${dataDimensions[1].label}`,
|
||||
frameId: "ds-frame",
|
||||
|
@ -219,8 +219,8 @@ const StyledProperties = styled.div`
|
||||
grid-template-columns: 120px 1fr;
|
||||
grid-column-gap: 24px;
|
||||
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-align: baseline;
|
||||
align-items: baseline;
|
||||
|
||||
.property-title {
|
||||
font-size: 13px;
|
||||
|
@ -176,7 +176,7 @@ const Accounts = (props) => {
|
||||
size="content"
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
|
@ -382,7 +382,7 @@ const User = ({
|
||||
size="content"
|
||||
modernView
|
||||
title={t("Common:Role")}
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
isLoading={isLoading}
|
||||
isMobileView={isMobileOnly}
|
||||
directionY="both"
|
||||
|
@ -138,15 +138,8 @@ class InsideGroupTableHeader extends React.Component {
|
||||
newFilter.sortBy = sortBy;
|
||||
|
||||
if (sortBy === "AZ") {
|
||||
if (
|
||||
newFilter.sortBy !== "lastname" &&
|
||||
newFilter.sortBy !== "firstname"
|
||||
) {
|
||||
newFilter.sortBy = "firstname";
|
||||
} else if (newFilter.sortBy === "lastname") {
|
||||
newFilter.sortBy = "firstname";
|
||||
} else {
|
||||
newFilter.sortBy = "lastname";
|
||||
if (newFilter.sortBy !== "displayname") {
|
||||
newFilter.sortBy = "displayname";
|
||||
}
|
||||
newFilter.sortOrder =
|
||||
newFilter.sortOrder === "ascending" ? "descending" : "ascending";
|
||||
@ -176,10 +169,7 @@ class InsideGroupTableHeader extends React.Component {
|
||||
} = this.props;
|
||||
const { sortOrder } = filter;
|
||||
|
||||
const sortBy =
|
||||
filter.sortBy === "firstname" || filter.sortBy === "lastname"
|
||||
? "AZ"
|
||||
: filter.sortBy;
|
||||
const sortBy = filter.sortBy === "displayname" ? "AZ" : filter.sortBy;
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
|
@ -399,7 +399,7 @@ const InsideGroupTableRow = (props) => {
|
||||
directionY="both"
|
||||
size="content"
|
||||
modernView
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
isLoading={isLoading}
|
||||
optionStyle={{ maxWidth: "400px" }}
|
||||
textOverflow
|
||||
@ -442,7 +442,7 @@ const InsideGroupTableRow = (props) => {
|
||||
size="content"
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth="auto"
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
|
@ -143,7 +143,7 @@ class PeopleTableHeader extends React.Component {
|
||||
|
||||
if (
|
||||
newFilter.sortBy === sortBy ||
|
||||
(sortBy === "AZ" && newFilter.sortBy === "firstname")
|
||||
(sortBy === "AZ" && newFilter.sortBy === "displayname")
|
||||
) {
|
||||
newFilter.sortOrder =
|
||||
newFilter.sortOrder === "ascending" ? "descending" : "ascending";
|
||||
@ -151,7 +151,7 @@ class PeopleTableHeader extends React.Component {
|
||||
newFilter.sortBy = sortBy;
|
||||
|
||||
if (sortBy === "AZ") {
|
||||
newFilter.sortBy = "firstname";
|
||||
newFilter.sortBy = "displayname";
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,10 +178,7 @@ class PeopleTableHeader extends React.Component {
|
||||
} = this.props;
|
||||
const { sortOrder } = filter;
|
||||
|
||||
const sortBy =
|
||||
filter.sortBy === "firstname" || filter.sortBy === "lastname"
|
||||
? "AZ"
|
||||
: filter.sortBy;
|
||||
const sortBy = filter.sortBy === "displayname" ? "AZ" : filter.sortBy;
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
|
@ -438,7 +438,7 @@ const PeopleTableRow = (props) => {
|
||||
size="content"
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth={"fit-content"}
|
||||
manualWidth={"auto"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
|
@ -1961,16 +1961,9 @@ const SectionFilterContent = ({
|
||||
const options = [];
|
||||
|
||||
const firstName = {
|
||||
id: "sort-by_first-name",
|
||||
key: "firstname",
|
||||
label: t("Common:FirstName"),
|
||||
default: true,
|
||||
};
|
||||
|
||||
const lastName = {
|
||||
id: "sort-by_last-name",
|
||||
key: "lastname",
|
||||
label: t("Common:LastName"),
|
||||
id: "sort-by_displayname",
|
||||
key: "displayname",
|
||||
label: t("Common:Name"),
|
||||
default: true,
|
||||
};
|
||||
|
||||
@ -2012,7 +2005,7 @@ const SectionFilterContent = ({
|
||||
hideableColumns.Storage = storage;
|
||||
}
|
||||
|
||||
options.push(firstName, lastName, type, department, email);
|
||||
options.push(firstName, type, department, email);
|
||||
if (showStorageInfo) options.push(storage);
|
||||
|
||||
return options;
|
||||
|
@ -112,7 +112,7 @@ const Layout = ({
|
||||
<DetailsNavigationHeader />
|
||||
) : currentPath === oauthCreatePath ||
|
||||
currentPath === oauthEditPath ? (
|
||||
<OAuthSectionHeader />
|
||||
<OAuthSectionHeader isEdit={currentPath === oauthEditPath} />
|
||||
) : (
|
||||
<SectionHeaderContent />
|
||||
)}
|
||||
|
@ -24,6 +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
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
|
@ -24,6 +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
|
||||
|
||||
import React from "react";
|
||||
import { TableRow, TableCell } from "@docspace/shared/components/table";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
@ -136,7 +136,7 @@ const UsersRowContent = (props: TypeSelectRowContentProps) => {
|
||||
size={ComboBoxSize.content}
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth="fit-content"
|
||||
manualWidth="auto"
|
||||
/>
|
||||
</div>
|
||||
</Box>,
|
||||
|
@ -149,7 +149,7 @@ const UsersTableRow = (props: TypeSelectTableRowProps) => {
|
||||
size={ComboBoxSize.content}
|
||||
displaySelectedOption
|
||||
modernView
|
||||
manualWidth="fit-content"
|
||||
manualWidth="auto"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
@ -130,6 +130,7 @@ const Manager = (props) => {
|
||||
);
|
||||
const [selectedColumns, setSelectedColumns] = useState([
|
||||
{ key: "Name", label: t("Common:Name") },
|
||||
{ key: "Size", label: t("Common:Size") },
|
||||
{ key: "Type", label: t("Common:Type") },
|
||||
{ key: "Tags", label: t("Common:Tags") },
|
||||
]);
|
||||
|
@ -18,6 +18,9 @@ export interface OAuthProps {
|
||||
previewDialogVisible?: boolean;
|
||||
disableDialogVisible?: boolean;
|
||||
deleteDialogVisible?: boolean;
|
||||
generateDeveloperTokenDialogVisible?: boolean;
|
||||
revokeDeveloperTokenDialogVisible?: boolean;
|
||||
|
||||
isInit: boolean;
|
||||
setIsInit: (value: boolean) => void;
|
||||
}
|
||||
|
@ -28,17 +28,15 @@ const OAuthSectionHeader = ({ isEdit }: { isEdit: boolean }) => {
|
||||
<HeaderContainer>
|
||||
<Headline type="content" truncate>
|
||||
<div className="settings-section_header">
|
||||
<div className="header">
|
||||
<IconButton
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
size={17}
|
||||
isFill
|
||||
onClick={onBack}
|
||||
className="arrow-button"
|
||||
/>
|
||||
<IconButton
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
size={17}
|
||||
isFill
|
||||
onClick={onBack}
|
||||
className="arrow-button"
|
||||
/>
|
||||
|
||||
{isEdit ? t("EditApp") : t("NewApp")}
|
||||
</div>
|
||||
{isEdit ? t("EditApp") : t("NewApp")}
|
||||
</div>
|
||||
</Headline>
|
||||
</HeaderContainer>
|
||||
|
@ -18,6 +18,8 @@ import DisableDialog from "./sub-components/DisableDialog";
|
||||
import DeleteDialog from "./sub-components/DeleteDialog";
|
||||
import OAuthEmptyScreen from "./sub-components/EmptyScreen";
|
||||
import List from "./sub-components/List";
|
||||
import GenerateDeveloperTokenDialog from "./sub-components/GenerateDeveloperTokenDialog";
|
||||
import RevokeDeveloperTokenDialog from "./sub-components/RevokeDeveloperTokenDialog";
|
||||
|
||||
const MIN_LOADER_TIME = 500;
|
||||
|
||||
@ -35,6 +37,8 @@ const OAuth = ({
|
||||
setIsInit,
|
||||
disableDialogVisible,
|
||||
deleteDialogVisible,
|
||||
generateDeveloperTokenDialogVisible,
|
||||
revokeDeveloperTokenDialogVisible,
|
||||
}: OAuthProps) => {
|
||||
const { t } = useTranslation(["OAuth"]);
|
||||
|
||||
@ -102,6 +106,8 @@ const OAuth = ({
|
||||
{disableDialogVisible && <DisableDialog />}
|
||||
{previewDialogVisible && <PreviewDialog visible={previewDialogVisible} />}
|
||||
{deleteDialogVisible && <DeleteDialog />}
|
||||
{generateDeveloperTokenDialogVisible && <GenerateDeveloperTokenDialog />}
|
||||
{revokeDeveloperTokenDialogVisible && <RevokeDeveloperTokenDialog />}
|
||||
</OAuthContainer>
|
||||
);
|
||||
};
|
||||
@ -128,6 +134,8 @@ export default inject(
|
||||
setIsInit,
|
||||
disableDialogVisible,
|
||||
deleteDialogVisible,
|
||||
generateDeveloperTokenDialogVisible,
|
||||
revokeDeveloperTokenDialogVisible,
|
||||
} = oauthStore;
|
||||
return {
|
||||
viewAs,
|
||||
@ -143,6 +151,8 @@ export default inject(
|
||||
setIsInit,
|
||||
disableDialogVisible,
|
||||
deleteDialogVisible,
|
||||
generateDeveloperTokenDialogVisible,
|
||||
revokeDeveloperTokenDialogVisible,
|
||||
};
|
||||
},
|
||||
)(observer(OAuth));
|
||||
|
@ -2,6 +2,7 @@ import React from "react";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { SelectorAddButton } from "@docspace/shared/components/selector-add-button";
|
||||
import { globalColors } from "@docspace/shared/themes";
|
||||
|
||||
import { StyledInputGroup } from "../ClientForm.styled";
|
||||
|
||||
@ -55,7 +56,8 @@ const SelectGroup = ({
|
||||
color=""
|
||||
textAlign=""
|
||||
>
|
||||
{label} *
|
||||
{label}{" "}
|
||||
<span style={{ color: globalColors.lightErrorStatus }}> *</span>
|
||||
</Text>
|
||||
</div>
|
||||
<div className="select">
|
||||
|
@ -0,0 +1,211 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { i18n } from "i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import copy from "copy-to-clipboard";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
import api from "@docspace/shared/api";
|
||||
import { IClientProps } from "@docspace/shared/utils/oauth/types";
|
||||
import {
|
||||
ModalDialog,
|
||||
ModalDialogType,
|
||||
} from "@docspace/shared/components/modal-dialog";
|
||||
import { Button, ButtonSize } from "@docspace/shared/components/button";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { TData } from "@docspace/shared/components/toast/Toast.type";
|
||||
import { InputBlock } from "@docspace/shared/components/input-block";
|
||||
import { InputSize, InputType } from "@docspace/shared/components/text-input";
|
||||
|
||||
import CopyReactSvgUrl from "PUBLIC_DIR/images/copy.react.svg?url";
|
||||
|
||||
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
||||
import { UserStore } from "@docspace/shared/store/UserStore";
|
||||
import { globalColors } from "@docspace/shared/themes";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
p {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dates {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
type GenerateDeveloperTokenDialogProps = {
|
||||
client?: IClientProps;
|
||||
|
||||
email?: string;
|
||||
|
||||
setGenerateDeveloperTokenDialogVisible?: (value: boolean) => void;
|
||||
};
|
||||
|
||||
const getDate = (date: Date, i18nArg: i18n) => {
|
||||
return moment(date)
|
||||
.locale(i18nArg.language)
|
||||
.tz(window.timezone)
|
||||
.format("MMM D, YYYY, h:mm:ss A");
|
||||
};
|
||||
|
||||
const GenerateDeveloperTokenDialog = ({
|
||||
client,
|
||||
email,
|
||||
|
||||
setGenerateDeveloperTokenDialogVisible,
|
||||
}: GenerateDeveloperTokenDialogProps) => {
|
||||
const { i18n: i18nParam } = useTranslation(["OAuth", "Common"]);
|
||||
|
||||
const theme = useTheme();
|
||||
const [token, setToken] = React.useState("");
|
||||
const [dates, setDates] = React.useState({
|
||||
created: getDate(new Date(), i18nParam),
|
||||
expires: getDate(new Date(), i18nParam),
|
||||
});
|
||||
const [requestRunning, setRequestRunning] = React.useState(false);
|
||||
|
||||
const onGenerate = async () => {
|
||||
if (token || !client || requestRunning) return;
|
||||
|
||||
try {
|
||||
const { clientId, clientSecret, scopes } = client;
|
||||
|
||||
setRequestRunning(true);
|
||||
|
||||
const data = await api.oauth.generateDevelopToken(
|
||||
clientId,
|
||||
clientSecret,
|
||||
scopes,
|
||||
);
|
||||
|
||||
setRequestRunning(false);
|
||||
|
||||
if (!data) return;
|
||||
|
||||
const { access_token: accessToken, expires_in: expiresIn } = data;
|
||||
|
||||
const created = new Date();
|
||||
// convert sec to ms
|
||||
const expires = new Date(created.getTime() + expiresIn * 1000);
|
||||
|
||||
if (accessToken) {
|
||||
setToken(accessToken);
|
||||
copy(accessToken);
|
||||
setDates({
|
||||
created: getDate(created, i18nParam),
|
||||
expires: getDate(expires, i18nParam),
|
||||
});
|
||||
toastr.success("Copied");
|
||||
}
|
||||
} catch (e) {
|
||||
toastr.error(e as TData);
|
||||
}
|
||||
};
|
||||
|
||||
const onCopyClick = async () => {
|
||||
copy(token);
|
||||
toastr.success("Copied");
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
if (requestRunning) return;
|
||||
|
||||
setGenerateDeveloperTokenDialogVisible?.(false);
|
||||
};
|
||||
return (
|
||||
<ModalDialog
|
||||
visible
|
||||
onClose={onClose}
|
||||
displayType={ModalDialogType.modal}
|
||||
autoMaxHeight
|
||||
scale
|
||||
>
|
||||
<ModalDialog.Header>Generate developer token</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<StyledContainer>
|
||||
<Text>
|
||||
By generating an developer access token, you will be able to make
|
||||
API calls for your own account without going through the
|
||||
authorization flow. To obtain access tokens for other users, use the
|
||||
standard OAuth flow.
|
||||
</Text>
|
||||
<Text>
|
||||
For scoped apps, the token will have the same scope as the app.
|
||||
</Text>
|
||||
{token ? (
|
||||
<>
|
||||
<Text
|
||||
color={
|
||||
theme.isBase
|
||||
? globalColors.lightErrorStatus
|
||||
: globalColors.darkErrorStatus
|
||||
}
|
||||
>
|
||||
This access token can be used to access your account ({email})
|
||||
via the API. Don`t share your access token with anyone.
|
||||
</Text>
|
||||
<InputBlock
|
||||
value={token}
|
||||
scale
|
||||
isReadOnly
|
||||
isDisabled
|
||||
size={InputSize.base}
|
||||
iconName={CopyReactSvgUrl}
|
||||
onIconClick={onCopyClick}
|
||||
type={InputType.text}
|
||||
/>
|
||||
<Text className="dates">
|
||||
Created: {dates.created}
|
||||
<br />
|
||||
Expires: {dates.expires}{" "}
|
||||
</Text>
|
||||
</>
|
||||
) : null}
|
||||
</StyledContainer>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
label="Generate developer token"
|
||||
primary
|
||||
scale
|
||||
onClick={onGenerate}
|
||||
isDisabled={!!token}
|
||||
isLoading={requestRunning}
|
||||
size={ButtonSize.small}
|
||||
/>
|
||||
<Button
|
||||
label="Cancel"
|
||||
scale
|
||||
onClick={onClose}
|
||||
size={ButtonSize.small}
|
||||
isDisabled={requestRunning}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({
|
||||
oauthStore,
|
||||
userStore,
|
||||
}: {
|
||||
oauthStore: OAuthStoreProps;
|
||||
userStore: UserStore;
|
||||
}) => {
|
||||
const { setGenerateDeveloperTokenDialogVisible, bufferSelection } =
|
||||
oauthStore;
|
||||
|
||||
const { user } = userStore;
|
||||
|
||||
return {
|
||||
setGenerateDeveloperTokenDialogVisible,
|
||||
client: bufferSelection,
|
||||
|
||||
email: user?.email,
|
||||
};
|
||||
},
|
||||
)(observer(GenerateDeveloperTokenDialog));
|
@ -0,0 +1,141 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import api from "@docspace/shared/api";
|
||||
import { IClientProps } from "@docspace/shared/utils/oauth/types";
|
||||
import {
|
||||
ModalDialog,
|
||||
ModalDialogType,
|
||||
} from "@docspace/shared/components/modal-dialog";
|
||||
import { Button, ButtonSize } from "@docspace/shared/components/button";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { TData } from "@docspace/shared/components/toast/Toast.type";
|
||||
import { InputBlock } from "@docspace/shared/components/input-block";
|
||||
import { InputSize, InputType } from "@docspace/shared/components/text-input";
|
||||
|
||||
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
||||
import { UserStore } from "@docspace/shared/store/UserStore";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
p {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
type GenerateDeveloperTokenDialogProps = {
|
||||
client?: IClientProps;
|
||||
|
||||
setRevokeDeveloperTokenDialogVisible?: (value: boolean) => void;
|
||||
};
|
||||
|
||||
const GenerateDeveloperTokenDialog = ({
|
||||
client,
|
||||
|
||||
setRevokeDeveloperTokenDialogVisible,
|
||||
}: GenerateDeveloperTokenDialogProps) => {
|
||||
// const {} = useTranslation(["OAuth", "Common"]);
|
||||
|
||||
const [token, setToken] = React.useState("");
|
||||
|
||||
const [requestRunning, setRequestRunning] = React.useState(false);
|
||||
|
||||
const onRevoke = async () => {
|
||||
if (!token || !client || requestRunning) return;
|
||||
|
||||
try {
|
||||
const { clientId, clientSecret } = client;
|
||||
|
||||
setRequestRunning(true);
|
||||
|
||||
await api.oauth.revokeDeveloperToken(token, clientId, clientSecret);
|
||||
|
||||
setRequestRunning(false);
|
||||
|
||||
setToken("");
|
||||
setRevokeDeveloperTokenDialogVisible?.(false);
|
||||
|
||||
toastr.success("Revoked");
|
||||
} catch (e) {
|
||||
toastr.error(e as TData);
|
||||
}
|
||||
};
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
|
||||
setToken(value);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
if (requestRunning) return;
|
||||
|
||||
setRevokeDeveloperTokenDialogVisible?.(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
visible
|
||||
onClose={onClose}
|
||||
displayType={ModalDialogType.modal}
|
||||
autoMaxHeight
|
||||
scale
|
||||
>
|
||||
<ModalDialog.Header>Revoke developer token</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<StyledContainer>
|
||||
<Text>Warning text</Text>
|
||||
<InputBlock
|
||||
value={token}
|
||||
scale
|
||||
placeholder="Enter developer token"
|
||||
type={InputType.text}
|
||||
size={InputSize.base}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
label="Revoke"
|
||||
primary
|
||||
scale
|
||||
onClick={onRevoke}
|
||||
isDisabled={!token}
|
||||
isLoading={requestRunning}
|
||||
size={ButtonSize.small}
|
||||
/>
|
||||
<Button
|
||||
label="Cancel"
|
||||
scale
|
||||
onClick={onClose}
|
||||
size={ButtonSize.small}
|
||||
isDisabled={requestRunning}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({
|
||||
oauthStore,
|
||||
userStore,
|
||||
}: {
|
||||
oauthStore: OAuthStoreProps;
|
||||
userStore: UserStore;
|
||||
}) => {
|
||||
const { setRevokeDeveloperTokenDialogVisible, bufferSelection } =
|
||||
oauthStore;
|
||||
|
||||
const { user } = userStore;
|
||||
|
||||
return {
|
||||
setRevokeDeveloperTokenDialogVisible,
|
||||
client: bufferSelection,
|
||||
|
||||
email: user?.email,
|
||||
};
|
||||
},
|
||||
)(observer(GenerateDeveloperTokenDialog));
|
@ -1256,71 +1256,10 @@ class ContextOptionsStore {
|
||||
!contextOptions.includes("finalize-version") &&
|
||||
contextOptions.includes("show-version-history");
|
||||
|
||||
const versionActions = isDesktop()
|
||||
? onlyShowVersionHistory
|
||||
? [
|
||||
{
|
||||
id: "option_show-version-history",
|
||||
key: "show-version-history",
|
||||
label: t("ShowVersionHistory"),
|
||||
icon: HistoryReactSvgUrl,
|
||||
onClick: () =>
|
||||
this.showVersionHistory(
|
||||
item.id,
|
||||
item.security,
|
||||
item?.requestToken,
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: "option_version",
|
||||
key: "version",
|
||||
label: t("VersionHistory"),
|
||||
icon: HistoryFinalizedReactSvgUrl,
|
||||
items: [
|
||||
{
|
||||
id: "option_finalize-version",
|
||||
key: "finalize-version",
|
||||
label: t("FinalizeVersion"),
|
||||
icon: HistoryFinalizedReactSvgUrl,
|
||||
onClick: () =>
|
||||
isEditing
|
||||
? this.onShowEditingToast(t)
|
||||
: this.finalizeVersion(item.id, item.security),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_version-history",
|
||||
key: "show-version-history",
|
||||
label: t("ShowVersionHistory"),
|
||||
icon: HistoryReactSvgUrl,
|
||||
onClick: () =>
|
||||
this.showVersionHistory(
|
||||
item.id,
|
||||
item.security,
|
||||
item?.requestToken,
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [
|
||||
const versionActions = onlyShowVersionHistory
|
||||
? [
|
||||
{
|
||||
id: "option_finalize-version",
|
||||
key: "finalize-version",
|
||||
label: t("FinalizeVersion"),
|
||||
icon: HistoryFinalizedReactSvgUrl,
|
||||
onClick: () =>
|
||||
isEditing
|
||||
? this.onShowEditingToast(t)
|
||||
: this.finalizeVersion(item.id),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_version-history",
|
||||
id: "option_show-version-history",
|
||||
key: "show-version-history",
|
||||
label: t("ShowVersionHistory"),
|
||||
icon: HistoryReactSvgUrl,
|
||||
@ -1332,8 +1271,43 @@ class ContextOptionsStore {
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: "option_version",
|
||||
key: "version",
|
||||
label: t("VersionHistory"),
|
||||
icon: HistoryFinalizedReactSvgUrl,
|
||||
items: [
|
||||
{
|
||||
id: "option_finalize-version",
|
||||
key: "finalize-version",
|
||||
label: t("FinalizeVersion"),
|
||||
icon: HistoryFinalizedReactSvgUrl,
|
||||
onClick: () =>
|
||||
isEditing
|
||||
? this.onShowEditingToast(t)
|
||||
: this.finalizeVersion(item.id, item.security),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "option_version-history",
|
||||
key: "show-version-history",
|
||||
label: t("ShowVersionHistory"),
|
||||
icon: HistoryReactSvgUrl,
|
||||
onClick: () =>
|
||||
this.showVersionHistory(
|
||||
item.id,
|
||||
item.security,
|
||||
item?.requestToken,
|
||||
),
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const moveActions = isDesktop()
|
||||
|
||||
const moveActions = !isInfoPanel
|
||||
? [
|
||||
{
|
||||
id: "option_move-or-copy",
|
||||
@ -1815,7 +1789,7 @@ class ContextOptionsStore {
|
||||
const pluginItems = this.onLoadPlugins(item);
|
||||
|
||||
if (pluginItems.length > 0) {
|
||||
if (!isDesktop() || pluginItems.length === 1) {
|
||||
if (pluginItems.length === 1) {
|
||||
pluginItems.forEach((plugin) => {
|
||||
options.splice(1, 0, {
|
||||
id: `option_${plugin.key}`,
|
||||
|
@ -125,6 +125,17 @@ class EditGroupStore {
|
||||
|
||||
const addedIds = Array.from(this.addedMembersMap.keys());
|
||||
const removedIds = Array.from(this.removedMembersMap.keys());
|
||||
const oldManager = this.group.manager;
|
||||
const oldManagerRemovedButRemainsAsMember =
|
||||
oldManager &&
|
||||
oldManager.id !== this.manager?.id &&
|
||||
!this.removedMembersMap.has(oldManager.id);
|
||||
|
||||
// Requires when new group is without manager and old manager moved to members. updateGroup api method doesn't provide possibility to do it without setting new manager
|
||||
if (this.manager === null && oldManagerRemovedButRemainsAsMember) {
|
||||
await api.groups.removeGroupMembers(this.group.id, [oldManager.id]);
|
||||
addedIds.push(oldManager.id);
|
||||
}
|
||||
|
||||
await updateGroup(
|
||||
this.group?.id,
|
||||
|
@ -489,7 +489,7 @@ class LdapFormStore {
|
||||
completed: true,
|
||||
percents: 100,
|
||||
certificateConfirmRequest: null,
|
||||
error: "",
|
||||
error: t("Common:UnexpectedError"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -518,7 +518,6 @@ class LdapFormStore {
|
||||
toastr.success(t("Common:SuccessfullyCompletedOperation"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toastr.error(error);
|
||||
this.endProcess();
|
||||
}
|
||||
|
@ -58,6 +58,12 @@ export interface OAuthStoreProps {
|
||||
resetDialogVisible: boolean;
|
||||
setResetDialogVisible: (value: boolean) => void;
|
||||
|
||||
generateDeveloperTokenDialogVisible: boolean;
|
||||
setGenerateDeveloperTokenDialogVisible: (value: boolean) => void;
|
||||
|
||||
revokeDeveloperTokenDialogVisible: boolean;
|
||||
setRevokeDeveloperTokenDialogVisible: (value: boolean) => void;
|
||||
|
||||
deleteDialogVisible: boolean;
|
||||
setDeleteDialogVisible: (value: boolean) => void;
|
||||
|
||||
@ -157,6 +163,10 @@ class OAuthStore implements OAuthStoreProps {
|
||||
|
||||
resetDialogVisible: boolean = false;
|
||||
|
||||
generateDeveloperTokenDialogVisible: boolean = false;
|
||||
|
||||
revokeDeveloperTokenDialogVisible: boolean = false;
|
||||
|
||||
selection: string[] = [];
|
||||
|
||||
bufferSelection: IClientProps | null = null;
|
||||
@ -216,6 +226,14 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.resetDialogVisible = value;
|
||||
};
|
||||
|
||||
setGenerateDeveloperTokenDialogVisible = (value: boolean) => {
|
||||
this.generateDeveloperTokenDialogVisible = value;
|
||||
};
|
||||
|
||||
setRevokeDeveloperTokenDialogVisible = (value: boolean) => {
|
||||
this.revokeDeveloperTokenDialogVisible = value;
|
||||
};
|
||||
|
||||
setClientSecret = (value: string) => {
|
||||
this.clientSecret = value;
|
||||
};
|
||||
@ -526,6 +544,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setInfoDialogVisible(true);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onRevoke = () => {
|
||||
@ -535,6 +555,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setRevokeDialogVisible(true);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onDisable = () => {
|
||||
@ -544,6 +566,30 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(true);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onGenerateDeveloperToken = () => {
|
||||
this.setBufferSelection(clientId);
|
||||
this.setPreviewDialogVisible(false);
|
||||
this.setInfoDialogVisible(false);
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(true);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onRevokeDeveloperToken = () => {
|
||||
this.setBufferSelection(clientId);
|
||||
this.setPreviewDialogVisible(false);
|
||||
this.setInfoDialogVisible(false);
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(true);
|
||||
};
|
||||
|
||||
const openOption = {
|
||||
@ -598,6 +644,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(true);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onShowPreview = () => {
|
||||
@ -607,6 +655,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
};
|
||||
|
||||
const onEnable = async (status: boolean) => {
|
||||
@ -615,6 +665,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setRevokeDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setGenerateDeveloperTokenDialogVisible(false);
|
||||
this.setRevokeDeveloperTokenDialogVisible(false);
|
||||
|
||||
if (isGroupContext) {
|
||||
try {
|
||||
@ -673,6 +725,20 @@ class OAuthStore implements OAuthStoreProps {
|
||||
onClick: onDisable,
|
||||
};
|
||||
|
||||
const generateDeveloperTokenOption = {
|
||||
key: "generate-token",
|
||||
icon: EnableReactSvgUrl,
|
||||
label: "Generate developer token",
|
||||
onClick: onGenerateDeveloperToken,
|
||||
};
|
||||
|
||||
const revokeDeveloperTokenOption = {
|
||||
key: "revoke-token",
|
||||
icon: EnableReactSvgUrl,
|
||||
label: "Revoke developer token",
|
||||
onClick: onRevokeDeveloperToken,
|
||||
};
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
key: "Separator dropdownItem",
|
||||
@ -708,6 +774,9 @@ class OAuthStore implements OAuthStoreProps {
|
||||
contextOptions.unshift(enableOption);
|
||||
}
|
||||
|
||||
contextOptions.unshift(revokeDeveloperTokenOption);
|
||||
contextOptions.unshift(generateDeveloperTokenOption);
|
||||
|
||||
if (!isInfo) contextOptions.unshift(infoOption);
|
||||
contextOptions.unshift(authButtonOption);
|
||||
contextOptions.unshift(editOption);
|
||||
|
@ -347,7 +347,7 @@ class ProfileActionsStore {
|
||||
|
||||
let bookTraining = null;
|
||||
|
||||
if (!isMobile && this.isTeamTrainingAlertAvailable) {
|
||||
if (!isMobile && this.authStore.isTeamTrainingAlertAvailable) {
|
||||
bookTraining = {
|
||||
key: "user-menu-book-training",
|
||||
icon: BookTrainingReactSvgUrl,
|
||||
|
@ -305,7 +305,7 @@ const Editor = ({
|
||||
newConfig.events.onRequestClose = onSDKRequestClose;
|
||||
}
|
||||
|
||||
if (config?.startFilling) {
|
||||
if (config?.startFilling && !IS_ZOOM) {
|
||||
newConfig.events.onRequestStartFilling = () =>
|
||||
onSDKRequestStartFilling?.(t("Common:ShareAndCollect"));
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { getSettings } from "@/utils/actions";
|
||||
import ProfileRemoveForm from "@/components/ProfileRemoveForm ";
|
||||
import ProfileRemoveForm from "@/components/ProfileRemoveForm";
|
||||
|
||||
async function Page() {
|
||||
const settings = await getSettings();
|
||||
|
@ -26,15 +26,33 @@
|
||||
|
||||
import ConfirmRoute from "@/components/ConfirmRoute";
|
||||
import { StyledBody, StyledPage } from "@/components/StyledConfirm.styled";
|
||||
import { TConfirmLinkParams } from "@/types";
|
||||
|
||||
import { getSettings } from "@/utils/actions";
|
||||
import { checkConfirmLink, getSettings } from "@/utils/actions";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
export default async function Layout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const settings = await getSettings();
|
||||
const hdrs = headers();
|
||||
const searchParams = hdrs.get("x-confirm-query") ?? "";
|
||||
const type = hdrs.get("x-confirm-type") ?? "";
|
||||
|
||||
const queryParams = Object.fromEntries(
|
||||
new URLSearchParams(searchParams.toString()),
|
||||
) as TConfirmLinkParams;
|
||||
|
||||
const confirmLinkParams: TConfirmLinkParams = Object.assign(
|
||||
{ type },
|
||||
queryParams,
|
||||
);
|
||||
|
||||
const [settings, confirmLinkResult] = await Promise.all([
|
||||
getSettings(),
|
||||
checkConfirmLink(confirmLinkParams),
|
||||
]);
|
||||
|
||||
return (
|
||||
<StyledPage id="confirm-page">
|
||||
@ -43,6 +61,9 @@ export default async function Layout({
|
||||
<ConfirmRoute
|
||||
defaultPage={settings?.defaultPage}
|
||||
socketUrl={settings?.socketUrl}
|
||||
confirmLinkResult={confirmLinkResult}
|
||||
confirmLinkParams={confirmLinkParams}
|
||||
confirmType={type}
|
||||
>
|
||||
{children}
|
||||
</ConfirmRoute>
|
||||
|
@ -26,23 +26,16 @@
|
||||
|
||||
"use client";
|
||||
|
||||
import { notFound, usePathname, useSearchParams } from "next/navigation";
|
||||
import React, { createContext, useCallback, useEffect, useState } from "react";
|
||||
import { notFound, useSearchParams } from "next/navigation";
|
||||
import React, { createContext, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getCookie } from "@docspace/shared/utils";
|
||||
import { LANGUAGE } from "@docspace/shared/constants";
|
||||
import { checkConfirmLink, logout } from "@docspace/shared/api/user";
|
||||
import AppLoader from "@docspace/shared/components/app-loader";
|
||||
import { logout } from "@docspace/shared/api/user";
|
||||
|
||||
import { AuthenticatedAction, ValidationResult } from "@/utils/enums";
|
||||
import {
|
||||
TConfirmLinkResult,
|
||||
TConfirmLinkParams,
|
||||
ConfirmRouteProps,
|
||||
TError,
|
||||
TConfirmRouteContext,
|
||||
} from "@/types";
|
||||
import { ConfirmRouteProps, TConfirmRouteContext } from "@/types";
|
||||
|
||||
export const ConfirmRouteContext = createContext<TConfirmRouteContext>({
|
||||
linkData: {},
|
||||
@ -55,54 +48,19 @@ function ConfirmRoute(props: ConfirmRouteProps) {
|
||||
defaultPage,
|
||||
socketUrl,
|
||||
children,
|
||||
confirmLinkResult,
|
||||
confirmLinkParams,
|
||||
confirmType,
|
||||
} = props;
|
||||
|
||||
const [stateData, setStateData] = useState<TConfirmRouteContext>({
|
||||
linkData: {},
|
||||
roomData: {},
|
||||
});
|
||||
const [confirmLinkResult, setConfirmLinkResult] =
|
||||
useState<TConfirmLinkResult>();
|
||||
const [error, setError] = useState<TError>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [stateData, setStateData] = useState<TConfirmRouteContext | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const { i18n, t } = useTranslation(["Common"]);
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
const isAuthenticated = !!socketUrl;
|
||||
|
||||
const getLinkParams = useCallback(() => {
|
||||
const queryParams = Object.fromEntries(
|
||||
new URLSearchParams(searchParams.toString()),
|
||||
) as TConfirmLinkParams;
|
||||
|
||||
const posSeparator = pathname.lastIndexOf("/");
|
||||
const type = !!posSeparator ? pathname?.slice(posSeparator + 1) : "";
|
||||
|
||||
const confirmLinkParams: TConfirmLinkParams = Object.assign(
|
||||
{ type },
|
||||
queryParams,
|
||||
);
|
||||
|
||||
return { type, confirmLinkParams };
|
||||
}, [pathname, searchParams]);
|
||||
|
||||
const getConfirmLinkResult = useCallback(async () => {
|
||||
const { confirmLinkParams } = getLinkParams();
|
||||
|
||||
try {
|
||||
const response = (await checkConfirmLink(
|
||||
confirmLinkParams,
|
||||
)) as TConfirmLinkResult;
|
||||
setConfirmLinkResult(response);
|
||||
} catch (error) {
|
||||
const knownError = error as TError;
|
||||
setError(knownError);
|
||||
}
|
||||
}, [getLinkParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.search.includes("culture")) return;
|
||||
const lng = getCookie(LANGUAGE);
|
||||
@ -121,15 +79,7 @@ function ConfirmRoute(props: ConfirmRouteProps) {
|
||||
}
|
||||
}, [doAuthenticated, isAuthenticated, defaultPage, socketUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
getConfirmLinkResult();
|
||||
}, [getConfirmLinkResult]);
|
||||
|
||||
useEffect(() => {
|
||||
if (confirmLinkResult?.result == undefined) return;
|
||||
|
||||
const { confirmLinkParams } = getLinkParams();
|
||||
|
||||
if (!stateData) {
|
||||
switch (confirmLinkResult.result) {
|
||||
case ValidationResult.Ok:
|
||||
const confirmHeader = searchParams.toString();
|
||||
@ -143,7 +93,6 @@ function ConfirmRoute(props: ConfirmRouteProps) {
|
||||
title: confirmLinkResult?.title,
|
||||
};
|
||||
setStateData((val) => ({ ...val, linkData, roomData }));
|
||||
setLoading(false);
|
||||
break;
|
||||
case ValidationResult.Invalid:
|
||||
case ValidationResult.Expired:
|
||||
@ -165,27 +114,19 @@ function ConfirmRoute(props: ConfirmRouteProps) {
|
||||
});
|
||||
notFound();
|
||||
}
|
||||
}, [confirmLinkResult, getLinkParams, t, searchParams]);
|
||||
}
|
||||
|
||||
const { type, confirmLinkParams } = getLinkParams();
|
||||
|
||||
if (!type && confirmLinkParams.type)
|
||||
if (!confirmType && confirmLinkParams.type)
|
||||
window.location.replace(
|
||||
`/confirm/${confirmLinkParams.type}?${searchParams.toString()}`,
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.error("FAILED checkConfirmLink", { error, confirmLinkParams });
|
||||
notFound();
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <AppLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmRouteContext.Provider
|
||||
value={{ linkData: stateData.linkData, roomData: stateData.roomData }}
|
||||
value={{
|
||||
linkData: stateData?.linkData ?? {},
|
||||
roomData: stateData?.roomData ?? {},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ConfirmRouteContext.Provider>
|
||||
|
@ -33,7 +33,6 @@ import { mobile, tablet } from "@docspace/shared/utils";
|
||||
export const RegisterContainer = styled.div<{
|
||||
registrationForm?: boolean;
|
||||
}>`
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
|
@ -44,7 +44,10 @@ export const GreetingContainer = styled.div`
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
p {
|
||||
.invitation-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -55,8 +58,10 @@ export const GreetingContainer = styled.div`
|
||||
|
||||
.portal-logo {
|
||||
width: 100%;
|
||||
max-width: 386px;
|
||||
height: 44px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 16px;
|
||||
height: 26.56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -72,7 +72,7 @@ export const GreetingCreateUserContainer = ({
|
||||
<img src={logoUrl} className="portal-logo" alt="greeting-logo" />
|
||||
{type === "LinkInvite" && (
|
||||
<div className="tooltip">
|
||||
<Text fontSize="16px">
|
||||
<Text fontSize="16px" as="div" className="invitation-text">
|
||||
{roomData.title ? (
|
||||
<Trans
|
||||
t={t}
|
||||
|
@ -94,7 +94,7 @@ const SimpleNav = ({
|
||||
return (
|
||||
<StyledSimpleNav id="login-header">
|
||||
<img className="logo" src={logoUrl} alt="logo-url" />
|
||||
{isLanguageComboboxVisible && <LanguageComboboxWrapper />}
|
||||
{isComboboxVisible && <LanguageComboboxWrapper />}
|
||||
</StyledSimpleNav>
|
||||
);
|
||||
};
|
||||
|
@ -79,6 +79,12 @@ export const StyledForm = styled(Box)`
|
||||
.set-app-description {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
|
||||
.portal-logo {
|
||||
margin: 0 auto;
|
||||
max-width: 386px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.set-app-title {
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
"use client";
|
||||
|
||||
import { ChangeEvent, useContext, useState } from "react";
|
||||
import { ChangeEvent, useContext, useEffect, useRef, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
@ -61,8 +61,6 @@ import { TError } from "@/types";
|
||||
import { ConfirmRouteContext } from "../ConfirmRoute";
|
||||
import { GreetingContainer } from "../GreetingContainer";
|
||||
|
||||
const PROXY_BASE_URL = combineUrl(window.ClientConfig?.proxy?.url, "/profile");
|
||||
|
||||
type TfaActivationFormProps = {
|
||||
secretKey: string;
|
||||
qrCode: string;
|
||||
@ -86,6 +84,14 @@ const TfaActivationForm = ({
|
||||
|
||||
const { confirmHeader = null } = linkData;
|
||||
|
||||
const proxyBaseUrl = useRef("");
|
||||
useEffect(() => {
|
||||
proxyBaseUrl.current = combineUrl(
|
||||
window.ClientConfig?.proxy?.url,
|
||||
"/profile",
|
||||
);
|
||||
}, []);
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
@ -97,7 +103,7 @@ const TfaActivationForm = ({
|
||||
}
|
||||
|
||||
sessionStorage.setItem(OPEN_BACKUP_CODES_DIALOG, "true");
|
||||
window.location.href = PROXY_BASE_URL;
|
||||
window.location.href = proxyBaseUrl.current;
|
||||
} catch (error) {
|
||||
const knownError = error as TError;
|
||||
let errorMessage: string;
|
||||
|
@ -49,9 +49,21 @@ export function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (request.nextUrl.pathname.includes("confirm")) {
|
||||
const type = request.nextUrl.searchParams.get("type");
|
||||
const queryType = request.nextUrl.searchParams.get("type");
|
||||
|
||||
const posSeparator = request.nextUrl.pathname.lastIndexOf("/");
|
||||
const type = !!posSeparator
|
||||
? request.nextUrl.pathname?.slice(posSeparator + 1)
|
||||
: "";
|
||||
requestHeaders.set("x-confirm-type", type);
|
||||
requestHeaders.set(
|
||||
"x-confirm-query",
|
||||
request.nextUrl.searchParams.toString(),
|
||||
);
|
||||
|
||||
return NextResponse.rewrite(
|
||||
`${request.nextUrl.origin}/login/confirm/${type}${request.nextUrl.search}`,
|
||||
`${request.nextUrl.origin}/login/confirm/${queryType}${request.nextUrl.search}`,
|
||||
{ headers: requestHeaders },
|
||||
);
|
||||
}
|
||||
|
||||
@ -88,5 +100,12 @@ export function middleware(request: NextRequest) {
|
||||
|
||||
// See "Matching Paths" below to learn more
|
||||
export const config = {
|
||||
matcher: ["/health", "/", "/not-found", "/consent", "/login", "/confirm"],
|
||||
matcher: [
|
||||
"/health",
|
||||
"/",
|
||||
"/not-found",
|
||||
"/consent",
|
||||
"/login",
|
||||
"/confirm/:path*",
|
||||
],
|
||||
};
|
||||
|
@ -156,6 +156,9 @@ export interface ConfirmRouteProps {
|
||||
defaultPage?: string;
|
||||
socketUrl?: string;
|
||||
children: ReactNode;
|
||||
confirmLinkResult: TConfirmLinkResult;
|
||||
confirmLinkParams: TConfirmLinkParams;
|
||||
confirmType: string;
|
||||
}
|
||||
|
||||
export type GreetingContainersProps = {
|
||||
|
@ -46,7 +46,11 @@ import {
|
||||
TVersionBuild,
|
||||
} from "@docspace/shared/api/settings/types";
|
||||
import { Encoder } from "@docspace/shared/utils/encoder";
|
||||
import { TTfaSecretKeyAndQR } from "@/types";
|
||||
import {
|
||||
TConfirmLinkParams,
|
||||
TConfirmLinkResult,
|
||||
TTfaSecretKeyAndQR,
|
||||
} from "@/types";
|
||||
import { TScope } from "@docspace/shared/utils/oauth/types";
|
||||
import { transformToClientProps } from "@docspace/shared/utils/oauth";
|
||||
|
||||
@ -369,3 +373,20 @@ export async function getTfaSecretKeyAndQR(confirmKey: string | null = null) {
|
||||
|
||||
return tfaSecretKeyAndQR.response as TTfaSecretKeyAndQR;
|
||||
}
|
||||
|
||||
export async function checkConfirmLink(data: TConfirmLinkParams) {
|
||||
const [checkConfirmLink] = createRequest(
|
||||
[`/authentication/confirm`],
|
||||
[["Content-Type", "application/json"]],
|
||||
"POST",
|
||||
JSON.stringify(data),
|
||||
);
|
||||
|
||||
const response = await fetch(checkConfirmLink);
|
||||
|
||||
if (!response.ok) throw new Error(response.statusText);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result.response as TConfirmLinkResult;
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ export const initSSR = (headers: Record<string, string>) => {
|
||||
client.initSSR(headers);
|
||||
};
|
||||
|
||||
export const request = (
|
||||
export const request = <T>(
|
||||
options: TReqOption & AxiosRequestConfig,
|
||||
skipRedirect = false,
|
||||
isOAuth = false,
|
||||
) => {
|
||||
return client.request(options, skipRedirect, isOAuth);
|
||||
): Promise<T> | undefined => {
|
||||
return client.request<T>(options, skipRedirect, isOAuth);
|
||||
};
|
||||
|
||||
export const setWithCredentialsStatus = (state: boolean) => {
|
||||
|
@ -146,6 +146,14 @@ export const addGroupMembers = (groupId: string, members: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const removeGroupMembers = (groupId: string, membersIds: string[]) => {
|
||||
return request({
|
||||
method: "delete",
|
||||
url: `/group/${groupId}/members`,
|
||||
data: { id: groupId, members: membersIds },
|
||||
}) as Promise<TGroup>;
|
||||
};
|
||||
|
||||
// * Delete
|
||||
|
||||
export const deleteGroup = (groupId: string) => {
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
IClientReqDTO,
|
||||
TConsentData,
|
||||
TConsentList,
|
||||
TGenerateDeveloperToken,
|
||||
} from "../../utils/oauth/types";
|
||||
|
||||
export const getClient = async (clientId: string): Promise<IClientProps> => {
|
||||
@ -237,3 +238,38 @@ export const onOAuthCancel = (clientId: string, clientState: string) => {
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
export const generateDevelopToken = (
|
||||
client_id: string,
|
||||
client_secret: string,
|
||||
scopes: string[],
|
||||
): Promise<TGenerateDeveloperToken> | undefined => {
|
||||
const params = new URLSearchParams();
|
||||
params.append("grant_type", "personal_access_token");
|
||||
params.append("client_id", client_id);
|
||||
params.append("client_secret", client_secret);
|
||||
params.append("scope", scopes.join(" "));
|
||||
|
||||
return request<TGenerateDeveloperToken>(
|
||||
{ method: "post", url: "/oauth2/token", data: params },
|
||||
false,
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
export const revokeDeveloperToken = (
|
||||
token: string,
|
||||
client_id: string,
|
||||
client_secret: string,
|
||||
) => {
|
||||
const params = new URLSearchParams();
|
||||
params.append("token", token);
|
||||
params.append("client_id", client_id);
|
||||
params.append("client_secret", client_secret);
|
||||
|
||||
return request(
|
||||
{ method: "post", url: "/oauth2/revoke", data: params },
|
||||
false,
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ import { getObjectByLocation, toUrlParams } from "../../utils/common";
|
||||
const DEFAULT_PAGE = 0;
|
||||
const DEFAULT_PAGE_COUNT = 25;
|
||||
const DEFAULT_TOTAL = 0;
|
||||
const DEFAULT_SORT_BY = "firstname";
|
||||
const DEFAULT_SORT_BY = "displayname";
|
||||
const DEFAULT_SORT_ORDER = "ascending";
|
||||
const DEFAULT_EMPLOYEE_STATUS = null;
|
||||
const DEFAULT_ACTIVATION_STATUS = null;
|
||||
|
@ -29,7 +29,7 @@ import moment from "moment";
|
||||
export const getCalendarYears = (observedDate: moment.Moment) => {
|
||||
const years = [];
|
||||
const selectedYear = observedDate.year();
|
||||
const firstYear = selectedYear - (selectedYear % 10) - 1;
|
||||
const firstYear = selectedYear - 1;
|
||||
|
||||
for (let i = firstYear; i <= firstYear + 15; i += 1) {
|
||||
years.push(moment(i, "YYYY").format("YYYY"));
|
||||
|
@ -26,21 +26,7 @@
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Base, TTheme, globalColors } from "../../themes";
|
||||
import { tablet, mobile, getCorrectFourValuesStyle } from "../../utils";
|
||||
|
||||
const styledTabletView = css<{ articleWidth: number }>`
|
||||
position: fixed;
|
||||
width: ${(props) => props.theme.newContextMenu.devices.tabletWidth};
|
||||
max-width: ${(props) => props.theme.newContextMenu.devices.tabletWidth};
|
||||
max-height: ${(props) => props.theme.newContextMenu.devices.maxHeight};
|
||||
inset-inline-start: ${(props) =>
|
||||
props.articleWidth
|
||||
? `${props.articleWidth}px`
|
||||
: props.theme.newContextMenu.devices.left};
|
||||
inset-inline-end: ${(props) => props.theme.newContextMenu.devices.right};
|
||||
bottom: ${(props) => props.theme.newContextMenu.devices.bottom};
|
||||
margin: ${(props) => props.theme.newContextMenu.devices.margin};
|
||||
`;
|
||||
import { mobile, getCorrectFourValuesStyle } from "../../utils";
|
||||
|
||||
const styledMobileView = css`
|
||||
position: fixed;
|
||||
@ -97,14 +83,13 @@ const StyledContextMenu = styled.div<{
|
||||
box-shadow: ${(props) => props.theme.newContextMenu.boxShadow};
|
||||
-moz-box-shadow: ${(props) => props.theme.newContextMenu.boxShadow};
|
||||
-webkit-box-shadow: ${(props) => props.theme.newContextMenu.boxShadow};
|
||||
padding: ${(props) => props.theme.newContextMenu.padding};
|
||||
|
||||
@media ${tablet} {
|
||||
${(props) => props.changeView && styledTabletView}
|
||||
}
|
||||
.scroll-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
@media ${mobile} {
|
||||
${(props) => props.changeView && styledMobileView}
|
||||
padding-inline-end: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +190,6 @@ const StyledContextMenu = styled.div<{
|
||||
box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
padding: 4px 0px;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@ -226,7 +210,7 @@ const StyledContextMenu = styled.div<{
|
||||
position: relative;
|
||||
border: ${(props) => props.theme.dropDownItem.border};
|
||||
margin: ${(props) => props.theme.dropDownItem.margin};
|
||||
padding: ${(props) => props.theme.dropDownItem.padding};
|
||||
padding: 0 16px;
|
||||
font-family: ${(props) => props.theme.fontFamily};
|
||||
font-style: normal;
|
||||
background: none;
|
||||
@ -241,10 +225,6 @@ const StyledContextMenu = styled.div<{
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.noHover
|
||||
@ -275,7 +255,7 @@ const StyledContextMenu = styled.div<{
|
||||
cursor: default !important;
|
||||
margin: ${(props) => props.theme.menuItem.separator.margin};
|
||||
height: ${(props) => props.theme.menuItem.separator.height};
|
||||
width: ${(props) => props.theme.menuItem.separator.width};
|
||||
|
||||
&:hover {
|
||||
cursor: default !important;
|
||||
}
|
||||
@ -284,6 +264,14 @@ const StyledContextMenu = styled.div<{
|
||||
.p-contextmenu .p-menuitem {
|
||||
position: relative;
|
||||
margin: ${(props) => props.theme.dropDownItem.margin};
|
||||
|
||||
max-width: calc(-32px + 100vw);
|
||||
width: fit-content;
|
||||
min-width: inherit;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.p-contextmenu .scroll-body .p-menuitem {
|
||||
@ -361,10 +349,47 @@ const StyledContextMenu = styled.div<{
|
||||
opacity: 1;
|
||||
transition: opacity 250ms;
|
||||
}
|
||||
|
||||
.p-contextmenu {
|
||||
@media ${mobile} {
|
||||
${(props) => props.changeView && styledMobileView}
|
||||
}
|
||||
|
||||
@media not ${mobile} {
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
}
|
||||
|
||||
.p-contextmenu ul {
|
||||
@media not ${mobile} {
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledContextMenu.defaultProps = {
|
||||
theme: Base,
|
||||
};
|
||||
|
||||
export const StyledList = styled.ul<{
|
||||
listHeight: number;
|
||||
widthSubMenu: null | number;
|
||||
}>`
|
||||
& > :first-child {
|
||||
.scroll-body {
|
||||
height: ${(props) => `${props.listHeight}px`};
|
||||
}
|
||||
}
|
||||
|
||||
& > :nth-child(1) {
|
||||
${(props) =>
|
||||
props.widthSubMenu &&
|
||||
css`
|
||||
.p-menuitem {
|
||||
max-width: ${`${props.widthSubMenu}px`};
|
||||
}
|
||||
`}
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledContextMenu };
|
||||
|
@ -57,6 +57,8 @@ import {
|
||||
ContextMenuRefType,
|
||||
} from "./ContextMenu.types";
|
||||
|
||||
const marginBorder = 16; // Indentation from the border of the screen
|
||||
|
||||
const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
(props, ref) => {
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
@ -65,9 +67,11 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
const [model, setModel] = React.useState<ContextMenuModel[] | null>(null);
|
||||
const [changeView, setChangeView] = React.useState(false);
|
||||
const [showMobileMenu, setShowMobileMenu] = React.useState(false);
|
||||
const [onLoad, setOnLoad] = React.useState<
|
||||
undefined | (() => Promise<ContextMenuModel[]>)
|
||||
>(undefined);
|
||||
const [mobileSubMenuItems, setMobileSubMenuItems] = React.useState<
|
||||
ContextMenuModel[] | undefined
|
||||
>([]);
|
||||
const [mobileHeader, setMobileHeader] = React.useState<string>("");
|
||||
|
||||
const [articleWidth, setArticleWidth] = React.useState(0);
|
||||
|
||||
const prevReshow = React.useRef(false);
|
||||
@ -211,7 +215,7 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
? rects.left - currentLeftOffset - currentRightOffset
|
||||
: event.pageX + 1;
|
||||
let top = rects ? rects.top : event.pageY + 1;
|
||||
const width =
|
||||
let width =
|
||||
menuRef.current && menuRef.current.offsetParent
|
||||
? menuRef.current.offsetWidth
|
||||
: DomHelpers.getHiddenElementOuterWidth(menuRef.current);
|
||||
@ -221,29 +225,30 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
: DomHelpers.getHiddenElementOuterHeight(menuRef.current);
|
||||
const viewport = DomHelpers.getViewport();
|
||||
|
||||
const mobileView =
|
||||
isMobileUtils() && (height > 210 || ignoreChangeView);
|
||||
|
||||
if (!mobileView) {
|
||||
const options =
|
||||
menuRef?.current?.getElementsByClassName("p-menuitem");
|
||||
const optionsWidth: number[] = [];
|
||||
|
||||
if (options) {
|
||||
Array.from(options).forEach((option) =>
|
||||
optionsWidth.push(option.clientWidth),
|
||||
);
|
||||
|
||||
const widthMaxContent = Math.max(...optionsWidth);
|
||||
|
||||
width = widthMaxContent;
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.interfaceDirection === "rtl" && !rects && left > width) {
|
||||
left = event.pageX - width + 1;
|
||||
}
|
||||
|
||||
if (
|
||||
isTabletUtils() &&
|
||||
(height > 483 ||
|
||||
(isMobileOnly && window.innerHeight < window.innerWidth))
|
||||
) {
|
||||
const article = document.getElementById("article-container");
|
||||
|
||||
let currentArticleWidth = 0;
|
||||
if (article) {
|
||||
currentArticleWidth = article.offsetWidth;
|
||||
}
|
||||
|
||||
setChangeView(true);
|
||||
setArticleWidth(currentArticleWidth);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMobileUtils() && (height > 210 || ignoreChangeView)) {
|
||||
if (mobileView) {
|
||||
setChangeView(true);
|
||||
setArticleWidth(0);
|
||||
|
||||
@ -267,7 +272,8 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
|
||||
// fit
|
||||
if (top < document.body.scrollTop) {
|
||||
top = document.body.scrollTop;
|
||||
if (document.body.scrollTop === 0) top = marginBorder;
|
||||
else top = document.body.scrollTop;
|
||||
}
|
||||
|
||||
if (containerRef) {
|
||||
@ -281,8 +287,10 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
}
|
||||
}
|
||||
if (menuRef.current) {
|
||||
menuRef.current.style.left = `${left}px`;
|
||||
menuRef.current.style.left = `${left || marginBorder}px`;
|
||||
menuRef.current.style.top = `${top}px`;
|
||||
|
||||
if (!mobileView) menuRef.current.style.width = `${width}px`;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -410,14 +418,20 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
};
|
||||
}, [documentResizeListener, onHide, visible]);
|
||||
|
||||
const onMobileItemClick = (
|
||||
const onMobileItemClick = async (
|
||||
e: React.MouseEvent | React.ChangeEvent<HTMLInputElement>,
|
||||
label: string,
|
||||
items?: ContextMenuModel[],
|
||||
loadFunc?: () => Promise<ContextMenuModel[]>,
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setShowMobileMenu(true);
|
||||
if (loadFunc) setOnLoad(loadFunc);
|
||||
|
||||
const res = loadFunc ? await loadFunc() : items;
|
||||
setMobileSubMenuItems(res);
|
||||
|
||||
setMobileHeader(label);
|
||||
};
|
||||
|
||||
const onBackClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@ -517,7 +531,7 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
)}
|
||||
|
||||
<Text className="text" truncate dir="auto">
|
||||
{header.title}
|
||||
{showMobileMenu ? mobileHeader : header.title}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
@ -527,7 +541,7 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
root
|
||||
resetMenu={resetMenu}
|
||||
onLeafClick={onLeafClick}
|
||||
onLoad={onLoad}
|
||||
mobileSubMenuItems={mobileSubMenuItems}
|
||||
/>
|
||||
) : (
|
||||
<SubMenu
|
||||
@ -535,8 +549,9 @@ const ContextMenu = React.forwardRef<ContextMenuRefType, ContextMenuProps>(
|
||||
root
|
||||
resetMenu={resetMenu}
|
||||
onLeafClick={onLeafClick}
|
||||
changeView={changeView}
|
||||
onMobileItemClick={onMobileItemClick}
|
||||
changeView={changeView}
|
||||
withHeader={withHeader}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -44,9 +44,9 @@ const MobileSubMenu = (props: {
|
||||
onLeafClick: (e: React.MouseEvent) => void;
|
||||
root?: boolean;
|
||||
resetMenu: boolean;
|
||||
onLoad?: () => Promise<ContextMenuModel[]>;
|
||||
mobileSubMenuItems?: ContextMenuModel[];
|
||||
}) => {
|
||||
const { onLeafClick, root, resetMenu, onLoad } = props;
|
||||
const { onLeafClick, root, resetMenu, mobileSubMenuItems } = props;
|
||||
|
||||
const [submenu, setSubmenu] = useState<null | ContextMenuModel[]>(null);
|
||||
|
||||
@ -91,16 +91,12 @@ const MobileSubMenu = (props: {
|
||||
}
|
||||
});
|
||||
|
||||
const fetchSubMenu = React.useCallback(async () => {
|
||||
const res = await onLoad?.();
|
||||
if (res) setSubmenu(res);
|
||||
|
||||
position();
|
||||
}, [position, setSubmenu, onLoad]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onLoad) fetchSubMenu();
|
||||
}, [onLoad, fetchSubMenu]);
|
||||
if (!mobileSubMenuItems?.length) return;
|
||||
|
||||
setSubmenu(mobileSubMenuItems);
|
||||
position();
|
||||
}, [mobileSubMenuItems, mobileSubMenuItems?.length, position]);
|
||||
|
||||
const onItemClick = (e: React.MouseEvent, item: ContextMenuType) => {
|
||||
const { disabled, url, onClick, items, action } = item;
|
||||
|
@ -33,20 +33,22 @@ import { useTheme } from "styled-components";
|
||||
import ArrowIcon from "PUBLIC_DIR/images/arrow.right.react.svg";
|
||||
import OutsdideIcon from "PUBLIC_DIR/images/arrow.outside.react.svg";
|
||||
import { isMobile as isMobileDevice } from "react-device-detect";
|
||||
|
||||
import { classNames, ObjectUtils, DomHelpers } from "../../../utils";
|
||||
import { classNames, ObjectUtils, DomHelpers, isMobile } from "../../../utils";
|
||||
import { ContextMenuSkeleton } from "../../../skeletons/context-menu";
|
||||
|
||||
import { Scrollbar } from "../../scrollbar";
|
||||
import { ToggleButton } from "../../toggle-button";
|
||||
import { Scrollbar } from "../../scrollbar";
|
||||
|
||||
import { SubMenuItem } from "../ContextMenu.styled";
|
||||
import { SubMenuItem, StyledList } from "../ContextMenu.styled";
|
||||
import {
|
||||
ContextMenuModel,
|
||||
ContextMenuType,
|
||||
SeparatorType,
|
||||
} from "../ContextMenu.types";
|
||||
|
||||
const submenuListMargin = 4; // Indentation of the second level menu from the first level
|
||||
const sectionPadding = 16; // Screen margin
|
||||
|
||||
const SubMenu = (props: {
|
||||
model: ContextMenuModel[];
|
||||
root?: boolean;
|
||||
@ -57,23 +59,28 @@ const SubMenu = (props: {
|
||||
) => void;
|
||||
onMobileItemClick?: (
|
||||
e: React.MouseEvent | React.ChangeEvent<HTMLInputElement>,
|
||||
loadFunc: () => Promise<ContextMenuModel[]>,
|
||||
label: string,
|
||||
items?: ContextMenuModel[],
|
||||
loadFunc?: () => Promise<ContextMenuModel[]>,
|
||||
) => void;
|
||||
changeView?: boolean;
|
||||
onLoad?: () => Promise<ContextMenuModel[]>;
|
||||
changeView?: boolean;
|
||||
withHeader?: boolean;
|
||||
}) => {
|
||||
const {
|
||||
onLeafClick,
|
||||
root,
|
||||
resetMenu,
|
||||
changeView,
|
||||
onMobileItemClick,
|
||||
onLoad,
|
||||
changeView,
|
||||
withHeader,
|
||||
} = props;
|
||||
|
||||
const [model, setModel] = useState(props?.model);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [activeItem, setActiveItem] = useState<ContextMenuType | null>(null);
|
||||
const [widthSubMenu, setWidthSubMenu] = useState<null | number>(null);
|
||||
|
||||
const subMenuRef = useRef<HTMLUListElement>(null);
|
||||
|
||||
@ -92,15 +99,19 @@ const SubMenu = (props: {
|
||||
e: React.MouseEvent | React.ChangeEvent<HTMLInputElement>,
|
||||
item: ContextMenuType,
|
||||
) => {
|
||||
const { url, onClick, items, action } = item;
|
||||
const { disabled, url, onClick, items, action, label } = item;
|
||||
|
||||
if (item.onLoad) {
|
||||
if (isMobile() && label && (items || item.onLoad)) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!isMobileDevice) return;
|
||||
|
||||
onMobileItemClick?.(e, item.onLoad);
|
||||
if (items) onMobileItemClick?.(e, label as string, items, undefined);
|
||||
else if (item.onLoad)
|
||||
onMobileItemClick?.(e, label as string, undefined, item.onLoad);
|
||||
return;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,7 +137,9 @@ const SubMenu = (props: {
|
||||
const containerOffset = DomHelpers.getOffset(parentItem);
|
||||
const viewport = DomHelpers.getViewport();
|
||||
|
||||
const subListWidth = subMenuRef.current?.offsetParent
|
||||
const options = subMenuRef.current?.getElementsByClassName("p-menuitem");
|
||||
|
||||
let subListWidth = subMenuRef.current?.offsetParent
|
||||
? subMenuRef.current.offsetWidth
|
||||
: DomHelpers.getHiddenElementOuterWidth(subMenuRef.current);
|
||||
|
||||
@ -136,42 +149,90 @@ const SubMenu = (props: {
|
||||
|
||||
const isRtl = theme.interfaceDirection === "rtl";
|
||||
|
||||
if (!isMobile() && options) {
|
||||
const optionsWidth: number[] = [];
|
||||
Array.from(options).forEach((option) =>
|
||||
optionsWidth.push(Math.ceil(option.getBoundingClientRect().width)),
|
||||
);
|
||||
|
||||
const widthMaxContent = Math.max(...optionsWidth);
|
||||
|
||||
if (root) subListWidth = subListWidth || widthMaxContent;
|
||||
else if (!subMenuRef?.current?.style.width)
|
||||
subListWidth = Math.max(subListWidth, widthMaxContent);
|
||||
}
|
||||
|
||||
if (subMenuRef.current) {
|
||||
subMenuRef.current.style.top = "0px";
|
||||
let subMenuRefTop = null;
|
||||
|
||||
if (!isMobile()) {
|
||||
if (root) subMenuRef.current.style.width = `${subListWidth}px`;
|
||||
else if (!subMenuRef?.current?.style.width) {
|
||||
subMenuRef.current.style.width = `${subListWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMobile() && !root) {
|
||||
const firstList = parentItem?.firstChild as HTMLElement;
|
||||
|
||||
const menuItemActive = firstList.querySelector(
|
||||
".p-menuitem-active",
|
||||
) as HTMLElement;
|
||||
|
||||
const top = menuItemActive.offsetTop;
|
||||
const scroller = firstList.querySelector(".scroller") as HTMLElement;
|
||||
const scrollTop = scroller.scrollTop;
|
||||
const positionActiveItem = top - scrollTop;
|
||||
|
||||
subMenuRefTop = positionActiveItem - 2;
|
||||
subMenuRef.current.style.top = `${subMenuRefTop}px`;
|
||||
}
|
||||
|
||||
const submenuRects = subMenuRef.current.getBoundingClientRect();
|
||||
|
||||
if (submenuRects.bottom > viewport.height) {
|
||||
if (submenuRects.bottom > viewport.height && subMenuRefTop) {
|
||||
const submenuMargin = 16;
|
||||
const topOffset = submenuRects.bottom - viewport.height + submenuMargin;
|
||||
|
||||
subMenuRef.current.style.top = `${-1 * topOffset}px`;
|
||||
const topOffset =
|
||||
subMenuRefTop -
|
||||
(submenuRects.bottom - viewport.height) -
|
||||
submenuMargin;
|
||||
|
||||
subMenuRef.current.style.top = `${topOffset}px`;
|
||||
}
|
||||
|
||||
const containerOffsetLeft = parseInt(`${containerOffset.left}`, 10);
|
||||
const freeSpaceRight =
|
||||
viewport.width - containerOffsetLeft - itemOuterWidth;
|
||||
const freeSpaceLeft = containerOffsetLeft;
|
||||
const submenuListMargin = 4;
|
||||
const sectionPadding = 17;
|
||||
|
||||
if (isRtl) {
|
||||
if (
|
||||
!root &&
|
||||
freeSpaceLeft > freeSpaceRight &&
|
||||
subListWidth > containerOffsetLeft
|
||||
) {
|
||||
// If the menu extends beyond the screen
|
||||
subMenuRef.current.style.width = `${containerOffsetLeft - submenuListMargin - sectionPadding}px`;
|
||||
}
|
||||
|
||||
if (
|
||||
subListWidth < containerOffsetLeft ||
|
||||
(!root && freeSpaceLeft > freeSpaceRight)
|
||||
) {
|
||||
subMenuRef.current.style.left = `${-1 * subListWidth}px`;
|
||||
|
||||
if (!root && subListWidth > containerOffsetLeft) {
|
||||
// If the menu extends beyond the screen
|
||||
const newWidth =
|
||||
containerOffsetLeft - submenuListMargin - sectionPadding;
|
||||
|
||||
subMenuRef.current.style.width = `${newWidth}px`;
|
||||
setWidthSubMenu(newWidth);
|
||||
}
|
||||
} else {
|
||||
subMenuRef.current.style.left = `${itemOuterWidth}px`;
|
||||
|
||||
if (!root) subMenuRef.current.style.marginLeft = `4px`;
|
||||
|
||||
if (!root && subListWidth > freeSpaceRight) {
|
||||
// If the menu extends beyond the screen
|
||||
const newWidth = freeSpaceRight - 3 * submenuListMargin;
|
||||
|
||||
subMenuRef.current.style.width = `${newWidth}px`;
|
||||
setWidthSubMenu(newWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,8 +241,22 @@ const SubMenu = (props: {
|
||||
viewport.width - DomHelpers.calculateScrollbarWidth();
|
||||
|
||||
if (!isRtl) {
|
||||
if (notEnoughWidthRight && containerOffsetLeft > subListWidth) {
|
||||
if (notEnoughWidthRight && freeSpaceLeft > freeSpaceRight) {
|
||||
subMenuRef.current.style.left = `${-1 * subListWidth}px`;
|
||||
|
||||
if (!root) subMenuRef.current.style.marginLeft = `-4px`;
|
||||
|
||||
if (
|
||||
notEnoughWidthRight &&
|
||||
!root &&
|
||||
subListWidth > containerOffsetLeft
|
||||
) {
|
||||
// If the menu extends beyond the screen
|
||||
const newWidth = containerOffsetLeft - 12;
|
||||
|
||||
subMenuRef.current.style.width = `${newWidth}px`;
|
||||
setWidthSubMenu(newWidth);
|
||||
}
|
||||
} else {
|
||||
subMenuRef.current.style.left = `${itemOuterWidth}px`;
|
||||
|
||||
@ -195,6 +270,7 @@ const SubMenu = (props: {
|
||||
sectionPadding;
|
||||
|
||||
subMenuRef.current.style.width = `${newWidth}px`;
|
||||
setWidthSubMenu(newWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,29 +307,6 @@ const SubMenu = (props: {
|
||||
/>
|
||||
);
|
||||
|
||||
const renderSubMenu = (item: ContextMenuType) => {
|
||||
const loaderItem = {
|
||||
id: "link-loader-option",
|
||||
key: "link-loader",
|
||||
isLoader: true,
|
||||
label: <ContextMenuSkeleton />,
|
||||
};
|
||||
|
||||
if (item.items || item.onLoad) {
|
||||
return (
|
||||
<SubMenu
|
||||
model={item.onLoad ? [loaderItem] : item.items || []}
|
||||
resetMenu={item !== activeItem}
|
||||
onLeafClick={onLeafClick}
|
||||
// onEnter={onEnter}
|
||||
onLoad={item.onLoad}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderMenuitem = (
|
||||
item: ContextMenuType,
|
||||
index: number,
|
||||
@ -301,7 +354,7 @@ const SubMenu = (props: {
|
||||
const subMenuIcon = (item.items || item.onLoad) && (
|
||||
<ArrowIcon className={subMenuIconClassName} />
|
||||
);
|
||||
const subMenu = renderSubMenu(item);
|
||||
|
||||
const dataKeys = Object.fromEntries(
|
||||
Object.entries(item).filter((el) => el[0].indexOf("data-") === 0),
|
||||
);
|
||||
@ -367,7 +420,6 @@ const SubMenu = (props: {
|
||||
onMouseEnter={(e) => onItemMouseEnter(e, item)}
|
||||
>
|
||||
{content}
|
||||
{subMenu}
|
||||
<ToggleButton
|
||||
isChecked={item.checked || false}
|
||||
onChange={onClick}
|
||||
@ -389,7 +441,6 @@ const SubMenu = (props: {
|
||||
onMouseEnter={(e) => onItemMouseEnter(e, item)}
|
||||
>
|
||||
{content}
|
||||
{subMenu}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
@ -428,78 +479,100 @@ const SubMenu = (props: {
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
if (model) {
|
||||
if (changeView) {
|
||||
const newModel = model.filter(
|
||||
(item: ContextMenuModel) => item && !item.disabled,
|
||||
if (!model) return null;
|
||||
|
||||
return model.map((item: ContextMenuModel, index: number) => {
|
||||
if (item?.disabled) return null;
|
||||
return renderItem(item, index);
|
||||
});
|
||||
};
|
||||
|
||||
const renderSubMenuLower = () => {
|
||||
if (!model) return null;
|
||||
const submenu: JSX.Element[] = [];
|
||||
|
||||
const loaderItem = {
|
||||
id: "link-loader-option",
|
||||
key: "link-loader",
|
||||
isLoader: true,
|
||||
label: <ContextMenuSkeleton />,
|
||||
};
|
||||
|
||||
model.forEach((item) => {
|
||||
const contextMenuTypeItem = item as ContextMenuType;
|
||||
|
||||
if (contextMenuTypeItem?.items || contextMenuTypeItem?.onLoad) {
|
||||
submenu.push(
|
||||
<SubMenu
|
||||
key={`sub-menu_${item.id}`}
|
||||
model={
|
||||
contextMenuTypeItem?.onLoad
|
||||
? [loaderItem]
|
||||
: contextMenuTypeItem?.items || []
|
||||
}
|
||||
resetMenu={item !== activeItem}
|
||||
onLeafClick={onLeafClick}
|
||||
onLoad={contextMenuTypeItem?.onLoad}
|
||||
/>,
|
||||
);
|
||||
const rowHeights: number[] = newModel.map((item: ContextMenuModel) => {
|
||||
if (!item) return 0;
|
||||
if (item.isSeparator) return 13;
|
||||
return 36;
|
||||
});
|
||||
|
||||
// const getItemSize = (index) => rowHeights[index];
|
||||
|
||||
const height = rowHeights.reduce((a, b) => a + b);
|
||||
const viewport = DomHelpers.getViewport();
|
||||
|
||||
const listHeight =
|
||||
height + 61 > viewport.height - 64
|
||||
? viewport.height - 125
|
||||
: height + 5;
|
||||
|
||||
return (
|
||||
<Scrollbar style={{ height: listHeight }}>
|
||||
{model.map((item: ContextMenuModel, index: number) => {
|
||||
if (!item || item?.disabled) return null;
|
||||
|
||||
return renderItem(item, index);
|
||||
})}
|
||||
</Scrollbar>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <VariableSizeList
|
||||
// height={listHeight}
|
||||
// width={"auto"}
|
||||
// itemCount={newModel.length}
|
||||
// itemSize={getItemSize}
|
||||
// itemData={newModel}
|
||||
// outerElementType={CustomScrollbarsVirtualList}
|
||||
// >
|
||||
// {renderItem}
|
||||
// </VariableSizeList>
|
||||
// );
|
||||
}
|
||||
});
|
||||
|
||||
return model.map((item: ContextMenuModel, index: number) => {
|
||||
if (item?.disabled) return null;
|
||||
return renderItem(item, index);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
return submenu;
|
||||
};
|
||||
|
||||
const className = classNames({ "p-submenu-list": !root });
|
||||
const submenu = renderMenu();
|
||||
const active = isActive();
|
||||
const submenuLower = renderSubMenuLower();
|
||||
|
||||
return (
|
||||
<CSSTransition
|
||||
nodeRef={subMenuRef}
|
||||
classNames="p-contextmenusub"
|
||||
in={active}
|
||||
timeout={{ enter: 0, exit: 0 }}
|
||||
unmountOnExit
|
||||
onEnter={onEnter}
|
||||
>
|
||||
<ul ref={subMenuRef} className={`${className} not-selectable`}>
|
||||
{submenu}
|
||||
</ul>
|
||||
</CSSTransition>
|
||||
);
|
||||
if (model.length) {
|
||||
const newModel = model.filter(
|
||||
(item: ContextMenuModel) => item && !item.disabled,
|
||||
);
|
||||
const rowHeights: number[] = newModel.map((item: ContextMenuModel) => {
|
||||
if (!item) return 0;
|
||||
if (item.isSeparator) return 13;
|
||||
return 36;
|
||||
});
|
||||
|
||||
const height = rowHeights.reduce((a, b) => a + b);
|
||||
const viewport = DomHelpers.getViewport();
|
||||
const paddingList = 12;
|
||||
const marginsList = 32;
|
||||
const backdrop = 64;
|
||||
const header = 55;
|
||||
|
||||
const listHeight =
|
||||
changeView && withHeader
|
||||
? height + paddingList + header > viewport.height
|
||||
? viewport.height - backdrop - header - paddingList
|
||||
: height + paddingList
|
||||
: height + paddingList + marginsList > viewport.height
|
||||
? viewport.height - marginsList
|
||||
: height + paddingList;
|
||||
|
||||
return (
|
||||
<CSSTransition
|
||||
nodeRef={subMenuRef}
|
||||
classNames="p-contextmenusub"
|
||||
in={active}
|
||||
timeout={{ enter: 0, exit: 0 }}
|
||||
unmountOnExit
|
||||
onEnter={onEnter}
|
||||
>
|
||||
<StyledList
|
||||
ref={subMenuRef}
|
||||
className={`${className} not-selectable`}
|
||||
listHeight={height + paddingList}
|
||||
widthSubMenu={widthSubMenu}
|
||||
>
|
||||
<Scrollbar style={{ height: listHeight }}>{submenu}</Scrollbar>
|
||||
{submenuLower}
|
||||
</StyledList>
|
||||
</CSSTransition>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { SubMenu };
|
||||
|
@ -94,7 +94,7 @@ const StyledDropdown = styled.div<{
|
||||
border: ${(props) => props.theme.dropDown.border};
|
||||
border-radius: ${(props) => props.theme.dropDown.borderRadius};
|
||||
-moz-border-radius: ${(props) => props.theme.dropDown.borderRadius};
|
||||
-webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius};dropDownMaxHeight
|
||||
-webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius};
|
||||
box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
|
||||
|
@ -42,7 +42,7 @@ const StyledErrorContainer = styled.div<{ isEditor: boolean }>`
|
||||
css`
|
||||
position: absolute;
|
||||
`}
|
||||
overflow-x: hidden;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
@ -996,6 +996,7 @@ export const ImageViewer = ({
|
||||
onDoubleClick={handleDoubleTapOrClick}
|
||||
onLoad={imageLoaded}
|
||||
onError={onError}
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
/>
|
||||
</ImageWrapper>
|
||||
</ImageViewerContainer>
|
||||
|
@ -36,7 +36,9 @@ export const PlayerBigPlayButton = ({
|
||||
if (!visible) return;
|
||||
|
||||
return (
|
||||
<WrapperPlayerBigPlayButton>
|
||||
<WrapperPlayerBigPlayButton
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
>
|
||||
<BigIconPlay onClick={onClick} />
|
||||
</WrapperPlayerBigPlayButton>
|
||||
);
|
||||
|
@ -406,6 +406,10 @@ export const ViewerPlayer = ({
|
||||
const percent = Number(event.target.value);
|
||||
const newCurrentTime = (percent / 100) * videoRef.current.duration;
|
||||
|
||||
const videoCurrentTime = videoRef.current.currentTime;
|
||||
|
||||
if (Math.abs(newCurrentTime - videoCurrentTime) <= 0.1) return;
|
||||
|
||||
handleProgress();
|
||||
setTimeline(percent);
|
||||
setCurrentTime(newCurrentTime);
|
||||
@ -653,6 +657,7 @@ export const ViewerPlayer = ({
|
||||
onDurationChange={handleDurationChange}
|
||||
onLoadedMetadata={handleLoadedMetaDataVideo}
|
||||
onPlay={() => setIsPlaying(true)}
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
/>
|
||||
<PlayerBigPlayButton
|
||||
onClick={handleBigPlayButtonClick}
|
||||
|
@ -79,7 +79,8 @@ const ControlButtons = ({
|
||||
onClick={onNavigationButtonClick}
|
||||
/>
|
||||
) : null;
|
||||
const children = tariffBar ? React.cloneElement(tariffBar, { title }) : null;
|
||||
const children =
|
||||
tariffBar && !isFrame ? React.cloneElement(tariffBar, { title }) : null;
|
||||
const isTabletView = isTablet();
|
||||
|
||||
const contextOptionsFolder = getContextOptionsFolder();
|
||||
|
@ -63,7 +63,7 @@ const AccessSelector = (props: AccessSelectorProps) => {
|
||||
options={accessRights as TOption[]}
|
||||
size={ComboBoxSize.content}
|
||||
scaled={false}
|
||||
manualWidth="fit-content"
|
||||
manualWidth="auto"
|
||||
selectedOption={selectedAccessRight as TOption}
|
||||
showDisabledItems
|
||||
directionX="right"
|
||||
@ -81,7 +81,7 @@ const AccessSelector = (props: AccessSelectorProps) => {
|
||||
directionX="right"
|
||||
directionY="top"
|
||||
fixedDirection={isMobileView}
|
||||
manualWidth={isMobileView ? "fit-content" : `${width}px`}
|
||||
manualWidth={isMobileView ? "auto" : `${width}px`}
|
||||
isAside={isMobileView}
|
||||
manualY={isMobileView ? "0px" : undefined}
|
||||
withoutBackground={isMobileView}
|
||||
|
@ -124,7 +124,9 @@ const Body = ({
|
||||
|
||||
const onBodyResize = React.useCallback(() => {
|
||||
if (bodyRef && bodyRef.current) {
|
||||
setBodyHeight(bodyRef.current.offsetHeight);
|
||||
setTimeout(() => {
|
||||
setBodyHeight(bodyRef.current!.offsetHeight);
|
||||
}, 20);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -480,7 +480,7 @@ export const enum ParseErrorTypes {
|
||||
}
|
||||
|
||||
export const enum ButtonKeys {
|
||||
enter = "enter",
|
||||
enter = "Enter",
|
||||
numpadEnter = "NumpadEnter",
|
||||
esc = "Escape",
|
||||
tab = "Tab",
|
||||
|
@ -2511,9 +2511,8 @@ export const getBaseTheme = () => {
|
||||
},
|
||||
separator: {
|
||||
borderBottom: `1px solid ${grayLightMid} !important`,
|
||||
margin: "6px 16px 6px 16px !important",
|
||||
margin: "6px 16px !important",
|
||||
height: "1px !important",
|
||||
width: "calc(100% - 32px) !important",
|
||||
},
|
||||
text: {
|
||||
header: {
|
||||
@ -2534,7 +2533,7 @@ export const getBaseTheme = () => {
|
||||
background: "none",
|
||||
svgFill: black,
|
||||
header: {
|
||||
height: "49px",
|
||||
height: "55px",
|
||||
borderBottom: `1px solid ${grayLightMid}`,
|
||||
marginBottom: "6px",
|
||||
},
|
||||
@ -2544,7 +2543,7 @@ export const getBaseTheme = () => {
|
||||
padding: "0 12px",
|
||||
mobile: {
|
||||
height: "36px",
|
||||
padding: "0 16px 6px",
|
||||
padding: "6px 16px",
|
||||
},
|
||||
},
|
||||
newContextMenu: {
|
||||
@ -2552,7 +2551,6 @@ export const getBaseTheme = () => {
|
||||
borderRadius: "6px",
|
||||
mobileBorderRadius: "6px 6px 0 0",
|
||||
boxShadow: `0px 8px 16px 0px ${boxShadowColor}`,
|
||||
padding: "6px 0px",
|
||||
border: "none",
|
||||
devices: {
|
||||
maxHeight: "calc(100vh - 64px)",
|
||||
|
@ -663,7 +663,7 @@ const Dark: TTheme = {
|
||||
|
||||
input: {
|
||||
color: white,
|
||||
disableColor: grayDarkStrong,
|
||||
disableColor: grayDarkText,
|
||||
|
||||
backgroundColor: black,
|
||||
disableBackgroundColor: grayDarkStrong,
|
||||
@ -2498,7 +2498,6 @@ const Dark: TTheme = {
|
||||
borderBottom: `1px solid ${grayDarkStrong} !important`,
|
||||
margin: "6px 16px 6px 16px !important",
|
||||
height: "1px !important",
|
||||
width: "calc(100% - 32px) !important",
|
||||
},
|
||||
text: {
|
||||
header: {
|
||||
@ -2519,7 +2518,7 @@ const Dark: TTheme = {
|
||||
background: "none",
|
||||
svgFill: white,
|
||||
header: {
|
||||
height: "49px",
|
||||
height: "55px",
|
||||
borderBottom: `1px solid ${grayDarkStrong}`,
|
||||
marginBottom: "6px",
|
||||
},
|
||||
@ -2529,7 +2528,7 @@ const Dark: TTheme = {
|
||||
padding: "0 12px",
|
||||
mobile: {
|
||||
height: "36px",
|
||||
padding: "0 16px 6px",
|
||||
padding: "6px 16px",
|
||||
},
|
||||
},
|
||||
newContextMenu: {
|
||||
@ -2537,7 +2536,6 @@ const Dark: TTheme = {
|
||||
borderRadius: "6px",
|
||||
mobileBorderRadius: "6px 6px 0 0",
|
||||
boxShadow: `0px 8px 16px 0px ${boxShadowDarkColor}`,
|
||||
padding: "6px 0px",
|
||||
border: `1px solid ${grayDarkStrong}`,
|
||||
devices: {
|
||||
maxHeight: "calc(100vh - 64px)",
|
||||
|
@ -75,6 +75,7 @@ export type TFrameEvents = {
|
||||
onAuthSuccess: null | ((e: Event) => void);
|
||||
onSignOut: null | ((e: Event) => void);
|
||||
onDownload: null | ((e: Event) => void);
|
||||
onNoAccess: null | ((e: Event) => void);
|
||||
};
|
||||
|
||||
export type TFrameConfig = {
|
||||
@ -96,6 +97,7 @@ export type TFrameConfig = {
|
||||
showSelectorCancel: boolean;
|
||||
showSelectorHeader: boolean;
|
||||
showHeader: boolean;
|
||||
showHeaderBanner: string;
|
||||
showTitle: boolean;
|
||||
showMenu: boolean;
|
||||
showFilter: boolean;
|
||||
|
@ -182,11 +182,11 @@ class AxiosClient {
|
||||
}
|
||||
};
|
||||
|
||||
request = (
|
||||
request = <T>(
|
||||
options: TReqOption & AxiosRequestConfig,
|
||||
skipRedirect = false,
|
||||
isOAuth = false,
|
||||
) => {
|
||||
): Promise<T> | undefined => {
|
||||
const onSuccess = (response: TRes) => {
|
||||
const error = this.getResponseError(response);
|
||||
|
||||
@ -237,10 +237,12 @@ class AxiosClient {
|
||||
}
|
||||
|
||||
const loginURL = combineUrl(proxyURL, "/login");
|
||||
|
||||
if (!this.isSSR) {
|
||||
switch (error.response?.status) {
|
||||
case 401: {
|
||||
if (options.skipUnauthorized) return Promise.resolve();
|
||||
if (options.skipUnauthorized || window?.ClientConfig?.isFrame)
|
||||
return Promise.resolve();
|
||||
if (options.skipLogout) return Promise.reject(error);
|
||||
|
||||
const opt: AxiosRequestConfig = {
|
||||
@ -261,14 +263,13 @@ class AxiosClient {
|
||||
break;
|
||||
case 403: {
|
||||
const pathname = window.location.pathname;
|
||||
const isFrame = window?.ClientConfig?.isFrame;
|
||||
|
||||
const isArchived = pathname.indexOf("/rooms/archived") !== -1;
|
||||
|
||||
const isRooms =
|
||||
pathname.indexOf("/rooms/shared") !== -1 || isArchived;
|
||||
|
||||
if (isRooms && !skipRedirect && !isFrame) {
|
||||
if (isRooms && !skipRedirect && !window?.ClientConfig?.isFrame) {
|
||||
setTimeout(() => {
|
||||
window.DocSpace.navigate(isArchived ? "/archived" : "/");
|
||||
}, 1000);
|
||||
@ -295,7 +296,9 @@ class AxiosClient {
|
||||
|
||||
return Promise.reject(error);
|
||||
};
|
||||
return this.client?.(options).then(onSuccess).catch(onError);
|
||||
return this.client?.(options).then(onSuccess).catch(onError) as
|
||||
| Promise<T>
|
||||
| undefined;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -150,3 +150,10 @@ export type IClientListProps = List<IClientProps>;
|
||||
export type IClientListDTO = List<IClientResDTO>;
|
||||
|
||||
export type TConsentList = List<TConsentData>;
|
||||
|
||||
export type TGenerateDeveloperToken = {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
scope: string;
|
||||
token_type: string;
|
||||
};
|
||||
|
@ -53,7 +53,7 @@
|
||||
showSignOut: true,
|
||||
destroyText: "",
|
||||
viewAs: "row", //TODO: ["row", "table", "tile"]
|
||||
viewTableColumns: "Name,Type,Tags",
|
||||
viewTableColumns: "Name,Size,Type,Tags",
|
||||
checkCSP: true,
|
||||
disableActionButton: false,
|
||||
showSettings: false,
|
||||
@ -97,6 +97,8 @@
|
||||
onAuthSuccess: null,
|
||||
onSignOut: null,
|
||||
onDownload: null,
|
||||
onNoAccess: null,
|
||||
onContentReady: null,
|
||||
},
|
||||
};
|
||||
|
||||
@ -813,7 +815,10 @@
|
||||
targetFrame.style.height = this.config.height;
|
||||
targetFrame.parentNode.style.height = "inherit";
|
||||
|
||||
if (loader) loader.remove();
|
||||
if (loader) {
|
||||
loader.remove();
|
||||
this.config.events.onContentReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user