Client:OAuth2: add preview dialog

This commit is contained in:
Timofey Boyko 2023-11-08 16:55:06 +03:00
parent 85c59b1075
commit 9067d28459
8 changed files with 325 additions and 16 deletions

View File

@ -17,4 +17,5 @@ export interface OAuthProps {
currentDeviceType: DeviceUnionType; currentDeviceType: DeviceUnionType;
infoDialogVisible?: boolean; infoDialogVisible?: boolean;
previewDialogVisible?: boolean;
} }

View File

@ -15,6 +15,7 @@ import List from "./sub-components/List";
import { OAuthContainer } from "./StyledOAuth"; import { OAuthContainer } from "./StyledOAuth";
import { OAuthProps } from "./OAuth.types"; import { OAuthProps } from "./OAuth.types";
import InfoDialog from "./sub-components/InfoDialog"; import InfoDialog from "./sub-components/InfoDialog";
import PreviewDialog from "./sub-components/PreviewDialog";
const MIN_LOADER_TIME = 500; const MIN_LOADER_TIME = 500;
@ -27,6 +28,7 @@ const OAuth = ({
fetchScopes, fetchScopes,
currentDeviceType, currentDeviceType,
infoDialogVisible, infoDialogVisible,
previewDialogVisible,
}: OAuthProps) => { }: OAuthProps) => {
const { t } = useTranslation(["OAuth"]); const { t } = useTranslation(["OAuth"]);
@ -83,6 +85,7 @@ const OAuth = ({
)} )}
</> </>
{infoDialogVisible && <InfoDialog visible={infoDialogVisible} />} {infoDialogVisible && <InfoDialog visible={infoDialogVisible} />}
{previewDialogVisible && <PreviewDialog visible={previewDialogVisible} />}
</OAuthContainer> </OAuthContainer>
); );
}; };
@ -98,6 +101,7 @@ export default inject(
fetchClients, fetchClients,
fetchScopes, fetchScopes,
infoDialogVisible, infoDialogVisible,
previewDialogVisible,
} = oauthStore; } = oauthStore;
return { return {
viewAs, viewAs,
@ -107,6 +111,7 @@ export default inject(
fetchClients, fetchClients,
currentDeviceType, currentDeviceType,
infoDialogVisible, infoDialogVisible,
previewDialogVisible,
fetchScopes, fetchScopes,
}; };
} }

View File

