├── .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 | } 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 |
17 | {spinner &&
} 18 |
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 &&
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 |
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 |
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)}
: 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 | 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 |
34 | { onToggleExpand && {expanded ? 'contract' : 'expand'} } 35 | {labels.map((label, i) => { 36 | return ( 37 |

{label}

41 | ) 42 | } 43 | )} 44 |
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 |
29 | Let's Get Started right
30 | 31 |
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 | 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 |
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 |
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 |