Skip to content

Commit

Permalink
Add option to log in to other solid identity providers
Browse files Browse the repository at this point in the history
+ multiple styling changes
+ highlight both knows and known, also include them in node radius
  • Loading branch information
mrkvon committed Jul 23, 2021
1 parent 9ce310d commit 469489e
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 33 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
# Friend Crawler
# Solid Friend Crawler

[live version](https://friends.livegraph.org)
Live at [friends.livegraph.org](https://friends.livegraph.org)

Visualize the network of friends on Solid

The app starts at the logged user (and timbl on solidcommunity.net) and crawls the foaf:knows connections.

## TODO

- [x] add people to history of browser (will allow browsing)
- [x] make people a variable size based on how many people know them
- [ ] show clearly what are the directions of :knows
- [x] show also who knows this person
- [ ] login for different pod providers
- [x] login for different pod providers
- [x] faster (parallel) crawling
- [x] search people
- [ ] make it more accessible and easier to navigate
- [ ] highlight also people who know the person
- [x] highlight also people who know the person
- [ ] highlight people whose button is crawled in PersonList
- [ ] add custom starting point for crawling
- [x] support extended profile (seeAlso, sameAs)
- [ ] figure hosting it as SPA with router
30 changes: 19 additions & 11 deletions src/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
handleIncomingRedirect,
} from '@inrupt/solid-client-authn-browser'
import { SessionContext, SessionInfo } from '../contexts/session'
import LoginPrompt from './LoginPrompt'

interface Props {
className?: string
Expand Down Expand Up @@ -32,14 +33,21 @@ const Login: React.FC<Props> = (
setLoading(false)
})
}, [setInfo])
const handleLogin = async () => {

const handleLogin = async (oidcIssuer: string) => {
setLoading(true)
await login({
oidcIssuer: 'https://solidcommunity.net',
redirectUrl: window.location.href,
clientName: 'Friends Crawler',
})
setLoading(false)
try {
await login({
oidcIssuer,
redirectUrl: window.location.href,
clientName: 'Friends Crawler',
})
} catch (error) {
alert(`Could not find a Solid Pod at ${oidcIssuer}`)
localStorage.removeItem('idp')
} finally {
setLoading(false)
}
}

const handleLogout = async () => {
Expand All @@ -55,15 +63,15 @@ const Login: React.FC<Props> = (
}

return loading ? (
<span {...commonProps}>Loading</span>
<button {...commonProps} disabled>
Loading
</button>
) : info?.isLoggedIn ? (
<button {...commonProps} onClick={handleLogout}>
{info?.webId} Logout
</button>
) : (
<button {...commonProps} onClick={handleLogin}>
Login
</button>
<LoginPrompt {...commonProps} onLogin={handleLogin} />
)
}

Expand Down
101 changes: 101 additions & 0 deletions src/components/LoginPrompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useState } from 'react'
import Modal from 'react-modal'

interface Props {
onLogin: (oidcIssuer: string) => void
}

const LoginPrompt: React.FC<Props> = ({ onLogin, ...props }: Props) => {
const [promptOpen, setPromptOpen] = useState(false)
const [idp, setIdp] = useState(
localStorage.getItem('idp') ?? 'https://solidcommunity.net',
)

const onSubmit: React.FormEventHandler = e => {
e.preventDefault()
localStorage.setItem('idp', idp)
onLogin(idp)
}

const onChangeInput = (e: React.FormEvent<HTMLInputElement>) => {
e.preventDefault()
const newValue = e.currentTarget.value
setIdp(newValue)
}

if (!promptOpen) {
return (
<>
<button
{...props}
onClick={e => {
e.preventDefault()
setPromptOpen(true)
}}
>
Login
</button>
</>
)
}

return (
<>
<Modal
isOpen={promptOpen}
onRequestClose={() => setPromptOpen(false)}
contentLabel="Connect your Solid Pod"
overlayClassName={{
base: 'modal modal-background is-active',
afterOpen: '',
beforeClose: '',
}}
className={{
base: 'modal-content',
afterOpen: '',
beforeClose: '',
}}
closeTimeoutMS={50}
>
<button className="modal-close" onClick={() => setPromptOpen(false)}>
close
</button>

<div className="card">
<header className="card-header">
<p className="card-header-title">
Select your Solid identity provider
</p>
</header>
<div className="card-content">
<form onSubmit={onSubmit}>
<div className="field">
<div className="control">
<input
id="idp"
className="input"
type="url"
value={idp}
onChange={onChangeInput}
placeholder="Where is your Solid Pod?"
/>
</div>
</div>
<div className="field">
<div className="control">
<input
type="submit"
value="Connect"
className="button is-link"
/>
</div>
</div>
</form>
</div>
</div>
</Modal>
</>
)
}

