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,

  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-example-project
      

      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";

/* 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&nbsp;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 the autosave 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&nbsp;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)

Additional Information

You can view the source on GitHub.