Client:OAuth2: add info dialog
This commit is contained in:
parent
7902e9824d
commit
76fdf6e9b6
@ -12,6 +12,9 @@ export interface OAuthProps {
|
||||
clientList: IClientProps[];
|
||||
isEmptyClientList: boolean;
|
||||
fetchClients: () => Promise<void>;
|
||||
fetchScopes: () => Promise<void>;
|
||||
|
||||
currentDeviceType: DeviceUnionType;
|
||||
|
||||
infoDialogVisible?: boolean;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import List from "./sub-components/List";
|
||||
|
||||
import { OAuthContainer } from "./StyledOAuth";
|
||||
import { OAuthProps } from "./OAuth.types";
|
||||
import InfoDialog from "./sub-components/InfoDialog";
|
||||
|
||||
const MIN_LOADER_TIME = 500;
|
||||
|
||||
@ -23,15 +24,21 @@ const OAuth = ({
|
||||
isEmptyClientList,
|
||||
setViewAs,
|
||||
fetchClients,
|
||||
fetchScopes,
|
||||
currentDeviceType,
|
||||
infoDialogVisible,
|
||||
}: OAuthProps) => {
|
||||
const { t } = useTranslation(["OAuth"]);
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
const startLoadingRef = React.useRef<null | Date>(null);
|
||||
|
||||
const getClientList = React.useCallback(async () => {
|
||||
await fetchClients();
|
||||
const getData = React.useCallback(async () => {
|
||||
const actions = [];
|
||||
|
||||
actions.push(fetchScopes(), fetchClients());
|
||||
|
||||
await Promise.all(actions);
|
||||
|
||||
if (startLoadingRef.current) {
|
||||
const currentDate = new Date();
|
||||
@ -57,8 +64,8 @@ const OAuth = ({
|
||||
|
||||
React.useEffect(() => {
|
||||
startLoadingRef.current = new Date();
|
||||
getClientList();
|
||||
}, [getClientList]);
|
||||
getData();
|
||||
}, [getData]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setDocumentTitle(t("OAuth"));
|
||||
@ -66,13 +73,16 @@ const OAuth = ({
|
||||
|
||||
return (
|
||||
<OAuthContainer>
|
||||
{isLoading ? (
|
||||
<div>Loading...</div>
|
||||
) : isEmptyClientList ? (
|
||||
<OAuthEmptyScreen t={t} />
|
||||
) : (
|
||||
<List t={t} clients={clientList} viewAs={viewAs} />
|
||||
)}
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div>Loading...</div>
|
||||
) : isEmptyClientList ? (
|
||||
<OAuthEmptyScreen t={t} />
|
||||
) : (
|
||||
<List t={t} clients={clientList} viewAs={viewAs} />
|
||||
)}
|
||||
</>
|
||||
{infoDialogVisible && <InfoDialog visible={infoDialogVisible} />}
|
||||
</OAuthContainer>
|
||||
);
|
||||
};
|
||||
@ -80,8 +90,15 @@ const OAuth = ({
|
||||
export default inject(
|
||||
({ oauthStore, auth }: { oauthStore: OAuthStoreProps; auth: any }) => {
|
||||
const { currentDeviceType } = auth.settingsStore;
|
||||
const { viewAs, setViewAs, clientList, isEmptyClientList, fetchClients } =
|
||||
oauthStore;
|
||||
const {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
clientList,
|
||||
isEmptyClientList,
|
||||
fetchClients,
|
||||
fetchScopes,
|
||||
infoDialogVisible,
|
||||
} = oauthStore;
|
||||
return {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
@ -89,6 +106,8 @@ export default inject(
|
||||
isEmptyClientList,
|
||||
fetchClients,
|
||||
currentDeviceType,
|
||||
infoDialogVisible,
|
||||
fetchScopes,
|
||||
};
|
||||
}
|
||||
)(observer(OAuth));
|
||||
|
@ -0,0 +1,386 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import styled from "styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { IClientProps, IScope } from "@docspace/common/utils/oauth/interfaces";
|
||||
import ScopeList from "@docspace/common/utils/oauth/ScopeList";
|
||||
//@ts-ignore
|
||||
import getCorrectDate from "@docspace/components/utils/getCorrectDate";
|
||||
//@ts-ignore
|
||||
import { getCookie } from "@docspace/components/utils/cookie";
|
||||
//@ts-ignore
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Text from "@docspace/components/text";
|
||||
import ContextMenuButton from "@docspace/components/context-menu-button";
|
||||
|
||||
//@ts-ignore
|
||||
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
||||
import Avatar from "@docspace/components/avatar";
|
||||
import Link from "@docspace/components/link";
|
||||
|
||||
const StyledContainer = styled.div<{
|
||||
showDescription: boolean;
|
||||
withShowText: boolean;
|
||||
}>`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
padding-top: 8px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.client-block {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
margin-bottom: 12px;
|
||||
|
||||
.client-block__info {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
gap: 8px;
|
||||
|
||||
.client-block__info-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
max-width: 32px;
|
||||
max-height: 32px;
|
||||
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
max-height: ${(props) =>
|
||||
props.showDescription ? "100%" : props.withShowText ? "100px" : "100%"};
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
margin-bottom: ${(props) => (props.withShowText ? "4px" : 0)};
|
||||
}
|
||||
|
||||
.desc-link {
|
||||
color: #adadad;
|
||||
}
|
||||
|
||||
.block-header {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
.creator-block {
|
||||
margin: 8px 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.privacy-block {
|
||||
display: flex;
|
||||
|
||||
.separator {
|
||||
display: inline-block;
|
||||
|
||||
margin-top: 2px;
|
||||
|
||||
height: 16px;
|
||||
width: 1px;
|
||||
|
||||
margin: 0 8px;
|
||||
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface InfoDialogProps {
|
||||
visible: boolean;
|
||||
scopeList?: IScope[];
|
||||
|
||||
setInfoDialogVisible?: (value: boolean) => void;
|
||||
getContextMenuItems?: (
|
||||
t: any,
|
||||
item: IClientProps,
|
||||
isInfo?: boolean
|
||||
) => {
|
||||
[key: string]: any | string | boolean | ((clientId: string) => void);
|
||||
}[];
|
||||
|
||||
client?: IClientProps;
|
||||
}
|
||||
|
||||
const InfoDialog = ({
|
||||
visible,
|
||||
|
||||
client,
|
||||
scopeList,
|
||||
|
||||
setInfoDialogVisible,
|
||||
getContextMenuItems,
|
||||
}: InfoDialogProps) => {
|
||||
const { t } = useTranslation(["Common"]);
|
||||
|
||||
const [showDescription, setShowDescription] = React.useState(false);
|
||||
const [isRender, setIsRender] = React.useState(false);
|
||||
const [withShowText, setWithShowText] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsRender(true);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const el = document.getElementById("client-info-description-text");
|
||||
if (!el) return;
|
||||
|
||||
setWithShowText(el?.offsetHeight >= 100);
|
||||
}, [isRender]);
|
||||
|
||||
const getContextOptions = () => {
|
||||
const contextOptions =
|
||||
client && getContextMenuItems && getContextMenuItems(t, client, true);
|
||||
|
||||
return contextOptions;
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setInfoDialogVisible?.(false);
|
||||
};
|
||||
|
||||
const locale = getCookie("asc_language");
|
||||
|
||||
const modifiedDate = getCorrectDate(locale, client?.modifiedOn);
|
||||
|
||||
return (
|
||||
<ModalDialog visible={visible} displayType={"aside"} onClose={onClose}>
|
||||
<ModalDialog.Header>Info</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<StyledContainer
|
||||
showDescription={showDescription}
|
||||
withShowText={withShowText}
|
||||
>
|
||||
<div className="client-block">
|
||||
<div className="client-block__info">
|
||||
<img className="client-block__info-logo" src={client?.logo} />
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
fontSize={"16px"}
|
||||
lineHeight={"22px"}
|
||||
fontWeight={"700"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{client?.name}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<ContextMenuButton getData={getContextOptions} />
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Creator
|
||||
</Text>
|
||||
<div className="creator-block">
|
||||
<Avatar source={client?.creatorAvatar} size={"small"} />
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{client?.creatorDisplayName}
|
||||
</Text>
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Description
|
||||
</Text>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
id={"client-info-description-text"}
|
||||
className={"description"}
|
||||
fontSize={"13px"}
|
||||
lineHeight={"20px"}
|
||||
fontWeight={"400"}
|
||||
noSelect
|
||||
>
|
||||
{client?.description}
|
||||
</Text>
|
||||
{withShowText && (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<Link
|
||||
className={"desc-link"}
|
||||
fontSize={"13px"}
|
||||
lineHeight={"15px"}
|
||||
fontWeight={"600"}
|
||||
isHovered
|
||||
onClick={() => setShowDescription((val) => !val)}
|
||||
type={"action"}
|
||||
>
|
||||
{showDescription ? "Hide" : "Show more"}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Website
|
||||
</Text>
|
||||
{/* @ts-ignore */}
|
||||
<Link
|
||||
fontSize={"13px"}
|
||||
lineHeight={"15px"}
|
||||
fontWeight={"600"}
|
||||
isHovered
|
||||
href={client?.websiteUrl}
|
||||
type={"action"}
|
||||
target={"_blank"}
|
||||
>
|
||||
{client?.websiteUrl}
|
||||
</Link>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Access
|
||||
</Text>
|
||||
<ScopeList
|
||||
selectedScopes={client?.scopes || []}
|
||||
scopes={scopeList || []}
|
||||
t={t}
|
||||
/>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"20px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Support & Legal info
|
||||
</Text>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"privacy-block"}
|
||||
fontSize={"13px"}
|
||||
lineHeight={"15px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<Link
|
||||
fontSize={"13px"}
|
||||
lineHeight={"15px"}
|
||||
fontWeight={"600"}
|
||||
isHovered
|
||||
href={client?.policyUrl}
|
||||
type={"action"}
|
||||
target={"_blank"}
|
||||
>
|
||||
Privacy policy
|
||||
</Link>
|
||||
<span className="separator"></span>
|
||||
{/* @ts-ignore */}
|
||||
<Link
|
||||
fontSize={"13px"}
|
||||
lineHeight={"15px"}
|
||||
fontWeight={"600"}
|
||||
isHovered
|
||||
href={client?.termsUrl}
|
||||
type={"action"}
|
||||
target={"_blank"}
|
||||
>
|
||||
Terms of Service
|
||||
</Link>
|
||||
</Text>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
className={"block-header"}
|
||||
fontSize={"14px"}
|
||||
lineHeight={"16px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
Last modified
|
||||
</Text>
|
||||
{/* @ts-ignore */}
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
lineHeight={"20px"}
|
||||
fontWeight={"600"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{modifiedDate}
|
||||
</Text>
|
||||
</StyledContainer>
|
||||
</ModalDialog.Body>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ oauthStore }: { oauthStore: OAuthStoreProps }) => {
|
||||
const {
|
||||
setInfoDialogVisible,
|
||||
bufferSelection,
|
||||
scopeList,
|
||||
getContextMenuItems,
|
||||
} = oauthStore;
|
||||
|
||||
return {
|
||||
setInfoDialogVisible,
|
||||
client: bufferSelection,
|
||||
scopeList,
|
||||
getContextMenuItems,
|
||||
};
|
||||
})(observer(InfoDialog));
|
@ -3,7 +3,6 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
//@ts-ignore
|
||||
import Row from "@docspace/components/row";
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
|
||||
import { RowContent } from "./RowContent";
|
||||
import { RowProps } from "./RowView.types";
|
||||
|
@ -65,8 +65,9 @@ const Row = (props: RowProps) => {
|
||||
|
||||
const contextOptions = getContextMenuItems && getContextMenuItems(t, item);
|
||||
|
||||
const local = getCookie("asc_language");
|
||||
const modifiedDate = getCorrectDate(local, item.modifiedOn);
|
||||
const locale = getCookie("asc_language");
|
||||
|
||||
const modifiedDate = getCorrectDate(locale, item.modifiedOn);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -35,6 +35,9 @@ export interface OAuthStoreProps {
|
||||
viewAs: ViewAsType;
|
||||
setViewAs: (value: ViewAsType) => void;
|
||||
|
||||
infoDialogVisible: boolean;
|
||||
setInfoDialogVisible: (value: boolean) => void;
|
||||
|
||||
deleteDialogVisible: boolean;
|
||||
setDeleteDialogVisible: (value: boolean) => void;
|
||||
|
||||
@ -79,7 +82,8 @@ export interface OAuthStoreProps {
|
||||
|
||||
getContextMenuItems: (
|
||||
t: any,
|
||||
item: IClientProps
|
||||
item: IClientProps,
|
||||
isInfo?: boolean
|
||||
) => {
|
||||
[key: string]: any | string | boolean | ((clientId: string) => void);
|
||||
}[];
|
||||
@ -97,6 +101,7 @@ class OAuthStore implements OAuthStoreProps {
|
||||
nextPage: number | null = null;
|
||||
itemCount: number = 0;
|
||||
|
||||
infoDialogVisible: boolean = false;
|
||||
deleteDialogVisible: boolean = false;
|
||||
|
||||
selection: string[] = [];
|
||||
@ -119,6 +124,10 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.viewAs = value;
|
||||
};
|
||||
|
||||
setInfoDialogVisible = (value: boolean) => {
|
||||
this.infoDialogVisible = value;
|
||||
};
|
||||
|
||||
setDeleteDialogVisible = (value: boolean) => {
|
||||
this.deleteDialogVisible = value;
|
||||
};
|
||||
@ -160,6 +169,7 @@ class OAuthStore implements OAuthStoreProps {
|
||||
};
|
||||
|
||||
editClient = (clientId: string) => {
|
||||
this.setInfoDialogVisible(false);
|
||||
//@ts-ignore
|
||||
window?.DocSpace?.navigate(
|
||||
`/portal-settings/developer-tools/oauth/${clientId}`
|
||||
@ -315,12 +325,13 @@ class OAuthStore implements OAuthStoreProps {
|
||||
}
|
||||
};
|
||||
|
||||
getContextMenuItems = (t: any, item: IClientProps) => {
|
||||
getContextMenuItems = (t: any, item: IClientProps, isInfo?: boolean) => {
|
||||
const { clientId } = item;
|
||||
|
||||
const isGroupContext = this.selection.length;
|
||||
|
||||
const onDelete = () => {
|
||||
this.setInfoDialogVisible(false);
|
||||
if (!isGroupContext) {
|
||||
this.setBufferSelection(clientId);
|
||||
}
|
||||
@ -328,7 +339,13 @@ class OAuthStore implements OAuthStoreProps {
|
||||
this.setDeleteDialogVisible(true);
|
||||
};
|
||||
|
||||
const onShowInfo = () => {
|
||||
this.setBufferSelection(clientId);
|
||||
this.setInfoDialogVisible(true);
|
||||
};
|
||||
|
||||
const onEnable = async (status: boolean) => {
|
||||
this.setInfoDialogVisible(false);
|
||||
if (isGroupContext) {
|
||||
try {
|
||||
const actions: Promise<void>[] = [];
|
||||
@ -372,7 +389,8 @@ class OAuthStore implements OAuthStoreProps {
|
||||
key: "info",
|
||||
icon: SettingsIcon,
|
||||
label: "Info",
|
||||
onClick: () => console.log(clientId),
|
||||
onClick: onShowInfo,
|
||||
isDisabled: isInfo,
|
||||
};
|
||||
|
||||
const enableOption = {
|
||||
@ -424,7 +442,7 @@ class OAuthStore implements OAuthStoreProps {
|
||||
contextOptions.unshift(enableOption);
|
||||
}
|
||||
|
||||
contextOptions.unshift(infoOption);
|
||||
if (!isInfo) contextOptions.unshift(infoOption);
|
||||
contextOptions.unshift(authButtonOption);
|
||||
contextOptions.unshift(editOption);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ const StyledScopeItem = styled.div`
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
background-color: ${(props) => props.theme.mainText};
|
||||
background: ${(props) => props.theme.mainText};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -65,6 +65,7 @@ const Base = {
|
||||
fontSize: "13px",
|
||||
interfaceDirection: "ltr",
|
||||
separatorColor: "#eceef1",
|
||||
mainText: black,
|
||||
|
||||
text: {
|
||||
color: black,
|
||||
|
@ -59,6 +59,7 @@ const Dark = {
|
||||
fontSize: "13px",
|
||||
interfaceDirection: "ltr",
|
||||
separatorColor: "#474747",
|
||||
mainText: white,
|
||||
|
||||
text: {
|
||||
color: grayMaxLight,
|
||||
|
Loading…
Reference in New Issue
Block a user