├── todo.md ├── zomes ├── whoami │ ├── zome.json │ └── code │ │ ├── .hcbuild │ │ ├── src │ │ ├── whoami_fn.rs │ │ └── lib.rs │ │ └── Cargo.toml └── happs │ ├── zome.json │ └── code │ ├── .hcbuild │ ├── Cargo.toml │ └── src │ ├── ratings │ ├── handlers.rs │ └── mod.rs │ ├── categories │ ├── handlers.rs │ └── mod.rs │ ├── happs │ ├── mod.rs │ └── handlers.rs │ └── lib.rs ├── ui-src ├── public │ ├── favicon.ico │ ├── favicon2.ico │ ├── holo-logo.png │ ├── manifest.json │ └── index.html └── src │ ├── pages │ ├── NoMatch.tsx │ ├── AllAppsPage.css │ ├── style.css │ ├── SplashScreen.tsx │ ├── AllAppsPage.tsx │ ├── SplashScreen.css │ └── NewApp.tsx │ ├── index.tsx │ ├── App.tsx │ ├── store.tsx │ ├── types │ └── app.ts │ ├── components │ ├── MainNav.css │ ├── MainNav.tsx │ └── AppCard.tsx │ ├── index.css │ ├── utils.ts │ ├── reducer.ts │ ├── actions.ts │ └── registerServiceWorker.ts ├── tsconfig.test.json ├── Cargo.toml ├── pkgs.nix ├── test ├── package.json ├── single_agent_tests │ ├── whoami_test.js │ ├── categories_test.js │ └── happs_test.js ├── index.js ├── multi_agent_tests │ └── upvote_test.js └── config.js ├── config ├── jest │ ├── typescriptTransform.js │ ├── fileTransform.js │ └── cssTransform.js ├── polyfills.js ├── paths.js ├── env.js ├── webpackDevServer.config.js ├── webpack.config.dev.js └── webpack.config.prod.js ├── .hcignore ├── shell.nix ├── test-create-app-query.json ├── .gitignore ├── default.nix ├── manifest.json ├── tslint.json ├── .circleci └── config.yml ├── CONTRIBUTING.md ├── scripts ├── test.js ├── start.js └── build.js ├── tsconfig.json ├── conductor-config.toml ├── dynamodb └── default.nix ├── @types ├── hchc │ └── index.d.ts └── holochain │ ├── index.d.ts │ └── holochain.d.ts ├── keystore.key ├── Makefile ├── holochain.ts ├── README.md ├── package.json └── LICENSE /todo.md: -------------------------------------------------------------------------------- 1 | - Validate form -------------------------------------------------------------------------------- /zomes/whoami/zome.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "The whoami App" 3 | } -------------------------------------------------------------------------------- /zomes/happs/zome.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "The happ-store App" 3 | } 4 | -------------------------------------------------------------------------------- /ui-src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/happ-store/HEAD/ui-src/public/favicon.ico -------------------------------------------------------------------------------- /ui-src/public/favicon2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/happ-store/HEAD/ui-src/public/favicon2.ico -------------------------------------------------------------------------------- /ui-src/public/holo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/happ-store/HEAD/ui-src/public/holo-logo.png -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "zomes/whoami/code", 5 | "zomes/happs/code", 6 | ] 7 | 8 | exclude = [ 9 | "holochain-rust", 10 | ] 11 | -------------------------------------------------------------------------------- /pkgs.nix: -------------------------------------------------------------------------------- 1 | import (fetchTarball { 2 | url = "https://github.com/Holo-Host/holo-nixpkgs/archive/9b8742481881e3b0efe0e7ece9b508b988c27bc7.tar.gz"; 3 | sha256 = "1zf55bl2pdj7n5f37fr9bigb7y6qbbsxrm1xhihm325y1iwvhskz"; 4 | }) 5 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": {}, 3 | "dependencies": { 4 | "@holochain/tryorama": "^0.3.4-dev.0", 5 | "faucet": "0.0.1", 6 | "json3": "^3.3.3", 7 | "tape": "^4.11.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /config/jest/typescriptTransform.js: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | 'use strict'; 4 | 5 | const tsJestPreprocessor = require('ts-jest/preprocessor'); 6 | 7 | module.exports = tsJestPreprocessor; 8 | -------------------------------------------------------------------------------- /.hcignore: -------------------------------------------------------------------------------- 1 | dist 2 | test 3 | common 4 | Cargo.lock 5 | Cargo.toml 6 | vendor 7 | tslint.json 8 | test-create-app-query.json 9 | package.json 10 | tsconfig.json 11 | tsconfig.test.json 12 | package-lock.json 13 | holochain-rust 14 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./pkgs.nix {}, shell ? false }: 2 | 3 | with pkgs; 4 | 5 | mkShell { 6 | inputsFrom = lib.attrValues (import ./. { 7 | inherit pkgs; 8 | shell = true; 9 | }); 10 | 11 | buildInputs = [ 12 | # additional packages go here 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /test-create-app-query.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc":"2.0", 3 | "method":"happ-store/happs/main/create_app", 4 | "id":123, 5 | "params": { 6 | "uuid": "762934-19234-123495-12354", 7 | "title": "Errand", 8 | "description": "A better Trello", 9 | "thumbnail": "/IMG.jpg" 10 | } 11 | } -------------------------------------------------------------------------------- /zomes/happs/code/.hcbuild: -------------------------------------------------------------------------------- 1 | { 2 | "steps": [ 3 | { 4 | "command": "cargo", 5 | "arguments": [ 6 | "build", 7 | "--release", 8 | "--target=wasm32-unknown-unknown" 9 | ] 10 | } 11 | ], 12 | "artifact": "../../../target/wasm32-unknown-unknown/release/happs.wasm" 13 | } 14 | -------------------------------------------------------------------------------- /zomes/whoami/code/.hcbuild: -------------------------------------------------------------------------------- 1 | { 2 | "steps": [ 3 | { 4 | "command": "cargo", 5 | "arguments": [ 6 | "build", 7 | "--release", 8 | "--target=wasm32-unknown-unknown" 9 | ] 10 | } 11 | ], 12 | "artifact": "../../../target/wasm32-unknown-unknown/release/whoami.wasm" 13 | } 14 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-HApps/ 3 | ui/ 4 | dna/ 5 | .dist/ 6 | .cache/ 7 | npm-debug.log 8 | bundle.json 9 | 10 | /dist 11 | /holochain-rust 12 | node_modules/ 13 | /target 14 | /result 15 | *.log 16 | 17 | tmp-storage 18 | 19 | # OS generated files # 20 | ###################### 21 | .tags* 22 | .DS_Store 23 | ._* 24 | .Spotlight-V100 25 | .tags* 26 | .Trashes 27 | ehthumbs.db 28 | .cargo 29 | *.zip 30 | -------------------------------------------------------------------------------- /ui-src/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /test/single_agent_tests/whoami_test.js: -------------------------------------------------------------------------------- 1 | const { one } = require('../config') 2 | 3 | module.exports = (scenario) => { 4 | scenario('get user address', async (s, t) => { 5 | 6 | const { liza } = await s.players({liza: one('liza')}, true) 7 | 8 | const result = await liza.call( "app", "whoami", "get_user", {}) 9 | console.log(result) 10 | t.ok(result.Ok) 11 | 12 | await liza.kill() 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./pkgs.nix {}, shell ? false }: 2 | 3 | with pkgs; 4 | 5 | let 6 | inherit (darwin.apple_sdk.frameworks) CoreServices Security; 7 | in 8 | 9 | { 10 | happ-store = buildDNA { 11 | inherit shell; 12 | 13 | name = "happ-store"; 14 | src = gitignoreSource ./.; 15 | 16 | nativeBuildInputs = [ 17 | zip 18 | ] 19 | ++ lib.optionals stdenv.isDarwin [ CoreServices ]; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /zomes/whoami/code/src/whoami_fn.rs: -------------------------------------------------------------------------------- 1 | use hdk::{ 2 | error::ZomeApiResult, 3 | holochain_json_api::{error::JsonError, json::JsonString}, 4 | AGENT_ADDRESS, AGENT_ID_STR, 5 | }; 6 | 7 | #[derive(Serialize, Deserialize, Debug, Clone, DefaultJson)] 8 | pub struct UserData { 9 | hash: String, 10 | name: String, 11 | } 12 | 13 | pub fn handle_get_agent() -> ZomeApiResult { 14 | Ok(UserData { 15 | hash: AGENT_ADDRESS.to_string(), 16 | name: AGENT_ID_STR.to_string(), 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /zomes/happs/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | workspace = "../../.." 3 | name = "happs" 4 | version = "0.6.0" 5 | authors = [ 6 | "Willem Olding ", 7 | "zo-el ", 8 | "Lisa Jetton " 9 | ] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | serde = "1.0" 14 | serde_json = "1.0" 15 | serde_derive = "1.0" 16 | syn = "=0.15.31" 17 | hdk = { path = "../../../holochain-rust/crates/hdk" } 18 | holochain_json_derive = "=0.0.23" 19 | 20 | [lib] 21 | path = "src/lib.rs" 22 | crate-type = ["cdylib"] 23 | -------------------------------------------------------------------------------- /zomes/whoami/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | workspace = "../../.." 3 | name = "whoami" 4 | version = "0.6.0" 5 | authors = [ 6 | "Willem Olding ", 7 | "Zo-El ", 8 | "Lisa Jetton " 9 | ] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | serde = "1.0" 14 | serde_json = "1.0" 15 | serde_derive = "1.0" 16 | syn = "=0.15.31" 17 | hdk = { path = "../../../holochain-rust/crates/hdk" } 18 | holochain_json_derive = "=0.0.23" 19 | 20 | [lib] 21 | path = "src/lib.rs" 22 | crate-type = ["cdylib"] 23 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hApp Store", 3 | "description": "The Holochain hApp Store.", 4 | "authors": [ 5 | { 6 | "identifier": [ 7 | "Zo-El ", 8 | "Lisa Jetton ", 9 | "Willem Olding " 10 | ], 11 | "public_key_source": "", 12 | "signature": "" 13 | } 14 | ], 15 | "uuid": "00000000-0000-0000-0000-000000000000", 16 | "version": "0.6.0", 17 | "dht": {}, 18 | "properties": { 19 | "sim2h_url": "ws://happstore.sim2h.net:9000" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], 3 | "linterOptions": { 4 | "exclude": [ 5 | "config/**/*.js", 6 | "node_modules/**/*.ts" 7 | ] 8 | }, 9 | "rules": { 10 | "array-type": false, 11 | "interface-name": [true, "never-prefix"], 12 | "max-classes-per-file": false, 13 | "no-bitwise": false, 14 | "no-console": false, 15 | "no-reference": false, 16 | "interface-over-type-literal": false, 17 | "object-literal-sort-keys": false, 18 | "ordered-imports": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ui-src/src/pages/NoMatch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Row, Col, CardPanel } from 'react-materialize'; 3 | 4 | const NoMatch = () => 5 |
6 | 7 | 8 | 9 |

404 App Not Found

10 |

11 | 12 | 🙄 13 | 14 |

15 |
16 | 17 |
18 |
; 19 | 20 | export default NoMatch; 21 | -------------------------------------------------------------------------------- /ui-src/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | import 'bootstrap/dist/css/bootstrap.min.css'; 6 | 7 | import { Provider } from 'react-redux'; 8 | 9 | import registerServiceWorker from './registerServiceWorker'; 10 | import CreateStore from './store'; 11 | 12 | const store = CreateStore() 13 | 14 | const root = 15 | 16 | 17 | 18 | ReactDOM.render( 19 | root, 20 | document.getElementById('root') as HTMLElement 21 | ); 22 | registerServiceWorker(); 23 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: nixos/nix 7 | # Required because hc package default DNA name is working directory name 8 | working_directory: ~/happ-store 9 | steps: 10 | - checkout 11 | - run: | 12 | nix-env -f pkgs.nix -iA git bash gnumake 13 | - run: | 14 | nix-shell \ 15 | --option substituters 'https://cache.holo.host https://cache.nixos.org' \ 16 | --option trusted-public-keys \ 17 | 'cache.holo.host-1:lNXIXtJgS9Iuw4Cu6X0HINLu9sTfcjEntnrgwMQIMcE= 18 | cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' \ 19 | --run 'make test' 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Build release DNA build 2 | 3 | Run `nix-build -A happ-store`. 4 | 5 | ## Develop against a local `holochain-rust` version 6 | 7 | Run `nix-shell -I holochain-rust=../holochain-rust`, where `../holochain-rust` 8 | is a path to `holochain-rust` checkout. Then, develop as usual: you can run 9 | `make build`, `make test` as usual. 10 | 11 | ## Develop against built-in `holochain-rust` version 12 | 13 | See previous section, run `nix-shell` without arguments. 14 | 15 | ## Use Holo cache to speed up builds 16 | 17 | Add these lines to your ~/.config/nix/nix.conf: 18 | 19 | substituters = https://cache.holo.host/ https://cache.nixos.org/ 20 | trusted-public-keys = cache.holo.host-1:lNXIXtJgS9Iuw4Cu6X0HINLu9sTfcjEntnrgwMQIMcE= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= 21 | -------------------------------------------------------------------------------- /zomes/whoami/code/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate hdk; 3 | extern crate serde; 4 | #[macro_use] 5 | extern crate serde_derive; 6 | #[macro_use] 7 | extern crate holochain_json_derive; 8 | 9 | use hdk::{ 10 | error::ZomeApiResult, 11 | holochain_json_api::{error::JsonError, json::JsonString}, 12 | }; 13 | 14 | pub mod whoami_fn; 15 | 16 | define_zome! { 17 | entries: [ ] 18 | 19 | init: || { Ok(()) } 20 | 21 | validate_agent: |validation_data : EntryValidationData::| { 22 | Ok(()) 23 | } 24 | 25 | functions: [ 26 | get_user: { 27 | inputs:| |, 28 | outputs: |result: ZomeApiResult|, 29 | handler: whoami_fn::handle_get_agent 30 | } 31 | ] 32 | 33 | traits: { 34 | hc_public [get_user] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ui-src/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as redux from 'redux'; 3 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom" 4 | 5 | import MainNav from "./components/MainNav" 6 | import SplashScreen from "./pages/SplashScreen" 7 | import AllAppsPage from "./pages/AllAppsPage" 8 | import NoMatch from "./pages/NoMatch" 9 | import NewApp from "./pages/NewApp" 10 | 11 | const exact: boolean = true; 12 | const App = () => 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
; 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /ui-src/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { combineReducers, createStore, applyMiddleware, compose } from 'redux' 2 | 3 | import { holochainMiddleware } from '@holochain/hc-redux-middleware' 4 | import { connect } from '@holochain/hc-web-client' 5 | 6 | import reducer from './reducer' 7 | import { URL } from './utils' 8 | 9 | // in production the conductor will tell us where to connect. For dev must speficy a port 10 | const url = process.env.NODE_ENV === 'production' ? undefined : 'ws://localhost:10002' 11 | // put middleware in this array to have it applied 12 | const middleware: Array = [holochainMiddleware(connect({url}))] 13 | 14 | // @ts-ignore 15 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose 16 | 17 | function CreateStore () { 18 | return createStore( 19 | reducer, 20 | composeEnhancers( 21 | applyMiddleware(...middleware) 22 | ) 23 | ) 24 | } 25 | 26 | export default CreateStore 27 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | let argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI, in coverage mode, or explicitly running all tests 22 | if ( 23 | !process.env.CI && 24 | argv.indexOf('--coverage') === -1 && 25 | argv.indexOf('--watchAll') === -1 26 | ) { 27 | argv.push('--watch'); 28 | } 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /ui-src/src/types/app.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface AppResource { 3 | location: string, 4 | hash: string, 5 | handle?: string, 6 | } 7 | 8 | export interface AppCreationSpec { 9 | title: string, 10 | description: string, 11 | thumbnailUrl: string, 12 | homepageUrl: string, 13 | dnas: Array, 14 | ui: AppResource | null, 15 | } 16 | 17 | export const defaultAppCreationSpec = { 18 | title: '', 19 | description: '', 20 | thumbnailUrl: '', 21 | homepageUrl: '', 22 | dnas: [], 23 | ui: null, 24 | } 25 | 26 | export interface AppCreationSpecSnake { 27 | title: string, 28 | description: string, 29 | thumbnail_url: string, 30 | homepage_url: string, 31 | dnas: Array, 32 | ui: AppResource | null, 33 | } 34 | 35 | export interface App { 36 | address: string, 37 | author: string, 38 | upvotes: number, 39 | upvotedByMe: boolean, 40 | appEntry: AppCreationSpec, 41 | hashAddress:string 42 | } 43 | -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "esnext", 5 | "target": "es5", 6 | "downlevelIteration": true, 7 | "lib": ["es6", "dom"], 8 | "sourceMap": true, 9 | "allowJs": true, 10 | "jsx": "react", 11 | "moduleResolution": "node", 12 | "rootDir": "src", 13 | "experimentalDecorators": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noImplicitReturns": false, 16 | "noImplicitThis": true, 17 | "noImplicitAny": false, 18 | "strictNullChecks": true, 19 | "suppressImplicitAnyIndexErrors": true, 20 | "noUnusedLocals": false 21 | }, 22 | "files": [ 23 | "./holochain.d.ts" 24 | ], 25 | "include": [ 26 | "ui-src/src/**/*" 27 | ], 28 | "exclude": [ 29 | "node_modules", 30 | "build-HApps", 31 | "scripts", 32 | "acceptance-tests", 33 | "webpack", 34 | "jest", 35 | "src/setupTests.ts", 36 | "ui-src/src/__tests__" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /conductor-config.toml: -------------------------------------------------------------------------------- 1 | [[agents]] 2 | id = 'happ-agent' 3 | name = 'hApp Store Agent' 4 | keystore_file = '/home/lisa/.config/holochain/keys/HcSCj35Fgj7smCq4bxhtf7rrJS4v8pxxi66xtnwOh4mB3fvjIncm4Axaig8ng6r' 5 | public_address = 'HcSCj35Fgj7smCq4bxhtf7rrJS4v8pxxi66xtnwOh4mB3fvjIncm4Axaig8ng6r' 6 | 7 | [[dnas]] 8 | id = "happ_store_dna" 9 | file = "dist/happ-store.dna.json" 10 | hash = "Qm328wyq38924x" 11 | 12 | [[instances]] 13 | id = "happ-store" 14 | dna = "happ_store_dna" 15 | agent = "hApp-agent" 16 | [instances.logger] 17 | type = "simple" 18 | file = "app_spec.log" 19 | [instances.storage] 20 | type = "file" 21 | path = "tmp-storage" 22 | 23 | [[interfaces]] 24 | id = "websocket_interface" 25 | [interfaces.driver] 26 | type = "websocket" 27 | port = 4000 28 | [[interfaces.instances]] 29 | id = "happ-store" 30 | 31 | [[ui_bundles]] 32 | id = "main" 33 | root_dir = "./ui" 34 | hash = "Qm000" 35 | 36 | [[ui_interfaces]] 37 | id = "ui-interface" 38 | bundle = "main" 39 | port = 3000 40 | dna_interface = "websocket_interface" 41 | -------------------------------------------------------------------------------- /dynamodb/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | let 3 | name = "dynamodb"; 4 | dynamo-jar = pkgs.stdenv.mkDerivation { 5 | name = name; 6 | 7 | src = pkgs.fetchurl { 8 | url = "https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_test_2018-03-23/dynamodb_local_latest.tar.gz"; 9 | sha256 = "0wzp07wdmay4kdc31fs14rbpwch0ncq6zsl7yl3vfa0rk9klgx9x"; 10 | }; 11 | 12 | nativeBuildInputs = [ pkgs.makeWrapper ]; 13 | 14 | unpackPhase = "tar -zxvf $src"; 15 | 16 | installPhase = 17 | '' 18 | pwd 19 | mkdir -p $out/lib 20 | mv ./DynamoDBLocal_lib $out/DynamoDBLocal_lib 21 | mv ./DynamoDBLocal.jar $out 22 | ''; 23 | }; 24 | 25 | script = pkgs.writeShellScriptBin "dynamodb" 26 | '' 27 | cd ${dynamo-jar} 28 | mkdir -p $TMP/dynamodb 29 | ${pkgs.jdk}/bin/java -Djava.library.path=./DynamoDBLocal_lib/ -jar ./DynamoDBLocal.jar -dbPath "$TMP/dynamodb" "$@" 30 | ''; 31 | 32 | inMemory = pkgs.writeShellScriptBin "dynamodb-memory" 33 | '' 34 | cd ${dynamo-jar} 35 | ${pkgs.jdk}/bin/java -Djava.library.path=./DynamoDBLocal_lib/ -jar ./DynamoDBLocal.jar -inMemory "$@" 36 | ''; 37 | in 38 | { 39 | buildInputs = [ pkgs.jdk script inMemory ]; 40 | } 41 | -------------------------------------------------------------------------------- /ui-src/src/components/MainNav.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | display: flex; 3 | flex-wrap: wrap; 4 | flex-direction: row !important; 5 | padding-left: 0; 6 | padding-bottom: 0; 7 | list-style: none; 8 | padding: 50px 15px; 9 | } 10 | 11 | .nav-link { 12 | color: #29a19e !important; 13 | border-radius: .25rem; 14 | text-align: center !important; 15 | padding: .5rem 1rem; 16 | display: block; 17 | flex: 1 1 auto !important; 18 | text-decoration: none; 19 | height: 36px; 20 | animation: fade-in-zoom-out .5s .5s 1 ease both 21 | } 22 | 23 | .nav-link.active { 24 | color: white !important; 25 | background-color:#026269 !important; /*alternative color: #0c5e5b , #367780, #275756 */ 26 | } 27 | 28 | .nav-link.disabled { 29 | color: #6c757d; 30 | } 31 | 32 | .app-logo { 33 | height: 75px; 34 | margin: -20px 5px 10px 5px; 35 | } 36 | 37 | .fade-in-logo { 38 | animation: fade-in-zoom-out .5s .5s 1 ease both 39 | } 40 | 41 | @keyframes fade-in-zoom-out { 42 | 0% { 43 | transform: translateZ(500px); 44 | opacity: 0; 45 | } 46 | 50% { 47 | opacity: 0; 48 | } 49 | 100% { 50 | transform: translateZ(0px); 51 | opacity: 1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /zomes/happs/code/src/ratings/handlers.rs: -------------------------------------------------------------------------------- 1 | use hdk::{ 2 | error::ZomeApiResult, 3 | holochain_core_types::{entry::Entry, link::LinkMatch}, 4 | holochain_persistence_api::cas::content::Address, 5 | utils, AGENT_ADDRESS, 6 | }; 7 | 8 | use crate::ratings::Ratings; 9 | 10 | pub fn handle_creating_ratings( 11 | rate: String, 12 | review: String, 13 | reviewed_hash: Address, 14 | ) -> ZomeApiResult
{ 15 | let ratings_entry = Entry::App( 16 | "ratings".into(), 17 | Ratings { 18 | rate: rate.to_string(), 19 | review: review.to_string(), 20 | author: AGENT_ADDRESS.to_string(), 21 | timestamp: "TODO:(Add Date)".to_string(), 22 | } 23 | .into(), 24 | ); 25 | 26 | let address = hdk::commit_entry(&ratings_entry)?; 27 | hdk::link_entries(&reviewed_hash, &address, "rating_tag", "")?; 28 | Ok(address) 29 | } 30 | 31 | pub fn handle_get_reviews_by_hash(reviewed_hash: Address) -> ZomeApiResult> { 32 | utils::get_links_and_load_type( 33 | &reviewed_hash, 34 | LinkMatch::Exactly("rating_tag"), 35 | LinkMatch::Any, 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /@types/hchc/index.d.ts: -------------------------------------------------------------------------------- 1 | /*Comments Zome*/ 2 | interface CreateCommentsParams { 3 | comment: "string"; 4 | commentedOnHash: "string"; 5 | } 6 | 7 | interface Comments { 8 | author: "string"; 9 | comment: "string"; 10 | timestamp: "string" 11 | } 12 | 13 | type Reply = Comment_Chain[] 14 | 15 | interface Comment_Chain { 16 | comment: Comment; 17 | reply: Reply; 18 | } 19 | 20 | /*Ratings Zome*/ 21 | interface CreateRatingsParams { 22 | rate: string; 23 | review: string; 24 | reviewedHash: string; 25 | } 26 | 27 | interface Ratings { 28 | author: string; 29 | rate: string; 30 | review: string; 31 | timestamp: string; 32 | } 33 | 34 | /*Tag Zome*/ 35 | interface AddTagParams{ 36 | tag:string; 37 | hash:string; 38 | } 39 | 40 | /*HCHC Zome Params*/ 41 | interface CreateAppParams { 42 | title: string; 43 | description?: string; 44 | thumbnail?: string; 45 | } 46 | 47 | interface CreateAppParams { 48 | title: string; 49 | description?: string; 50 | thumbnail?: string; 51 | } 52 | 53 | interface CodeParams { 54 | dna: string; 55 | test: string; 56 | } 57 | 58 | interface UiSkinParams { 59 | title: string; 60 | link: string; 61 | author: string; 62 | thumbnail?: string; 63 | } 64 | -------------------------------------------------------------------------------- /ui-src/src/components/MainNav.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { connect } from 'react-redux'; 3 | import { fetchPOST } from '../utils'; 4 | 5 | import { Whoami } from '../actions'; 6 | 7 | import "./MainNav.css"; 8 | 9 | class MainNav extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.props.fetchAgent(); 13 | } 14 | 15 | public render() { 16 | if (!this.props.currentAgent) { 17 | return
18 | } 19 | 20 | const agent = this.props.currentAgent || {name: 'none', hash: 'none'}; 21 | let agentName = agent.name; 22 | if (agentName.length > 15 ) { 23 | agentName = agentName.substring(0,15) + "..."; 24 | } 25 | return ( 26 | 31 | ) 32 | } 33 | } 34 | 35 | const mapStateToProps = ({ currentAgent }) => ({ currentAgent }); 36 | const mapDispatchToProps = dispatch => ({ 37 | fetchAgent: () => dispatch(Whoami.create({})), 38 | }); 39 | 40 | export default connect(mapStateToProps, mapDispatchToProps)(MainNav); 41 | -------------------------------------------------------------------------------- /ui-src/src/pages/AllAppsPage.css: -------------------------------------------------------------------------------- 1 | .all-apps-header { 2 | font-family: 'Poppins', sans-serif; 3 | text-align: center; 4 | padding: 20px 50px 0px 50px; 5 | margin: 50px; 6 | margin-bottom: 25px; 7 | color: #d7e0e0; /* #a9dee4 */ 8 | } 9 | 10 | @keyframes fade-in-zoom-out { 11 | 0% { 12 | transform: translateZ(500px); 13 | opacity: 0; 14 | } 15 | 50% { 16 | opacity: 0; 17 | } 18 | 100% { 19 | transform: translateZ(0px); 20 | opacity: 1; 21 | } 22 | } 23 | 24 | .page-header { 25 | background-color: #222; 26 | height: 150px; 27 | padding: 20px; 28 | color: white; 29 | } 30 | 31 | 32 | .App { 33 | margin: auto; 34 | text-align: center; 35 | } 36 | /* 37 | .app-logo { 38 | animation: App-logo-spin .48 5s linear; 39 | animation-delay: 2000; 40 | height: 150px; 41 | margin: 0px 5px 10px 5px; 42 | } */ 43 | /* 44 | @keyframes App-logo-spin { 45 | from { transform: rotate(0deg); } 46 | to { transform: rotate(360deg); } 47 | } */ 48 | 49 | .app-title { 50 | font-size: 1.5em; 51 | } 52 | 53 | .app-intro { 54 | font-size: large; 55 | } 56 | 57 | .appstore-app-icons { 58 | display: inline-block; 59 | flex: auto; 60 | position: relative; 61 | animation-name: slide-in-from-right ; 62 | animation-duration: 4s; 63 | } 64 | 65 | @keyframes slide-in-from-right { 66 | 0% { right: -100% } 67 | 50% { right: -2% } 68 | 100% { right: 0% } 69 | } 70 | -------------------------------------------------------------------------------- /@types/holochain/index.d.ts: -------------------------------------------------------------------------------- 1 | // holochain ambient type defs for API 2 | 3 | /// 4 | 5 | declare function property(name: string): string; 6 | declare function makeHash (entryType: string, entryData: any): Hash; 7 | declare function debug(value: any): void; 8 | declare function call(zomeName: string, functionName: string, arguments: string | object): any; 9 | declare function bridge(appDNAHash: Hash, zomeName: string, functionName: string, arguments: string | object): any; 10 | declare function getBridges(): BridgeStatus[]; 11 | declare function sign(doc: string): string; 12 | declare function verifySignature(signature: string, data: string, pubKey: string): boolean; 13 | declare function commit(entryType: string, entryData: string | object): Hash; 14 | declare function get(hash: Hash, options?: object): GetResponse | any; 15 | declare function getLinks(base: Hash, tag: string, options?: object): GetLinksResponse[]; 16 | declare function update(entryType: string, entryData: string | object, replaces: Hash) : Hash; 17 | declare function updateAgent(options: object): Hash; 18 | declare function remove(entryHash: Hash, message: string): Hash; 19 | declare function query(options?: object): QueryResponse[] | any[]; 20 | declare function send(to: Hash, message: object, options?: object): any; 21 | declare function bundleStart(timeout: number, userParam: any): void; 22 | declare function bundleClose(commit: boolean): void; 23 | 24 | declare var HC: HolochainSystemGlobals; 25 | declare var App: HolochainAppGlobals; -------------------------------------------------------------------------------- /@types/holochain/holochain.d.ts: -------------------------------------------------------------------------------- 1 | // holochain type definitions 2 | 3 | 4 | type Hash = string; 5 | type Signature = string; 6 | type HolochainError = object; 7 | type PackageRequest = object; 8 | 9 | /*============================================ 10 | = Holochain Data Types = 11 | ============================================*/ 12 | 13 | interface Header { 14 | Type: string; 15 | Time: string; 16 | HeaderLink: Hash; 17 | EntryLink: Hash; 18 | TypeLink: Hash; 19 | Sig: Signature; 20 | Change: Hash; 21 | } 22 | 23 | interface GetResponse { 24 | Entry?: any; 25 | EntryType?: string; 26 | Sources?: Hash[]; 27 | } 28 | 29 | interface GetLinksResponse { 30 | Hash: Hash; 31 | Entry?: any; 32 | EntryType?: string; 33 | Tag?: string; 34 | Source?: Hash; 35 | } 36 | 37 | interface QueryResponse { 38 | Hash?: string 39 | Entry?: any 40 | Header?: Header 41 | } 42 | 43 | interface BridgeStatus { 44 | Side: number; 45 | CalleeName?: string; 46 | CalleeApp?: Hash; 47 | Token?: string; 48 | } 49 | 50 | 51 | /*===== End of Holochain Data Types ======*/ 52 | 53 | 54 | interface HolochainSystemGlobals { 55 | Version: string; 56 | HashNotFound: any; 57 | Status: any; 58 | GetMask: any; 59 | LinkAction: any; 60 | PkgReq: any; 61 | Bridge: any; 62 | SysEntryType: any; 63 | BundleCancel: any; 64 | } 65 | 66 | interface HolochainAppGlobals { 67 | Name: string; 68 | DNA: { 69 | Hash: Hash; 70 | }; 71 | Key: { 72 | Hash: Hash; 73 | } 74 | Agent: { 75 | Hash: Hash; 76 | TopHash: Hash; 77 | String: string; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { Orchestrator, tapeExecutor, Config, singleConductor, combine, callSync, localOnly } = require('@holochain/tryorama') 2 | 3 | const MIN_EXPECTED_SCENARIOS = 1 4 | 5 | process.on('unhandledRejection', error => { 6 | // Will print "unhandledRejection err is not defined" 7 | console.error('got unhandledRejection:', error); 8 | }); 9 | 10 | 11 | let middleware = combine( 12 | // by default, combine conductors into a single conductor for in-memory networking 13 | // NB: this middleware makes a really huge difference! and it's not very well tested, 14 | // as of Oct 1 2019. So, keep an eye out. 15 | tapeExecutor(require('tape')), 16 | localOnly, 17 | callSync 18 | ); 19 | 20 | 21 | const orchestrator = new Orchestrator({ 22 | middleware, 23 | waiter: { 24 | softTimeout: 5000, 25 | hardTimeout: 10000, 26 | } 27 | }) 28 | 29 | require('./single_agent_tests/categories_test')(orchestrator.registerScenario); 30 | require('./single_agent_tests/happs_test')(orchestrator.registerScenario); 31 | require('./single_agent_tests/whoami_test')(orchestrator.registerScenario); 32 | require('./multi_agent_tests/upvote_test')(orchestrator.registerScenario); 33 | 34 | 35 | // Check to see that we haven't accidentally disabled a bunch of scenarios 36 | const num = orchestrator.numRegistered() 37 | if (num < MIN_EXPECTED_SCENARIOS) { 38 | console.error(`Expected at least ${MIN_EXPECTED_SCENARIOS} scenarios, but only ${num} were registered!`) 39 | process.exit(1) 40 | } 41 | else { 42 | console.log(`Registered ${num} scenarios (at least ${MIN_EXPECTED_SCENARIOS} were expected)`) 43 | } 44 | 45 | orchestrator.run().then(stats => { 46 | console.log("All done.") 47 | }) 48 | -------------------------------------------------------------------------------- /zomes/happs/code/src/ratings/mod.rs: -------------------------------------------------------------------------------- 1 | /// This file holds everything that represents the "post" entry type. 2 | // use boolinator::*; 3 | use hdk::{ 4 | self, 5 | entry_definition::ValidatingEntryType, 6 | holochain_core_types::dna::entry_types::Sharing, 7 | holochain_json_api::{error::JsonError, json::JsonString}, 8 | }; 9 | 10 | pub mod handlers; 11 | 12 | /// We declare the structure of our entry type with this Rust struct. 13 | /// It will be checked automatically by the macro below, similar 14 | /// to how this happens with functions parameters and zome_functions!. 15 | /// 16 | /// So this is our normative schema definition: 17 | #[derive(Clone, Serialize, Deserialize, Debug, DefaultJson)] 18 | pub struct Ratings { 19 | pub rate: String, 20 | pub review: String, 21 | pub author: String, 22 | pub timestamp: String, 23 | } 24 | 25 | pub fn rating_definition() -> ValidatingEntryType { 26 | entry!( 27 | name: "ratings", 28 | description: "Ratings for a given hash", 29 | sharing: Sharing::Public, 30 | 31 | validation_package: || { 32 | hdk::ValidationPackageDefinition::Entry 33 | }, 34 | 35 | validation: |_validation_data: hdk::EntryValidationData| { 36 | Ok(()) 37 | }, 38 | 39 | links: [ 40 | from!( 41 | "app", 42 | link_type: "rating_tag", 43 | 44 | validation_package: || { 45 | hdk::ValidationPackageDefinition::Entry 46 | }, 47 | 48 | validation: |_validation_data: hdk::LinkValidationData| { 49 | Ok(()) 50 | } 51 | ) 52 | ] 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /zomes/happs/code/src/categories/handlers.rs: -------------------------------------------------------------------------------- 1 | // TODO: Get the categories of the a app hash/address 2 | use hdk::{ 3 | self, 4 | error::ZomeApiResult, 5 | holochain_core_types::entry::Entry, 6 | holochain_json_api::json::RawString, 7 | holochain_persistence_api::{cas::content::Address, cas::content::AddressableContent}, 8 | utils, 9 | }; 10 | 11 | use crate::happs::{get_linked_apps, AppResponse}; 12 | 13 | pub fn handle_add_app_to_category(app_address: Address, category: String) -> ZomeApiResult<()> { 14 | let category_anchor_entry = 15 | Entry::App("category_anchor".into(), RawString::from(category).into()); 16 | let category_anchor_addr = hdk::commit_entry(&category_anchor_entry)?; 17 | utils::link_entries_bidir( 18 | &app_address, 19 | &category_anchor_addr, 20 | "in", 21 | "contains", 22 | "", 23 | "", 24 | )?; 25 | Ok(()) 26 | } 27 | 28 | pub fn handle_add_app_to_tag(app_address: Address, tag: String) -> ZomeApiResult<()> { 29 | let tag_anchor_entry = Entry::App("tag_anchor".into(), RawString::from(tag).into()); 30 | let tag_anchor_addr = hdk::commit_entry(&tag_anchor_entry)?; 31 | utils::link_entries_bidir(&app_address, &tag_anchor_addr, "in", "contains", "", "")?; 32 | Ok(()) 33 | } 34 | 35 | pub fn handle_get_apps_by_category(category: String) -> ZomeApiResult> { 36 | let category_anchor_entry = 37 | Entry::App("category_anchor".into(), RawString::from(category).into()); 38 | get_linked_apps(&category_anchor_entry.address(), "contains") 39 | } 40 | 41 | pub fn handle_get_apps_by_tag(tag: String) -> ZomeApiResult> { 42 | let tag_anchor_entry = Entry::App("tag_anchor".into(), RawString::from(tag).into()); 43 | get_linked_apps(&tag_anchor_entry.address(), "contains") 44 | } 45 | -------------------------------------------------------------------------------- /ui-src/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | font-family: 'Poppins', sans-serif; 4 | /* font-family: 'Roboto Slab', serif; */ 5 | /* font-family: 'PT Sans', sans-serif; */ 6 | /* font-family: 'Kosugi Maru', sans-serif; */ 7 | } 8 | 9 | body { 10 | margin: 0 auto; 11 | line-height: 1.3em; 12 | height: 100%; 13 | background: radial-gradient(circle, #36383F, #202125); 14 | background-attachment: fixed; 15 | background-repeat: no-repeat; 16 | background-size: cover; 17 | width: 100%; /* Prevent jump from scrollbar appearing on only some pages */ 18 | } 19 | 20 | .app { 21 | padding: 15px; 22 | height: 90vh; 23 | } 24 | 25 | .brand-logo { 26 | margin-left: 20px; 27 | } 28 | 29 | .nav-item { 30 | margin-right: 10px; 31 | } 32 | 33 | .list-entry-item { 34 | cursor: pointer; 35 | font-weight: bold; 36 | margin: 20px 80px; 37 | } 38 | 39 | h6 { 40 | line-height: 2.1; 41 | } 42 | 43 | .jumbotron-title { 44 | color: white; 45 | text-align: center; 46 | padding: 20px 50px 50px 50px; 47 | margin: 50px; 48 | } 49 | 50 | .card-panel { 51 | margin-top: 30px; 52 | color: white !important; 53 | background: #0c5e5b ; 54 | /* background: radial-gradient(rgba(0, 0, 0, 1), hsla(185, 30%, 50%, 1.6), hsla(170, 50%, 10%, 0)); */ 55 | border: 2px solid #282a2f; 56 | min-height: 300px; 57 | } 58 | 59 | .list-group-item { 60 | text-align: left; 61 | background-color: #202125 !important; 62 | } 63 | 64 | .app-background { 65 | margin: 5px; 66 | background: #1e2126; 67 | } 68 | 69 | hr { 70 | height: .75px; 71 | width: 85%; 72 | background: #026269; 73 | } 74 | 75 | td { 76 | color: black !important; 77 | } 78 | 79 | .nav-btn { 80 | background: #367780; /* #075250 */ 81 | } 82 | 83 | .detail-view.hide { 84 | visibility: hidden; 85 | } 86 | 87 | .loading-text { 88 | color: white; 89 | font-size: 3em; 90 | position: relative; 91 | top: 30%; 92 | } 93 | -------------------------------------------------------------------------------- /test/single_agent_tests/categories_test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const { one } = require('../config') 3 | 4 | const App1 = { 5 | title: "HoloChat", 6 | description: "A better Chat", 7 | thumbnail_url: "/IMG.jpg", 8 | homepage_url: "home/page", 9 | dnas: [{ 10 | location: "/dna/url", 11 | hash: "QmHash", 12 | }], 13 | ui: { 14 | location: "ui/url", 15 | hash: "QmHash", 16 | } 17 | } 18 | 19 | const App2 = { 20 | title: "Clutter", 21 | description: "A better Twiter", 22 | thumbnail_url: "/IMG.jpg", 23 | homepage_url: "home/page", 24 | dnas: [{ 25 | location: "/dna/url", 26 | hash: "QmHash", 27 | }], 28 | ui: null, 29 | } 30 | 31 | module.exports = (scenario) => { 32 | 33 | scenario('add Category ', async (s, t) => { 34 | 35 | const { liza } = await s.players({liza: one('liza')}, true) 36 | 37 | const create_result = await liza.callSync( "app", "happs", "create_app", App1); 38 | console.log(create_result) 39 | const app_address = create_result.Ok 40 | t.equal(app_address.length, 46) 41 | 42 | const create_result2 = await liza.callSync( "app", "happs", "create_app", App2); 43 | console.log(create_result2) 44 | const app_address2 = create_result2.Ok 45 | t.equal(app_address2.length, 46) 46 | 47 | const result1 = await liza.callSync( "app", "happs", "add_app_to_category", {app_address: app_address, category: "good apps"}) 48 | console.log(result1) 49 | t.equal(result1.Ok, null) 50 | 51 | const result2 = await liza.callSync( "app", "happs", "add_app_to_category", {app_address: app_address2, category: "good apps"}) 52 | console.log(result2) 53 | t.equal(result2.Ok, null) 54 | 55 | const result3 = await liza.callSync( "app", "happs", "get_apps_by_category", {category:"good apps"}) 56 | console.log(result3) 57 | t.equal(result3.Ok.length, 2) 58 | 59 | await liza.kill() 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /ui-src/src/utils.ts: -------------------------------------------------------------------------------- 1 | /* Test CONSTS: */ 2 | // github: UI URL: https://github.com/Holo-Host/holofuel-gui/releases/download/0.1.0/holofuel-gui-v0.1.0.zip 3 | // github link >> DNAURL: https://github.com/Holo-Host/holofuel/releases/download/v0.8.2-alpha/holofuel.dna.json 4 | // QmSN2JqK2YTsPU1JR6xMDDghr7gKvV2ieQjfyH2uJUFFPQ 5 | 6 | 7 | // website: holofuel6example.org 8 | // domain: holofuel6example.holohost.net 9 | // uiUrl:`http://localhost:5555/holofuel/ui.zip` 10 | // uiHash: `QMhfFakeHash` 11 | // dnaUrl: `http://localhost:5555/holofuel/dist/holofuel.dna.json` 12 | // dnaHash: `QmUrd3q8fF71VM3eA1mSEhcEzRsAiE8XiLnMZXBW6omHdV` 13 | // hCLient agent_id = HcScIk8YYA4M39b7zsX9VCYzkAOett3i9v98krbi55JMf8kju9VSxaG3Myzw8bz 14 | 15 | // ====================================== 16 | // Type and Action Imports 17 | // ====================================== 18 | import { Hash } from '../../holochain' 19 | import store from './store' 20 | import axios from 'axios' 21 | 22 | import * as express from 'express' 23 | import * as multer from 'multer' 24 | import * as fs from 'fs' 25 | import * as path from 'path' 26 | 27 | // ======================== 28 | // UI Constants 29 | // ======================== 30 | export const ICON_SIZE = "100px"; 31 | export const URL = `ws://localhost:3400` 32 | 33 | // ======================== 34 | // API CALLS 35 | // ======================== 36 | export const fetchPOST = (url: string, data?: any, extraPayload?: any): Promise => { 37 | extraPayload = extraPayload || {} 38 | return fetch(url, { 39 | ...extraPayload, 40 | body: JSON.stringify(data), 41 | headers: { 42 | 'Content-Type': 'application/json' 43 | }, 44 | method: 'post', 45 | }).then(r => r.json()) 46 | } 47 | 48 | export const fetchFormPOST = (url: string, data?: any, extraPayload?: any): Promise => { 49 | extraPayload = extraPayload || {} 50 | return fetch(url, { 51 | ...extraPayload, 52 | body: JSON.stringify(data), 53 | headers: { 54 | 'Content-Type': 'multipart/form-data' 55 | }, 56 | method: 'post', 57 | }).then(r => r.json()) 58 | } 59 | -------------------------------------------------------------------------------- /test/multi_agent_tests/upvote_test.js: -------------------------------------------------------------------------------- 1 | const { one } = require('../config') 2 | const App1 = { 3 | title: "HoloChat", 4 | description: "A better Chat", 5 | thumbnail_url: "/IMG.jpg", 6 | homepage_url: "home/page", 7 | dnas: [{ 8 | location: "/dna/url", 9 | hash: "QmHash", 10 | handle: "holochat", 11 | }], 12 | ui: { 13 | location: "ui/url", 14 | hash: "QmHash", 15 | } 16 | } 17 | 18 | module.exports = (scenario) => { 19 | 20 | scenario('Create an app, add dna+ui bundles and retrieve', async (s, t) => { 21 | 22 | const { liza, jack } = await s.players({liza: one('liza'),jack: one('jack')}, true) 23 | 24 | // liza creates an app 25 | const create_result = await liza.callSync( "app", "happs", "create_app", App1); 26 | console.log("**DEBUG***: liza; create_result: " + JSON.stringify(create_result)) 27 | const app_address = create_result.Ok 28 | t.equal(app_address.length, 46) 29 | 30 | //jack upvotes it 31 | const upvote_result = await jack.callSync( "app", 'happs', 'upvote_app', {app_address: app_address}) 32 | console.log("**DEBUG***: jack: upvote result: " + JSON.stringify( upvote_result )) 33 | t.notEqual(upvote_result.Ok, undefined) 34 | 35 | // jack can see it 36 | const jack_get_all_apps_result_after_upvote = await jack.call( "app", 'happs', "get_all_apps", {}) 37 | console.log("**DEBUG***: jack; get all after upvote: " + JSON.stringify( jack_get_all_apps_result_after_upvote )) 38 | t.equal(jack_get_all_apps_result_after_upvote.Ok[0].upvotes , 1) 39 | t.equal(jack_get_all_apps_result_after_upvote.Ok[0].upvotedByMe , true) 40 | 41 | // liza can see it 42 | const liza_get_all_apps_result_after_upvote = await liza.call( "app", 'happs', "get_all_apps", {}) 43 | console.log("**DEBUG***: liza; get all after upvote" + JSON.stringify( liza_get_all_apps_result_after_upvote )) 44 | t.equal(liza_get_all_apps_result_after_upvote.Ok[0].upvotes , 1) 45 | t.equal(liza_get_all_apps_result_after_upvote.Ok[0].upvotedByMe , false) 46 | 47 | await liza.kill() 48 | await jack.kill() 49 | }) 50 | 51 | } 52 | -------------------------------------------------------------------------------- /test/single_agent_tests/happs_test.js: -------------------------------------------------------------------------------- 1 | const { one } = require('../config') 2 | 3 | const App1 = { 4 | title: "HoloChat", 5 | description: "A better Chat", 6 | thumbnail_url: "/IMG.jpg", 7 | homepage_url: "home/page", 8 | dnas: [{ 9 | location: "/dna/url", 10 | hash: "QmHash", 11 | handle: "holochat", 12 | }], 13 | ui: { 14 | location: "ui/url", 15 | hash: "QmHash", 16 | } 17 | } 18 | 19 | const App2 = { 20 | title: "Clutter", 21 | description: "A better Twiter", 22 | thumbnail_url: "/IMG.jpg", 23 | homepage_url: "home/page", 24 | dnas: [{ 25 | location: "/dna/url", 26 | hash: "QmHash", 27 | handle: "clutter", 28 | }], 29 | ui: null, 30 | } 31 | 32 | module.exports = (scenario) => { 33 | 34 | scenario('Create an app, add dna+ui bundles and retrieve', async (s, t) => { 35 | const { liza } = await s.players({liza: one('liza')}, true) 36 | 37 | const create_result = await liza.callSync( "app", "happs", "create_app", App1); 38 | console.log(create_result) 39 | const app_address = create_result.Ok 40 | t.equal(app_address.length, 46) 41 | 42 | const get_app_result = await liza.call( "app", 'happs', "get_app", {app_hash:app_address}) 43 | console.log(get_app_result) 44 | const app_details = get_app_result.Ok 45 | t.equal(app_details.uuid, App1.uuid) 46 | 47 | const get_all_apps_result = await liza.call( "app", 'happs', "get_all_apps", {}) 48 | console.log(get_all_apps_result) 49 | const all_app_details = get_all_apps_result.Ok 50 | t.equal(all_app_details.length , 1) 51 | 52 | const upvote_result = await liza.callSync( "app", 'happs', 'upvote_app', {app_address: app_address}) 53 | console.log(upvote_result) 54 | t.notEqual(upvote_result.Ok, undefined) 55 | 56 | const get_all_apps_result_after_upvote = await liza.call( "app", 'happs', "get_all_apps", {}) 57 | console.log(get_all_apps_result_after_upvote) 58 | t.equal(get_all_apps_result_after_upvote.Ok[0].upvotes , 1) 59 | t.equal(get_all_apps_result_after_upvote.Ok[0].upvotedByMe , true) 60 | 61 | await liza.kill() 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /ui-src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 27 | HApps Store 28 | 29 | 30 | 33 |
34 | 35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ui-src/src/reducer.ts: -------------------------------------------------------------------------------- 1 | import { Hash } from '../../holochain'; 2 | import { Map } from "immutable" 3 | import { App } from "./types/app"; 4 | 5 | import { ActionType, getType } from 'typesafe-actions' 6 | import * as appActions from './actions' 7 | export type AppAction = ActionType 8 | 9 | 10 | 11 | interface State { 12 | readonly apps: Array 13 | readonly currentAgent?: {name: string, hash: string}, 14 | readonly currentApp?: App, 15 | readonly connected: boolean, 16 | awaitingResponse: boolean, 17 | }; 18 | 19 | const defaultState: State = { 20 | apps: [], 21 | currentAgent: undefined, 22 | currentApp: undefined, 23 | connected: false, 24 | awaitingResponse: false, 25 | } 26 | 27 | export default (state: State = defaultState, action: AppAction): State => { 28 | switch (action.type) { 29 | case getType(appActions.GetAllApps.success): 30 | console.log("get all hApps : ", action.payload); 31 | const apps = action.payload 32 | return {...state, apps} 33 | case getType(appActions.Whoami.success): 34 | console.log("agent nickname : ", JSON.parse(action.payload.name).nick); 35 | const newAgent = { 36 | hash: action.payload.hash, 37 | name: JSON.parse(action.payload.name).nick 38 | } 39 | return {...state, currentAgent: newAgent} 40 | case getType(appActions.GetApp.success): 41 | return {...state, currentApp: action.payload} 42 | case 'HOLOCHAIN_WEBSOCKET_CONNECTED': 43 | return { ...state, connected: true} 44 | case getType(appActions.CreateApp.request): 45 | return { ...state, awaitingResponse: true} 46 | case getType(appActions.CreateApp.success): 47 | console.log("create app success >> CREATE APP payload : ", action.payload); 48 | return { ...state, awaitingResponse: false} 49 | case getType(appActions.UpvoteApp.success): 50 | // do a local state refresh of the upvote status for those reactive UI feels 51 | const updatedApps: Array = state.apps.map((app) => { 52 | if (app.address === action.payload) { 53 | return {...app, upvotes: app.upvotedByMe ? app.upvotes : app.upvotes+1, upvotedByMe: true} 54 | } else { 55 | return app 56 | } 57 | }) 58 | return { ...state, apps: updatedApps} 59 | default: 60 | return state 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Use base dir for node_modules and stuff, but 8 | // point to the app subdir for actual code 9 | const appSubdirectory = 'ui-src' 10 | 11 | // Make sure any symlinks in the project folder are resolved: 12 | // https://github.com/facebookincubator/create-react-app/issues/637 13 | const baseDirectory = fs.realpathSync(process.cwd()); 14 | const appDirectory = path.join(baseDirectory, appSubdirectory); 15 | const resolveBase = relativePath => path.resolve(baseDirectory, relativePath); 16 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 17 | 18 | const envPublicUrl = process.env.PUBLIC_URL; 19 | 20 | function ensureSlash(path, needsSlash) { 21 | const hasSlash = path.endsWith('/'); 22 | if (hasSlash && !needsSlash) { 23 | return path.substr(path, path.length - 1); 24 | } else if (!hasSlash && needsSlash) { 25 | return `${path}/`; 26 | } else { 27 | return path; 28 | } 29 | } 30 | 31 | const getPublicUrl = appPackageJson => 32 | envPublicUrl || require(appPackageJson).homepage; 33 | 34 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 35 | // "public path" at which the app is served. 36 | // Webpack needs to know it to put the right