├── .babelrc
├── .eslintrc
├── .gitignore
├── .snyk
├── .travis.yml
├── LICENSE
├── README.md
├── app.html
├── app.js
├── appveyor.yml
├── assets
├── LICENSE.txt
├── Metropolis-Bold.otf
├── Metropolis-Medium.otf
├── Metropolis-Regular.ttf
├── fontawesome-webfont.woff2
├── icon.icns
├── icon.ico
├── icon.png
├── siaLogo.svg
├── trayTemplate.png
├── trayTemplate@2x.png
├── trayWin.png
└── trayWin@2x.png
├── css
├── font-awesome.min.css
├── fonts.css
├── general.css
├── grids-responsive-min.css
├── plugin-standard.css
└── pure-min.css
├── doc
├── Developers.md
├── DevelopmentFlow.md
├── PluginTutorial.md
├── Plugins.md
├── Structure.md
├── Technologies.md
├── Testing.md
├── assets
│ ├── basic-overview.png
│ ├── files.png
│ ├── github-plugin.png
│ ├── sidebar.png
│ ├── styled-overview.png
│ ├── update-button.png
│ ├── wallet-notifications.png
│ ├── wallet-tooltip.png
│ └── working-overview.png
└── spec
│ ├── PluginAPI.md
│ ├── Siad.md
│ └── Startup.md
├── internals
├── scripts
│ ├── CheckNodeEnv.js
│ └── CheckPortInUse.js
└── webpack
│ ├── webpack.config.base.js
│ ├── webpack.config.eslint.js
│ ├── webpack.config.main.prod.js
│ ├── webpack.config.renderer.dev.js
│ └── webpack.config.renderer.prod.js
├── js
├── mainjs
│ ├── appMenu.js
│ ├── config.js
│ ├── contextMenu.js
│ ├── initWindow.js
│ └── trayMenu.js
└── rendererjs
│ ├── disabledplugin.js
│ ├── index.js
│ ├── loadingScreen.js
│ ├── pluginapi.js
│ ├── plugins.js
│ └── statusbar.js
├── package-lock.json
├── package.json
├── plugins
├── About
│ ├── assets
│ │ └── button.png
│ ├── css
│ │ └── about.css
│ ├── index.html
│ └── js
│ │ └── index.js
├── Files
│ ├── assets
│ │ └── button.png
│ ├── css
│ │ └── files.css
│ ├── index.html
│ └── js
│ │ ├── actions
│ │ └── files.js
│ │ ├── components
│ │ ├── addfolderbutton.js
│ │ ├── addfolderdialog.js
│ │ ├── allowanceconfirmation.js
│ │ ├── allowancedialog.js
│ │ ├── app.js
│ │ ├── contractorstatus.js
│ │ ├── deletedialog.js
│ │ ├── directoryinfobar.js
│ │ ├── downloadlist.js
│ │ ├── dragoverlay.js
│ │ ├── file.js
│ │ ├── filebrowser.js
│ │ ├── filecontrols.js
│ │ ├── filelist.js
│ │ ├── filetransfers.js
│ │ ├── progressbar.js
│ │ ├── redundancystatus.js
│ │ ├── renamedialog.js
│ │ ├── searchbutton.js
│ │ ├── searchfield.js
│ │ ├── setallowancebutton.js
│ │ ├── transfer.js
│ │ ├── transferlist.js
│ │ ├── transfersbutton.js
│ │ ├── unlockwarning.js
│ │ ├── uploadbutton.js
│ │ ├── uploaddialog.js
│ │ ├── uploadlist.js
│ │ └── usagestats.js
│ │ ├── constants
│ │ └── files.js
│ │ ├── containers
│ │ ├── addfolderbutton.js
│ │ ├── addfolderdialog.js
│ │ ├── allowancedialog.js
│ │ ├── app.js
│ │ ├── contractorstatus.js
│ │ ├── deletedialog.js
│ │ ├── filebrowser.js
│ │ ├── filecontrols.js
│ │ ├── filelist.js
│ │ ├── filetransfers.js
│ │ ├── renamedialog.js
│ │ ├── searchbutton.js
│ │ ├── searchfield.js
│ │ ├── setallowancebutton.js
│ │ ├── transfersbutton.js
│ │ ├── uploadbutton.js
│ │ ├── uploaddialog.js
│ │ └── usagestats.js
│ │ ├── index.js
│ │ ├── reducers
│ │ ├── allowancedialog.js
│ │ ├── deletedialog.js
│ │ ├── files.js
│ │ ├── index.js
│ │ ├── renamedialog.js
│ │ └── wallet.js
│ │ └── sagas
│ │ ├── files.js
│ │ ├── helpers.js
│ │ └── index.js
├── Hosting
│ ├── assets
│ │ └── button.png
│ ├── css
│ │ └── hosting.css
│ ├── index.html
│ └── js
│ │ ├── actions
│ │ └── actions.js
│ │ ├── components
│ │ ├── announce.js
│ │ ├── app.js
│ │ ├── body.js
│ │ ├── fileslist.js
│ │ ├── header.js
│ │ ├── hoststatus.js
│ │ ├── resizeDialog.js
│ │ ├── settingslist.js
│ │ ├── walletmodal.js
│ │ ├── warningbar.js
│ │ └── warningmodal.js
│ │ ├── constants
│ │ └── constants.js
│ │ ├── containers
│ │ ├── announce.js
│ │ ├── body.js
│ │ ├── fileslist.js
│ │ ├── header.js
│ │ ├── resizeDialog.js
│ │ ├── settingslist.js
│ │ └── walletmodal.js
│ │ ├── index.js
│ │ ├── main.js
│ │ ├── reducers
│ │ ├── hosting.js
│ │ ├── index.js
│ │ ├── modal.js
│ │ └── setting.js
│ │ ├── sagas
│ │ └── saga.js
│ │ └── utils
│ │ └── host.js
├── Logs
│ ├── assets
│ │ └── button.png
│ ├── index.html
│ └── js
│ │ ├── actions.js
│ │ ├── components
│ │ ├── app.js
│ │ ├── filtercontrol.js
│ │ ├── filtercontrols.js
│ │ └── logview.js
│ │ ├── constants.js
│ │ ├── containers
│ │ ├── filtercontrols.js
│ │ └── logview.js
│ │ ├── filters.js
│ │ ├── index.js
│ │ ├── logparse.js
│ │ ├── main.js
│ │ ├── reducer.js
│ │ └── utils.js
├── Terminal
│ ├── assets
│ │ ├── button.png
│ │ ├── roboto-mono-100.woff2
│ │ ├── roboto-mono-300.woff2
│ │ └── roboto-mono.woff2
│ ├── css
│ │ ├── roboto-mono.css
│ │ └── style.css
│ ├── index.html
│ └── js
│ │ ├── actions
│ │ └── commandline.js
│ │ ├── components
│ │ ├── app.js
│ │ ├── commandhistorylist.js
│ │ ├── commandinput.js
│ │ ├── commandline.js
│ │ ├── seedprompt.js
│ │ └── walletpasswordprompt.js
│ │ ├── constants
│ │ ├── commandline.js
│ │ └── helper.js
│ │ ├── containers
│ │ ├── commandhistorylist.js
│ │ ├── commandinput.js
│ │ ├── commandline.js
│ │ ├── seedprompt.js
│ │ └── walletpasswordprompt.js
│ │ ├── index.js
│ │ ├── reducers
│ │ ├── commandline.js
│ │ └── index.js
│ │ └── utils
│ │ └── helpers.js
└── Wallet
│ ├── assets
│ └── button.png
│ ├── css
│ └── wallet.css
│ ├── index.html
│ └── js
│ ├── actions
│ ├── error.js
│ └── wallet.js
│ ├── components
│ ├── app.js
│ ├── backupbutton.js
│ ├── backupprompt.js
│ ├── balanceinfo.js
│ ├── changepasswordbutton.js
│ ├── changepassworddialog.js
│ ├── confirmationdialog.js
│ ├── initseedform.js
│ ├── lockbutton.js
│ ├── lockscreen.js
│ ├── newwalletdialog.js
│ ├── newwalletform.js
│ ├── passwordprompt.js
│ ├── receivebutton.js
│ ├── receiveprompt.js
│ ├── recoverbutton.js
│ ├── recoverydialog.js
│ ├── rescandialog.js
│ ├── sendbutton.js
│ ├── sendprompt.js
│ ├── transactionlist.js
│ ├── uninitializedwalletdialog.js
│ └── wallet.js
│ ├── constants
│ ├── error.js
│ └── wallet.js
│ ├── containers
│ ├── backupbutton.js
│ ├── backupprompt.js
│ ├── balanceinfo.js
│ ├── changepasswordbutton.js
│ ├── changepassworddialog.js
│ ├── confirmationdialog.js
│ ├── lockbutton.js
│ ├── lockscreen.js
│ ├── newwalletdialog.js
│ ├── newwalletform.js
│ ├── passwordprompt.js
│ ├── receivebutton.js
│ ├── receiveprompt.js
│ ├── recoverbutton.js
│ ├── recoverydialog.js
│ ├── sendprompt.js
│ ├── transactionlist.js
│ ├── uninitializedwalletdialog.js
│ └── wallet.js
│ ├── index.js
│ ├── main.js
│ ├── reducers
│ ├── index.js
│ ├── newwalletdialog.js
│ ├── passwordprompt.js
│ ├── receiveprompt.js
│ ├── sendprompt.js
│ └── wallet.js
│ └── sagas
│ ├── helpers.js
│ ├── index.js
│ └── wallet.js
├── release.sh
└── test
├── app.js
├── config.unit.js
├── dom.setup.js
├── files
├── filebrowser.component.js
├── filelist.component.js
├── files.sagas.js
├── helpers.js
├── uploadbutton.component.js
├── uploaddialog.component.js
└── usagestats.component.js
├── logs
├── logs.integration.js
├── logs.unit.js
└── testdir
│ ├── consensus
│ └── consensus.log
│ ├── gateway
│ └── gateway.log
│ ├── host
│ ├── host.log
│ └── storagemanager
│ │ └── storagemanager.log
│ ├── miner
│ └── miner.log
│ ├── renter
│ ├── contractor.log
│ ├── hostdb.log
│ └── renter.log
│ ├── siad-output.log
│ └── wallet
│ └── wallet.log
├── pluginapi.unit.js
├── plugins.unit.js
├── wallet.integration.js
└── wallet
├── balanceinfo.component.js
├── lockscreen.component.js
├── passwordprompt.component.js
├── receiveprompt.component.js
├── sendprompt.component.js
├── transactionlist.component.js
└── wallet.component.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "targets": {
7 | "electron": "2.0.2",
8 | "node": 8
9 | },
10 | "useBuiltIns": true
11 | }
12 | ],
13 | "stage-0",
14 | "react"
15 | ],
16 | "plugins": [
17 | "add-module-exports"
18 | ],
19 | "env": {
20 | "production": {
21 | "presets": [
22 | "react-optimize"
23 | ]
24 | },
25 | "development": {
26 | "plugins": [
27 | "transform-class-properties",
28 | "transform-es2015-classes",
29 | "react-hot-loader/babel"
30 | ]
31 | },
32 | "plugins": ["syntax-dynamic-import"]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "parserOptions": {
4 | "sourceType": "module",
5 | "allowImportExportEverywhere": true
6 | },
7 | "env": {
8 | "browser": true,
9 | "node": true
10 | },
11 | "settings": {
12 | "import/resolver": {
13 | "webpack": {
14 | "config": "./internals/webpack/webpack.config.eslint.js"
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated Items
2 | release
3 | node_modules
4 | npm-debug.log
5 | config.json
6 | Sia
7 | doc/Sia-UI
8 | errors.log
9 | dist
10 | sia-testing
11 | profiles
12 | test.json
13 |
14 | #VSCode
15 | .vscode
16 |
17 | # Vim
18 | *.sw?
19 |
20 | # IntelliJ
21 | .idea
22 | *.iml
23 |
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
2 | version: v1.7.1
3 | ignore: {}
4 | # patches apply the minimum changes required to fix a vulnerability
5 | patch:
6 | 'npm:ms:20170412':
7 | - electron > extract-zip > debug > ms:
8 | patched: '2017-09-28T17:53:43.125Z'
9 | 'npm:debug:20170905':
10 | - electron > extract-zip > debug:
11 | patched: '2017-09-28T17:53:43.125Z'
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "8.11.1"
5 |
6 | notifications:
7 | email: false
8 |
9 | sudo: false
10 |
11 | branches:
12 | only:
13 | - master
14 |
15 | before_script:
16 | - "export DISPLAY=:99.0"
17 | - "sh -e /etc/init.d/xvfb start"
18 | - sleep 3
19 |
20 | install:
21 | - wget https://github.com/NebulousLabs/Sia/releases/download/v1.3.3/Sia-v1.3.3-linux-amd64.zip
22 | - unzip Sia-v1.3.3-linux-amd64.zip
23 | - mv Sia-v1.3.3-linux-amd64 Sia
24 | - export PATH=$PATH:`pwd`/Sia/
25 | - npm install
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Nebulous
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [](http://sia.tech/) User Interface
2 |
3 | This project has moved to Gitlab: https://gitlab.com/NebulousLabs/Sia-UI
--------------------------------------------------------------------------------
/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sia-UI
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
41 | Tooltip
42 |
50 |
51 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | import { app } from 'electron'
2 | import Path from 'path'
3 | import loadConfig from './js/mainjs/config.js'
4 | import initWindow from './js/mainjs/initWindow.js'
5 |
6 | // load config.json manager
7 | global.config = loadConfig(Path.join(app.getPath('userData'), 'config.json'))
8 | let mainWindow
9 |
10 | // disable hardware accelerated rendering
11 | app.disableHardwareAcceleration()
12 |
13 | // Fixes Chrome bug to render correct colors:
14 | // https://github.com/electron/electron/issues/10732
15 | app.commandLine.appendSwitch('force-color-profile', 'srgb')
16 |
17 | // Allow only one instance of Sia-UI
18 | const shouldQuit = app.makeSingleInstance(() => {
19 | if (mainWindow) {
20 | if (mainWindow.isMinimized()) {
21 | mainWindow.restore()
22 | }
23 | mainWindow.focus()
24 | }
25 | })
26 |
27 | if (shouldQuit) {
28 | app.quit()
29 | }
30 |
31 | // When Electron loading has finished, start Sia-UI.
32 | app.on('ready', () => {
33 | // Load mainWindow
34 | mainWindow = initWindow(config)
35 | })
36 |
37 | // Quit once all windows have been closed.
38 | app.on('window-all-closed', () => {
39 | app.quit()
40 | })
41 |
42 | // On quit, save the config. There's no need to call siad.stop here, since if
43 | // siad was launched by the UI, it will be a descendant of the UI in the
44 | // process tree and will therefore be killed.
45 | app.on('quit', () => {
46 | config.save()
47 | })
48 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: "{build}"
2 |
3 | platform: x64
4 | environment:
5 | nodejs_version: "6.9.2"
6 |
7 | install:
8 | - ps: wget https://github.com/NebulousLabs/Sia/releases/download/v1.0.3/Sia-v1.0.3-windows-amd64.zip -O Sia-v1.0.3-windows-amd64.zip
9 | - ps: 7z.exe x Sia-v1.0.3-windows-amd64.zip
10 | - rename Sia-v1.0.3-windows-amd64 Sia
11 | - npm install
12 |
13 |
14 | build: off
15 |
--------------------------------------------------------------------------------
/assets/Metropolis-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/Metropolis-Bold.otf
--------------------------------------------------------------------------------
/assets/Metropolis-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/Metropolis-Medium.otf
--------------------------------------------------------------------------------
/assets/Metropolis-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/Metropolis-Regular.ttf
--------------------------------------------------------------------------------
/assets/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/assets/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/icon.icns
--------------------------------------------------------------------------------
/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/icon.ico
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/icon.png
--------------------------------------------------------------------------------
/assets/trayTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/trayTemplate.png
--------------------------------------------------------------------------------
/assets/trayTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/trayTemplate@2x.png
--------------------------------------------------------------------------------
/assets/trayWin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/trayWin.png
--------------------------------------------------------------------------------
/assets/trayWin@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/assets/trayWin@2x.png
--------------------------------------------------------------------------------
/css/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Metropolis';
3 | src: url('Metropolis-Regular.ttf') format('truetype');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'Metropolis';
10 | src: url('Metropolis-Bold.ttf') format('truetype');
11 | font-weight: bold;
12 | font-style: normal;
13 | }
14 |
15 | @font-face {
16 | font-family: 'Metropolis';
17 | src: url('Metropolis-Medium.ttf') format('truetype');
18 | font-weight: 500;
19 | font-style: normal;
20 | }
21 |
--------------------------------------------------------------------------------
/doc/Developers.md:
--------------------------------------------------------------------------------
1 | # Developers
2 |
3 | To get Sia-UI running, ensure you have installed node, and npm. After that,
4 | starting the application is as easy as running: `npm install && npm start`
5 |
6 | ## Resources
7 |
8 | The following documents explains the environment that Sia-UI runs in.
9 |
10 | * [Technologies](Technologies.md)
11 | * [Testing](Testing.md)
12 | * [Structure](Structure.md)
13 |
14 | ## Development
15 |
16 | The following explains some aspects of how I, [Ming
17 | Luo](https://github.com/Mingling94), develop Sia-UI and my general workflow
18 |
19 | * [Development Flow](DevelopmentFlow.md)
20 | * [Plugins](Plugins.md)
21 |
22 |
--------------------------------------------------------------------------------
/doc/DevelopmentFlow.md:
--------------------------------------------------------------------------------
1 | # Development Flow
2 |
3 | ## Packaging & Releasing Sia-UI
4 |
5 | There are some packaging scripts (using electron-packager) in the package.json.
6 |
7 | For them to work, you will need to have release archives of the correct version
8 | in the `release` folder of the Sia package in your GOPATH. To do so, run `make
9 | xc` from the Sia repository followed by `npm run release` from the Sia-UI
10 | repository.
11 |
12 | ## Building Distributables
13 |
14 | Places packaged versions into release/ folder, see the package.json for details.
15 |
16 | * `npm run release`
17 |
18 | ## Other Commands
19 |
20 | Useful commands for development.
21 |
22 | * `npm run clean`
23 | will remove node_modules, your Sia state kept in lib/Sia, and the
24 | configuration settings from config.json.
25 | * `npm run fresh`
26 | will run clean, install, then start to simulate a fresh install run of the UI.
27 | * `npm run debug`
28 | will run the UI with a debug port to aide in inspecting the main process.
29 | * `npm run doc`
30 | will generate documentation about the UI's classes and functions. It's somewhat
31 | messy though.
32 | * `npm run lint`
33 | will output style suggestions for the UI's javascript, including for plugins.
34 |
35 |
--------------------------------------------------------------------------------
/doc/Plugins.md:
--------------------------------------------------------------------------------
1 | # Plugins
2 |
3 | In order to form a more modular codebase, most functionality in the UI is
4 | contained in webpage like structures we call "Plugins".
5 |
6 | ## What is a plugin?
7 |
8 | A plugin, in the context of Sia-UI, is a self-contained add-on that offers
9 | graphical functionality to interact with the Sia-network. We'll develop plugins
10 | we believe would be widely used, but we're also redesigning Sia-UI to enable
11 | third-party developers interested in our project to make their own plugins.
12 |
13 | The structure of a plugin is the exact same as a webpage, with some added
14 | functionality via Node.js & Electron. There are only two hard-rules to a
15 | plugin:
16 |
17 | 1. It must be self-contained in a directory of its name.
18 | 2. It must have an index.html in this directory.
19 |
20 | ## Why plugins?
21 |
22 | We at NebulousLabs are all about decentralization... of everything! Thus we are
23 | redesigning our GUI desktop application with that in mind. We want the
24 | community interested in the Sia network to be able to:
25 |
26 | 1. Use Sia-UI in the way they want with only the plugins they care about instead
27 | of using our rigid set of tools.
28 | 2. Be able to design and implement their own plugins.
29 | 3. Customize their own UI experience simply without obfuscating menubars.
30 |
31 |
--------------------------------------------------------------------------------
/doc/Technologies.md:
--------------------------------------------------------------------------------
1 | # Technologies
2 |
3 | We use three major tools in this application and they follow this hierarchy:
4 | Javascript -> Node/NPM -> Electron.
5 |
6 | ### Javascript
7 |
8 | This should be familiar to most webdevs. We mostly adhere to [certain style
9 | conventions](http://javascript.crockford.com/code.html)
10 |
11 | ### [NPM](https://www.npmjs.com/)
12 |
13 | Node Package Manager gives easy package management. We use NPM to manage our
14 | dependencies (such as Electron) and our development dependencies (such as
15 | JSHint or JSDoc). NPM also handles our command scripting. To understand this
16 | and view available npm scripts, see the [package.json](../package.json) file
17 | and [this npm documentation](https://docs.npmjs.com/misc/scripts) on how npm
18 | commands work.
19 |
20 | A shortcut explanation of the npm commands you most commonly run from terminal
21 |
22 | * `npm install` installs node packages from the npm registry listed under
23 | `devDependencies` and `dependencies` in the package.json.
24 | * `npm start` runs the app
25 | * `npm test` lints the app with [JSHint](http://jshint.com/about/) and then
26 | runs some test scripts in the `test` folder
27 | * There's a slew of other custom npm commands under `scripts` in the
28 | package.json
29 |
30 | OPTIONAL: [A useful guide about using NPM as a build
31 | tool](http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/)
32 |
33 | ### [Electron](http://electron.atom.io/)
34 |
35 | It's the core set of libararies that power the Atom text editor and is useful
36 | for creating cross-platform desktop applications.
37 |
38 | Making this a desktop application instead of a webapp gives us libraries to
39 | access filepaths and other OS resources (via Node libraries) that a webapp
40 | would be limited from.
41 |
42 | The code does not have to adhere to compatibility for all browsers (looking
43 | at you, Internet Explorer) because electron is run on chromium. This extends
44 | from JS to CSS (with the use of -webkit- rules when applicable).
45 |
46 | We do occasionally use ES2015 and ES2016 conventions and syntax in the code
47 | base with no worries for the same reason
48 |
49 |
--------------------------------------------------------------------------------
/doc/Testing.md:
--------------------------------------------------------------------------------
1 | # Testing
2 |
3 | Testing Sia-UI is fairly immature and needs many more tests written to aide
4 | development.
5 |
6 | ## Technologies
7 | The testing environment has a lot of node modules that aren't used elsewhere in
8 | the app. The open source community really shines here in that the testing
9 | environment for our app, though using a somewhat obscure framework like
10 | electron, has a lot of node packages that cater to its testing needs.
11 |
12 | ### [WebDriverIO](http://webdriver.io/)
13 | The primary technology used for testing is since it seems to have [some level
14 | of
15 | support](https://github.com/atom/electron/blob/master/docs/tutorial/using-selenium-and-webdriver.md#setting-up-with-webdriverio)
16 | for an electron framework. It is a module of node.js bindings for the older
17 | Selenium WebDriver.
18 |
19 | #### [spectron](https://github.com/kevinsawicki/spectron)
20 | You'll notice WebDriverIO isn't actually a devDependency in the `package.json`,
21 | that's because there's a lot of heavy lifting to make WebDriverIO cooperate
22 | with electron that is already done in this package. This module is super
23 | useful and a great source of examples for testing and working with Javascript
24 | Promises in general.
25 |
26 | ### [Mocha](https://mochajs.org/)
27 | This is a popular choice for running series of tests within a node environment.
28 |
29 | #### [Electron-mocha](https://github.com/jprichardson/electron-mocha)
30 | This module enables electron modules to be `require()`ed in js test files ran
31 | by mocha.
32 |
33 | ### [Chai](http://chaijs.com/)
34 | This module allows for clean testing syntax in the form of
35 | behavior-driven-development [(BDD)](http://chaijs.com/api/bdd/).
36 |
37 | #### [Chai As Promised](https://github.com/domenic/chai-as-promised)
38 | This builds off of Chai and "pairs really nicely with" WebDriverIO as detailed
39 | in [spectron's
40 | README](https://github.com/kevinsawicki/spectron#with-chai-as-promised)
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/doc/assets/basic-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/basic-overview.png
--------------------------------------------------------------------------------
/doc/assets/files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/files.png
--------------------------------------------------------------------------------
/doc/assets/github-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/github-plugin.png
--------------------------------------------------------------------------------
/doc/assets/sidebar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/sidebar.png
--------------------------------------------------------------------------------
/doc/assets/styled-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/styled-overview.png
--------------------------------------------------------------------------------
/doc/assets/update-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/update-button.png
--------------------------------------------------------------------------------
/doc/assets/wallet-notifications.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/wallet-notifications.png
--------------------------------------------------------------------------------
/doc/assets/wallet-tooltip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/wallet-tooltip.png
--------------------------------------------------------------------------------
/doc/assets/working-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/doc/assets/working-overview.png
--------------------------------------------------------------------------------
/doc/spec/PluginAPI.md:
--------------------------------------------------------------------------------
1 | # Sia-UI Plugin API Specification
2 |
3 | ## Introduction
4 |
5 | This specification outlines the functionality that Sia-UI's plugin API exposes to developers.
6 |
7 | ## Functionality
8 |
9 | The Sia-UI Plugin api exposes a simple interface for making API calls to siad, creating file dialogs, displaying notifications, and displaying error messages. This interface is assigned to the `window` object of each plugin, and has the following functions:
10 |
11 | - `SiaAPI.call()`, a wrapper to the configured `sia.js`'s `.apiCall` function.
12 | - `SiaAPI.config`, the current Sia-UI config.
13 | - `SiaAPI.hastingsToSiacoins`, conversion function from hastings to siacoins. Returns a `BigNumber` and takes either a `BigNumber` or `string`.
14 | - `SiaAPI.siacoinsToHastings`, conversion function from siacoins to hastings.
15 | - `SiaAPI.openFile(options)`, a wrapper which calls Electron.dialog.showOpenDialog with `options`.
16 | - `SiaAPI.saveFile(options)`, a wrapper which calls Electron.dialog.showSaveDialog with `options`.
17 | - `SiaAPI.showMessage(options)`, a wrapper which calls Electron.showMessageBox with `options`.
18 | - `SiaAPI.showerror(options)`, a wrapper which calls Electron.showErrorBox with `options`.
19 |
--------------------------------------------------------------------------------
/doc/spec/Siad.md:
--------------------------------------------------------------------------------
1 | # Sia-UI Siad lifecycle specification
2 |
3 | ## Introduction
4 |
5 | The purpose of this spec is to outline the desired behaviour of Sia-UI as it relates to starting, stopping, or connecting to an existing Siad.
6 |
7 | ## Desired Functionality
8 |
9 | - Sia-UI should check for the existence of a running daemon on launch, by calling `/daemon/version` using the UI's current config.
10 | If the daemon isn't running, Sia-UI should launch a new siad instance, using the bundled siad binary. If a bundled binary cannot be found, prompt the user for the location of their `siad`. Siad's lifetime should be bound to Sia-UI, meaning that `/daemon/stop` should be called when Sia-UI is exited.
11 | - Alternatively, if an instance of `siad` is found to be running when Sia-UI starts up, Sia-UI should not quit the daemon when it is exited.
12 |
13 | This behaviour can be implemented without any major changes to the codebase by leveraging the existing `detached` flag.
14 |
15 | ## Considerations
16 |
17 | - Calling `/daemon/version` using the UI's config does not actually tell you whether or not there is an active `siad` running on the host, since a different `siad` instance could be running using a bindaddr different than the one specified in `config`.
18 |
--------------------------------------------------------------------------------
/doc/spec/Startup.md:
--------------------------------------------------------------------------------
1 | # Sia-UI Startup Behaviour Specification
2 |
3 | ## Introduction
4 |
5 | This specification outlines the desired behaviour of Sia-UI when it first launches.
6 |
7 | ## Desired Functionality
8 |
9 | ### Main Process
10 | - Initialize electron's main window. Register applicable event listeners (`close`, `closed`, etc), and load the renderer's entrypoint `index.html`.
11 |
12 | ### Renderer Process
13 | - Display a loading screen until communication with an active `siad` has been established.
14 | - Disable the loading screen and initialize the plugin system.
15 |
--------------------------------------------------------------------------------
/internals/scripts/CheckNodeEnv.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk'
2 |
3 | export default function CheckNodeEnv (expectedEnv) {
4 | if (!expectedEnv) {
5 | throw new Error('"expectedEnv" not set')
6 | }
7 |
8 | if (process.env.NODE_ENV !== expectedEnv) {
9 | console.log(
10 | chalk.whiteBright.bgRed.bold(
11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
12 | )
13 | )
14 | process.exit(2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/internals/scripts/CheckPortInUse.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk'
2 | import detectPort from 'detect-port'
3 |
4 | ;(function CheckPortInUse () {
5 | const port = process.env.PORT || '1212'
6 |
7 | detectPort(port, (err, availablePort) => {
8 | if (port !== String(availablePort)) {
9 | throw new Error(
10 | chalk.whiteBright.bgRed.bold(
11 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm run dev`
12 | )
13 | )
14 | } else {
15 | process.exit(0)
16 | }
17 | })
18 | })()
19 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import webpack from 'webpack'
3 | const version = require('../../package.json').version
4 |
5 | export default {
6 | module: {
7 | rules: [
8 | {
9 | test: /\.jsx?$/,
10 | exclude: /node_modules/,
11 | use: {
12 | loader: 'babel-loader',
13 | options: {
14 | cacheDirectory: true
15 | }
16 | }
17 | }
18 | ]
19 | },
20 |
21 | output: {
22 | path: path.resolve('./dist'),
23 | // https://github.com/webpack/webpack/issues/1114
24 | libraryTarget: 'commonjs2'
25 | },
26 |
27 | resolve: {
28 | extensions: ['.js', '.jsx', '.json'],
29 | modules: [path.join(__dirname, 'app'), 'node_modules']
30 | },
31 | plugins: [
32 | new webpack.EnvironmentPlugin({
33 | NODE_ENV: 'production'
34 | }),
35 |
36 | new webpack.DefinePlugin({
37 | VERSION: JSON.stringify(version)
38 | }),
39 |
40 | new webpack.NamedModulesPlugin()
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.config.eslint.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 |
3 | module.exports = require('./webpack.config.renderer.dev')
4 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.config.main.prod.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Webpack config for production electron main process
3 | */
4 |
5 | import webpack from 'webpack'
6 | import merge from 'webpack-merge'
7 | import UglifyJSPlugin from 'uglifyjs-webpack-plugin'
8 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
9 | import baseConfig from './webpack.config.base'
10 | import CheckNodeEnv from '../scripts/CheckNodeEnv'
11 | import path from 'path'
12 |
13 | CheckNodeEnv('production')
14 |
15 | export default merge.smart(baseConfig, {
16 | devtool: 'none',
17 |
18 | mode: 'production',
19 |
20 | target: 'electron-main',
21 |
22 | entry: './app.js',
23 |
24 | output: {
25 | path: path.resolve('./dist'),
26 | filename: './main.js'
27 | },
28 |
29 | plugins: [
30 | new UglifyJSPlugin({
31 | parallel: true,
32 | sourceMap: true
33 | }),
34 |
35 | new BundleAnalyzerPlugin({
36 | analyzerMode: process.env.OPEN_ANALYZER === 'true'
37 | ? 'server'
38 | : 'disabled',
39 | openAnalyzer: process.env.OPEN_ANALYZER === 'true'
40 | }),
41 |
42 | /**
43 | * Create global constants which can be configured at compile time.
44 | *
45 | * Useful for allowing different behaviour between development builds and
46 | * release builds
47 | *
48 | * NODE_ENV should be production so that modules do not perform certain
49 | * development checks
50 | */
51 | new webpack.EnvironmentPlugin({
52 | NODE_ENV: 'production',
53 | DEBUG_PROD: 'false'
54 | })
55 | ],
56 |
57 | /**
58 | * Disables webpack processing of __dirname and __filename.
59 | * If you run the bundle in node.js it falls back to these values of node.js.
60 | * https://github.com/webpack/webpack/issues/2010
61 | */
62 | node: {
63 | __dirname: false,
64 | __filename: false
65 | }
66 | })
67 |
--------------------------------------------------------------------------------
/js/mainjs/appMenu.js:
--------------------------------------------------------------------------------
1 | import { Menu } from 'electron'
2 |
3 | export default function (window) {
4 | // Template for OSX app menu commands
5 | // Selectors call the main app's NSApplication methods.
6 | const menutemplate = [
7 | {
8 | label: 'Sia',
9 | submenu: [
10 | { label: 'About Sia', selector: 'orderFrontStandardAboutPanel:' },
11 | { type: 'separator' },
12 | { label: 'Hide Sia', accelerator: 'CmdOrCtrl+H', selector: 'hide:' },
13 | { type: 'separator' },
14 | {
15 | label: 'Quit',
16 | accelerator: 'CmdOrCtrl+Q',
17 | click: () => window.webContents.send('quit')
18 | }
19 | ]
20 | },
21 | {
22 | label: 'Edit',
23 | submenu: [
24 | { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
25 | { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
26 | { type: 'separator' },
27 | { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
28 | { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
29 | { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
30 | {
31 | label: 'Select All',
32 | accelerator: 'CmdOrCtrl+A',
33 | selector: 'selectAll:'
34 | }
35 | ]
36 | }
37 | ]
38 |
39 | return Menu.buildFromTemplate(menutemplate)
40 | }
41 |
--------------------------------------------------------------------------------
/js/mainjs/trayMenu.js:
--------------------------------------------------------------------------------
1 | import { Menu } from 'electron'
2 |
3 | export default function (window) {
4 | // Template for Sia-UI tray menu.
5 | const menutemplate = [
6 | {
7 | label: 'Show Sia',
8 | click: () => window.show()
9 | },
10 | { type: 'separator' },
11 | {
12 | label: 'Hide Sia',
13 | click: () => window.hide()
14 | },
15 | { type: 'separator' },
16 | {
17 | label: 'Quit Sia',
18 | click: () => {
19 | window.webContents.send('quit')
20 | }
21 | }
22 | ]
23 |
24 | return Menu.buildFromTemplate(menutemplate)
25 | }
26 |
--------------------------------------------------------------------------------
/js/rendererjs/disabledplugin.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { shell } from 'electron'
4 |
5 | const containerStyle = {
6 | display: 'flex',
7 | alignItems: 'center',
8 | justifyContent: 'center',
9 | flexDirection: 'column',
10 | backgroundColor: '#C6C6C6',
11 | width: '100%',
12 | height: '100%'
13 | }
14 |
15 | const errorLogStyle = {
16 | height: '300px',
17 | width: '80%',
18 | overflow: 'auto',
19 | marginBottom: '15px'
20 | }
21 |
22 | const reportStyle = {
23 | color: 'blue',
24 | cursor: 'pointer'
25 | }
26 |
27 | const handleReport = () => {
28 | shell.openExternal('https://github.com/NebulousLabs/Sia/issues')
29 | }
30 |
31 | const DisabledPlugin = ({ errorMsg, startSiad }) => (
32 |
33 |
34 | Siad has exited unexpectedly. Please submit a bug report including the
35 | error log
36 |
37 | here.
38 |
39 |
40 |
Error Log:
41 |
44 |
Start Siad
45 |
46 | )
47 |
48 | DisabledPlugin.propTypes = {
49 | errorMsg: PropTypes.string.isRequired,
50 | startSiad: PropTypes.func.isRequired
51 | }
52 |
53 | export default DisabledPlugin
54 |
--------------------------------------------------------------------------------
/plugins/About/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/About/assets/button.png
--------------------------------------------------------------------------------
/plugins/About/css/about.css:
--------------------------------------------------------------------------------
1 | /* Style Guide:
2 | * Transparent: 70% Opacity
3 | *
4 | * White: #FFFFFF
5 | * Grey-White: #F5F5F5
6 | * Faint-Grey: #ECECEC
7 | * Light-Grey: #DDDDDD
8 | * Grey: #C5C5C5
9 | * Grey-Black: #4A4A4A
10 | * Black: #000000
11 | */
12 | .about {
13 | color: #c5c5c5;
14 | margin: 20px 20px;
15 | padding-left: 10px;
16 | padding-bottom: 10px;
17 | border-bottom: 1px solid #e0e0e0;
18 | }
19 | .about .version {
20 | display: inline-block;
21 | color: #4a4a4a;
22 | }
23 | button {
24 | color: #4a4a4a;
25 | }
26 |
--------------------------------------------------------------------------------
/plugins/About/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | About
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 | Sia UI version:
25 |
26 |
27 |
28 |
29 |
30 | Sia version:
31 |
32 |
33 |
34 |
35 |
36 | Check for Update
37 |
38 |
39 | Show Sia Data
40 |
41 |
42 | You are on the latest version.
43 |
44 |
45 | A new version is available:
46 |
47 |
52 |
53 |
54 |
55 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/plugins/About/js/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import { platform } from 'os'
3 | import { shell } from 'electron'
4 |
5 | // If dev enable window reload
6 | if (process.env.NODE_ENV === 'development') {
7 | require('electron-css-reload')()
8 | }
9 |
10 | // Set UI version via package.json.
11 | document.getElementById('uiversion').innerHTML = VERSION
12 |
13 | // Set daemon version via API call.
14 | SiaAPI.call('/daemon/version', (err, result) => {
15 | if (err) {
16 | SiaAPI.showError('Error', err.toString())
17 | } else {
18 | document.getElementById('siaversion').innerHTML = result.version
19 | }
20 | })
21 |
22 | function genDownloadLink (version, thePlatform) {
23 | let plat = thePlatform
24 | if (plat === 'darwin') {
25 | plat = 'osx'
26 | }
27 |
28 | return `https://github.com/NebulousLabs/Sia-UI/releases/download/v${version}/Sia-UI-v${version}-${plat}-x64.zip`
29 | }
30 |
31 | function updateCheck () {
32 | SiaAPI.call('/daemon/update', (err, result) => {
33 | if (err) {
34 | SiaAPI.showError('Error', err.toString())
35 | } else if (result.available) {
36 | document.getElementById('newversion').innerHTML = result.version
37 | document.getElementById('downloadlink').href = genDownloadLink(
38 | result.version,
39 | platform()
40 | )
41 | document.getElementById('nonew').style.display = 'none'
42 | document.getElementById('yesnew').style.display = 'block'
43 | } else {
44 | document.getElementById('nonew').style.display = 'block'
45 | document.getElementById('yesnew').style.display = 'none'
46 | }
47 | })
48 | }
49 |
50 | document.getElementById('updatecheck').onclick = updateCheck
51 | document.getElementById('datadiropen').onclick = () => {
52 | shell.showItemInFolder(SiaAPI.config.siad.datadir)
53 | }
54 |
--------------------------------------------------------------------------------
/plugins/Files/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Files/assets/button.png
--------------------------------------------------------------------------------
/plugins/Files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Files
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/addfolderbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const AddFolderButton = ({ actions }) => {
4 | const handleClick = () => actions.showAddFolderDialog()
5 | return (
6 |
7 |
8 | New Folder
9 |
10 | )
11 | }
12 |
13 | export default AddFolderButton
14 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/addfolderdialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const AddFolderDialog = ({ actions }) => {
4 | const onConfirmClick = e => {
5 | e.preventDefault()
6 | actions.addFolder(e.target.name.value)
7 | actions.hideAddFolderDialog()
8 | }
9 | const onCancelClick = () => actions.hideAddFolderDialog()
10 | return (
11 |
12 |
13 |
Enter a name for the new folder:
14 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default AddFolderDialog
31 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/allowanceconfirmation.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const ConfirmationDialog = ({ allowance, onConfirmClick, onCancelClick }) => {
5 | const confirmationStyle = {
6 | padding: '40px 40px 40px 40px',
7 | display: 'flex',
8 | flexDirection: 'column',
9 | alignItems: 'center',
10 | justifyContent: 'space-around',
11 | backgroundColor: '#ececec',
12 | width: '50%',
13 | height: '400px'
14 | }
15 | const buttonStyle = {
16 | marginLeft: '5px',
17 | marginRight: '5px'
18 | }
19 | const confirmationTextStyle = {
20 | marginBottom: '20px',
21 | fontSize: '24px'
22 | }
23 | return (
24 |
25 |
26 | Please confirm that you would like to set aside {allowance} SC for
27 | storage on the Sia network.
28 |
29 |
30 |
31 | Confirm
32 |
33 |
34 | Cancel
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | ConfirmationDialog.propTypes = {
42 | allowance: PropTypes.string.isRequired,
43 | onConfirmClick: PropTypes.func.isRequired,
44 | onCancelClick: PropTypes.func.isRequired
45 | }
46 |
47 | export default ConfirmationDialog
48 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/app.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import FileBrowser from '../containers/filebrowser.js'
4 | import AllowanceDialog from '../containers/allowancedialog.js'
5 |
6 | const FilesApp = ({ showAllowanceDialog }) => (
7 |
8 | {showAllowanceDialog ?
: null}
9 |
10 |
11 | )
12 |
13 | FilesApp.propTypes = {
14 | showAllowanceDialog: PropTypes.bool.isRequired
15 | }
16 |
17 | export default FilesApp
18 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/contractorstatus.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const ContractorStatus = ({ settingAllowance, contractCount }) => (
5 |
6 | {settingAllowance ? (
7 |
8 |
9 | Forming Contracts...
10 |
11 | ) : (
12 |
{contractCount} contracts
13 | )}
14 |
15 | )
16 |
17 | ContractorStatus.propTypes = {
18 | settingAllowance: PropTypes.bool.isRequired,
19 | contractCount: PropTypes.number.isRequired
20 | }
21 |
22 | export default ContractorStatus
23 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/deletedialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { List } from 'immutable'
4 |
5 | const DeleteDialog = ({ files, actions }) => {
6 | const onYesClick = () => {
7 | files.map(actions.deleteFile)
8 | actions.hideDeleteDialog()
9 | }
10 | const onNoClick = () => actions.hideDeleteDialog()
11 | return (
12 |
13 |
14 |
Confirm Deletion
15 |
16 | Are you sure you want to delete {files.size}
17 | {files.size === 1 ? ' file' : ' files'}
18 |
19 |
20 | Yes
21 | No
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | DeleteDialog.propTypes = {
29 | files: PropTypes.instanceOf(List).isRequired
30 | }
31 |
32 | export default DeleteDialog
33 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/directoryinfobar.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const colorBackDisabled = '#C5C5C5'
5 | const colorBackEnabled = '#00CBA0'
6 |
7 | const DirectoryInfoBar = ({
8 | path,
9 | nfiles,
10 | onBackClick,
11 | setDragFolderTarget
12 | }) => {
13 | const backButtonStyle = {
14 | color: (() => {
15 | if (path === '') {
16 | return colorBackDisabled
17 | }
18 | return colorBackEnabled
19 | })()
20 | }
21 | // handle file drag onto the info bar: move the file into the parent
22 | // directory
23 | const handleDragOver = () => {
24 | setDragFolderTarget('../')
25 | }
26 | return (
27 |
28 |
33 |
34 | Back
35 |
36 |
37 | {path}
38 |
39 | {nfiles} {nfiles === 1 ? 'file' : 'files'}
40 |
41 |
42 |
43 | )
44 | }
45 |
46 | DirectoryInfoBar.propTypes = {
47 | path: PropTypes.string.isRequired,
48 | nfiles: PropTypes.number.isRequired,
49 | onBackClick: PropTypes.func.isRequired,
50 | setDragFolderTarget: PropTypes.func.isRequired
51 | }
52 |
53 | export default DirectoryInfoBar
54 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/downloadlist.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import TransferList from './transferlist.js'
4 | import { List } from 'immutable'
5 |
6 | const DownloadList = ({ downloads, onDownloadClick, onClearClick }) => (
7 |
8 |
Downloads
9 |
10 |
11 | Clear Downloads
12 |
13 |
14 | )
15 |
16 | DownloadList.propTypes = {
17 | downloads: PropTypes.instanceOf(List).isRequired,
18 | onDownloadClick: PropTypes.func,
19 | onClearClick: PropTypes.func
20 | }
21 |
22 | export default DownloadList
23 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/dragoverlay.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const DragOverlay = () => (
4 |
5 |
6 |
7 | Drag to Upload
8 |
9 |
10 | )
11 |
12 | export default DragOverlay
13 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/filecontrols.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { Set } from 'immutable'
4 | import Path from 'path'
5 |
6 | const FileControls = ({ files, actions }) => {
7 | const onDownloadClick = () => {
8 | const downloadpath = SiaAPI.openFile({
9 | title: 'Where should we download?',
10 | properties: ['openDirectory', 'createDirectories']
11 | })
12 | if (downloadpath.length === 0) {
13 | // No files selected, nop
14 | return
15 | }
16 | files.forEach(async file => {
17 | actions.downloadFile(
18 | file,
19 | Path.join(downloadpath[0], Path.basename(file.siapath))
20 | )
21 | await new Promise(resolve => setTimeout(resolve, 300))
22 | })
23 | }
24 | const onDeleteClick = () => {
25 | actions.showDeleteDialog(files.toList())
26 | }
27 | const onRenameClick = () => {
28 | actions.showRenameDialog(files.first())
29 | }
30 | return (
31 |
32 | {files.size} {files.size === 1 ? ' item' : ' items'} selected
33 |
34 |
35 |
36 | {files.size === 1 ? (
37 |
38 |
39 |
40 | ) : null}
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | FileControls.propTypes = {
49 | files: PropTypes.instanceOf(Set).isRequired
50 | }
51 |
52 | export default FileControls
53 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/filetransfers.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { List } from 'immutable'
4 | import UploadList from './uploadlist.js'
5 | import DownloadList from './downloadlist.js'
6 | import { shell } from 'electron'
7 |
8 | const FileTransfers = ({ uploads, downloads, actions }) => {
9 | const onCloseClick = () => actions.hideFileTransfers()
10 | const onDownloadClick = download => () =>
11 | shell.showItemInFolder(download.destination)
12 | const onDownloadsClearClick = () => {
13 | actions.clearDownloads()
14 | actions.getDownloads()
15 | }
16 | return (
17 |
18 |
19 |
20 |
21 | {downloads.size === 0 && uploads.size === 0 ? (
22 |
No file transfers in progress.
23 | ) : null}
24 | {downloads.size > 0 ? (
25 |
30 | ) : null}
31 | {uploads.size > 0 ?
: null}
32 |
33 | )
34 | }
35 |
36 | FileTransfers.propTypes = {
37 | uploads: PropTypes.instanceOf(List).isRequired,
38 | downloads: PropTypes.instanceOf(List).isRequired
39 | }
40 |
41 | export default FileTransfers
42 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/progressbar.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const ProgressBar = ({ progress }) => {
5 | const style = {
6 | width: progress.toString() + '%',
7 | height: '100%',
8 | transition: 'width 200ms',
9 | backgroundColor: '#46CF87'
10 | }
11 | return (
12 |
15 | )
16 | }
17 |
18 | ProgressBar.propTypes = {
19 | progress: PropTypes.number.isRequired
20 | }
21 |
22 | export default ProgressBar
23 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/redundancystatus.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const colorNotAvailable = '#FF8080'
5 | const colorGoodRedundancy = '#20EE82'
6 | const colorNegativeRedundancy = '#b7afaf'
7 |
8 | const RedundancyStatus = ({ available, redundancy, uploadprogress }) => {
9 | const indicatorStyle = {
10 | opacity: (() => {
11 | if (!available || redundancy < 1.0) {
12 | return 1
13 | }
14 | if (uploadprogress > 100) {
15 | return 1
16 | }
17 | return uploadprogress / 100
18 | })(),
19 | color: (() => {
20 | if (redundancy < 0) {
21 | return colorNegativeRedundancy
22 | }
23 | if (!available || redundancy < 1.0) {
24 | return colorNotAvailable
25 | }
26 | return colorGoodRedundancy
27 | })()
28 | }
29 | return (
30 |
31 |
32 |
33 | {redundancy > 0 ? redundancy + 'x' : '--'}
34 |
35 |
36 | )
37 | }
38 |
39 | RedundancyStatus.propTypes = {
40 | available: PropTypes.bool.isRequired,
41 | redundancy: PropTypes.number.isRequired,
42 | uploadprogress: PropTypes.number.isRequired
43 | }
44 |
45 | export default RedundancyStatus
46 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/renamedialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import Path from 'path'
4 |
5 | const RenameDialog = ({ file, actions }) => {
6 | const onYesClick = e => {
7 | e.preventDefault()
8 | actions.renameFile(
9 | file,
10 | Path.posix.join(Path.posix.dirname(file.siapath), e.target.newname.value)
11 | )
12 | }
13 | const onNoClick = () => actions.hideRenameDialog()
14 | return (
15 |
16 |
17 |
18 | Enter a new name for {Path.basename(file.siapath)}:
19 |
20 |
37 |
38 |
39 | )
40 | }
41 |
42 | RenameDialog.propTypes = {
43 | file: PropTypes.object.isRequired
44 | }
45 |
46 | export default RenameDialog
47 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/searchbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const SearchButton = ({ path, actions }) => {
4 | const handleClick = () => {
5 | actions.toggleSearchField()
6 | actions.setSearchText('', path)
7 | }
8 | return (
9 |
10 |
11 | Search Files
12 |
13 | )
14 | }
15 |
16 | export default SearchButton
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/searchfield.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const SearchField = ({ searchText, path, actions }) => {
5 | const onSearchChange = e => actions.setSearchText(e.target.value, path)
6 | return (
7 |
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | SearchField.propTypes = {
15 | searchText: PropTypes.string.isRequired,
16 | path: PropTypes.string.isRequired
17 | }
18 | export default SearchField
19 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/setallowancebutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const SetAllowanceButton = ({ actions }) => {
4 | const handleClick = () => actions.showAllowanceDialog()
5 | return (
6 |
7 |
8 | Create Allowance
9 |
10 | )
11 | }
12 |
13 | export default SetAllowanceButton
14 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/transfer.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import ProgressBar from './progressbar.js'
4 |
5 | const Transfer = ({ name, progress, status, speed, onClick }) => {
6 | const statusText = status === 'Downloading' ? status + ' - ' + speed : status
7 | return (
8 |
9 |
10 |
{name}
11 |
12 |
{statusText}
13 |
14 |
15 | )
16 | }
17 |
18 | Transfer.propTypes = {
19 | name: PropTypes.string.isRequired,
20 | progress: PropTypes.number.isRequired,
21 | status: PropTypes.string.isRequired,
22 | onClick: PropTypes.func.isRequired
23 | }
24 |
25 | export default Transfer
26 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/transferlist.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { List } from 'immutable'
4 | import Transfer from './transfer.js'
5 |
6 | const defaultTransferClick = () => () => {}
7 |
8 | const TransferList = ({
9 | transfers,
10 | onTransferClick = defaultTransferClick
11 | }) => {
12 | const transferComponents = transfers.map((transfer, key) => (
13 |
21 | ))
22 | return
23 | }
24 |
25 | TransferList.propTypes = {
26 | transfers: PropTypes.instanceOf(List).isRequired,
27 | onTransferClick: PropTypes.func
28 | }
29 |
30 | export default TransferList
31 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/transfersbutton.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const FileTransfersButton = ({ unread, actions }) => {
5 | const onTransfersClick = () => actions.toggleFileTransfers()
6 | return (
7 |
8 |
9 | {unread > 0 ? (
10 |
11 | {unread > 10 ? '10+' : unread}
12 |
13 | ) : null}
14 | File Transfers
15 |
16 | )
17 | }
18 |
19 | FileTransfersButton.propTypes = {
20 | unread: PropTypes.number.isRequired
21 | }
22 |
23 | export default FileTransfersButton
24 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/unlockwarning.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const UnlockWarning = ({ onClick }) => (
5 |
6 |
7 | Your wallet must be unlocked and synchronized to buy storage.
8 |
9 |
10 |
11 | OK
12 |
13 |
14 |
15 | )
16 |
17 | UnlockWarning.propTypes = {
18 | onClick: PropTypes.func.isRequired
19 | }
20 |
21 | export default UnlockWarning
22 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/uploadbutton.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const minimumContracts = 14
5 |
6 | const UploadButton = ({ contracts = minimumContracts, actions }) => {
7 | const onUploadClick = type => () => {
8 | if (contracts < minimumContracts) {
9 | SiaAPI.showError({
10 | title: 'Sia-UI files error',
11 | content:
12 | 'Not enough contracts to upload. You must buy storage before uploading, or wait for contracts to form.'
13 | })
14 | return
15 | }
16 | let dialogProperties
17 | if (type === 'folder') {
18 | dialogProperties = ['openDirectory']
19 | } else if (type === 'file') {
20 | dialogProperties = ['openFile', 'multiSelections']
21 | }
22 | const filepaths = SiaAPI.openFile({
23 | title: 'Choose a ' + type + ' to upload',
24 | properties: dialogProperties
25 | })
26 | if (filepaths) {
27 | actions.showUploadDialog(filepaths)
28 | }
29 | }
30 | return (
31 |
32 |
33 |
34 | Upload Files
35 |
36 |
37 |
38 | Upload Folder
39 |
40 |
41 | )
42 | }
43 |
44 | UploadButton.propTypes = {
45 | contracts: PropTypes.number
46 | }
47 |
48 | export default UploadButton
49 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/uploaddialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import fs from 'graceful-fs'
4 |
5 | const UploadDialog = ({ source, path, actions }) => {
6 | const onUploadClick = () => {
7 | source.forEach(file => {
8 | if (fs.statSync(file).isDirectory()) {
9 | actions.uploadFolder(path, file)
10 | } else {
11 | actions.uploadFile(path, file)
12 | }
13 | })
14 | actions.hideUploadDialog()
15 | }
16 | const onCancelClick = () => actions.hideUploadDialog()
17 | return (
18 |
19 |
20 |
Confirm Upload
21 |
22 | Would you like to upload {source.length}{' '}
23 | {source.length === 1 ? 'item' : 'items'}?
24 |
25 |
26 | Upload
27 | Cancel
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | UploadDialog.propTypes = {
35 | source: PropTypes.array.isRequired,
36 | path: PropTypes.string.isRequired
37 | }
38 |
39 | export default UploadDialog
40 |
--------------------------------------------------------------------------------
/plugins/Files/js/components/uploadlist.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import TransferList from './transferlist.js'
4 | import { List } from 'immutable'
5 |
6 | const UploadList = ({ uploads, onUploadClick }) => (
7 |
8 |
Uploads
9 |
10 |
11 | )
12 |
13 | UploadList.propTypes = {
14 | uploads: PropTypes.instanceOf(List).isRequired,
15 | onUploadClick: PropTypes.func
16 | }
17 |
18 | export default UploadList
19 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/addfolderbutton.js:
--------------------------------------------------------------------------------
1 | import AddFolderButtonView from '../components/addfolderbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showAddFolderDialog } from '../actions/files.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showAddFolderDialog }, dispatch)
9 | })
10 |
11 | const AddFolderButton = connect(mapStateToProps, mapDispatchToProps)(
12 | AddFolderButtonView
13 | )
14 | export default AddFolderButton
15 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/addfolderdialog.js:
--------------------------------------------------------------------------------
1 | import AddFolderDialogView from '../components/addfolderdialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { hideAddFolderDialog, addFolder } from '../actions/files.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ hideAddFolderDialog, addFolder }, dispatch)
9 | })
10 |
11 | const AddFolderDialog = connect(mapStateToProps, mapDispatchToProps)(
12 | AddFolderDialogView
13 | )
14 | export default AddFolderDialog
15 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/allowancedialog.js:
--------------------------------------------------------------------------------
1 | import AllowanceDialogView from '../components/allowancedialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | showAllowanceConfirmation,
6 | hideAllowanceConfirmation,
7 | closeAllowanceDialog,
8 | setAllowance,
9 | setFeeEstimate,
10 | getStorageEstimate
11 | } from '../actions/files.js'
12 |
13 | const mapStateToProps = state => ({
14 | unlocked: state.wallet.get('unlocked'),
15 | synced: state.wallet.get('synced'),
16 | storageEstimate: state.allowancedialog.get('storageEstimate'),
17 | feeEstimate: state.allowancedialog.get('feeEstimate'),
18 | confirmationAllowance: state.allowancedialog.get('confirmationAllowance'),
19 | confirming: state.allowancedialog.get('confirming')
20 | })
21 | const mapDispatchToProps = dispatch => ({
22 | actions: bindActionCreators(
23 | {
24 | getStorageEstimate,
25 | setFeeEstimate,
26 | showAllowanceConfirmation,
27 | setAllowance,
28 | hideAllowanceConfirmation,
29 | closeAllowanceDialog
30 | },
31 | dispatch
32 | )
33 | })
34 |
35 | const AllowanceDialog = connect(mapStateToProps, mapDispatchToProps)(
36 | AllowanceDialogView
37 | )
38 | export default AllowanceDialog
39 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/app.js:
--------------------------------------------------------------------------------
1 | import AppView from '../components/app.js'
2 | import { connect } from 'react-redux'
3 | import { hot } from 'react-hot-loader'
4 |
5 | const mapStateToProps = state => ({
6 | showAllowanceDialog: state.files.get('showAllowanceDialog')
7 | })
8 |
9 | const App = connect(mapStateToProps)(AppView)
10 | export default hot(module)(App)
11 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/contractorstatus.js:
--------------------------------------------------------------------------------
1 | import ContractorStatusView from '../components/contractorstatus.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | settingAllowance: state.files.get('settingAllowance'),
6 | contractCount: state.files.get('contractCount')
7 | })
8 |
9 | const ContractorStatus = connect(mapStateToProps)(ContractorStatusView)
10 | export default ContractorStatus
11 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/deletedialog.js:
--------------------------------------------------------------------------------
1 | import DeleteDialogView from '../components/deletedialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { hideDeleteDialog, deleteFile } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | files: state.deletedialog.get('files')
8 | })
9 | const mapDispatchToProps = dispatch => ({
10 | actions: bindActionCreators({ hideDeleteDialog, deleteFile }, dispatch)
11 | })
12 |
13 | const DeleteDialog = connect(mapStateToProps, mapDispatchToProps)(
14 | DeleteDialogView
15 | )
16 | export default DeleteDialog
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/filebrowser.js:
--------------------------------------------------------------------------------
1 | import FileBrowserView from '../components/filebrowser.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | setDragging,
6 | deselectAll,
7 | setNotDragging,
8 | showUploadDialog
9 | } from '../actions/files.js'
10 |
11 | const mapStateToProps = state => ({
12 | dragging: state.files.get('dragging'),
13 | settingAllowance: state.files.get('settingAllowance'),
14 | showRenameDialog: state.files.get('showRenameDialog'),
15 | showUploadDialog: state.files.get('showUploadDialog'),
16 | showFileTransfers: state.files.get('showFileTransfers'),
17 | showDeleteDialog: state.files.get('showDeleteDialog'),
18 | showAddFolderDialog: state.files.get('showAddFolderDialog'),
19 | dragUploadEnabled: state.files.get('dragUploadEnabled')
20 | })
21 |
22 | const mapDispatchToProps = dispatch => ({
23 | actions: bindActionCreators(
24 | { setDragging, deselectAll, setNotDragging, showUploadDialog },
25 | dispatch
26 | )
27 | })
28 |
29 | const FileBrowser = connect(mapStateToProps, mapDispatchToProps)(
30 | FileBrowserView
31 | )
32 | export default FileBrowser
33 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/filecontrols.js:
--------------------------------------------------------------------------------
1 | import FileControlsView from '../components/filecontrols.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | downloadFile,
6 | showRenameDialog,
7 | showDeleteDialog
8 | } from '../actions/files.js'
9 |
10 | const mapStateToProps = state => ({
11 | files: state.files.get('selected')
12 | })
13 | const mapDispatchToProps = dispatch => ({
14 | actions: bindActionCreators(
15 | { downloadFile, showRenameDialog, showDeleteDialog },
16 | dispatch
17 | )
18 | })
19 |
20 | const FileControls = connect(mapStateToProps, mapDispatchToProps)(
21 | FileControlsView
22 | )
23 | export default FileControls
24 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/filelist.js:
--------------------------------------------------------------------------------
1 | import FileListView from '../components/filelist.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | renameSiaUIFolder,
6 | deleteSiaUIFolder,
7 | renameFile,
8 | getFiles,
9 | setDragFolderTarget,
10 | setDragFileOrigin,
11 | setDragUploadEnabled,
12 | setPath,
13 | selectUpTo,
14 | deselectFile,
15 | deselectAll,
16 | selectFile,
17 | downloadFile,
18 | showDeleteDialog,
19 | showRenameDialog
20 | } from '../actions/files.js'
21 |
22 | const mapStateToProps = state => ({
23 | files: state.files.get('workingDirectoryFiles'),
24 | selected: state.files.get('selected'),
25 | searchResults: state.files.get('searchResults'),
26 | path: state.files.get('path'),
27 | showSearchField: state.files.get('showSearchField'),
28 | dragFolderTarget: state.files.get('dragFolderTarget'),
29 | dragFileOrigin: state.files.get('dragFileOrigin')
30 | })
31 | const mapDispatchToProps = dispatch => ({
32 | actions: bindActionCreators(
33 | {
34 | renameSiaUIFolder,
35 | deleteSiaUIFolder,
36 | getFiles,
37 | renameFile,
38 | setDragFileOrigin,
39 | setDragFolderTarget,
40 | setDragUploadEnabled,
41 | selectUpTo,
42 | setPath,
43 | deselectFile,
44 | deselectAll,
45 | selectFile,
46 | showRenameDialog,
47 | downloadFile,
48 | showDeleteDialog
49 | },
50 | dispatch
51 | )
52 | })
53 |
54 | const FileList = connect(mapStateToProps, mapDispatchToProps)(FileListView)
55 | export default FileList
56 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/filetransfers.js:
--------------------------------------------------------------------------------
1 | import FileTransfersView from '../components/filetransfers.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | getDownloads,
6 | clearDownloads,
7 | hideFileTransfers
8 | } from '../actions/files.js'
9 |
10 | const mapStateToProps = state => ({
11 | uploads: state.files.get('uploading'),
12 | downloads: state.files.get('downloading')
13 | })
14 | const mapDispatchToProps = dispatch => ({
15 | actions: bindActionCreators(
16 | { getDownloads, clearDownloads, hideFileTransfers },
17 | dispatch
18 | )
19 | })
20 |
21 | const FileTransfers = connect(mapStateToProps, mapDispatchToProps)(
22 | FileTransfersView
23 | )
24 | export default FileTransfers
25 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/renamedialog.js:
--------------------------------------------------------------------------------
1 | import RenameDialogView from '../components/renamedialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { hideRenameDialog, renameFile } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | file: state.renamedialog.get('file')
8 | })
9 | const mapDispatchToProps = dispatch => ({
10 | actions: bindActionCreators({ hideRenameDialog, renameFile }, dispatch)
11 | })
12 |
13 | const RenameDialog = connect(mapStateToProps, mapDispatchToProps)(
14 | RenameDialogView
15 | )
16 | export default RenameDialog
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/searchbutton.js:
--------------------------------------------------------------------------------
1 | import SearchButtonView from '../components/searchbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { toggleSearchField, setSearchText } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | path: state.files.get('path')
8 | })
9 | const mapDispatchToProps = dispatch => ({
10 | actions: bindActionCreators({ toggleSearchField, setSearchText }, dispatch)
11 | })
12 |
13 | const SearchButton = connect(mapStateToProps, mapDispatchToProps)(
14 | SearchButtonView
15 | )
16 | export default SearchButton
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/searchfield.js:
--------------------------------------------------------------------------------
1 | import SearchFieldView from '../components/searchfield.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { setSearchText } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | searchText: state.files.get('searchText'),
8 | path: state.files.get('path')
9 | })
10 | const mapDispatchToProps = dispatch => ({
11 | actions: bindActionCreators({ setSearchText }, dispatch)
12 | })
13 |
14 | const SearchField = connect(mapStateToProps, mapDispatchToProps)(
15 | SearchFieldView
16 | )
17 | export default SearchField
18 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/setallowancebutton.js:
--------------------------------------------------------------------------------
1 | import SetAllowanceButtonView from '../components/setallowancebutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showAllowanceDialog } from '../actions/files.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showAllowanceDialog }, dispatch)
9 | })
10 |
11 | const SetAllowanceButton = connect(mapStateToProps, mapDispatchToProps)(
12 | SetAllowanceButtonView
13 | )
14 | export default SetAllowanceButton
15 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/transfersbutton.js:
--------------------------------------------------------------------------------
1 | import TransfersButtonView from '../components/transfersbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { toggleFileTransfers } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | unread:
8 | state.files.get('unreadUploads').size +
9 | state.files.get('unreadDownloads').size
10 | })
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators({ toggleFileTransfers }, dispatch)
13 | })
14 |
15 | const TransfersButton = connect(mapStateToProps, mapDispatchToProps)(
16 | TransfersButtonView
17 | )
18 | export default TransfersButton
19 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/uploadbutton.js:
--------------------------------------------------------------------------------
1 | import UploadButtonView from '../components/uploadbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showUploadDialog } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | contracts: state.files.get('contractCount')
8 | })
9 | const mapDispatchToProps = dispatch => ({
10 | actions: bindActionCreators({ showUploadDialog }, dispatch)
11 | })
12 |
13 | const UploadButton = connect(mapStateToProps, mapDispatchToProps)(
14 | UploadButtonView
15 | )
16 | export default UploadButton
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/uploaddialog.js:
--------------------------------------------------------------------------------
1 | import UploadDialogView from '../components/uploaddialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { hideUploadDialog, uploadFile, uploadFolder } from '../actions/files.js'
5 |
6 | const mapStateToProps = state => ({
7 | source: state.files.get('uploadSource'),
8 | path: state.files.get('path')
9 | })
10 | const mapDispatchToProps = dispatch => ({
11 | actions: bindActionCreators(
12 | { hideUploadDialog, uploadFile, uploadFolder },
13 | dispatch
14 | )
15 | })
16 |
17 | const UploadDialog = connect(mapStateToProps, mapDispatchToProps)(
18 | UploadDialogView
19 | )
20 | export default UploadDialog
21 |
--------------------------------------------------------------------------------
/plugins/Files/js/containers/usagestats.js:
--------------------------------------------------------------------------------
1 | import UsageStatsView from '../components/usagestats.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | allowance: state.files.get('allowance'),
6 | downloadspending: state.files.get('downloadspending'),
7 | uploadspending: state.files.get('uploadspending'),
8 | storagespending: state.files.get('storagespending'),
9 | contractspending: state.files.get('contractspending'),
10 | unspent: state.files.get('unspent'),
11 | renewheight: state.files.get('renewheight')
12 | })
13 |
14 | const UsageStats = connect(mapStateToProps)(UsageStatsView)
15 | export default UsageStats
16 |
--------------------------------------------------------------------------------
/plugins/Files/js/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import createSagaMiddleware from 'redux-saga'
4 | import { createStore, applyMiddleware } from 'redux'
5 | import { Provider } from 'react-redux'
6 | import rootReducer from './reducers/index.js'
7 | import rootSaga from './sagas/index.js'
8 | import App from './containers/app.js'
9 | import { fetchData } from './actions/files.js'
10 |
11 | // If dev enable window reload
12 | if (process.env.NODE_ENV === 'development') {
13 | require('electron-css-reload')()
14 | }
15 |
16 | const sagaMiddleware = createSagaMiddleware()
17 | const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
18 | sagaMiddleware.run(rootSaga)
19 |
20 | const rootElement = (
21 |
22 |
23 |
24 | )
25 | ReactDOM.render(rootElement, document.getElementById('react-root'))
26 |
27 | // update state when plugin is focused
28 | window.onfocus = () => {
29 | store.dispatch(fetchData())
30 | }
31 |
--------------------------------------------------------------------------------
/plugins/Files/js/reducers/allowancedialog.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/files.js'
3 |
4 | const initialState = Map({
5 | storageEstimate: '0 B',
6 | feeEstimate: 0,
7 | confirming: false,
8 | confirmationAllowance: '0'
9 | })
10 |
11 | export default function allowancedialogReduceR (state = initialState, action) {
12 | switch (action.type) {
13 | case constants.SHOW_ALLOWANCE_CONFIRMATION:
14 | return state
15 | .set('confirming', true)
16 | .set('confirmationAllowance', action.allowance)
17 | case constants.HIDE_ALLOWANCE_CONFIRMATION:
18 | return state.set('confirming', false)
19 | case constants.CLOSE_ALLOWANCE_DIALOG:
20 | return state.set('confirming', false)
21 | case constants.SET_FEE_ESTIMATE:
22 | return state.set('feeEstimate', action.estimate)
23 | case constants.SET_STORAGE_ESTIMATE:
24 | return state.set('storageEstimate', action.estimate)
25 | default:
26 | return state
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugins/Files/js/reducers/deletedialog.js:
--------------------------------------------------------------------------------
1 | import { Map, List } from 'immutable'
2 | import * as constants from '../constants/files.js'
3 |
4 | const initialState = Map({
5 | files: List()
6 | })
7 |
8 | export default function deletedialogReducer (state = initialState, action) {
9 | switch (action.type) {
10 | case constants.SHOW_DELETE_DIALOG:
11 | return state.set('files', action.files)
12 | default:
13 | return state
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/Files/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import wallet from './wallet.js'
3 | import files from './files.js'
4 | import deletedialog from './deletedialog.js'
5 | import renamedialog from './renamedialog.js'
6 | import allowancedialog from './allowancedialog.js'
7 |
8 | const rootReducer = combineReducers({
9 | wallet,
10 | files,
11 | deletedialog,
12 | renamedialog,
13 | allowancedialog
14 | })
15 |
16 | export default rootReducer
17 |
--------------------------------------------------------------------------------
/plugins/Files/js/reducers/renamedialog.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/files.js'
3 |
4 | const initialState = Map({
5 | file: {}
6 | })
7 |
8 | export default function renamedialogReducer (state = initialState, action) {
9 | switch (action.type) {
10 | case constants.SHOW_RENAME_DIALOG:
11 | return state.set('file', action.file)
12 | default:
13 | return state
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/plugins/Files/js/reducers/wallet.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/files.js'
3 |
4 | const initialState = Map({
5 | unlocked: false,
6 | synced: false,
7 | balance: ''
8 | })
9 |
10 | export default function walletReducer (state = initialState, action) {
11 | switch (action.type) {
12 | case constants.RECEIVE_WALLET_LOCKSTATE:
13 | return state.set('unlocked', action.unlocked)
14 | case constants.RECEIVE_WALLET_BALANCE:
15 | return state.set('balance', action.balance)
16 | case constants.SET_WALLET_SYNCSTATE:
17 | return state.set('synced', action.synced)
18 | default:
19 | return state
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/plugins/Files/js/sagas/index.js:
--------------------------------------------------------------------------------
1 | import * as sagas from './files.js'
2 | import { fork } from 'redux-saga/effects'
3 |
4 | export default function * rootSaga () {
5 | const watchers = Object.values(sagas).map(fork)
6 | yield watchers
7 | }
8 |
--------------------------------------------------------------------------------
/plugins/Hosting/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Hosting/assets/button.png
--------------------------------------------------------------------------------
/plugins/Hosting/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hosting
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/announce.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const AnnounceDialogModal = ({ announceAddress, actions }) => {
4 | const handleSettingInput = e =>
5 | actions.updateModal('announceAddress', e.target.value)
6 | const hideAnnounceDialog = address => actions.hideAnnounceDialog(address)
7 | const closeAnnounceDialog = () => hideAnnounceDialog('')
8 | const handleSubmit = () => {
9 | if (announceAddress !== '') {
10 | hideAnnounceDialog(announceAddress)
11 | }
12 | }
13 |
14 | const handleSettingKeyDown = e => {
15 | if (e.keyCode === 13) {
16 | handleSubmit()
17 | e.preventDefault()
18 | }
19 | }
20 |
21 | return (
22 |
59 | )
60 | }
61 |
62 | export default AnnounceDialogModal
63 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Header from '../containers/header.js'
3 | import Body from '../containers/body.js'
4 | import ResizeDialog from '../containers/resizeDialog.js'
5 | import AnnounceDialog from '../containers/announce.js'
6 | import WalletModal from '../containers/walletmodal.js'
7 | import { hot } from 'react-hot-loader'
8 |
9 | const HostingApp = () => (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | )
18 |
19 | export default hot(module)(HostingApp)
20 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/body.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import FilesList from '../containers/fileslist.js'
3 | import SettingsList from '../containers/settingslist.js'
4 |
5 | const Body = ({ actions }) => {
6 | const announceHost = () => actions.announceHost()
7 |
8 | return (
9 |
10 |
11 |
12 |
Help
13 |
14 |
15 |
16 | Announce
17 |
18 |
19 |
20 |
21 |
22 | To start hosting:
23 |
24 | Add a storage folder.
25 |
26 | Set your prefered price, bandwidth cost, collateral, and
27 | duration.
28 |
29 | Set 'Accepting Contracts' to 'Yes'
30 |
31 | Announce your host by clicking the above 'Announce' button.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default Body
45 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import BigNumber from 'bignumber.js'
3 | import WarningBar from './warningbar.js'
4 | import HostStatus from './hoststatus.js'
5 |
6 | const Header = ({
7 | numContracts,
8 | earned,
9 | expected,
10 | walletsize,
11 | walletLocked,
12 | workingstatus,
13 | connectabilitystatus
14 | }) => (
15 |
41 | )
42 |
43 | export default Header
44 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/hoststatus.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const HostStatus = ({ connectabilitystatus, workingstatus }) => {
5 | if (connectabilitystatus === 'checking' && workingstatus === 'checking') {
6 | return (
7 |
8 |
9 |
Checking Host Status...
10 |
11 | Sia-UI is determining the status of your Host.
12 |
13 |
14 | )
15 | }
16 |
17 | if (
18 | connectabilitystatus === 'not connectable' &&
19 | workingstatus === 'not working'
20 | ) {
21 | return (
22 |
23 |
24 |
Host Unreachable
25 |
26 | Your host is not connectable at the configured net address. Check your
27 | UPNP or NAT settings.
28 |
29 |
30 | )
31 | }
32 |
33 | if (
34 | connectabilitystatus === 'connectable' &&
35 | workingstatus === 'not working'
36 | ) {
37 | return (
38 |
39 |
40 |
Host Inactive
41 |
42 | Your host is connectable, but it is not being used by any renters.
43 |
44 |
45 | )
46 | }
47 |
48 | return (
49 |
50 |
51 |
Host Online
52 |
53 | Your host is connectable and is being contacted by renters.
54 |
55 |
56 | )
57 | }
58 |
59 | HostStatus.propTypes = {
60 | connectabilitystatus: PropTypes.string.isRequired,
61 | workingstatus: PropTypes.string.isRequired
62 | }
63 |
64 | export default HostStatus
65 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/resizeDialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Map } from 'immutable'
3 | import Path from 'path'
4 |
5 | const ResizeDialogModal = ({
6 | resizePath,
7 | resizeSize,
8 | initialSize,
9 | actions
10 | }) => {
11 | const handleSettingInput = e =>
12 | actions.updateModal('resizeSize', e.target.value)
13 | const hideResizeDialog = newSize =>
14 | actions.hideResizeDialog(Map({ path: resizePath, size: newSize }))
15 | const closeResizeDialog = () => hideResizeDialog(0)
16 | const handleSubmit = () => {
17 | if (resizeSize >= 35 && resizeSize !== initialSize.toString()) {
18 | hideResizeDialog(resizeSize)
19 | }
20 | }
21 |
22 | const handleSettingKeyDown = e => {
23 | if (e.keyCode === 13) {
24 | handleSubmit()
25 | e.preventDefault()
26 | }
27 | }
28 |
29 | return (
30 |
67 | )
68 | }
69 |
70 | export default ResizeDialogModal
71 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/walletmodal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const WalletUnlockModal = ({ walletLocked }) => (
4 |
7 |
8 |
You must unlock the wallet to host files.
9 |
10 |
11 |
12 | )
13 |
14 | export default WalletUnlockModal
15 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/warningbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const WarningBarModal = ({ title, message }) => (
4 |
5 |
6 |
{title}
7 |
{message}
8 |
9 |
10 | )
11 |
12 | export default WarningBarModal
13 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/components/warningmodal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const WarningModalModal = ({ title, message, actions }) => {
4 | const handleAccept = () => actions.acceptModal()
5 | const handleDecline = () => actions.declineModal()
6 |
7 | return (
8 |
9 |
10 |
11 | X
12 |
13 |
{title}
14 |
{message}
15 |
16 |
22 |
23 |
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default WarningModalModal
36 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/constants/constants.js:
--------------------------------------------------------------------------------
1 | export const UPDATE_MODAL = 'UPDATE_MODAL'
2 | export const PUSH_SETTINGS = 'PUSH_SETTINGS'
3 | export const UPDATE_SETTINGS = 'UPDATE_SETTINGS'
4 | export const UPDATE_DEFAULT_SETTINGS = 'UPDATE_DEFAULT_SETTINGS'
5 | export const FETCH_DATA = 'FETCH_DATA'
6 | export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS'
7 | export const TOGGLE_ACCEPTING = 'TOGGLE_ACCEPTING'
8 | export const SHOW_TOGGLE_ACCEPTING_MODAL = 'SHOW_TOGGLE_ACCEPTING_MODAL'
9 | export const HIDE_TOGGLE_ACCEPTING_MODAL = 'HIDE_TOGGLE_ACCEPTING_MODAL'
10 | export const RESET_HOST = 'RESET_HOST'
11 | export const ADD_FOLDER = 'ADD_FOLDER'
12 | export const ADD_FOLDER_ASK = 'ADD_FOLDER_ASK'
13 | export const REMOVE_FOLDER = 'REMOVE_FOLDER'
14 | export const UPDATE_FOLDER_TO_REMOVE = 'UPDATE_FOLDER_TO_REMOVE'
15 | export const RESIZE_FOLDER = 'RESIZE_FOLDER'
16 | export const SHOW_RESIZE_DIALOG = 'SHOW_RESIZE_DIALOG'
17 | export const HIDE_RESIZE_DIALOG = 'HIDE_RESIZE_DIALOG'
18 | export const ANNOUNCE_HOST = 'ANNOUNCE_HOST'
19 | export const SHOW_ANNOUNCE_DIALOG = 'SHOW_ANNOUNCE_DIALOG'
20 | export const HIDE_ANNOUNCE_DIALOG = 'HIDE_ANNOUNCE_DIALOG'
21 | export const REQUEST_DEFAULT_SETTINGS = 'REQUEST_DEFAULT_SETTINGS'
22 | export const RECEIVE_DEFAULT_SETTINGS = 'RECEIVE_DEFAULT_SETTINGS'
23 | export const GET_HOST_STATUS = 'GET_HOST_STATUS'
24 | export const SET_HOST_STATUS = 'SET_HOST_STATUS'
25 | export const SET_ESTIMATED_SCORE = 'SET_ESTIMATED_SCORE'
26 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/announce.js:
--------------------------------------------------------------------------------
1 | import AnnounceDialogView from '../components/announce.js'
2 | import { connect } from 'react-redux'
3 | import { hideAnnounceDialog, updateModal } from '../actions/actions.js'
4 | import { bindActionCreators } from 'redux'
5 |
6 | const mapStateToProps = state => ({
7 | shouldShowAnnounceDialog: state.modalReducer.get('shouldShowAnnounceDialog'),
8 | announceAddress: state.modalReducer.get('announceAddress')
9 | })
10 |
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators({ hideAnnounceDialog, updateModal }, dispatch)
13 | })
14 |
15 | const AnnounceDialog = connect(mapStateToProps, mapDispatchToProps)(
16 | AnnounceDialogView
17 | )
18 | export default AnnounceDialog
19 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/body.js:
--------------------------------------------------------------------------------
1 | import BodyView from '../components/body.js'
2 | import { connect } from 'react-redux'
3 | import { updateSettings, announceHost } from '../actions/actions.js'
4 | import { bindActionCreators } from 'redux'
5 |
6 | const mapDispatchToProps = dispatch => ({
7 | actions: bindActionCreators({ updateSettings, announceHost }, dispatch)
8 | })
9 |
10 | const mapStateToProps = state => ({
11 | usersettings: state.hostingReducer.get('usersettings'),
12 | defaultsettings: state.hostingReducer.get('defaultsettings'),
13 | acceptingContracts: state.hostingReducer.get('acceptingContracts'),
14 | files: state.hostingReducer.get('files')
15 | })
16 |
17 | const Body = connect(mapStateToProps, mapDispatchToProps)(BodyView)
18 | export default Body
19 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/fileslist.js:
--------------------------------------------------------------------------------
1 | import FilesListView from '../components/fileslist.js'
2 | import { connect } from 'react-redux'
3 | import {
4 | addFolderAskPathSize,
5 | removeFolder,
6 | resizeFolder,
7 | updateFolderToRemove
8 | } from '../actions/actions.js'
9 | import { bindActionCreators } from 'redux'
10 |
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators(
13 | { addFolderAskPathSize, removeFolder, resizeFolder, updateFolderToRemove },
14 | dispatch
15 | )
16 | })
17 |
18 | const mapStateToProps = state => ({
19 | folders: state.hostingReducer.get('files'),
20 | folderPathToRemove: state.modalReducer.get('folderPathToRemove')
21 | })
22 |
23 | const FilesList = connect(mapStateToProps, mapDispatchToProps)(FilesListView)
24 | export default FilesList
25 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/header.js:
--------------------------------------------------------------------------------
1 | import HeaderView from '../components/header.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | numContracts: state.hostingReducer.get('numContracts'),
6 | storage: state.hostingReducer.get('storage'),
7 | earned: state.hostingReducer.get('earned'),
8 | expected: state.hostingReducer.get('expected'),
9 | walletsize: state.hostingReducer.get('walletsize'),
10 | walletLocked: state.hostingReducer.get('walletLocked'),
11 | connectabilitystatus: state.hostingReducer.get('connectabilitystatus'),
12 | workingstatus: state.hostingReducer.get('workingstatus')
13 | })
14 |
15 | const Header = connect(mapStateToProps)(HeaderView)
16 | export default Header
17 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/resizeDialog.js:
--------------------------------------------------------------------------------
1 | import ResizeDialogView from '../components/resizeDialog.js'
2 | import { connect } from 'react-redux'
3 | import { hideResizeDialog, updateModal } from '../actions/actions.js'
4 | import { bindActionCreators } from 'redux'
5 |
6 | const mapStateToProps = state => ({
7 | resizePath: state.modalReducer.get('resizePath'),
8 | resizeSize: state.modalReducer.get('resizeSize'),
9 | initialSize: state.modalReducer.get('initialSize')
10 | })
11 |
12 | const mapDispatchToProps = dispatch => ({
13 | actions: bindActionCreators({ hideResizeDialog, updateModal }, dispatch)
14 | })
15 |
16 | const ResizeDialog = connect(mapStateToProps, mapDispatchToProps)(
17 | ResizeDialogView
18 | )
19 | export default ResizeDialog
20 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/settingslist.js:
--------------------------------------------------------------------------------
1 | import SettingsListView from '../components/settingslist.js'
2 | import { connect } from 'react-redux'
3 | import {
4 | showToggleAcceptingModal,
5 | hideToggleAcceptingModal,
6 | updateSettings,
7 | pushSettings
8 | } from '../actions/actions.js'
9 | import { bindActionCreators } from 'redux'
10 | import { Map } from 'immutable'
11 |
12 | const mapDispatchToProps = dispatch => ({
13 | actions: bindActionCreators(
14 | {
15 | showToggleAcceptingModal,
16 | hideToggleAcceptingModal,
17 | updateSettings,
18 | pushSettings
19 | },
20 | dispatch
21 | )
22 | })
23 |
24 | const mapStateToProps = state => ({
25 | usersettings: Map({
26 | maxduration: Map({
27 | name: 'Max Duration (Weeks)',
28 | value: state.settingsReducer.get('maxduration'),
29 | min: 12
30 | }),
31 | collateral: Map({
32 | name: 'Collateral per TB per Month (SC)',
33 | value: state.settingsReducer.get('collateral')
34 | }),
35 | storageprice: Map({
36 | name: 'Price per TB per Month (SC)',
37 | value: state.settingsReducer.get('storageprice')
38 | }),
39 | downloadbandwidthprice: Map({
40 | name: 'Bandwidth Price (SC/TB)',
41 | value: state.settingsReducer.get('downloadbandwidthprice')
42 | })
43 | }),
44 | conversionRate: state.settingsReducer.get('conversionRate'),
45 | acceptingContracts: state.settingsReducer.get('acceptingContracts'),
46 | settingsChanged: state.settingsReducer.get('settingsChanged'),
47 | defaultsettings: state.settingsReducer.get('defaultsettings'),
48 | shouldShowToggleAcceptingModal: state.modalReducer.get(
49 | 'shouldShowToggleAcceptingModal'
50 | )
51 | })
52 |
53 | const SettingsList = connect(mapStateToProps, mapDispatchToProps)(
54 | SettingsListView
55 | )
56 | export default SettingsList
57 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/containers/walletmodal.js:
--------------------------------------------------------------------------------
1 | import WalletUnlockView from '../components/walletmodal.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | walletLocked: state.hostingReducer.get('walletLocked')
6 | })
7 |
8 | const WalletUnlock = connect(mapStateToProps)(WalletUnlockView)
9 | export default WalletUnlock
10 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/index.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom'
2 | import { hostingPlugin } from './main.js'
3 |
4 | // If dev enable window reload
5 | if (process.env.NODE_ENV === 'development') {
6 | require('electron-css-reload')()
7 | }
8 |
9 | ReactDOM.render(hostingPlugin(), document.getElementById('react-root'))
10 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createStore, applyMiddleware } from 'redux'
3 | import createSagaMiddleware from 'redux-saga'
4 | import { Provider } from 'react-redux'
5 | import rootReducer from './reducers/index.js'
6 | import rootSaga from './sagas/saga.js'
7 | import HostingApp from './components/app.js'
8 | import * as actions from './actions/actions.js'
9 |
10 | export const hostingPlugin = () => {
11 | const sagaMiddleware = createSagaMiddleware()
12 | const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
13 | sagaMiddleware.run(rootSaga)
14 |
15 | store.dispatch(actions.requestDefaultSettings())
16 | store.dispatch(actions.fetchData())
17 |
18 | const updateState = () => {
19 | store.dispatch(actions.fetchData())
20 | store.dispatch(actions.getHostStatus())
21 | }
22 |
23 | // Poll Siad for state changes.
24 | setInterval(updateState, 20000)
25 |
26 | // update state immediately when this plugin is focused
27 | window.onfocus = updateState
28 |
29 | return (
30 |
31 |
32 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/reducers/hosting.js:
--------------------------------------------------------------------------------
1 | import { Map, List } from 'immutable'
2 | import * as constants from '../constants/constants.js'
3 |
4 | const initialState = Map({
5 | numContracts: 0,
6 | storage: 0,
7 | earned: 0,
8 | expected: 0,
9 | walletLocked: true,
10 | walletsize: 0,
11 | files: List([]),
12 |
13 | workingstatus: 'checking',
14 | connectabilitystatus: 'checking'
15 | })
16 |
17 | export default function hostingReducer (state = initialState, action) {
18 | switch (action.type) {
19 | case constants.SET_HOST_STATUS:
20 | return state
21 | .set('workingstatus', action.working)
22 | .set('connectabilitystatus', action.connectable)
23 | case constants.FETCH_DATA_SUCCESS:
24 | return state.merge(action.data)
25 | default:
26 | return state
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import hostingReducer from './hosting.js'
3 | import settingsReducer from './setting.js'
4 | import modalReducer from './modal.js'
5 |
6 | const rootReducer = combineReducers({
7 | hostingReducer,
8 | settingsReducer,
9 | modalReducer
10 | })
11 |
12 | export default rootReducer
13 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/reducers/modal.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/constants.js'
3 |
4 | const initialState = Map({
5 | resizeSize: 0,
6 | initialSize: 0,
7 | resizePath: '',
8 | folderPathToRemove: '',
9 | announceAddress: undefined,
10 | defaultAnnounceAddress: '',
11 | shouldShowToggleAcceptingModal: false
12 | })
13 |
14 | export default function modalReducer (state = initialState, action) {
15 | switch (action.type) {
16 | case constants.UPDATE_MODAL:
17 | return state.set(action.key, action.value)
18 |
19 | case constants.SHOW_TOGGLE_ACCEPTING_MODAL:
20 | return state.set('shouldShowToggleAcceptingModal', true)
21 |
22 | case constants.HIDE_TOGGLE_ACCEPTING_MODAL:
23 | return state.set('shouldShowToggleAcceptingModal', false)
24 |
25 | case constants.SHOW_RESIZE_DIALOG:
26 | return state
27 | .set('resizePath', action.folder.get('path'))
28 | .set('resizeSize', action.folder.get('size'))
29 | .set(
30 | 'initialSize',
31 | action.ignoreInitial ? 0 : action.folder.get('size')
32 | )
33 |
34 | case constants.HIDE_RESIZE_DIALOG:
35 | return state.set('resizePath', '')
36 |
37 | case constants.HIDE_ANNOUNCE_DIALOG:
38 | return state.set('announceAddress', undefined)
39 |
40 | case constants.SHOW_ANNOUNCE_DIALOG:
41 | return state.set(
42 | 'announceAddress',
43 | action.address || state.get('defaultAnnounceAddress')
44 | )
45 |
46 | case constants.UPDATE_FOLDER_TO_REMOVE:
47 | return state.set('folderPathToRemove', action.folder || '')
48 |
49 | case constants.FETCH_DATA_SUCCESS:
50 | return state.merge(action.modals)
51 |
52 | default:
53 | return state
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/reducers/setting.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/constants.js'
3 |
4 | const initialState = Map({
5 | maxduration: 0,
6 | collateral: 0,
7 | conversionRate: 0,
8 | storageprice: 0,
9 | downloadbandwidthprice: 0,
10 | acceptingContracts: false,
11 | defaultsettings: Map(),
12 | settingsChanged: false
13 | })
14 |
15 | export default function settingsReducer (state = initialState, action) {
16 | switch (action.type) {
17 | case constants.UPDATE_SETTINGS:
18 | return state.merge(action.settings).set('settingsChanged', true)
19 | case constants.PUSH_SETTINGS:
20 | return state
21 | .set('defaultsettings', action.settings)
22 | .set('settingsChanged', false)
23 | case constants.RECEIVE_DEFAULT_SETTINGS:
24 | return state
25 | .set('defaultsettings', action.settings)
26 | .merge(action.settings)
27 | case constants.FETCH_DATA_SUCCESS:
28 | return state.get('settingsChanged') ? state : state.merge(action.settings)
29 | case constants.SET_ESTIMATED_SCORE:
30 | return state.set('conversionRate', action.conversionRate)
31 | default:
32 | return state
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugins/Hosting/js/utils/host.js:
--------------------------------------------------------------------------------
1 | import BigNumber from 'bignumber.js'
2 |
3 | export const chooseFileLocation = function () {
4 | const selectedFile = SiaAPI.openFile({
5 | title: 'Choose new storage location.',
6 | properties: ['openDirectory']
7 | })
8 | if (selectedFile) {
9 | return selectedFile[0]
10 | } else {
11 | return undefined
12 | }
13 | }
14 |
15 | export const hastingsByteToSCTB = hastings =>
16 | SiaAPI.hastingsToSiacoins(hastings).times('1e12')
17 |
18 | export const SCTBToHastingsByte = SC =>
19 | SiaAPI.siacoinsToHastings(SC).dividedBy('1e12')
20 |
21 | export const validNumbers = values =>
22 | // Expects array of dict, first is value, second is minimum.
23 | values.reduce(
24 | (isValid, val) =>
25 | !isNaN(val.value) && val.value > (val.min || 0) && isValid,
26 | true
27 | )
28 |
29 | export const hastingsByteBlockToSCTBMonth = hastings =>
30 | hastingsByteToSCTB(hastings).times('4320') // 4320 = blocks per month
31 |
32 | export const SCTBMonthToHastingsByteBlock = SC =>
33 | SCTBToHastingsByte(SC).dividedBy('4320') // 4320 = blocks per month
34 |
35 | export const blocksToWeeks = blocks => new BigNumber(blocks).dividedBy('1008') // 1008 = blocks per week
36 |
37 | export const weeksToBlocks = weeks => new BigNumber(weeks).times('1008') // 1008 = blocks per week
38 |
--------------------------------------------------------------------------------
/plugins/Logs/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Logs/assets/button.png
--------------------------------------------------------------------------------
/plugins/Logs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logs
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/plugins/Logs/js/actions.js:
--------------------------------------------------------------------------------
1 | import * as constants from './constants.js'
2 |
3 | export const addLogFilters = filters => ({
4 | type: constants.ADD_LOG_FILTERS,
5 | filters
6 | })
7 | export const removeLogFilters = filters => ({
8 | type: constants.REMOVE_LOG_FILTERS,
9 | filters
10 | })
11 | export const setLogFilters = filters => ({
12 | type: constants.SET_LOG_FILTERS,
13 | filters
14 | })
15 | export const incrementLogSize = (increment = 50000) => ({
16 | type: constants.INCREMENT_LOG_SIZE,
17 | increment
18 | })
19 | export const reloadLog = () => ({
20 | type: constants.RELOAD_LOG
21 | })
22 | export const setScrolling = () => ({
23 | type: constants.SET_SCROLLING
24 | })
25 | export const setNotScrolling = () => ({
26 | type: constants.SET_NOT_SCROLLING
27 | })
28 |
--------------------------------------------------------------------------------
/plugins/Logs/js/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import FilterControls from '../containers/filtercontrols.js'
3 | import LogView from '../containers/logview.js'
4 | import { hot } from 'react-hot-loader'
5 |
6 | const App = () => (
7 |
8 |
9 |
10 |
11 | )
12 |
13 | export default hot(module)(App)
14 |
--------------------------------------------------------------------------------
/plugins/Logs/js/components/filtercontrol.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const FilterControl = ({
5 | name,
6 | filters,
7 | checked,
8 | addLogFilters,
9 | removeLogFilters,
10 | setLogFilters
11 | }) => {
12 | const filterControlStyle = {
13 | width: '100px',
14 | height: '50px',
15 | margin: '0',
16 | padding: '0',
17 | display: 'flex',
18 | alignItems: 'center',
19 | justifyContent: 'center',
20 | cursor: 'pointer',
21 | borderBottom: checked ? '4px solid #00CBA0' : '1px solid #00CBA0'
22 | }
23 | const onFilterClick = e => {
24 | if (!e.shiftKey && !e.ctrlKey) {
25 | setLogFilters(filters)
26 | return
27 | }
28 | if (checked) {
29 | removeLogFilters(filters)
30 | return
31 | }
32 | addLogFilters(filters)
33 | }
34 | return (
35 |
36 | {name}
37 |
38 | )
39 | }
40 |
41 | FilterControl.propTypes = {
42 | name: PropTypes.string.isRequired,
43 | filters: PropTypes.array.isRequired,
44 | checked: PropTypes.bool.isRequired,
45 | addLogFilters: PropTypes.func.isRequired,
46 | removeLogFilters: PropTypes.func.isRequired,
47 | setLogFilters: PropTypes.func.isRequired
48 | }
49 |
50 | export default FilterControl
51 |
--------------------------------------------------------------------------------
/plugins/Logs/js/components/filtercontrols.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { Set } from 'immutable'
4 | import FilterControl from './filtercontrol'
5 | import { filters } from '../filters.js'
6 |
7 | const filterControlsStyle = {
8 | width: '100%',
9 | position: 'absolute',
10 | top: '0',
11 | margin: '0',
12 | padding: '0',
13 | backgroundColor: '#ECECEC',
14 | display: 'flex',
15 | alignItems: 'center',
16 | justifyContent: 'center'
17 | }
18 |
19 | const FilterControls = ({ logFilters, actions }) => (
20 |
21 | {filters.map((filter, key) => (
22 |
27 | isChecked || logFilters.includes(filtertext),
28 | false
29 | )}
30 | addLogFilters={actions.addLogFilters}
31 | removeLogFilters={actions.removeLogFilters}
32 | setLogFilters={actions.setLogFilters}
33 | key={key}
34 | />
35 | ))}
36 |
37 | )
38 |
39 | FilterControls.propTypes = {
40 | logFilters: PropTypes.instanceOf(Set).isRequired
41 | }
42 |
43 | export default FilterControls
44 |
--------------------------------------------------------------------------------
/plugins/Logs/js/components/logview.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const logViewStyle = {
5 | position: 'absolute',
6 | top: '55px',
7 | bottom: '0',
8 | left: '2px',
9 | right: '0',
10 | margin: '0',
11 | padding: '0',
12 | overflowY: 'scroll',
13 | whiteSpace: 'pre',
14 | fontSize: '12px',
15 | fontFamily: 'monospace'
16 | }
17 |
18 | export default class LogView extends React.Component {
19 | componentDidMount () {
20 | this._logView.scrollTop = this._logView.scrollHeight
21 | this._logView.addEventListener('scroll', this.handleScroll.bind(this))
22 | }
23 | componentDidUpdate () {
24 | if (!this.props.scrolling) {
25 | this._logView.scrollTop = this._logView.scrollHeight
26 | }
27 | }
28 | handleScroll () {
29 | if (this._logView.scrollTop === 0) {
30 | this.props.actions.incrementLogSize()
31 | this._logView.scrollTop = 1
32 | }
33 | if (
34 | this._logView.scrollTop ===
35 | this._logView.scrollHeight - this._logView.clientHeight
36 | ) {
37 | this.props.actions.setNotScrolling()
38 | } else {
39 | this.props.actions.setScrolling()
40 | }
41 | }
42 | render () {
43 | return (
44 | (this._logView = lv)}>
45 | {this.props.logText}
46 |
47 | )
48 | }
49 | }
50 |
51 | LogView.propTypes = {
52 | logText: PropTypes.string.isRequired,
53 | scrolling: PropTypes.bool.isRequired
54 | }
55 |
--------------------------------------------------------------------------------
/plugins/Logs/js/constants.js:
--------------------------------------------------------------------------------
1 | export const ADD_LOG_FILTERS = 'ADD_LOG_FILTERS'
2 | export const REMOVE_LOG_FILTERS = 'REMOVE_LOG_FILTERS'
3 | export const SET_LOG_FILTERS = 'SET_LOG_FILTERS'
4 | export const INCREMENT_LOG_SIZE = 'INCREMENT_LOG_SIZE'
5 | export const RELOAD_LOG = 'RELOAD_LOG'
6 | export const SET_SCROLLING = 'SET_SCROLLING'
7 | export const SET_NOT_SCROLLING = 'SET_NOT_SCROLLING'
8 |
--------------------------------------------------------------------------------
/plugins/Logs/js/containers/filtercontrols.js:
--------------------------------------------------------------------------------
1 | import FilterControlsView from '../components/filtercontrols.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { setLogFilters, addLogFilters, removeLogFilters } from '../actions.js'
5 |
6 | const mapStateToProps = state => ({
7 | logFilters: state.get('logFilters')
8 | })
9 |
10 | const mapDispatchToProps = dispatch => ({
11 | actions: bindActionCreators(
12 | { setLogFilters, addLogFilters, removeLogFilters },
13 | dispatch
14 | )
15 | })
16 |
17 | const FilterControls = connect(mapStateToProps, mapDispatchToProps)(
18 | FilterControlsView
19 | )
20 | export default FilterControls
21 |
--------------------------------------------------------------------------------
/plugins/Logs/js/containers/logview.js:
--------------------------------------------------------------------------------
1 | import LogViewView from '../components/logview.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { setScrolling, setNotScrolling, incrementLogSize } from '../actions.js'
5 |
6 | const mapStateToProps = state => ({
7 | logText: state.get('logText'),
8 | scrolling: state.get('scrolling')
9 | })
10 |
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators(
13 | { setScrolling, setNotScrolling, incrementLogSize },
14 | dispatch
15 | )
16 | })
17 |
18 | const LogView = connect(mapStateToProps, mapDispatchToProps)(LogViewView)
19 | export default LogView
20 |
--------------------------------------------------------------------------------
/plugins/Logs/js/filters.js:
--------------------------------------------------------------------------------
1 | export const filters = [
2 | {
3 | name: 'Siad',
4 | filters: ['siad-output.log']
5 | },
6 | {
7 | name: 'Consensus',
8 | filters: ['consensus.log']
9 | },
10 | {
11 | name: 'Gateway',
12 | filters: ['gateway.log']
13 | },
14 | {
15 | name: 'Host',
16 | filters: ['host.log', 'storagemanager.log']
17 | },
18 | {
19 | name: 'Renter',
20 | filters: ['renter/contractor.log', 'renter/hostdb.log', 'renter.log']
21 | },
22 | {
23 | name: 'Miner',
24 | filters: ['miner.log']
25 | },
26 | {
27 | name: 'Wallet',
28 | filters: ['wallet.log']
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/plugins/Logs/js/index.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom'
2 | import { logsPlugin } from './main.js'
3 |
4 | // If dev enable window reload
5 | if (process.env.NODE_ENV === 'development') {
6 | require('electron-css-reload')()
7 | }
8 |
9 | ReactDOM.render(logsPlugin(), document.getElementById('react-root'))
10 |
--------------------------------------------------------------------------------
/plugins/Logs/js/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createStore } from 'redux'
3 | import { Provider } from 'react-redux'
4 | import loggingReducer from './reducer.js'
5 | import App from './components/app.js'
6 | import * as actions from './actions.js'
7 |
8 | export const logsPlugin = () => {
9 | const store = createStore(loggingReducer)
10 | setInterval(() => {
11 | store.dispatch(actions.reloadLog())
12 | }, 10000)
13 | return (
14 |
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/plugins/Logs/js/reducer.js:
--------------------------------------------------------------------------------
1 | import { Map, Set } from 'immutable'
2 | import * as constants from './constants.js'
3 | import {
4 | addLogFilters,
5 | removeLogFilters,
6 | updateLogFilters,
7 | parseLogs
8 | } from './logparse.js'
9 |
10 | const siadir = SiaAPI.config.siad.datadir
11 | const defaultLogSize = 50000
12 |
13 | const initialState = Map({
14 | logFilters: Set(['consensus.log', 'gateway.log']),
15 | logText: parseLogs(siadir, 50000, ['consensus.log', 'gateway.log']),
16 | logSize: defaultLogSize,
17 |
18 | scrolling: false
19 | })
20 |
21 | export default function loggingReducer (state = initialState, action) {
22 | switch (action.type) {
23 | case constants.INCREMENT_LOG_SIZE:
24 | return updateLogFilters(
25 | state,
26 | state.get('logSize') + action.increment,
27 | state.get('logFilters')
28 | )
29 | case constants.RELOAD_LOG:
30 | return updateLogFilters(
31 | state,
32 | state.get('logSize'),
33 | state.get('logFilters')
34 | )
35 | case constants.ADD_LOG_FILTERS:
36 | return addLogFilters(state, action.filters)
37 | case constants.REMOVE_LOG_FILTERS:
38 | return removeLogFilters(state, action.filters)
39 | case constants.SET_LOG_FILTERS:
40 | return updateLogFilters(
41 | state,
42 | state.get('logSize'),
43 | Set(action.filters)
44 | ).set('logSize', defaultLogSize)
45 | case constants.SET_SCROLLING:
46 | return state.set('scrolling', true)
47 | case constants.SET_NOT_SCROLLING:
48 | return state.set('scrolling', false)
49 | default:
50 | return state
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/plugins/Logs/js/utils.js:
--------------------------------------------------------------------------------
1 | import fs from 'graceful-fs'
2 | import { List } from 'immutable'
3 | import Path from 'path'
4 |
5 | export const readdirRecursive = (dir, files = List()) => {
6 | const dirfiles = fs.readdirSync(dir)
7 | let filelist = files
8 | dirfiles.forEach(file => {
9 | const filepath = Path.join(dir, file)
10 | const stat = fs.statSync(filepath)
11 | if (stat.isDirectory()) {
12 | filelist = readdirRecursive(filepath, filelist)
13 | } else if (stat.isFile()) {
14 | filelist = filelist.push(filepath)
15 | }
16 | })
17 | return filelist
18 | }
19 |
--------------------------------------------------------------------------------
/plugins/Terminal/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Terminal/assets/button.png
--------------------------------------------------------------------------------
/plugins/Terminal/assets/roboto-mono-100.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Terminal/assets/roboto-mono-100.woff2
--------------------------------------------------------------------------------
/plugins/Terminal/assets/roboto-mono-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Terminal/assets/roboto-mono-300.woff2
--------------------------------------------------------------------------------
/plugins/Terminal/assets/roboto-mono.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Terminal/assets/roboto-mono.woff2
--------------------------------------------------------------------------------
/plugins/Terminal/css/roboto-mono.css:
--------------------------------------------------------------------------------
1 | /* latin */
2 | @font-face {
3 | font-family: 'Roboto Mono';
4 | font-style: normal;
5 | font-weight: 400;
6 | src: local('Roboto Mono'), local('RobotoMono-Regular'), url('../assets/roboto-mono.woff2') format('woff2');
7 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
8 | }
9 |
10 | /* latin */
11 | @font-face {
12 | font-family: 'Roboto Mono';
13 | font-style: normal;
14 | font-weight: 300;
15 | src: local('Roboto Mono Light'), local('RobotoMono-Light'), url('../assets/roboto-mono-300.woff2') format('woff2');
16 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
17 | }
18 |
19 | /* latin */
20 | @font-face {
21 | font-family: 'Roboto Mono';
22 | font-style: normal;
23 | font-weight: 100;
24 | src: local('Roboto Mono Thin'), local('RobotoMono-Thin'), url('../assets/roboto-mono-100.woff2') format('woff2');
25 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
26 | }
27 |
--------------------------------------------------------------------------------
/plugins/Terminal/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wallet
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/actions/commandline.js:
--------------------------------------------------------------------------------
1 | import * as constants from '../constants/commandline.js'
2 |
3 | export const addCommand = command => ({
4 | type: constants.ADD_COMMAND,
5 | command
6 | })
7 | export const updateCommand = (command, id, dataChunk) => ({
8 | type: constants.UPDATE_COMMAND,
9 | command,
10 | id,
11 | dataChunk
12 | })
13 | export const endCommand = (command, id) => ({
14 | type: constants.END_COMMAND,
15 | command,
16 | id
17 | })
18 |
19 | export const loadPrevCommand = () => ({
20 | type: constants.LOAD_PREV_COMMAND
21 | })
22 | export const loadNextCommand = () => ({
23 | type: constants.LOAD_NEXT_COMMAND
24 | })
25 | export const setCurrentCommand = commandText => ({
26 | type: constants.SET_CURRENT_COMMAND,
27 | command: commandText
28 | })
29 |
30 | export const setWalletPassword = walletPassword => ({
31 | type: constants.SET_WALLET_PASSWORD,
32 | walletPassword
33 | })
34 |
35 | export const showWalletPrompt = () => ({
36 | type: constants.SHOW_WALLET_PROMPT
37 | })
38 | export const hideWalletPrompt = () => ({
39 | type: constants.HIDE_WALLET_PROMPT
40 | })
41 |
42 | export const showSeedPrompt = () => ({
43 | type: constants.SHOW_SEED_PROMPT
44 | })
45 | export const hideSeedPrompt = () => ({
46 | type: constants.HIDE_SEED_PROMPT
47 | })
48 |
49 | export const showCommandOverview = () => ({
50 | type: constants.SHOW_COMMAND_OVERVIEW
51 | })
52 | export const hideCommandOverview = () => ({
53 | type: constants.HIDE_COMMAND_OVERVIEW
54 | })
55 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CommandLine from '../containers/commandline.js'
3 | import { hot } from 'react-hot-loader'
4 |
5 | const CommandLineApp = () => (
6 |
7 |
8 |
9 | )
10 |
11 | export default hot(module)(CommandLineApp)
12 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/components/commandhistorylist.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { List } from 'immutable'
4 |
5 | export default class CommandHistoryList extends React.Component {
6 | componentDidUpdate () {
7 | this._commandHistoryList.scrollTop = this._commandHistoryList.scrollHeight
8 | }
9 |
10 | render () {
11 | const CommandHistoryComponents = this.props.commandHistory
12 | .filterNot(
13 | command =>
14 | command.get('command') === 'help' || command.get('command') === '?'
15 | )
16 | .map((command, key) => (
17 |
18 |
19 | {command.get('command')}
20 |
26 |
27 | {command.get('result')}
28 |
29 | ))
30 |
31 | return (
32 | (this._commandHistoryList = c)}
35 | >
36 |
{CommandHistoryComponents}
37 |
38 | )
39 | }
40 | }
41 |
42 | CommandHistoryList.propTypes = { commandHistory: PropTypes.instanceOf(List) }
43 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/components/commandinput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { commandInputHelper } from '../utils/helpers.js'
3 |
4 | export default class CommandInput extends React.Component {
5 | componentDidUpdate () {
6 | // Give DOM time to register the update.
7 | if (!this.props.showWalletPrompt && !this.props.showSeedPrompt) {
8 | this._input.focus()
9 | }
10 | }
11 |
12 | render () {
13 | const handleTextInput = e =>
14 | this.props.actions.setCurrentCommand(e.target.value)
15 | const handleKeyboardPress = e =>
16 | commandInputHelper(
17 | e,
18 | this.props.actions,
19 | this.props.currentCommand,
20 | this.props.showCommandOverview,
21 | this.props.commandHistory.size
22 | )
23 | return (
24 | (this._input = c)}
33 | />
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/components/commandline.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CommandHistoryList from '../containers/commandhistorylist.js'
3 | import CommandInput from '../containers/commandinput.js'
4 | import WalletPasswordPrompt from '../containers/walletpasswordprompt.js'
5 | import WalletSeedPrompt from '../containers/seedprompt.js'
6 |
7 | const CommandLine = ({ showCommandOverview, actions }) => {
8 | const toggleCommandOverview = () => {
9 | if (showCommandOverview) {
10 | actions.hideCommandOverview()
11 | } else {
12 | actions.showCommandOverview()
13 | }
14 | }
15 |
16 | return (
17 |
20 |
21 |
22 |
27 |
28 | ?
29 |
30 |
31 |
Available Commands:
32 |
33 | consensus Print the current state of consensus
34 | gateway Perform gateway actions
35 | host Perform host actions
36 | hostdb View or modify the host database
37 | miner Perform miner actions
38 | renter Perform renter actions
39 | stop Stop the Sia daemon
40 | update Update Sia
41 | version Print version information
42 | wallet Perform wallet actions
43 |
44 | Use '[command] --help' for more information about a command.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
56 | export default CommandLine
57 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/components/seedprompt.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { httpCommand } from '../utils/helpers.js'
3 | import querystring from 'querystring'
4 |
5 | export default class WalletSeedPrompt extends React.Component {
6 | componentDidUpdate () {
7 | // Give DOM time to register the update.
8 | if (this.props.showSeedPrompt) {
9 | this._seedPasswd.focus()
10 | }
11 | }
12 |
13 | render () {
14 | const handleKeyboardPress = e => {
15 | if (e.keyCode === 13) {
16 | // Grab input, spawn process, and pipe text field to stdin.
17 | const siac = httpCommand(
18 | this.props.currentCommand,
19 | this.props.actions,
20 | this.props.commandHistory.size
21 | )
22 |
23 | siac.write(
24 | querystring.stringify({
25 | encryptionpassword: this.props.walletPassword,
26 | seed: e.target.value,
27 | dictionary: 'english'
28 | })
29 | )
30 | siac.end()
31 | e.target.value = ''
32 | this.props.actions.setWalletPassword('')
33 | this.props.actions.hideSeedPrompt()
34 | }
35 | }
36 |
37 | return (
38 |
42 |
43 |
New seed
44 |
Please type your new seed and press enter to continue.
45 |
(this._seedPasswd = c)}
50 | />
51 |
52 |
53 | )
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/constants/commandline.js:
--------------------------------------------------------------------------------
1 | export const ADD_COMMAND = 'ADD_COMMAND'
2 | export const UPDATE_COMMAND = 'UPDATE_COMMAND'
3 | export const END_COMMAND = 'END_COMMAND'
4 | export const LOAD_PREV_COMMAND = 'LOAD_PREV_COMMAND'
5 | export const LOAD_NEXT_COMMAND = 'LOAD_NEXT_COMMAND'
6 | export const SET_CURRENT_COMMAND = 'SET_CURRENT_COMMAND'
7 | export const SET_WALLET_PASSWORD = 'SET_WALLET_PASSWORD'
8 | export const SHOW_WALLET_PROMPT = 'SHOW_WALLET_PROMPT'
9 | export const HIDE_WALLET_PROMPT = 'HIDE_WALLET_PROMPT'
10 | export const SHOW_SEED_PROMPT = 'SHOW_SEED_PROMPT'
11 | export const HIDE_SEED_PROMPT = 'HIDE_SEED_PROMPT'
12 | export const SHOW_COMMAND_OVERVIEW = 'SHOW_COMMAND_OVERVIEW'
13 | export const HIDE_COMMAND_OVERVIEW = 'HIDE_COMMAND_OVERVIEW'
14 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/constants/helper.js:
--------------------------------------------------------------------------------
1 | export const REGULAR_COMMAND = -1
2 | export const WALLET_SEED = 0
3 | export const WALLET_INIT_SEED = 1
4 | export const WALLET_UNLOCK = 2
5 | export const WALLET_033X = 3
6 | export const WALLET_SIAG = 4
7 | export const HELP = 5
8 | export const HELP_QMARK = 6
9 |
10 | // These commands need a password prompt or other special handling.
11 | export const specialCommands = [
12 | ['wallet', 'load', 'seed'],
13 | ['wallet', 'init-seed'],
14 | ['wallet', 'unlock'],
15 | ['wallet', 'load', '033x'],
16 | ['wallet', 'load', 'siag'],
17 | ['help'],
18 | ['?']
19 | ]
20 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/containers/commandhistorylist.js:
--------------------------------------------------------------------------------
1 | import CommandHistoryListView from '../components/commandhistorylist.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | commandHistory: state.commandLineReducer.get('commandHistory')
6 | })
7 |
8 | const CommandHistoryList = connect(mapStateToProps)(CommandHistoryListView)
9 | export default CommandHistoryList
10 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/containers/commandinput.js:
--------------------------------------------------------------------------------
1 | import CommandInputView from '../components/commandinput.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | addCommand,
6 | updateCommand,
7 | endCommand,
8 | loadPrevCommand,
9 | loadNextCommand,
10 | setCurrentCommand,
11 | showWalletPrompt,
12 | showSeedPrompt,
13 | hideSeedPrompt,
14 | hideWalletPrompt,
15 | showCommandOverview,
16 | hideCommandOverview
17 | } from '../actions/commandline.js'
18 |
19 | const mapStateToProps = state => ({
20 | currentCommand: state.commandLineReducer.get('currentCommand'),
21 | commandIndex: state.commandLineReducer.get('commandIndex'),
22 | showCommandOverview: state.commandLineReducer.get('showCommandOverview'),
23 | commandRunning: state.commandLineReducer.get('commandRunning'),
24 | commandHistory: state.commandLineReducer.get('commandHistory'),
25 | showWalletPrompt: state.commandLineReducer.get('showWalletPrompt'),
26 | showSeedPrompt: state.commandLineReducer.get('showSeedPrompt')
27 | })
28 |
29 | const mapDispatchToProps = dispatch => ({
30 | actions: bindActionCreators(
31 | {
32 | addCommand,
33 | updateCommand,
34 | endCommand,
35 | loadPrevCommand,
36 | loadNextCommand,
37 | setCurrentCommand,
38 | showWalletPrompt,
39 | hideWalletPrompt,
40 | showSeedPrompt,
41 | hideSeedPrompt,
42 | showCommandOverview,
43 | hideCommandOverview
44 | },
45 | dispatch
46 | )
47 | })
48 |
49 | const CommandInput = connect(mapStateToProps, mapDispatchToProps)(
50 | CommandInputView
51 | )
52 | export default CommandInput
53 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/containers/commandline.js:
--------------------------------------------------------------------------------
1 | import CommandLineView from '../components/commandline.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | showCommandOverview,
6 | hideCommandOverview
7 | } from '../actions/commandline.js'
8 |
9 | const mapStateToProps = state => ({
10 | showCommandOverview: state.commandLineReducer.get('showCommandOverview')
11 | })
12 |
13 | const mapDispatchToProps = dispatch => ({
14 | actions: bindActionCreators(
15 | { showCommandOverview, hideCommandOverview },
16 | dispatch
17 | )
18 | })
19 |
20 | const CommandLine = connect(mapStateToProps, mapDispatchToProps)(
21 | CommandLineView
22 | )
23 | export default CommandLine
24 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/containers/seedprompt.js:
--------------------------------------------------------------------------------
1 | import WalletSeedPromptView from '../components/seedprompt.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | addCommand,
6 | updateCommand,
7 | endCommand,
8 | setWalletPassword,
9 | hideSeedPrompt,
10 | hideCommandOverview
11 | } from '../actions/commandline.js'
12 |
13 | const mapStateToProps = state => ({
14 | showSeedPrompt: state.commandLineReducer.get('showSeedPrompt'),
15 | currentCommand: state.commandLineReducer.get('currentCommand'),
16 | commandHistory: state.commandLineReducer.get('commandHistory'),
17 | walletPassword: state.commandLineReducer.get('walletPassword')
18 | })
19 |
20 | const mapDispatchToProps = dispatch => ({
21 | actions: bindActionCreators(
22 | {
23 | addCommand,
24 | updateCommand,
25 | endCommand,
26 | setWalletPassword,
27 | hideSeedPrompt,
28 | hideCommandOverview
29 | },
30 | dispatch
31 | )
32 | })
33 |
34 | const WalletSeedPrompt = connect(mapStateToProps, mapDispatchToProps)(
35 | WalletSeedPromptView
36 | )
37 | export default WalletSeedPrompt
38 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/containers/walletpasswordprompt.js:
--------------------------------------------------------------------------------
1 | import WalletPasswordPromptView from '../components/walletpasswordprompt.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | addCommand,
6 | updateCommand,
7 | endCommand,
8 | setWalletPassword,
9 | hideWalletPrompt,
10 | showSeedPrompt,
11 | hideCommandOverview
12 | } from '../actions/commandline.js'
13 |
14 | const mapStateToProps = state => ({
15 | showWalletPrompt: state.commandLineReducer.get('showWalletPrompt'),
16 | currentCommand: state.commandLineReducer.get('currentCommand'),
17 | commandHistory: state.commandLineReducer.get('commandHistory'),
18 | walletPassword: state.commandLineReducer.get('walletPassword')
19 | })
20 |
21 | const mapDispatchToProps = dispatch => ({
22 | actions: bindActionCreators(
23 | {
24 | addCommand,
25 | updateCommand,
26 | endCommand,
27 | setWalletPassword,
28 | hideWalletPrompt,
29 | showSeedPrompt,
30 | hideCommandOverview
31 | },
32 | dispatch
33 | )
34 | })
35 |
36 | const WalletPasswordPrompt = connect(mapStateToProps, mapDispatchToProps)(
37 | WalletPasswordPromptView
38 | )
39 | export default WalletPasswordPrompt
40 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/index.js:
--------------------------------------------------------------------------------
1 | // index.js: main entrypoint for the Sia-UI wallet plugin.
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import CommandLine from './components/app.js'
5 | import { createStore } from 'redux'
6 | import { Provider } from 'react-redux'
7 | import rootReducer from './reducers/index.js'
8 | import { initPlugin } from './utils/helpers.js'
9 |
10 | // If dev enable window reload
11 | if (process.env.NODE_ENV === 'development') {
12 | require('electron-css-reload')()
13 | }
14 |
15 | // Render the wallet plugin
16 | const store = createStore(rootReducer)
17 | const rootElement = (
18 |
19 |
20 |
21 | )
22 |
23 | initPlugin()
24 | ReactDOM.render(rootElement, document.getElementById('react-root'))
25 |
--------------------------------------------------------------------------------
/plugins/Terminal/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import commandLineReducer from './commandline.js'
3 |
4 | const rootReducer = combineReducers({
5 | commandLineReducer
6 | })
7 |
8 | export default rootReducer
9 |
--------------------------------------------------------------------------------
/plugins/Wallet/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NebulousLabs/Sia-UI/cd7e221b98fc7a395de25bd8dfaa4f4de2a6e3d2/plugins/Wallet/assets/button.png
--------------------------------------------------------------------------------
/plugins/Wallet/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wallet
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/actions/error.js:
--------------------------------------------------------------------------------
1 | import { WALLET_UNLOCK_ERROR } from '../constants/error.js'
2 |
3 | export const walletUnlockError = err => ({
4 | type: WALLET_UNLOCK_ERROR,
5 | err
6 | })
7 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import LockScreen from '../containers/lockscreen.js'
3 | import Wallet from '../containers/wallet.js'
4 | import { hot } from 'react-hot-loader'
5 |
6 | const WalletApp = () => (
7 |
8 |
9 |
10 |
11 | )
12 |
13 | export default hot(module)(WalletApp)
14 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/backupbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const BackupButton = ({ actions }) => {
4 | const handleClick = () => actions.showBackupPrompt()
5 | return (
6 |
7 |
8 | Backup Wallet
9 |
10 | )
11 | }
12 |
13 | export default BackupButton
14 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/backupprompt.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const BackupPrompt = ({ primarySeed, auxSeeds, actions }) => {
5 | const handleOkClick = () => actions.hideBackupPrompt()
6 | return (
7 |
8 |
9 | {auxSeeds.length === 0 ? (
10 |
11 | Write down your seed to back up your wallet. You can restore your
12 | wallet using only this seed.
13 |
14 | ) : (
15 |
16 | Write down your seeds to back up your wallet. You can restore your
17 | wallet using only these seeds.
18 |
19 | )}
20 |
21 |
Primary Seed:
22 |
{primarySeed}
23 | {auxSeeds.length > 0 ?
Auxiliary Seeds: : null}
24 | {auxSeeds.length > 0 ? (
25 |
26 | {auxSeeds.map((seed, key) => (
27 |
28 | {seed}
29 |
30 | ))}
31 |
32 | ) : null}
33 |
34 | OK
35 |
36 |
37 |
38 | )
39 | }
40 | BackupPrompt.propTypes = {
41 | primarySeed: PropTypes.string.isRequired,
42 | auxSeeds: PropTypes.array.isRequired
43 | }
44 |
45 | export default BackupPrompt
46 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/balanceinfo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const BalanceInfo = ({
5 | synced,
6 | confirmedbalance,
7 | unconfirmedbalance,
8 | siafundbalance,
9 | siacoinclaimbalance
10 | }) => (
11 |
12 | Confirmed Balance: {confirmedbalance} SC
13 | Unconfirmed Delta: {unconfirmedbalance} SC
14 | {siafundbalance !== '0' ? (
15 | Siafund Balance: {siafundbalance} SF
16 | ) : null}
17 | {siacoinclaimbalance !== '0' ? (
18 | Siacoin Claim Balance: {siacoinclaimbalance} SC
19 | ) : null}
20 | {!synced ? (
21 |
25 | Your wallet is not synced, balances are not final.
26 |
27 | ) : null}
28 |
29 | )
30 | BalanceInfo.propTypes = {
31 | synced: PropTypes.bool.isRequired,
32 | confirmedbalance: PropTypes.string.isRequired,
33 | unconfirmedbalance: PropTypes.string.isRequired,
34 | siafundbalance: PropTypes.string.isRequired,
35 | siacoinclaimbalance: PropTypes.string.isRequired
36 | }
37 | export default BalanceInfo
38 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/changepasswordbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const ChangePasswordButton = ({ actions }) => {
4 | const handleChangePasswordClick = () => actions.showChangePasswordDialog()
5 | return (
6 |
10 |
11 | Change Password
12 |
13 | )
14 | }
15 |
16 | export default ChangePasswordButton
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/changepassworddialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const ChangePasswordDialog = ({ changePasswordError, actions }) => {
5 | const handleChangePasswordClick = e => {
6 | e.preventDefault()
7 | if (e.target.newpassword.value !== e.target['newpassword-again'].value) {
8 | actions.setChangePasswordError('passwords did not match!')
9 | return
10 | }
11 | actions.changePassword(
12 | e.target.currentpassword.value,
13 | e.target.newpassword.value
14 | )
15 | }
16 | const handleCancelClick = e => {
17 | e.preventDefault()
18 | actions.hideChangePasswordDialog()
19 | }
20 |
21 | return (
22 |
62 | )
63 | }
64 |
65 | ChangePasswordDialog.propTypes = {
66 | changePasswordError: PropTypes.string.isRequired
67 | }
68 |
69 | export default ChangePasswordDialog
70 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/confirmationdialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const ConfirmationDialog = ({ seed, password, error, actions }) => {
5 | const onCancelClick = () => actions.hideConfirmationDialog()
6 | const onOkClick = e => {
7 | e.preventDefault()
8 | if (e.target.seed.value === seed && e.target.password.value === password) {
9 | actions.dismissNewWalletDialog()
10 | } else if (e.target.seed.value !== seed) {
11 | actions.setConfirmationError('seed did not match!')
12 | } else if (e.target.password.value !== password) {
13 | actions.setConfirmationError('password did not match!')
14 | }
15 | }
16 | return (
17 |
18 |
19 |
Please confirm your password and seed to continue
20 |
42 |
{error}
43 |
44 |
45 | )
46 | }
47 |
48 | ConfirmationDialog.propTypes = {
49 | seed: PropTypes.string.isRequired,
50 | password: PropTypes.string.isRequired,
51 | error: PropTypes.string.isRequired
52 | }
53 |
54 | export default ConfirmationDialog
55 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/initseedform.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const InitSeedForm = ({
4 | initializingSeed,
5 | useCustomPassphrase,
6 | hideInitSeedForm,
7 | createNewWallet
8 | }) => {
9 | if (initializingSeed) {
10 | return (
11 |
12 |
Initializing seed...
13 |
14 |
15 | )
16 | }
17 | const handleInitSeedClick = e => {
18 | e.preventDefault()
19 | if (useCustomPassphrase) {
20 | createNewWallet(
21 | e.target.password.value.trim(),
22 | e.target.seed.value.trim()
23 | )
24 | } else {
25 | createNewWallet(undefined, e.target.seed.value.trim())
26 | }
27 | }
28 | const handleCancelClick = e => {
29 | e.preventDefault()
30 | hideInitSeedForm()
31 | }
32 | return (
33 |
49 | )
50 | }
51 |
52 | export default InitSeedForm
53 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/lockbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const LockButton = ({ actions }) => {
4 | const handleLockButtonClick = () => actions.lockWallet()
5 | return (
6 |
7 |
8 | Lock Wallet
9 |
10 | )
11 | }
12 |
13 | export default LockButton
14 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/lockscreen.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import PasswordPrompt from '../containers/passwordprompt.js'
4 | import UninitializedWalletDialog from '../containers/uninitializedwalletdialog.js'
5 | import RescanDialog from './rescandialog.js'
6 |
7 | const LockScreen = ({ unlocked, unlocking, encrypted, rescanning }) => {
8 | if (unlocked && encrypted && !unlocking && !rescanning) {
9 | // Wallet is unlocked and encrypted, return an empty lock screen.
10 | return
11 | }
12 | let lockscreenContents
13 | if (!unlocked && encrypted && !rescanning) {
14 | lockscreenContents =
15 | } else if (rescanning) {
16 | lockscreenContents =
17 | } else if (!encrypted) {
18 | // Wallet is not encrypted, return a lockScreen that initializes a new wallet.
19 | lockscreenContents =
20 | }
21 | return (
22 |
23 |
{lockscreenContents}
24 |
25 | )
26 | }
27 | LockScreen.propTypes = {
28 | unlocked: PropTypes.bool.isRequired,
29 | unlocking: PropTypes.bool.isRequired,
30 | encrypted: PropTypes.bool.isRequired,
31 | rescanning: PropTypes.bool.isRequired
32 | }
33 |
34 | export default LockScreen
35 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/newwalletdialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import ConfirmationDialog from '../containers/confirmationdialog.js'
4 |
5 | const NewWalletDialog = ({
6 | password,
7 | seed,
8 | showConfirmationDialog,
9 | actions
10 | }) => {
11 | const handleDismissClick = () => actions.showConfirmationDialog()
12 | return (
13 |
14 | {showConfirmationDialog ?
: null}
15 |
16 | You have created a new wallet! Please write down the seed and password
17 | in a safe place. If you forget your password, you won't be able to
18 | access your wallet.
19 |
20 |
Seed:
21 |
{seed}
22 |
Password:
23 |
{password}
24 |
25 | I have written these down in a safe place
26 |
27 |
28 | )
29 | }
30 |
31 | NewWalletDialog.propTypes = {
32 | password: PropTypes.string.isRequired,
33 | seed: PropTypes.string.isRequired
34 | }
35 |
36 | export default NewWalletDialog
37 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/newwalletform.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const NewWalletForm = ({ actions }) => {
4 | const handleCreateWalletClick = e => {
5 | e.preventDefault()
6 | actions.createNewWallet(e.target.password.value)
7 | actions.hideNewWalletForm()
8 | }
9 | const handleCancelClick = e => {
10 | e.preventDefault()
11 | actions.hideNewWalletForm()
12 | }
13 | return (
14 |
25 | )
26 | }
27 |
28 | export default NewWalletForm
29 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/passwordprompt.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const PasswordPrompt = ({ password, error, unlocking, actions }) => {
5 | const onPasswordChange = e => actions.handlePasswordChange(e.target.value)
6 | const onUnlockClick = () => actions.unlockWallet(password)
7 | if (unlocking) {
8 | return (
9 |
10 | Unlocking your wallet, this may take a while (up to several minutes)...
11 |
12 | )
13 | }
14 | return (
15 |
16 |
Wallet Locked
17 |
Enter your wallet password to unlock the wallet.
18 |
19 |
25 |
26 | Unlock
27 |
28 |
{error}
29 |
30 | )
31 | }
32 | PasswordPrompt.propTypes = {
33 | password: PropTypes.string.isRequired,
34 | error: PropTypes.string
35 | }
36 |
37 | export default PasswordPrompt
38 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/receivebutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const ReceiveButton = ({ actions }) => {
4 | const handleReceiveButtonClick = () => actions.showReceivePrompt()
5 | return (
6 |
10 |
11 | Receive Siacoin
12 |
13 | )
14 | }
15 |
16 | export default ReceiveButton
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/recoverbutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const RecoverButton = ({ actions }) => {
4 | const handleRecoverButtonClick = () => actions.showSeedRecoveryDialog()
5 | return (
6 |
10 |
11 | Recover Seed
12 |
13 | )
14 | }
15 |
16 | export default RecoverButton
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/recoverydialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const RecoveryDialog = ({ recovering, actions }) => {
5 | const handleRecoverClick = e => {
6 | e.preventDefault()
7 | actions.recoverSeed(e.target.seed.value)
8 | }
9 | const handleCancelClick = e => {
10 | e.preventDefault()
11 | actions.hideSeedRecoveryDialog()
12 | }
13 |
14 | if (recovering) {
15 | return (
16 |
17 |
18 |
22 |
Recovering seed, this may take a long time...
23 |
24 |
25 | )
26 | }
27 |
28 | return (
29 |
46 | )
47 | }
48 |
49 | RecoveryDialog.propTypes = {
50 | recovering: PropTypes.bool.isRequired
51 | }
52 |
53 | export default RecoveryDialog
54 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/rescandialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const RescanDialog = () => (
4 |
5 |
6 |
Scanning the Blockchain...
7 |
8 | )
9 |
10 | export default RescanDialog
11 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/sendbutton.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 |
4 | const SendButton = ({ currencytype, onClick }) => (
5 |
6 |
7 | Send {currencytype}
8 |
9 | )
10 |
11 | SendButton.propTypes = {
12 | currencytype: PropTypes.string.isRequired
13 | }
14 |
15 | export default SendButton
16 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/sendprompt.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import BigNumber from 'bignumber.js'
4 |
5 | const SendPrompt = ({
6 | currencytype,
7 | sendAddress,
8 | sendAmount,
9 | feeEstimate,
10 | sendError,
11 | actions
12 | }) => {
13 | const handleSendAddressChange = e => actions.setSendAddress(e.target.value)
14 | const handleSendAmountChange = e => actions.setSendAmount(e.target.value)
15 | const handleSendClick = () => {
16 | try {
17 | new BigNumber(sendAmount)
18 | actions.setSendError('')
19 | actions.sendCurrency(sendAddress, sendAmount, currencytype)
20 | } catch (e) {
21 | actions.setSendError('could not parse send amount')
22 | }
23 | }
24 | const handleCancelClick = () => actions.closeSendPrompt()
25 | return (
26 |
27 |
28 |
29 |
Send Amount {currencytype === 'siacoins' ? '(SC)' : '(SF)'}
30 |
31 |
32 |
33 |
To Address
34 |
35 |
36 |
Estimated fee: {feeEstimate}
37 |
{sendError}
38 |
39 |
40 | Send
41 |
42 |
43 | Cancel
44 |
45 |
46 |
47 |
48 | )
49 | }
50 | SendPrompt.propTypes = {
51 | sendAddress: PropTypes.string.isRequired,
52 | sendError: PropTypes.string.isRequired,
53 | sendAmount: PropTypes.string.isRequired,
54 | currencytype: PropTypes.string.isRequired,
55 | feeEstimate: PropTypes.string.isRequired
56 | }
57 |
58 | export default SendPrompt
59 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/uninitializedwalletdialog.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import NewWalletForm from '../containers/newwalletform.js'
4 | import InitSeedForm from './initseedform.js'
5 |
6 | const UninitializedWalletDialog = ({
7 | initializingSeed,
8 | useCustomPassphrase,
9 | showInitSeedForm,
10 | showNewWalletForm,
11 | actions
12 | }) => {
13 | if (showNewWalletForm && useCustomPassphrase) {
14 | return
15 | }
16 | if (showInitSeedForm) {
17 | return (
18 |
24 | )
25 | }
26 | if (showNewWalletForm && !useCustomPassphrase) {
27 | actions.createNewWallet()
28 | }
29 |
30 | const handleCustomPasswordClick = () =>
31 | actions.setUseCustomPassphrase(!useCustomPassphrase)
32 | const handleCreateWalletClick = () => actions.showNewWalletForm()
33 | const handleCreateWalletFromSeedClick = () => actions.showInitSeedForm()
34 |
35 | return (
36 |
37 |
38 |
39 |
40 |
Create a new wallet
41 |
42 |
43 |
47 |
Load a wallet from a seed
48 |
49 |
50 |
51 |
56 | Use custom passphrase
57 |
58 |
59 | )
60 | }
61 |
62 | UninitializedWalletDialog.propTypes = {
63 | useCustomPassphrase: PropTypes.bool.isRequired,
64 | showNewWalletForm: PropTypes.bool.isRequired,
65 | initializingSeed: PropTypes.bool.isRequired
66 | }
67 |
68 | export default UninitializedWalletDialog
69 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/components/wallet.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import TransactionList from '../containers/transactionlist.js'
4 | import SendButton from './sendbutton.js'
5 | import SendPrompt from '../containers/sendprompt.js'
6 | import ReceiveButton from '../containers/receivebutton.js'
7 | import ReceivePrompt from '../containers/receiveprompt.js'
8 | import NewWalletDialog from '../containers/newwalletdialog.js'
9 | import LockButton from '../containers/lockbutton.js'
10 | import RecoverButton from '../containers/recoverbutton.js'
11 | import RecoveryDialog from '../containers/recoverydialog.js'
12 | import ChangePasswordButton from '../containers/changepasswordbutton.js'
13 | import ChangePasswordDialog from '../containers/changepassworddialog.js'
14 | import BackupButton from '../containers/backupbutton.js'
15 | import BackupPrompt from '../containers/backupprompt.js'
16 | import BalanceInfo from '../containers/balanceinfo.js'
17 |
18 | const Wallet = ({
19 | showBackupPrompt,
20 | siafundbalance,
21 | showReceivePrompt,
22 | showChangePasswordDialog,
23 | showSendPrompt,
24 | showNewWalletDialog,
25 | showRecoveryDialog,
26 | actions
27 | }) => {
28 | const onSendClick = currencytype => () =>
29 | actions.startSendPrompt(currencytype)
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {siafundbalance !== '0' ? (
39 |
43 | ) : null}
44 |
45 |
46 |
47 | {showNewWalletDialog ?
: null}
48 | {showSendPrompt ?
: null}
49 | {showReceivePrompt ?
: null}
50 | {showRecoveryDialog ?
: null}
51 | {showChangePasswordDialog ?
: null}
52 | {showBackupPrompt ?
: null}
53 |
54 |
55 | )
56 | }
57 |
58 | Wallet.propTypes = {
59 | siafundbalance: PropTypes.string.isRequired,
60 | showNewWalletDialog: PropTypes.bool,
61 | showSendPrompt: PropTypes.bool,
62 | showReceivePrompt: PropTypes.bool,
63 | showChangePasswordDialog: PropTypes.bool,
64 | showBackupPrompt: PropTypes.bool
65 | }
66 |
67 | export default Wallet
68 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/constants/error.js:
--------------------------------------------------------------------------------
1 | export const WALLET_UNLOCK_ERROR = 'WALLET_UNLOCK_ERROR'
2 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/backupbutton.js:
--------------------------------------------------------------------------------
1 | import BackupButtonView from '../components/backupbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showBackupPrompt } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showBackupPrompt }, dispatch)
9 | })
10 |
11 | const BackupButton = connect(mapStateToProps, mapDispatchToProps)(
12 | BackupButtonView
13 | )
14 | export default BackupButton
15 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/backupprompt.js:
--------------------------------------------------------------------------------
1 | import BackupPromptView from '../components/backupprompt.js'
2 | import { hideBackupPrompt } from '../actions/wallet.js'
3 | import { bindActionCreators } from 'redux'
4 | import { connect } from 'react-redux'
5 |
6 | const mapStateToProps = state => ({
7 | primarySeed: state.wallet.get('primarySeed'),
8 | auxSeeds: state.wallet.get('auxSeeds')
9 | })
10 | const mapDispatchToProps = dispatch => ({
11 | actions: bindActionCreators({ hideBackupPrompt }, dispatch)
12 | })
13 |
14 | const BackupPrompt = connect(mapStateToProps, mapDispatchToProps)(
15 | BackupPromptView
16 | )
17 | export default BackupPrompt
18 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/balanceinfo.js:
--------------------------------------------------------------------------------
1 | import BalanceInfoView from '../components/balanceinfo.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | synced: state.wallet.get('synced'),
6 | confirmedbalance: state.wallet.get('confirmedbalance'),
7 | unconfirmedbalance: state.wallet.get('unconfirmedbalance'),
8 | siafundbalance: state.wallet.get('siafundbalance'),
9 | siacoinclaimbalance: state.wallet.get('siacoinclaimbalance')
10 | })
11 | const mapDispatchToProps = () => ({})
12 |
13 | const BalanceInfo = connect(mapStateToProps, mapDispatchToProps)(
14 | BalanceInfoView
15 | )
16 | export default BalanceInfo
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/changepasswordbutton.js:
--------------------------------------------------------------------------------
1 | import ChangePasswordButtonView from '../components/changepasswordbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showChangePasswordDialog } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showChangePasswordDialog }, dispatch)
9 | })
10 |
11 | const ChangePasswordButton = connect(mapStateToProps, mapDispatchToProps)(
12 | ChangePasswordButtonView
13 | )
14 | export default ChangePasswordButton
15 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/changepassworddialog.js:
--------------------------------------------------------------------------------
1 | import ChangePasswordDialogView from '../components/changepassworddialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | hideChangePasswordDialog,
6 | changePassword,
7 | setChangePasswordError
8 | } from '../actions/wallet.js'
9 |
10 | const mapStateToProps = state => ({
11 | changePasswordError: state.wallet.get('changePasswordError')
12 | })
13 | const mapDispatchToProps = dispatch => ({
14 | actions: bindActionCreators(
15 | { hideChangePasswordDialog, changePassword, setChangePasswordError },
16 | dispatch
17 | )
18 | })
19 |
20 | const ChangePasswordDialog = connect(mapStateToProps, mapDispatchToProps)(
21 | ChangePasswordDialogView
22 | )
23 | export default ChangePasswordDialog
24 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/confirmationdialog.js:
--------------------------------------------------------------------------------
1 | import ConfirmationDialogView from '../components/confirmationdialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import {
5 | hideConfirmationDialog,
6 | dismissNewWalletDialog,
7 | setConfirmationError
8 | } from '../actions/wallet.js'
9 |
10 | const mapStateToProps = state => ({
11 | seed: state.newwalletdialog.get('seed'),
12 | password: state.newwalletdialog.get('password'),
13 | error: state.newwalletdialog.get('confirmationerror')
14 | })
15 | const mapDispatchToProps = dispatch => ({
16 | actions: bindActionCreators(
17 | { hideConfirmationDialog, dismissNewWalletDialog, setConfirmationError },
18 | dispatch
19 | )
20 | })
21 |
22 | const ConfirmationDialog = connect(mapStateToProps, mapDispatchToProps)(
23 | ConfirmationDialogView
24 | )
25 | export default ConfirmationDialog
26 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/lockbutton.js:
--------------------------------------------------------------------------------
1 | import LockButtonView from '../components/lockbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { lockWallet } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ lockWallet }, dispatch)
9 | })
10 |
11 | const LockButton = connect(mapStateToProps, mapDispatchToProps)(LockButtonView)
12 | export default LockButton
13 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/lockscreen.js:
--------------------------------------------------------------------------------
1 | import LockScreenView from '../components/lockscreen.js'
2 | import { connect } from 'react-redux'
3 |
4 | const mapStateToProps = state => ({
5 | unlocked: state.wallet.get('unlocked'),
6 | unlocking: state.wallet.get('unlocking'),
7 | encrypted: state.wallet.get('encrypted'),
8 | rescanning: state.wallet.get('rescanning')
9 | })
10 |
11 | const LockScreen = connect(mapStateToProps)(LockScreenView)
12 | export default LockScreen
13 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/newwalletdialog.js:
--------------------------------------------------------------------------------
1 | import NewWalletDialogView from '../components/newwalletdialog.js'
2 | import { showConfirmationDialog } from '../actions/wallet.js'
3 | import { bindActionCreators } from 'redux'
4 | import { connect } from 'react-redux'
5 |
6 | const mapStateToProps = state => ({
7 | password: state.newwalletdialog.get('password'),
8 | seed: state.newwalletdialog.get('seed'),
9 | showConfirmationDialog: state.newwalletdialog.get('showConfirmationDialog')
10 | })
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators({ showConfirmationDialog }, dispatch)
13 | })
14 |
15 | const NewWalletDialog = connect(mapStateToProps, mapDispatchToProps)(
16 | NewWalletDialogView
17 | )
18 | export default NewWalletDialog
19 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/newwalletform.js:
--------------------------------------------------------------------------------
1 | import NewWalletFormView from '../components/newwalletform.js'
2 | import { createNewWallet, hideNewWalletForm } from '../actions/wallet.js'
3 | import { connect } from 'react-redux'
4 | import { bindActionCreators } from 'redux'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ createNewWallet, hideNewWalletForm }, dispatch)
9 | })
10 |
11 | const NewWalletForm = connect(mapStateToProps, mapDispatchToProps)(
12 | NewWalletFormView
13 | )
14 | export default NewWalletForm
15 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/passwordprompt.js:
--------------------------------------------------------------------------------
1 | import PasswordPromptView from '../components/passwordprompt.js'
2 | import { unlockWallet, handlePasswordChange } from '../actions/wallet.js'
3 | import { bindActionCreators } from 'redux'
4 | import { connect } from 'react-redux'
5 |
6 | const mapStateToProps = state => ({
7 | password: state.passwordprompt.get('password'),
8 | error: state.passwordprompt.get('error'),
9 | unlocking: state.wallet.get('unlocking')
10 | })
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators({ handlePasswordChange, unlockWallet }, dispatch)
13 | })
14 |
15 | const PasswordPrompt = connect(mapStateToProps, mapDispatchToProps)(
16 | PasswordPromptView
17 | )
18 | export default PasswordPrompt
19 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/receivebutton.js:
--------------------------------------------------------------------------------
1 | import ReceiveButtonView from '../components/receivebutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showReceivePrompt } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showReceivePrompt }, dispatch)
9 | })
10 |
11 | const ReceiveButton = connect(mapStateToProps, mapDispatchToProps)(
12 | ReceiveButtonView
13 | )
14 | export default ReceiveButton
15 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/receiveprompt.js:
--------------------------------------------------------------------------------
1 | import ReceivePromptView from '../components/receiveprompt.js'
2 | import { connect } from 'react-redux'
3 | import {
4 | hideReceivePrompt,
5 | setAddressDescription,
6 | getNewReceiveAddress,
7 | saveAddress
8 | } from '../actions/wallet.js'
9 | import { bindActionCreators } from 'redux'
10 |
11 | const mapStateToProps = state => ({
12 | address: state.receiveprompt.get('address'),
13 | addresses: state.receiveprompt.get('addresses'),
14 | description: state.receiveprompt.get('description')
15 | })
16 | const mapDispatchToProps = dispatch => ({
17 | actions: bindActionCreators(
18 | {
19 | hideReceivePrompt,
20 | getNewReceiveAddress,
21 | saveAddress,
22 | setAddressDescription
23 | },
24 | dispatch
25 | )
26 | })
27 |
28 | const ReceivePrompt = connect(mapStateToProps, mapDispatchToProps)(
29 | ReceivePromptView
30 | )
31 | export default ReceivePrompt
32 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/recoverbutton.js:
--------------------------------------------------------------------------------
1 | import RecoverButtonView from '../components/recoverbutton.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showSeedRecoveryDialog } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = () => ({})
7 | const mapDispatchToProps = dispatch => ({
8 | actions: bindActionCreators({ showSeedRecoveryDialog }, dispatch)
9 | })
10 |
11 | const RecoverButton = connect(mapStateToProps, mapDispatchToProps)(
12 | RecoverButtonView
13 | )
14 | export default RecoverButton
15 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/recoverydialog.js:
--------------------------------------------------------------------------------
1 | import RecoveryDialogView from '../components/recoverydialog.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { recoverSeed, hideSeedRecoveryDialog } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = state => ({
7 | recovering: state.wallet.get('recovering')
8 | })
9 | const mapDispatchToProps = dispatch => ({
10 | actions: bindActionCreators({ recoverSeed, hideSeedRecoveryDialog }, dispatch)
11 | })
12 |
13 | const RecoveryDialog = connect(mapStateToProps, mapDispatchToProps)(
14 | RecoveryDialogView
15 | )
16 | export default RecoveryDialog
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/sendprompt.js:
--------------------------------------------------------------------------------
1 | import SendPromptView from '../components/sendprompt.js'
2 | import {
3 | setSendError,
4 | setSendAddress,
5 | setSendAmount,
6 | closeSendPrompt,
7 | sendCurrency
8 | } from '../actions/wallet.js'
9 | import { bindActionCreators } from 'redux'
10 | import { connect } from 'react-redux'
11 |
12 | const mapStateToProps = state => ({
13 | sendAddress: state.sendprompt.get('sendaddress'),
14 | sendAmount: state.sendprompt.get('sendamount'),
15 | currencytype: state.sendprompt.get('currencytype'),
16 | feeEstimate: state.sendprompt.get('feeEstimate'),
17 | sendError: state.sendprompt.get('error')
18 | })
19 | const mapDispatchToProps = dispatch => ({
20 | actions: bindActionCreators(
21 | {
22 | closeSendPrompt,
23 | setSendError,
24 | setSendAddress,
25 | setSendAmount,
26 | sendCurrency
27 | },
28 | dispatch
29 | )
30 | })
31 |
32 | const SendPrompt = connect(mapStateToProps, mapDispatchToProps)(SendPromptView)
33 | export default SendPrompt
34 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/transactionlist.js:
--------------------------------------------------------------------------------
1 | import TransactionListView from '../components/transactionlist.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { showMoreTransactions, toggleFilter } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = state => ({
7 | filter: state.wallet.get('filter'),
8 | transactions: state.wallet.get('transactions'),
9 | ntransactions: state.wallet.get('ntransactions')
10 | })
11 | const mapDispatchToProps = dispatch => ({
12 | actions: bindActionCreators({ showMoreTransactions, toggleFilter }, dispatch)
13 | })
14 |
15 | const TransactionList = connect(mapStateToProps, mapDispatchToProps)(
16 | TransactionListView
17 | )
18 | export default TransactionList
19 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/uninitializedwalletdialog.js:
--------------------------------------------------------------------------------
1 | import UninitializedWalletDialogView from '../components/uninitializedwalletdialog.js'
2 | import {
3 | showInitSeedForm,
4 | hideInitSeedForm,
5 | showNewWalletForm,
6 | setUseCustomPassphrase,
7 | createNewWallet
8 | } from '../actions/wallet.js'
9 | import { connect } from 'react-redux'
10 | import { bindActionCreators } from 'redux'
11 |
12 | const mapStateToProps = state => ({
13 | showNewWalletForm: state.wallet.get('showNewWalletForm'),
14 | showInitSeedForm: state.wallet.get('showInitSeedForm'),
15 | useCustomPassphrase: state.wallet.get('useCustomPassphrase'),
16 | initializingSeed: state.wallet.get('initializingSeed')
17 | })
18 | const mapDispatchToProps = dispatch => ({
19 | actions: bindActionCreators(
20 | {
21 | showInitSeedForm,
22 | hideInitSeedForm,
23 | showNewWalletForm,
24 | setUseCustomPassphrase,
25 | createNewWallet
26 | },
27 | dispatch
28 | )
29 | })
30 |
31 | const UninitializedWalletDialog = connect(mapStateToProps, mapDispatchToProps)(
32 | UninitializedWalletDialogView
33 | )
34 | export default UninitializedWalletDialog
35 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/containers/wallet.js:
--------------------------------------------------------------------------------
1 | import WalletView from '../components/wallet.js'
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { startSendPrompt } from '../actions/wallet.js'
5 |
6 | const mapStateToProps = state => ({
7 | siafundbalance: state.wallet.get('siafundbalance'),
8 | showReceivePrompt: state.wallet.get('showReceivePrompt'),
9 | showSendPrompt: state.wallet.get('showSendPrompt'),
10 | showNewWalletDialog: state.wallet.get('showNewWalletDialog'),
11 | showRecoveryDialog: state.wallet.get('showRecoveryDialog'),
12 | showChangePasswordDialog: state.wallet.get('showChangePasswordDialog'),
13 | showBackupPrompt: state.wallet.get('showBackupPrompt')
14 | })
15 | const mapDispatchToProps = dispatch => ({
16 | actions: bindActionCreators({ startSendPrompt }, dispatch)
17 | })
18 |
19 | const Wallet = connect(mapStateToProps, mapDispatchToProps)(WalletView)
20 | export default Wallet
21 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/index.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom'
2 | import { initWallet } from './main.js'
3 |
4 | // If dev enable window reload
5 | if (process.env.NODE_ENV === 'development') {
6 | require('electron-css-reload')()
7 | }
8 |
9 | ReactDOM.render(initWallet(), document.getElementById('react-root'))
10 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import createSagaMiddleware from 'redux-saga'
3 | import { createStore, applyMiddleware } from 'redux'
4 | import { Provider } from 'react-redux'
5 | import rootReducer from './reducers/index.js'
6 | import rootSaga from './sagas/index.js'
7 | import { fetchData } from './actions/wallet.js'
8 | import WalletApp from './components/app.js'
9 |
10 | // initWallet initializes a new wallet plugin and returns the root react
11 | // component for the plugin.
12 | export const initWallet = () => {
13 | // initialize the redux store
14 | const sagaMiddleware = createSagaMiddleware()
15 | const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
16 | sagaMiddleware.run(rootSaga)
17 |
18 | // update state when plugin is focused
19 | window.onfocus = () => {
20 | store.dispatch(fetchData())
21 | }
22 |
23 | // return the wallet plugin root component, a redux Provider wrapping the
24 | // root wallet component
25 | return (
26 |
27 |
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import wallet from './wallet.js'
3 | import passwordprompt from './passwordprompt.js'
4 | import newwalletdialog from './newwalletdialog.js'
5 | import sendprompt from './sendprompt.js'
6 | import receiveprompt from './receiveprompt.js'
7 |
8 | const rootReducer = combineReducers({
9 | wallet,
10 | passwordprompt,
11 | newwalletdialog,
12 | sendprompt,
13 | receiveprompt
14 | })
15 |
16 | export default rootReducer
17 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/reducers/newwalletdialog.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import {
3 | SHOW_NEW_WALLET_DIALOG,
4 | SHOW_CONFIRMATION_DIALOG,
5 | HIDE_CONFIRMATION_DIALOG,
6 | SET_CONFIRMATION_ERROR
7 | } from '../constants/wallet.js'
8 |
9 | const initialState = Map({
10 | password: '',
11 | seed: '',
12 | showConfirmationDialog: false,
13 | confirmationerror: ''
14 | })
15 |
16 | export default function newwalletdialog (state = initialState, action) {
17 | switch (action.type) {
18 | case SET_CONFIRMATION_ERROR:
19 | return state.set('confirmationerror', action.error)
20 | case SHOW_NEW_WALLET_DIALOG:
21 | return state.set('password', action.password).set('seed', action.seed)
22 | case SHOW_CONFIRMATION_DIALOG:
23 | return state.set('showConfirmationDialog', true)
24 | case HIDE_CONFIRMATION_DIALOG:
25 | return state.set('showConfirmationDialog', false)
26 | default:
27 | return state
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/reducers/passwordprompt.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import * as constants from '../constants/wallet.js'
3 | import { WALLET_UNLOCK_ERROR } from '../constants/error.js'
4 |
5 | const initialState = Map({
6 | password: '',
7 | error: ''
8 | })
9 |
10 | export default function passwordpromptReducer (state = initialState, action) {
11 | switch (action.type) {
12 | case constants.HANDLE_PASSWORD_CHANGE:
13 | return state.set('password', action.password)
14 | case WALLET_UNLOCK_ERROR:
15 | return state.set('error', action.err)
16 | default:
17 | return state
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/reducers/receiveprompt.js:
--------------------------------------------------------------------------------
1 | import { Map, List } from 'immutable'
2 | import {
3 | SET_RECEIVE_ADDRESS,
4 | SET_RECEIVE_ADDRESSES,
5 | SET_ADDRESS_DESCRIPTION
6 | } from '../constants/wallet.js'
7 |
8 | const initialState = Map({
9 | address: '',
10 | description: '',
11 | addresses: List([])
12 | })
13 | export default function receivePromptReducer (state = initialState, action) {
14 | switch (action.type) {
15 | case SET_RECEIVE_ADDRESSES:
16 | return state.set('addresses', action.addresses)
17 | case SET_ADDRESS_DESCRIPTION:
18 | return state.set('description', action.description)
19 | case SET_RECEIVE_ADDRESS:
20 | return state.set('address', action.address)
21 | default:
22 | return state
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/reducers/sendprompt.js:
--------------------------------------------------------------------------------
1 | import { Map } from 'immutable'
2 | import {
3 | SET_SEND_ERROR,
4 | SET_FEE_ESTIMATE,
5 | SET_SEND_AMOUNT,
6 | SET_SEND_ADDRESS,
7 | START_SEND_PROMPT
8 | } from '../constants/wallet.js'
9 |
10 | const initialState = Map({
11 | sendaddress: '',
12 | sendamount: '',
13 | currencytype: 'siacoins',
14 | feeEstimate: '0 SC/KB',
15 | error: ''
16 | })
17 | export default function sendPromptReducer (state = initialState, action) {
18 | switch (action.type) {
19 | case SET_FEE_ESTIMATE:
20 | return state.set('feeEstimate', action.estimate)
21 | case SET_SEND_AMOUNT:
22 | return state.set('sendamount', action.amount)
23 | case SET_SEND_ADDRESS:
24 | return state.set('sendaddress', action.address)
25 | case START_SEND_PROMPT:
26 | return state.set('currencytype', action.currencytype)
27 | case SET_SEND_ERROR:
28 | return state.set('error', action.error)
29 | default:
30 | return state
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/plugins/Wallet/js/sagas/index.js:
--------------------------------------------------------------------------------
1 | import * as sagas from './wallet.js'
2 | import { fork } from 'redux-saga/effects'
3 |
4 | export default function * rootSaga () {
5 | const watchers = Object.values(sagas).map(fork)
6 | yield watchers
7 | }
8 |
--------------------------------------------------------------------------------
/test/config.unit.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai'
2 | import fs from 'fs'
3 | import proxyquire from 'proxyquire'
4 | import { version } from '../package.json'
5 |
6 | const mock = {
7 | 'electron': {
8 | 'app': {
9 | getPath: () => './',
10 | },
11 | '@noCallThru': true,
12 | },
13 | }
14 | const loadConfig = proxyquire('../js/mainjs/config.js', mock)
15 |
16 | describe('config.js', () => {
17 | afterEach(() => {
18 | try {
19 | fs.unlinkSync('test.json')
20 | } catch (err) {
21 | console.error('error cleaning up test: ', err.toString())
22 | }
23 | })
24 | it('loads the default config successfully when an invalid path is given', () => {
25 | loadConfig('/invalid/path')
26 | })
27 | it('saves and loads the config successfully when a valid path is given', () => {
28 | const config = loadConfig('test.json')
29 | config.save()
30 | const config2 = loadConfig('test.json')
31 | expect(config2).to.deep.equal(config)
32 | })
33 | it('sets the default siad path if the config version is undefined', () => {
34 | const config = loadConfig('test.json')
35 | const defaultSiadPath = config.siad.path
36 | config.version = undefined
37 | config.siad.path = '/test/siad/path'
38 | config.save()
39 | const config2 = loadConfig('test.json')
40 | expect(config2.siad.path).to.equal(defaultSiadPath)
41 | expect(config2.version).to.equal(version)
42 | })
43 | it('sets the default siad path if the config version is outdated', () => {
44 | const config = loadConfig('test.json')
45 | const defaultSiadPath = config.siad.path
46 | config.version = '1.2.0'
47 | config.siad.path = '/test/siad/path'
48 | config.save()
49 | const config2 = loadConfig('test.json')
50 | expect(config2.siad.path).to.equal(defaultSiadPath)
51 | expect(config2.version).to.equal(version)
52 | config2.siad.path = '/test/new/path'
53 | config2.save()
54 | const config3 = loadConfig('test.json')
55 | expect(config3.siad.path).to.equal('/test/new/path')
56 | })
57 | })
58 |
59 |
60 |
--------------------------------------------------------------------------------
/test/dom.setup.js:
--------------------------------------------------------------------------------
1 | import { jsdom } from 'jsdom'
2 | import Path from 'path'
3 |
4 | const testdir = Path.resolve('./test/logs/testdir')
5 | const exposedProperties = ['window', 'navigator', 'document']
6 |
7 | global.HTMLElement = function() {}
8 | global.document = jsdom('')
9 | global.window = document.defaultView
10 | global.SiaAPI = {
11 | config: {
12 | siad: {
13 | datadir: testdir,
14 | },
15 | },
16 | }
17 |
18 | Object.keys(document.defaultView).forEach((property) => {
19 | if (typeof global[property] === 'undefined') {
20 | exposedProperties.push(property)
21 | global[property] = document.defaultView[property]
22 | }
23 | })
24 |
25 | global.navigator = {
26 | userAgent: 'node.js',
27 | }
28 |
--------------------------------------------------------------------------------
/test/files/filebrowser.component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 |
3 | import React from 'react'
4 | import { shallow } from 'enzyme'
5 | import { expect } from 'chai'
6 | import sinon from 'sinon'
7 |
8 | import FileBrowser from '../../plugins/Files/js/components/filebrowser.js'
9 |
10 | const testDrag = (files) => {
11 | const uploadSpy = sinon.spy()
12 | const notDraggingSpy = sinon.spy()
13 | const testActions = {
14 | showUploadDialog: uploadSpy,
15 | setNotDragging: notDraggingSpy,
16 | }
17 | const fileBrowser = shallow( )
18 | fileBrowser.find('.file-browser').first().simulate('drop', {
19 | dataTransfer: {
20 | files: files.map((file) => ({ path: file })),
21 | },
22 | preventDefault: () => undefined,
23 | })
24 | return {uploadSpy, notDraggingSpy}
25 | }
26 |
27 | describe('files drag upload', () => {
28 | it('dispatches proper action for a single file drag upload', () => {
29 | const testFiles = ['filename.png']
30 | const spies = testDrag(testFiles)
31 | expect(spies.uploadSpy.alwaysCalledWithExactly(testFiles)).to.be.true
32 | expect(spies.uploadSpy.calledOnce).to.be.true
33 | expect(spies.notDraggingSpy.calledOnce).to.be.true
34 | })
35 |
36 | it('dispatches proper action for a multiple file drag upload', () => {
37 | const testFiles = ['filename.png', 'another file name.har', 'yesStillAnotherName.wohoo']
38 | const spies = testDrag(testFiles)
39 | expect(spies.uploadSpy.alwaysCalledWithExactly(testFiles)).to.be.true
40 | expect(spies.uploadSpy.calledOnce).to.be.true
41 | expect(spies.notDraggingSpy.calledOnce).to.be.true
42 | })
43 |
44 | it('dispatches proper action for a single folder drag upload', () => {
45 | const testFiles = ['Awesome Folder']
46 | const spies = testDrag(testFiles)
47 | expect(spies.uploadSpy.alwaysCalledWithExactly(testFiles)).to.be.true
48 | expect(spies.uploadSpy.calledOnce).to.be.true
49 | expect(spies.notDraggingSpy.calledOnce).to.be.true
50 | })
51 |
52 | it('dispatches proper action for a multiple folder drag upload', () => {
53 | const testFiles = ['An amazing Folder', 'Another Amazing Folder', 'Still Another More Amazing Folder']
54 | const spies = testDrag(testFiles)
55 | expect(spies.uploadSpy.alwaysCalledWithExactly(testFiles)).to.be.true
56 | expect(spies.uploadSpy.calledOnce).to.be.true
57 | expect(spies.notDraggingSpy.calledOnce).to.be.true
58 | })
59 | })
60 |
61 | /* eslint-enable no-unused-expressions */
62 |
--------------------------------------------------------------------------------
/test/files/usagestats.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import { expect } from 'chai'
4 | import UsageStats from '../../plugins/Files/js/components/usagestats.js'
5 |
6 | describe('files usage stats component', () => {
7 | it('displays a spending bar with four sub-bars', () => {
8 | const component = shallow( )
9 | expect(component.find('.spending-bar')).to.have.length(1)
10 | expect(component.find('.spending-bar').children()).to.have.length(4)
11 | })
12 | it('renders subbars with correct width attributes', () => {
13 | const component = shallow( )
14 | expect(component.find('.download-spending').props().style).to.have.property('width', (5/40)*100+ '%')
15 | expect(component.find('.upload-spending').props().style).to.have.property('width', (5/40)*100+ '%')
16 | expect(component.find('.storage-spending').props().style).to.have.property('width', (20/40)*100+ '%')
17 | expect(component.find('.contract-spending').props().style).to.have.property('width', (10/40)*100+ '%')
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/test/logs/logs.unit.js:
--------------------------------------------------------------------------------
1 | import { readLog, cleanLog } from '../../plugins/Logs/js/logparse.js'
2 | import { expect } from 'chai'
3 | import Path from 'path'
4 | import fs from 'fs'
5 |
6 | describe('log parsing', () => {
7 | const consensusLog = Path.join(SiaAPI.config.siad.datadir, './consensus/consensus.log')
8 | const consensusLogText = cleanLog(fs.readFileSync(consensusLog, 'utf8'))
9 | describe('readLog', () => {
10 | it('reads logs given a log path', () => {
11 | expect(readLog(consensusLog).length > 0).to.equal(true)
12 | expect(readLog(consensusLog)).to.equal(consensusLogText)
13 | })
14 | it('reads logs given a log path and nbytes', () => {
15 | expect(readLog(consensusLog, fs.statSync(consensusLog).size)).to.equal(consensusLogText)
16 | expect(readLog(consensusLog, 2).indexOf('\n')).to.equal(-1)
17 | expect(readLog(consensusLog, 144).indexOf('\n') > -1).to.equal(true)
18 | expect(readLog(consensusLog, 5000000).length).to.equal(consensusLogText.length)
19 | })
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/test/logs/testdir/consensus/consensus.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.127304 persist.go:67: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.731598 persist.go:67: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.101821 persist.go:67: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.455985 persist.go:67: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.929268 persist.go:67: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.977671 persist.go:67: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/logs/testdir/host/storagemanager/storagemanager.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.152282 dependencies.go:106: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.743923 dependencies.go:106: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.108863 dependencies.go:106: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.463429 dependencies.go:106: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.937596 dependencies.go:106: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.984405 dependencies.go:106: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/logs/testdir/miner/miner.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.151602 persist.go:58: STARTUP: Logging has started.
2 | 2016/11/16 22:06:34.151994 blockmanager.go:32: wallet must be unlocked before it can be used
3 | 2016/11/16 22:08:55.741386 persist.go:58: STARTUP: Logging has started.
4 | 2016/11/16 22:09:41.107839 persist.go:58: STARTUP: Logging has started.
5 | 2016/11/16 22:10:57.462957 persist.go:58: STARTUP: Logging has started.
6 | 2016/11/16 22:11:58.937165 persist.go:58: STARTUP: Logging has started.
7 | 2016/11/16 22:13:01.983707 persist.go:58: STARTUP: Logging has started.
8 |
--------------------------------------------------------------------------------
/test/logs/testdir/renter/contractor.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.171719 contractor.go:125: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.751678 contractor.go:125: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.116607 contractor.go:125: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.471657 contractor.go:125: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.944293 contractor.go:125: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.991246 contractor.go:125: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/logs/testdir/renter/hostdb.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.171328 hostdb.go:89: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.750992 hostdb.go:89: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.116451 hostdb.go:89: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.471467 hostdb.go:89: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.944126 hostdb.go:89: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.991080 hostdb.go:89: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/logs/testdir/renter/renter.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.171975 persist.go:409: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.752174 persist.go:409: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.116889 persist.go:409: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.471785 persist.go:409: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.944410 persist.go:409: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.991344 persist.go:409: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/logs/testdir/siad-output.log:
--------------------------------------------------------------------------------
1 | Loading...
2 | (0/7) Loading siad...
3 | (1/7) Loading gateway...
4 | (2/7) Loading consensus...
5 | (3/7) Loading transaction pool...
6 | (4/7) Loading wallet...
7 | (5/7) Loading miner...
8 | (6/7) Loading host...
9 | (7/7) Loading renter...
10 | Finished loading in 0.014237815000000001 seconds
11 | Closing renter...
12 | Closing host...
13 |
--------------------------------------------------------------------------------
/test/logs/testdir/wallet/wallet.log:
--------------------------------------------------------------------------------
1 | 2016/11/16 22:06:34.151368 persist.go:117: STARTUP: Logging has started.
2 | 2016/11/16 22:08:55.740361 persist.go:117: STARTUP: Logging has started.
3 | 2016/11/16 22:09:41.107360 persist.go:117: STARTUP: Logging has started.
4 | 2016/11/16 22:10:57.462813 persist.go:117: STARTUP: Logging has started.
5 | 2016/11/16 22:11:58.937008 persist.go:117: STARTUP: Logging has started.
6 | 2016/11/16 22:13:01.983486 persist.go:117: STARTUP: Logging has started.
7 |
--------------------------------------------------------------------------------
/test/pluginapi.unit.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | /* eslint-disable no-invalid-this */
3 | import { expect } from 'chai'
4 | import sinon from 'sinon'
5 | import { shallow } from 'enzyme'
6 | import proxyquire from 'proxyquire'
7 | import React from 'react'
8 | import DisabledPlugin from '../js/rendererjs/disabledplugin.js'
9 |
10 | let running = true
11 | const mock = {
12 | 'react-dom': {
13 | render: sinon.spy(),
14 | },
15 | 'sia.js': {
16 | isRunning: () => new Promise((resolve) => {
17 | resolve(running)
18 | }),
19 | launch: sinon.spy(),
20 | },
21 | 'electron': {
22 | remote: {
23 | getCurrentWindow: sinon.spy(),
24 | getGlobal: (name) => {
25 | if (name === 'config') {
26 | return {
27 | siad: {
28 | path: 'testpath/siad',
29 | datadir: 'testpath/datadir',
30 | detached: false,
31 | },
32 | }
33 | }
34 | return null
35 | },
36 | dialog: {
37 | showOpenDialog: sinon.spy(),
38 | showSaveDialog: sinon.spy(),
39 | showMessageBox: sinon.spy(),
40 | showErrorBox: sinon.spy(),
41 | },
42 | require: sinon.spy(),
43 | },
44 | },
45 | }
46 |
47 | proxyquire('../js/rendererjs/pluginapi.js', mock)
48 |
49 | describe('plugin API', () => {
50 | it('creates a SiaAPI window object', () => {
51 | expect(window.SiaAPI).to.exist
52 | })
53 | it('does not mount disabled plugin component if siad is running', function(done) {
54 | running = true
55 | this.timeout(10000)
56 | const poll = setInterval(() => {
57 | if (mock['react-dom'].render.called === false) {
58 | clearInterval(poll)
59 | done()
60 | }
61 | }, 50)
62 | })
63 | it('mounts disabled plugin if siad is not running', function(done) {
64 | running = false
65 | this.timeout(10000)
66 | const poll = setInterval(() => {
67 | if (mock['react-dom'].render.called === true) {
68 | clearInterval(poll)
69 | done()
70 | }
71 | }, 50)
72 | })
73 | describe('DisabledPlugin component', () => {
74 | it('calls siajs.launch on click', () => {
75 | const component = shallow( )
76 | component.find('button').first().simulate('click')
77 | expect(mock['sia.js'].launch.called).to.be.true
78 | })
79 | })
80 | })
81 | /* eslint-enable no-unused-expressions */
82 | /* eslint-enable no-invalid-this */
83 |
--------------------------------------------------------------------------------
/test/plugins.unit.js:
--------------------------------------------------------------------------------
1 | import Path from 'path'
2 | import { getPluginName, getOrderedPlugins } from '../js/rendererjs/plugins.js'
3 | import { expect } from 'chai'
4 |
5 | const pluginDir = Path.join(__dirname, '../plugins')
6 | const nPlugins = 6
7 |
8 | describe('plugin system', () => {
9 | describe('getOrderedPlugins', () => {
10 | it('returns an array of the correct length', () => {
11 | expect(getOrderedPlugins(pluginDir, 'Files').size).to.equal(nPlugins)
12 | })
13 | it('has About plugin last', () => {
14 | expect(getPluginName(getOrderedPlugins(pluginDir, 'Files').last())).to.equal('About')
15 | })
16 | it('has home plugin first', () => {
17 | expect(getPluginName(getOrderedPlugins(pluginDir, 'Files').first())).to.equal('Files')
18 | })
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/test/wallet/balanceinfo.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import { expect } from 'chai'
4 | import BalanceInfo from '../../plugins/Wallet/js/components/balanceinfo.js'
5 |
6 | describe('wallet balance info component', () => {
7 | it('renders balance info', () => {
8 | const component= shallow( )
9 | expect(component.find('.balance-info').children()).to.have.length(2)
10 | expect(component.find('.balance-info').children().first().text()).to.contain('Confirmed Balance: 10 SC')
11 | expect(component.find('.balance-info').children().last().text()).to.contain('Unconfirmed Delta: 1 SC')
12 | })
13 | it('renders siafund balance when it is non-zero', () => {
14 | const component = shallow( )
15 | expect(component.find('.balance-info').children()).to.have.length(3)
16 | expect(component.find('.balance-info').children().last().text()).to.contain('Siafund Balance: 1 SF')
17 | })
18 | it('renders a warning when not synced', () => {
19 | const component = shallow( )
20 | expect(component.find('.balance-info').children()).to.have.length(3)
21 | expect(component.find('.balance-info').children().last().text()).to.contain('Your wallet is not synced, balances are not final.')
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/test/wallet/lockscreen.component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import React from 'react'
3 | import { shallow } from 'enzyme'
4 | import { expect } from 'chai'
5 | import RescanDialog from '../../plugins/Wallet/js/components/rescandialog.js'
6 | import LockScreen from '../../plugins/Wallet/js/components/lockscreen.js'
7 | import PasswordPrompt from '../../plugins/Wallet/js/containers/passwordprompt.js'
8 | import UninitializedWalletDialog from '../../plugins/Wallet/js/containers/uninitializedwalletdialog.js'
9 |
10 | const lockedEncryptedScreen = shallow( )
11 | const lockedUnencryptedScreen = shallow( )
12 | const unlockedEncryptedScreen = shallow( )
13 | describe('wallet lock screen component', () => {
14 | it('renders an empty div if the wallet is unlocked and encrypted and not unlocking', () => {
15 | expect(unlockedEncryptedScreen.equals(
)).to.be.true
16 | })
17 | it('renders a password prompt modal if wallet is encrypted and not unlocked or unlocking', () => {
18 | expect(lockedEncryptedScreen.contains( )).to.be.true
19 | })
20 | it('renders a new wallet button if the wallet is locked but not encrypted or unlocking', () => {
21 | expect(lockedUnencryptedScreen.contains( )).to.be.true
22 | })
23 | it('renders a passwordprompt if the wallet is unlocking', () => {
24 | expect(shallow( ).contains( )).to.be.true
25 | })
26 | it('renders a rescandialog if the wallet is rescanning', () => {
27 | expect(shallow( ).contains( )).to.be.true
28 | expect(shallow( ).contains( )).to.be.false
29 | expect(shallow( ).contains( )).to.be.false
30 | })
31 | })
32 | /* eslint-enable no-unused-expressions */
33 |
--------------------------------------------------------------------------------
/test/wallet/passwordprompt.component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import React from 'react'
3 | import { shallow } from 'enzyme'
4 | import { expect } from 'chai'
5 | import { spy } from 'sinon'
6 | import PasswordPrompt from '../../plugins/Wallet/js/components/passwordprompt.js'
7 |
8 | const testActions = {
9 | handlePasswordChange: spy(),
10 | unlockWallet: spy(),
11 | }
12 |
13 | const passwordpromptComponent = shallow( )
14 |
15 | describe('wallet password prompt component', () => {
16 | it('renders a password input', () => {
17 | expect(passwordpromptComponent.find('input .password-input')).to.have.length(1)
18 | expect(passwordpromptComponent.find('input .password-input').first().prop('type')).to.equal('password')
19 | })
20 | it('renders a wallet unlock error', () => {
21 | expect(passwordpromptComponent.find('.password-prompt-error').first().text()).to.equal('')
22 | const erroredComponent = shallow( )
23 | expect(erroredComponent.find('.password-prompt-error').first().text()).to.equal('testerror')
24 | })
25 | it('calls unlockWallet with password on unlock button click', () => {
26 | passwordpromptComponent.find('.unlock-button').first().simulate('click')
27 | expect(testActions.unlockWallet.calledWith('testpw')).to.be.true
28 | })
29 | it('calls handlePasswordChange with new password on password input change', () => {
30 | passwordpromptComponent.find('.password-input').first().simulate('change', {target: { value: 'test-changed'}})
31 | expect(testActions.handlePasswordChange.calledWith('test-changed')).to.be.true
32 | })
33 | })
34 | /* eslint-enable no-unused-expressions */
35 |
--------------------------------------------------------------------------------