├── .babelrc
├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .storybook
├── config.js
└── webpack.config.js
├── .yarnclean
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTOR.md
├── DEVELOPER.md
├── Dockerfile
├── LICENSE.md
├── app
├── .eslintrc
├── app.html
├── app.icns
├── backend.js
├── dist
│ ├── 0b2cc683696912226d490919898396b0.svg
│ ├── 1fcaa4ac54baaca933141dfc7652c0d6.woff
│ ├── 974aca06c9be1fc5804191117dad73fd.ttf
│ ├── bf10b22424dd12059cb140e89718e334.eot
│ ├── renderer.prod.js
│ ├── ss-pika.eot
│ ├── ss-pika.svg
│ ├── ss-pika.ttf
│ ├── ss-pika.woff
│ ├── style.css
│ └── style.css.map
├── main.dev.js
├── main.prod.js
├── menu.js
├── package.json
├── touchbar.js
└── yarn.lock
├── build
├── icon.icns
├── icon.ico
└── icon.png
├── codecov.yml
├── dll
├── renderer.dev.dll.js
└── renderer.json
├── docs
└── COMMON_PITFALLS.md
├── icongen
├── internals
├── mocks
│ └── fileMock.js
└── scripts
│ ├── CheckBuiltsExist.js
│ └── CheckNodeEnv.js
├── lib
├── .eslintrc
├── actions
│ ├── __tests__
│ │ ├── dataset.test.js
│ │ ├── peers.test.js
│ │ └── session.test.js
│ ├── app.js
│ ├── body.js
│ ├── console.js
│ ├── dataset.js
│ ├── editor.js
│ ├── history.js
│ ├── layout.js
│ ├── localModels.js
│ ├── profiles.js
│ ├── registry.js
│ ├── session.js
│ └── transfers.js
├── app.global.scss
├── assets
│ ├── ss-pika.eot
│ ├── ss-pika.svg
│ ├── ss-pika.ttf
│ ├── ss-pika.woff
│ └── style.css
├── components
│ ├── App.js
│ ├── AppDrag.js
│ ├── AppLoading.js
│ ├── AppUnavail.js
│ ├── ChoosePeername.js
│ ├── CodeViewer.js
│ ├── Collection.js
│ ├── DatasetName.js
│ ├── Datasets.js
│ ├── Datestamp.js
│ ├── ExternalLink.electron.js
│ ├── ExternalLink.web.js
│ ├── HandsonTable.js
│ ├── Hash.js
│ ├── Header.js
│ ├── Json.js
│ ├── List.js
│ ├── Menu.js
│ ├── Network.js
│ ├── Online.js
│ ├── PageTitle.js
│ ├── QuitApp.electron.js
│ ├── QuitApp.web.js
│ ├── ReadOnly.js
│ ├── SearchResults.js
│ ├── StatsBar.js
│ ├── StatsLine.js
│ ├── StatsSection.js
│ ├── TabPanel.js
│ ├── TagList.js
│ ├── Welcome.js
│ ├── chrome
│ │ ├── Button.js
│ │ ├── DownloadBar.js
│ │ ├── Dropdown.js
│ │ ├── DropdownMenu.js
│ │ ├── LoadMore.js
│ │ ├── NavLinks.js
│ │ ├── Spinner.js
│ │ └── TopBar.js
│ ├── dataset
│ │ ├── Body.js
│ │ ├── BodyReadOnly.js
│ │ ├── Commits.js
│ │ ├── Dataset.js
│ │ ├── DatasetButtonGroup.js
│ │ ├── DatasetHeader.js
│ │ ├── DatasetRouter.js
│ │ ├── DeleteDataset.js
│ │ ├── ExportDataset.js
│ │ ├── Meta.js
│ │ ├── MetaSummary.js
│ │ ├── MetadataViewer.js
│ │ ├── Overview.js
│ │ ├── RenameDataset.js
│ │ ├── Render.js
│ │ ├── Structure.js
│ │ ├── StructureViewer.js
│ │ ├── Transform.js
│ │ └── Viz.js
│ ├── editor
│ │ ├── EditBody.js
│ │ ├── EditMeta.js
│ │ ├── EditStructure.js
│ │ ├── EditTransform.js
│ │ ├── EditViz.js
│ │ ├── Editor.js
│ │ ├── Overview.js
│ │ ├── Results.js
│ │ └── SectionPicker.js
│ ├── form
│ │ ├── DateInput.js
│ │ ├── DropFile.js
│ │ ├── KeyValueInput.js
│ │ ├── LanguageInput.js
│ │ ├── MetadataForm.js
│ │ ├── MonthCalendar.js
│ │ ├── ProfileForm.js
│ │ ├── RadioInput.js
│ │ ├── SaveMetadataForm.js
│ │ ├── SearchBar.js
│ │ ├── StructureForm.js
│ │ ├── StructureFormatConfig.js
│ │ ├── TagInput.js
│ │ ├── TextInput.js
│ │ ├── UrlInput.js
│ │ ├── ValidCheck.js
│ │ ├── ValidCitationsInput.js
│ │ ├── ValidContributorsInput.js
│ │ ├── ValidDateTimeInput.js
│ │ ├── ValidInput.js
│ │ ├── ValidLicenseInput.js
│ │ ├── ValidPeriodicityInput.js
│ │ ├── ValidSelect.js
│ │ ├── ValidTextarea.js
│ │ └── ViewModeButton.js
│ ├── item
│ │ ├── CitationItem.js
│ │ ├── CommitItem.js
│ │ ├── ContributorItem.js
│ │ ├── DatasetItem.js
│ │ ├── FieldItem.js
│ │ ├── KeyValueItem.js
│ │ ├── ProfileItem.js
│ │ ├── SearchItem.js
│ │ ├── StatItem.js
│ │ └── TagItem.js
│ ├── profile
│ │ ├── NoProfile.js
│ │ ├── Profile.js
│ │ ├── ProfileBar.js
│ │ ├── ProfileEditor.js
│ │ ├── ProfileHeader.js
│ │ └── ProfilePhoto.js
│ ├── schema
│ │ ├── EditSchema.js
│ │ └── schema.js
│ └── stylesheet
│ │ ├── Stylesheet.js
│ │ └── Swatch.js
├── constants
│ ├── app.js
│ ├── body.js
│ ├── cafs.js
│ ├── console.js
│ ├── dataset.js
│ ├── editor.js
│ ├── history.js
│ ├── layout.js
│ ├── profiles.js
│ ├── registry.js
│ ├── session.js
│ └── transfers.js
├── containers
│ ├── App.js
│ ├── ChoosePeername.js
│ ├── Collection.js
│ ├── CommitItem.js
│ ├── Commits.js
│ ├── Dataset.js
│ ├── Editor.js
│ ├── Network.js
│ ├── Profile.js
│ ├── ProfileEditor.js
│ ├── Root.js
│ ├── SearchResults.js
│ ├── TopBar.js
│ ├── Transform.js
│ ├── Viz.js
│ └── Welcome.js
├── history.js
├── index.js
├── middleware
│ ├── api.js
│ ├── caf.js
│ ├── extractBody.js
│ └── localModels.js
├── monaco.js
├── propTypes
│ ├── bbox.js
│ ├── commitProps.js
│ ├── datasetRefProps.js
│ ├── metaProps.js
│ ├── palette.js
│ ├── profile.js
│ └── schema.js
├── qri
│ └── generate.js
├── reducers
│ ├── __tests__
│ │ ├── app.test.js
│ │ ├── body.test.js
│ │ ├── editor.test.js
│ │ ├── layout.test.js
│ │ ├── locals.test.js
│ │ ├── session.test.js
│ │ └── transfers.test.js
│ ├── app.js
│ ├── body.js
│ ├── cafs.js
│ ├── editor.js
│ ├── index.js
│ ├── layout.js
│ ├── locals.js
│ ├── paginate.js
│ ├── pagination.js
│ ├── session.js
│ └── transfers.js
├── routes.js
├── schemas.js
├── scss
│ ├── _buttons.scss
│ ├── _chrome.scss
│ ├── _dataset.scss
│ ├── _dropdown.scss
│ ├── _editor.scss
│ ├── _form.scss
│ ├── _forms.scss
│ ├── _handsontable.scss
│ ├── _hotable.scss
│ ├── _item.scss
│ ├── _mixins.scss
│ ├── _modal.scss
│ ├── _normalize.scss
│ ├── _profile.scss
│ ├── _schema.scss
│ ├── _site.scss
│ ├── _stylesheet.scss
│ ├── _type.scss
│ ├── _variables.scss
│ ├── mixins
│ │ ├── _border-radius.scss
│ │ ├── _breakpoints.scss
│ │ ├── _buttons.scss
│ │ ├── _forms.scss
│ │ ├── _gradients.scss
│ │ ├── _hover.scss
│ │ ├── _lists.scss
│ │ ├── _nav-divider.scss
│ │ ├── _tab-focus.scss
│ │ └── _table-row.scss
│ └── style.scss
├── selectors
│ ├── __tests__
│ │ ├── dataset.test.js
│ │ ├── layout.test.js
│ │ ├── peers.test.js
│ │ ├── session.test.js
│ │ └── transfers.test.js
│ ├── app.js
│ ├── cafs.js
│ ├── dataset.js
│ ├── history.js
│ ├── layout.js
│ ├── pagination.js
│ ├── profiles.js
│ ├── registry.js
│ ├── search.js
│ ├── session.js
│ ├── stats.js
│ ├── transfers.js
│ ├── viewConfig.js
│ └── viewData.js
├── store
│ ├── configureStore.dev.js
│ ├── configureStore.js
│ └── configureStore.prod.js
└── utils
│ ├── __tests__
│ ├── .eslintrc
│ └── ref.test.js
│ ├── date.js
│ ├── defaultColumnWidths.js
│ ├── filesize.js
│ ├── links.js
│ ├── localStore.js
│ ├── ref.js
│ └── reflect.js
├── package.json
├── readme.md
├── release
└── github
│ └── latest-mac.json
├── resources
├── icons
│ ├── 1024x1024.png
│ ├── 128x128.png
│ ├── 16x16.png
│ ├── 24x24.png
│ ├── 256x256.png
│ ├── 32x32.png
│ ├── 48x48.png
│ ├── 512x512.png
│ ├── 64x64.png
│ └── 96x96.png
├── index.tpl.html
├── install.sh
├── ipfs_config
└── qri
├── stories
└── index.js
├── test.js
├── version.js
├── webpack.config.base.js
├── webpack.config.eslint.js
├── webpack.config.main.prod.js
├── webpack.config.readonly.dev.js
├── webpack.config.readonly.prod.js
├── webpack.config.renderer.dev.dll.js
├── webpack.config.renderer.dev.js
├── webpack.config.renderer.prod.js
├── webpack.config.webapp.dev.js
├── webpack.config.webapp.prod.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "targets": { "node": 10 },
5 | "useBuiltIns": 'usage',
6 | "corejs": "2.0.0"
7 | }],
8 | "@babel/preset-react"
9 | ],
10 | "plugins": [
11 | "add-module-exports",
12 | "dynamic-import-webpack",
13 | "@babel/plugin-syntax-dynamic-import"
14 | ],
15 | "env": {
16 | "production": {
17 | "plugins": [
18 | "@babel/plugin-proposal-class-properties",
19 | "@babel/plugin-transform-classes"
20 | ]
21 | },
22 | "development": {
23 | "plugins": [
24 | "@babel/plugin-proposal-class-properties",
25 | "@babel/plugin-transform-classes"
26 | ]
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: ~/frontend
5 | docker:
6 | - image: circleci/node:10.10.0
7 | steps:
8 | - checkout
9 | - restore_cache:
10 | key: dependency-cache-{{ checksum "yarn.lock" }}
11 | - run:
12 | name: yarn install
13 | command: yarn install
14 | - save_cache:
15 | key: dependency-cache-{{ checksum "yarn.lock" }}
16 | paths:
17 | - ./node_modules
18 | - run:
19 | name: lint with standard.js
20 | command: yarn lint
21 | - run:
22 | name: test
23 | command: yarn test
24 | - run:
25 | name: upload to codecov
26 | command: yarn codecov
27 | - store_artifacts:
28 | path: test-results.xml
29 | prefix: tests
30 | - store_artifacts:
31 | path: coverage
32 | prefix: coverage
33 | - store_test_results:
34 | path: test-results.xml
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /server.js
2 | /src/routes.js
3 | /css
4 | /dist
5 | /scripts
6 | /src/scss/*
7 | /src/ace
8 | /src/__mocks__
9 | /src/utils/__mocks__
10 | /src/components/__tests__
11 | /src//selectors/__tests__
12 |
13 | # Logs
14 | logs
15 | *.log
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 |
28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (http://nodejs.org/api/addons.html)
35 | build/Release
36 | .eslintcache
37 |
38 | # Dependency directory
39 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
40 | node_modules
41 | app/node_modules
42 |
43 | # OSX
44 | .DS_Store
45 |
46 | # flow-typed
47 | flow-typed/npm/*
48 | !flow-typed/npm/module_vx.x.x.js
49 |
50 | # App packaged
51 | release
52 | app/main.prod.js
53 | app/main.prod.js.map
54 | app/renderer.prod.js
55 | app/renderer.prod.js.map
56 | app/style.css
57 | app/style.css.map
58 | dist
59 | dll
60 | main.js
61 | main.js.map
62 |
63 | .idea
64 | npm-debug.log.*
65 | __snapshots__
66 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "parserOptions": {
4 | "sourceType": "module",
5 | "allowImportExportEverywhere": true
6 | },
7 | "extends": "standard",
8 | "env": {
9 | "browser": true,
10 | "node": true
11 | },
12 | "rules": {
13 | "react/jsx-uses-react": 2,
14 | "react/jsx-uses-vars": 2,
15 | "react/react-in-jsx-scope": 2
16 | },
17 | "plugins": [
18 | "import",
19 | "promise",
20 | "compat",
21 | "react"
22 | ],
23 | "settings": {
24 | "import/resolver": {
25 | "webpack": {
26 | "config": "webpack.config.eslint.js"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Platform**
11 | How were you accessing the qri frontend?
12 | [ ] app.qri.io
13 | [ ] electron app
14 | [ ] dev webapp
15 |
16 | **Version**
17 | What version of the Qri frontend are you using?
18 |
19 | **Describe the bug**
20 | A clear and concise description of what the bug is.
21 |
22 | **To Reproduce**
23 | Steps to reproduce the behavior:
24 | 1. Go to '...'
25 | 2. Click on '....'
26 | 3. Scroll down to '....'
27 | 4. See error
28 |
29 | **Expected behavior**
30 | A clear and concise description of what you expected to happen.
31 |
32 | **Screenshots**
33 | If applicable, add screenshots to help explain your problem.
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: 'Feature Request:'
5 | labels: discussion, enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Before writing this feature request I have:**
11 | [ ] checked through the [issue queue](https://github.com/qri-io/frontend/issues) to see if this feature has already been requested
12 | [ ] checked through the [rfcs] (https://github.com/qri-io/rfcs) to see if this feature has already been requested
13 |
14 | **Is your feature request related to a problem? Please describe.**
15 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
16 |
17 | **Describe the solution you'd like**
18 | A clear and concise description of what you want to happen.
19 |
20 | **Describe alternatives you've considered**
21 | A clear and concise description of any alternative solutions or features you've considered.
22 |
23 | **Additional context**
24 | Add any other context or screenshots about the feature request here.
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | $HOME
2 | ~
3 | .DS_Store
4 | .sublime-project
5 | .sublime-workspace
6 | node_modules
7 | npm-debug.log
8 |
9 | # ignore dist dir
10 | dist
11 |
12 | # Logs
13 | logs
14 | *.log
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 |
27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (http://nodejs.org/api/addons.html)
34 | build/Release
35 | .eslintcache
36 |
37 | # Dependency directory
38 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
39 | node_modules
40 | app/node_modules
41 |
42 | # OSX
43 | .DS_Store
44 |
45 | # flow-typed
46 | flow-typed/npm/*
47 | !flow-typed/npm/module_vx.x.x.js
48 |
49 | # App packaged
50 | release
51 | app/main.prod.js
52 | app/main.prod.js.map
53 | app/renderer.prod.js
54 | app/renderer.prod.js.map
55 | app/style.css
56 | app/style.css.map
57 | dist/
58 | dll
59 | main.js
60 | main.js.map
61 |
62 | .idea
63 | npm-debug.log.*
64 |
65 | resources/qri
66 | resources/mac/
67 | resources/win/
68 | resources/linux/
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/react';
2 |
3 | function loadStories() {
4 | require('../stories/index.js');
5 | // You can require as many stories as you need.
6 | }
7 |
8 | configure(loadStories, module);
--------------------------------------------------------------------------------
/.yarnclean:
--------------------------------------------------------------------------------
1 | # test directories
2 | __tests__
3 | node_modules/*/test
4 | node_modules/*/tests
5 | powered-test
6 |
7 | # asset directories
8 | docs
9 | doc
10 | website
11 | images
12 |
13 | # examples
14 | example
15 | examples
16 |
17 | # code coverage directories
18 | coverage
19 | .nyc_output
20 |
21 | # build scripts
22 | Makefile
23 | Gulpfile.js
24 | Gruntfile.js
25 |
26 | # configs
27 | .tern-project
28 | .gitattributes
29 | .editorconfig
30 | .*ignore
31 | .eslintrc
32 | .jshintrc
33 | .flowconfig
34 | .documentup.json
35 | .yarn-metadata.json
36 |
37 | # misc
38 | *.gz
39 | *.md
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | Online or off, Qri is a harrassment-free environment for everyone, regardless of gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, age or religion or technical skill level. We do not tolerate harassment of participants in any form.
4 |
5 | Harassment includes verbal comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, age, religion, sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, and unwelcome sexual attention. Participants asked to stop any harassing behavior are expected to comply immediately.
6 |
7 | If a participant engages in harassing behaviour, the organizers may take any action they deem appropriate, including warning the offender or expulsion from events and online forums.
8 |
9 | If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of the organizing team immediately.
10 |
11 | At offline events, organizers will identify themselves, and will help participants contact venue security or local law enforcement, provide escorts, or otherwise assist those experiencing harassment to feel safe for the duration of the event. We value your participation!
12 |
13 | This document is based on a similar code from [EDGI](https://envirodatagov.org/) and [Civic Tech Toronto](http://civictech.ca/about-us/), itself derived from the [Recurse Center’s Social Rules](https://www.recurse.com/manual#sec-environment), and the [anti-harassment policy from the Geek Feminism Wiki](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:9.1.0
2 |
3 | RUN mkdir -p ~/frontend
4 | ADD . ~/frontend
5 | # RUN yarn add
6 | WORKDIR ~/frontend
7 | RUN yarn test
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Qri
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.
--------------------------------------------------------------------------------
/app/.eslintrc:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "standard"
3 | };
--------------------------------------------------------------------------------
/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | qri
6 |
25 |
26 |
27 |
28 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/app.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/app.icns
--------------------------------------------------------------------------------
/app/dist/1fcaa4ac54baaca933141dfc7652c0d6.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/1fcaa4ac54baaca933141dfc7652c0d6.woff
--------------------------------------------------------------------------------
/app/dist/974aca06c9be1fc5804191117dad73fd.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/974aca06c9be1fc5804191117dad73fd.ttf
--------------------------------------------------------------------------------
/app/dist/bf10b22424dd12059cb140e89718e334.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/bf10b22424dd12059cb140e89718e334.eot
--------------------------------------------------------------------------------
/app/dist/ss-pika.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/ss-pika.eot
--------------------------------------------------------------------------------
/app/dist/ss-pika.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/ss-pika.ttf
--------------------------------------------------------------------------------
/app/dist/ss-pika.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/app/dist/ss-pika.woff
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qri-webapp",
3 | "productName": "qri",
4 | "version": "0.7.2-dev",
5 | "description": "qri (\"query\") frontend application",
6 | "main": "./main.prod.js",
7 | "author": {
8 | "name": "Brendan O'Brien",
9 | "email": "sparkle_pony_2000@qri.io",
10 | "url": "github.com/qri-io"
11 | },
12 | "scripts": {
13 | "postinstall": "npm rebuild --runtime=electron --target=1.6.6 --disturl=https://atom.io/download/atom-shell --build-from-source"
14 | },
15 | "license": "MIT",
16 | "dependencies": {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/touchbar.js:
--------------------------------------------------------------------------------
1 | import { TouchBar } from 'electron'
2 | const {TouchBarLabel, TouchBarButton, TouchBarSpacer} = TouchBar
3 |
4 | let spinning = false
5 |
6 | // Reel labels
7 | const reel1 = new TouchBarLabel()
8 | const reel2 = new TouchBarLabel()
9 | const reel3 = new TouchBarLabel()
10 |
11 | // Spin result label
12 | const result = new TouchBarLabel()
13 |
14 | // Spin button
15 | const spin = new TouchBarButton({
16 | label: '🎰 Spin',
17 | backgroundColor: '#7851A9',
18 | click: () => {
19 | // Ignore clicks if already spinning
20 | if (spinning) {
21 | return
22 | }
23 |
24 | spinning = true
25 | result.label = ''
26 |
27 | let timeout = 10
28 | const spinLength = 4 * 1000 // 4 seconds
29 | const startTime = Date.now()
30 |
31 | const spinReels = () => {
32 | updateReels()
33 |
34 | if ((Date.now() - startTime) >= spinLength) {
35 | finishSpin()
36 | } else {
37 | // Slow down a bit on each spin
38 | timeout *= 1.1
39 | setTimeout(spinReels, timeout)
40 | }
41 | }
42 |
43 | spinReels()
44 | }
45 | })
46 |
47 | const getRandomValue = () => {
48 | const values = ['🍒', '💎', '7️⃣', '🍊', '🔔', '⭐', '🍇', '🍀']
49 | return values[Math.floor(Math.random() * values.length)]
50 | }
51 |
52 | const updateReels = () => {
53 | reel1.label = getRandomValue()
54 | reel2.label = getRandomValue()
55 | reel3.label = getRandomValue()
56 | }
57 |
58 | const finishSpin = () => {
59 | const uniqueValues = new Set([reel1.label, reel2.label, reel3.label]).size
60 | if (uniqueValues === 1) {
61 | // All 3 values are the same
62 | result.label = '💰 Jackpot!'
63 | result.textColor = '#FDFF00'
64 | } else if (uniqueValues === 2) {
65 | // 2 values are the same
66 | result.label = '😍 Winner!'
67 | result.textColor = '#FDFF00'
68 | } else {
69 | // No values are the same
70 | result.label = '🙁 Spin Again'
71 | result.textColor = null
72 | }
73 | spinning = false
74 | }
75 |
76 | const touchBar = new TouchBar([
77 | spin,
78 | new TouchBarSpacer({size: 'large'}),
79 | reel1,
80 | new TouchBarSpacer({size: 'small'}),
81 | reel2,
82 | new TouchBarSpacer({size: 'small'}),
83 | reel3,
84 | new TouchBarSpacer({size: 'large'}),
85 | result
86 | ])
87 |
88 | export default touchBar
89 |
--------------------------------------------------------------------------------
/app/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/build/icon.ico
--------------------------------------------------------------------------------
/build/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/build/icon.png
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | ci:
3 | - "ci/circle-ci"
4 | notify:
5 | require_ci_to_pass: no
6 | after_n_builds: 2
7 | coverage:
8 | range: "80...100"
9 | comment: off
--------------------------------------------------------------------------------
/icongen:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # small script to generate icons for electron app
4 | # usage:
5 | # ./icongen /path/to/image
6 |
7 | # get filepath from arguments
8 | filepath=$1
9 |
10 | # color:
11 | RED='\033[0;31m'
12 | NC='\033[0m' # No Color
13 | GREEN='\033[0;32m'
14 |
15 | # run scripts
16 | err=$(sips -s format ico -z 256 256 $filepath --out ./build/icon.ico 2>&1 >/dev/null)
17 |
18 | if [[ $err ]]; then
19 | echo -e "${RED}${err}${NC}"
20 | else
21 | echo -e "${GREEN}Created icon.ico${NC}"
22 | fi
23 |
24 | err=$(sips -s format png -z 256 256 $filepath --out ./build/icon.png 2>&1 >/dev/null)
25 |
26 | if [[ $err ]]; then
27 | echo -e "${RED}${err}${NC}"
28 | else
29 | echo -e "${GREEN}Created icon.png${NC}"
30 | fi
31 |
32 | err=$(sips -s format icns -z 512 512 $filepath --out ./build/icon.icns 2>&1 >/dev/null)
33 |
34 | if [[ $err ]]; then
35 | echo -e "${RED}${err}${NC}"
36 | else
37 | echo -e "${GREEN}Created icon.icns${NC}"
38 | fi
--------------------------------------------------------------------------------
/internals/mocks/fileMock.js:
--------------------------------------------------------------------------------
1 | export default 'test-file-stub';
2 |
--------------------------------------------------------------------------------
/internals/scripts/CheckBuiltsExist.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // Check if the renderer and main bundles are built
3 | import path from 'path';
4 | import chalk from 'chalk';
5 | import fs from 'fs';
6 |
7 | function CheckBuildsExist() {
8 | const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js');
9 | const rendererPath = path.join(__dirname, '..', '..', 'app', 'dist', 'renderer.prod.js');
10 |
11 | if (!fs.existsSync(mainPath)) {
12 | throw new Error(chalk.whiteBright.bgRed.bold(
13 | 'The main process is not built yet. Build it by running "npm run build-main"'
14 | ));
15 | }
16 |
17 | if (!fs.existsSync(rendererPath)) {
18 | throw new Error(chalk.whiteBright.bgRed.bold(
19 | 'The renderer process is not built yet. Build it by running "npm run build-renderer"'
20 | ));
21 | }
22 | }
23 |
24 | CheckBuildsExist();
25 |
--------------------------------------------------------------------------------
/internals/scripts/CheckNodeEnv.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import chalk from 'chalk'
3 |
4 | export default function CheckNodeEnv (expectedEnv) {
5 | if (!expectedEnv) {
6 | throw new Error('"expectedEnv" not set')
7 | }
8 |
9 | if (process.env.NODE_ENV !== expectedEnv) {
10 | console.log(chalk.whiteBright.bgRed.bold(
11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
12 | ))
13 | process.exit(2)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/.eslintrc:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "standard"
3 | };
--------------------------------------------------------------------------------
/lib/actions/__tests__/session.test.js:
--------------------------------------------------------------------------------
1 | /* globals describe, it, expect, afterEach */
2 | import configureMockStore from 'redux-mock-store'
3 | import CustomMiddleware from '../../middleware/api'
4 | import { loadSessionProfile } from '../session'
5 | import { SESSION_PROFILE_REQUEST, SESSION_PROFILE_SUCCESS } from '../../constants/session'
6 | import fetchMock from 'fetch-mock'
7 | import thunk from 'redux-thunk'
8 |
9 | const middlewares = [CustomMiddleware, thunk]
10 | const mockStore = configureMockStore(middlewares)
11 |
12 | const body = {
13 | 'data': {
14 | 'id': 'profileid',
15 | 'created': '0001-01-01T00:00:00Z',
16 | 'updated': '0001-01-01T00:00:00Z',
17 | 'peername': 'peer',
18 | 'type': 'user',
19 | 'email': '',
20 | 'name': '',
21 | 'description': '',
22 | 'homeUrl': '',
23 | 'color': '',
24 | 'thumb': '/',
25 | 'profile': '/',
26 | 'poster': '/',
27 | 'twitter': ''
28 | },
29 | 'meta': {
30 | 'code': 200
31 | }
32 | }
33 |
34 | const response = {
35 | 'entities': {
36 | 'profiles': {
37 | 'profileid': {
38 | 'color': '',
39 | 'created': '0001-01-01T00:00:00Z',
40 | 'description': '',
41 | 'email': '',
42 | 'homeUrl': '',
43 | 'id': 'profileid',
44 | 'name': '',
45 | 'peername': 'peer',
46 | 'poster': '/',
47 | 'profile': '/',
48 | 'thumb': '/',
49 | 'twitter': '',
50 | 'type': 'user',
51 | 'updated': '0001-01-01T00:00:00Z'
52 | }
53 | }
54 | },
55 | 'result': {
56 | 'data': 'profileid',
57 | 'meta': {
58 | 'code': 200
59 | }
60 | }
61 | }
62 |
63 | describe('Session Actions', () => {
64 | afterEach(() => {
65 | fetchMock.reset()
66 | fetchMock.restore()
67 | })
68 |
69 | it('creates SESSION_PROFILE_SUCCESS when fetching profile has been done', () => {
70 | // mock for endpoint `/me`
71 | fetchMock
72 | .getOnce(/\/me$/, { body })
73 |
74 | const expectedActions = [
75 | { type: SESSION_PROFILE_REQUEST },
76 | { type: SESSION_PROFILE_SUCCESS, response }
77 | ]
78 | const store = mockStore({})
79 |
80 | return store.dispatch(loadSessionProfile()).then(() => {
81 | // return of async actions
82 | expect(store.getActions()).toEqual(expectedActions)
83 | })
84 | })
85 | })
86 |
--------------------------------------------------------------------------------
/lib/actions/body.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from '../middleware/api'
2 | import Schemas from '../schemas'
3 |
4 | import {
5 | CLEAR_BODY,
6 | BODY_REQUEST,
7 | BODY_SUCCESS,
8 | BODY_FAILURE
9 | } from '../constants/body'
10 |
11 | export function clearBody () {
12 | return {
13 | type: CLEAR_BODY
14 | }
15 | }
16 |
17 | export function loadBody (peername, name, path, bodypath, offset = 0, limit = 100) {
18 | return (dispatch, getState) => {
19 | return dispatch(fetchBody(peername, name, path, bodypath, offset, limit))
20 | }
21 | }
22 |
23 | export function fetchBody (peername, name, path, bodypath, offset = 0, limit = 100) {
24 | var endpoint
25 | if (peername === '' || name === '') {
26 | endpoint = `at${path}`
27 | } else {
28 | endpoint = `${peername}/${name}/at${path}`
29 | }
30 | return {
31 | [CALL_API]: {
32 | types: [BODY_REQUEST, BODY_SUCCESS, BODY_FAILURE],
33 | endpoint: `/body/${endpoint}`,
34 | schema: Schemas.STRUCTURED_DATA,
35 | data: { offset, limit, format: 'json' }
36 | },
37 | path,
38 | bodypath,
39 | pageSize: limit
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/actions/console.js:
--------------------------------------------------------------------------------
1 | import {
2 | CONSOLE_SET_CHART_OPTIONS,
3 | CONSOLE_RESET_CHART_OPTIONS,
4 | CONSOLE_SET_DATA_LOADING
5 | } from '../constants/console'
6 |
7 | export function setChartOptions (options) {
8 | return {
9 | type: CONSOLE_SET_CHART_OPTIONS,
10 | value: options
11 | }
12 | }
13 |
14 | export function resetChartOptions () {
15 | return {
16 | type: CONSOLE_RESET_CHART_OPTIONS
17 | }
18 | }
19 |
20 | export function setLoadingData (loading) {
21 | return {
22 | type: CONSOLE_SET_DATA_LOADING,
23 | value: loading
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/actions/history.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from '../middleware/api'
2 | import Schemas from '../schemas'
3 |
4 | import {
5 | HISTORY_REQUEST,
6 | HISTORY_SUCCESS,
7 | HISTORY_FAILURE
8 | } from '../constants/history'
9 |
10 | export function fetchHistoryByName (peername, name, page = 1, pageSize = 30) {
11 | return {
12 | [CALL_API]: {
13 | types: [HISTORY_REQUEST, HISTORY_SUCCESS, HISTORY_FAILURE],
14 | endpoint: `/history/${peername}/${name}`,
15 | data: { page, pageSize },
16 | schema: Schemas.DATASET_ARRAY
17 | },
18 | page,
19 | pageSize,
20 | path: `/${peername}/${name}`
21 | }
22 | }
23 |
24 | export function loadHistoryByName (peername, name, page = 1, pageSize = 30) {
25 | return (dispatch, getState) => {
26 | // TODO - check pagination
27 | return dispatch(fetchHistoryByName(peername, name, page, pageSize))
28 | }
29 | }
30 |
31 | export function fetchHistory (path, page = 1, pageSize = 30) {
32 | return {
33 | [CALL_API]: {
34 | types: [HISTORY_REQUEST, HISTORY_SUCCESS, HISTORY_FAILURE],
35 | endpoint: `/history/at${path}`,
36 | data: { page, pageSize },
37 | schema: Schemas.DATASET_ARRAY
38 | },
39 | page,
40 | pageSize,
41 | path
42 | }
43 | }
44 |
45 | export function loadHistory (path, page = 1, pageSize = 30) {
46 | return (dispatch, getState) => {
47 | // TODO - check pagination
48 | return dispatch(fetchHistory(path, page, pageSize))
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/actions/layout.js:
--------------------------------------------------------------------------------
1 | import {
2 | LAYOUT_RESIZE
3 | } from '../constants/layout'
4 |
5 | export function layoutResize (width, height) {
6 | return {
7 | type: LAYOUT_RESIZE,
8 | stage: { width, height }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/actions/localModels.js:
--------------------------------------------------------------------------------
1 | import { LOCAL_ACTION, NEW_MODEL, UPDATE_MODEL, EDIT_MODEL, CLEAR_MODEL } from '../middleware/localModels'
2 |
3 | export function newLocalModel (schema, type, attributes = {}) {
4 | return {
5 | [LOCAL_ACTION]: {
6 | method: NEW_MODEL,
7 | type,
8 | schema,
9 | attributes
10 | }
11 | }
12 | }
13 |
14 | export function removeLocalModel (schema, type, id) {
15 | return {
16 | [LOCAL_ACTION]: {
17 | method: CLEAR_MODEL,
18 | type,
19 | schema,
20 | id
21 | }
22 | }
23 | }
24 |
25 | export function updateLocalModel (schema, type, attributes) {
26 | return {
27 | [LOCAL_ACTION]: {
28 | method: UPDATE_MODEL,
29 | type,
30 | schema,
31 | attributes
32 | }
33 | }
34 | }
35 |
36 | export function editModel (schema, type, attributes) {
37 | return {
38 | [LOCAL_ACTION]: {
39 | method: EDIT_MODEL,
40 | type,
41 | schema,
42 | attributes
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/actions/profiles.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from '../middleware/api'
2 | import Schemas from '../schemas'
3 | import { selectProfileById, selectProfileByName } from '../selectors/profiles'
4 |
5 | import {
6 | PROFILE_BY_ID_REQUEST,
7 | PROFILE_BY_ID_SUCCESS,
8 | PROFILE_BY_ID_FAILURE,
9 | PROFILE_BY_NAME_REQUEST,
10 | PROFILE_BY_NAME_SUCCESS,
11 | PROFILE_BY_NAME_FAILURE,
12 |
13 | PROFILES_REQUEST,
14 | PROFILES_SUCCESS,
15 | PROFILES_FAILURE
16 | } from '../constants/profiles'
17 |
18 | export function fetchProfiles (page = 1, pageSize = 30) {
19 | return {
20 | [CALL_API]: {
21 | types: [PROFILES_REQUEST, PROFILES_SUCCESS, PROFILES_FAILURE],
22 | endpoint: '/peers',
23 | data: { page, pageSize },
24 | schema: Schemas.PROFILE_ARRAY
25 | },
26 | page,
27 | pageSize
28 | }
29 | }
30 |
31 | export function loadProfiles (page = 1, pageSize = 30) {
32 | return (dispatch, getState) => {
33 | // TODO - check pagination
34 | return dispatch(fetchProfiles(page, pageSize))
35 | }
36 | }
37 |
38 | export function fetchProfileById (id) {
39 | return {
40 | [CALL_API]: {
41 | types: [PROFILE_BY_ID_REQUEST, PROFILE_BY_ID_SUCCESS, PROFILE_BY_ID_FAILURE],
42 | endpoint: `/connect/${id}`,
43 | schema: Schemas.PROFILE
44 | }
45 | }
46 | }
47 |
48 | export function loadProfileById (id) {
49 | return (dispatch, getState) => {
50 | if (selectProfileById(getState(), id)) {
51 | return new Promise((resolve, reject) => { resolve() })
52 | }
53 | return dispatch(fetchProfileById(id))
54 | }
55 | }
56 |
57 | export function fetchProfileByName (name) {
58 | return {
59 | [CALL_API]: {
60 | types: [PROFILE_BY_NAME_REQUEST, PROFILE_BY_NAME_SUCCESS, PROFILE_BY_NAME_FAILURE],
61 | endpoint: `/${name}`,
62 | schema: Schemas.PROFILE
63 | }
64 | }
65 | }
66 |
67 | export function loadProfileByName (name) {
68 | return (dispatch, getState) => {
69 | if (selectProfileByName(getState(), name)) {
70 | return new Promise((resolve, reject) => {})
71 | }
72 | return dispatch(
73 | fetchProfileByName(name)
74 | )
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/actions/registry.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from '../middleware/api'
2 |
3 | import Schemas from '../schemas'
4 |
5 | import {
6 | REGISTRY_LIST_REQUEST,
7 | REGISTRY_LIST_SUCCESS,
8 | REGISTRY_LIST_FAILURE,
9 | REGISTRY_DATASET_REQUEST,
10 | REGISTRY_DATASET_SUCCESS,
11 | REGISTRY_DATASET_FAILURE
12 | } from '../constants/registry'
13 |
14 | export function fetchRegistryDatasets (page = 1, pageSize = 25) {
15 | return {
16 | [CALL_API]: {
17 | types: [REGISTRY_LIST_REQUEST, REGISTRY_LIST_SUCCESS, REGISTRY_LIST_FAILURE],
18 | endpoint: `/registry/datasets`,
19 | data: { page, pageSize },
20 | schema: Schemas.DATASET_ARRAY
21 | },
22 | page,
23 | pageSize
24 | }
25 | }
26 |
27 | export function loadRegistryDatasets (page = 1, pageSize = 25) {
28 | return (dispatch, getState) => {
29 | // TODO - check pagination
30 | return dispatch(fetchRegistryDatasets(page, pageSize))
31 | }
32 | }
33 |
34 | export function fetchRegistryDatasetByName (handle, name) {
35 | return {
36 | [CALL_API]: {
37 | types: [REGISTRY_DATASET_REQUEST, REGISTRY_DATASET_SUCCESS, REGISTRY_DATASET_FAILURE],
38 | endpoint: `/registry/${handle}/${name}`,
39 | schema: Schemas.DATASET,
40 | method: 'GET',
41 | silentError: true
42 | }
43 | }
44 | }
45 |
46 | export function fetchRegistryDatasetByPath (path, handle, name) {
47 | const namedPath = handle && name ? `/${handle}/${name}/at${path}` : `/at${path}`
48 | return {
49 | [CALL_API]: {
50 | types: [REGISTRY_DATASET_REQUEST, REGISTRY_DATASET_SUCCESS, REGISTRY_DATASET_FAILURE],
51 | endpoint: namedPath,
52 | schema: Schemas.DATASET,
53 | path,
54 | silentError: true
55 | }
56 | }
57 | }
58 |
59 | // TODO: look through registry and see if we already have this registry dataset before fetching
60 | export function loadRegistryDatasetByName (handle, name, requiredFields = []) {
61 | return (dispatch, getState) => {
62 | return dispatch(fetchRegistryDatasetByName(handle, name))
63 | }
64 | }
65 |
66 | // TODO: look through registry and see if we already have this registry dataset before fetching
67 | export function loadRegistryDatasetByPath (path, handle, name, requiredFields = []) {
68 | return (dispatch, getState) => {
69 | return dispatch(fetchRegistryDatasetByPath(path, handle, name))
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/actions/transfers.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_TRANSFER_STATUS,
3 | REMOVE_TRANSFER_STATUS
4 | } from '../constants/transfers'
5 |
6 | export function setTransferStatus (id, status = 0) {
7 | return {
8 | type: SET_TRANSFER_STATUS,
9 | id,
10 | status
11 | }
12 | }
13 |
14 | export function removeTransferStatus (id) {
15 | return {
16 | type: REMOVE_TRANSFER_STATUS,
17 | id
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/app.global.scss:
--------------------------------------------------------------------------------
1 | @import "scss/style"
--------------------------------------------------------------------------------
/lib/assets/ss-pika.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/lib/assets/ss-pika.eot
--------------------------------------------------------------------------------
/lib/assets/ss-pika.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/lib/assets/ss-pika.ttf
--------------------------------------------------------------------------------
/lib/assets/ss-pika.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/lib/assets/ss-pika.woff
--------------------------------------------------------------------------------
/lib/components/AppDrag.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class AppDrag extends React.PureComponent {
5 | render () {
6 | const { style } = this.props
7 |
8 | return (
9 |
10 | )
11 | }
12 | }
13 |
14 | AppDrag.propTypes = {
15 | style: PropTypes.object
16 | }
17 |
18 | AppDrag.defaultProps = {
19 | }
20 |
--------------------------------------------------------------------------------
/lib/components/AppLoading.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Spinner from './chrome/Spinner'
4 | import AppDrag from './AppDrag'
5 | import version from '../../version'
6 |
7 | export default class AppLoading extends React.PureComponent {
8 | render () {
9 | return (
10 | // this component often loads and renders before any styling has arrived
11 | // styles are inlined here to avoid visual stutter
12 |
13 |
14 |
15 |
Starting qri (aka "query") version {version}
16 |
Have a wonderful day!
17 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
24 | AppLoading.propTypes = {
25 | }
26 |
27 | AppLoading.defaultProps = {
28 | }
29 |
--------------------------------------------------------------------------------
/lib/components/AppUnavail.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import AppDrag from './AppDrag'
3 |
4 | export default class AppUnavail extends React.PureComponent {
5 | render () {
6 | return (
7 |
8 |
9 |
10 |
Qri backend is unavailable
11 |
😭 😭 😭
12 |
13 |
If you are running locally try typing
14 |
qri connect --disable-webapp
15 |
into your terminal to start up your local qri server
16 |
17 |
18 | )
19 | }
20 | }
21 |
22 | AppUnavail.propTypes = {
23 | }
24 |
25 | AppUnavail.defaultProps = {
26 | }
27 |
--------------------------------------------------------------------------------
/lib/components/CodeViewer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class CodeViewer extends React.PureComponent {
5 | render () {
6 | const { code } = this.props
7 | return (
8 |
9 |
{code.split('\n').map((_, i) => {i + 1}
)}
10 |
{code}
11 |
12 | )
13 | }
14 | }
15 |
16 | CodeViewer.propTypes = {
17 | code: PropTypes.string.isRequired
18 | }
19 |
20 | CodeViewer.defaultProps = {
21 | url: ''
22 | }
23 |
--------------------------------------------------------------------------------
/lib/components/DatasetName.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { NavLink } from 'react-router-dom'
4 |
5 | export default class DatasetName extends React.PureComponent {
6 | constructor (props) {
7 | super(props);
8 | [
9 | 'renderName'
10 | ].forEach((m) => { this[m] = this[m].bind(this) })
11 | }
12 |
13 | renderName (link, peername, name) {
14 | if (link) {
15 | return (
16 |
17 | {peername}
18 | /
19 | {name}
21 |
22 | )
23 | }
24 |
25 | if (peername) {
26 | return ({peername}/{name} )
27 | }
28 | return ({name} )
29 | }
30 |
31 | render () {
32 | const { peername, name, large, style, rename, xlarge, link } = this.props
33 | var datasetClass = 'linkMedium'
34 | if (large) {
35 | datasetClass = 'linkLarge'
36 | }
37 | if (xlarge) {
38 | datasetClass = 'linkXLarge'
39 | }
40 | return (
41 |
42 | { this.renderName(link, peername, name) }
43 | { rename ? rename()}>pen : undefined }
44 |
45 | )
46 | }
47 | }
48 |
49 | DatasetName.propTypes = {
50 | style: PropTypes.object,
51 | peername: PropTypes.string,
52 | name: PropTypes.string.isRequired,
53 | large: PropTypes.bool.isRequired,
54 | rename: PropTypes.func
55 | }
56 |
57 | DatasetName.defaultProps = {
58 | style: {},
59 | large: false
60 | }
61 |
--------------------------------------------------------------------------------
/lib/components/Datasets.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { datasetProps } from './../propTypes/datasetRefProps'
4 | import DatasetItem from './item/DatasetItem'
5 | import List from './List'
6 |
7 | const Datasets = (props) => {
8 | const { datasets, message, loading, fetchedAll, showPublishedStatus, hitBottom, loadNextPage } = props
9 | return (
10 | {message}}
14 | loading={loading}
15 | fetchedAll={fetchedAll}
16 | type='datasets'
17 | border
18 | showPublishedStatus={showPublishedStatus}
19 | hitBottom={hitBottom}
20 | loadMore={loadNextPage}
21 | />
22 | )
23 | }
24 |
25 | Datasets.propTypes = {
26 | datasets: PropTypes.arrayOf(datasetProps),
27 | loading: PropTypes.bool,
28 | fetchedAll: PropTypes.bool
29 | }
30 |
31 | Datasets.defaultProps = {
32 | message: 'No Datasets available'
33 | }
34 |
35 | export default Datasets
36 |
--------------------------------------------------------------------------------
/lib/components/ExternalLink.electron.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { shell } from 'electron'
3 |
4 | export default class ExternalLink extends Component {
5 | render () {
6 | return ( {
7 | e.preventDefault()
8 | shell.openExternal(this.props.href)
9 | }} {...this.props}>{this.props.children} )
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/components/ExternalLink.web.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | export default class ExternalLink extends Component {
4 | render () {
5 | return {this.props.children}
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lib/components/HandsonTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import HotTable from 'react-handsontable'
3 | import PropTypes from 'prop-types'
4 |
5 | export default class HandsonTable extends React.PureComponent {
6 | render () {
7 | const { body, colHeaders, readOnly, layout, onAfterChange } = this.props
8 |
9 | var width = layout && layout.width
10 | var height = layout && layout.height
11 |
12 | return (
13 |
14 |
31 |
32 | )
33 | }
34 | }
35 |
36 | HandsonTable.propTypes = {
37 | body: PropTypes.array.isRequired,
38 | colHeaders: PropTypes.array,
39 | onAfterChange: PropTypes.func,
40 | edit: PropTypes.bool
41 | }
42 |
43 | HandsonTable.defaultProps = {
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/lib/components/Hash.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class Hash extends React.PureComponent {
5 | render () {
6 | const { hash, datasetRef, short, noPrefix, style, old } = this.props
7 | let displayHash = hash || datasetRef.path
8 | if (!displayHash || displayHash.length < 52) {
9 | displayHash = 'invalid hash'
10 | } else {
11 | displayHash = displayHash.slice('/ipfs/'.length)
12 | displayHash = short ? displayHash.slice(0, 2) + '...' + displayHash.slice(-6) : displayHash
13 | }
14 | return (
15 |
16 | {!noPrefix && /ipfs/ }
17 | {displayHash}
18 | {old ? not latest version : undefined}
19 |
20 | )
21 | }
22 | }
23 |
24 | Hash.propTypes = {
25 | short: PropTypes.bool,
26 | noPrefix: PropTypes.bool,
27 | style: PropTypes.object
28 | }
29 |
30 | Hash.defaultProps = {
31 | style: {},
32 | short: false,
33 | noPrefix: false,
34 | datasetRef: {}
35 | }
36 |
--------------------------------------------------------------------------------
/lib/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | // import NavLinks from './chrome/NavLinks'
5 | import PageTitle from './PageTitle'
6 | // import Spinner from './chrome/Spinner'
7 |
8 | export default class Header extends React.PureComponent {
9 | render () {
10 | // const { text, leftChild, rightChild, linkList, url, smLink, spinner } = this.props
11 |
12 | return (
13 |
14 | {/*
15 |
16 |
19 |
20 | {leftChild ||
{text} }
21 | {rightChild}
22 |
23 |
24 | { url && linkList && }
25 |
26 |
27 | */}
28 |
29 |
30 | )
31 | }
32 | }
33 |
34 | Header.propTypes = {
35 | linkList: PropTypes.arrayOf(
36 | PropTypes.shape(
37 | {
38 | link: PropTypes.string,
39 | name: PropTypes.string
40 | }
41 | )
42 | ),
43 | text: PropTypes.string,
44 | leftChild: PropTypes.object,
45 | rightChild: PropTypes.object,
46 | smLink: PropTypes.bool
47 | }
48 |
49 | Header.defaultProps = {
50 | }
51 |
--------------------------------------------------------------------------------
/lib/components/List.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Spinner from './chrome/Spinner'
4 |
5 | function selectFunc (fn, data, i) {
6 | return () => {
7 | if (fn) {
8 | return fn(i, data)
9 | }
10 | }
11 | }
12 |
13 | const List = (props) => {
14 | const {
15 | data,
16 | onSelectItem,
17 | className,
18 | emptyComponent,
19 | style,
20 | loading,
21 | onAdd,
22 | small,
23 | loadMore,
24 | fetchedAll,
25 | hitBottom
26 | } = props
27 |
28 | if (!loading && (!data || data.length === 0)) {
29 | if (emptyComponent) {
30 | return (
31 |
32 | {emptyComponent}
33 |
34 | )
35 | }
36 | return
37 | }
38 |
39 | const safeProps = Object.keys(props).reduce((obj, key) => {
40 | // array of props already used by List
41 | if (![
42 | 'data',
43 | 'component',
44 | 'onSelectItem',
45 | 'emptyComponent',
46 | 'loading',
47 | 'fetchedAll',
48 | 'onClick',
49 | 'type',
50 | 'hitBottom',
51 | 'loadMore'
52 | ].includes(key)) {
53 | obj[key] = props[key]
54 | }
55 | return obj
56 | }, {})
57 |
58 | return (
59 |
60 | {data.map((d, i) =>
)}
61 | {loading &&
}
62 | {!fetchedAll && !loading && hitBottom && loadMore && loadMore()}
63 |
64 | )
65 | }
66 |
67 | List.propTypes = {
68 | data: PropTypes.array,
69 | // eslint-disable-next-line react/no-unused-prop-types
70 | component: PropTypes.func.isRequired,
71 | emptyComponent: PropTypes.element,
72 | onSelectItem: PropTypes.func,
73 | small: PropTypes.bool
74 | }
75 |
76 | List.defaultProps = {
77 | data: [],
78 | className: 'list'
79 | }
80 |
81 | export default List
82 |
--------------------------------------------------------------------------------
/lib/components/Menu.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 | import { Link } from 'react-router-dom'
5 |
6 | export default class Menu extends React.PureComponent {
7 | checkActive (location) {
8 | return (path) => (location.pathname === path) ? 'menu-active' : 'menu-inactive'
9 | }
10 |
11 | render () {
12 | const { style, location } = this.props
13 | const isActive = this.checkActive(location)
14 |
15 | return (
16 |
17 |
18 | layers
19 | user
20 | usergroup
21 | { __BUILD__.MODE !== 'production' && settingsfile}
22 |
23 |
24 | )
25 | }
26 | }
27 |
28 | Menu.propTypes = {
29 | location: PropTypes.object,
30 | user: PropTypes.object
31 | }
32 |
33 | Menu.defaultProps = {
34 | location: {}
35 | }
36 |
--------------------------------------------------------------------------------
/lib/components/Online.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class Online extends React.PureComponent {
5 | render () {
6 | const { online, style, small, blue } = this.props
7 | const className = small ? 'online-small' : 'online-regular'
8 | const iconStyle = online ? 'connected' : ''
9 | const textStyle = blue ? 'linkMedium' : ''
10 | return (
11 |
12 | {online ? 'flash' : 'hyphen'}
13 | { online ? ' online' : ' offline' }
14 |
15 | )
16 | }
17 | }
18 |
19 | Online.propTypes = {
20 | online: PropTypes.bool,
21 | style: PropTypes.object,
22 | small: PropTypes.bool
23 | }
24 |
25 | Online.defaultProps = {
26 | }
27 |
--------------------------------------------------------------------------------
/lib/components/PageTitle.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import Button from './chrome/Button'
5 |
6 | export default class PageTitle extends React.PureComponent {
7 | render () {
8 | const { pageTitle, sectionTitle, buttonText, onClick } = this.props
9 |
10 | return (
11 |
12 |
{pageTitle &&
{pageTitle} }
13 | {buttonText && onClick &&
}
14 | {sectionTitle &&
{sectionTitle}
}
15 |
16 | )
17 | }
18 | }
19 |
20 | PageTitle.propTypes = {
21 | pageTitle: PropTypes.string,
22 | sectionTitle: PropTypes.string,
23 | buttonText: PropTypes.string,
24 | buttonLink: PropTypes.string
25 | }
26 |
27 | PageTitle.defaultProps = {
28 | }
29 |
--------------------------------------------------------------------------------
/lib/components/QuitApp.electron.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { remote } from 'electron'
3 |
4 | // Sister component to QuitApp.web.js
5 | export default class QuitApp extends Component {
6 | render () {
7 | return ( {
8 | remote.app.quit()
9 | }}>exit
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/components/QuitApp.web.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 |
3 | // Sister component to QuitApp.electron.js
4 | // when we are in the webapp, we don't need any `exit`, the user can just x-out the tab
5 | export default class QuitApp extends Component {
6 | render () {
7 | return null
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/components/ReadOnly.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import AppDrag from './AppDrag'
3 | import Button from './chrome/Button'
4 |
5 | export default class ReadOnly extends React.PureComponent {
6 | render () {
7 | return (
8 |
9 |
10 |
11 |
This qri server
12 |
is in read-only mode!
13 |
Only calls to /network and /[peername]/[datasetname] are active
14 |
15 |
16 |
17 | )
18 | }
19 | }
20 |
21 | ReadOnly.propTypes = {
22 | }
23 |
24 | ReadOnly.defaultProps = {
25 | }
26 |
--------------------------------------------------------------------------------
/lib/components/StatsBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class StatsBar extends React.PureComponent {
5 | constructor (props) {
6 | super(props);
7 | [
8 | 'renderStat',
9 | 'parseDate'
10 | ].forEach((m) => { this[m] = this[m].bind(this) })
11 | }
12 |
13 | parseDate (datetime) {
14 | if (!datetime) {
15 | return 'no date given'
16 | }
17 | const date = new Date(datetime)
18 | const options = { month: 'short', day: 'numeric', year: 'numeric' }
19 | return date.toLocaleDateString('en-US', options)
20 | }
21 |
22 | renderStat (stat, space, index, style) {
23 | if (stat && stat.name) {
24 | const num = stat.value > 1000 ? Math.round(stat.value / 1000) + 'K' : stat.value
25 | return (
26 |
27 | {num}
28 | {stat.name}
29 |
30 | )
31 | }
32 | }
33 |
34 | render () {
35 | const { stats, extraSpace, large, style, updated } = this.props
36 | const space = extraSpace ? 'extraSpace' : 'stats-bar-space'
37 | const font = large ? 'stats-large' : 'stats-medium'
38 | return (
39 |
40 | {stats.map((stat, index) => this.renderStat(stat, space, index))}
41 | {updated ? {this.parseDate(updated)}updated : undefined}
42 |
43 | )
44 | }
45 | }
46 |
47 | StatsBar.propTypes = {
48 | stats: PropTypes.arrayOf(PropTypes.object).isRequired,
49 | muted: PropTypes.bool,
50 | extraSpace: PropTypes.bool,
51 | style: PropTypes.object
52 | }
53 |
54 | StatsBar.defaultProps = {
55 | style: {},
56 | stats: [],
57 | muted: false,
58 | extraSpace: false,
59 | large: false
60 | }
61 |
--------------------------------------------------------------------------------
/lib/components/StatsLine.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class StatsLine extends React.PureComponent {
5 | constructor (props) {
6 | super(props);
7 | [
8 | 'renderStat',
9 | 'parseDate'
10 | ].forEach((m) => { this[m] = this[m].bind(this) })
11 | }
12 |
13 | parseDate (datetime) {
14 | if (!datetime) {
15 | return 'no date given'
16 | }
17 | const date = new Date(datetime)
18 | const options = { month: 'short', day: 'numeric', year: 'numeric' }
19 | return date.toLocaleDateString('en-US', options)
20 | }
21 |
22 | renderStat (stat, index, style) {
23 | if (stat && stat.value && stat.name) {
24 | const num = stat.value > 1000 ? Math.round(stat.value / 1000) + 'K' : stat.value
25 | return {num} {stat.name} |
26 | }
27 | }
28 |
29 | render () {
30 | const { stats, style, updated, published } = this.props
31 | return (
32 |
33 |
34 | {stats.map((stat, index) => this.renderStat(stat, index))}
35 | {updated ? {this.parseDate(updated)} : undefined}
36 |
37 | {published &&
published }
38 |
39 | )
40 | }
41 | }
42 |
43 | StatsLine.propTypes = {
44 | stats: PropTypes.arrayOf(PropTypes.object).isRequired,
45 | muted: PropTypes.bool,
46 | extraSpace: PropTypes.bool,
47 | style: PropTypes.object
48 | }
49 |
50 | StatsLine.defaultProps = {
51 | style: {},
52 | stats: [],
53 | muted: false,
54 | extraSpace: false,
55 | large: false
56 | }
57 |
--------------------------------------------------------------------------------
/lib/components/StatsSection.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class StatsSection extends React.PureComponent {
5 | render () {
6 | const { stats } = this.props
7 | return (
8 |
9 | {stats.map((s, i) => {
10 | return (
11 | {s.stat}
12 | {s.title}
13 | )
14 | })}
15 |
16 | )
17 | }
18 | }
19 |
20 | StatsSection.propTypes = {
21 | stats: PropTypes.array
22 | }
23 |
24 | StatsSection.defaultProps = {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/lib/components/TabPanel.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | // TODO (ramfox): marked for deprecation, we don't use this component for anything anymore
5 | export default class TabPanel extends React.PureComponent {
6 | constructor (props) {
7 | super(props)
8 |
9 | this.state = {
10 | index: 0
11 | };
12 |
13 | [
14 | 'handleSetPanel'
15 | ].forEach((m) => { this[m] = this[m].bind(this) })
16 | }
17 |
18 | handleSetPanel (i, onSelectPanel, e) {
19 | if (typeof onSelectPanel === 'function') {
20 | onSelectPanel(i)
21 | } else {
22 | this.setState({ index: i })
23 | }
24 | }
25 |
26 | render () {
27 | const { index, expanded, labels = [], components, onSelectPanel, onToggleExpand, clearBackground } = this.props
28 | const panel = (index !== undefined) ? index : this.state.index
29 | const component = components[panel]
30 | const cssWrap = clearBackground ? 'tab-panel-wrap' : 'tab-panel-wrap tab-panel-background'
31 | return (
32 |
33 |
45 |
46 | {component}
47 |
48 |
49 | )
50 | }
51 | }
52 |
53 | TabPanel.propTypes = {
54 | labels: PropTypes.array.isRequired,
55 | components: PropTypes.array.isRequired,
56 | index: PropTypes.number,
57 | onSelectPanel: PropTypes.func,
58 | expanded: PropTypes.bool,
59 | onToggleExpand: PropTypes.func,
60 | clearBackground: PropTypes.bool
61 | }
62 |
63 | TabPanel.defaultProps = {
64 | }
65 |
--------------------------------------------------------------------------------
/lib/components/TagList.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import TagItem from './item/TagItem'
4 |
5 | export default class TagList extends React.PureComponent {
6 | render () {
7 | const { tags } = this.props
8 |
9 | return (
10 |
11 | {tags.map((tag, index) => )}
12 |
13 | )
14 | }
15 | }
16 |
17 | TagList.propTypes = {
18 | tags: PropTypes.array
19 | // add in when we move styling to scss
20 | // color: PropTypes.string
21 | }
22 |
23 | TagList.defaultProps = {
24 | // color: 'a'
25 | }
26 |
--------------------------------------------------------------------------------
/lib/components/Welcome.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import AppDrag from './AppDrag'
4 | import QuitApp from './QuitApp.APP_TARGET'
5 | import ExternalLink from './ExternalLink.APP_TARGET'
6 | import version from '../../version'
7 |
8 | export default class Welcome extends React.PureComponent {
9 | render () {
10 | return (
11 |
12 |
13 |
14 |
15 |
Welcome To Qri!
16 | You're using version {version}
17 |
18 |
19 |
We’re currently in Beta. There will be bugs, and features will change quickly & often. We hope you’ll come on this adventure with us! Our github org is the best place to stay informed.
20 |
21 | A few notes before we get started:
22 |
23 | By using Qri you agree to our Terms of Service
24 | All Data on Qri is Public
25 |
26 |
27 |
28 |
32 |
33 |
34 | )
35 | }
36 | }
37 |
38 | Welcome.propTypes = {
39 | }
40 |
41 | Welcome.defaultProps = {
42 | onAccept: () => {},
43 | onExit: () => {}
44 | }
45 |
--------------------------------------------------------------------------------
/lib/components/chrome/DownloadBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import ExternalLink from '../ExternalLink.APP_TARGET'
5 |
6 | const DownloadBar = ({ layout, peername, name }) => {
7 | // get layout from Dataset, assume height is same as topbar => 55px
8 | return (
9 |
10 |
11 |
12 | DOWNLOAD QRI HERE
13 | to get the underlying data from {peername}/{name}
14 |
15 |
16 |
Need help?
17 |
18 | )
19 | }
20 |
21 | DownloadBar.propTypes = {
22 | layout: PropTypes.object.isRequired,
23 | peername: PropTypes.string.isRequired,
24 | name: PropTypes.string.isRequired
25 | }
26 |
27 | DownloadBar.defaultProps = {
28 | layout: { height: 55, left: 0 }
29 | }
30 |
31 | export default DownloadBar
32 |
--------------------------------------------------------------------------------
/lib/components/chrome/Dropdown.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import Button from './Button'
4 |
5 | // Dropdown is a dropdown menu, made from a Qri button, a html button (with a carot) and a passed in dropdown component
6 | const Dropdown = ({ color, onClick, loading, text, download, disabled, options, downloadName, onChooseOption, DropdownMenu }) => {
7 | const [display, setDisplay] = useState(false)
8 | const onClickOption = (opt) => {
9 | setDisplay(false)
10 | onChooseOption(opt)
11 | }
12 | const className = 'btn btn-' + color
13 |
14 | return (
15 |
16 |
17 |
27 | setDisplay(!display)}
31 | >
32 | dropdown
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | Dropdown.propTypes = {
43 | // options: options to display in the dropdown menu
44 | options: PropTypes.array,
45 | // onChooseOption: function that gets triggered when you choose an option (not when you click the button)
46 | onChooseOption: PropTypes.func,
47 | // text: the text that is displayed on the dropdown button
48 | text: PropTypes.string.isRequired,
49 | // color: the color of the button, default is 'primary'
50 | color: PropTypes.string,
51 | // onClick: the function that fires when the dropdown button is clicked
52 | onClick: PropTypes.func,
53 | // dropdown: the component that gets displayed when you show the dropdown menu
54 | dropdown: PropTypes.func,
55 | // download: the href that would get passed down to the Button
56 | download: PropTypes.string,
57 | // downloadName: name that would get passed down to the Button
58 | downloadName: PropTypes.string
59 | }
60 |
61 | Dropdown.defaultProps = {
62 | color: 'primary'
63 | }
64 |
65 | export default Dropdown
66 |
--------------------------------------------------------------------------------
/lib/components/chrome/DropdownMenu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const DropdownMenu = ({ display, options, onChooseOption }) => {
5 | return (
6 |
7 | {options.map((opt, i) => {
8 | return
{opt}
9 | })}
10 |
11 | )
12 | }
13 |
14 | DropdownMenu.propTypes = {
15 | options: PropTypes.array,
16 | onChooseOption: PropTypes.func,
17 | display: PropTypes.bool.isRequired
18 | }
19 |
20 | DropdownMenu.defaultProps = {
21 | options: ['Action', 'Another Action', 'Something else here'],
22 | onChooseOption: (opt) => { console.log('chose option:', opt) }
23 | }
24 |
25 | export default DropdownMenu
26 |
--------------------------------------------------------------------------------
/lib/components/chrome/LoadMore.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Button from './Button'
4 |
5 | const LoadMore = ({ loading, onLoadMore }) => {
6 | return (
7 | Load more entries:
8 | onLoadMore(100)} />
9 | onLoadMore(500)} />
10 | onLoadMore(1000)} />
11 |
12 | )
13 | }
14 |
15 | LoadMore.propTypes = {
16 | onLoadMore: PropTypes.func.isRequired,
17 | loading: PropTypes.bool.isRequired
18 | }
19 |
20 | export default LoadMore
21 |
--------------------------------------------------------------------------------
/lib/components/chrome/NavLinks.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { NavLink } from 'react-router-dom'
3 | import PropTypes from 'prop-types'
4 |
5 | // NavLinks component is used in the Dataset and Editor to naviate around
6 | // the different sections of a Dataset
7 | const NavLinks = ({ url, linkList, sm }) => {
8 | // style based on sm or normal
9 | const margin = sm ? 'nav-links-link-sm' : 'nav-links-link'
10 | const marginRight = sm ? 10 : 15
11 | const className = sm ? 'linkMediumMuted' : 'linkLargeMuted'
12 | const activeClassName = sm ? 'linkMedium' : 'linkLarge'
13 | return (
14 |
15 | {
16 | linkList.map((item, index) => {
17 | return (
18 |
23 |
26 | {item.name}
27 |
28 |
29 | )
30 | })
31 | }
32 |
33 | )
34 | }
35 |
36 | NavLinks.propTypes = {
37 | // linkList: array of objects that includes the link and the display name
38 | linkList: PropTypes.arrayOf(
39 | PropTypes.shape(
40 | {
41 | link: PropTypes.string,
42 | name: PropTypes.string
43 | }
44 | )
45 | ).isRequired,
46 | // url: the base url that the link links to
47 | url: PropTypes.string.isRequired,
48 | // sm: when sm is true, the NavLinks have a smaller formatting
49 | sm: PropTypes.bool
50 | }
51 |
52 | export default NavLinks
53 |
--------------------------------------------------------------------------------
/lib/components/chrome/Spinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const Spinner = ({ center, button, white, large }) => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | Spinner.propTypes = {
17 | button: PropTypes.bool,
18 | center: PropTypes.bool.isRequired,
19 | white: PropTypes.bool,
20 | large: PropTypes.bool
21 | }
22 |
23 | Spinner.defaultProps = {
24 | center: true
25 | }
26 |
27 | export default Spinner
28 |
--------------------------------------------------------------------------------
/lib/components/dataset/BodyReadOnly.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Button from '../chrome/Button'
4 | import ExternalLink from '../ExternalLink.APP_TARGET'
5 |
6 | export default class BodyReadOnly extends React.PureComponent {
7 | render () {
8 | const { peername, name } = this.props
9 | return (
10 |
11 |
View the dataset body using the Qri Desktop App
12 |
13 |
Launch the app and navigate to:
14 |
{`${peername}/${name}`}
15 |
Then add the dataset to view the body!
16 |
17 | )
18 | }
19 | }
20 |
21 | BodyReadOnly.propTypes = {
22 | }
23 |
24 | BodyReadOnly.defaultProps = {
25 | }
26 |
--------------------------------------------------------------------------------
/lib/components/dataset/Commits.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import List from '../List'
4 | import CommitItemContainer from '../../containers/CommitItem'
5 |
6 | export default class Commits extends React.PureComponent {
7 | constructor (props) {
8 | super(props);
9 |
10 | [
11 | 'handleLoadNextPage'
12 | ].forEach((m) => { this[m] = this[m].bind(this) })
13 | }
14 |
15 | componentDidMount () {
16 | if (!this.props.skipLoad) {
17 | this.props.loadHistoryByName(this.props.peername, this.props.name)
18 | }
19 | }
20 |
21 | handleLoadNextPage () {
22 | this.props.loadHistoryByName(this.props.peername, this.props.name, this.props.nextPage)
23 | }
24 |
25 | render () {
26 | const {
27 | log,
28 | currentID,
29 | datasetRef,
30 | registryVersion,
31 | fromRegistry,
32 | sessionProfile,
33 | hitBottom,
34 | loading,
35 | fetchedAll
36 | } = this.props
37 |
38 | return (
39 |
40 |
}
45 | loading={loading}
46 | fetchedAll={fetchedAll}
47 | type='history'
48 | currentID={currentID}
49 | registryVersion={registryVersion}
50 | sessionProfile={sessionProfile}
51 | loadMore={this.handleLoadNextPage}
52 | hitBottom={hitBottom}
53 | />
54 | {fromRegistry && sessionProfile &&
Note: this dataset is from the registry, so it's history may be incomplete
}
55 |
56 | )
57 | }
58 | }
59 |
60 | Commits.propTypes = {
61 | searchString: PropTypes.string,
62 | log: PropTypes.array.isRequired,
63 | nextPage: PropTypes.number.isRequired,
64 | fetchedAll: PropTypes.bool,
65 | loadHistoryByName: PropTypes.func.isRequired,
66 | skipLoad: PropTypes.bool
67 | }
68 |
69 | Commits.defaultProps = {
70 | skipLoad: false
71 | }
72 |
--------------------------------------------------------------------------------
/lib/components/dataset/DatasetHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { withRouter } from 'react-router-dom'
4 |
5 | import DatasetRefProps from '../../propTypes/datasetRefProps'
6 | import DatasetName from '../DatasetName'
7 | import Hash from '../Hash'
8 | import NavLinks from '../chrome/NavLinks'
9 |
10 | import { addActiveToLink, datasetLinks } from '../../utils/links.js'
11 | class DatasetHeader extends React.PureComponent {
12 | constructor (props) {
13 | super(props)
14 | this.state = {
15 | readMore: true
16 | };
17 | [
18 | 'handleReadMore',
19 | 'renderRef'
20 | ].forEach((m) => { this[m] = this[m].bind(this) })
21 | }
22 |
23 | handleReadMore (e) {
24 | this.setState({ readMore: !this.state.readMore })
25 | }
26 |
27 | renderRef () {
28 | const { peername, name, datasetRef, isLatestDataset } = this.props
29 | return (
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | render () {
38 | const { url, datasetRef, fromRegistry, isLocal, sessionProfile } = this.props
39 | const { dataset } = datasetRef
40 | const pathname = this.props.location.pathname // this is why we need to wrap with 'withRouter'
41 |
42 | const linkList = datasetLinks(dataset, fromRegistry, isLocal, sessionProfile)
43 |
44 | return (
45 |
46 | {this.renderRef()}
47 |
48 | { url && linkList && }
49 |
50 |
51 | )
52 | }
53 | }
54 |
55 | export default withRouter(DatasetHeader)
56 |
57 | DatasetHeader.propTypes = {
58 | // dataset data model
59 | datasetRef: DatasetRefProps,
60 | onGoBack: PropTypes.func,
61 | exportPath: PropTypes.string,
62 | onEdit: PropTypes.func,
63 | onDelete: PropTypes.func,
64 | onAdd: PropTypes.func,
65 | peer: PropTypes.bool,
66 | sessionProfile: PropTypes.string,
67 | peername: PropTypes.string,
68 | name: PropTypes.string
69 | }
70 |
71 | DatasetHeader.defaultProps = {
72 | }
73 |
--------------------------------------------------------------------------------
/lib/components/dataset/Meta.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import MetadataViewer from './MetadataViewer'
4 |
5 | import MetaProps from '../../propTypes/metaProps.js'
6 |
7 | export default class Meta extends React.PureComponent {
8 | render () {
9 | const { meta } = this.props
10 |
11 | if (meta === undefined) {
12 | return (No meta found for this dataset.
)
13 | }
14 |
15 | let md = meta
16 | if (typeof meta === 'string') {
17 | md = { path: meta }
18 | }
19 | return (
20 |
21 | )
22 | }
23 | }
24 |
25 | Meta.propTypes = {
26 | meta: MetaProps
27 | }
28 |
29 | Meta.defaultProps = {
30 | }
31 |
--------------------------------------------------------------------------------
/lib/components/dataset/MetaSummary.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import Spinner from '../chrome/Spinner'
5 |
6 | import TagItem from '../item/TagItem'
7 | import MetaProps from '../../propTypes/metaProps'
8 |
9 | export default class MetaSummary extends React.PureComponent {
10 | constructor (props) {
11 | super(props);
12 |
13 | [
14 | 'renderTheme',
15 | 'renderTags'
16 | ].forEach((m) => { this[m] = this[m].bind(this) })
17 | }
18 |
19 | renderTheme (themes) {
20 | if (themes.length === 0) {
21 | return undefined
22 | }
23 | return (
24 |
25 | {themes.map((t, i) =>
26 |
27 |
{t}
28 |
29 | )}
30 |
31 | )
32 | }
33 |
34 | renderTags (tags) {
35 | if (tags.length === 0) {
36 | return undefined
37 | }
38 | return (
39 |
40 | {tags.map((t, i) =>
41 |
42 | )}
43 |
44 | )
45 | }
46 |
47 | render () {
48 | const { meta, name, loading } = this.props
49 |
50 | if (loading) {
51 | return (
)
52 | }
53 |
54 | if (!meta && !name) {
55 | return (No Metadata given for this dataset
)
56 | }
57 |
58 | return (
59 |
60 | {
{(meta && meta.title) || name} }
61 | {meta && meta.theme && this.renderTheme(meta.theme)}
62 |
63 | {meta && meta.description &&
{meta.description}
}
64 | {meta && meta.keywords && this.renderTags(meta.keywords)}
65 |
66 |
67 | )
68 | }
69 | }
70 |
71 | MetaSummary.propTypes = {
72 | meta: MetaProps,
73 | loading: PropTypes.bool,
74 | name: PropTypes.string
75 | }
76 |
77 | MetaSummary.defaultProps = {
78 | }
79 |
--------------------------------------------------------------------------------
/lib/components/dataset/Render.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | export default class Render extends React.PureComponent {
3 | render () {
4 | const { peername, name, path, layout } = this.props
5 | var url = `${peername}/${name}`
6 | if (path !== '') {
7 | url += `/at${path}`
8 | }
9 | console.log(url)
10 | return (
11 |
12 | )
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/components/dataset/Structure.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { structureProps } from '../../propTypes/datasetRefProps.js'
3 |
4 | import StructureViewer from './StructureViewer'
5 |
6 | export default class Structure extends React.PureComponent {
7 | render () {
8 | const { structure } = this.props
9 | return (
10 |
11 | )
12 | }
13 | }
14 |
15 | Structure.propTypes = {
16 | structure: structureProps
17 | }
18 |
19 | Structure.defaultProps = {
20 | }
21 |
--------------------------------------------------------------------------------
/lib/components/dataset/Transform.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Spinner from '../chrome/Spinner'
4 | import CodeViewer from '../CodeViewer'
5 |
6 | export default class Transform extends React.PureComponent {
7 | componentDidMount () {
8 | const { dataset, transformString, loading } = this.props
9 | const key = dataset && dataset.transform && dataset.transform.scriptPath
10 |
11 | if (!transformString && key && !loading) {
12 | this.props.loadTransform(key)
13 | }
14 | }
15 |
16 | componentDidUpdate (prevProps) {
17 | const prevDataset = prevProps && prevProps.dataset
18 | const prevKey = prevDataset && prevDataset.transform && prevDataset.transform.scriptPath
19 |
20 | const { dataset, loading } = this.props
21 | const key = dataset && dataset.transform && dataset.transform.scriptPath
22 |
23 | if (!loading && prevKey !== key) {
24 | this.props.loadTransform(key)
25 | }
26 | }
27 |
28 | render () {
29 | const { transformString, error, loading } = this.props
30 |
31 | if (loading) {
32 | return
33 | }
34 |
35 | if (transformString) {
36 | return
37 | }
38 |
39 | if (error) {
40 | return {error}
41 | }
42 | return No transform provided in this dataset
43 | }
44 | }
45 |
46 | Transform.propTypes = {
47 |
48 | }
49 |
50 | Transform.defaultProps = {
51 | }
52 |
--------------------------------------------------------------------------------
/lib/components/dataset/Viz.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Spinner from '../chrome/Spinner'
4 | import CodeViewer from '../CodeViewer'
5 |
6 | export default class Viz extends React.PureComponent {
7 | componentDidMount () {
8 | const { dataset, vizString, loading } = this.props
9 | const key = dataset && dataset.viz && dataset.viz.scriptPath
10 |
11 | if (!vizString && key && !loading) {
12 | this.props.loadViz(key)
13 | }
14 | }
15 |
16 | componentDidUpdate (prevProps) {
17 | const prevDataset = prevProps && prevProps.dataset
18 | const prevKey = prevDataset && prevDataset.viz && prevDataset.viz.scriptPath
19 |
20 | const { dataset, loading } = this.props
21 | const key = dataset && dataset.viz && dataset.viz.scriptPath
22 |
23 | if (!loading && prevKey !== key) {
24 | this.props.loadTransform(key)
25 | }
26 | }
27 |
28 | render () {
29 | const { vizString, error, loading } = this.props
30 |
31 | if (loading) {
32 | return
33 | }
34 |
35 | if (vizString) {
36 | return
37 | }
38 |
39 | if (error) {
40 | return {error}
41 | }
42 | return No viz provided in this dataset
43 | }
44 | }
45 |
46 | Viz.propTypes = {
47 |
48 | }
49 |
50 | Viz.defaultProps = {
51 | }
52 |
--------------------------------------------------------------------------------
/lib/components/editor/EditMeta.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Button from '../chrome/Button'
4 | import MetadataForm from '../form/MetadataForm'
5 |
6 | export default class EditMeta extends React.PureComponent {
7 | constructor (props) {
8 | super(props);
9 |
10 | [
11 | 'onChange',
12 | 'onAddMeta',
13 | 'onRemoveMeta'
14 | ].forEach(m => { this[m] = this[m].bind(this) })
15 | }
16 |
17 | onChange (key, value) {
18 | const { meta } = this.props
19 | this.props.onChange(Object.assign({}, meta, { [key]: value }))
20 | }
21 |
22 | onAddMeta () {
23 | this.props.onChange(Object.assign({ title: 'my dataset' }))
24 | }
25 |
26 | onRemoveMeta () {
27 | this.props.onRemove('meta')
28 | }
29 |
30 | render () {
31 | const { meta } = this.props
32 |
33 | if (!Object.keys(meta).length) {
34 | return (
35 |
40 | )
41 | }
42 |
43 | return (
44 |
56 | )
57 | }
58 | }
59 |
60 | EditMeta.propTypes = {
61 | meta: PropTypes.object,
62 | onChange: PropTypes.func.isRequired,
63 | onRemove: PropTypes.func.isRequired
64 | }
65 |
66 | EditMeta.defaultProps = {
67 | meta: {}
68 | }
69 |
--------------------------------------------------------------------------------
/lib/components/editor/EditStructure.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import Button from '../chrome/Button'
5 |
6 | import StructureForm from '../form/StructureForm'
7 |
8 | export default class EditStructure extends React.PureComponent {
9 | constructor (props) {
10 | super(props);
11 |
12 | [
13 | 'onChange',
14 | 'onRemoveStructure',
15 | 'onAddStructure'
16 | ].forEach(m => { this[m] = this[m].bind(this) })
17 | }
18 |
19 | onChange (key, value) {
20 | const { structure } = this.props
21 | this.props.onChangeStructure(Object.assign({}, structure, { [key]: value }))
22 | }
23 |
24 | onRemoveStructure () {
25 | this.props.onRemove('structure')
26 | }
27 |
28 | onAddStructure () {
29 | this.props.onChangeStructure({
30 | format: 'json'
31 | })
32 | this.props.onChangeSchema('')
33 | }
34 |
35 | render () {
36 | const { structure, onChangeSchema, layout } = this.props
37 | var schema = structure.schema
38 | if (typeof schema !== 'string') {
39 | schema = JSON.stringify(schema, null, 2)
40 | } else if (!schema) {
41 | schema = ''
42 | }
43 | if (!Object.keys(structure).length) {
44 | return (
45 |
50 | )
51 | }
52 |
53 | return (
54 |
69 | )
70 | }
71 | }
72 |
73 | EditStructure.propTypes = {
74 | structure: PropTypes.object,
75 | onChangeStructure: PropTypes.func.isRequired
76 | }
77 |
78 | EditStructure.defaultProps = {
79 | structure: {}
80 | }
81 |
--------------------------------------------------------------------------------
/lib/components/editor/Overview.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Json from '../Json'
4 | import ValidInput from '../form/ValidInput'
5 |
6 | export default class Overview extends React.PureComponent {
7 | filterKeys (dataset) {
8 | let ds = Object.assign({}, dataset)
9 | delete ds['path']
10 | return ds
11 | }
12 |
13 | render () {
14 | const { name, localDataset, onChangeName } = this.props
15 |
16 | return (
17 |
18 |
Choose a tab above to start editing
19 |
20 |
Overview
21 |
22 |
Dataset Name:
23 | { onChangeName(name) }} isDatasetRef />
24 |
25 |
This is the JSON form of the dataset as it'll be sent to qri for processing:
26 |
27 |
28 |
29 | )
30 | }
31 | }
32 |
33 | Overview.propTypes = {
34 | localDataset: PropTypes.object,
35 | onChangeName: PropTypes.func
36 | }
37 |
38 | Overview.defaultProps = {
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/lib/components/editor/Results.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Json from '../Json'
4 |
5 | export default class Results extends React.PureComponent {
6 | render () {
7 | const { resultDataset, message, error } = this.props
8 |
9 | return (
10 |
11 | {/* {localDataset &&
} */}
12 | {message &&
13 |
14 |
Message:
15 |
{message}
16 |
}
17 | {error &&
18 |
19 |
Error:
20 |
{error}
21 |
}
22 | {resultDataset &&
23 |
24 | Results:
25 |
26 | }
27 |
28 | )
29 | }
30 | }
31 |
32 | Results.propTypes = {
33 | localDataset: PropTypes.object
34 | }
35 |
36 | Results.defaultProps = {
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/lib/components/editor/SectionPicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { NavLink } from 'react-router-dom'
4 |
5 | export default class SectionPicker extends React.PureComponent {
6 | render () {
7 | const { links, current } = this.props
8 |
9 | return (
10 |
11 |
12 | {links.map((link, i) => (
13 |
14 | {link.title}
15 | )
16 | )}
17 |
18 |
19 | )
20 | }
21 | }
22 |
23 | SectionPicker.propTypes = {
24 | current: PropTypes.string,
25 | links: PropTypes.array
26 | }
27 |
28 | SectionPicker.defaultProps = {
29 | links: []
30 | }
31 |
--------------------------------------------------------------------------------
/lib/components/form/ProfileForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ValidInput from './ValidInput'
4 | import ValidTextarea from './ValidTextarea'
5 |
6 | import ProfileProps from '../../propTypes/profile'
7 |
8 | export default class ProfileForm extends React.PureComponent {
9 | render () {
10 | const { profile, onChange } = this.props
11 | return (
12 |
13 |
19 |
25 |
31 |
37 |
43 |
50 |
51 | )
52 | }
53 | }
54 |
55 | ProfileForm.propTypes = {
56 | profile: ProfileProps,
57 | onChange: PropTypes.func.isRequired
58 | }
59 |
60 | ProfileForm.defaultProps = {
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/lib/components/form/RadioInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class RadioInput extends React.PureComponent {
5 | render () {
6 | const { name, value, text, color, defaultChecked, onChange } = this.props
7 | const className = 'radio-' + color
8 | return (
9 | {text}
10 | )
11 | }
12 | }
13 |
14 | RadioInput.propTypes = {
15 | name: PropTypes.string.isRequired,
16 | value: PropTypes.string.isRequired,
17 | text: PropTypes.string.isRequired,
18 | color: PropTypes.string.isRequired,
19 | defaultChecked: PropTypes.bool,
20 | onChange: PropTypes.func
21 | }
22 |
23 | RadioInput.defaultProps = {
24 | color: 'primary-muted'
25 | }
26 |
--------------------------------------------------------------------------------
/lib/components/form/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class SearchBar extends React.PureComponent {
5 | constructor (props) {
6 | super(props)
7 | this.state = {
8 | search: ''
9 | };
10 | [
11 | ].forEach((m) => { this[m] = this[m].bind(this) })
12 | }
13 |
14 | componentDidMount () {
15 | this.setState({ search: this.props.searchString })
16 | }
17 |
18 | static getDerivedStateFromProps (props, state) {
19 | const searchString = props && props.searchString
20 | const prevSearchString = state && state.search
21 | if (searchString !== prevSearchString) {
22 | return { search: searchString }
23 | }
24 | return null
25 | }
26 |
27 | render () {
28 | const { onKeyUp, onChange } = this.props
29 | return (
30 |
31 | )
32 | }
33 | }
34 |
35 | SearchBar.propTypes = {
36 | onChange: PropTypes.func,
37 | onKeyUp: PropTypes.func,
38 | searchString: PropTypes.string
39 | }
40 |
41 | SearchBar.defaultProps = {
42 | }
43 |
--------------------------------------------------------------------------------
/lib/components/form/StructureFormatConfig.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class StructureFormatConfig extends React.PureComponent {
5 | constructor (props) {
6 | super(props);
7 |
8 | [
9 | 'renderCSV',
10 | 'onChange'
11 | ].forEach((m) => { this[m] = this[m].bind(this) })
12 | }
13 |
14 | onChange (name, value) {
15 | const { formatConfig } = this.props
16 | this.props.onChange('formatConfig', Object.assign({}, formatConfig, { [name]: value }))
17 | }
18 |
19 | renderCSV () {
20 | const { formatConfig } = this.props
21 |
22 | return (
23 |
24 |
Configure CSV format:
25 |
26 |
header row: { this.onChange('headerRow', e.target.checked) }} checked={!!formatConfig.headerRow} />
27 | Whether CSV body has a header row
28 |
29 |
30 |
31 |
lazy quotes: { this.onChange('lazyQuotes', e.target.checked) }} checked={!!formatConfig.lazyQuotes} />
32 | Whether CSV data should be interpreted both with & without quoted fields
33 |
34 |
35 |
36 |
variadic fields: { this.onChange('variadicFields', e.target.checked) }} checked={!!formatConfig.variadicFields} />
37 | permits records to have a variable number of fields. avoid using this if you can.
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | render () {
45 | const { format } = this.props
46 |
47 | switch (format) {
48 | case 'csv':
49 | return this.renderCSV()
50 | default:
51 | return
52 | }
53 | }
54 | }
55 |
56 | StructureFormatConfig.propTypes = {
57 | format: PropTypes.string,
58 | formatConfig: PropTypes.object,
59 | onChange: PropTypes.func.isRequired
60 | }
61 |
62 | StructureFormatConfig.defaultProps = {
63 | formatConfig: {}
64 | }
65 |
--------------------------------------------------------------------------------
/lib/components/form/TextInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class TextInput extends React.PureComponent {
5 | render () {
6 | const { label, name, type, value, errorText, helpText, showHelpText, onChange, placeholder, white } = this.props
7 | const feedbackColor = errorText ? 'error' : showHelpText && helpText && 'textMuted'
8 | const feedback = errorText || (showHelpText &&
9 | helpText)
10 | const labelColor = white ? 'white' : 'primary'
11 | return (
12 |
13 |
14 | {label}
15 | { onChange(name, e.target.value, e) }}
23 | />
24 |
25 |
26 | {feedback &&
{feedback} }
27 |
28 |
29 | )
30 | }
31 | }
32 |
33 | TextInput.propTypes = {
34 | label: PropTypes.string,
35 | name: PropTypes.string,
36 | type: PropTypes.string,
37 | value: PropTypes.string,
38 | errorText: PropTypes.string,
39 | helpText: PropTypes.string,
40 | placeholder: PropTypes.string,
41 | showHelpText: PropTypes.bool,
42 | onChange: PropTypes.func
43 | }
44 |
45 | TextInput.defaultProps = {
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/lib/components/form/ValidCheck.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class ValidCheck extends React.PureComponent {
5 | render () {
6 | const { label, name, className, checked, showError, error, helpText, showHelpText, onChange, labelTop } = this.props
7 | const errorClass = (error && showError) ? 'has-error' : ''
8 | const labelPosition = labelTop ? 'form-control-top' : 'form-control-bottom'
9 | const inputPosition = labelTop ? 'form-control-bottom' : 'form-control-top'
10 | return (
11 |
12 |
13 |
{ onChange(name, e.target.value, e) }} />
14 | {label &&
{label}
}
15 |
16 | {(error !== '' && showError) ?
{error}
: undefined}
17 | {(helpText && showHelpText) &&
{helpText}
}
18 |
19 | )
20 | }
21 | }
22 |
23 | ValidCheck.propTypes = {
24 | // if provided it'll create a label element to accompany the field
25 | label: PropTypes.string,
26 | // required name for the field
27 | name: PropTypes.string.isRequired,
28 | // className will set on the containing div
29 | className: PropTypes.string,
30 | // whether the input starts checked
31 | checked: PropTypes.bool,
32 | showError: PropTypes.bool,
33 | // value to display in the field
34 | value: PropTypes.string,
35 | // short message to help the user
36 | helpText: PropTypes.string,
37 | // weather to show help text or not
38 | showHelpText: PropTypes.bool,
39 | // change handler func. will be called with (name, value, event)
40 | onChange: PropTypes.func.isRequired,
41 | // show label on top
42 | labelTop: PropTypes.bool,
43 | // should a "none" option be allowed?
44 | allowEmpty: PropTypes.bool
45 | }
46 |
47 | ValidCheck.defaultProps = {
48 | name: undefined,
49 | showError: true,
50 | error: undefined,
51 | helpText: '',
52 | showHelpText: false,
53 | allowEmpty: true
54 | }
55 |
--------------------------------------------------------------------------------
/lib/components/form/ValidCitationsInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import List from '../List'
4 | import CitationItem from '../item/CitationItem'
5 |
6 | export default class ValidCitationsInput extends React.PureComponent {
7 | constructor (props) {
8 | super(props);
9 |
10 | [
11 | 'handleRemoveRow',
12 | 'handleChange',
13 | 'handleAddRow'
14 | ].forEach((m) => { this[m] = this[m].bind(this) })
15 | }
16 |
17 | handleAddRow () {
18 | const { value } = this.props
19 | this.handleChange(value.length, {})
20 | }
21 |
22 | handleChange (index, citation) {
23 | const { onChange, value } = this.props
24 | var newCitations = value.slice(0, value.length)
25 | newCitations[index] = citation
26 | onChange('citations', newCitations)
27 | }
28 |
29 | handleRemoveRow (i) {
30 | const { onChange, value } = this.props
31 | var newCitations = value.slice(0, i).concat(value.slice(i + 1))
32 | if (newCitations.length === 0) {
33 | newCitations = undefined
34 | }
35 | onChange('citations', newCitations)
36 | }
37 |
38 | render () {
39 | const { value, error, showError, helpText, showHelpText } = this.props
40 | return (
41 |
42 |
Citations
43 |
49 |
add
50 | {(error !== '' && showError) ?
{error}
: undefined}
51 | {(helpText && showHelpText) &&
{helpText}
}
52 |
53 | )
54 | }
55 | }
56 |
57 | ValidCitationsInput.propTypes = {
58 | value: PropTypes.array,
59 | onChange: PropTypes.func,
60 | // whether or not to actually display any passed-in errors
61 | showError: PropTypes.bool,
62 | // an error message to display
63 | error: PropTypes.string,
64 | // short message to help the user
65 | helpText: PropTypes.string,
66 | // weather to show help text or not
67 | showHelpText: PropTypes.bool
68 | }
69 |
70 | ValidCitationsInput.defaultProps = {
71 | value: []
72 | }
73 |
--------------------------------------------------------------------------------
/lib/components/form/ValidContributorsInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import List from '../List'
4 | import ContributorItem from '../item/ContributorItem'
5 |
6 | export default class ValidContributorsInput extends React.PureComponent {
7 | constructor (props) {
8 | super(props);
9 |
10 | [
11 | 'handleRemoveRow',
12 | 'handleChange',
13 | 'handleAddRow'
14 | ].forEach((m) => { this[m] = this[m].bind(this) })
15 | }
16 |
17 | handleAddRow () {
18 | const { value } = this.props
19 | this.handleChange(value.length, {})
20 | }
21 |
22 | handleChange (index, contributor) {
23 | const { onChange, value } = this.props
24 | var newContributors = value.slice(0, value.length)
25 | newContributors[index] = contributor
26 | onChange('contributors', newContributors)
27 | }
28 |
29 | handleRemoveRow (i) {
30 | const { onChange, value } = this.props
31 | var newContributors = value.slice(0, i).concat(value.slice(i + 1))
32 | if (newContributors.length === 0) {
33 | newContributors = undefined
34 | }
35 | onChange('contributors', newContributors)
36 | }
37 |
38 | render () {
39 | const { value, error, showError, helpText, showHelpText } = this.props
40 | return (
41 |
42 |
Contributors
43 |
49 |
add
50 | {(error !== '' && showError) ?
{error}
: undefined}
51 | {(helpText && showHelpText) &&
{helpText}
}
52 |
53 | )
54 | }
55 | }
56 |
57 | ValidContributorsInput.propTypes = {
58 | value: PropTypes.array,
59 | onChange: PropTypes.func,
60 | // whether or not to actually display any passed-in errors
61 | showError: PropTypes.bool,
62 | // an error message to display
63 | error: PropTypes.string,
64 | // short message to help the user
65 | helpText: PropTypes.string,
66 | // weather to show help text or not
67 | showHelpText: PropTypes.bool
68 | }
69 |
70 | ValidContributorsInput.defaultProps = {
71 | value: []
72 | }
73 |
--------------------------------------------------------------------------------
/lib/components/form/ValidDateTimeInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import DateInput from './DateInput'
5 |
6 | export default class ValidDateTimeInput extends React.PureComponent {
7 | render () {
8 | const { name, value, label, placeholder, disabled, error, showError, helpText, showHelpText, className, onChange, labelTop } = this.props
9 | const errorClass = (error && showError) ? 'has-error' : ''
10 | const labelPosition = labelTop ? 'form-control-top' : 'form-control-bottom'
11 | const inputPosition = labelTop ? 'form-control-bottom' : 'form-control-top'
12 | return (
13 |
14 |
15 |
23 | {label &&
{label}
}
24 |
25 | {(helpText && showHelpText) &&
{helpText}
}
26 |
27 | )
28 | }
29 | }
30 |
31 | ValidDateTimeInput.propTypes = {
32 | // gotta name yo fields
33 | name: PropTypes.string.isRequired,
34 | // if provided it'll create a label element to accompany the field
35 | label: PropTypes.string,
36 | // field value
37 | value: PropTypes.instanceOf(Date),
38 | // placeholder text
39 | placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
40 | // enable / disable the field
41 | disabled: PropTypes.bool,
42 | // error text, if any
43 | error: PropTypes.string,
44 | // explicit control over weather or not to display validation
45 | showError: PropTypes.bool,
46 | // short message to help the user
47 | helpText: PropTypes.string,
48 | // weather to show help text or not
49 | showHelpText: PropTypes.bool,
50 | // className will set on the containing div
51 | className: PropTypes.string,
52 | // onChange in the form (value, name)
53 | onChange: PropTypes.func.isRequired,
54 | // show label on top
55 | labelTop: PropTypes.bool
56 | }
57 |
58 | ValidDateTimeInput.defaultProps = {
59 | name: '',
60 | value: new Date(),
61 | placeholder: '',
62 | helpText: '',
63 | showHelpText: false
64 | }
65 |
--------------------------------------------------------------------------------
/lib/components/form/ViewModeButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class ViewModeButton extends React.PureComponent {
5 | render () {
6 | const { mode, onClick, active } = this.props
7 |
8 | return (
9 |
10 |
11 | {mode === 'viz' ? 'view' : 'form'}
12 |
13 |
14 | )
15 | }
16 | }
17 |
18 | ViewModeButton.propTypes = {
19 | onClick: PropTypes.func.isRequired,
20 | mode: PropTypes.oneOf(['viz', 'dataset'])
21 | }
22 |
23 | ViewModeButton.defaultProps = {
24 | }
25 |
--------------------------------------------------------------------------------
/lib/components/item/CitationItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ValidInput from '../form/ValidInput'
4 |
5 | const CitationItem = ({ index, data, onChange, onRemove }) => {
6 | const handleChange = (name, value, e) => {
7 | data[name] = value
8 | onChange(index, data)
9 | }
10 |
11 | return (
12 |
43 | )
44 | }
45 |
46 | CitationItem.propTypes = {
47 | data: PropTypes.object,
48 | onChange: PropTypes.func,
49 | onRemove: PropTypes.func
50 | }
51 |
52 | CitationItem.defaultProps = {
53 | }
54 |
55 | export default CitationItem
56 |
--------------------------------------------------------------------------------
/lib/components/item/CommitItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Link } from 'react-router-dom'
4 |
5 | import ProfilePhoto from '../profile/ProfilePhoto'
6 | import Hash from '../Hash'
7 | import Datestamp from '../Datestamp'
8 |
9 | const CommitItem = ({ data, index, currentID, profile, registryVersion, sessionProfile }) => {
10 | const commit = data.dataset ? data.dataset.commit : {}
11 | const path = data && data.path
12 | const peername = data && data.peername
13 | const name = data && data.name
14 | const title = commit.title ? commit.title : `change #${index}`
15 | const message = commit.message
16 | const endpoint = `/${peername}/${name}/at${path}`
17 |
18 | const info = currentID === path ? 'commit-item-info commit-info-no-click' : 'commit-item-info'
19 |
20 | return (
21 |
22 | {sessionProfile ?
23 |
24 | :
}
25 |
26 |
{peername}:
{title || (index ? `commit #${index + 1}` : 'Current Dataset')}
27 | {message &&
{message} }
28 |
29 |
30 |
31 | {sessionProfile && registryVersion === path &&
star
}
32 |
33 | )
34 | }
35 |
36 | CommitItem.propTypes = {
37 | data: PropTypes.object.isRequired,
38 | index: PropTypes.number.isRequired
39 | }
40 |
41 | CommitItem.defaultProps = {
42 | }
43 |
44 | export default CommitItem
45 |
--------------------------------------------------------------------------------
/lib/components/item/ContributorItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ValidInput from '../form/ValidInput'
4 |
5 | const ContributorItem = ({ index, data, onChange, onRemove }) => {
6 | const handleChange = (name, value, e) => {
7 | onChange(index, Object.assign({}, data, { [name]: value }))
8 | }
9 |
10 | return (
11 |
43 | )
44 | }
45 |
46 | ContributorItem.propTypes = {
47 | data: PropTypes.object,
48 | onChange: PropTypes.func,
49 | onRemove: PropTypes.func
50 | }
51 |
52 | ContributorItem.defaultProps = {
53 | }
54 |
55 | export default ContributorItem
56 |
--------------------------------------------------------------------------------
/lib/components/item/FieldItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { defaultPalette } from '../proptypes/palette'
4 | function dataTypeColor (type, palette = defaultPalette) {
5 | switch (type) {
6 | case 'string':
7 | return palette.text
8 | case 'integer':
9 | return palette.primary
10 | case 'float':
11 | return palette.primary
12 | case 'boolean':
13 | return palette.primary
14 | case 'date':
15 | return palette.primary
16 | default:
17 | return palette.text
18 | }
19 | }
20 |
21 | const FieldItem = ({ data, onSelect, index }) => {
22 | return (
23 |
24 |
{index + 1} {data.type}
25 |
{data.name}
26 |
27 | {data.missingValue &&
default value: {data.missingValue}
}
28 | {data.description &&
{data.description}
}
29 |
30 | )
31 | }
32 |
33 | FieldItem.propTypes = {
34 | data: PropTypes.object.isRequired,
35 | onSelect: PropTypes.func.isRequired
36 | }
37 |
38 | FieldItem.defaultProps = {
39 | }
40 |
41 | export default FieldItem
42 |
--------------------------------------------------------------------------------
/lib/components/item/KeyValueItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import ValidInput from '../form/ValidInput'
4 |
5 | const KeyValueItem = ({ index, data, onChange, onRemove }) => {
6 | const handleChange = (name, value, e) => {
7 | onChange(index, Object.assign({}, data, { [name]: value }))
8 | }
9 |
10 | return (
11 |
34 | )
35 | }
36 |
37 | KeyValueItem.propTypes = {
38 | data: PropTypes.object,
39 | onChange: PropTypes.func,
40 | onRemove: PropTypes.func
41 | }
42 |
43 | KeyValueItem.defaultProps = {
44 | }
45 |
46 | export default KeyValueItem
47 |
--------------------------------------------------------------------------------
/lib/components/item/ProfileItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Link } from 'react-router-dom'
4 |
5 | import ProfilePhoto from '../profile/ProfilePhoto'
6 | import Online from '../Online'
7 | const ProfileItem = ({ data, showStatus }) => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | { data.connected ?
⚡ : undefined }
15 |
16 |
{data.peername || 'unnamed peer'}
17 |
18 | {data.name &&
{data.name}
}
19 | {showStatus ?
: undefined}
20 |
21 |
22 | )
23 | }
24 |
25 | ProfileItem.propTypes = {
26 | data: PropTypes.object.isRequired,
27 | link: PropTypes.bool,
28 | showStatus: PropTypes.bool
29 | }
30 |
31 | ProfileItem.defaultProps = {
32 | showStatus: true
33 | }
34 |
35 | export default ProfileItem
36 |
--------------------------------------------------------------------------------
/lib/components/item/SearchItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import DatasetItem from './DatasetItem'
5 | import ProfileItem from './ProfileItem'
6 |
7 | const processDatasetResult = (data) => {
8 | return {
9 | peername: data['Value']['Handle'],
10 | name: data['Value']['Name'],
11 | path: data['Value']['path'],
12 | dataset: {
13 | meta: data['Value']['meta'],
14 | structure: data['Value']['structure'],
15 | commit: data['Value']['commit']
16 | }
17 | }
18 | }
19 |
20 | const processProfileResult = (data) => {
21 | return data['Value']
22 | }
23 |
24 | const SearchItem = (props) => {
25 | const { data } = props
26 |
27 | switch (data['Type']) {
28 | case 'peer':
29 | return
30 | case 'dataset':
31 | default:
32 | return
33 | // return error: search result has no type. must be either 'dataset' or 'peer'
34 | // the default should really be the above, but since the search responses have been returning
35 | // without a 'Type', I'm going to have the default return dataset
36 | }
37 | }
38 |
39 | SearchItem.propTypes = {
40 | data: PropTypes.object.isRequired
41 | }
42 |
43 | export default SearchItem
44 |
--------------------------------------------------------------------------------
/lib/components/item/StatItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const StatItem = ({ data, style }) => {
5 | return (
6 |
7 |
{data.icon}
8 |
9 |
10 |
{data.stat}
11 |
12 |
13 | )
14 | }
15 |
16 | StatItem.propTypes = {
17 | stats: PropTypes.arrayOf(PropTypes.object).isRequired,
18 | muted: PropTypes.bool,
19 | extraSpace: PropTypes.bool,
20 | style: PropTypes.object
21 | }
22 |
23 | StatItem.defaultProps = {
24 | style: {},
25 | stats: [],
26 | muted: false,
27 | extraSpace: false,
28 | large: false
29 | }
30 |
31 | export default StatItem
32 |
--------------------------------------------------------------------------------
/lib/components/item/TagItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const TagItem = ({ tag }) => {
5 | return {tag}
6 | }
7 |
8 | TagItem.propTypes = {
9 | tag: PropTypes.string
10 | // add in when we move styling to scss
11 | // color: PropTypes.string
12 | }
13 |
14 | TagItem.defaultProps = {
15 | }
16 |
17 | export default TagItem
18 |
--------------------------------------------------------------------------------
/lib/components/profile/NoProfile.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | import Header from '../Header'
5 | export default class NoProfile extends React.PureComponent {
6 | constructor (props) {
7 | super(props);
8 |
9 | [
10 | 'renderHeader'
11 | ].forEach((m) => { this[m] = this[m].bind(this) })
12 | }
13 |
14 | render () {
15 | const session = this.props.sessionProfileId ? 'no-profile-session' : 'no-profile-no-session'
16 | return (
17 |
18 |
19 |
20 |
21 |
Profile '{this.props.peername}' not found or cannot be loaded
22 |
23 |
24 |
25 | )
26 | }
27 | }
28 |
29 | NoProfile.propTypes = {
30 | onGoBack: PropTypes.func.isRequired,
31 | peername: PropTypes.string.isRequired,
32 | sessionProfileId: PropTypes.string
33 | }
34 |
35 | NoProfile.defaultProps = {
36 | }
37 |
--------------------------------------------------------------------------------
/lib/components/profile/ProfilePhoto.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | import React from 'react'
3 | import PropTypes from 'prop-types'
4 |
5 | export default class ProfilePhoto extends React.PureComponent {
6 | render () {
7 | const { profile = {} } = this.props
8 | const { size } = this.props
9 | let side = { 'sm': 34, 'md': 64, 'lg': 100, 'xlg': 150 }[size]
10 | let borderRadius = { 'sm': 4, 'md': 6, 'lg': 8, 'xlg': 10 }[size]
11 | return (
12 |
18 | )
19 | }
20 | }
21 |
22 | ProfilePhoto.propTypes = {
23 | size: PropTypes.oneOf(['sm', 'md', 'lg', 'xlg']),
24 | profile: PropTypes.object
25 | }
26 |
27 | ProfilePhoto.defaultProps = {
28 | size: 'md'
29 | }
30 |
--------------------------------------------------------------------------------
/lib/components/stylesheet/Swatch.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class Swatch extends React.PureComponent {
5 | render () {
6 | const { style, title, hex } = this.props
7 | return (
8 |
9 |
10 |
{title}
11 |
{hex}
12 |
13 | )
14 | }
15 | }
16 |
17 | Swatch.propTypes = {
18 | style: PropTypes.object,
19 | title: PropTypes.string,
20 | hex: PropTypes.string
21 | }
22 |
--------------------------------------------------------------------------------
/lib/constants/app.js:
--------------------------------------------------------------------------------
1 | export const APP_ACCEPT_TOS = 'ACCEPT_TOS'
2 | export const APP_HAS_SET_PEERNAME = 'APP_HAS_SET_PEERNAME'
3 | export const APP_PING_API = 'APP_PING_API'
4 | export const APP_TOGGLE_MENU = 'APP_TOGGLE_MENU'
5 | export const APP_HIDE_MENU = 'APP_HIDE_MENU'
6 | export const APP_SHOW_MODAL = 'APP_SHOW_MODAL'
7 | export const APP_HIDE_MODAL = 'APP_HIDE_MODAL'
8 | export const APP_AT_BOTTOM = 'APP_AT_BOTTOM'
9 | export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE'
10 | export const RESET_ERROR_MESSAGE = 'RESET_ERROR_MESSAGE'
11 | export const RESET_MESSAGE = 'RESET_MESSAGE'
12 | export const SET_MESSAGE = 'SET_MESSAGE'
13 | export const REMOVE_MODEL = 'REMOVE_MODEL'
14 | export const SET_SEARCH = 'SET_SEARCH'
15 | export const CLEAR_PAGINATION = 'CLEAR_PAGINATION'
16 | export const CLEAR_PAGINATION_IDS = 'CLEAR_PAGINATION_IDS'
17 | export const API_CONNECTION_FAILURE = 'API_CONNECTION_FAILURE'
18 | export const SET_VIEW_MODE = 'SET_VIEW_MODE'
19 | export const SET_LOCATION_TEXT = 'SET_LOCATION_TEXT'
20 |
--------------------------------------------------------------------------------
/lib/constants/body.js:
--------------------------------------------------------------------------------
1 | export const CLEAR_BODY = 'CLEAR_BODY'
2 | export const BODY_REQUEST = 'BODY_REQUEST'
3 | export const BODY_SUCCESS = 'BODY_SUCCESS'
4 | export const BODY_FAILURE = 'BODY_FAILURE'
5 |
--------------------------------------------------------------------------------
/lib/constants/cafs.js:
--------------------------------------------------------------------------------
1 | // GET_CAF actions
2 | export const CAFS_REQUEST = 'CAFS_REQUEST'
3 | export const CAFS_SUCCESS = 'CAFS_SUCCESS'
4 | export const CAFS_FAILURE = 'CAFS_FAILURE'
5 | export const CAFS_TRANSFORM_REQUEST = 'CAFS_TRANSFORM_REQUEST'
6 | export const CAFS_TRANSFORM_SUCCESS = 'CAFS_TRANSFORM_SUCCESS'
7 | export const CAFS_TRANSFORM_FAILURE = 'CAFS_TRANSFORM_FAILURE'
8 | export const CAFS_VIZ_REQUEST = 'CAFS_VIZ_REQUEST'
9 | export const CAFS_VIZ_SUCCESS = 'CAFS_VIZ_SUCCESS'
10 | export const CAFS_VIZ_FAILURE = 'CAFS_VIZ_FAILURE'
11 | export const CAFS_BODY_FILE_REQUEST = 'CAFS_BODY_FILE_REQUEST'
12 | export const CAFS_BODY_FILE_SUCCESS = 'CAFS_BODY_FILE_SUCCESS'
13 | export const CAFS_BODY_FILE_FAILURE = 'CAFS_BODY_FILE_FAILURE'
14 |
--------------------------------------------------------------------------------
/lib/constants/console.js:
--------------------------------------------------------------------------------
1 | export const CONSOLE_SET_CHART_OPTIONS = 'CONSOLE_SET_CHART_OPTIONS'
2 | export const CONSOLE_RESET_CHART_OPTIONS = 'CONSOLE_RESET_CHART_OPTIONS'
3 | export const CONSOLE_SET_DATA_LOADING = 'CONSOLE_SET_DATA_LOADING'
4 |
--------------------------------------------------------------------------------
/lib/constants/editor.js:
--------------------------------------------------------------------------------
1 | export const EDITOR_INIT_DATASET = 'EDITOR_INIT_DATASET'
2 | export const EDITOR_SET_NAME = 'EDITOR_SET_NAME'
3 | export const EDITOR_SET_COMMIT = 'EDITOR_SET_COMMIT'
4 | export const EDITOR_SET_META = 'EDITOR_SET_META'
5 | export const EDITOR_SET_STRUCTURE = 'EDITOR_SET_STRUCTURE'
6 | export const EDITOR_SET_VIZ = 'EDITOR_SET_VIZ'
7 | export const EDITOR_SET_TRANSFORM = 'EDITOR_SET_TRANSFORM'
8 | export const EDITOR_SET_BODY = 'EDITOR_SET_BODY'
9 | export const EDITOR_SET_VIZ_SCRIPT = 'EDITOR_SET_VIZ_SCRIPT'
10 | export const EDITOR_SET_TRANSFORM_SCRIPT = 'EDITOR_SET_TRANFORM_SCRIPT'
11 | export const EDITOR_UPDATE_BODY = 'EDITOR_UPDATE_BODY'
12 | export const EDITOR_REMOVE_SECTION = 'EDITOR_REMOVE_SECTION'
13 | export const EDITOR_SET_SCHEMA = 'EDITOR_SET_SCHEMA'
14 | export const EDITOR_SET_BODY_VIEW = 'EDITOR_SET_BODY_VIEW'
15 | export const EDITOR_SET_COL_ORDER = 'EDITOR_SET_COL_ORDER'
16 | export const EDITOR_SET_ROW_ORDER = 'EDITOR_SET_ROW_ORDER'
17 | export const EDITOR_SET_BODY_ERROR = 'EDITOR_SET_BODY_ERROR'
18 | export const EDITOR_SET_BODYFILE = 'EDITOR_SET_BODYFILE'
19 |
--------------------------------------------------------------------------------
/lib/constants/history.js:
--------------------------------------------------------------------------------
1 | export const HISTORY_REQUEST = 'HISTORY_REQUEST'
2 | export const HISTORY_SUCCESS = 'HISTORY_SUCCESS'
3 | export const HISTORY_FAILURE = 'HISTORY_FAILURE'
4 |
--------------------------------------------------------------------------------
/lib/constants/layout.js:
--------------------------------------------------------------------------------
1 | export const LAYOUT_RESIZE = 'LAYOUT_RESIZE'
2 |
--------------------------------------------------------------------------------
/lib/constants/profiles.js:
--------------------------------------------------------------------------------
1 | export const PROFILE_BY_ID_REQUEST = 'PROFILE_BY_ID_REQUEST'
2 | export const PROFILE_BY_ID_SUCCESS = 'PROFILE_BY_ID_SUCCESS'
3 | export const PROFILE_BY_ID_FAILURE = 'PROFILE_BY_ID_FAILURE'
4 | export const PROFILE_BY_NAME_REQUEST = 'PROFILE_BY_NAME_REQUEST'
5 | export const PROFILE_BY_NAME_SUCCESS = 'PROFILE_BY_NAME_SUCCESS'
6 | export const PROFILE_BY_NAME_FAILURE = 'PROFILE_BY_NAME_FAILURE'
7 | export const PROFILES_REQUEST = 'PROFILES_REQUEST'
8 | export const PROFILES_SUCCESS = 'PROFILES_SUCCESS'
9 | export const PROFILES_FAILURE = 'PROFILES_FAILURE'
10 |
--------------------------------------------------------------------------------
/lib/constants/registry.js:
--------------------------------------------------------------------------------
1 | export const REGISTRY_LIST_REQUEST = 'REGISTRY_LIST_REQUEST'
2 | export const REGISTRY_LIST_SUCCESS = 'REGISTRY_LIST_SUCCESS'
3 | export const REGISTRY_LIST_FAILURE = 'REGISTRY_LIST_FAILURE'
4 | export const REGISTRY_DATASET_REQUEST = 'REGISTRY_DATASET_REQUEST'
5 | export const REGISTRY_DATASET_SUCCESS = 'REGISTRY_DATASET_SUCCESS'
6 | export const REGISTRY_DATASET_FAILURE = 'REGISTRY_DATASET_FAILURE'
7 |
--------------------------------------------------------------------------------
/lib/constants/session.js:
--------------------------------------------------------------------------------
1 | export const SESSION_PROFILE_REQUEST = 'SESSION_PROFILE_REQUEST'
2 | export const SESSION_PROFILE_SUCCESS = 'SESSION_PROFILE_SUCCESS'
3 | export const SESSION_PROFILE_FAILURE = 'SESSION_PROFILE_FAILURE'
4 | export const SESSION_DATASETS_REQUEST = 'SESSION_DATASETS_REQUEST'
5 | export const SESSION_DATASETS_SUCCESS = 'SESSION_DATASETS_SUCCESS'
6 | export const SESSION_DATASETS_FAILURE = 'SESSION_DATASETS_FAILURE'
7 | export const SET_PROFILE_PHOTO_REQUEST = 'SET_PROFILE_PHOTO_REQUEST'
8 | export const SET_PROFILE_PHOTO_SUCCESS = 'SET_PROFILE_PHOTO_SUCCESS'
9 | export const SET_PROFILE_PHOTO_FAILURE = 'SET_PROFILE_PHOTO_FAILURE'
10 | export const SET_PROFILE_POSTER_REQUEST = 'SET_PROFILE_POSTER_REQUEST'
11 | export const SET_PROFILE_POSTER_SUCCESS = 'SET_PROFILE_POSTER_SUCCESS'
12 | export const SET_PROFILE_POSTER_FAILURE = 'SET_PROFILE_POSTER_FAILURE'
13 | export const SET_SESSION_PROFILE_REQUEST = 'SET_SESSION_PROFILE_REQUEST'
14 | export const SET_SESSION_PROFILE_SUCCESS = 'SET_SESSION_PROFILE_SUCCESS'
15 | export const SET_SESSION_PROFILE_FAILURE = 'SET_SESSION_PROFILE_FAILURE'
16 | export const SESSION_UPDATE = 'SESSION_UPDATE'
17 | export const CREATE_LOCAL_SESSION_PROFILE = 'CREATE_LOCAL_SESSION_PROFILE'
18 |
--------------------------------------------------------------------------------
/lib/constants/transfers.js:
--------------------------------------------------------------------------------
1 | export const SET_TRANSFER_STATUS = 'SET_TRANSFER_STATUS'
2 | export const REMOVE_TRANSFER_STATUS = 'REMOVE_TRANSFER_STATUS'
3 |
--------------------------------------------------------------------------------
/lib/containers/App.js:
--------------------------------------------------------------------------------
1 |
2 | import { connect } from 'react-redux'
3 |
4 | import { hideMenu, resetMessage, resetErrorMessage, showModal, hideModal, pingApi, apiConnectionFail, setApphitBottom } from '../actions/app'
5 | import { layoutResize } from '../actions/layout'
6 | import { loadDatasets } from '../actions/dataset'
7 | import { loadRegistryDatasets } from '../actions/registry'
8 | import { loadProfiles } from '../actions/profiles'
9 | import { loadHistoryByName } from '../actions/history'
10 | import { loadSessionProfile } from '../actions/session'
11 | import { selectSessionProfileId, selectSessionDatasetIds, selectSessionDatasets, selectSessionProfile } from '../selectors/session'
12 |
13 | import App from '../components/App'
14 |
15 | const AppContainer = connect(
16 | (state, ownProps) => {
17 | return Object.assign({
18 | editorDirty: state.editor.dirty,
19 | acceptedTOS: state.app.acceptedTOS,
20 | hasSetPeername: state.app.hasSetPeername,
21 | apiConnection: state.app.apiConnection,
22 | message: state.app.message,
23 | errorMessage: state.errorMessage,
24 | sessionProfile: selectSessionProfileId(state),
25 | profile: selectSessionProfile(state),
26 | showMenu: state.app.showMenu,
27 | layout: state.layout,
28 | // https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking
29 | location: state.router.location,
30 | modal: state.app.modal,
31 | datasetIds: selectSessionDatasetIds(state),
32 | datasets: selectSessionDatasets(state),
33 | hitBottom: state.app.hitBottom
34 | }, ownProps)
35 | }, {
36 | resetMessage,
37 | resetErrorMessage,
38 | loadSessionProfile,
39 | layoutResize,
40 | hideMenu,
41 | showModal,
42 | hideModal,
43 | pingApi,
44 | apiConnectionFail,
45 | loadDatasets,
46 | loadProfiles,
47 | loadHistoryByName,
48 | loadRegistryDatasets,
49 | setApphitBottom
50 | }
51 | )(App, 'App')
52 |
53 | export default AppContainer
54 |
--------------------------------------------------------------------------------
/lib/containers/ChoosePeername.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { loadDatasets } from '../actions/dataset'
4 | import { loadSessionProfile, saveSessionProfile, updateSession, createLocalSession } from '../actions/session'
5 | import { selectSessionProfile, selectLocalProfile } from '../selectors/session'
6 | import { hasSetPeername } from '../actions/app'
7 |
8 | import ChoosePeername from '../components/ChoosePeername'
9 |
10 | const ChoosePeernameContainer = connect(
11 | (state, ownProps) => {
12 | return Object.assign({
13 | sessionProfile: selectSessionProfile(state),
14 | localProfile: selectLocalProfile(state)
15 | }, ownProps)
16 | }, {
17 | loadSessionProfile,
18 | updateSession,
19 | saveSessionProfile,
20 | createLocalSession,
21 | hasSetPeername,
22 | loadDatasets
23 | }
24 | )(ChoosePeername, 'ChoosePeername')
25 |
26 | export default ChoosePeernameContainer
27 |
--------------------------------------------------------------------------------
/lib/containers/Collection.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { showModal, clearPaginationIds } from '../actions/app'
4 | import { loadDatasets, addDataset } from '../actions/dataset'
5 | import { selectNoDatasets, selectDatasets } from '../selectors/dataset'
6 | import { selectSessionProfileId } from '../selectors/session'
7 | import { selectStats } from '../selectors/stats'
8 | import { selectIsFetching, selectPageCount, selectFetchedAll } from '../selectors/pagination'
9 |
10 | import Collection from '../components/Collection'
11 |
12 | const CollectionContainer = connect(
13 | (state, ownProps) => {
14 | const sessionProfileId = selectSessionProfileId(state)
15 | const paginationSection = 'popularDatasets'
16 | const paginationNode = sessionProfileId
17 | return Object.assign({
18 | sessionProfileId,
19 | datasets: selectDatasets(state, paginationSection, paginationNode),
20 | noDatasets: selectNoDatasets(state, paginationSection, paginationNode),
21 | loading: selectIsFetching(state, paginationSection, paginationNode),
22 | nextPage: selectPageCount(state, paginationSection, paginationNode) + 1,
23 | fetchedAll: selectFetchedAll(state, paginationSection, paginationNode),
24 | stats: selectStats(state),
25 | hitBottom: state.app.hitBottom
26 | }, ownProps)
27 | }, {
28 | addDataset,
29 | // TODO - restore search
30 | // setDatasetSearch,
31 | // runDatasetSearch,
32 | showModal,
33 | loadDatasets,
34 | clearPaginationIds
35 | }
36 | )(Collection, 'Collection')
37 |
38 | export default CollectionContainer
39 |
--------------------------------------------------------------------------------
/lib/containers/CommitItem.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { selectProfileById } from '../selectors/profiles'
4 | import { loadProfileById } from '../actions/profiles'
5 | import CommitItem from '../components/item/CommitItem'
6 |
7 | const CommitItemContainer = connect(
8 | (state, ownProps) => {
9 | const profile = ownProps.data && selectProfileById(state, ownProps.data.profileID)
10 | return Object.assign({}, {
11 | profile
12 | }, state.console, ownProps)
13 | }, {
14 | loadProfileById
15 | }
16 | )(CommitItem, 'CommitItem')
17 |
18 | export default CommitItemContainer
19 |
--------------------------------------------------------------------------------
/lib/containers/Commits.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { loadHistoryByName } from '../actions/history'
4 | import { selectHistory } from '../selectors/history'
5 | import { selectIsFetching, selectPageCount, selectFetchedAll } from '../selectors/pagination'
6 | import { selectSessionProfileId } from '../selectors/session'
7 |
8 | import Commits from '../components/dataset/Commits'
9 |
10 | const CommitsContainer = connect(
11 | (state, ownProps) => {
12 | // const searchString = selectHistoryearchString(state)
13 | const paginationSection = 'datasetHistory'
14 | const searchString = `/${ownProps.peername}/${ownProps.name}`
15 | return Object.assign({
16 | sessionProfile: selectSessionProfileId(state),
17 | log: selectHistory(state, paginationSection, searchString),
18 | loading: selectIsFetching(state, paginationSection, searchString),
19 | nextPage: selectPageCount(state, paginationSection, searchString) + 1,
20 | fetchedAll: selectFetchedAll(state, paginationSection, searchString),
21 | hitBottom: state.app.hitBottom
22 | }, ownProps)
23 | }, {
24 | loadHistoryByName
25 | }
26 | )(Commits, 'History')
27 |
28 | export default CommitsContainer
29 |
--------------------------------------------------------------------------------
/lib/containers/Network.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { showModal } from '../actions/app'
4 | import { loadProfiles } from '../actions/profiles'
5 | import { loadRegistryDatasets } from '../actions/registry'
6 | import { selectProfiles } from '../selectors/profiles'
7 | import { selectIsFetching, selectPageCount, selectFetchedAll } from '../selectors/pagination'
8 | import {
9 | selectRegistryListIsFetching,
10 | selectRegistryList,
11 | selectRegistryListFetchedAll,
12 | selectRegistryListError,
13 | selectRegistryListPageCount
14 | } from '../selectors/registry'
15 | import { selectSessionProfileId } from '../selectors/session'
16 |
17 | import Network from '../components/Network'
18 |
19 | const NetworkContainer = connect(
20 | (state, ownProps) => {
21 | // const searchString = selectProfileSearchString(state)
22 | const paginationSection = 'popularProfiles'
23 | const searchString = 'popularProfiles'
24 | return Object.assign({
25 | sessionProfile: selectSessionProfileId(state),
26 | profiles: selectProfiles(state, paginationSection, searchString),
27 | profilesLoading: selectIsFetching(state, paginationSection, searchString),
28 | profileFetchedAll: selectFetchedAll(state, paginationSection, searchString),
29 | profileNextPage: selectPageCount(state, paginationSection, searchString) + 1,
30 | // TODO: list the peer's datasets
31 | datasets: selectRegistryList(state),
32 | datasetsLoading: selectRegistryListIsFetching(state),
33 | datasetsFetchedAll: selectRegistryListFetchedAll(state),
34 | datasetsFetchedError: selectRegistryListError(state),
35 | datasetsNextPage: selectRegistryListPageCount(state) + 1,
36 | hitBottom: state.app.hitBottom
37 | }, ownProps)
38 | }, {
39 | showModal,
40 | loadProfiles,
41 | loadRegistryDatasets
42 | }
43 | )(Network, 'Network')
44 |
45 | export default NetworkContainer
46 |
--------------------------------------------------------------------------------
/lib/containers/ProfileEditor.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { loadSessionProfile, saveSessionProfile, updateSession, createLocalSession } from '../actions/session'
4 | import { selectSessionProfile, selectLocalProfile } from '../selectors/session'
5 | import { hideModal } from '../actions/app'
6 |
7 | import ProfileEditor from '../components/profile/ProfileEditor'
8 |
9 | const ProfileEditorContainer = connect(
10 | (state, ownProps) => {
11 | return Object.assign({
12 | profile: selectSessionProfile(state),
13 | localProfile: selectLocalProfile(state)
14 | }, ownProps)
15 | }, {
16 | loadSessionProfile,
17 | updateSession,
18 | saveSessionProfile,
19 | createLocalSession,
20 | hideModal
21 | }
22 | )(ProfileEditor, 'ProfileEditor')
23 |
24 | export default ProfileEditorContainer
25 |
--------------------------------------------------------------------------------
/lib/containers/Root.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ConnectedRouter } from 'connected-react-router'
3 | import { Provider } from 'react-redux'
4 | import Routes from '../routes'
5 | import history from '../history'
6 |
7 | export default function Root ({ store }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/lib/containers/SearchResults.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { parse } from 'query-string'
3 |
4 | import { selectSearchString } from '../selectors/app'
5 | import { selectSessionProfileId } from '../selectors/session'
6 | import { selectSearchResults } from '../selectors/search'
7 | import { selectIsFetching, selectPageCount, selectFetchedAll, selectError } from '../selectors/pagination'
8 | import { runDatasetSearch } from '../actions/dataset'
9 |
10 | import SearchResults from '../components/SearchResults'
11 |
12 | const SearchResultsContainer = connect(
13 | (state, ownProps) => {
14 | const { location } = ownProps
15 | const sessionProfile = selectSessionProfileId(state)
16 | const searchParams = location && parse(location.search)
17 | const searchString = searchParams['q'] || selectSearchString(state)
18 |
19 | const searchSection = 'searchedDatasets'
20 |
21 | return Object.assign({
22 | sessionProfile,
23 | searchString,
24 | searchResults: selectSearchResults(state, searchString),
25 | isFetching: selectIsFetching(state, searchSection, searchString),
26 | nextPage: selectPageCount(state, searchSection, searchString) + 1,
27 | fetchedAll: selectFetchedAll(state, searchSection, searchString),
28 | error: selectError(state, searchSection, searchString),
29 | hitBottom: state.app.hitBottom
30 | }, ownProps)
31 | }, {
32 | runDatasetSearch
33 | }
34 | )(SearchResults, 'SearchResults')
35 |
36 | export default SearchResultsContainer
37 |
--------------------------------------------------------------------------------
/lib/containers/TopBar.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { setLocationBarText, setSearch } from '../actions/app'
4 | import { loadHistoryByName } from '../actions/history'
5 | import { selectSessionProfileId } from '../selectors/session'
6 | import history from '../history'
7 | import { selectLocation } from '../selectors/app'
8 | import { runDatasetSearch } from '../actions/dataset'
9 |
10 | import TopBar from '../components/chrome/TopBar'
11 |
12 | const TopBarContainer = connect(
13 | (state, ownProps) => {
14 | return {
15 | sessionProfile: selectSessionProfileId(state),
16 | showMenu: state.app.showMenu,
17 | layout: state.layout.topbar,
18 | location: history.location,
19 | viewMode: state.app.viewMode,
20 | searchString: selectLocation(state),
21 | history
22 | }
23 | }, {
24 | loadHistoryByName,
25 | setLocationBarText,
26 | runDatasetSearch,
27 | setSearch
28 | }
29 | )(TopBar, 'TopBar')
30 |
31 | export default TopBarContainer
32 |
--------------------------------------------------------------------------------
/lib/containers/Transform.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { selectCAFSString, selectCAFSLoading, selectCAFSError } from '../selectors/cafs'
4 | import { loadTransform } from '../actions/dataset'
5 | import Transform from '../components/dataset/Transform'
6 |
7 | const TransformContainer = connect(
8 | (state, ownProps) => {
9 | const { dataset } = ownProps
10 | const key = dataset && dataset.transform && dataset.transform.scriptPath
11 | return Object.assign({}, {
12 | transformString: key && selectCAFSString(state, key),
13 | loading: key && selectCAFSLoading(state, key),
14 | error: key && selectCAFSError(state, key)
15 | }, state.console, ownProps)
16 | }, {
17 | loadTransform
18 | }
19 | )(Transform, 'Transform')
20 |
21 | export default TransformContainer
22 |
--------------------------------------------------------------------------------
/lib/containers/Viz.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { selectCAFSString, selectCAFSLoading, selectCAFSError } from '../selectors/cafs'
4 | import { loadViz } from '../actions/dataset'
5 | import Viz from '../components/dataset/viz'
6 |
7 | const VizContainer = connect(
8 | (state, ownProps) => {
9 | const { dataset } = ownProps
10 | const key = dataset && dataset.viz && dataset.viz.scriptPath
11 | return Object.assign({}, {
12 | vizString: key && selectCAFSString(state, key),
13 | loading: key && selectCAFSLoading(state, key),
14 | error: key && selectCAFSError(state, key)
15 | }, state.console, ownProps)
16 | }, {
17 | loadViz
18 | }
19 | )(Viz, 'Viz')
20 |
21 | export default VizContainer
22 |
--------------------------------------------------------------------------------
/lib/containers/Welcome.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 |
3 | import { acceptTos } from '../actions/app'
4 | import Welcome from '../components/Welcome'
5 |
6 | const WelcomeContainer = connect(
7 | (state, ownProps) => {
8 | return Object.assign({}, ownProps)
9 | }, {
10 | onAccept: acceptTos
11 | }
12 | )(Welcome, 'Welcome')
13 |
14 | export default WelcomeContainer
15 |
--------------------------------------------------------------------------------
/lib/history.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | import { createHashHistory, createBrowserHistory } from 'history'
3 |
4 | const history = (__BUILD__.ELECTRON) ? createHashHistory() : createBrowserHistory()
5 | export default history
6 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { AppContainer as ReactHotAppContainer } from 'react-hot-loader'
4 | import Root from './containers/Root'
5 | import configureStore from './store/configureStore'
6 | import './app.global.scss'
7 | import './monaco'
8 |
9 | const store = configureStore()
10 |
11 | render(
12 |
13 |
14 | ,
15 | document.getElementById('root')
16 | )
17 |
18 | if (module.hot) {
19 | module.hot.accept('./containers/Root', () => {
20 | const NextRoot = require('./containers/Root') // eslint-disable-line global-require
21 | render(
22 |
23 |
24 | ,
25 | document.getElementById('root')
26 | )
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/lib/middleware/extractBody.js:
--------------------------------------------------------------------------------
1 | import { BODY_SUCCESS } from '../constants/body'
2 |
3 | export default store => next => action => {
4 | if (action.type !== BODY_SUCCESS) {
5 | return next(action)
6 | }
7 | const body = action && action.response && action.response.entities && action.response.entities.body
8 | const keys = body && Object.keys(body)
9 | const key = keys && keys.length > 0 && keys[0]
10 | const data = body && key && body[key] && body[key].data
11 | return next({
12 | type: BODY_SUCCESS,
13 | data
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/lib/middleware/localModels.js:
--------------------------------------------------------------------------------
1 | import { normalize } from 'normalizr'
2 |
3 | export const NEW_MODEL = 'NEW_MODEL'
4 | export const UPDATE_MODEL = 'UPDATE_MODEL'
5 | export const EDIT_MODEL = 'EDIT_MODEL'
6 | export const CLEAR_MODEL = 'CLEAR_MODEL'
7 |
8 | export const LOCAL_ACTION = Symbol('LOCAL MODEL ACTION')
9 |
10 | export default store => next => action => {
11 | const modelAction = action[LOCAL_ACTION]
12 | if (typeof modelAction === 'undefined') {
13 | return next(action)
14 | }
15 |
16 | const { method, type, schema, attributes } = modelAction
17 |
18 | if (!method) {
19 | console.warn('model action is missing method. type: %s. schema: %s, attributes:', type, schema.getKey(), attributes)
20 | }
21 | if (!type) {
22 | // TODO - warnings
23 | console.warn('model action is missing method. type: %s. schema: %s, attributes:', type, schema.getKey(), attributes)
24 | }
25 | if (!schema) {
26 | // TODO - warnings
27 | }
28 | if (!attributes) {
29 | // TODO - warnings
30 | }
31 |
32 | function actionWith (data) {
33 | const finalAction = Object.assign({}, action, { type }, data)
34 | delete finalAction[LOCAL_ACTION]
35 | return finalAction
36 | }
37 |
38 | switch (method) {
39 | case NEW_MODEL:
40 | const model = schema.new(attributes)
41 | return next(actionWith({ locals: normalize(model, schema) }))
42 | case UPDATE_MODEL:
43 | return next(actionWith({ locals: normalize(attributes, schema) }))
44 | case EDIT_MODEL:
45 | return next(actionWith({ locals: normalize(attributes, schema) }))
46 | case CLEAR_MODEL:
47 | // TODO
48 | // return next(actionWith({ locals: { schema.getKey() : { schema.getId(attributes) : undefined }}}))
49 | break
50 | default:
51 | console.warn('unknown model action method: %s', method)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/monaco.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__, self */
2 |
3 | if (__BUILD__.ELECTRON) {
4 | // configure monaco environment
5 | self.MonacoEnvironment = {
6 | getWorkerUrl: function (moduleId, label) {
7 | if (label === 'json') {
8 | return './json.worker.bundle.js'
9 | }
10 | if (label === 'css') {
11 | return './css.worker.bundle.js'
12 | }
13 | if (label === 'html') {
14 | return './html.worker.bundle.js'
15 | }
16 | if (label === 'typescript' || label === 'javascript') {
17 | return './ts.worker.bundle.js'
18 | }
19 | if (__BUILD__.MODE === 'production') {
20 | return './editor.worker.prod.js'
21 | }
22 | return './editor.worker.bundle.js'
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/propTypes/bbox.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | // bbox represents a css bounding box (position: :absolute")
4 | export default PropTypes.shape({
5 | width: PropTypes.number.isRequired,
6 | height: PropTypes.number.isRequired,
7 | top: PropTypes.number,
8 | left: PropTypes.number,
9 | bottom: PropTypes.number,
10 | right: PropTypes.number,
11 | collapsed: PropTypes.bool
12 | })
13 |
--------------------------------------------------------------------------------
/lib/propTypes/commitProps.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { userProps } from './metaProps'
3 |
4 | export default PropTypes.shape({
5 | author: userProps,
6 | message: PropTypes.string,
7 | path: PropTypes.string,
8 | qri: PropTypes.oneOf(['cm:0']),
9 | signature: PropTypes.string,
10 | timestamp: PropTypes.string,
11 | title: PropTypes.string
12 | })
13 |
--------------------------------------------------------------------------------
/lib/propTypes/datasetRefProps.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import MetaProps from './metaProps'
3 |
4 | export const formatProps = PropTypes.oneOf(['csv', 'json'])
5 |
6 | export const typeProps = PropTypes.oneOf([
7 | 'string',
8 | 'number',
9 | 'integer',
10 | 'boolean',
11 | 'null',
12 | 'object',
13 | 'array'
14 | ])
15 |
16 | export const commitProps = PropTypes.oneOfType([
17 | PropTypes.string,
18 | PropTypes.shape({
19 | timestamp: PropTypes.string
20 | })
21 | ])
22 |
23 | export const structureProps = PropTypes.oneOfType([
24 | PropTypes.string,
25 | PropTypes.shape({
26 | format: formatProps,
27 | formatConfig: PropTypes.object,
28 | schema: PropTypes.object,
29 | length: PropTypes.number.isRequired
30 | })
31 | ])
32 |
33 | export const transformProps = PropTypes.oneOfType([
34 | PropTypes.string,
35 | PropTypes.shape({
36 | outputStructure: structureProps,
37 | statement: PropTypes.string,
38 | structures: PropTypes.objectOf(structureProps),
39 | syntax: PropTypes.string,
40 | resources: PropTypes.objectOf(
41 | PropTypes.oneOfType([PropTypes.string, PropTypes.object])
42 | )
43 | })
44 | ])
45 |
46 | export const datasetProps = PropTypes.shape({
47 | commit: commitProps,
48 | meta: MetaProps,
49 | transform: transformProps,
50 | structure: structureProps,
51 | data: PropTypes.string,
52 | prevPath: PropTypes.string
53 | })
54 |
55 | export default PropTypes.shape({
56 | dataset: datasetProps.isRequired,
57 | name: PropTypes.string,
58 | path: PropTypes.string.isRequired
59 | })
60 |
--------------------------------------------------------------------------------
/lib/propTypes/metaProps.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export const citationProps = PropTypes.shape({
4 | name: PropTypes.string,
5 | url: PropTypes.string,
6 | email: PropTypes.string
7 | })
8 |
9 | export const userProps = PropTypes.shape({
10 | id: PropTypes.string,
11 | fullname: PropTypes.string,
12 | email: PropTypes.string
13 | })
14 |
15 | export default PropTypes.oneOfType([
16 | PropTypes.string,
17 | PropTypes.shape({
18 | path: PropTypes.string,
19 | accessPath: PropTypes.string,
20 | accrualPeriodicity: PropTypes.string,
21 | citations: PropTypes.arrayOf(citationProps),
22 | contributors: PropTypes.arrayOf(userProps),
23 | description: PropTypes.string,
24 | downloadPath: PropTypes.string,
25 | homePath: PropTypes.string,
26 | identifier: PropTypes.string,
27 | keywords: PropTypes.arrayOf(PropTypes.string),
28 | language: PropTypes.arrayOf(PropTypes.string),
29 | license: PropTypes.Object,
30 | qri: PropTypes.string,
31 | readmePath: PropTypes.string,
32 | title: PropTypes.string,
33 | theme: PropTypes.arrayOf(PropTypes.string),
34 | version: PropTypes.string
35 | })
36 | ])
37 |
--------------------------------------------------------------------------------
/lib/propTypes/palette.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export const Palette = PropTypes.shape({
4 | background: PropTypes.text,
5 | sink: PropTypes.text,
6 | primary: PropTypes.text,
7 | primaryDark: PropTypes.text,
8 | primaryMuted: PropTypes.text,
9 | primaryLight: PropTypes.text,
10 | text: PropTypes.text,
11 | textMuted: PropTypes.text,
12 | textLight: PropTypes.text,
13 | error: PropTypes.text,
14 | connected: PropTypes.text,
15 | success: PropTypes.text
16 | })
17 |
18 | export const defaultPalette = {
19 | background: '#FFFFFF',
20 | sink: '#F6F6F8',
21 | primary: '#0061A6',
22 | primaryDark: '#004E86',
23 | primaryMuted: '#B3CFE4',
24 | primaryLight: '#F6F6F8',
25 | text: '#303030',
26 | textMuted: '#646464',
27 | textLight: '#C9C9C9',
28 | error: '#EB0000',
29 | connected: '#F8CD1C',
30 | success: '#B3CFE4'
31 | }
32 |
--------------------------------------------------------------------------------
/lib/propTypes/profile.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export default PropTypes.shape({
4 | id: PropTypes.string.isRequired,
5 | created: PropTypes.string,
6 | updated: PropTypes.string,
7 | username: PropTypes.string,
8 | type: PropTypes.string,
9 | emails: PropTypes.string,
10 | name: PropTypes.string,
11 | description: PropTypes.string,
12 | homeUrl: PropTypes.string,
13 | color: PropTypes.string,
14 | thumbUrl: PropTypes.string,
15 | profileUrl: PropTypes.string,
16 | twitter: PropTypes.string
17 | })
18 |
--------------------------------------------------------------------------------
/lib/propTypes/schema.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export default PropTypes.oneOfType([
4 | PropTypes.bool,
5 | PropTypes.object
6 | ])
7 |
--------------------------------------------------------------------------------
/lib/reducers/__tests__/session.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import { Reducer } from 'redux-testkit'
3 | import SessionReducer from '../session'
4 | import { SESSION_PROFILE_SUCCESS, SESSION_PROFILE_FAILURE } from '../../constants/session'
5 |
6 | describe('Session Reducer', () => {
7 | const initialState = ''
8 |
9 | it('should have an initialState (@@INIT)', () => {
10 | expect(SessionReducer(undefined, { type: '@@INIT' })).toEqual(initialState)
11 | })
12 |
13 | it('should handle SESSION_PROFILE_SUCCESS on an initial state', () => {
14 | const action = {
15 | type: SESSION_PROFILE_SUCCESS,
16 | response: {
17 | result: {
18 | data: 'profileid'
19 | }
20 | }
21 | }
22 | const state = 'profileid'
23 |
24 | Reducer(SessionReducer).expect(action).toReturnState(state)
25 | })
26 |
27 | it('should handle SESSION_PROFILE_SUCCESS on an existing state', () => {
28 | const state = 'profileid'
29 | const action = {
30 | type: SESSION_PROFILE_SUCCESS,
31 | response: {
32 | result: {
33 | data: 'newProfileid'
34 | }
35 | }
36 | }
37 | const newState = 'newProfileid'
38 |
39 | Reducer(SessionReducer).withState(state).expect(action).toReturnState(newState)
40 | })
41 |
42 | it('should handle SESSION_PROFILE_FAILURE on an initial state', () => {
43 | const action = { type: SESSION_PROFILE_FAILURE }
44 |
45 | Reducer(SessionReducer).expect(action).toChangeInState(initialState)
46 | })
47 |
48 | it('should handle SESSION_PROFILE_FAILURE on an existing state', () => {
49 | const action = { type: SESSION_PROFILE_FAILURE }
50 | const state = 'profileid'
51 |
52 | Reducer(SessionReducer).withState(state).expect(action).toReturnState(state)
53 | })
54 |
55 | it('should return the same state after accepting a non existing action on an initial state', () => {
56 | const action = { type: 'NON_EXISTING' }
57 | Reducer(SessionReducer).expect(action).toReturnState(initialState)
58 | })
59 |
60 | it('should return the same state after accepting a non existing action on an existing state', () => {
61 | const action = { type: 'NON_EXISTING' }
62 | const state = 'profileid'
63 | Reducer(SessionReducer).withState(state).expect(action).toReturnState(state)
64 | })
65 | })
66 |
--------------------------------------------------------------------------------
/lib/reducers/body.js:
--------------------------------------------------------------------------------
1 | import {
2 | CLEAR_BODY,
3 | BODY_REQUEST,
4 | BODY_SUCCESS,
5 | BODY_FAILURE
6 | } from '../constants/body'
7 |
8 | import cloneDeep from 'clone-deep'
9 |
10 | export const initialState = {
11 | id: '',
12 | selector: '',
13 | pageSize: 100,
14 | fetchedAll: false,
15 | loading: true,
16 | data: undefined,
17 | error: ''
18 | }
19 |
20 | export default function bodyPaginationReducer (state = initialState, action) {
21 | switch (action.type) {
22 | case CLEAR_BODY:
23 | return initialState
24 | case BODY_REQUEST:
25 | if (action.bodypath !== state.id || action.selector !== state.selector) {
26 | return Object.assign({}, initialState, { id: action.bodypath, selector: action.selector, pageSize: action.pageSize })
27 | }
28 | return Object.assign({}, state, { pageSize: action.pageSize, loading: true })
29 | case BODY_SUCCESS:
30 | var fetchedAll = state.fetchedAll
31 | const newData = action.data
32 | var length = Object.keys(newData).length
33 | if (state.pageSize === -1 || length < state.pageSize) {
34 | fetchedAll = true
35 | }
36 | var data = cloneDeep(state.data)
37 | if (!data) { data = Array.isArray(newData) ? [] : {} }
38 |
39 | // assumes that newData will be the same type as data
40 | if (Array.isArray(data)) {
41 | data.push(...newData)
42 | } else {
43 | data = Object.assign(data, newData)
44 | }
45 | return Object.assign({}, state,
46 | { data, loading: false, fetchedAll })
47 | case BODY_FAILURE:
48 | return Object.assign({}, state,
49 | { loading: false, error: action.error })
50 | default:
51 | return state
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/reducers/cafs.js:
--------------------------------------------------------------------------------
1 | import {
2 | CAFS_REQUEST,
3 | CAFS_SUCCESS,
4 | CAFS_FAILURE,
5 | CAFS_TRANSFORM_REQUEST,
6 | CAFS_TRANSFORM_SUCCESS,
7 | CAFS_TRANSFORM_FAILURE,
8 | CAFS_VIZ_REQUEST,
9 | CAFS_VIZ_SUCCESS,
10 | CAFS_VIZ_FAILURE,
11 | CAFS_BODY_FILE_REQUEST,
12 | CAFS_BODY_FILE_SUCCESS,
13 | CAFS_BODY_FILE_FAILURE
14 | } from '../constants/cafs'
15 |
16 | const initialState = {}
17 |
18 | // mapActionType takes the given type and tries to match it to the
19 | // cafs request, success, and failure types
20 | // any new cafs actions that get added need to update these lists
21 | function mapActionType (type) {
22 | const requestList = [
23 | CAFS_TRANSFORM_REQUEST,
24 | CAFS_VIZ_REQUEST,
25 | CAFS_BODY_FILE_REQUEST
26 | ]
27 | const successList = [
28 | CAFS_TRANSFORM_SUCCESS,
29 | CAFS_VIZ_SUCCESS,
30 | CAFS_BODY_FILE_SUCCESS
31 | ]
32 | const failureList = [
33 | CAFS_TRANSFORM_FAILURE,
34 | CAFS_VIZ_FAILURE,
35 | CAFS_BODY_FILE_FAILURE
36 | ]
37 |
38 | if (requestList.includes(type)) {
39 | return CAFS_REQUEST
40 | }
41 | if (successList.includes(type)) {
42 | return CAFS_SUCCESS
43 | }
44 | if (failureList.includes(type)) {
45 | return CAFS_FAILURE
46 | }
47 | }
48 |
49 | // Creates a reducer managing cafs, given the action types to handle,
50 | // and a function telling how to extract the key from an action.
51 | export default function cafs (state = initialState, action) {
52 | const { type, key, data, error } = action
53 |
54 | switch (mapActionType(type)) {
55 | case CAFS_REQUEST:
56 | return Object.assign({}, state, {
57 | [key]: {
58 | loading: true
59 | }
60 | })
61 | case CAFS_SUCCESS:
62 | return Object.assign({}, state, {
63 | [key]: {
64 | loading: false,
65 | data
66 | }
67 | })
68 | case CAFS_FAILURE:
69 | return Object.assign({}, state, {
70 | [key]: {
71 | loading: false,
72 | data: null,
73 | error
74 | }
75 | })
76 | default:
77 | return state
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { connectRouter } from 'connected-react-router'
3 | import merge from 'lodash/merge'
4 |
5 | import * as ActionTypes from '../constants/app'
6 |
7 | import pagination from './pagination'
8 | import sessionReducer from './session'
9 | import transfersReducer from './transfers'
10 | import layoutReducer from './layout'
11 | import appReducer from './app'
12 | import cafsReducer from './cafs'
13 | import locals from './locals'
14 | import editor from './editor'
15 | import body from './body'
16 |
17 | const initialEntitiesState = {
18 | app: {},
19 | session: {},
20 | datasets: {},
21 | body: {},
22 | search: {},
23 | transfers: {},
24 | cafs: {},
25 | datasetDryRuns: {}
26 | }
27 |
28 | // Updates an entity cache in response to any action with response.entities.
29 | // see api middleware
30 | function entities (state = initialEntitiesState, action) {
31 | if (action.response && action.response.entities) {
32 | return merge({}, state, action.response.entities)
33 | }
34 |
35 | if (action.type === ActionTypes.REMOVE_MODEL) {
36 | const newState = merge({}, state)
37 | newState[action.schema.getKey()] = merge({}, newState[action.schema.getKey()])
38 | delete newState[action.schema.getKey()][action.id]
39 | return newState
40 | }
41 |
42 | return state
43 | }
44 |
45 | // Updates error message to notify about the failed fetches.
46 | function errorMessage (state = null, action) {
47 | const { type, error, silentError } = action
48 |
49 | if (type === ActionTypes.RESET_ERROR_MESSAGE) {
50 | return null
51 | } else if (error && !silentError) {
52 | return error
53 | }
54 |
55 | return state
56 | }
57 |
58 | function message (state = null, action) {
59 | const { type, message } = action
60 |
61 | if (type === ActionTypes.RESET_MESSAGE) {
62 | return null
63 | } else if (message) {
64 | return message
65 | }
66 |
67 | return state
68 | }
69 |
70 | export default (history) => combineReducers({
71 | entities,
72 | locals,
73 | pagination,
74 | errorMessage,
75 | message,
76 | editor,
77 | body,
78 |
79 | transfers: transfersReducer,
80 | session: sessionReducer,
81 | layout: layoutReducer,
82 | app: appReducer,
83 | cafs: cafsReducer,
84 | router: connectRouter(history)
85 | })
86 |
--------------------------------------------------------------------------------
/lib/reducers/layout.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | import { LAYOUT_RESIZE } from '../constants/layout'
3 | import { SESSION_PROFILE_SUCCESS, SESSION_PROFILE_FAILURE } from '../constants/session'
4 |
5 | let topbarHeight = 55
6 | if (__BUILD__.ELECTRON) {
7 | topbarHeight = 75
8 | }
9 |
10 | const initialState = {
11 | size: 'xs',
12 | stage: { width: 100, height: 100 },
13 | topbar: { width: 100, height: topbarHeight, left: 0, top: 0 },
14 | main: { width: 100, height: 100, left: 0, top: 0 },
15 | sidebar: { width: 0, height: 100, left: 0, top: 0 },
16 | session: true
17 | }
18 |
19 | export default function layoutReducer (state = initialState, action) {
20 | switch (action.type) {
21 | case SESSION_PROFILE_SUCCESS:
22 | return layout(Object.assign({}, state, { session: true }))
23 | case SESSION_PROFILE_FAILURE:
24 | return layout(Object.assign({}, state, { session: false }))
25 | case LAYOUT_RESIZE:
26 | if (action && action.stage && !(action.stage.width < 200) && !(action.stage.height < 200)) {
27 | return layout(Object.assign({}, state, action))
28 | }
29 | }
30 |
31 | return state
32 | }
33 |
34 | function layout (state) {
35 | const { stage, sidebar, session, topbar } = state
36 | return {
37 | size: sizeClass(state.stage.width),
38 | stage,
39 | topbar: {
40 | width: stage.width,
41 | height: topbar.height,
42 | top: 0,
43 | left: 0
44 | },
45 | main: {
46 | width: session ? stage.width - sidebar.width : stage.width,
47 | height: stage.height - topbar.height,
48 | top: topbar.height,
49 | left: session ? sidebar.width : 0
50 | },
51 | sidebar: {
52 | height: stage.height - topbar.height,
53 | width: sidebar.width,
54 | left: 0,
55 | top: topbar.height
56 | },
57 | session
58 | }
59 | }
60 |
61 | // @return {string} string representation of windowidth size class
62 | function sizeClass (width) {
63 | if (width >= 1200) {
64 | return 'xl'
65 | } else if (width >= 992) {
66 | return 'lg'
67 | } else if (width >= 768) {
68 | return 'md'
69 | } else if (width >= 544) {
70 | return 'sm'
71 | } else {
72 | return 'xs'
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/reducers/locals.js:
--------------------------------------------------------------------------------
1 | import { REMOVE_MODEL } from '../constants/app'
2 |
3 | const initialState = {
4 | datasets: {}
5 | }
6 |
7 | // updates an entity cache in response to any actuion with response.local.
8 | // see local middleware
9 | export default function locals (state = initialState, action) {
10 | if (action.locals && action.locals.entities) {
11 | for (const key in action.locals.entities) {
12 | if (!action.locals.entities[key]) {
13 | return state
14 | }
15 | }
16 | return Object.assign({}, state, action.locals.entities)
17 | }
18 |
19 | if (action.type === REMOVE_MODEL && action.schema && action.id) {
20 | const newState = Object.assign({}, state)
21 | newState[action.schema.key] = Object.assign({}, newState[action.schema.key])
22 | delete newState[action.schema.key][action.id]
23 | return newState
24 | }
25 | // TODO - remove local model action?
26 |
27 | return state
28 | }
29 |
--------------------------------------------------------------------------------
/lib/reducers/pagination.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import paginate from './paginate'
3 |
4 | import {
5 | DATASETS_REQUEST,
6 | DATASETS_SUCCESS,
7 | DATASETS_FAILURE,
8 |
9 | DATASET_SEARCH_REQUEST,
10 | DATASET_SEARCH_SUCCESS,
11 | DATASET_SEARCH_FAILURE
12 |
13 | } from '../constants/dataset'
14 |
15 | import {
16 | HISTORY_REQUEST,
17 | HISTORY_SUCCESS,
18 | HISTORY_FAILURE
19 | } from '../constants/history'
20 |
21 | import {
22 | PROFILES_REQUEST,
23 | PROFILES_SUCCESS,
24 | PROFILES_FAILURE
25 | } from '../constants/profiles'
26 |
27 | import {
28 | REGISTRY_LIST_REQUEST,
29 | REGISTRY_LIST_SUCCESS,
30 | REGISTRY_LIST_FAILURE
31 | } from '../constants/registry'
32 |
33 | // Updates the pagination data for different actions.
34 | const pagination = combineReducers({
35 | popularDatasets: paginate({
36 | name: 'popularDatasets',
37 | mapActionToKey: action => action.id,
38 | types: [
39 | DATASETS_REQUEST,
40 | DATASETS_SUCCESS,
41 | DATASETS_FAILURE
42 | ]
43 | }),
44 |
45 | popularProfiles: paginate({
46 | name: 'popularProfiles',
47 | mapActionToKey: action => 'popularProfiles',
48 | types: [
49 | PROFILES_REQUEST,
50 | PROFILES_SUCCESS,
51 | PROFILES_FAILURE
52 | ]
53 | }),
54 |
55 | searchedDatasets: paginate({
56 | name: 'searchedDatasets',
57 | mapActionToKey: action => action.searchString,
58 | types: [
59 | DATASET_SEARCH_REQUEST,
60 | DATASET_SEARCH_SUCCESS,
61 | DATASET_SEARCH_FAILURE
62 | ]
63 | }),
64 |
65 | datasetHistory: paginate({
66 | name: 'datasetHistory',
67 | mapActionToKey: action => action.path,
68 | types: [
69 | HISTORY_REQUEST,
70 | HISTORY_SUCCESS,
71 | HISTORY_FAILURE
72 | ]
73 | }),
74 |
75 | registryDatasets: paginate({
76 | name: 'registryDatasets',
77 | mapActionToKey: action => 'registryDatasets',
78 | types: [
79 | REGISTRY_LIST_REQUEST,
80 | REGISTRY_LIST_SUCCESS,
81 | REGISTRY_LIST_FAILURE
82 | ]
83 | })
84 | })
85 |
86 | export default pagination
87 |
--------------------------------------------------------------------------------
/lib/reducers/session.js:
--------------------------------------------------------------------------------
1 | import { SESSION_PROFILE_SUCCESS } from '../constants/session'
2 |
3 | const initialState = ''
4 |
5 | export default function sessionReducer (state = initialState, action) {
6 | switch (action.type) {
7 | case SESSION_PROFILE_SUCCESS:
8 | return action && action.response && action.response.result && action.response.result.data
9 | default:
10 | return state
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/reducers/transfers.js:
--------------------------------------------------------------------------------
1 | // transfering -1 for err
2 | // 0 nothing transfered yet
3 | // btw 0 & 1 percentage
4 | // if greater than 1, loading complete
5 |
6 | // see if id is in object
7 | // get status of transfer
8 | // add transfer to object
9 | // update transfer progress
10 | // remove transfer
11 |
12 | import {
13 | SET_TRANSFER_STATUS,
14 | REMOVE_TRANSFER_STATUS
15 | } from '../constants/transfers'
16 |
17 | const initialState = {
18 | }
19 |
20 | export default function transfersReducer (state = initialState, action) {
21 | switch (action.type) {
22 | case SET_TRANSFER_STATUS:
23 | return Object.assign({}, state, { [action.id]: action.status })
24 | case REMOVE_TRANSFER_STATUS:
25 | return Object.keys(state)
26 | .filter(e => e !== action.id)
27 | .reduce((obj, key) => {
28 | obj[key] = state[key]
29 | return obj
30 | }, {})
31 | }
32 |
33 | return state
34 | }
35 |
--------------------------------------------------------------------------------
/lib/routes.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | import React from 'react'
3 | import { Switch, Route } from 'react-router-dom'
4 |
5 | import AppContainer from './containers/App'
6 | import CollectionContainer from './containers/Collection'
7 | import DatasetContainer from './containers/Dataset'
8 | import ProfileContainer from './containers/Profile'
9 | import NetworkContainer from './containers/Network'
10 | import Stylesheet from './components/stylesheet/Stylesheet'
11 | import SearchResultsContainer from './containers/SearchResults'
12 | import Spinner from './components/chrome/Spinner'
13 |
14 | const EditorContainer = React.lazy(() => import('./containers/Editor.js'))
15 |
16 | export default () => (
17 |
18 | }>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {/*
30 | TODO: for some reason the SearchResultsContainer doesn't seem
31 | to get called when we go to the `/search` endpoint
32 | */}
33 |
34 |
35 | {!__BUILD__.READONLY && }
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 |
--------------------------------------------------------------------------------
/lib/schemas.js:
--------------------------------------------------------------------------------
1 | import { schema } from 'normalizr'
2 | // TODO result_cid is assigned a value but never used, consider depreciation
3 | // TODO resutl_cid is not in camel case
4 | // let result_cid = 0
5 |
6 | // We use this Normalizr schemas to transform API responses from a nested form
7 | // to a flat form where repos and users are placed in `entities`, and nested
8 | // JSON objects are replaced with their IDs. This is very convenient for
9 | // consumption by reducers, because we can easily build a normalized tree
10 | // and keep it updated as we fetch more data.
11 |
12 | // Read more about Normalizr: https://github.com/paularmstrong/normalizr
13 | const datasetSchema = new schema.Entity('datasets', {}, { idAttribute: 'path' })
14 | const datasetDryRunSchema = new schema.Entity('datasetDryRuns', {}, { idAttribute: 'path' })
15 | const bodySchema = new schema.Entity('body', {}, {
16 | idAttribute: 'path' })
17 | const profileSchema = new schema.Entity('profiles', {}, { idAttribute: 'id' })
18 | const searchSchema = new schema.Entity('search', {}, { idAttribute: 'ID' })
19 |
20 | datasetSchema.new = function (attrs) {
21 | return Object.assign({ path: 'new' }, attrs)
22 | }
23 |
24 | profileSchema.new = function (attrs) {
25 | return Object.assign({}, { id: 'new' }, attrs)
26 | }
27 |
28 | bodySchema.new = function (attrs) {
29 | return Object.assign({}, { id: 'new' }, attrs)
30 | }
31 |
32 | searchSchema.new = function (attrs) {
33 | return Object.assign({}, { id: 'new' }, attrs)
34 | }
35 |
36 | // Schemas for Github API responses.
37 | const Schemas = {
38 | DATASET: datasetSchema,
39 | DATASET_ARRAY: new schema.Array(datasetSchema),
40 |
41 | DATASET_DRY_RUN: datasetDryRunSchema,
42 |
43 | STRUCTURED_DATA: bodySchema,
44 |
45 | PROFILE: profileSchema,
46 | PROFILE_ARRAY: new schema.Array(profileSchema),
47 |
48 | SEARCH: searchSchema,
49 | SEARCH_ARRAY: new schema.Array(searchSchema)
50 | }
51 |
52 | export default Schemas
53 |
--------------------------------------------------------------------------------
/lib/scss/_handsontable.scss:
--------------------------------------------------------------------------------
1 | // styling for the HandsOnTableEditor
2 |
3 | .editor.handsontable th {
4 | color: $primary-light;
5 | }
6 |
7 | // for some reason this specific left side border wants to be a weird color
8 | // this random css fixes it so it looks like every other border
9 | .editor.handsontable.htRowHeaders thead tr th:nth-child(2) {
10 | border-left: 1px solid #1e1e1e;
11 | border-left-width: 1px;
12 | border-left-style: solid;
13 | border-left-color: rgb(30, 30, 30);
14 | }
15 |
16 | .editBody .handsontable th:first-child {
17 | border: 1px solid #1e1e1e;
18 | }
--------------------------------------------------------------------------------
/lib/scss/_hotable.scss:
--------------------------------------------------------------------------------
1 |
2 | $borderColor: #1e1e1e;
3 | $highlightColor: #333;
4 |
5 | .editBody .handsontable {
6 | color: white;
7 |
8 | th {
9 | background: #000;
10 | border-right: 1px solid $borderColor;
11 | border-bottom: 1px solid $borderColor;
12 | }
13 | tr {
14 | background: #111;
15 | }
16 | td {
17 | background: #111;
18 | border-right: 1px solid $borderColor;
19 | border-bottom: 1px solid $borderColor;
20 | }
21 |
22 | tr:first-child th,
23 | tr:first-child td {
24 | border-top: 1px solid $borderColor;
25 | }
26 |
27 | th:first-child,
28 | th:nth-child(2),
29 | td:first-of-type,
30 | .htNoFrame + th,
31 | .htNoFrame + td {
32 | border-left: 1px solid $borderColor;
33 | }
34 |
35 | .ht__highlight {
36 | background: $highlightColor;
37 | }
38 |
39 | // tr:first-child th,
40 | // .handsontable tr:first-child td {
41 | // }
42 | }
--------------------------------------------------------------------------------
/lib/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Toggles
2 | //
3 | // Used in conjunction with global variables to enable certain theme features.
4 |
5 | @mixin box-shadow($shadow...) {
6 | @if $enable-shadows {
7 | box-shadow: $shadow;
8 | }
9 | }
10 |
11 | @mixin transition($transition...) {
12 | @if $enable-transitions {
13 | transition: $transition;
14 | }
15 | }
16 |
17 | // Utilities
18 | @import "mixins/breakpoints";
19 | @import "mixins/hover";
20 | @import "mixins/tab-focus";
21 |
22 | // Components
23 | @import "mixins/buttons";
24 | @import "mixins/lists";
25 | @import "mixins/forms";
26 | @import "mixins/nav-divider";
27 |
28 | // Skins
29 | @import "mixins/border-radius";
30 | @import "mixins/gradients";
--------------------------------------------------------------------------------
/lib/scss/_modal.scss:
--------------------------------------------------------------------------------
1 | #modal-wrap {
2 | background: rgba(0,0,0,0.45);
3 | width: 100%;
4 | height: 100%;
5 | position: absolute;
6 | top: 0;
7 | left: 0;
8 | transition: all 500ms ease;
9 | -webkit-transition: all 500ms ease;
10 | -moz-transition: all 500ms ease;
11 | -o-transition: all 500ms ease;
12 | }
13 |
14 | #modal-wrap.modal-wrap-in {
15 | opacity: 1;
16 | z-index: 4;
17 | }
18 |
19 | #modal-wrap.modal-wrap-out {
20 | opacity: 0;
21 | z-index: -1;
22 | }
23 |
24 | #modal {
25 | z-index: 5;
26 | box-shadow: 0 0 8px rgba(0,0,0,0.3);
27 | background: $primary;
28 | position: absolute;
29 | overflow-y: auto;
30 | top: 0;
31 | bottom: 0;
32 | right: 0;
33 | width: 0;
34 | transition: all 500ms ease;
35 | -webkit-transition: all 500ms ease;
36 | -moz-transition: all 500ms ease;
37 | -o-transition: all 500ms ease;
38 | }
39 |
40 | #modal.modal-in {
41 | width: 360px
42 | }
43 |
44 | #modal.modal-out {
45 | width: 0;
46 | }
47 |
--------------------------------------------------------------------------------
/lib/scss/_schema.scss:
--------------------------------------------------------------------------------
1 | //
2 | // SCSS for Schema components
3 | //
4 | // EditSchema
5 | // schema
6 |
7 | ////////////////////////////
8 | // schema //
9 | ////////////////////////////
10 | .schema-wrap {
11 | border-radius: 3px;
12 | border: 1px solid #eee;
13 | margin: 5px 10px;
14 | }
15 |
16 | .schema-properties {
17 | margin: 5px 10px;
18 | }
19 |
20 | ////////////////////////////
21 | // EditSchema //
22 | ////////////////////////////
23 | .edit-schema-wrap {
24 | width: 100%;
25 | border-radius: 2px;
26 | border: 1px solid $primary-muted;
27 | margin: 5px 14px;
28 | }
--------------------------------------------------------------------------------
/lib/scss/_stylesheet.scss:
--------------------------------------------------------------------------------
1 | //
2 | // SCSS for Stylesheet components
3 | //
4 | // Stylesheet
5 | // Swatch
6 |
7 | ////////////////////////////
8 | // Stylesheet //
9 | ////////////////////////////
10 | .stylesheet {
11 | margin-top: 40px;
12 | padding-left: 20px;
13 | padding-right: 20px;
14 | }
15 |
16 | .stylesheet-container {
17 | margin-bottom: 100px;
18 | }
19 |
20 | .stylesheet-header {
21 | margin-bottom: 40px;
22 | }
23 |
24 | .stylesheet-spacer {
25 | margin-bottom: 30px;
26 | }
27 |
28 | .stylesheet-dataset-name {
29 | color: $primary;
30 | }
31 |
32 | .stylesheet-label {
33 | color: $primary-muted;
34 | }
35 |
36 | .stylesheet-button {
37 | margin-top: 5px;
38 | }
39 |
40 | .stylesheet-bottom {
41 | margin-bottom: 200px;
42 | }
43 |
44 | ////////////////////////////
45 | // Swatch //
46 | ////////////////////////////
47 | .swatch-wrap {
48 | margin-left: 20px;
49 | display: inline-block;
50 | }
51 |
52 | .swatch {
53 | height: 140px;
54 | width: 140px;
55 | margin-bottom: 5px;
56 | }
--------------------------------------------------------------------------------
/lib/scss/mixins/_border-radius.scss:
--------------------------------------------------------------------------------
1 | // Single side border-radius
2 |
3 | @mixin border-radius($radius: $border-radius) {
4 | @if $enable-rounded {
5 | border-radius: $radius;
6 | }
7 | }
8 |
9 | @mixin border-top-radius($radius) {
10 | @if $enable-rounded {
11 | border-top-right-radius: $radius;
12 | border-top-left-radius: $radius;
13 | }
14 | }
15 |
16 | @mixin border-right-radius($radius) {
17 | @if $enable-rounded {
18 | border-bottom-right-radius: $radius;
19 | border-top-right-radius: $radius;
20 | }
21 | }
22 |
23 | @mixin border-bottom-radius($radius) {
24 | @if $enable-rounded {
25 | border-bottom-right-radius: $radius;
26 | border-bottom-left-radius: $radius;
27 | }
28 | }
29 |
30 | @mixin border-left-radius($radius) {
31 | @if $enable-rounded {
32 | border-bottom-left-radius: $radius;
33 | border-top-left-radius: $radius;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/scss/mixins/_gradients.scss:
--------------------------------------------------------------------------------
1 | // Gradients
2 |
3 | @mixin gradient-bg($color) {
4 | @if $enable-gradients {
5 | background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x;
6 | } @else {
7 | background-color: $color;
8 | }
9 | }
--------------------------------------------------------------------------------
/lib/scss/mixins/_hover.scss:
--------------------------------------------------------------------------------
1 | @mixin hover {
2 | @if $enable-hover-media-query {
3 | // See Media Queries Level 4: http://drafts.csswg.org/mediaqueries/#hover
4 | // Currently shimmed by https://github.com/twbs/mq4-hover-shim
5 | @media (hover: hover) {
6 | &:hover { @content }
7 | }
8 | }
9 | @else {
10 | &:hover { @content }
11 | }
12 | }
13 |
14 | @mixin hover-focus {
15 | @if $enable-hover-media-query {
16 | &:focus { @content }
17 | @include hover { @content }
18 | }
19 | @else {
20 | &:focus,
21 | &:hover {
22 | @content
23 | }
24 | }
25 | }
26 |
27 | @mixin plain-hover-focus {
28 | @if $enable-hover-media-query {
29 | &,
30 | &:focus {
31 | @content
32 | }
33 | @include hover { @content }
34 | }
35 | @else {
36 | &,
37 | &:focus,
38 | &:hover {
39 | @content
40 | }
41 | }
42 | }
43 |
44 | @mixin hover-focus-active {
45 | @if $enable-hover-media-query {
46 | &:focus,
47 | &:active {
48 | @content
49 | }
50 | @include hover { @content }
51 | }
52 | @else {
53 | &:focus,
54 | &:active,
55 | &:hover {
56 | @content
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/scss/mixins/_lists.scss:
--------------------------------------------------------------------------------
1 | // Lists
2 |
3 | // Unstyled keeps list items block level, just removes default browser padding and list-style
4 | @mixin list-unstyled {
5 | padding-left: 0;
6 | list-style: none;
7 | }
8 |
--------------------------------------------------------------------------------
/lib/scss/mixins/_nav-divider.scss:
--------------------------------------------------------------------------------
1 | // Horizontal dividers
2 | //
3 | // Dividers (basically an hr) within dropdowns and nav lists
4 |
5 | @mixin nav-divider($color: $nav-divider-color, $margin-y: $nav-divider-margin-y) {
6 | height: 0;
7 | margin: $margin-y 0;
8 | overflow: hidden;
9 | border-top: 1px solid $color;
10 | }
--------------------------------------------------------------------------------
/lib/scss/mixins/_tab-focus.scss:
--------------------------------------------------------------------------------
1 | // WebKit-style focus
2 |
3 | @mixin tab-focus() {
4 | // Default
5 | outline: none;
6 | }
7 |
--------------------------------------------------------------------------------
/lib/scss/mixins/_table-row.scss:
--------------------------------------------------------------------------------
1 | // Tables
2 |
3 | @mixin table-row-variant($state, $background) {
4 | // Exact selectors below required to override `.table-striped` and prevent
5 | // inheritance to nested tables.
6 | .table-#{$state} {
7 | &,
8 | > th,
9 | > td {
10 | background-color: $background;
11 | }
12 | }
13 |
14 | // Hover states for `.table-hover`
15 | // Note: this is not available for cells or rows within `thead` or `tfoot`.
16 | .table-hover {
17 | $hover-background: darken($background, 5%);
18 |
19 | .table-#{$state} {
20 | @include hover {
21 | background-color: $hover-background;
22 |
23 | > td,
24 | > th {
25 | background-color: $hover-background;
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/scss/style.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 | @import "mixins";
3 |
4 | @import "normalize";
5 |
6 | @import "type";
7 | @import "forms";
8 | @import "buttons";
9 | @import "modal";
10 | @import "dropdown";
11 | @import "hotable";
12 |
13 | @import "site";
14 |
15 | @import "handsontable";
16 | @import "chrome";
17 | @import "dataset";
18 | @import "editor";
19 | @import "schema";
20 | @import "form";
21 | @import "item";
22 | @import "profile";
23 | @import "stylesheet";
--------------------------------------------------------------------------------
/lib/selectors/__tests__/dataset.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import * as sel from '../dataset'
3 |
4 | const dsA = {
5 | path: '/path/datasetA',
6 | name: 'a',
7 | dataset: {}
8 | }
9 |
10 | const state = {
11 | entities: {
12 | datasets: {
13 | '/path/datasetA': dsA
14 | }
15 | }
16 | }
17 |
18 | describe('dataset', () => {
19 | it('selectDataset', () => {
20 | const ds = sel.selectDatasetByPath(state, '/path/datasetA')
21 | expect(ds).toEqual(dsA)
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/lib/selectors/__tests__/layout.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import * as layout from '../layout'
3 | import { defaultPalette } from '../../propTypes/palette'
4 |
5 | const initialState = {
6 | layout: {
7 | size: 'xs',
8 | stage: { width: 100, height: 100 },
9 | main: { width: 100, height: 100, left: 0, top: 0 },
10 | sidebar: { width: 80, height: 100, left: 0, top: 0 },
11 | theme: defaultPalette,
12 | session: true
13 | }
14 | }
15 |
16 | const main = {
17 | width: 100,
18 | height: 100,
19 | left: 0,
20 | top: 0
21 | }
22 |
23 | describe('Layout Main Selector: ', () => {
24 | it('should return the main section of layout', () => {
25 | const m = layout.selectLayoutMain(initialState)
26 | expect(m).toEqual(main)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/lib/selectors/__tests__/session.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import * as session from '../session'
3 |
4 | const profileid = 'profileid'
5 |
6 | const initialState = {
7 | session: ''
8 | }
9 |
10 | const state = {
11 | session: profileid
12 | }
13 |
14 | describe('Session Selector: ', () => {
15 | it('should return an empty string when selecting from an initialState', () => {
16 | const id = session.selectSessionProfileId(initialState)
17 | expect(id).toEqual('')
18 | })
19 |
20 | it('should return a profileid when selecting from a state with a session profile', () => {
21 | const id = session.selectSessionProfileId(state)
22 | expect(id).toEqual(profileid)
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/lib/selectors/__tests__/transfers.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import * as transfers from '../transfers'
3 |
4 | const initialState = {
5 | transfers: {
6 |
7 | }
8 | }
9 |
10 | const state = {
11 | transfers: {
12 | 'id_1': 0,
13 | 'id_2': -1
14 | }
15 | }
16 |
17 | describe('Transfers Selector: ', () => {
18 | it('should return undefined when selecting a status from initialState', () => {
19 | const status = transfers.selectTransferStatus(initialState, 'id_test')
20 | expect(status).toEqual(undefined)
21 | })
22 |
23 | it('should return an integer status when selecting a status from a state that contains the given id', () => {
24 | const status = transfers.selectTransferStatus(state, 'id_1')
25 | expect(status).toEqual(0)
26 | })
27 |
28 | it('should return false when selecting IsTransfering from an initial state', () => {
29 | const isTransfering = transfers.selectIsTransfering(initialState, 'id_1')
30 | expect(isTransfering).toEqual(false)
31 | })
32 |
33 | it('should return true when selecting IsTransfering from a state that does not contain the given id', () => {
34 | const isTransfering = transfers.selectIsTransfering(state, 'id_3')
35 | expect(isTransfering).toEqual(false)
36 | })
37 |
38 | it('should return true when selecting IsTransfering from a state that contains the given id', () => {
39 | const isTransfering = transfers.selectIsTransfering(state, 'id_2')
40 | expect(isTransfering).toEqual(true)
41 | })
42 |
43 | it('should return and empty list when selecting all the transfers from an initalState', () => {
44 | const transfersList = transfers.selectTransfers(initialState)
45 | expect(transfersList).toEqual([])
46 | })
47 |
48 | it('should return a list of ids when selecting all the transfers from a state', () => {
49 | const transfersList = transfers.selectTransfers(state)
50 | expect(transfersList).toEqual(['id_1', 'id_2'])
51 | })
52 | })
53 |
--------------------------------------------------------------------------------
/lib/selectors/app.js:
--------------------------------------------------------------------------------
1 | export function selectSearchString (state) {
2 | return state && state.app && state.app.search
3 | }
4 |
5 | export function selectLocation (state) {
6 | return state && state.app && state.app.location
7 | }
8 |
--------------------------------------------------------------------------------
/lib/selectors/cafs.js:
--------------------------------------------------------------------------------
1 | export function selectCAFSLoading (state, key) {
2 | return state && state.cafs && state.cafs[key] && state.cafs[key].loading
3 | }
4 |
5 | export function selectCAFSData (state, key) {
6 | return state && state.cafs && state.cafs[key] && state.cafs[key].data
7 | }
8 |
9 | export function selectCAFSError (state, key) {
10 | return state && state.cafs && state.cafs[key] && state.cafs[key].error
11 | }
12 |
13 | export function selectCAFSString (state, key) {
14 | const buffer = selectCAFSData(state, key)
15 | return String.fromCharCode.apply(null, new Int8Array(buffer))
16 | }
17 |
--------------------------------------------------------------------------------
/lib/selectors/history.js:
--------------------------------------------------------------------------------
1 | import { selectIds } from './pagination'
2 |
3 | const usersHistorySection = 'datasetHistory'
4 | const usersHistoryNode = 'datasetHistory'
5 |
6 | export function selectHistory (state, section, node) {
7 | if (!section && !node) {
8 | section = usersHistorySection
9 | node = usersHistoryNode
10 | }
11 | const { datasets } = state.entities
12 |
13 | function compareHistory (a, b) {
14 | const timestampA = datasets[a].dataset.commit.timestamp
15 | const timestampB = datasets[b].dataset.commit.timestamp
16 | //
17 | if (timestampB < timestampA) {
18 | return -1
19 | }
20 | if (timestampA < timestampB) {
21 | return 1
22 | }
23 | return 0
24 | }
25 | return selectIds(state, section, node, compareHistory).map(id => datasets[id])
26 | }
27 |
--------------------------------------------------------------------------------
/lib/selectors/layout.js:
--------------------------------------------------------------------------------
1 | export function selectLayoutMain (state) {
2 | return state && state.layout && state.layout.main
3 | }
4 |
--------------------------------------------------------------------------------
/lib/selectors/pagination.js:
--------------------------------------------------------------------------------
1 | export function selectIds (state, section, node, compare) {
2 | let ids = (state.pagination[section] && state.pagination[section][node]) ? state.pagination[section][node].ids.slice() : []
3 | if (compare) {
4 | ids.sort(compare)
5 | }
6 | return ids
7 | }
8 | export function selectPage (state, section, node, entitiesSection) {
9 | return selectIds(state, section, node).map(id => state.entities[entitiesSection][id])
10 | }
11 |
12 | export function selectIsFetching (state, section, node) {
13 | return (state.pagination[section] && state.pagination[section][node]) ? state.pagination[section][node].isFetching : false
14 | }
15 |
16 | export function selectPageCount (state, section, node) {
17 | return (state.pagination[section] && state.pagination[section][node]) ? state.pagination[section][node].pageCount : 0
18 | }
19 |
20 | export function selectFetchedAll (state, section, node) {
21 | return (state.pagination[section] && state.pagination[section][node]) ? state.pagination[section][node].fetchedAll : false
22 | }
23 |
24 | export function selectError (state, section, node) {
25 | return (state.pagination[section] && state.pagination[section][node]) ? state.pagination[section][node].error : ''
26 | }
27 |
--------------------------------------------------------------------------------
/lib/selectors/profiles.js:
--------------------------------------------------------------------------------
1 | import { selectIds } from './pagination'
2 |
3 | const usersProfilesSection = 'popularProfiles'
4 | const usersProfilesNode = 'popularProfiles'
5 |
6 | export function selectProfiles (state, section, node) {
7 | if (!section && !node) {
8 | section = usersProfilesSection
9 | node = usersProfilesNode
10 | }
11 | const profiles = state && state.entities && state.entities.profiles
12 | return selectIds(state, section, node).map(id => {
13 | return profiles[id]
14 | })
15 | }
16 |
17 | export function selectProfileById (state, path) {
18 | const profiles = state && state.entities && state.entities.profiles
19 | return profiles && profiles[path]
20 | }
21 |
22 | export function selectProfileByName (state, name) {
23 | const id = selectProfileIdByName(state, name)
24 | return id ? state.entities.profiles[id] : undefined
25 | }
26 |
27 | export function selectProfileIdByName (state, name) {
28 | const profiles = state && state.entities && state.entities.profiles
29 | if (!profiles) {
30 | return ''
31 | }
32 | const id = Object.keys(profiles).find(id => profiles[id].peername === name)
33 | return id || ''
34 | }
35 |
--------------------------------------------------------------------------------
/lib/selectors/search.js:
--------------------------------------------------------------------------------
1 | import { selectIds } from './pagination'
2 |
3 | const searchSection = 'searchedDatasets'
4 |
5 | export function selectSearchResults (state, searchResults) {
6 | const ids = selectIds(state, searchSection, searchResults)
7 | return ids.map(id => state && state.entities && state.entities.search && state.entities.search[id])
8 | }
9 |
--------------------------------------------------------------------------------
/lib/selectors/session.js:
--------------------------------------------------------------------------------
1 | import { selectProfileById } from './profiles'
2 | import { selectIds } from './pagination'
3 | import fileSize from '../utils/filesize'
4 |
5 | const datasetsSection = 'popularDatasets'
6 | const datasetHistory = 'datasetHistory'
7 |
8 | export function selectSessionProfileId (state) {
9 | return state.session
10 | }
11 |
12 | export function selectSessionProfileHandle (state) {
13 | const sessionProfile = state.session
14 | const profile = selectProfileById(state, sessionProfile)
15 | return profile ? profile.peername : undefined
16 | }
17 |
18 | export function selectSessionProfile (state) {
19 | const sessionProfile = state.session
20 | return selectProfileById(state, sessionProfile)
21 | }
22 |
23 | export function selectLocalProfile (state) {
24 | const sessionProfile = state.session
25 | return state.locals.profiles && state.locals.profiles[sessionProfile]
26 | }
27 |
28 | export function selectSessionDatasetIds (state) {
29 | const sessionId = selectSessionProfileId(state)
30 | return selectIds(state, datasetsSection, sessionId)
31 | }
32 |
33 | export function selectSessionDatasets (state) {
34 | return selectSessionDatasetIds(state).map(id => {
35 | return state.entities.datasets[id]
36 | })
37 | }
38 |
39 | export function selectIsSessionDataset (state, datasetRef) {
40 | if (!datasetRef) {
41 | return false
42 | }
43 | return !!(selectSessionDatasetIds(state).find(id => { return id === datasetRef.path }) || selectIsInSessionHistory(state, datasetRef.path))
44 | }
45 |
46 | export function selectIsInSessionHistory (state, path) {
47 | return selectSessionDatasets(state).find(datasetRef => {
48 | const dsn = `/${datasetRef.peername}/${datasetRef.name}`
49 | const ids = selectIds(state, datasetHistory, dsn)
50 | return ids.find(id => {
51 | return id === path
52 | })
53 | })
54 | }
55 |
56 | export function selectSessionDatasetsCount (state) {
57 | const ds = selectSessionDatasetIds(state)
58 | return ds ? ds.length : undefined
59 | }
60 |
61 | export function selectSessionRepoSize (state) {
62 | const ds = selectSessionDatasets(state)
63 | var size = 0
64 | ds.forEach((d, i) => {
65 | size += d.dataset.structure.length
66 | })
67 | return fileSize(size)
68 | }
69 |
--------------------------------------------------------------------------------
/lib/selectors/stats.js:
--------------------------------------------------------------------------------
1 | import { selectSessionDatasetsCount, selectSessionRepoSize } from './session'
2 |
3 | export function selectStats (state) {
4 | const datasetCount = selectSessionDatasetsCount(state)
5 | const repoSize = selectSessionRepoSize(state)
6 | return [
7 | {
8 | title: 'datasets',
9 | stat: datasetCount
10 | },
11 | {
12 | title: 'repo size (heads)',
13 | stat: repoSize.value + repoSize.name
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/lib/selectors/transfers.js:
--------------------------------------------------------------------------------
1 | export function selectTransferStatus (state, id) {
2 | return state && state.transfers && state.transfers[id]
3 | }
4 |
5 | export function selectIsTransfering (state, id) {
6 | const path = state && state.transfers && Object.keys(state.transfers).find(elm => elm === id)
7 | if (!path) {
8 | return false
9 | }
10 | const status = state.transfers[path]
11 | return status <= 0
12 | }
13 |
14 | export function selectTransfers (state) {
15 | return state && Object.keys(state.transfers)
16 | }
17 |
--------------------------------------------------------------------------------
/lib/selectors/viewConfig.js:
--------------------------------------------------------------------------------
1 |
2 | export function dimensions (device, view) {
3 | let width = 300
4 | let height = 400
5 | switch (device.size) {
6 | case 'xs':
7 | width = 300
8 | break
9 | case 'sm':
10 | width = 450
11 | break
12 | case 'md':
13 | width = 600
14 | break
15 | case 'lg':
16 | width = 900
17 | height = 400
18 | break
19 | case 'xl':
20 | width = 1100
21 | height = 500
22 | break
23 | }
24 |
25 | return { width, height }
26 | }
27 |
28 | export function wrapperStyle (device, view) {
29 | return { background: '#272727', border: 'none', borderRadius: 3 }
30 | }
31 |
32 | export function defaultColor (index) {
33 | return ['#49C797', '#E6DB74', '#B04BD8', '#E2542E', '#DFE2CE', '#49C3C7'][index]
34 | }
35 |
--------------------------------------------------------------------------------
/lib/store/configureStore.dev.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import { routerMiddleware } from 'connected-react-router'
3 | import thunk from 'redux-thunk'
4 |
5 | import history from '../history'
6 | import api from '../middleware/api'
7 | import localModels from '../middleware/localModels'
8 | import caf from '../middleware/caf'
9 | import extractBody from '../middleware/extractBody'
10 | import createRootReducer from '../reducers'
11 |
12 | const configureStore = (initialState) => {
13 | const middleware = [routerMiddleware(history), thunk, api, extractBody, caf, localModels]
14 | const enhancers = []
15 | const actionCreators = {}
16 |
17 | // If Redux DevTools Extension is installed use it, otherwise use Redux compose
18 | /* eslint-disable no-underscore-dangle */
19 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
20 | ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
21 | // Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
22 | actionCreators
23 | })
24 | : compose
25 | /* eslint-enable no-underscore-dangle */
26 |
27 | // Apply Middleware & Compose Enhancers
28 | enhancers.push(applyMiddleware(...middleware))
29 | const enhancer = composeEnhancers(...enhancers)
30 |
31 | // Create Store
32 | const rootReducerWithRouter = createRootReducer(history)
33 | const store = createStore(rootReducerWithRouter, initialState, enhancer)
34 |
35 | if (module.hot) {
36 | module.hot.accept('../reducers', () =>
37 | store.replaceReducer(require('../reducers')) // eslint-disable-line global-require
38 | )
39 | }
40 |
41 | return store
42 | }
43 |
44 | export default configureStore
45 |
--------------------------------------------------------------------------------
/lib/store/configureStore.js:
--------------------------------------------------------------------------------
1 | /* globals __BUILD__ */
2 | if (__BUILD__.MODE === 'production') {
3 | module.exports = require('./configureStore.prod')
4 | } else if (__BUILD__.MODE === 'development') {
5 | module.exports = require('./configureStore.dev')
6 | }
7 |
--------------------------------------------------------------------------------
/lib/store/configureStore.prod.js:
--------------------------------------------------------------------------------
1 | /* global __BUILD__ */
2 | import { createStore, applyMiddleware } from 'redux'
3 | import { routerMiddleware } from 'connected-react-router'
4 | import thunk from 'redux-thunk'
5 | import { createLogger } from 'redux-logger'
6 |
7 | import history from '../history'
8 | import api from '../middleware/api'
9 | import localModels from '../middleware/localModels'
10 | import caf from '../middleware/caf'
11 | import createRootReducer from '../reducers'
12 | import extractBody from '../middleware/extractBody'
13 |
14 | function configureStore (preloadedState) {
15 | const middleware = [routerMiddleware(history), thunk, api, extractBody, caf, localModels]
16 |
17 | if (__BUILD__.DEBUG_PROD) {
18 | const logger = createLogger({
19 | level: 'info',
20 | collapsed: true
21 | })
22 | middleware.push(logger)
23 | }
24 |
25 | const enhancer = applyMiddleware(...middleware)
26 | const rootReducerWithRouter = createRootReducer(history)
27 | return createStore(rootReducerWithRouter, preloadedState, enhancer)
28 | }
29 |
30 | export default configureStore
31 |
--------------------------------------------------------------------------------
/lib/utils/__tests__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "import/no-extraneous-dependencies": 0,
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/lib/utils/__tests__/ref.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | import { makeRef } from '../ref'
3 |
4 | describe('makeRef', () => {
5 | it('makes a ref from a peername and name', () => {
6 | const ref = makeRef('peername', 'name')
7 | expect(ref).toEqual('peername/name')
8 | })
9 | it('returns an empty string if nothing given', () => {
10 | const ref = makeRef()
11 | expect(ref).toEqual('')
12 | })
13 | it('returns an empty string if only name given', () => {
14 | const ref = makeRef('', 'name')
15 | expect(ref).toEqual('')
16 | })
17 | it('returns a ref with only the profileID if only the profileID given', () => {
18 | const ref = makeRef('', '', 'profileID')
19 | expect(ref).toEqual('@profileID')
20 | })
21 | it('returns a ref with only the profileID if only the profileID given', () => {
22 | const ref = makeRef('', '', 'profileID')
23 | expect(ref).toEqual('@profileID')
24 | })
25 | it('returns a ref with only the path if only the path given', () => {
26 | const ref = makeRef('', '', '', 'path')
27 | expect(ref).toEqual('@/path')
28 | })
29 | it('returns a full ref', () => {
30 | const ref = makeRef('peername', 'name', 'profileID', 'path')
31 | expect(ref).toEqual('peername/name@profileID/path')
32 | })
33 | it('returns the correct ref if all but profileID are given', () => {
34 | const ref = makeRef('peername', 'name', '', 'path')
35 | expect(ref).toEqual('peername/name@/path')
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/lib/utils/date.js:
--------------------------------------------------------------------------------
1 |
2 | export function relDate (datetime) {
3 | if (!datetime) {
4 | return ''
5 | }
6 |
7 | // Make a fuzzy time
8 | const now = new Date()
9 | const date = new Date(datetime)
10 |
11 | var delta = Math.round((now.valueOf() - date.valueOf()) / 1000)
12 |
13 | let minute = 60; let hour = minute * 60; let day = hour * 24
14 | let week = day * 7
15 |
16 | var fuzzy = delta.toFixed(0)
17 |
18 | if (delta < 30) {
19 | fuzzy = 'now'
20 | } else if (delta < minute) {
21 | fuzzy = delta + ' seconds ago'
22 | } else if (delta < 2 * minute) {
23 | fuzzy = 'a minute ago'
24 | } else if (delta < hour) {
25 | fuzzy = Math.floor(delta / minute) + ' minutes ago'
26 | } else if (Math.floor(delta / hour) === 1) {
27 | fuzzy = '1 hour ago'
28 | } else if (delta < day) {
29 | fuzzy = Math.floor(delta / hour) + ' hours ago'
30 | } else if (delta < day * 2) {
31 | fuzzy = 'yesterday'
32 | } else if (delta < week) {
33 | fuzzy = 'a week ago'
34 | } else if (delta < week / 3) {
35 | fuzzy = `${Math.round(delta / week)} weeks ago`
36 | } else if (delta < day * 30) {
37 | fuzzy = 'a month ago'
38 | } else if (delta / (day * 30) < 12) {
39 | fuzzy = `${Math.round(delta / (day * 30))} months ago`
40 | } else {
41 | fuzzy = `${Math.round(delta / (day * 365))} years ago`
42 | }
43 | return fuzzy
44 | }
45 |
--------------------------------------------------------------------------------
/lib/utils/filesize.js:
--------------------------------------------------------------------------------
1 | export default function fileSize (l) {
2 | var length = { name: '', value: 0 }
3 | if (l > Math.pow(2, 80)) {
4 | length.name = 'YB'
5 | length.value = Math.trunc(l / Math.pow(2, 80))
6 | } else if (l > Math.pow(2, 70)) {
7 | length.name = 'ZB'
8 | length.value = Math.trunc(l / Math.pow(2, 70))
9 | } else if (l > Math.pow(2, 60)) {
10 | length.name = 'EB'
11 | length.value = Math.trunc(l / Math.pow(2, 60))
12 | } else if (l > Math.pow(2, 50)) {
13 | length.name = 'PB'
14 | length.value = Math.trunc(l / Math.pow(2, 50))
15 | } else if (l > Math.pow(2, 40)) {
16 | length.name = 'TB'
17 | length.value = Math.trunc(l / Math.pow(2, 40))
18 | } else if (l > Math.pow(2, 30)) {
19 | length.name = 'GB'
20 | length.value = Math.trunc(l / Math.pow(2, 30))
21 | } else if (l > Math.pow(2, 20)) {
22 | length.name = 'MB'
23 | length.value = Math.trunc(l / Math.pow(2, 20))
24 | } else if (l > Math.pow(2, 10)) {
25 | length.name = 'KB'
26 | length.value = Math.trunc(l / Math.pow(2, 10))
27 | } else if (l > 0) {
28 | length.name = 'byte'
29 | length.value = l
30 | }
31 | if (l !== 1) {
32 | length.name += 's'
33 | }
34 | return length
35 | }
36 |
--------------------------------------------------------------------------------
/lib/utils/links.js:
--------------------------------------------------------------------------------
1 | export function addActiveToLink (linkList, pathname, defaultLinkName) {
2 | var active
3 | var newLinkList = linkList.map(link => {
4 | if (link.link !== '' && pathname.endsWith(link.link)) {
5 | active = true
6 | link['active'] = true
7 | return link
8 | }
9 | return link
10 | })
11 | if (!active) {
12 | newLinkList = newLinkList.map(link => {
13 | if (link.name === defaultLinkName) {
14 | link['active'] = true
15 | return link
16 | }
17 | return link
18 | })
19 | }
20 | return newLinkList
21 | }
22 |
23 | export function datasetLinks (dataset = {}, fromRegistry, isLocal, sessionProfile) {
24 | const keys = Object.keys(dataset)
25 | var linkList = [
26 | {
27 | name: 'Overview',
28 | link: 'overview'
29 | }
30 | ]
31 | if (keys.includes('viz') && dataset.viz.scriptPath) {
32 | linkList.push(
33 | {
34 | name: 'Viz',
35 | link: 'viz'
36 | }
37 | )
38 | }
39 | if (!sessionProfile || !fromRegistry) {
40 | linkList.push(
41 | {
42 | name: 'Body',
43 | link: 'body'
44 | }
45 | )
46 | }
47 | if (keys.includes('meta')) {
48 | linkList.push(
49 | {
50 | name: 'Meta',
51 | link: 'meta'
52 | }
53 | )
54 | }
55 | if (keys.includes('structure')) {
56 | linkList.push(
57 | {
58 | name: 'Structure',
59 | link: 'structure'
60 | }
61 | )
62 | }
63 | if (keys.includes('transform') && dataset.transform.scriptPath) {
64 | linkList.push(
65 | {
66 | name: 'Transform',
67 | link: 'transform'
68 | }
69 | )
70 | }
71 | linkList.push(
72 | {
73 | name: 'History',
74 | link: 'history'
75 | }
76 | )
77 | return linkList
78 | }
79 |
--------------------------------------------------------------------------------
/lib/utils/localStore.js:
--------------------------------------------------------------------------------
1 | /* globals window */
2 |
3 | class Storage {
4 | constructor () {
5 | this.store = {}
6 | }
7 |
8 | getItem (key) {
9 | return this.store[key]
10 | }
11 | setItem (key, value) {
12 | this.store[key] = value
13 | }
14 | removeItem (key) {
15 | delete this.store
16 | }
17 | clear () {
18 | this.store = {}
19 | }
20 | }
21 |
22 | export default function store () {
23 | if (window.localStorage) {
24 | return window.localStorage
25 | }
26 | return new Storage()
27 | }
28 |
--------------------------------------------------------------------------------
/lib/utils/ref.js:
--------------------------------------------------------------------------------
1 | export function makeRef (peername = '', name = '', profileID = '', path = '') {
2 | var ref = ''
3 | if (peername && name) {
4 | ref += `${peername}/${name}`
5 | }
6 | if (profileID || path) {
7 | ref += '@'
8 | if (profileID) {
9 | ref += profileID
10 | }
11 | if (path) {
12 | if (path[0] !== '/') {
13 | ref += '/'
14 | }
15 | ref += path
16 | }
17 | }
18 | return ref
19 | }
20 |
--------------------------------------------------------------------------------
/lib/utils/reflect.js:
--------------------------------------------------------------------------------
1 | // kinda like the golang reflect package, these funcs give introspection into
2 | // data structures
3 |
4 | export function isEmpty (v) {
5 | if (v === undefined) { return true }
6 |
7 | switch (v.constructor) {
8 | case Object:
9 | return isEmptyObj(v)
10 | case Array:
11 | return v.length === 0
12 | }
13 | return !v
14 | }
15 |
16 | export function isEmptyObj (obj = {}) {
17 | return Object.keys(obj).length === 0 && obj.constructor === Object
18 | }
19 |
--------------------------------------------------------------------------------
/release/github/latest-mac.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.1",
3 | "releaseDate": "2018-05-15T18:40:43.136Z",
4 | "url": "https://github.com/qri-io/frontend/releases/download/v0.0.1/qri-webapp-0.0.1-mac.zip"
5 | }
6 |
--------------------------------------------------------------------------------
/resources/icons/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/1024x1024.png
--------------------------------------------------------------------------------
/resources/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/128x128.png
--------------------------------------------------------------------------------
/resources/icons/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/16x16.png
--------------------------------------------------------------------------------
/resources/icons/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/24x24.png
--------------------------------------------------------------------------------
/resources/icons/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/256x256.png
--------------------------------------------------------------------------------
/resources/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/32x32.png
--------------------------------------------------------------------------------
/resources/icons/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/48x48.png
--------------------------------------------------------------------------------
/resources/icons/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/512x512.png
--------------------------------------------------------------------------------
/resources/icons/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/64x64.png
--------------------------------------------------------------------------------
/resources/icons/96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/icons/96x96.png
--------------------------------------------------------------------------------
/resources/index.tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | qri
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/resources/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # https://stackoverflow.com/a/24067243
4 | function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
5 |
6 | path=$(which qri)
7 |
8 | if [ $path ]
9 | then
10 | current=$(qri version -c)
11 | latest=$(./qri version -c)
12 | if version_gt $latest $current; then
13 | cp ./qri $path
14 | fi
15 | else
16 | path='/usr/local/bin/qri'
17 | cp $BINARY_PATH /usr/local/bin/
18 | fi
19 |
20 | echo $path
--------------------------------------------------------------------------------
/resources/qri:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qri-io/2017-frontend/e19bc3a7e7ae94687e2f4e34eb7f39f39609e4c8/resources/qri
--------------------------------------------------------------------------------
/stories/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { action } from '@storybook/addon-actions'
4 | import Button from '../lib/components/chrome/Button.js'
5 | import DownloadBar from '../lib/components/chrome/DownloadBar.js'
6 |
7 | // Don't forget to import our stylesheet:
8 | import '../lib/app.global.scss'
9 |
10 | storiesOf('Button', module)
11 | .add('with text', () => )
12 | .add('loading', () => )
13 |
14 | storiesOf('Download bar', module)
15 | .add('standard', () => )
16 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 | // var standard = require('standard')
3 | // var test = require('tape')
4 |
5 | // test('api: lintFiles', function (t) {
6 | // t.plan(3)
7 | // standard.lintFiles([], { cwd: 'bin' }, function (err, result) {
8 | // t.error(err, 'no error while linting')
9 | // t.equal(typeof result, 'object', 'result is an object')
10 | // t.equal(result.errorCount, 0)
11 | // })
12 | // })
13 |
14 | // test('api: lintText', function (t) {
15 | // t.plan(3)
16 | // standard.lintText('console.log("hi there")\n', function (err, result) {
17 | // t.error(err, 'no error while linting')
18 | // t.equal(typeof result, 'object', 'result is an object')
19 | // t.equal(result.errorCount, 1, 'should have used single quotes')
20 | // })
21 | // })
22 |
23 | describe('TestSuite', () => {
24 | it('Dummy Test', () => {
25 | expect(true).toEqual(true)
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/version.js:
--------------------------------------------------------------------------------
1 | export default '0.7.2'
2 |
--------------------------------------------------------------------------------
/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Base webpack config used across other specific configs
3 | */
4 |
5 | import path from 'path'
6 | import webpack from 'webpack'
7 | import { dependencies as externals } from './app/package.json'
8 |
9 | export default {
10 | externals: Object.keys(externals || {}),
11 |
12 | module: {
13 | rules: [{
14 | test: /\.jsx?$/,
15 | exclude: /node_modules/,
16 | use: {
17 | loader: 'babel-loader',
18 | options: {
19 | cacheDirectory: true
20 | }
21 | }
22 | }]
23 | },
24 |
25 | output: {
26 | path: path.join(__dirname, 'app'),
27 | filename: 'renderer.dev.js',
28 | // https://github.com/webpack/webpack/issues/1114
29 | libraryTarget: 'commonjs2'
30 | },
31 |
32 | /**
33 | * Determine the array of extensions that should be used to resolve modules.
34 | */
35 | resolve: {
36 | extensions: ['.js', '.jsx', '.json'],
37 | modules: [
38 | path.join(__dirname, 'app'),
39 | 'node_modules'
40 | ]
41 | },
42 |
43 | plugins: [
44 | new webpack.DefinePlugin({
45 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
46 | 'process.env.API_URL': JSON.stringify('http://localhost:2503')
47 | }),
48 |
49 | new webpack.NamedModulesPlugin()
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/webpack.config.eslint.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 |
3 | module.exports = require('./webpack.config.renderer.dev')
4 |
--------------------------------------------------------------------------------