Browse Source

feat(images): implement adding and deleting custom nodes

master
jamaljsr 5 years ago
parent
commit
0fc109bbce
  1. 8
      .lintstagedrc
  2. 32
      src/components/nodes/CustomNodesTable.tsx
  3. 21
      src/components/nodes/NodesView.tsx
  4. 6
      src/i18n/locales/en-US.json
  5. 10
      src/store/models/app.ts

8
.lintstagedrc

@ -1,12 +1,10 @@
{ {
"*.{ts,tsx}": [ "*.{ts,tsx}": [
"eslint --ext .ts,.tsx --fix", "eslint --ext .ts,.tsx --fix",
"prettier --single-quote --write", "prettier --single-quote --write"
"git add"
], ],
"{.{babelrc,eslintrc,prettierrc,stylelintrc}}": [ "{.{babelrc,eslintrc,prettierrc,stylelintrc}}": [
"prettier --parser json --write", "prettier --parser json --write"
"git add"
], ],
"*.{yml,md}": ["prettier --single-quote --write", "git add"] "*.{yml,md}": ["prettier --single-quote --write"]
} }

32
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 { DeleteOutlined, FormOutlined } from '@ant-design/icons';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Button, Table } from 'antd'; import { Button, Modal, Table } from 'antd';
import { usePrefixedTranslation } from 'hooks'; import { usePrefixedTranslation } from 'hooks';
import { NodeImplementation } from 'shared/types'; import { NodeImplementation } from 'shared/types';
import { useStoreActions } from 'store';
import { CustomNode } from 'types'; import { CustomNode } from 'types';
import { dockerConfigs } from 'utils/constants'; import { dockerConfigs } from 'utils/constants';
import { getPolarPlatform } from 'utils/system'; import { getPolarPlatform } from 'utils/system';
@ -43,16 +44,37 @@ const CustomNodesTable: React.FC<Props> = ({ nodes }) => {
const { l } = usePrefixedTranslation('cmps.nodes.CustomNodesTable'); const { l } = usePrefixedTranslation('cmps.nodes.CustomNodesTable');
const currPlatform = getPolarPlatform(); const currPlatform = getPolarPlatform();
const [editingNode, setEditingNode] = useState<CustomNode>(); const [editingNode, setEditingNode] = useState<CustomNode>();
const { removeCustomNode, notify } = useStoreActions(s => s.app);
const handleEdit = (node: CustomNodeView) => { const handleEdit = (node: CustomNodeView) => {
const { id, implementation, dockerImage, command } = node; const { id, implementation, dockerImage, command } = node;
setEditingNode({ id, implementation, dockerImage, command }); setEditingNode({ id, implementation, dockerImage, command });
}; };
const handleDelete = (node: CustomNodeView) => { let modal: any;
console.warn(node); 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 // don't show the table if there are no custom nodes
if (!nodes.length) { if (!nodes.length) {
return null; return null;
@ -101,7 +123,7 @@ const CustomNodesTable: React.FC<Props> = ({ nodes }) => {
<Styled.DeleteButton <Styled.DeleteButton
type="link" type="link"
icon={<DeleteOutlined />} icon={<DeleteOutlined />}
onClick={() => handleDelete(node)} onClick={() => showRemoveModal(node)}
></Styled.DeleteButton> ></Styled.DeleteButton>
</> </>
)} )}

21
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 { info } from 'electron-log';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -7,8 +7,10 @@ import { usePrefixedTranslation } from 'hooks';
import { useTheme } from 'hooks/useTheme'; import { useTheme } from 'hooks/useTheme';
import { useStoreActions, useStoreState } from 'store'; import { useStoreActions, useStoreState } from 'store';
import { ThemeColors } from 'theme/colors'; import { ThemeColors } from 'theme/colors';
import { CustomNode } from 'types';
import { dockerConfigs } from 'utils/constants';
import { HOME } from 'components/routing'; import { HOME } from 'components/routing';
import { CustomNodesTable, ManagedNodesTable } from './'; import { CustomNodeModal, CustomNodesTable, ManagedNodesTable } from './';
const Styled = { const Styled = {
PageHeader: styled(PageHeader)<{ colors: ThemeColors['pageHeader'] }>` PageHeader: styled(PageHeader)<{ colors: ThemeColors['pageHeader'] }>`
@ -28,9 +30,19 @@ const NodesView: React.FC = () => {
const { l } = usePrefixedTranslation('cmps.nodes.NodesView'); const { l } = usePrefixedTranslation('cmps.nodes.NodesView');
const theme = useTheme(); const theme = useTheme();
const [addingNode, setAddingNode] = useState<CustomNode>();
const { managedNodes, settings } = useStoreState(s => s.app); const { managedNodes, settings } = useStoreState(s => s.app);
const { navigateTo } = useStoreActions(s => s.app); const { navigateTo } = useStoreActions(s => s.app);
const handleAdd = () => {
setAddingNode({
id: '',
implementation: 'LND',
dockerImage: '',
command: dockerConfigs.LND.command,
});
};
return ( return (
<> <>
<Styled.PageHeader <Styled.PageHeader
@ -38,7 +50,7 @@ const NodesView: React.FC = () => {
colors={theme.pageHeader} colors={theme.pageHeader}
onBack={() => navigateTo(HOME)} onBack={() => navigateTo(HOME)}
extra={ extra={
<Button type="primary" icon={<PlusOutlined />}> <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
{l('addBtn')} {l('addBtn')}
</Button> </Button>
} }
@ -52,6 +64,9 @@ const NodesView: React.FC = () => {
<CustomNodesTable nodes={settings.nodes.custom} /> <CustomNodesTable nodes={settings.nodes.custom} />
<ManagedNodesTable nodes={managedNodes} /> <ManagedNodesTable nodes={managedNodes} />
{addingNode && (
<CustomNodeModal node={addingNode} onClose={() => setAddingNode(undefined)} />
)}
</> </>
); );
}; };

6
src/i18n/locales/en-US.json

@ -274,6 +274,12 @@
"cmps.nodes.CustomNodesTable.command": "Command", "cmps.nodes.CustomNodesTable.command": "Command",
"cmps.nodes.CustomNodesTable.manage": "Manage", "cmps.nodes.CustomNodesTable.manage": "Manage",
"cmps.nodes.CustomNodesTable.edit": "Edit", "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.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.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", "cmps.nodes.ManagedNodeModal.dockerImage": "Docker Image",

10
src/store/models/app.ts

@ -40,6 +40,7 @@ export interface AppModel {
updateSettings: Thunk<AppModel, Partial<AppSettings>, StoreInjections, RootModel>; updateSettings: Thunk<AppModel, Partial<AppSettings>, StoreInjections, RootModel>;
updateManagedNode: Thunk<AppModel, ManagedNode, StoreInjections, RootModel>; updateManagedNode: Thunk<AppModel, ManagedNode, StoreInjections, RootModel>;
saveCustomNode: Thunk<AppModel, CustomNode, StoreInjections, RootModel>; saveCustomNode: Thunk<AppModel, CustomNode, StoreInjections, RootModel>;
removeCustomNode: Thunk<AppModel, CustomNode, StoreInjections, RootModel>;
initialize: Thunk<AppModel, any, StoreInjections, RootModel>; initialize: Thunk<AppModel, any, StoreInjections, RootModel>;
setDockerVersions: Action<AppModel, DockerVersions>; setDockerVersions: Action<AppModel, DockerVersions>;
getDockerVersions: Thunk<AppModel, { throwErr?: boolean }, StoreInjections, RootModel>; getDockerVersions: Thunk<AppModel, { throwErr?: boolean }, StoreInjections, RootModel>;
@ -158,6 +159,15 @@ const appModel: AppModel = {
nodes: { ...nodes, custom }, 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) => { setDockerVersions: action((state, versions) => {
state.dockerVersions = versions; state.dockerVersions = versions;
}), }),

Loading…
Cancel
Save