@ -70,15 +70,19 @@ const ClientForm = ({
const isEdit = !!id; const isEdit = !!id;
const onSaveClick = async () => { const onSaveClick = async () => {
if (!id) { try {
setIsRequestRunning(true); if (!id) {
setIsRequestRunning(true);
await saveClient?.(form); await saveClient?.(form);
} else { } else {
await updateClient?.(clientId, form); await updateClient?.(clientId, form);
}
onCancelClick();
} catch (e) {
console.log(e);
} }
onCancelClick();
}; };
const onCancelClick = () => { const onCancelClick = () => {

View File

@ -0,0 +1,277 @@
import React from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import { IClientProps } from "@docspace/common/utils/oauth/interfaces";
//@ts-ignore
import ModalDialog from "@docspace/components/modal-dialog";
//@ts-ignore
import SocialButton from "@docspace/components/social-button";
import Text from "@docspace/components/text";
//@ts-ignore
import Textarea from "@docspace/components/textarea";
import OnlyofficeLight from "PUBLIC_DIR/images/onlyoffice.light.react.svg";
import OnlyofficeDark from "PUBLIC_DIR/images/onlyoffice.dark.react.svg";
//@ts-ignore
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
const StyledContainer = styled.div`
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 20px;
`;
const StyledPreviewContainer = styled.div`
width: 100%;
height: 152px;
box-sizing: border-box;
border: 1px solid #242424;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
.social-button {
max-width: 226px;
padding: 11px 16px;
box-sizing: border-box;
display: flex;
gap: 16px;
.iconWrapper {
padding: 0;
margin: 0;
}
}
`;
const StyledBlocksContainer = styled.div`
width: 100%;
height: auto;
display: flex;
flex-direction: column;
gap: 12px;
.block-container {
display: flex;
flex-direction: column;
gap: 4px;
}
`;
const htmlBlock = `<body>
<button id="docspace-button" class="docspace-button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.89992 18.7913L1.47441 15.2914C0.841864 14.9858 0.841864 14.5136 1.47441 14.2359L4.05959 13.0137L8.87242 15.2914C9.50497 15.5969 10.5225 15.5969 11.1276 15.2914L15.9404 13.0137L18.5256 14.2359C19.1581 14.5414 19.1581 15.0136 18.5256 15.2914L11.1001 18.7913C10.5225 19.069 9.50497 19.069 8.89992 18.7913Z" fill="#FF6F3D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 14.4606L1.47296 10.9566C0.842346 10.6507 0.842346 10.178 1.47296 9.89989L3.99543 8.7041L8.87586 11.0123C9.50647 11.3182 10.5209 11.3182 11.1241 11.0123L16.0046 8.7041L18.527 9.89989C19.1577 10.2058 19.1577 10.6785 18.527 10.9566L11.1241 14.4606C10.4935 14.7665 9.47906 14.7665 8.87586 14.4606Z" fill="#95C038"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 10.1747L1.47296 6.72119C0.842346 6.41969 0.842346 5.95374 1.47296 5.67965L8.87586 2.22612C9.50647 1.92463 10.5209 1.92463 11.1241 2.22612L18.527 5.67965C19.1577 5.98115 19.1577 6.4471 18.527 6.72119L11.1241 10.1747C10.4935 10.4488 9.47906 10.4488 8.87586 10.1747Z" fill="#5DC0E8"/>
</svg>
Sign in with DocSpace
</button>
</body>`;
const styleBlock = `<style>
.docspace-button {
width: auto;
padding: 0 20px;
display: flex;
flex-direction: row;
align-items: center;
text-decoration: none;
border-radius: 2px;
height: 40px;
border: none;
stroke: none;
background: #ffffff;
box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 1px, rgba(0, 0, 0, 0.12) 0px 0px 1px;
color: rgb(163, 169, 174);
font-weight: 600;
font-size: 14px;
line-height: 14px;
user-select: none;
font-family: Roboto, "Open Sans", sans-serif, Arial;
}
.docspace-button:hover {
box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 1px, rgba(0, 0, 0, 0.12) 0px 0px 1px;
cursor: pointer;
color: #333333;
}
.docspace-button:active {
background-color: #F8F9F9;
color: #333333;
cursor: pointer;
}
.logo-svg {
width: 18px;
min-width: 18px;
height: 18px;
min-height: 18px;
margin: 11px 16px;
}
</style>`;
const linkParams =
"width=800,height=800,status=no,toolbar=no,menubar=no,resizable=yes,scrollbars=no";
interface PreviewDialogProps {
visible: boolean;
setPreviewDialogVisible?: (value: boolean) => void;
client?: IClientProps;
theme?: any;
}
const PreviewDialog = ({
visible,
setPreviewDialogVisible,
client,
theme,
}: PreviewDialogProps) => {
const onClose = () => setPreviewDialogVisible?.(false);
const icon = theme.isBase ? OnlyofficeLight : OnlyofficeDark;
const scopesString = client?.scopes.join(" ");
const encodingScopes = encodeURI(scopesString || "");
const getLink = () => {
return `${window.location.origin}/oauth2/authorize?response_type=code&client_id=${client?.clientId}&redirect_uri=${client?.redirectUris[0]}&scope=${encodingScopes}&code_challenge_method=S256&code_challenge=ZX8YUY6qL0EweJXhjDug0S2XuKI8beOWb1LGujmgfuQ`;
};
const link = getLink();
const scriptBlock = `<script>
const button = document.getElementById('docspace-button')
function openOAuthPage() {
window.open(
"${link}",
"login",
${linkParams}
);
}
button.addEventListener('click', openOAuthPage)
</script>`;
return (
<ModalDialog visible={visible} displayType={"aside"} onClose={onClose}>
<ModalDialog.Header>Auth button</ModalDialog.Header>
<ModalDialog.Body>
<StyledContainer>
<StyledPreviewContainer>
<SocialButton
className={"social-button"}
label={"Sign in with DocSpace"}
IconComponent={icon}
onClick={() => {
window.open(link, "login", linkParams);
}}
/>
</StyledPreviewContainer>
<StyledBlocksContainer>
<div className="block-container">
{/* @ts-ignore */}
<Text
fontWeight={600}
lineHeight={"20px"}
fontSize={"13px"}
noSelect
>
HTML
</Text>
<Textarea
heightTextArea={64}
enableCopy
isReadOnly
isDisabled
value={htmlBlock}
/>
</div>
<div className="block-container">
{/* @ts-ignore */}
<Text
fontWeight={600}
lineHeight={"20px"}
fontSize={"13px"}
noSelect
>
CSS
</Text>
<Textarea
heightTextArea={64}
enableCopy
isReadOnly
isDisabled
value={styleBlock}
/>
</div>
<div className="block-container">
{/* @ts-ignore */}
<Text
fontWeight={600}
lineHeight={"20px"}
fontSize={"13px"}
noSelect
>
JavaScript
</Text>
<Textarea
heightTextArea={64}
enableCopy
isReadOnly
isDisabled
value={scriptBlock}
/>
</div>
</StyledBlocksContainer>
</StyledContainer>
</ModalDialog.Body>
</ModalDialog>
);
};
export default inject(
({ oauthStore, auth }: { auth: any; oauthStore: OAuthStoreProps }) => {
const { setPreviewDialogVisible, bufferSelection } = oauthStore;
const { theme } = auth.settingsStore;
return {
setPreviewDialogVisible,
client: bufferSelection,
theme,
};
}
)(observer(PreviewDialog));

View File

@ -38,6 +38,9 @@ export interface OAuthStoreProps {
infoDialogVisible: boolean; infoDialogVisible: boolean;
setInfoDialogVisible: (value: boolean) => void; setInfoDialogVisible: (value: boolean) => void;
previewDialogVisible: boolean;
setPreviewDialogVisible: (value: boolean) => void;
deleteDialogVisible: boolean; deleteDialogVisible: boolean;
setDeleteDialogVisible: (value: boolean) => void; setDeleteDialogVisible: (value: boolean) => void;
@ -102,6 +105,7 @@ class OAuthStore implements OAuthStoreProps {
itemCount: number = 0; itemCount: number = 0;
infoDialogVisible: boolean = false; infoDialogVisible: boolean = false;
previewDialogVisible: boolean = false;
deleteDialogVisible: boolean = false; deleteDialogVisible: boolean = false;
selection: string[] = []; selection: string[] = [];
@ -128,6 +132,10 @@ class OAuthStore implements OAuthStoreProps {
this.infoDialogVisible = value; this.infoDialogVisible = value;
}; };
setPreviewDialogVisible = (value: boolean) => {
this.previewDialogVisible = value;
};
setDeleteDialogVisible = (value: boolean) => { setDeleteDialogVisible = (value: boolean) => {
this.deleteDialogVisible = value; this.deleteDialogVisible = value;
}; };
@ -240,7 +248,7 @@ class OAuthStore implements OAuthStoreProps {
const newClient = await addClient(client); const newClient = await addClient(client);
runInAction(() => { runInAction(() => {
this.clients.unshift(newClient); this.clients = [{ ...newClient }, ...this.clients];
}); });
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -260,8 +268,6 @@ class OAuthStore implements OAuthStoreProps {
creatorAvatar: this.clients[idx].creatorAvatar, creatorAvatar: this.clients[idx].creatorAvatar,
creatorDisplayName: this.clients[idx].creatorDisplayName, creatorDisplayName: this.clients[idx].creatorDisplayName,
}; };
console.log(this.clients[idx]);
}); });
} }
} catch (e) { } catch (e) {
@ -332,6 +338,7 @@ class OAuthStore implements OAuthStoreProps {
const onDelete = () => { const onDelete = () => {
this.setInfoDialogVisible(false); this.setInfoDialogVisible(false);
this.setPreviewDialogVisible(false);
if (!isGroupContext) { if (!isGroupContext) {
this.setBufferSelection(clientId); this.setBufferSelection(clientId);
} }
@ -341,11 +348,19 @@ class OAuthStore implements OAuthStoreProps {
const onShowInfo = () => { const onShowInfo = () => {
this.setBufferSelection(clientId); this.setBufferSelection(clientId);
this.setPreviewDialogVisible(false);
this.setInfoDialogVisible(true); this.setInfoDialogVisible(true);
}; };
const onShowPreview = () => {
this.setBufferSelection(clientId);
this.setInfoDialogVisible(false);
this.setPreviewDialogVisible(true);
};
const onEnable = async (status: boolean) => { const onEnable = async (status: boolean) => {
this.setInfoDialogVisible(false); this.setInfoDialogVisible(false);
this.setPreviewDialogVisible(false);
if (isGroupContext) { if (isGroupContext) {
try { try {
const actions: Promise<void>[] = []; const actions: Promise<void>[] = [];
@ -382,7 +397,7 @@ class OAuthStore implements OAuthStoreProps {
key: "auth-button", key: "auth-button",
icon: CodeReactSvgUrl, icon: CodeReactSvgUrl,
label: "Auth button", label: "Auth button",
onClick: () => console.log(clientId), onClick: onShowPreview,
}; };
const infoOption = { const infoOption = {

View File

@ -92,7 +92,7 @@ export const changeClientStatus = async (
await request({ await request({
method: "patch", method: "patch",
url: `/clients/${clientId}/activation`, url: `/clients/${clientId}/activation`,
data: { body: status }, data: { status },
}); });
}; };
@ -149,12 +149,9 @@ export const onOAuthSubmit = (
) => { ) => {
const formData = new FormData(); const formData = new FormData();
// console.log(window.location.search);
formData.append("client_id", clientId); formData.append("client_id", clientId);
formData.append("state", clientState); formData.append("state", clientState);
formData.append("scope", "accounts:write"); formData.append("scope", scope.join(","));
// formData.append("scope", scope.join(","));
return request({ return request({
method: "post", method: "post",

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M8.89992 18.7913L1.47441 15.2914C0.841864 14.9858 0.841864 14.5136 1.47441 14.2359L4.05959 13.0137L8.87242 15.2914C9.50497 15.5969 10.5225 15.5969 11.1276 15.2914L15.9404 13.0137L18.5256 14.2359C19.1581 14.5414 19.1581 15.0136 18.5256 15.2914L11.1001 18.7913C10.5225 19.069 9.50497 19.069 8.89992 18.7913Z" fill="white"/>
<path opacity="0.75" fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 14.4606L1.47296 10.9566C0.842346 10.6507 0.842346 10.178 1.47296 9.89989L3.99543 8.7041L8.87586 11.0123C9.50647 11.3182 10.5209 11.3182 11.1241 11.0123L16.0046 8.7041L18.527 9.89989C19.1577 10.2058 19.1577 10.6785 18.527 10.9566L11.1241 14.4606C10.4935 14.7665 9.47906 14.7665 8.87586 14.4606Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 10.1747L1.47296 6.72119C0.842346 6.41969 0.842346 5.95374 1.47296 5.67965L8.87586 2.22612C9.50647 1.92463 10.5209 1.92463 11.1241 2.22612L18.527 5.67965C19.1577 5.98115 19.1577 6.4471 18.527 6.72119L11.1241 10.1747C10.4935 10.4488 9.47906 10.4488 8.87586 10.1747Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.89992 18.7913L1.47441 15.2914C0.841864 14.9858 0.841864 14.5136 1.47441 14.2359L4.05959 13.0137L8.87242 15.2914C9.50497 15.5969 10.5225 15.5969 11.1276 15.2914L15.9404 13.0137L18.5256 14.2359C19.1581 14.5414 19.1581 15.0136 18.5256 15.2914L11.1001 18.7913C10.5225 19.069 9.50497 19.069 8.89992 18.7913Z" fill="#FF6F3D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 14.4606L1.47296 10.9566C0.842346 10.6507 0.842346 10.178 1.47296 9.89989L3.99543 8.7041L8.87586 11.0123C9.50647 11.3182 10.5209 11.3182 11.1241 11.0123L16.0046 8.7041L18.527 9.89989C19.1577 10.2058 19.1577 10.6785 18.527 10.9566L11.1241 14.4606C10.4935 14.7665 9.47906 14.7665 8.87586 14.4606Z" fill="#95C038"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.87586 10.1747L1.47296 6.72119C0.842346 6.41969 0.842346 5.95374 1.47296 5.67965L8.87586 2.22612C9.50647 1.92463 10.5209 1.92463 11.1241 2.22612L18.527 5.67965C19.1577 5.98115 19.1577 6.4471 18.527 6.72119L11.1241 10.1747C10.4935 10.4488 9.47906 10.4488 8.87586 10.1747Z" fill="#5DC0E8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB