From d045c5ce2dacb53a66a8d5fbeab5e198181e410d Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 22 Jan 2018 04:20:39 +0200 Subject: [PATCH] Add search feature --- client/App.jsx | 35 +++++++---- client/Credits.jsx | 7 +-- client/FixedContainer.jsx | 42 +++++++++++++ client/GraphMap.jsx | 38 +++++++++++- client/LNTips.jsx | 2 - client/NetworkInfo.jsx | 7 +-- client/NodeInfo.jsx | 2 +- client/NodeItem.jsx | 13 ++++ client/ObjectInfo.jsx | 7 +-- client/Search.jsx | 124 ++++++++++++++++++++++++++++++++++++++ package-lock.json | 47 +++++++++------ package.json | 3 + webpack.config.js | 19 ++++-- 13 files changed, 289 insertions(+), 57 deletions(-) create mode 100644 client/FixedContainer.jsx create mode 100644 client/NodeItem.jsx create mode 100644 client/Search.jsx diff --git a/client/App.jsx b/client/App.jsx index ce5ebb8..d5c00a9 100644 --- a/client/App.jsx +++ b/client/App.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import FixedContainer from './FixedContainer.jsx'; import GraphMap from './GraphMap.jsx'; import ObjectInfo from './ObjectInfo.jsx'; import NodeInfo from './NodeInfo.jsx'; @@ -6,6 +7,7 @@ import ChannelInfo from './ChannelInfo.jsx'; import NetworkInfo from './NetworkInfo.jsx'; import Credits from './Credits.jsx'; import Warning from './Warning.jsx'; +import Search from './Search.jsx'; import Title from './Title.jsx'; import { ObjectTypes } from './DataStore.js'; import { observer } from 'mobx-react'; @@ -31,18 +33,27 @@ export default class App extends React.Component { - {selectedObject ? ( - - {selectedObject.type == ObjectTypes.NODE ? ( - - ) : ( - - )} - - ) : ''} - - #recksplorer - + + {selectedObject ? ( + + {selectedObject.type == ObjectTypes.NODE ? ( + + ) : ( + + )} + + ) : ''} + + + + + + + #recksplorer + + + + ); diff --git a/client/Credits.jsx b/client/Credits.jsx index 2c289d9..b1b8ecb 100644 --- a/client/Credits.jsx +++ b/client/Credits.jsx @@ -4,14 +4,9 @@ import LNTips from './LNTips.jsx'; var styles = { container:{ - position: 'fixed', - bottom: 0, - right: 0, padding: '5px', fontSize: '11px', - backgroundColor: '#FFFFFF', - borderTop: '1px solid #891AFF', - borderLeft: '1px solid #891AFF' + backgroundColor: '#FFFFFF' }, } diff --git a/client/FixedContainer.jsx b/client/FixedContainer.jsx new file mode 100644 index 0000000..9d3def8 --- /dev/null +++ b/client/FixedContainer.jsx @@ -0,0 +1,42 @@ +import React from 'react'; + +var styles = { + container:{ + position: 'fixed' + }, +} + +const border = '1px solid #891AFF'; + +// Capitalizes first letter +function jsUcfirst(string) +{ + return string.charAt(0).toUpperCase() + string.slice(1); +} + +export default class FixedContainer extends React.Component { + render() { + var customStyle = {}; + + // Transforms "top-left" into {top: 0, left: 0} + if (this.props.position) { + customStyle = this.props.position.split('-').reduce((style, position) => { + style[position] = 0; + return style; + }, customStyle); + } + + if (this.props.border) { + customStyle = this.props.border.split('-').reduce((style, position) => { + style['border' + jsUcfirst(position)] = border; + return style; + }, customStyle); + } + + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/client/GraphMap.jsx b/client/GraphMap.jsx index 3c624ae..75c52fa 100644 --- a/client/GraphMap.jsx +++ b/client/GraphMap.jsx @@ -1,6 +1,7 @@ import React from 'react'; import Graph from 'vis-react'; -import { computed } from 'mobx'; +import { ObjectTypes } from './DataStore.js'; +import { computed, autorun } from 'mobx'; import { observer } from 'mobx-react'; @observer @@ -58,7 +59,10 @@ export default class App extends React.Component { id: data.nodes[i].pub_key, label: label, color: { - border: data.nodes[i].color + border: data.nodes[i].color, + highlight: { + border: data.nodes[i].color + } } }); } @@ -70,7 +74,9 @@ export default class App extends React.Component { to: data.edges[i].node1_pub, from: data.edges[i].node2_pub, width: Math.log(data.edges[i].capacity)/6, - color: {inherit:'both'} + color: { + inherit:'both' + } }); } @@ -82,6 +88,32 @@ export default class App extends React.Component { this.network = network; } + componentDidMount() + { + this.selectObserver = autorun(() => { + var object = this.props.store.selectedObjectData; + + if (!this.network || !object) + return; + + console.log(object); + + if (object.type == ObjectTypes.NODE) + { + this.network.selectNodes([object.pub_key]); + } + else + { + this.network.setSelection({ + nodes: [object.node1_pub, object.node2_pub], + edges: [object.channel_id] + }, { + highlightEdges: false + }); + } + }); + } + componentDidUpdate(prevProps, prevState) { if (this.network) { diff --git a/client/LNTips.jsx b/client/LNTips.jsx index 323f5d8..6350f0d 100644 --- a/client/LNTips.jsx +++ b/client/LNTips.jsx @@ -1,6 +1,5 @@ import React from 'react'; import AppStyles from './App.css'; -import { observer } from 'mobx-react'; import Axios from 'axios'; import { RHashArrayToHexString, ParseAxiosError } from './Utils.js'; @@ -32,7 +31,6 @@ var styles = { } } -@observer export default class LNTips extends React.Component { state = { paymentState: PaymentState.PAYMENT_STATE_WAITING_FOR_INPUT, diff --git a/client/NetworkInfo.jsx b/client/NetworkInfo.jsx index a84e3cb..f62bb60 100644 --- a/client/NetworkInfo.jsx +++ b/client/NetworkInfo.jsx @@ -5,14 +5,9 @@ import { observer } from 'mobx-react'; var styles = { container:{ - position: 'fixed', - top: 0, - left: 0, padding: '5px', fontSize: '11px', - backgroundColor: '#FFFFFF', - borderBottom: '1px solid #891AFF', - borderRight: '1px solid #891AFF' + backgroundColor: '#FFFFFF' }, tiny: { fontSize: '9px' diff --git a/client/NodeInfo.jsx b/client/NodeInfo.jsx index 6cdfa40..66afdd1 100644 --- a/client/NodeInfo.jsx +++ b/client/NodeInfo.jsx @@ -17,7 +17,7 @@ export default class NodeInfo extends React.Component { Alias - {node.alias.replace(/\0/g, '')} + {node.alias.replace(/\0/g, '').substring(0, 18)} URI diff --git a/client/NodeItem.jsx b/client/NodeItem.jsx new file mode 100644 index 0000000..946e62b --- /dev/null +++ b/client/NodeItem.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; + +export default class NodeItem extends React.Component { + render() { + return ( +
  • + + {this.props.data.pub_key.substring(0,18)} +
  • + ); + } +} diff --git a/client/ObjectInfo.jsx b/client/ObjectInfo.jsx index a3f97d0..9179560 100644 --- a/client/ObjectInfo.jsx +++ b/client/ObjectInfo.jsx @@ -3,14 +3,9 @@ import AppStyles from './App.css'; var styles = { container:{ - position: 'fixed', - bottom: 0, - left: 0, padding: '5px', fontSize: '11px', - backgroundColor: '#FFFFFF', - borderTop: '1px solid #891AFF', - borderRight: '1px solid #891AFF' + backgroundColor: '#FFFFFF' } } diff --git a/client/Search.jsx b/client/Search.jsx new file mode 100644 index 0000000..47714e5 --- /dev/null +++ b/client/Search.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import FontAwesome from 'react-fontawesome'; +import FA from 'font-awesome/css/font-awesome.css'; + +var styles = { + container:{ + padding: '5px', + fontSize: '11px', + backgroundColor: '#FFFFFF', + borderTop: '1px solid #891AFF' + }, + input: { + width: '100%', + boxSizing: 'border-box' + }, + results: { + maxHeight: '400px', + overflowY: 'scroll' + }, + ul: { + listStyleType: 'none', + padding: 0 + }, + li: { + padding: '5px', + cursor: 'pointer', + ':hover': { + backgroundColor: '#EEE' + } + }, + text: { + paddingLeft: '10px' + } +} + +class NodeItem extends React.Component { + render() { + return ( +
  • + + {this.props.data.alias.replace(/\0/g, '').substring(0, 18)} +
  • + ); + } +} + +class ChannelItem extends React.Component { + render() { + return ( +
  • + + {this.props.data.channel_id} +
  • + ); + } +} + +export default class Search extends React.Component { + state = { + searchText: '', + results: [] + } + + onSearchTextChange(event) + { + const store = this.props.store; + const value = event.target.value.toUpperCase(); + + if (!value.length) + { + this.setState({ + searchText: value, + results: [] + }); + + return; + } + + var nodes = store.networkData.nodes.filter(node => { + return node.pub_key.toUpperCase().includes(value) || node.alias.toUpperCase().includes(value) || (node.addresses.length && node.addresses[0].addr.toUpperCase().includes(value)); + }); + + var channels = store.networkData.edges.filter(edge => { + return edge.channel_id.toUpperCase().includes(value) || edge.chan_point.toUpperCase().includes(value); + }); + + var results = [ + ...nodes, + ...channels + ]; + + this.setState({ + searchText: value, + results: results + }); + } + + onClickItem(item) + { + this.props.store.selectObject(item); + } + + render() { + const results = this.state.results.map((item) => { + if (item.pub_key) + return this.onClickItem(item.pub_key)} />; + else + return this.onClickItem(item.channel_id)} />; + }); + + return ( +
    + this.onSearchTextChange(event)} style={styles.input} /> + { results.length ? ( +
    +
      + {results} +
    +
    + ) : ('')} +
    + ); + } +} diff --git a/package-lock.json b/package-lock.json index bf34509..8defea4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,6 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, "requires": { "co": "4.6.0", "fast-deep-equal": "1.0.0", @@ -1284,8 +1283,7 @@ "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" }, "binary-extensions": { "version": "1.11.0", @@ -1639,8 +1637,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { "version": "1.0.4", @@ -2214,8 +2211,7 @@ "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" }, "encodeurl": { "version": "1.0.1", @@ -2518,14 +2514,12 @@ "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fastparse": { "version": "1.1.1", @@ -2652,6 +2646,11 @@ } } }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4936,14 +4935,12 @@ "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "keycharm": { "version": "0.2.0", @@ -4995,7 +4992,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, "requires": { "big.js": "3.2.0", "emojis-list": "2.1.0", @@ -6517,6 +6513,14 @@ "prop-types": "15.6.0" } }, + "react-fontawesome": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/react-fontawesome/-/react-fontawesome-1.6.1.tgz", + "integrity": "sha1-7dzhfn3HMaoJ/UoYZoimF5OhbFw=", + "requires": { + "prop-types": "15.6.0" + } + }, "react-proxy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz", @@ -6870,7 +6874,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, "requires": { "ajv": "5.5.2" } @@ -7394,6 +7397,16 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, + "url-loader": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", + "integrity": "sha512-h3qf9TNn53BpuXTTcpC+UehiRrl0Cv45Yr/xWayApjw6G8Bg2dGke7rIwDQ39piciWCWrC+WiqLjOh3SUp9n0Q==", + "requires": { + "loader-utils": "1.1.0", + "mime": "1.4.1", + "schema-utils": "0.3.0" + } + }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", diff --git a/package.json b/package.json index 19769a8..e86e905 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "command-line-usage": "^4.1.0", "express": "^4.16.2", "express-cache-controller": "^1.1.0", + "font-awesome": "^4.7.0", "grpc": "^1.8.0", "js-cookie": "^2.2.0", "mobx": "^3.4.1", @@ -22,7 +23,9 @@ "normalize.css": "^7.0.0", "react": "^16.2.0", "react-dom": "^16.2.0", + "react-fontawesome": "^1.6.1", "typeface-roboto": "0.0.50", + "url-loader": "^0.6.2", "vis": "^4.21.0", "vis-react": "^0.3.0" }, diff --git a/webpack.config.js b/webpack.config.js index cfb0701..c325cec 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -46,6 +46,13 @@ module.exports = { test: /\.json?$/, loader: 'json' }, + { + test: /\font-awesome.css$/, + loader: 'css-loader', + query: { + modules: false + } + }, { test: /\.css$/, loaders: [ @@ -64,10 +71,14 @@ module.exports = { } ], }, - { - test: /\.(eot|svg|ttf|woff|woff2)$/, - loader: 'file-loader?name=public/fonts/[name].[ext]' - } + { + test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: "url-loader?limit=10000&mimetype=application/font-woff" + }, + { + test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: "file-loader" + }, ] } };