From b8b29b84ea73e801bec8a4105cf79cf31a179d78 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 22 Jul 2019 13:14:36 +0200 Subject: [PATCH 1/4] List WebIDs of contacts on the dashboard --- dashboard/datasister-dashboard/Dashboard.tsx | 4 ++ .../datasister-dashboard/widgets/Contacts.tsx | 52 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 dashboard/datasister-dashboard/widgets/Contacts.tsx diff --git a/dashboard/datasister-dashboard/Dashboard.tsx b/dashboard/datasister-dashboard/Dashboard.tsx index d1b1e2e8..3c393384 100644 --- a/dashboard/datasister-dashboard/Dashboard.tsx +++ b/dashboard/datasister-dashboard/Dashboard.tsx @@ -1,5 +1,6 @@ import React from 'react' import { ProfileWidget } from './widgets/Profile' +import { ContactsWidget } from './widgets/Contacts' import { BookmarksWidget } from './widgets/Bookmarks' import { FolderWidget } from './widgets/Folder' import { AppsWidget } from './widgets/Apps' @@ -13,6 +14,9 @@ export const Dashboard: React.FC<{
+
+ +
diff --git a/dashboard/datasister-dashboard/widgets/Contacts.tsx b/dashboard/datasister-dashboard/widgets/Contacts.tsx new file mode 100644 index 00000000..1ac3f885 --- /dev/null +++ b/dashboard/datasister-dashboard/widgets/Contacts.tsx @@ -0,0 +1,52 @@ +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 ContactsWidget: React.FC = () => { + const { store, fetcher, podOrigin } = React.useContext(DataBrowserContext) + + const contacts = useContacts(store, fetcher) + + if (!Array.isArray(contacts) || contacts.length === 0) { + return null + } + + return ( +
+
+

Contacts

+
+
    + {contacts.slice(0, 5).map((contact) =>
  • {contact}
  • )} +
+
+
+
+ ) +} + +function useContacts (store: $rdf.IndexedFormula, fetcher: $rdf.Fetcher) { + const webId = useWebId() + const [contacts, setContacts] = React.useState() + + React.useEffect(() => { + if (!webId) { + return + } + getContacts(store, fetcher, webId) + .then(setContacts) + .catch((e) => console.log('Error fetching contacts:', e)) + }, [store, fetcher, webId]) + + return contacts +} + +async function getContacts (store: $rdf.IndexedFormula, fetcher: $rdf.Fetcher, webId: string) { + const profile = $rdf.sym(webId) + const knowsStatements = store.statementsMatching(profile, ns.foaf('knows'), null, profile.doc()) + return knowsStatements.map(st => st.object.value) +} From 4fa5d6bc5b7c8d3f89c2250826d19d4f234ca890 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 22 Jul 2019 14:10:59 +0200 Subject: [PATCH 2/4] Fetch and display a contact's foaf:name, if set --- .../components/ProfileBadge.tsx | 33 +++++++++++++++++++ .../datasister-dashboard/widgets/Contacts.tsx | 5 +-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 dashboard/datasister-dashboard/components/ProfileBadge.tsx diff --git a/dashboard/datasister-dashboard/components/ProfileBadge.tsx b/dashboard/datasister-dashboard/components/ProfileBadge.tsx new file mode 100644 index 00000000..a40c6adf --- /dev/null +++ b/dashboard/datasister-dashboard/components/ProfileBadge.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import $rdf from 'rdflib' +import namespaces from 'solid-namespace' +import { DataBrowserContext } from '../context' + +const ns = namespaces($rdf) + +interface Props { + webId: string; +}; + +export const ProfileBadge: React.FC = (props) => { + const { store, fetcher } = React.useContext(DataBrowserContext) + const [ name, setName ] = React.useState(props.webId) + + React.useEffect(() => { + fetcher.load(props.webId).then(() => { + const [ nameStatement ] = store.statementsMatching($rdf.sym(props.webId), ns.foaf('name'), null as any, null as any, true) + console.log({ nameStatement }) + if (nameStatement) { + setName(nameStatement.object.value) + } + }) + }) + + return ( + <> + + {name} + + + ) +} diff --git a/dashboard/datasister-dashboard/widgets/Contacts.tsx b/dashboard/datasister-dashboard/widgets/Contacts.tsx index 1ac3f885..b0bfd5da 100644 --- a/dashboard/datasister-dashboard/widgets/Contacts.tsx +++ b/dashboard/datasister-dashboard/widgets/Contacts.tsx @@ -3,11 +3,12 @@ import $rdf from 'rdflib' import namespaces from 'solid-namespace' import { DataBrowserContext } from '../context' import { useWebId } from '../hooks/useWebId' +import { ProfileBadge } from '../components/ProfileBadge' const ns = namespaces($rdf) export const ContactsWidget: React.FC = () => { - const { store, fetcher, podOrigin } = React.useContext(DataBrowserContext) + const { store, fetcher } = React.useContext(DataBrowserContext) const contacts = useContacts(store, fetcher) @@ -21,7 +22,7 @@ export const ContactsWidget: React.FC = () => {

Contacts

    - {contacts.slice(0, 5).map((contact) =>
  • {contact}
  • )} + {contacts.slice(0, 5).map((contact) => )}
From b5629899cdcadecb613bae26fe2a7ec464d125b3 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 22 Jul 2019 14:12:13 +0200 Subject: [PATCH 3/4] List *all* contacts People trying it out now probably do not have that many contacts yet, and since we're not linking to a Pane/View/App/whatever to list all their contacts, it'd be confusing to only show the first five. --- dashboard/datasister-dashboard/components/ProfileBadge.tsx | 1 - dashboard/datasister-dashboard/widgets/Contacts.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dashboard/datasister-dashboard/components/ProfileBadge.tsx b/dashboard/datasister-dashboard/components/ProfileBadge.tsx index a40c6adf..80e82268 100644 --- a/dashboard/datasister-dashboard/components/ProfileBadge.tsx +++ b/dashboard/datasister-dashboard/components/ProfileBadge.tsx @@ -16,7 +16,6 @@ export const ProfileBadge: React.FC = (props) => { React.useEffect(() => { fetcher.load(props.webId).then(() => { const [ nameStatement ] = store.statementsMatching($rdf.sym(props.webId), ns.foaf('name'), null as any, null as any, true) - console.log({ nameStatement }) if (nameStatement) { setName(nameStatement.object.value) } diff --git a/dashboard/datasister-dashboard/widgets/Contacts.tsx b/dashboard/datasister-dashboard/widgets/Contacts.tsx index b0bfd5da..acf49f5d 100644 --- a/dashboard/datasister-dashboard/widgets/Contacts.tsx +++ b/dashboard/datasister-dashboard/widgets/Contacts.tsx @@ -22,7 +22,7 @@ export const ContactsWidget: React.FC = () => {

Contacts

    - {contacts.slice(0, 5).map((contact) => )} + {contacts.map((contact) => )}
From bb99960599125c356799c1d3787288158c2d2357 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 22 Jul 2019 15:10:30 +0200 Subject: [PATCH 4/4] Allow adding a contact through the Dashboard --- .../datasister-dashboard/widgets/Contacts.tsx | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/dashboard/datasister-dashboard/widgets/Contacts.tsx b/dashboard/datasister-dashboard/widgets/Contacts.tsx index acf49f5d..2a12dae8 100644 --- a/dashboard/datasister-dashboard/widgets/Contacts.tsx +++ b/dashboard/datasister-dashboard/widgets/Contacts.tsx @@ -8,12 +8,31 @@ import { ProfileBadge } from '../components/ProfileBadge' const ns = namespaces($rdf) export const ContactsWidget: React.FC = () => { - const { store, fetcher } = React.useContext(DataBrowserContext) + const { store, fetcher, updater } = React.useContext(DataBrowserContext) + const webId = useWebId() - const contacts = useContacts(store, fetcher) + const storedContacts = useContacts(store, fetcher) + const [addedContacts, addContact] = React.useReducer>( + (previouslyAdded, newContact) => previouslyAdded.concat(newContact), + [] + ) - if (!Array.isArray(contacts) || contacts.length === 0) { - return null + const contacts = (storedContacts || []).concat(addedContacts) + function onAddContact (contactWebId: string) { + if (!webId || !contactWebId) { + return + } + + const profile = $rdf.sym(webId) + updater.update( + [], + [$rdf.st(profile, ns.foaf('knows'), $rdf.sym(contactWebId), profile.doc())], + (_uri, success, _errorBody) => { + if (success) { + addContact(contactWebId) + } + } + ) } return ( @@ -22,14 +41,49 @@ export const ContactsWidget: React.FC = () => {

Contacts

    - {contacts.map((contact) => )} + {contacts.map((contact) =>
  • )}
+

Add a contact

+ ) } +const WebIdForm: React.FC<{ onSubmit: (webId: string) => void }> = (props) => { + const [webId, setWebId] = React.useState('') + + function handleSubmit (event: React.FormEvent) { + event.preventDefault() + + props.onSubmit(webId) + setWebId('') + } + + return ( +
+
+ +
+
+ +
+
+ ) +} + function useContacts (store: $rdf.IndexedFormula, fetcher: $rdf.Fetcher) { const webId = useWebId() const [contacts, setContacts] = React.useState()