Sample Application#
The following outlines how a sample application uses the Inrupt’s Solid React SDK to log in to a user’s Pod and displays the Profile. The sample application allows users to view/edit user profile.
Set Up#
To set up the sample application locally,
You can either:
Clone the application’s GitHub repo, or
Use Next.js application generator to set up the project.
npx create-next-app -e https://github.com/inrupt/solid-ui-react-demo
When prompted for the project name, either accept the default or enter a new name.
Go to your project directory, substituting your project name for
<app-name>
(e.g.,my-app
, etc.):cd <app-name>
Optional. Next.js collects anonymous telemetry data. To opt out, run the following in the root of your project directory
npx next telemetry disable
After you have set up the project, go to the project directory and run either:
npm ci npm run dev
or
yarn dev
When the application is running locally, open the browser to
https://localhost:3000
.
Examination of the Code#
Session Provider#
The sample application uses the SessionProvider and
wraps the application in the SessionProvider
import { SessionProvider } from "@inrupt/solid-ui-react";
// ... Content omitted
function AppContainer({ children }) {
const bem = useBem(useStyles());
return (
<SessionProvider>
<StylesProvider jss={jss}>
<ThemeProvider theme={theme}>
<CssBaseline />
<div className={bem("app-layout")}>
<Header />
<Nav />
<main className={bem("app-layout__main")}>
<PageHeader title={CONFIG.demoTitle} actions={[]}>
{CONFIG.demoDescription}
</PageHeader>
<Container>{children}</Container>
</main>
<Footer />
<div className={bem("app-layout__mobile-nav-push")} />
</div>
</ThemeProvider>
</StylesProvider>
</SessionProvider>
);
}
(Source: components/appContainer/index.jsx)
Session and Login Status#
Wrapped within the SessionProvider
, the sample application can use the
Inrupt SDK’s useSession
interface to access the current
Session and check its login
status.
import { useSession } from "@inrupt/solid-ui-react";
import Profile from "../components/profile";
export default function Home() {
const { session } = useSession();
return (
<div>
<h1>Demo</h1>
{session.info.isLoggedIn && <Profile />}
</div>
);
}
(Source: pages/index.jsx)
Log In#
To log in, sample application creates a LoginForm
that uses Inrupt
SDK’s LoginButton. Specify the
oidcIssuer
and redirectUrl
attributes for the
LoginButton.
For example, the sample application includes oidcIssuer
and
redirectUrl
attributes as well as the optional onError
and
authOptions
attributes.
import { LoginButton } from "@inrupt/solid-ui-react";
// ... Content omitted
export default function LoginForm() {
const [idp, setIdp] = useState("https://login.inrupt.com");
const [currentUrl, setCurrentUrl] = useState("https://localhost:3000");
const bem = useBem(useStyles());
const classes = useStyles();
useEffect(() => {
setCurrentUrl(window.location.href);
}, [setCurrentUrl]);
return (
<div className={classes.loginFormContainer}>
<Input
id="idp"
label="IDP"
placeholder="Identity Provider"
defaultValue={idp}
onChange={(e) => setIdp(e.target.value)}
/>
<LoginButton
authOptions={{ clientName: CONFIG.demoTitle }}
oidcIssuer={idp}
redirectUrl={currentUrl}
onError={console.error}
>
<LinkButton
variant="small"
className={bem("user-menu__list-item-trigger")}
>
Log In
</LinkButton>
</LoginButton>
</div>
);
}
(Source: components/loginForm/index.jsx)
Display the Profile#
The sample application uses the Inrupt SDK’s to display the profile data:
The CombinedDataProvider specifies the URL of the profile SolidDataset.
The following Inrupt SDK components display various properties from the profile. The components provide an
edit
attribute to toggle these fields to be editable or non-editable. You can also specify theautosave
attribute.
import { useState } from "react";
import {
useSession,
CombinedDataProvider,
Image,
Text,
} from "@inrupt/solid-ui-react";
import {
Button,
Card,
CardActions,
CardActionArea,
CardContent,
Container,
Typography,
} from "@material-ui/core";
import BusinessIcon from "@material-ui/icons/Business";
import { FOAF, VCARD } from "@inrupt/lit-generated-vocab-common";
import ContactTable from "../contactTable";
import BirthdateRow from "../birthdateRow";
export default function LoginForm() {
const { session } = useSession();
const { webId } = session.info;
const [editing, setEditing] = useState(false);
return (
<Container fixed>
<CombinedDataProvider datasetUrl={webId} thingUrl={webId}>
<Card style={{ maxWidth: 480 }}>
<CardActionArea
style={{
justifyContent: "center",
display: "flex",
}}
>
<Image property={VCARD.hasPhoto.iri.value} width={480} />
</CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
<Text property={FOAF.name.iri.value} edit={editing} autosave />
</Typography>
<Typography
variant="body2"
color="textSecondary"
component="p"
style={{
display: "flex",
alignItems: "center",
}}
>
<BusinessIcon />
<Text
property={VCARD.organization_name.iri.value}
edit={editing}
autosave
/>
</Typography>
<Typography
variant="body2"
color="textSecondary"
component="p"
style={{
display: "flex",
alignItems: "baseline",
}}
>
<label htmlFor="birthdate-input">Born: </label>
<BirthdateRow edit={editing} setEdit={setEditing} />
</Typography>
</CardContent>
<CardContent>
<Typography gutterBottom variant="h6" component="h3">
Email Addresses
</Typography>
<ContactTable property={VCARD.hasEmail.value} edit={editing} />
</CardContent>
<CardContent>
<Typography gutterBottom variant="h6" component="h3">
Phone Numbers
</Typography>
<ContactTable property={VCARD.hasTelephone.value} edit={editing} />
</CardContent>
<CardActions>
<Button
size="small"
color="primary"
onClick={() => setEditing(!editing)}
>
Toggle Edit
</Button>
</CardActions>
</Card>
</CombinedDataProvider>
</Container>
);
}
(Source: components/profile/index.jsx)
Additional Information#
You can view the source on GitHub.