Web:Client:Plugins: update list of plugins at portal settings, change settings plugin dialog

This commit is contained in:
Timofey Boyko 2023-09-14 18:27:37 +03:00
parent 3ceccbba29
commit e26bcc6ee2
19 changed files with 676 additions and 184 deletions

View File

@ -0,0 +1,6 @@
{
"Description": "You can embed software modules to extend the functionality of DocSpace. Click download plugin and select a file in zip archive format.",
"Plugins": "Plugins",
"UploadPlugin": "Upload plugin",
"Metadata": "Metadata"
}

View File

@ -1,26 +1,38 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import ModalDialog from "@docspace/components/modal-dialog";
import {
PluginSettingsType,
PluginComponents,
} from "SRC_DIR/helpers/plugins/constants";
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
import Header from "./sub-components/Header";
import Info from "./sub-components/Info";
import Footer from "./sub-components/Footer";
const SettingsPluginDialog = ({
plugin,
onLoad,
customSettings,
settings,
saveButton,
settingsPluginDialogVisible,
onClose,
...rest
}) => {
console.log(rest);
const { t } = useTranslation("WebPlugins", "Common");
const [customSettingsProps, setCustomSettingsProps] =
React.useState(customSettings);
React.useState(settings);
const [saveButtonProps, setSaveButtonProps] = React.useState(saveButton);
const [modalRequestRunning, setModalRequestRunning] = React.useState(false);
@ -28,7 +40,12 @@ const SettingsPluginDialog = ({
if (!onLoad) return;
const res = await onLoad();
setCustomSettingsProps(res.customSettings);
setCustomSettingsProps(res.settings);
if (res.saveButton)
setSaveButtonProps({
...res.saveButton,
props: { ...res.saveButton, scale: true },
});
}, [onLoad]);
React.useEffect(() => {
@ -43,11 +60,14 @@ const SettingsPluginDialog = ({
return (
<ModalDialog
visible={settingsPluginDialogVisible}
displayType="modal"
displayType="aside"
onClose={onCloseAction}
autoMaxHeight
withBodyScroll
withFooterBorder
>
<ModalDialog.Header>{plugin?.name}</ModalDialog.Header>
<ModalDialog.Header>
<Header t={t} name={plugin?.name} />
</ModalDialog.Header>
<ModalDialog.Body>
<WrappedComponent
pluginId={plugin.id}
@ -55,9 +75,22 @@ const SettingsPluginDialog = ({
component: PluginComponents.box,
props: customSettingsProps,
}}
saveButton={saveButton}
setSaveButtonProps={setSaveButtonProps}
setModalRequestRunning={setModalRequestRunning}
/>
<Info t={t} plugin={plugin} />
</ModalDialog.Body>
<ModalDialog.Footer>
<Footer
t={t}
id={plugin?.id}
saveButtonProps={saveButtonProps}
setModalRequestRunning={setModalRequestRunning}
onCloseAction={onCloseAction}
modalRequestRunning={modalRequestRunning}
/>
</ModalDialog.Footer>
</ModalDialog>
);
};
@ -69,42 +102,22 @@ export default inject(({ pluginStore }) => {
setSettingsPluginDialogVisible,
currentSettingsDialogPlugin,
setCurrentSettingsDialogPlugin,
setIsAdminSettingsDialog,
isAdminSettingsDialog,
updateStatus,
setPluginDialogVisible,
setPluginDialogProps,
} = pluginStore;
const isUserDialog = !isAdminSettingsDialog;
const plugin = pluginList.find((p) => p.id === currentSettingsDialogPlugin);
const pluginSettings = isUserDialog
? plugin?.getUserPluginSettings()
: plugin?.getAdminPluginSettings();
const pluginSettings = plugin?.getAdminPluginSettings();
const onClose = () => {
setSettingsPluginDialogVisible(false);
setCurrentSettingsDialogPlugin(null);
setIsAdminSettingsDialog(false);
};
if (pluginSettings.type === PluginSettingsType.settingsPage) {
onClose();
}
return {
plugin,
...pluginSettings,
settingsPluginDialogVisible,
currentSettingsDialogPlugin,
onClose,
isUserDialog,
setSettingsPluginDialogVisible,
setCurrentSettingsDialogPlugin,
updateStatus,
setPluginDialogVisible,
setPluginDialogProps,
};
})(observer(SettingsPluginDialog));

View File

@ -0,0 +1,53 @@
import React from "react";
import styled from "styled-components";
import Button from "@docspace/components/button";
import { PluginComponent } from "SRC_DIR/helpers/plugins/WrappedComponent";
const StyledContainer = styled.div`
width: 100%;
display: flex;
align-items: space-between;
gap: 10px;
`;
const Footer = ({
t,
id,
saveButtonProps,
modalRequestRunning,
setModalRequestRunning,
onCloseAction,
}) => {
return (
<StyledContainer>
<PluginComponent
component={{
...saveButtonProps,
props: {
...saveButtonProps.props,
scale: true,
isSaveButton: true,
primary: true,
size: "normal",
label: t("Common:SaveButton"),
modalRequestRunning,
setSettingsModalRequestRunning: setModalRequestRunning,
onCloseAction,
},
}}
pluginId={id}
/>
<Button
scale={true}
size={"normal"}
onClick={onCloseAction}
label={t("Common:CancelButton")}
/>
</StyledContainer>
);
};
export default Footer;

View File

@ -0,0 +1,25 @@
import styled from "styled-components";
import Text from "@docspace/components/text";
const StyledHeader = styled.div`
display: flex;
align-items: center;
div {
color: #a3a9ae;
// margin-left: 2px;
}
`;
const Header = ({ t, name }) => {
return (
<StyledHeader>
{t("Common:Settings")}&nbsp;
<div>({name})</div>
</StyledHeader>
);
};
export default Header;

View File

@ -0,0 +1,140 @@
import React from "react";
import styled from "styled-components";
import Text from "@docspace/components/text";
import Link from "@docspace/components/link";
const StyledContainer = styled.div`
width: 100%;
`;
const StyledSeparator = styled.div`
width: 100%;
height: 1px;
margin: 24px 0;
background-color: #eceef1;
`;
const StyledInfo = styled.div`
margin-top: 24px;
width: 100%;
height: auto;
display: grid;
grid-template-columns: max-content 1fr;
gap: 8px 24px;
`;
const Info = ({ t, plugin }) => {
console.log(plugin);
return (
<StyledContainer>
<StyledSeparator />
<Text fontSize={"14px"} fontWeight={600} lineHeight={"16px"} noSelect>
{t("Metadata")}
</Text>
<StyledInfo>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Author
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
{plugin?.author}
</Text>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Version
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
{plugin?.version}
</Text>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Uploader
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
{plugin?.createBy}
</Text>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Upload date
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
{plugin?.createOn}
</Text>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Status
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
Not need enter settings
</Text>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Homepage
</Text>
<Link
fontSize={"13px"}
fontWeight={600}
lineHeight={"20px"}
type={"page"}
href={plugin?.homePage}
target={"_blank"}
noSelect
isHovered
>
{plugin?.homePage}
</Link>
<Text
fontSize={"13px"}
fontWeight={400}
lineHeight={"20px"}
noSelect
truncate
>
Description
</Text>
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
{plugin?.description}
</Text>
</StyledInfo>
</StyledContainer>
);
};
export default Info;

View File

@ -46,10 +46,14 @@ const ComponentPure = ({
} = React.useContext(PropsContext);
React.useEffect(() => {
if (!component.contextName || !contextProps[component.contextName]) return;
if (
!component.contextName ||
(contextProps && !contextProps[component.contextName])
)
return;
setElementProps(contextProps[component.contextName]);
}, [contextProps[component.contextName]]);
contextProps && setElementProps(contextProps[component.contextName]);
}, [contextProps && contextProps[component.contextName]]);
React.useEffect(() => {
setElementProps(component.props);
@ -62,7 +66,7 @@ const ComponentPure = ({
case PluginComponents.box: {
const childrenComponents = elementProps?.children?.map(
(item, index) => (
<Component
<PluginComponent
key={`box-${index}-${item.component}`}
component={item}
pluginId={pluginId}
@ -194,13 +198,24 @@ const ComponentPure = ({
}
case PluginComponents.button: {
const { withLoadingAfterClick, disableWhileRequestRunning, ...rest } =
elementProps;
const {
withLoadingAfterClick,
disableWhileRequestRunning,
isSaveButton,
modalRequestRunning,
setSettingsModalRequestRunning,
onCloseAction,
...rest
} = elementProps;
const onClickAction = async () => {
if (withLoadingAfterClick) {
setIsRequestRunning(true);
setIsRequestRunning && setIsRequestRunning(true);
setModalRequestRunning && setModalRequestRunning(true);
if (isSaveButton) {
setSettingsModalRequestRunning &&
setSettingsModalRequestRunning(true);
}
}
const message = await elementProps.onClick();
@ -224,16 +239,26 @@ const ComponentPure = ({
updateFileItems
);
setIsRequestRunning(false);
setIsRequestRunning && setIsRequestRunning(false);
if (isSaveButton) {
setSettingsModalRequestRunning &&
setSettingsModalRequestRunning(false);
onCloseAction && onCloseAction();
}
};
const isLoading = withLoadingAfterClick
? isRequestRunning
? isSaveButton
? modalRequestRunning
: isRequestRunning
? isRequestRunning
: rest.isLoading
: rest.isLoading;
const isDisabled = disableWhileRequestRunning
? isRequestRunning
? isSaveButton
? modalRequestRunning
: isRequestRunning
? isRequestRunning
: rest.isDisabled
: rest.isDisabled;
@ -295,7 +320,7 @@ const ComponentPure = ({
return element;
};
const Component = inject(({ pluginStore }) => {
export const PluginComponent = inject(({ pluginStore }) => {
const {
updatePluginStatus,
setCurrentSettingsDialogPlugin,
@ -326,16 +351,29 @@ const Component = inject(({ pluginStore }) => {
};
})(observer(ComponentPure));
const WrappedComponent = ({ component, pluginId, setModalRequestRunning }) => {
const WrappedComponent = ({
pluginId,
component,
saveButton,
setSaveButtonProps,
setModalRequestRunning,
}) => {
const [contextProps, setContextProps] = React.useState({});
const [isRequestRunning, setIsRequestRunning] = React.useState(false);
const updatePropsContext = (name, props) => {
const newProps = { ...contextProps };
newProps[name] = props;
if (saveButton && name === saveButton.contextName) {
setSaveButtonProps && setSaveButtonProps((val) => ({ ...val, props }));
} else {
const newProps = { ...contextProps };
newProps[name] = props;
setContextProps(newProps);
setContextProps(newProps);
}
};
return (
@ -348,7 +386,7 @@ const WrappedComponent = ({ component, pluginId, setModalRequestRunning }) => {
setModalRequestRunning,
}}
>
<Component component={component} pluginId={pluginId} />
<PluginComponent component={component} pluginId={pluginId} />
</PropsContext.Provider>
);
};

View File

@ -5,8 +5,6 @@ import PluginHeader from "./PluginHeader";
import PluginInfo from "./PluginInfo";
import PluginSettings from "./PluginSettings";
import { PluginSettingsType } from "SRC_DIR/helpers/plugins/constants";
import { PluginScopes } from "../constants";
const StyledPlugin = styled.div`
@ -57,21 +55,10 @@ const Plugin = ({
isLast,
}) => {
const withSettings = scopes.includes(PluginScopes.Settings);
const pluginSettings = isUserSettings
? userPluginSettings
: adminPluginSettings;
const showPluginSettingsPage =
withSettings &&
pluginSettings &&
(pluginSettings.type === PluginSettingsType.both ||
pluginSettings.type === PluginSettingsType.settingsPage);
const showModalPluginSettings =
withSettings && pluginSettings?.type && !showPluginSettingsPage;
return (
<StyledPlugin>
<PluginHeader

View File

@ -20,13 +20,6 @@ export const PluginScopes = Object.freeze({
export const PluginStatus = Object.freeze({
active: "active",
hide: "hide",
pending: "pending",
});
export const PluginSettingsType = Object.freeze({
modal: "modal",
settingsPage: "settings-page",
both: "both",
});
export const PluginActions = Object.freeze({
@ -37,8 +30,8 @@ export const PluginActions = Object.freeze({
showToast: "show-toast",
showSettingsModal: "show-settings-modal",
closeSettingsModal: "close-settings-modal",
// showSettingsModal: "show-settings-modal",
// closeSettingsModal: "close-settings-modal",
showCreateDialogModal: "show-create-dialog-modal",

View File

@ -38,7 +38,7 @@ export const messageActions = (
case PluginActions.updateContext:
if (message.contextProps) {
message.contextProps.forEach((prop) => {
updatePropsContext(prop.name, prop.props);
updatePropsContext && updatePropsContext(prop.name, prop.props);
});
}
break;
@ -102,39 +102,40 @@ export const messageActions = (
case PluginActions.showModal:
if (message.modalDialogProps) {
setPluginDialogVisible(true);
setPluginDialogProps(message.modalDialogProps);
setPluginDialogVisible && setPluginDialogVisible(true);
setPluginDialogProps &&
setPluginDialogProps(message.modalDialogProps);
}
break;
case PluginActions.closeModal:
setPluginDialogVisible(false);
setPluginDialogProps(null);
setPluginDialogVisible && setPluginDialogVisible(false);
setPluginDialogProps && setPluginDialogProps(null);
break;
case PluginActions.updateContextMenuItems:
updateContextMenuItems(pluginId);
updateContextMenuItems && updateContextMenuItems(pluginId);
break;
case PluginActions.updateInfoPanelItems:
updateInfoPanelItems(pluginId);
updateInfoPanelItems && updateInfoPanelItems(pluginId);
break;
case PluginActions.updateMainButtonItems:
updateMainButtonItems(pluginId);
updateMainButtonItems && updateMainButtonItems(pluginId);
break;
case PluginActions.updateProfileMenuItems:
updateProfileMenuItems(pluginId);
updateProfileMenuItems && updateProfileMenuItems(pluginId);
break;
case PluginActions.updateEventListenerItems:
updateEventListenerItems(pluginId);
updateEventListenerItems && updateEventListenerItems(pluginId);
break;
case PluginActions.updateFileItems:
updateFileItems(pluginId);
updateFileItems && updateFileItems(pluginId);
break;

View File

@ -28,7 +28,6 @@ const useFiles = ({
isAccountsPage,
isSettingsPage,
isPluginsSettingsPage,
location,
@ -86,7 +85,7 @@ const useFiles = ({
};
React.useEffect(() => {
if (isAccountsPage || isSettingsPage || isPluginsSettingsPage) return;
if (isAccountsPage || isSettingsPage) return;
setIsLoading(true);
if (!window.location.href.includes("#preview")) {
@ -272,13 +271,7 @@ const useFiles = ({
.finally(() => {
setIsLoading(false);
});
}, [
location.pathname,
location.search,
isAccountsPage,
isSettingsPage,
isPluginsSettingsPage,
]);
}, [location.pathname, location.search, isAccountsPage, isSettingsPage]);
return { onDrop };
};

View File

@ -0,0 +1,13 @@
import styled from "styled-components";
const StyledContainer = styled.div`
width: 100%;
max-width: 700px;
display: flex;
flex-direction: column;
gap: 20px;
`;
export { StyledContainer };

View File

@ -1,104 +1,56 @@
import React from "react";
import styled from "styled-components";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import Plugin from "SRC_DIR/helpers/plugins/components/Plugin";
import EmptyScreen from "SRC_DIR/helpers/plugins/components/EmptyScreen";
import Button from "@docspace/components/button";
import { Events } from "@docspace/common/constants";
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
const StyledPluginsSettings = styled.div`
width: 100%;
import Header from "./sub-components/header";
import UploadButton from "./sub-components/button";
import PluginItem from "./sub-components/plugin";
.add-button {
margin-bottom: 32px;
}
`;
const StyledPluginList = styled.div`
display: flex;
flex-direction: column;
gap: 32px;
`;
export { StyledPluginsSettings, StyledPluginList };
import { StyledContainer } from "./StyledPlugins";
const PluginPage = ({
withDelete,
withUpload,
pluginList,
changePluginStatus,
openSettingsDialog,
uninstallPlugin,
updatePluginStatus,
changePluginStatus,
addPlugin,
uninstallPlugin,
currentColorScheme,
}) => {
const { t } = useTranslation(["FilesSettings"]);
const { t } = useTranslation(["WebPlugins", "Common", "FilesSettings"]);
const pluginInputRef = React.useRef(null);
const onAddAction = () => {
pluginInputRef.current.click();
};
const onInputClick = (e) => {
e.target.value = null;
};
const onFileChange = (e) => {
let formData = new FormData();
formData.append("file", e.target.files[0]);
addPlugin(formData);
};
React.useEffect(() => {
setDocumentTitle(t("Plugins"));
}, []);
return (
<StyledPluginsSettings>
{pluginList.length > 0 ? (
<>
<Button
className={"add-button"}
label={"UploadPlugin"}
primary
size={"normal"}
onClick={onAddAction}
/>
<StyledPluginList>
{pluginList.map((plugin, index) => (
<Plugin
key={`plugin-${plugin.name}-${plugin.version}`}
{...plugin}
changePluginStatus={changePluginStatus}
withDelete={withDelete}
openSettingsDialog={openSettingsDialog}
isLast={index === pluginList.length - 1}
uninstallPlugin={uninstallPlugin}
updatePluginStatus={updatePluginStatus}
/>
))}
</StyledPluginList>
</>
) : (
<EmptyScreen t={t} onAddAction={onAddAction} />
)}
<input
id="customPluginInput"
className="custom-file-input"
type="file"
accept=".zip"
onChange={onFileChange}
onClick={onInputClick}
ref={pluginInputRef}
style={{ display: "none" }}
/>
</StyledPluginsSettings>
<StyledContainer>
<Header t={t} currentColorScheme={currentColorScheme} />
{withUpload && <UploadButton t={t} addPlugin={addPlugin} />}
{pluginList.map((plugin) => (
<PluginItem
key={`plugin-${plugin.name}-${plugin.version}`}
withDelete={withDelete}
openSettingsDialog={openSettingsDialog}
uninstallPlugin={uninstallPlugin}
changePluginStatus={changePluginStatus}
{...plugin}
/>
))}
</StyledContainer>
);
};
export default inject(({ auth, pluginStore }) => {
const { enablePlugins, pluginOptions } = auth.settingsStore;
const { pluginOptions, currentColorScheme } = auth.settingsStore;
const withUpload = pluginOptions.includes("upload");
const withDelete = pluginOptions.includes("delete");
@ -108,27 +60,30 @@ export default inject(({ auth, pluginStore }) => {
changePluginStatus,
setCurrentSettingsDialogPlugin,
setSettingsPluginDialogVisible,
setIsAdminSettingsDialog,
uninstallPlugin,
updatePluginStatus,
addPlugin,
} = pluginStore;
const openSettingsDialog = (pluginId) => {
setSettingsPluginDialogVisible(true);
setCurrentSettingsDialogPlugin(pluginId);
setIsAdminSettingsDialog(true);
};
return {
enablePlugins,
withUpload,
withDelete,
pluginList,
changePluginStatus,
openSettingsDialog,
uninstallPlugin,
updatePluginStatus,
addPlugin,
currentColorScheme,
};
})(observer(PluginPage));

View File

@ -0,0 +1,53 @@
import React from "react";
import styled from "styled-components";
import Button from "@docspace/components/button";
const StyledUploadButton = styled.div`
width: auto;
`;
const UploadButton = ({ t, addPlugin }) => {
const pluginInputRef = React.useRef(null);
const onAddAction = () => {
pluginInputRef.current.click();
};
const onInputClick = (e) => {
e.target.value = null;
};
const onFileChange = (e) => {
let formData = new FormData();
formData.append("file", e.target.files[0]);
addPlugin(formData);
};
return (
<StyledUploadButton>
<Button
className={"add-button"}
label={t("UploadPlugin")}
primary
size={"normal"}
scale={false}
onClick={onAddAction}
/>
<input
id="customPluginInput"
className="custom-file-input"
type="file"
accept=".zip"
onChange={onFileChange}
onClick={onInputClick}
ref={pluginInputRef}
style={{ display: "none" }}
/>
</StyledUploadButton>
);
};
export default UploadButton;

View File

@ -0,0 +1,63 @@
import styled from "styled-components";
import Heading from "@docspace/components/heading";
import HelpButton from "@docspace/components/help-button";
import Text from "@docspace/components/text";
import Link from "@docspace/components/link";
const StyledHeader = styled.div`
width: 100%;
display: flex;
flex-direction: column;
.plugin-list-header {
font-size: 16px;
font-weight: 700;
lien-height: 22px;
margin: 0 4px 0 0;
padding: 0;
}
.header-container {
margin-bottom: 8px;
}
div {
display: flex;
align-items: center;
}
`;
const Header = ({ t, currentColorScheme }) => {
return (
<StyledHeader>
<div className="header-container">
<Heading className={"plugin-list-header"}>{t("Plugins")}</Heading>
<HelpButton
offsetBottom={0}
offsetLeft={0}
offsetRight={0}
offsetTop={0}
tooltipContent={<>TODO: Plugin help button</>}
/>
</div>
<div>
<Text>
{t("Description")}{" "}
<Link
color={currentColorScheme?.main?.accent}
type={"page"}
target={"_blank"}
href={"/"}
>
{t("Common:LearnMore")}
</Link>
</Text>
</div>
</StyledHeader>
);
};
export default Header;

View File

@ -0,0 +1,153 @@
import React from "react";
import styled from "styled-components";
import Heading from "@docspace/components/heading";
import IconButton from "@docspace/components/icon-button";
import ToggleButton from "@docspace/components/toggle-button";
import Badge from "@docspace/components/badge";
import Text from "@docspace/components/text";
import PluginSettingsIconUrl from "PUBLIC_DIR/images/plugin.settings.react.svg?url";
import PluginDefaultLogoUrl from "PUBLIC_DIR/images/plugin.default-logo.png";
import { getPluginUrl } from "SRC_DIR/helpers/plugins/utils";
import { PluginScopes } from "SRC_DIR/helpers/plugins/constants";
const StyledPluginItem = styled.div`
width: 100%;
max-width: 500px;
height: auto;
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 64px 1fr;
gap: 20px;
border: 1px solid #d0d5da;
border-radius: 12px;
padding: 24px;
box-sizing: border-box;
.plugin-logo {
width: 64px;
height: 64px;
border-radius: 4px;
}
.plugin-info {
width: 100%;
height: auto;
display: flex;
flex-direction: column;
gap: 8px;
}
`;
const StyledPluginHeader = styled.div`
width: 100%;
height: 22px;
display: flex;
align-items: center;
justify-content: space-between;
.plugin-name {
margin: 0;
padding: 0;
}
.plugin-controls {
height: 100%;
display: flex;
gap: 16px;
.plugin-toggle-button {
position: relative;
gap: 0;
}
}
`;
const PluginItem = ({
id,
name,
version,
description,
enabled,
changePluginStatus,
scopes,
openSettingsDialog,
image,
url,
...rest
}) => {
const imgSrc = image ? getPluginUrl(url, `/assets/${image}`) : null;
const withSettings = scopes.includes(PluginScopes.Settings);
const onChangeStatus = () => {
changePluginStatus && changePluginStatus(id, !enabled);
};
const onOpenSettingsDialog = () => {
openSettingsDialog && openSettingsDialog(id);
};
return (
<StyledPluginItem>
<img
className="plugin-logo"
src={imgSrc || PluginDefaultLogoUrl}
alt={"Plugin logo"}
/>
<div className="plugin-info">
<StyledPluginHeader>
<Heading className={"plugin-name"}>{name}</Heading>
<div className="plugin-controls">
{withSettings && (
<IconButton
iconName={PluginSettingsIconUrl}
size={16}
onClick={onOpenSettingsDialog}
/>
)}
<ToggleButton
className="plugin-toggle-button"
onChange={onChangeStatus}
isChecked={enabled}
/>
</div>
</StyledPluginHeader>
<Badge
label={version}
fontSize={"12px"}
fontWeight={700}
noHover={true}
backgroundColor={"#22C386"}
/>
{imgSrc && description && (
<Text fontWeight={400} lineHeight={"20px"}>
{description}
</Text>
)}
</div>
</StyledPluginItem>
);
};
export default PluginItem;

View File

@ -45,6 +45,7 @@ const DeveloperToolsWrapper = (props) => {
"JavascriptSdk",
"Webhooks",
"Settings",
"WebPlugins",
]);
const [isPending, startTransition] = useTransition();
@ -85,7 +86,7 @@ const DeveloperToolsWrapper = (props) => {
if (enablePlugins) {
data.push({
id: "plugins",
name: "Plugins",
name: t("WebPlugins:Plugins"),
content: <PluginPage />,
});
}

View File

@ -42,7 +42,6 @@ class PluginStore {
settingsPluginDialogVisible = false;
currentSettingsDialogPlugin = null;
isAdminSettingsDialog = false;
pluginDialogVisible = false;
pluginDialogProps = null;
@ -71,10 +70,6 @@ class PluginStore {
this.settingsPluginDialogVisible = value;
};
setIsAdminSettingsDialog = (value) => {
this.isAdminSettingsDialog = value;
};
setPluginDialogVisible = (value) => {
this.pluginDialogVisible = value;
};
@ -230,13 +225,13 @@ class PluginStore {
};
changePluginStatus = async (id, status) => {
if (status === "true") {
if (status) {
this.activatePlugin(id);
} else {
this.deactivatePlugin(id);
}
const plugin = await api.plugins.activatePlugin(id, status === "true");
const plugin = await api.plugins.activatePlugin(id, status);
return plugin;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5_16946)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.98173 1.76932C3.1458 1.637 3.37667 1.63302 3.55181 1.75028L5.06682 2.76461C5.47482 2.53553 5.91232 2.35277 6.3722 2.22344L6.72621 0.435008C6.76716 0.228156 6.93333 0.0676944 7.14301 0.0453639C7.42457 0.0153787 7.71049 0 8 0C8.28951 0 8.57543 0.0153788 8.85699 0.0453639C9.06667 0.0676944 9.23284 0.228155 9.27379 0.435008L9.6278 2.22344C10.0877 2.35277 10.5252 2.53553 10.9332 2.76461L12.4482 1.75028C12.6233 1.63302 12.8542 1.637 13.0183 1.76932C13.4643 2.12903 13.871 2.53567 14.2307 2.98173C14.363 3.1458 14.367 3.37667 14.2497 3.55181L13.2354 5.06682C13.4645 5.47482 13.6472 5.91232 13.7766 6.3722L15.565 6.72621C15.7718 6.76716 15.9323 6.93333 15.9546 7.14301C15.9846 7.42457 16 7.71049 16 8C16 8.28951 15.9846 8.57543 15.9546 8.85699C15.9323 9.06667 15.7718 9.23284 15.565 9.27379L13.7766 9.6278C13.6472 10.0877 13.4645 10.5252 13.2354 10.9332L14.2497 12.4482C14.367 12.6233 14.363 12.8542 14.2307 13.0183C13.871 13.4643 13.4643 13.871 13.0183 14.2307C12.8542 14.363 12.6233 14.367 12.4482 14.2497L10.9332 13.2354C10.5252 13.4645 10.0877 13.6472 9.6278 13.7766L9.27379 15.565C9.23284 15.7718 9.06667 15.9323 8.85699 15.9546C8.57543 15.9846 8.28951 16 8 16C7.71049 16 7.42457 15.9846 7.14301 15.9546C6.93333 15.9323 6.76716 15.7718 6.72621 15.565L6.3722 13.7766C5.91232 13.6472 5.47482 13.4645 5.06682 13.2354L3.55181 14.2497C3.37667 14.367 3.1458 14.363 2.98173 14.2307C2.53567 13.871 2.12903 13.4643 1.76932 13.0183C1.637 12.8542 1.63302 12.6233 1.75028 12.4482L2.76461 10.9332C2.53553 10.5252 2.35277 10.0877 2.22344 9.6278L0.435008 9.27379C0.228156 9.23284 0.0676944 9.06667 0.0453639 8.85699C0.0153788 8.57543 0 8.28951 0 8C0 7.71049 0.0153788 7.42457 0.0453639 7.14301C0.0676944 6.93333 0.228155 6.76716 0.435008 6.72621L2.22344 6.3722C2.35277 5.91232 2.53553 5.47482 2.76461 5.06682L1.75028 3.55181C1.63302 3.37667 1.637 3.1458 1.76932 2.98173C2.12903 2.53567 2.53567 2.12903 2.98173 1.76932ZM8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z" fill="#A3A9AE"/>
</g>
<defs>
<clipPath id="clip0_5_16946">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB