/app/utils$1'
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webIdManager",
3 | "version": "1.0.0",
4 | "productName": "webIdManager",
5 | "identifier": "net.maidsafe.web-id-manager",
6 | "vendor": "MaidSafe.net Ltd",
7 | "author": {
8 | "name": "Maidsafe.net Ltd",
9 | "email": "qa@maidsafe.net",
10 | "url": "https://github.com/maidsafe"
11 | },
12 | "description": "Manage RDF data on the SAFE Network",
13 | "main": "src/index.js",
14 | "license": "MIT",
15 | "scripts": {
16 | "test": "cross-env NODE_ENV=test jest --notify",
17 | "build": "rimraf dist && NODE_ENV=production yarn parcel build public/index.html",
18 | "start": "cross-env NODE_ENV=development parcel public/index.html",
19 | "lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .",
20 | "lint-fix": "yarn run lint -- --fix",
21 | "prepush": "yarn test"
22 | },
23 | "dependencies": {
24 | "@babel/plugin-proposal-class-properties": "^7.0.0",
25 | "add": "2.0.6",
26 | "antd": "3.10.9",
27 | "cross-env": "5.2.0",
28 | "enzyme": "3.7.0",
29 | "enzyme-adapter-react-16": "1.7.0",
30 | "prop-types": "15.6.2",
31 | "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0-0",
32 | "react-dom": "16.6.3",
33 | "react-redux": "5.1.1",
34 | "react-router-dom": "4.3.1",
35 | "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0",
36 | "redux-actions": "2.6.4",
37 | "redux-promise": "0.6.0",
38 | "redux-thunk": "2.3.0",
39 | "schema.org": "3.1.1"
40 | },
41 | "devDependencies": {
42 | "@babel/core": "^7.1.6",
43 | "@babel/preset-env": "^7.0.0",
44 | "@babel/preset-react": "^7.0.0",
45 | "babel-eslint": "10.0.1",
46 | "babel-plugin-import": "1.11.0",
47 | "eslint": "5.9.0",
48 | "eslint-config-airbnb": "17.1.0",
49 | "eslint-formatter-pretty": "2.0.0",
50 | "eslint-plugin-compat": "2.6.3",
51 | "eslint-plugin-flowtype": "3.2.0",
52 | "eslint-plugin-flowtype-errors": "3.6.0",
53 | "eslint-plugin-import": "2.14.0",
54 | "eslint-plugin-jest": "22.1.0",
55 | "eslint-plugin-jsx-a11y": "6.1.2",
56 | "eslint-plugin-promise": "4.0.1",
57 | "eslint-plugin-react": "7.11.1",
58 | "flow": "0.2.3",
59 | "flow-bin": "0.87.0",
60 | "jest": "23.6.0",
61 | "jest-tobetype": "1.2.0",
62 | "less": "3.9",
63 | "less-vars-to-js": "1.3.0",
64 | "parcel-bundler": "1.10.3",
65 | "postcss-modules": "1.4.1",
66 | "raf": "3.4.1",
67 | "rimraf": "2.6.2"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SAFE WebID Manager
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/actions/safe_actions.js:
--------------------------------------------------------------------------------
1 | import { createActions } from 'redux-actions';
2 | import { APP_INFO } from '../constants';
3 | import {message} from 'antd';
4 |
5 | export const TYPES = {
6 | SAFE_AUTHORISE : 'SAFE_AUTHORISE'
7 | };
8 |
9 |
10 | export const {
11 | safeAuthorise
12 | // , removeBookmark
13 |
14 | } = createActions( {
15 | [TYPES.SAFE_AUTHORISE] : async () =>
16 | {
17 | if ( window.name ) return; // jest short circuit
18 |
19 | if ( !safe )
20 | {
21 | throw new Error( 'SAFE APIs are missing' );
22 | }
23 |
24 | try
25 | {
26 | const app = await safe.initialiseApp( APP_INFO.info );
27 | const authUri = await app.auth.genAuthUri( APP_INFO.permissions, APP_INFO.opts );
28 | const response = await safe.authorise( authUri );
29 | const connectedApp = await app.auth.loginFromUri( response );
30 |
31 | return { idApp: connectedApp };
32 | }
33 | catch ( e )
34 | {
35 | message.error('Error authenticating on the network.')
36 | console.log( 'Error in auth attempt', e );
37 | }
38 |
39 | message.success('Authenticated successfully.')
40 | }
41 | } );
42 |
--------------------------------------------------------------------------------
/src/actions/webIds_actions.js:
--------------------------------------------------------------------------------
1 | import { createActions } from 'redux-actions';
2 | import { message } from 'antd';
3 | import { SAFE_CONSTANTS } from '../constants';
4 |
5 | export const TYPES = {
6 | ADD_WEB_ID : 'ADD_WEB_ID',
7 | UPDATE_WEB_ID : 'UPDATE_WEB_ID',
8 | GET_AVAILABLE_WEB_IDS : 'GET_AVAILABLE_WEB_IDS'
9 | };
10 |
11 | const TYPE_TAG = 16048;
12 |
13 | const sanitizeWebId = ( webId ) =>
14 | {
15 | const newWebId = {};
16 |
17 | // sanitize for webid rdf for now.
18 | Object.keys( webId ).forEach( key =>
19 | {
20 | if ( webId[key] && typeof webId[key] !== 'undefined' )
21 | {
22 | newWebId[key] = webId[key];
23 | if ( typeof newWebId[key] === 'string' )
24 | {
25 | newWebId[key] = webId[key].trim();
26 | }
27 |
28 | if ( key === 'uri' || key === 'website' )
29 | {
30 | newWebId[key] = `safe://${newWebId[key]}`;
31 | }
32 | }
33 | } );
34 | console.log( 'post sanitizing', newWebId );
35 |
36 | return newWebId;
37 | };
38 |
39 |
40 | export const {
41 | addWebId,
42 | updateWebId,
43 | getAvailableWebIds
44 | } = createActions( {
45 |
46 | [TYPES.ADD_WEB_ID] : async ( payload ) =>
47 | {
48 | const { idApp, history, webId } = payload;
49 |
50 | if ( !idApp )
51 | {
52 | message.error( 'Not authorised.' );
53 | console.log( 'Not authorise' );
54 | throw new Error( 'No idApp provided to action' );
55 | }
56 |
57 | const newWebId = sanitizeWebId( webId );
58 |
59 | if ( window.name ) return newWebId; // jest short circuit
60 |
61 | try
62 | {
63 | if (newWebId.image && newWebId.imageMimeType) {
64 | // let's store the image first
65 | const imdWriter = await idApp.immutableData.create();
66 | await imdWriter.write(newWebId.image);
67 | const cipherOpt = await idApp.cipherOpt.newPlainText();
68 | const { xorUrl } = await imdWriter.close(cipherOpt, true, newWebId.imageMimeType);
69 | newWebId.image = xorUrl;
70 | }
71 |
72 | const md = await idApp.mutableData.newRandomPublic( TYPE_TAG );
73 | await md.quickSetup( {} );
74 | const webIdRDF = await md.emulateAs( 'WebID' );
75 | await webIdRDF.create( newWebId, newWebId.nick );
76 | }
77 | catch ( e )
78 | {
79 | if ( e && e.message === 'No ID has been found in the RDF graph.' )
80 | {
81 | message.error( 'This publicName already exists (created by another app). You can\'t make a webId here, sorry! ' );
82 | return {};
83 | }
84 |
85 | console.error( 'Error in addWebId', e );
86 | message.error( 'Error creating webId on the network' );
87 | return {};
88 | }
89 | message.success( 'WebId created succesfully' );
90 |
91 | console.log( 'WebId created on the network.' );
92 | history.push( '/' ); // back to main page
93 |
94 | const webIdForApp = {
95 | ...newWebId,
96 | uri : newWebId.uri.replace( 'safe://', '' ),
97 | website : newWebId.website ? newWebId.website.replace( 'safe://', '' ) : ''
98 | };
99 |
100 | return webIdForApp;
101 | },
102 | [TYPES.UPDATE_WEB_ID] : async ( payload ) =>
103 | {
104 | const { idApp, webId, history } = payload;
105 |
106 | if ( !idApp ) throw new Error( 'No idApp provided to update action' );
107 |
108 | const newWebId = sanitizeWebId( webId );
109 |
110 | if ( window.name ) return newWebId; // jest short circuit
111 |
112 | try
113 | {
114 | if (newWebId.image && newWebId.imageMimeType) {
115 | // let's store the image first
116 | const imdWriter = await idApp.immutableData.create();
117 | await imdWriter.write(newWebId.image);
118 | const cipherOpt = await idApp.cipherOpt.newPlainText();
119 | const { xorUrl } = await imdWriter.close(cipherOpt, true, newWebId.imageMimeType);
120 | newWebId.image = xorUrl;
121 | }
122 |
123 | const mdUri = newWebId.uri;
124 |
125 | const { content, resourceType } = await idApp.fetch( mdUri );
126 |
127 | let pulledWebId;
128 | if ( resourceType === 'RDF' )
129 | {
130 | pulledWebId = await content.emulateAs( 'WebID' );
131 | await pulledWebId.fetchContent();
132 | await pulledWebId.update( newWebId );
133 | }
134 | }
135 | catch ( e )
136 | {
137 | console.error( 'Error in updateWebId', e );
138 | message.error( 'Error updating webID on the network' );
139 | return e;
140 | }
141 |
142 | console.log( 'WebId updated on the network.', history );
143 | message.success( 'WebId updated successfully' );
144 |
145 | // why is this undefined? poush to newnickname....
146 | history.push( '/' ); // back to main page
147 | return newWebId;
148 | },
149 | [TYPES.GET_AVAILABLE_WEB_IDS] : async ( payload ) =>
150 | {
151 | console.log( 'Getting available ids' );
152 | const { idApp } = payload;
153 |
154 | if ( window.name ) return []; // jest short circuit
155 |
156 | if ( ! safeExperimentsEnabled )
157 | {
158 | message.error('The experimental APIs are disabled, please enable them from the SAFE Browser');
159 | }
160 |
161 | const webIds = await idApp.web.getWebIds();
162 |
163 | const actualIds = await Promise.all(webIds.map( async webId =>
164 | {
165 | const me = webId['#me'];
166 |
167 | // remove what is appended later
168 | me.uri = webId['@id'].replace( 'safe://', '' );
169 | if ( me.website )
170 | {
171 | const website = me.website['@id'] ? me.website['@id'] : me.website;
172 | me.website = website.replace('safe://', '');
173 | }
174 |
175 | if ( me.image && me.image['@id'] )
176 | {
177 | me.image = me.image['@id'];
178 | }
179 |
180 | if ( me.inbox && me.inbox['@id'] )
181 | {
182 | me.inbox = me.inbox['@id'];
183 | }
184 |
185 | return me;
186 | } ));
187 | return actualIds;
188 | }
189 | } );
190 |
--------------------------------------------------------------------------------
/src/components/Avatar/Avatar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Upload, message } from 'antd';
3 |
4 | function getBase64( img, callback )
5 | {
6 | const reader = new FileReader();
7 | reader.addEventListener( 'load', () => callback( reader.result ) );
8 | reader.readAsDataURL( img );
9 | }
10 |
11 | function getBinary( img, callback )
12 | {
13 | const reader = new FileReader();
14 | reader.addEventListener( 'load', () => callback( reader.result ) );
15 | reader.readAsArrayBuffer( img );
16 | }
17 |
18 | function beforeUpload( file )
19 | {
20 | const isJPG = file.type === 'image/jpeg';
21 | if ( !isJPG )
22 | {
23 | message.error( 'You can only upload JPG file!' );
24 | }
25 | const isLt2M = file.size / 1024 / 1024 < 2;
26 | if ( !isLt2M )
27 | {
28 | message.error( 'Image must smaller than 2MB!' );
29 | }
30 |
31 | // we dont want to attempt to trigger an upload.
32 | // there is no server! so should return false if not for updating FORM issue (doesnt happen if no upload,
33 | // so looking at a potential dummy server in browser)
34 | return isJPG && isLt2M;
35 | }
36 |
37 | class Avatar extends React.Component
38 | {
39 | state = {
40 | loading : false,
41 | };
42 |
43 | handleChange = ( info, fileList, event ) =>
44 | {
45 | console.log( 'handling file change, ', info );
46 | // if ( info.file.status === 'uploading' )
47 | // {
48 | // this.setState( { loading: true } );
49 | // return;
50 | // }
51 | // if ( info.file.status === 'done' )
52 | // {
53 | // Get this url from response in real world.
54 |
55 | // var event = new Event('input', { bubbles: true });
56 | const uploader = this.uploader;
57 |
58 | getBase64( info.file.originFileObj, imageUrl =>
59 | {
60 | this.setState( { imageUrl } );
61 | } );
62 |
63 | getBinary( info.file.originFileObj, imageBinary =>
64 | {
65 | this.setState( { imageBinary, imageMimeType: info.file.type } );
66 | } );
67 | }
68 | render()
69 | {
70 | const { value } = this.props;
71 | const uploadButton = (
72 |
73 | { this.state.loading ? 'loading' : '' }
74 |
Upload
75 |
76 | );
77 |
78 | const imageUrl = this.state.imageUrl || value;
79 | return (
80 |
82 | {
83 | this.uploader = c;
84 | } }
85 | name="avatar"
86 | listType="picture-card"
87 | className="avatar-uploader"
88 | showUploadList={ false }
89 | ref={(c)=> {this.uploader = c}}
90 | beforeUpload={ beforeUpload }
91 | onChange={ this.handleChange }
92 | >
93 | {imageUrl ?
: uploadButton}
94 |
95 | );
96 | }
97 | }
98 |
99 | export default Avatar;
100 |
--------------------------------------------------------------------------------
/src/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | export default from './Avatar.jsx';
2 |
--------------------------------------------------------------------------------
/src/components/Editor/Editor.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Switch, Route } from 'react-router-dom';
4 |
5 | import IdForm from '../IdForm/IdForm';
6 |
7 |
8 | export default class Editor extends React.Component
9 | {
10 | static propTypes =
11 | {
12 | webIds : PropTypes.arrayOf( PropTypes.object ),
13 | idApp : PropTypes.shape(),
14 | match : PropTypes.shape( { url: PropTypes.string } ).isRequired,
15 | updateWebId : PropTypes.func.isRequired
16 | }
17 |
18 | static defaultProps =
19 | {
20 | webIds : []
21 | }
22 |
23 | render()
24 | {
25 | const { match, idApp, updateWebId, webIds, history } = this.props;
26 | const params = match.params || {}; //or for testing.
27 | const idToMatch = params.id || '';
28 |
29 | const webId = webIds.find( id => id.uri === idToMatch );
30 | if( !webId ) return No matching WebId found
;
31 |
32 | return (
33 |
34 |
{ `Editing`}
35 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Editor/index.js:
--------------------------------------------------------------------------------
1 | export default from './Editor.jsx';
2 |
--------------------------------------------------------------------------------
/src/components/Header/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 | import { Menu } from 'antd';
4 | import {PATHS} from '../../constants';
5 |
6 | const Header = ( props ) =>
7 | {
8 | const selectedKeys = [];
9 | const location = props.location.pathname;
10 |
11 | // set menu as active on load if on a specific path
12 | Object.keys( PATHS ).forEach( path =>
13 | {
14 | location.startsWith( PATHS[path] ) ? selectedKeys.push( PATHS[path] ) : '';
15 | } );
16 |
17 | return (
18 |
19 |
WebID Profile Manager
20 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default Header;
34 |
--------------------------------------------------------------------------------
/src/components/Header/index.js:
--------------------------------------------------------------------------------
1 | export default from './Header.jsx';
2 |
--------------------------------------------------------------------------------
/src/components/IdForm/IdForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Redirect } from 'react-router-dom';
4 | import { Form, Input, Button, Icon } from 'antd';
5 | import Avatar from '../Avatar/Avatar';
6 |
7 | const FormItem = Form.Item;
8 |
9 | const uriRegex = '^([-a-z0-9]{3,63}\\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\\.[a-z0-9][-a-z0-9]{0,61}[a-z0-9]{2,6}(/[-\\w@\\+\\.~#\\?&/=%]*)?$';
10 |
11 |
12 | const defaultId = {
13 | name : '',
14 | nick : '',
15 | uri : '',
16 | website : '',
17 | image : ''
18 | };
19 |
20 | /**
21 | * Helper function for ant design forms to populate
22 | * the form with passed values.
23 | * @param {Object} id webId object
24 | */
25 | const mapPropsToFields = ( { webId } ) =>
26 | {
27 | // if( !id ) return;
28 | let idToUse = webId || defaultId;
29 |
30 | idToUse = idToUse['#me'] || idToUse;
31 |
32 | return {
33 | nick : Form.createFormField( {
34 | ...idToUse,
35 | value : idToUse.nick || '',
36 | } ),
37 | name : Form.createFormField( {
38 | ...idToUse,
39 | value : idToUse.name || '',
40 | } ),
41 | uri : Form.createFormField( {
42 | ...idToUse,
43 | value : idToUse.uri || '',
44 | } ),
45 | website : Form.createFormField( {
46 | ...idToUse,
47 | value : idToUse.website || '',
48 | } ),
49 | image : Form.createFormField( {
50 | ...idToUse,
51 | value : idToUse.image || '',
52 | } ),
53 | // pk : Form.createFormField( {
54 | // ...id,
55 | // value : id.pk || '',
56 | // } ),
57 | };
58 | };
59 |
60 | /**
61 | * Form for WebId creation/editing. Uses and design Form components (and form.create() method)
62 | * to create a form that can be easily validated/populated.
63 | * @extends React
64 | */
65 | class IdForm extends React.Component
66 | {
67 | static propTypes = {
68 | webId : PropTypes.shape( {
69 | nick : PropTypes.string,
70 | name : PropTypes.string,
71 | website : PropTypes.string,
72 | uri : PropTypes.string,
73 | pk : PropTypes.string
74 | } ),
75 | webIds : PropTypes.arrayOf( PropTypes.shape ).isRequired,
76 | submit : PropTypes.func.isRequired,
77 | getAvailableWebIds : PropTypes.func.isRequired,
78 | // idApp : PropTypes.shape.isRequired
79 | }
80 | static defaultProps = {
81 | webId : defaultId
82 | }
83 |
84 | isEditing = () => {
85 | return !!this.props.webId.uri;
86 | }
87 |
88 | componentWillReceiveProps = ( newProps ) =>
89 | {
90 | const { idApp } = this.props;
91 |
92 | // didnt have app, but now we doooo....
93 | if ( !idApp && newProps.idApp && newProps.webIds.length === 0 )
94 | {
95 | this.getIds( newProps.idApp );
96 | }
97 | }
98 |
99 | // TODO: Dry this out across components
100 | getIds = ( passedIdApp ) =>
101 | {
102 | const { getAvailableWebIds, idApp } = this.props;
103 |
104 | const appToUse = passedIdApp || idApp;
105 | if ( appToUse )
106 | {
107 | getAvailableWebIds( { idApp: appToUse } );
108 | }
109 | }
110 |
111 | handleSubmit = ( e ) =>
112 | {
113 | e.preventDefault();
114 |
115 | const {
116 | match, submit, idApp, history, webId
117 | } = this.props;
118 |
119 | const imageBinary = this.theAvatar.state.imageBinary;
120 | const imageMimeType = this.theAvatar.state.imageMimeType;
121 |
122 | this.props.form.validateFields( ( err, values ) =>
123 | {
124 | if ( !err )
125 | {
126 | let image = imageBinary;
127 | if (!image && webId) {
128 | image = webId.image;
129 | }
130 |
131 | const webIdWithImageAndUpdates = { ...webId, ...values, image, imageMimeType };
132 | console.log( 'ON CLICK:: Updating with', webIdWithImageAndUpdates, history );
133 |
134 | // history to move us on a page when successful
135 | submit( { idApp, webId: webIdWithImageAndUpdates, history } );
136 | }
137 | } );
138 | }
139 |
140 | validateUniqueId = async ( rule, value, callback ) =>
141 | {
142 | const { webIds, webId } = this.props;
143 |
144 | // if we're editing do nothing...
145 | if ( webId.uri ) return callback();
146 |
147 | const foundId = webIds.find( aWebId =>
148 | aWebId['@id'] === `safe://${value}#me` );
149 |
150 | if ( foundId ) return callback( 'Error.' );
151 |
152 | return callback();
153 | }
154 |
155 | render = ( ) =>
156 | {
157 | const { getFieldDecorator } = this.props.form;
158 |
159 | return (
160 |
222 |
223 | */}
224 |
229 |
230 | );
231 | }
232 | }
233 |
234 | const WrappedIdForm = Form.create( { mapPropsToFields } )( IdForm );
235 |
236 | export default WrappedIdForm;
237 |
--------------------------------------------------------------------------------
/src/components/IdForm/index.js:
--------------------------------------------------------------------------------
1 | export default from './IdForm.jsx';
2 |
--------------------------------------------------------------------------------
/src/components/List/List.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import { Menu, List, Button, Avatar } from 'antd';
4 | import { PATHS } from '../../constants';
5 |
6 | class IdList extends React.Component
7 | {
8 | static defaultProps = {
9 | webIds : []
10 | }
11 |
12 | componentDidMount = () =>
13 | {
14 | this.getIds();
15 | }
16 |
17 | componentWillReceiveProps = ( newProps ) =>
18 | {
19 | const { idApp } = this.props;
20 |
21 | // didnt have app, but now we doooo....
22 | if ( !idApp && newProps.idApp )
23 | {
24 | this.getIds( newProps.idApp );
25 | }
26 | }
27 |
28 |
29 | getIds = ( passedIdApp ) =>
30 | {
31 | const { getAvailableWebIds, idApp } = this.props;
32 |
33 | const appToUse = passedIdApp || idApp;
34 |
35 | if ( appToUse )
36 | {
37 | getAvailableWebIds( { idApp: appToUse } );
38 | }
39 | }
40 |
41 |
42 | handleGetIds = ( newProps ) =>
43 | {
44 | this.getIds();
45 | }
46 |
47 | render = () =>
48 | {
49 | const { idApp, webIds, history } = this.props;
50 |
51 | // TODO: Make real loading state.
52 | const isLoading = !idApp;
53 |
54 | const createNewWebId = () =>
55 | {
56 | history.push( PATHS.CREATE )
57 | }
58 |
59 | const IdList = webIds.map( ( webId, i ) =>
60 | {
61 | const nickname = webId.nick.length ? webId.nick : '';
62 | const { uri, image } = webId;
63 | const safeUri = `safe://${uri}`;
64 | return (
65 | edit] }
68 | >
69 |
72 | :
73 |
74 | {nickname ? nickname.substring(0, 1).toUpperCase() : ''}
75 |
76 | }
77 | title={ nickname }
78 | description={
79 | {safeUri}
80 | }
81 | />
82 |
83 | );
84 | } );
85 |
86 | return (
87 |
88 |
Your Current WebIds:
89 |
96 |
99 | {IdList}
100 |
101 |
102 | );
103 | }
104 | }
105 |
106 | export default IdList;
107 |
--------------------------------------------------------------------------------
/src/components/List/index.js:
--------------------------------------------------------------------------------
1 | export default from './List.jsx';
2 |
--------------------------------------------------------------------------------
/src/constants/appInfo.js:
--------------------------------------------------------------------------------
1 | import pkg from '../../package.json';
2 |
3 | export const APP_INFO = {
4 | info : {
5 | id : pkg.identifier,
6 | scope : null,
7 | name : pkg.productName,
8 | vendor : pkg.author.name
9 | },
10 | opts : {
11 | own_container : true,
12 | },
13 | permissions : {
14 | _public : ['Read', 'Insert', 'Update', 'Delete'],
15 | _publicNames : ['Read', 'Insert', 'Update', 'Delete']
16 | },
17 | };
18 |
19 | // export default APP_INFO;
20 |
--------------------------------------------------------------------------------
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | export { PATHS } from './paths';
2 | export { APP_INFO } from './appInfo';
3 | export { SAFE_CONSTANTS } from './safeConstants';
4 |
--------------------------------------------------------------------------------
/src/constants/paths.js:
--------------------------------------------------------------------------------
1 | export const PATHS = {
2 | CREATE : '/create/new',
3 | EDIT : '/edit',
4 | LIST : '/list',
5 | };
6 |
--------------------------------------------------------------------------------
/src/constants/safeConstants.js:
--------------------------------------------------------------------------------
1 |
2 | export const SAFE_CONSTANTS = {
3 | ACCESS_CONTAINERS : {
4 | // PUBLIC: '_public',
5 | PUBLIC_NAMES : '_publicNames',
6 | },
7 | };
8 |
9 | // export default APP_INFO;
10 |
--------------------------------------------------------------------------------
/src/containers/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withRouter, Route, Switch, Redirect } from 'react-router-dom';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import * as webIdsActions from '../actions/webIds_actions';
6 | import * as safeActions from '../actions/safe_actions';
7 |
8 | import styles from './global.css';
9 | import Header from '../components/Header/Header';
10 | import Editor from '../components/Editor/Editor';
11 | import List from '../components/List/List';
12 |
13 | import IdForm from '../components/IdForm/IdForm';
14 |
15 | import { Layout, Row, Col } from 'antd';
16 |
17 | const { Content } = Layout;
18 |
19 | class App extends React.Component
20 | {
21 | componentDidMount = () =>
22 | {
23 | const { safeAuthorise, safe } = this.props;
24 | const { idApp } = safe;
25 |
26 | if ( !idApp )
27 | {
28 | safeAuthorise();
29 | }
30 | }
31 |
32 |
33 | render = () =>
34 | {
35 | const {
36 | webIds
37 | , history
38 | , addWebId
39 | , getAvailableWebIds
40 | , updateWebId
41 | , safe
42 | } = this.props;
43 |
44 | return (
45 |
51 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
67 |
}
68 | />
69 | ( )
78 | }
79 | />
80 | ( ) }
89 | />
90 | } />
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | );
100 | }
101 | }
102 |
103 |
104 | function mapDispatchToProps( dispatch )
105 | {
106 | const actions =
107 | {
108 | ...safeActions,
109 | ...webIdsActions
110 | };
111 | return bindActionCreators( actions, dispatch );
112 | }
113 |
114 | function mapStateToProps( state )
115 | {
116 | return {
117 | webIds : state.webIds,
118 | safe : state.safe
119 | };
120 | }
121 | export default withRouter( connect( mapStateToProps, mapDispatchToProps )( App ) );
122 |
--------------------------------------------------------------------------------
/src/containers/global.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .appContainer {
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
5 | "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
6 | SimSun, sans-serif;
7 | }
8 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { HashRouter } from 'react-router-dom';
4 | import { createStore, applyMiddleware, compose } from 'redux';
5 | import thunk from 'redux-thunk';
6 | import promiseMiddleware from 'redux-promise';
7 | import "@babel/polyfill";
8 |
9 | import App from './containers/App';
10 | import rootReducer from './reducers';
11 |
12 | if ( window.webIdEventEmitter )
13 | {
14 | console.log( 'webId emitter exists!' );
15 |
16 | window.webIdEventEmitter.once( 'update', ( webId ) =>
17 | {
18 | console.log( 'WebId has been updated (though not sure what to do with it...)', webId );
19 | } );
20 | }
21 |
22 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
23 | const middleware = [thunk, promiseMiddleware];
24 | const enhancer = composeEnhancers( applyMiddleware( ...middleware ) );
25 |
26 |
27 | function configureStore( initialState )
28 | {
29 | return createStore(
30 | rootReducer,
31 | initialState,
32 | enhancer
33 | );
34 | }
35 |
36 | const store = configureStore( {} );
37 |
38 | const reactRoot = document.getElementById( 'react-root' );
39 |
40 | render(
41 |
42 |
43 |
44 | ,
45 | reactRoot
46 | );
47 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import webIds from './webIds_reducer';
3 | import safe from './safe_reducer';
4 |
5 | // Add your new reducer here
6 | const reducers = {
7 | safe,
8 | webIds
9 | };
10 |
11 | const rootReducer = combineReducers( reducers );
12 |
13 | export default rootReducer;
14 |
--------------------------------------------------------------------------------
/src/reducers/safe_reducer.js:
--------------------------------------------------------------------------------
1 | import { TYPES } from '../actions/safe_actions';
2 |
3 | export const initialState = {
4 | }
5 |
6 | export default ( state = initialState, action ) =>
7 | {
8 | const { payload } = action;
9 |
10 | switch ( action.type )
11 | {
12 | case TYPES.SAFE_AUTHORISE: {
13 | return { ...state, ...payload };
14 | }
15 |
16 | // from browser not needed
17 | // case TYPES.SET_SELECTED_WEB_ID: {
18 | // const selectedId = payload;
19 | // }
20 |
21 | default: {
22 | return state;
23 | }
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/src/reducers/webIds_reducer.js:
--------------------------------------------------------------------------------
1 | import { TYPES } from '../actions/webIds_actions';
2 |
3 | export const initialState = [
4 | ];
5 |
6 | export default ( state = initialState, action ) =>
7 | {
8 | const { payload } = action;
9 | switch ( action.type )
10 | {
11 | case TYPES.ADD_WEB_ID:
12 | {
13 | if( ! payload.nick )
14 | return state;
15 |
16 | return [...state, payload];
17 | }
18 | case TYPES.UPDATE_WEB_ID:
19 | {
20 | const oldIdIndex = state.findIndex( webId => webId.uri === payload.uri );
21 | const oldId = state[oldIdIndex];
22 | const updatedId = { ...oldId, ...payload };
23 |
24 | const newState = [ ...state ];
25 |
26 | newState[oldIdIndex] = updatedId;
27 | return newState;
28 | }
29 | case TYPES.GET_AVAILABLE_WEB_IDS: {
30 | return [ ...payload ];
31 | }
32 | // from browser not needed
33 | // case TYPES.SET_SELECTED_WEB_ID: {
34 | // const selectedId = payload;
35 | // }
36 |
37 | default:
38 | return state;
39 | }
40 | };
41 |
--------------------------------------------------------------------------------