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,

  1. 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.

  2. Go to your project directory, substituting your project name for <app-name> (e.g., my-app, etc.):

    cd <app-name>
    
  3. Optional. Next.js collects anonymous telemetry data. To opt out, run the following in the root of your project directory

    npx next telemetry disable
    
  4. After you have set up the project, go to the project directory and run either:

    npm ci
    npm run dev
    

    or

    yarn dev
    
  5. 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 the autosave 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:&nbsp;</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.