export default LoginPrompt
18 changes: 10 additions & 8 deletions src/components/PersonCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ const PersonCard = ({ person, knows, known, onSelectPerson }: Props) => {
return (
<div className="card">
<header className="card-header">
<p className="card-header-title">
<p className="card-header-title" style={{ overflowWrap: 'anywhere' }}>
<a href={person.uri}>{person.name || person.uri}</a>
</p>
<button
className="card-header-icon"
aria-label="close"
onClick={() => onSelectPerson('')}
>
close
</button>
<span className="card-header-icon">
<button
className="delete"
aria-label="close"
onClick={() => onSelectPerson('')}
>
close
</button>
</span>
</header>
{person.photo && (
<div className="card-image">
Expand Down
23 changes: 14 additions & 9 deletions src/components/VisualizationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const InfoContainer = ({ children }: ICProps) => (
overflowX: 'hidden',
}}
>
<div className="columns mr-1 mt-6">
<div style={{ marginTop: '4rem' }} className="columns mr-1 ml-1">
<div className="column is-one-quarter is-offset-three-quarters">
<div style={{ pointerEvents: 'all', overflowX: 'auto', width: '100%' }}>
{children}
Expand Down Expand Up @@ -155,7 +155,7 @@ const transformLayout = (
}

function nodeRadius(person: Person) {
let count = person.known?.size ?? 0
let count = new Set([...(person?.known ?? new Set()), ...person.knows]).size
count = count < 1 ? 1 : count
return count ** 0.42 * 5
}
Expand All @@ -165,7 +165,12 @@ const selectNodeDependencies = (
graph: PeopleGraph,
): string[] => {
if (!selectedNodeUri) return []
return Array.from(graph?.[selectedNodeUri]?.knows ?? new Set())
return [
...new Set([
...(graph?.[selectedNodeUri]?.knows ?? new Set()),
...(graph?.[selectedNodeUri]?.known ?? new Set()),
]),
]
}

const VisualizationContainer: React.FC<RouteComponentProps> = ({
Expand Down Expand Up @@ -266,15 +271,15 @@ const VisualizationContainer: React.FC<RouteComponentProps> = ({

const grid = transformGrid(matrix, basicGrid)

let person, knows, known
let person
let knows: Person[] = []
let known: Person[] = []

if (selectedNode) {
person = people[selectedNode]
if (person) {
knows = Array.from(person.knows).map(f => people[f])
if (person.known) {
known = Array.from(person.known).map(f => people[f])
}
knows = [...person.knows].map(f => people[f])
known = [...(person?.known ?? new Set())].map(f => people[f])
}
}

Expand All @@ -297,7 +302,7 @@ const VisualizationContainer: React.FC<RouteComponentProps> = ({
</Helmet>

<InfoContainer>
{person && knows && known ? (
{person ? (
<PersonCard
person={person}
knows={knows}
Expand Down
6 changes: 5 additions & 1 deletion src/data/BFSFriends.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ export const findFriends = async (webId: IriString): Promise<PersonData> => {
data.friends = data.friends
.concat(friends)
.filter((a, i, data) => data.indexOf(a) === i)
if (!data.name) data.name = getTerm(person, foaf.name)?.value ?? ''
if (!data.name)
data.name =
getTerm(person, foaf.name)?.value ??
getTerm(person, vcard.fn)?.value ??
''
if (!data.photo)
data.photo =
getTerm(person, vcard.hasPhoto)?.value ??
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"downlevelIteration": true,
"jsx": "react-jsx"
},
"include": [
Expand Down

0 comments on commit 469489e

Please sign in to comment.