diff --git a/README.md b/README.md index ec8e93451..6cd2f977b 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ import Footer from '../components/view/footer' export default function MyPane { return ( - +
@@ -117,7 +117,8 @@ Now, to incorporate your pane into Station iself so it is visible, you have to i The components are classes exported with CamelCase names. The corresponding files have the associated class name with hyphen-separated-words. So, e.g., `simple-stat.js` exports a class named `SimpleStat`. + [**Button**](./src/js/components/view/button.js) is a simple button with text. -+ [**File**](./src/js/components/view/file.js) is used within a file list to describe a file with a button to copy its link. ++ [**CheckboxBlock**](./src/js/components/view/checkbox-block.js) is like an `InfoBlock`, but with a checkbox attached to it. ++ [**FileBlock**](./src/js/components/view/file-block.js) is used within a file list to describe a file with a button to copy its link. + [**Footer**](./src/js/components/view/footer.js) is the footer of a pane. + [**Header**](./src/js/components/view/header.js) is the header of a pane. + [**Heartbeat**](./src/js/components/view/heartbeat.js) displays an heartbeat-like animation with the IPFS logo. @@ -125,8 +126,7 @@ The components are classes exported with CamelCase names. The corresponding file + [**IconDropdownList**](./src/js/components/view/icon-dropdown-list.js) is a dropdown list with an icon. + [**Icon**](./src/js/components/view/icon.js) shows an icon. + [**InfoBlock**](./src/js/components/view/info-block.js) shows a block of information (used on node info pane). -+ [**Loader**](./src/js/components/view/loader.js) is a loader. -+ [**Peer**](./src/js/components/view/peer.js) shows a peer information. ++ [**MenuOption**](./src/js/components/view/menu-option.js) is a menu option to show within a menu bar. ## Contribute diff --git a/src/config.js b/src/config.js index e2943f7fc..14f5ecdbb 100644 --- a/src/config.js +++ b/src/config.js @@ -92,17 +92,6 @@ export default { ice: logo('ice'), black: logo('black') }, - // Will be replaced by a BrowserWindow instance. - settingsWindow: { - index: `file://${__dirname}/views/settings.html`, - title: 'IPFS Station Settings', - show: false, - icon: logo('ice'), - backgroundColor: '#252525', - resizable: false, - width: 450, - height: 450 - }, // Will be replaced by a Menubar instance. menubar: { index: `file://${__dirname}/views/menubar.html`, @@ -112,9 +101,9 @@ export default { window: { resizable: false, skipTaskbar: true, - width: 850, + width: 600, height: 400, - backgroundColor: '#000000', + backgroundColor: (settingsStore.get('lightTheme') ? '#FFFFFF' : '#000000'), webPreferences: { nodeIntegration: true, webSecurity: false diff --git a/src/controls/main/settings.js b/src/controls/main/settings.js index 9b7f292a2..c06562eca 100644 --- a/src/controls/main/settings.js +++ b/src/controls/main/settings.js @@ -1,31 +1,5 @@ import {join} from 'path' -import {shell, ipcMain, BrowserWindow} from 'electron' - -function setupWindow (opts) { - const options = opts.settingsWindow - const window = new BrowserWindow(options) - - window.setMenu(null) - window.loadURL(options.index) - window.on('close', (event) => { - event.preventDefault() - window.hide() - }) - - // Replace the window settings by the actual - // instance of BrowserWindow - opts.settingsWindow = window -} - -function open (opts) { - const {settingsWindow, settingsStore} = opts - - return () => { - settingsWindow.webContents.send('settings', settingsStore.toObject()) - settingsWindow.show() - settingsWindow.focus() - } -} +import {shell, ipcMain} from 'electron' function openNodeConfig (opts) { const {settingsStore} = opts @@ -39,19 +13,20 @@ function openNodeConfig (opts) { function updateSettings (opts) { const {settingsStore} = opts - return (event, settings) => { - for (const key in settings) { - if (settings.hasOwnProperty(key)) { - settingsStore.set(key, settings[key]) - } - } + return (event, key, value) => { + settingsStore.set(key, value) } } export default function (opts) { - setupWindow(opts) + const {send, settingsStore} = opts + + const handler = () => { + send('settings', settingsStore.toObject()) + } - ipcMain.on('open-settings', open(opts)) - ipcMain.on('update-settings', updateSettings(opts)) + ipcMain.on('request-settings', handler) + settingsStore.on('change', handler) + ipcMain.on('update-setting', updateSettings(opts)) ipcMain.on('open-node-settings', openNodeConfig(opts)) } diff --git a/src/index.js b/src/index.js index ef13f9155..a0474be5b 100644 --- a/src/index.js +++ b/src/index.js @@ -24,15 +24,6 @@ app.makeSingleInstance(() => { ) }) -// Make sure Settings Window is closed when quitting the application. -app.on('before-quit', (event) => { - const {settingsWindow} = config - - if (settingsWindow instanceof BrowserWindow) { - settingsWindow.destroy() - } -}) - // Local Variables let poller = null diff --git a/src/js/components/view/checkbox-item.js b/src/js/components/view/checkbox-block.js similarity index 56% rename from src/js/components/view/checkbox-item.js rename to src/js/components/view/checkbox-block.js index d4382cef2..56f7d443d 100644 --- a/src/js/components/view/checkbox-item.js +++ b/src/js/components/view/checkbox-block.js @@ -1,7 +1,19 @@ import React from 'react' import PropTypes from 'prop-types' -export default function CheckboxItem (props) { +/** + * Is a Checkbox Block. + * + * @param {Object} props + * + * @prop {Bool} value - the current value (checked or not) + * @prop {Any} info - the description + * @prop {String} title - the option's title + * @prop {Function} onChange - the on change handler + * + * @return {ReactElement} + */ +export default function CheckboxBlock (props) { const _onClick = () => { props.onChange(!props.value) } @@ -9,22 +21,21 @@ export default function CheckboxItem (props) { return (
-
- - -

{props.title}

{props.info}

+
+ + +
) } -CheckboxItem.propTypes = { +CheckboxBlock.propTypes = { value: PropTypes.bool.isRequired, - id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, info: PropTypes.any diff --git a/src/js/components/view/file.js b/src/js/components/view/file-block.js similarity index 95% rename from src/js/components/view/file.js rename to src/js/components/view/file-block.js index b35a870d1..6e5d3bce3 100644 --- a/src/js/components/view/file.js +++ b/src/js/components/view/file-block.js @@ -8,7 +8,7 @@ import Button from './button' import Icon from './icon' /** - * Is a File. + * Is a File Block. * * @param {Object} props * @@ -18,7 +18,7 @@ import Icon from './icon' * * @return {ReactElement} */ -export default function File (props) { +export default function FileBlock (props) { const extension = fileExtension(props.name) let icon = 'file' if (fileTypes[extension]) { @@ -45,7 +45,7 @@ export default function File (props) { ) } -File.propTypes = { +FileBlock.propTypes = { name: PropTypes.string.isRequired, date: PropTypes.string.isRequired, hash: PropTypes.string.isRequired diff --git a/src/js/components/view/icon.js b/src/js/components/view/icon.js index ca2b7cfc9..5caac9967 100644 --- a/src/js/components/view/icon.js +++ b/src/js/components/view/icon.js @@ -11,6 +11,14 @@ import PropTypes from 'prop-types' * @return {ReactElement} */ export default function Icon (props) { + if (props.name === 'ipfs') { + return ( + + + + ) + } + return ( ) diff --git a/src/js/components/view/info-block.js b/src/js/components/view/info-block.js index 64676253d..1443df76c 100644 --- a/src/js/components/view/info-block.js +++ b/src/js/components/view/info-block.js @@ -14,31 +14,19 @@ import Button from '../view/button' * @prop {Bool} [button=true] - sets 'onClick' to be triggered when clicking a button. * Otherwise it triggeres when clicking the whole block. * @prop {String} [buttonMessage=Copy] - sets the button message. - * @prop {Bool} [pre=false] - tells if the information should be wrapped inside a 'pre' block. * * @return {ReactElement} */ export default function InfoBlock (props) { let info = null - if (props.pre) { - if (Array.isArray(props.info)) { - info = [] - props.info.forEach((element, index) => { - info.push((
{element}
)) - }) - } else { - info = (
{props.info}
) - } + if (Array.isArray(props.info)) { + info = [] + props.info.forEach((element, index) => { + info.push((

{element}

)) + }) } else { - if (Array.isArray(props.info)) { - info = [] - props.info.forEach((element, index) => { - info.push((

{element}

)) - }) - } else { - info = (

{props.info}

) - } + info = (

{props.info}

) } let button = null @@ -72,12 +60,10 @@ InfoBlock.propTypes = { title: PropTypes.string.isRequired, onClick: PropTypes.func, button: PropTypes.bool, - buttonMessage: PropTypes.string, - pre: PropTypes.bool + buttonMessage: PropTypes.string } InfoBlock.defaultProps = { - pre: false, button: true, buttonMessage: 'Copy', onClick: null diff --git a/src/js/components/view/key-combo.js b/src/js/components/view/key-combo.js new file mode 100644 index 000000000..202310059 --- /dev/null +++ b/src/js/components/view/key-combo.js @@ -0,0 +1,29 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Key from './key' + +/** + * Is a Key Combination + * + * @param {Object} props + * + * @prop {Array} keys - must be an array of strings. + * + * @return {ReactElement} + */ +export default function KeyCombo (props) { + const keys = [] + + props.keys.forEach((key, index) => { + keys.push({key}) + keys.push( + ) + }) + + keys.pop() + return keys +} + +KeyCombo.propTypes = { + keys: PropTypes.array.isRequired +} diff --git a/src/js/components/view/key.js b/src/js/components/view/key.js new file mode 100644 index 000000000..f3428f44b --- /dev/null +++ b/src/js/components/view/key.js @@ -0,0 +1,23 @@ +import React from 'react' +import PropTypes from 'prop-types' + +/** + * Is a Key. + * + * @param {Object} props + * + * @prop {Node} children - must be a string or a node. + * + * @return {ReactElement} + */ +export default function Key (props) { + return ( + + {props.children} + + ) +} + +Key.propTypes = { + children: PropTypes.node.isRequired +} diff --git a/src/js/components/view/loader.js b/src/js/components/view/loader.js deleted file mode 100644 index ee0512a97..000000000 --- a/src/js/components/view/loader.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' - -/** - * Is a Loader. - * - * @return {ReactElement} - */ -export default function Loader () { - return ( -
- - - - - - - - - - - -
- ) -} diff --git a/src/js/components/view/menu-option.js b/src/js/components/view/menu-option.js new file mode 100644 index 000000000..91a0a8739 --- /dev/null +++ b/src/js/components/view/menu-option.js @@ -0,0 +1,35 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Icon from './icon' + +/** + * Is a menu option. + * + * @param {Object} props + * + * @prop {Bool} [active] + * @prop {Function} onClick + * @prop {String} icon + * @prop {String} title + * + * @return {ReactElement} + */ +export default function MenuOption (props) { + let className = 'menu-option' + if (props.active) className += ' active' + + return ( + + ) +} + +MenuOption.propTypes = { + active: PropTypes.bool, + onClick: PropTypes.func.isRequired, + icon: PropTypes.string.isRequired, + title: PropTypes.string.isRequired +} diff --git a/src/js/components/view/pane-container.js b/src/js/components/view/pane-container.js index 36b51f549..3b673a516 100644 --- a/src/js/components/view/pane-container.js +++ b/src/js/components/view/pane-container.js @@ -7,17 +7,24 @@ import PropTypes from 'prop-types' * @param {Object} props * * @prop {Node} children - must be elements of component Pane + * @prop {String} [class] - additional class names * * @return {ReactElement} */ export default function PaneContainer (props) { + let className = 'panes' + if (props.className) { + className += ' ' + props.className + } + return ( -
+
{props.children}
) } PaneContainer.propTypes = { - children: PropTypes.node.isRequired + children: PropTypes.node.isRequired, + className: PropTypes.string } diff --git a/src/js/components/view/peer.js b/src/js/components/view/peer.js deleted file mode 100644 index 47607c530..000000000 --- a/src/js/components/view/peer.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -/** - * Is a Peer. - * - * @param {Object} props - * - * @prop {String} id - * @prop {Object} location - * @prop {String} location.formatted - * - * @return {ReactElement} - */ -export default function Peer (props) { - return ( -
-

{props.id}

-

{props.location.formatted}

-
- ) -} - -Peer.propTypes = { - id: PropTypes.string.isRequired, - location: PropTypes.shape({ - formatted: PropTypes.string.isRequired - }).isRequired -} diff --git a/src/js/panes/files.js b/src/js/panes/files.js index 19cb7ba20..2934c26bf 100644 --- a/src/js/panes/files.js +++ b/src/js/panes/files.js @@ -7,7 +7,7 @@ import {DropTarget} from 'react-dnd' import Pane from '../components/view/pane' import Header from '../components/view/header' import Footer from '../components/view/footer' -import File from '../components/view/file' +import File from '../components/view/file-block' import IconButton from '../components/view/icon-button' const fileTarget = { @@ -32,7 +32,6 @@ class Files extends Component { static propTypes = { connectDropTarget: PropTypes.func.isRequired, - changeRoute: PropTypes.func.isRequired, isOver: PropTypes.bool.isRequired, canDrop: PropTypes.bool.isRequired, files: PropTypes.array @@ -82,7 +81,7 @@ class Files extends Component { return connectDropTarget(
- +
@@ -94,7 +93,6 @@ class Files extends Component {