diff --git a/.lintstagedrc b/.lintstagedrc index 8a218d0..f57caff 100644 --- a/.lintstagedrc +++ b/.lintstagedrc @@ -1,12 +1,10 @@ { "*.{ts,tsx}": [ "eslint --ext .ts,.tsx --fix", - "prettier --single-quote --write", - "git add" + "prettier --single-quote --write" ], "{.{babelrc,eslintrc,prettierrc,stylelintrc}}": [ - "prettier --parser json --write", - "git add" + "prettier --parser json --write" ], - "*.{yml,md}": ["prettier --single-quote --write", "git add"] + "*.{yml,md}": ["prettier --single-quote --write"] } diff --git a/src/components/nodes/CustomNodesTable.tsx b/src/components/nodes/CustomNodesTable.tsx index aabf942..f04b6d9 100644 --- a/src/components/nodes/CustomNodesTable.tsx +++ b/src/components/nodes/CustomNodesTable.tsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { DeleteOutlined, FormOutlined } from '@ant-design/icons'; import styled from '@emotion/styled'; -import { Button, Table } from 'antd'; +import { Button, Modal, Table } from 'antd'; import { usePrefixedTranslation } from 'hooks'; import { NodeImplementation } from 'shared/types'; +import { useStoreActions } from 'store'; import { CustomNode } from 'types'; import { dockerConfigs } from 'utils/constants'; import { getPolarPlatform } from 'utils/system'; @@ -43,16 +44,37 @@ const CustomNodesTable: React.FC = ({ nodes }) => { const { l } = usePrefixedTranslation('cmps.nodes.CustomNodesTable'); const currPlatform = getPolarPlatform(); const [editingNode, setEditingNode] = useState(); + const { removeCustomNode, notify } = useStoreActions(s => s.app); const handleEdit = (node: CustomNodeView) => { const { id, implementation, dockerImage, command } = node; setEditingNode({ id, implementation, dockerImage, command }); }; - const handleDelete = (node: CustomNodeView) => { - console.warn(node); + let modal: any; + const showRemoveModal = (node: CustomNodeView) => { + const { dockerImage } = node; + modal = Modal.confirm({ + title: l('confirmTitle', { dockerImage }), + content: l('confirmText'), + okText: l('confirmBtn'), + okType: 'danger', + cancelText: l('cancelBtn'), + onOk: async () => { + try { + await removeCustomNode(node); + notify({ message: l('success', { dockerImage }) }); + } catch (error) { + notify({ message: l('error'), error }); + throw error; + } + }, + }); }; + // cleanup the modal when the component unmounts + useEffect(() => () => modal && modal.destroy(), [modal]); + // don't show the table if there are no custom nodes if (!nodes.length) { return null; @@ -101,7 +123,7 @@ const CustomNodesTable: React.FC = ({ nodes }) => { } - onClick={() => handleDelete(node)} + onClick={() => showRemoveModal(node)} > )} diff --git a/src/components/nodes/NodesView.tsx b/src/components/nodes/NodesView.tsx index 87094d7..08e35ca 100644 --- a/src/components/nodes/NodesView.tsx +++ b/src/components/nodes/NodesView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { info } from 'electron-log'; import { PlusOutlined } from '@ant-design/icons'; import styled from '@emotion/styled'; @@ -7,8 +7,10 @@ import { usePrefixedTranslation } from 'hooks'; import { useTheme } from 'hooks/useTheme'; import { useStoreActions, useStoreState } from 'store'; import { ThemeColors } from 'theme/colors'; +import { CustomNode } from 'types'; +import { dockerConfigs } from 'utils/constants'; import { HOME } from 'components/routing'; -import { CustomNodesTable, ManagedNodesTable } from './'; +import { CustomNodeModal, CustomNodesTable, ManagedNodesTable } from './'; const Styled = { PageHeader: styled(PageHeader)<{ colors: ThemeColors['pageHeader'] }>` @@ -28,9 +30,19 @@ const NodesView: React.FC = () => { const { l } = usePrefixedTranslation('cmps.nodes.NodesView'); const theme = useTheme(); + const [addingNode, setAddingNode] = useState(); const { managedNodes, settings } = useStoreState(s => s.app); const { navigateTo } = useStoreActions(s => s.app); + const handleAdd = () => { + setAddingNode({ + id: '', + implementation: 'LND', + dockerImage: '', + command: dockerConfigs.LND.command, + }); + }; + return ( <> { colors={theme.pageHeader} onBack={() => navigateTo(HOME)} extra={ - } @@ -52,6 +64,9 @@ const NodesView: React.FC = () => { + {addingNode && ( + setAddingNode(undefined)} /> + )} ); }; diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 216d524..94cfc22 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -274,6 +274,12 @@ "cmps.nodes.CustomNodesTable.command": "Command", "cmps.nodes.CustomNodesTable.manage": "Manage", "cmps.nodes.CustomNodesTable.edit": "Edit", + "cmps.nodes.CustomNodesTable.confirmTitle": "Are you sure you want to remove the custom image '{{dockerImage}}'?", + "cmps.nodes.CustomNodesTable.confirmText": "This image will no longer be displayed in the network sidebar. Any nodes in existing networks will continue to use this image until they are removed.", + "cmps.nodes.CustomNodesTable.confirmBtn": "Yes", + "cmps.nodes.CustomNodesTable.cancelBtn": "Cancel", + "cmps.nodes.CustomNodesTable.success": "The custom image '{{dockerImage}}' has been removed", + "cmps.nodes.CustomNodesTable.error": "Unable to remove the custom node", "cmps.nodes.ManagedNodeModal.title": "Customize Managed Node - {{implementation}} v{{version}}", "cmps.nodes.ManagedNodeModal.summary": "This form allows you to customize the command used to start nodes. All nodes that use the specified Docker Image will use this command to startup.", "cmps.nodes.ManagedNodeModal.dockerImage": "Docker Image", diff --git a/src/store/models/app.ts b/src/store/models/app.ts index b414f1b..073339e 100644 --- a/src/store/models/app.ts +++ b/src/store/models/app.ts @@ -40,6 +40,7 @@ export interface AppModel { updateSettings: Thunk, StoreInjections, RootModel>; updateManagedNode: Thunk; saveCustomNode: Thunk; + removeCustomNode: Thunk; initialize: Thunk; setDockerVersions: Action; getDockerVersions: Thunk; @@ -158,6 +159,15 @@ const appModel: AppModel = { nodes: { ...nodes, custom }, }); }), + removeCustomNode: thunk(async (actions, node, { getState }) => { + const { nodes } = getState().settings; + // remove the custom node + const custom = nodes.custom.filter(c => c.id !== node.id); + // update the settings in state and on disk + await actions.updateSettings({ + nodes: { ...nodes, custom }, + }); + }), setDockerVersions: action((state, versions) => { state.dockerVersions = versions; }),