From 9248c3873c700944e2795448cef821d1749fa256 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 12 Jun 2019 09:09:26 +0200 Subject: [PATCH 1/4] Use React in a new Markdown pane --- .babelrc | 1 + .eslintrc | 3 +- index.js | 1 + markdown/index.tsx | 49 ++++ markdown/service.ts | 13 ++ markdown/view.tsx | 45 ++++ package-lock.json | 541 +++++++++++++++++++++++++++++++++++++++++++- package.json | 7 + tsconfig.json | 2 +- types.ts | 16 +- webpack.config.js | 4 +- 11 files changed, 653 insertions(+), 29 deletions(-) create mode 100644 markdown/index.tsx create mode 100644 markdown/service.ts create mode 100644 markdown/view.tsx diff --git a/.babelrc b/.babelrc index eb6e320a..630c5d11 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,7 @@ { "presets": [ "@babel/preset-env", + "@babel/preset-react", "@babel/preset-typescript" ], } diff --git a/.eslintrc b/.eslintrc index f51aa813..89511dbf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,7 +14,8 @@ "project": "./tsconfig.json" }, "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "react" ], "rules": { "no-unused-vars": ["warn", { diff --git a/index.js b/index.js index e609c51b..bd54dfbc 100644 --- a/index.js +++ b/index.js @@ -47,6 +47,7 @@ if (typeof window !== 'undefined') { let register = panes.register +register(require('./markdown/index.tsx').Pane) register(require('issue-pane')) register(require('contacts-pane')) diff --git a/markdown/index.tsx b/markdown/index.tsx new file mode 100644 index 00000000..40f20a63 --- /dev/null +++ b/markdown/index.tsx @@ -0,0 +1,49 @@ +import * as React from 'react' +import * as ReactDOM from 'react-dom' +import { PaneDefinition, NewPaneOptions } from '../types' +import $rdf from 'rdflib' +import solidUi from 'solid-ui' +import { saveMarkdown, loadMarkdown } from './service' +import { View } from './view' + +const { icons, store } = solidUi + +export const Pane: PaneDefinition = { + icon: `${icons.iconBase}noun_79217.svg`, + name: 'MarkdownPane', + label: (subject) => subject.uri.endsWith('.md') ? 'Handle markdown file' : null, + mintNew: function (options) { + const newInstance = createFileName(options) + return saveMarkdown(store, newInstance.uri, '# This is your markdown file\n\nHere be stuff!') + .then(() => ({ + ...options, + newInstance + })) + .catch((err: any) => { + console.error('Error creating new instance of markdown file', err) + return options + }) + }, + render: (subject) => { + const container = document.createElement('div') + + loadMarkdown(store, subject.uri).then((markdown) => { + const view = ( + saveMarkdown(store, subject.uri, newMarkdown)} + /> + ) + ReactDOM.render(view, container) + }) + return container + } +} + +function createFileName (options: NewPaneOptions): $rdf.NamedNode { + let uri = options.newBase + if (uri.endsWith('/')) { + uri = uri.slice(0, -1) + '.md' + } + return $rdf.sym(uri) +} diff --git a/markdown/service.ts b/markdown/service.ts new file mode 100644 index 00000000..6efb3be9 --- /dev/null +++ b/markdown/service.ts @@ -0,0 +1,13 @@ +import { IndexedFormula } from 'rdflib' + +export function loadMarkdown (store: IndexedFormula, uri: string): Promise { + return (store as any).fetcher.webOperation('GET', uri) + .then((response: any) => response.responseText) +} + +export function saveMarkdown (store: IndexedFormula, uri: string, data: string): Promise { + return (store as any).fetcher.webOperation('PUT', uri, { + data, + contentType: 'text/markdown; charset=UTF-8' + }) +} diff --git a/markdown/view.tsx b/markdown/view.tsx new file mode 100644 index 00000000..f9810876 --- /dev/null +++ b/markdown/view.tsx @@ -0,0 +1,45 @@ +import * as React from 'react' +import Markdown from 'react-markdown' + +interface Props { + markdown: string; + onSave: (newMarkdown: string) => Promise; +} + +export const View: React.FC = (props) => { + const [phase, setPhase] = React.useState<'loading' | 'rendering' | 'editing'>('rendering') + const [rawText, setRawText] = React.useState(props.markdown) + + function storeMarkdown () { + setPhase('loading') + props.onSave(rawText).then(() => { + setPhase('rendering') + }) + } + + if (phase === 'loading') { + return
Loading…
+ } + + if (phase === 'editing') { + return ( +
+
{ e.preventDefault(); storeMarkdown() }}> + + + , +
+ +`; + +exports[`should properly render markdown 1`] = ` +
+
+

+ Some + + awesome + + markdown +

+ +
+
+`; diff --git a/markdown/actErrorWorkaround.ts b/markdown/actErrorWorkaround.ts new file mode 100644 index 00000000..cf3a88b3 --- /dev/null +++ b/markdown/actErrorWorkaround.ts @@ -0,0 +1,26 @@ +/* eslint-env jest */ + +/* istanbul ignore next [This is a test helper, so it doesn't need to be tested itself] */ +/** + * This is a workaround for a bug that will be fixed in react-dom@16.9 + * + * The bug results in a warning being thrown about calls not being wrapped in `act()` + * when a component calls `setState` twice. + * More info about the issue: https://github.com/testing-library/react-testing-library/issues/281#issuecomment-480349256 + * The PR that will fix it: https://github.com/facebook/react/pull/14853 + */ +export function workaroundActError () { + const originalError = console.error + beforeAll(() => { + console.error = (...args) => { + if (/Warning.*not wrapped in act/.test(args[0])) { + return + } + originalError.call(console, ...args) + } + }) + + afterAll(() => { + console.error = originalError + }) +} diff --git a/markdown/view.test.tsx b/markdown/view.test.tsx new file mode 100644 index 00000000..d30ae4e6 --- /dev/null +++ b/markdown/view.test.tsx @@ -0,0 +1,44 @@ +/* eslint-env jest */ +import * as React from 'react' +import { + render, + fireEvent +} from '@testing-library/react' +import { View } from './view' +import { workaroundActError } from './actErrorWorkaround' + +workaroundActError() + +it('should properly render markdown', () => { + const { container } = render() + + expect(container).toMatchSnapshot() +}) + +describe('Edit mode', () => { + it('should properly render the edit form', () => { + const { container, getByRole } = render() + + const editButton = getByRole('button') + editButton.click() + + expect(container).toMatchSnapshot() + }) + + it('should call the onSave handler after saving the new content', () => { + const mockHandler = jest.fn().mockReturnValue(Promise.resolve()) + const { getByRole, getByDisplayValue } = render() + + const editButton = getByRole('button') + editButton.click() + + const textarea = getByDisplayValue('Arbitrary markdown') + fireEvent.change(textarea, { target: { value: 'Some _other_ markdown' } }) + + const renderButton = getByRole('button') + renderButton.click() + + expect(mockHandler.mock.calls.length).toBe(1) + expect(mockHandler.mock.calls[0][0]).toBe('Some _other_ markdown') + }) +}) diff --git a/package-lock.json b/package-lock.json index f0bff555..5ebfd5be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2489,6 +2489,12 @@ "@types/yargs": "^12.0.9" } }, + "@sheerun/mutationobserver-shim": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz", + "integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==", + "dev": true + }, "@solid/better-simple-slideshow": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@solid/better-simple-slideshow/-/better-simple-slideshow-0.1.0.tgz", @@ -2571,6 +2577,29 @@ } } }, + "@testing-library/dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.2.0.tgz", + "integrity": "sha512-nFaZes/bzDfMqwZpQXdiPyj3WXU16FYf5k5NCFu/qJM4JdRJLHEtSRYtrETmk7nCf+qLVoHCqRduGi/4KE83Gw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.5", + "@sheerun/mutationobserver-shim": "^0.3.2", + "aria-query": "3.0.0", + "pretty-format": "^24.8.0", + "wait-for-expect": "^1.2.0" + } + }, + "@testing-library/react": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-8.0.1.tgz", + "integrity": "sha512-N/1pJfhEnNYkGyxuw4xbp03evaS0z/CT8o0QgTfJqGlukAcU15xf9uU1w03NHKZJcU69nOCBAoAkXHtHzYwMbg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.5", + "@testing-library/dom": "^5.0.0" + } + }, "@trust/jose": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/@trust/jose/-/jose-0.1.7.tgz", @@ -3124,6 +3153,16 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -3220,6 +3259,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -12559,6 +12604,12 @@ "browser-process-hrtime": "^0.1.2" } }, + "wait-for-expect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-1.2.0.tgz", + "integrity": "sha512-EJhKpA+5UHixduMBEGhTFuLuVgQBKWxkFbefOdj2bbk2/OpA5Opsc4aUTGmF+qJ+v3kTGxDRNYwKaT4j6g5n8Q==", + "dev": true + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", diff --git a/package.json b/package.json index 1c57eda8..34bd6a2d 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@babel/preset-env": "^7.4.4", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", + "@testing-library/react": "^8.0.1", "@types/jest": "^24.0.12", "@types/rdflib": "^0.20.0", "@types/react": "^16.8.19", From 760db5c52f41cb27827f8bdb0de7a86dbddea8ed Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 16 Jul 2019 13:09:54 +0200 Subject: [PATCH 4/4] Add the dashboard from datasister We can't literally inject the module, since it determines the current user in a different way (based on who's logged in rather than on which Pod we're running), so this is currently a copy-paste of most code in there, with a few import paths changed around. Still, it at least shows where to copy changes from datasister into, and vice versa: it maintains a similar overall structure, so modifications made here can relatively easily be carried over to datasister. --- dashboard/dashboardPane.tsx | 40 +++++++++++ dashboard/datasister-dashboard/Dashboard.tsx | 28 ++++++++ .../components/ResourceLink.tsx | 47 ++++++++++++ dashboard/datasister-dashboard/context.tsx | 29 ++++++++ .../datasister-dashboard/hooks/useWebId.ts | 11 +++ .../datasister-dashboard/widgets/Apps.tsx | 23 ++++++ .../widgets/Apps/pixolid.svg | 25 +++++++ .../widgets/Bookmarks.tsx | 71 +++++++++++++++++++ .../datasister-dashboard/widgets/Folder.tsx | 26 +++++++ .../datasister-dashboard/widgets/Profile.tsx | 12 ++++ dashboard/wrapper.tsx | 28 ++++++++ index.js | 3 +- package-lock.json | 6 +- package.json | 2 +- 14 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 dashboard/dashboardPane.tsx create mode 100644 dashboard/datasister-dashboard/Dashboard.tsx create mode 100644 dashboard/datasister-dashboard/components/ResourceLink.tsx create mode 100644 dashboard/datasister-dashboard/context.tsx create mode 100644 dashboard/datasister-dashboard/hooks/useWebId.ts create mode 100644 dashboard/datasister-dashboard/widgets/Apps.tsx create mode 100644 dashboard/datasister-dashboard/widgets/Apps/pixolid.svg create mode 100644 dashboard/datasister-dashboard/widgets/Bookmarks.tsx create mode 100644 dashboard/datasister-dashboard/widgets/Folder.tsx create mode 100644 dashboard/datasister-dashboard/widgets/Profile.tsx create mode 100644 dashboard/wrapper.tsx diff --git a/dashboard/dashboardPane.tsx b/dashboard/dashboardPane.tsx new file mode 100644 index 00000000..6b1b5e40 --- /dev/null +++ b/dashboard/dashboardPane.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import UI from 'solid-ui' +import panes from 'pane-registry' +import { Wrapper } from './wrapper' +import { PaneDefinition } from '../types' + +const HomePane: PaneDefinition = { + icon: UI.icons.iconBase + 'noun_547570.svg', // noun_25830 + + name: 'dashboard', + + label: function () { + return 'Dashboard' + }, + + render: function (subject, dom) { + const container = document.createElement('div') + const loadResource = (resourcePath: string) => { + panes.getOutliner(dom).GotoSubject(resourcePath, true, undefined, true) + } + UI.authn.solidAuthClient.currentSession().then((session: any) => { + ReactDOM.render( + , + container + ) + }) + + return container + } +} // pane object + +// ends +export default HomePane diff --git a/dashboard/datasister-dashboard/Dashboard.tsx b/dashboard/datasister-dashboard/Dashboard.tsx new file mode 100644 index 00000000..d1b1e2e8 --- /dev/null +++ b/dashboard/datasister-dashboard/Dashboard.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { ProfileWidget } from './widgets/Profile' +import { BookmarksWidget } from './widgets/Bookmarks' +import { FolderWidget } from './widgets/Folder' +import { AppsWidget } from './widgets/Apps' + +export const Dashboard: React.FC<{ +}> = (props) => { + return ( + <> +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ ) +} diff --git a/dashboard/datasister-dashboard/components/ResourceLink.tsx b/dashboard/datasister-dashboard/components/ResourceLink.tsx new file mode 100644 index 00000000..189c7444 --- /dev/null +++ b/dashboard/datasister-dashboard/components/ResourceLink.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import { NamedNode } from 'rdflib' +import UI from 'solid-ui' +import { DataBrowserContext } from '../context' + +interface OwnProps { + resource: NamedNode; +}; + +type Props = Omit, keyof OwnProps> & OwnProps; + +export const ResourceLink: React.FC = (props) => { + const { store, loadResource, podOrigin } = React.useContext(DataBrowserContext) + const clickHandler = (event: React.MouseEvent) => { + if (props.resource.uri.substring(0, podOrigin.length) === podOrigin) { + event.preventDefault() + loadResource(props.resource.uri) + } + } + + const children = (props.children) + ? props.children + : UI.label(props.resource, store, podOrigin) + + let title = props.title + if (!title) { + title = (typeof children === 'string') + ? `View ${children}` + : `View ${UI.label(props.resource, store, podOrigin)}` + } + + const anchorProps = { + ...props, + title: title, + resource: undefined + } + + return ( + + {children} + + ) +} diff --git a/dashboard/datasister-dashboard/context.tsx b/dashboard/datasister-dashboard/context.tsx new file mode 100644 index 00000000..782c2234 --- /dev/null +++ b/dashboard/datasister-dashboard/context.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import $rdf, { IndexedFormula, Fetcher, UpdateManager, NamedNode } from 'rdflib' + +export interface DataBrowserContextData { + store: IndexedFormula; + fetcher: Fetcher; + updater: UpdateManager; + podOrigin: string; + webId: string; + loadResource: (resourcePath: string) => void; +}; + +const defaultContext: DataBrowserContextData = { + podOrigin: document.location.origin, + store: $rdf.graph(), + fetcher: new Fetcher($rdf.graph(), undefined), + updater: new UpdateManager($rdf.graph()), + webId: 'http://example.com', + loadResource: () => undefined +} + +/** + * The context allows the data browser to easily access global values + * everywhere in the application. + * Individual Panes, however, should get these values as properties, + * to avoid a hard dependency on the data browser. + * This will allow them to be used as e.g. individual apps or in browser extensions. + */ +export const DataBrowserContext = React.createContext(defaultContext) diff --git a/dashboard/datasister-dashboard/hooks/useWebId.ts b/dashboard/datasister-dashboard/hooks/useWebId.ts new file mode 100644 index 00000000..c5c8afc2 --- /dev/null +++ b/dashboard/datasister-dashboard/hooks/useWebId.ts @@ -0,0 +1,11 @@ +import React from 'react' +import { DataBrowserContext } from '../context' + +/** + * API-compatible with @solid/react's `useWebId`, but fetches it from the context object + */ +export function useWebId () { + const { webId } = React.useContext(DataBrowserContext) + + return webId +} diff --git a/dashboard/datasister-dashboard/widgets/Apps.tsx b/dashboard/datasister-dashboard/widgets/Apps.tsx new file mode 100644 index 00000000..cadc2a3f --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Apps.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import { DataBrowserContext } from '../context' + +export const AppsWidget: React.FC = () => { + const { podOrigin } = React.useContext(DataBrowserContext) + + const appLink = (podOrigin) + ? `https://pixolid.netlify.com/?idp=${podOrigin}` + : 'https://pixolid.netlify.com/' + + return ( +
+
+

Try this app

+

+ + Pixolid + +

+
+
+ ) +} diff --git a/dashboard/datasister-dashboard/widgets/Apps/pixolid.svg b/dashboard/datasister-dashboard/widgets/Apps/pixolid.svg new file mode 100644 index 00000000..c6749a9a --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Apps/pixolid.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dashboard/datasister-dashboard/widgets/Bookmarks.tsx b/dashboard/datasister-dashboard/widgets/Bookmarks.tsx new file mode 100644 index 00000000..6b149cbb --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Bookmarks.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import $rdf from 'rdflib' +import namespaces from 'solid-namespace' +import { DataBrowserContext } from '../context' +import { useWebId } from '../hooks/useWebId' + +const ns = namespaces($rdf) + +export const BookmarksWidget: React.FC = () => { + const { store, fetcher, podOrigin } = React.useContext(DataBrowserContext) + + const bookmarks = useBookmarks(store, fetcher) + + if (!Array.isArray(bookmarks) || bookmarks.length === 0) { + return null + } + + return ( +
+
+

Latest bookmarks

+
+ +
+ All bookmarks +
+
+ ) +} + +function useBookmarks (store: $rdf.IndexedFormula, fetcher: $rdf.Fetcher) { + const webId = useWebId() + const [bookmarks, setBookmarks] = React.useState>() + + React.useEffect(() => { + if (!webId) { + return + } + getBookmarks(store, fetcher, webId) + .then(setBookmarks) + .catch((e) => console.log('Error fetching bookmarks:', e)) + }, [store, fetcher, webId]) + + return bookmarks +} + +async function getBookmarks (store: $rdf.IndexedFormula, fetcher: $rdf.Fetcher, webId: string) { + const profile = $rdf.sym(webId) + const [ publicTypeIndexStatement ] = store.statementsMatching(profile, ns.solid('publicTypeIndex'), null, profile.doc(), true) + const publicTypeIndex = publicTypeIndexStatement.object + await fetcher.load(publicTypeIndex as any as $rdf.NamedNode) + const bookmarkClass = new $rdf.NamedNode('http://www.w3.org/2002/01/bookmark#Bookmark') + const [ bookmarkRegistryStatement ] = store.statementsMatching(null, ns.solid('forClass'), bookmarkClass, publicTypeIndex, true) + const bookmarkRegistry = bookmarkRegistryStatement.subject + const [ bookmarkRegistryInstanceStatement ] = store.statementsMatching(bookmarkRegistry, ns.solid('instance'), null, publicTypeIndex, true) + const bookmarkRegistryInstance = bookmarkRegistryInstanceStatement.object + await fetcher.load(bookmarkRegistryInstance as any as $rdf.NamedNode) + const bookmarkStatements = store.statementsMatching(null, ns.rdf('type'), bookmarkClass, bookmarkRegistryInstance) + return bookmarkStatements.map((statement) => { + const recalls = new $rdf.NamedNode('http://www.w3.org/2002/01/bookmark#recalls') + const bookmarkNode = statement.subject + const [ titleStatement ] = store.statementsMatching(bookmarkNode, ns.dct('title'), null, bookmarkRegistryInstance) + const [ urlStatement ] = store.statementsMatching(bookmarkNode, recalls, null, bookmarkRegistryInstance) + return { + title: titleStatement.object.value, + url: urlStatement.object.value + } + }) +} diff --git a/dashboard/datasister-dashboard/widgets/Folder.tsx b/dashboard/datasister-dashboard/widgets/Folder.tsx new file mode 100644 index 00000000..b48e87a6 --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Folder.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import $rdf from 'rdflib' +import { ResourceLink } from '../components/ResourceLink' +import { DataBrowserContext } from '../context' + +export const FolderWidget: React.FC = () => { + const { podOrigin } = React.useContext(DataBrowserContext) + + return ( +
+
+

Raw data

+

+ Public data + Private data +

+
+
+ ) +} diff --git a/dashboard/datasister-dashboard/widgets/Profile.tsx b/dashboard/datasister-dashboard/widgets/Profile.tsx new file mode 100644 index 00000000..c8d80c7d --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Profile.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const ProfileWidget: React.FC = () => { + return ( +
+
+

My profile

+

No name provided…

+
+
+ ); +} diff --git a/dashboard/wrapper.tsx b/dashboard/wrapper.tsx new file mode 100644 index 00000000..98b5a422 --- /dev/null +++ b/dashboard/wrapper.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { DataBrowserContextData, DataBrowserContext } from './datasister-dashboard/context'; +import { IndexedFormula, Fetcher, UpdateManager } from 'rdflib'; +import { Dashboard } from './datasister-dashboard/Dashboard'; + +interface Props { + store: IndexedFormula, + fetcher: Fetcher, + updater: UpdateManager, + webId: string, + loadResource: (resourcePath: string) => void +} +export const Wrapper: React.FC = (props) => { + const dataBrowserContext: DataBrowserContextData = { + store: props.store, + fetcher: props.fetcher, + updater: props.updater, + webId: props.webId, + podOrigin: document.location.origin, + loadResource: props.loadResource + } + + return ( + + + + ) +} diff --git a/index.js b/index.js index bd54dfbc..697853d1 100644 --- a/index.js +++ b/index.js @@ -128,10 +128,11 @@ register(require('./sharing/sharingPane.js')) // The internals pane is always (almost?) the last as it is the least user-friendly register(require('./internalPane.js')) -// The home pane is a 2016 experiment. Always there. register(require('./profile/profilePane').default) // edit your public profile register(require('./trustedApplications/trustedApplicationsPane').default) // manage your trusted applications +// The home pane is a 2016 experiment. Always there. register(require('./home/homePane').default) +register(require('./dashboard/dashboardPane').default) // ENDS diff --git a/package-lock.json b/package-lock.json index 5ebfd5be..f3108d40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12231,9 +12231,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", - "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 34bd6a2d..01b1eb40 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "fork-ts-checker-webpack-plugin": "^1.3.0", "jest": "^24.8.0", "ts-jest": "^24.0.2", - "typescript": "^3.4.5", + "typescript": "^3.5.3", "webpack": "^4.30.0", "webpack-cli": "^3.3.2" }