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. You can play with the application at https://react-demo.docs.inrupt.com/.
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-example-project
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";
/* eslint react/jsx-props-no-spreading: 0 */
interface IApp {
Component: React.ComponentType<any>;
pageProps: any;
}
export default function App(props: IApp): React.ReactElement {
const { Component, pageProps } = props;
return (
<SessionProvider sessionId="react-sdk-example-project">
<Component {...pageProps} />
</SessionProvider>
);
}
(Source: pages/_app.tsx)
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/dist";
import LoginForm from "../components/loginForm";
import Profile from "../components/profile";
export default function Home(): React.ReactElement {
const { session } = useSession();
if (!session.info.isLoggedIn) {
return <LoginForm />;
}
(Source: pages/index.tsx)
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
:
import { useState, useEffect } from "react";
import { LoginButton } from "@inrupt/solid-ui-react";
import { Button, TextField, FormGroup, Container } from "@material-ui/core";
export default function LoginForm(): React.ReactElement {
const [idp, setIdp] = useState("https://inrupt.net");
const [currentUrl, setCurrentUrl] = useState("https://localhost:3000");
useEffect(() => {
setCurrentUrl(window.location.href);
}, [setCurrentUrl]);
return (
<Container fixed>
<FormGroup>
<TextField
label="Identity Provider"
placeholder="Identity Provider"
type="url"
value={idp}
onChange={(e) => setIdp(e.target.value)}
InputProps={{
endAdornment: (
<LoginButton oidcIssuer={idp} redirectUrl={currentUrl}>
<Button variant="contained" color="primary">
Log in
</Button>
</LoginButton>
),
}}
/>
</FormGroup>
</Container>
);
}
(Source: components/loginForm/index.tsx)
You can also specify an optional onError
attributes for the
LoginButton.
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,
LogoutButton,
Text,
Value,
} from "@inrupt/solid-ui-react";
import {
Box,
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";
export default function LoginForm(): React.ReactElement {
const { session } = useSession();
const { webId } = session.info;
const [editing, setEditing] = useState(false);
return (
<Container fixed>
<Box style={{ marginBottom: 16, textAlign: "right" }}>
<LogoutButton>
<Button variant="contained" color="primary">
Log out
</Button>
</LogoutButton>
</Box>
<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">
{"Born: "}
<Value
property={VCARD.bday.iri.value}
dataType="datetime"
edit={editing}
autosave
/>
</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.tsx)