Skip to content

Latest commit

 

History

History
234 lines (177 loc) · 6.02 KB

README.md

File metadata and controls

234 lines (177 loc) · 6.02 KB

react-earthstar

What is this?

A library of React hooks and components for usage with Earthstar, storage for private, distributed, offline-first application.

This library is just a few small hooks for subscribing to some events in Earthstar, with the same API for querying and writing data.

It's recommended that you check out Earthstar's README before reading on if you haven't already.

Getting started

First, install react-earthstar and earthstar as dependencies to your project:

npm install earthstar react-earthstar

Here's a basic applet which creates a IndexedDB replica for a +gardening share, and renders a list of notes using useReplica.

import * as Earthstar from "earthstar";
import { useReplica } from 'react-earthstar';

// You get the share address and secret from someone else,
// Or using Earthstar.Crypto.generateShareKeypair()

const MY_SHARE_ADDRESS =
  "+gardening.bhyux4opeug2ieqcy36exrf4qymc56adwll4zeazm42oamxtr7heq";

const MY_SHARE_SECRET = "buaqth6jr5wkksnhdlpfi64cqcnjzfx3r6cssnfqdvitjmfygsk3q";

const replica = new Earthstar.Replica({
  driver: new ReplicaDriverWeb(MY_SHARE_ADDRESS),
  shareSecret: MY_SHARE_SECRET,
});

function App() {
  const cache = useReplica(replica);

  // This will update whenever a document with a path starting with '/notes' updates.
  const notes = cache.queryDocs({
    filter: {
      pathStartsWith: "/notes",
      author: 
    },
  });

  return (
    <div>
      <h1>My notes</h1>
      { notes.map((noteDoc) => 
        <li>noteDoc.text</li>
      )}
      
    </div>
  );
}

Hooks

useReplica

This is the workhorse of the library, used for querying documents from replicas.

const replica = useReplica(myReplica);

const txtDocs = replica.queryDocs({ filter: { pathEndsWith: ".txt" } });

This hook returns an instance of Earthstar's ReplicaCache class, with the exact same API as Replica except synchronous.

When you query docs the replica keeps track of what was queried for, and only updates React when the results of those queries are updated. This results in a best-of-both-worlds API where we can have the same API as the vanilla Earthstar library, and components which only re-render when they need to. Nice!

usePeerReplicas

Returns an updating array of Replica for a given Peer.

function ReplicaList() {
  const replicas = usePeerReplicas(myPeer);

  return (
    <div>
      <h1>My replicas</h1>
      <ul>
        {replicas.map((replica) => <li>{replica.share}</li>)}
      </ul>
    </div>
  );
}

useAuthorSettings

Returns a getter and setter for the SharedSettings' current author setting. Uses the SharedSettingsContext.

function CurrentAuthorAddress() {
  const [author, setAuthor] = useAuthorSettings();

  return <div>{author.address}</div>;
}

useShareSettings

Returns the shares + a callback to add a share + a callback to remove a share from SharedSettings' shares setting. Uses the SharedSettingsContext.

function KnownShares() {
  const [shares, addShare, removeShare] = useAuthorSettings();

  return (
    <div>
      <h1>My shares</h1>
      <ul>
        {shares.map((share) => <li>{share}</li>)}
      </ul>
    </div>
  );
}

useShareSecretSettings

Returns the secrets + a callback to add a secret + a callback to remove a secret from SharedSettings' share secrets setting. Uses the SharedSettingsContext.

function KnownSecrets() {
  const [secrets, addSecret, removeSecret] = useAuthorSettings();

  return (
    <div>
      <h1>Known secrets...p</h1>
      <ul>
        {Object.keys(secrets).map((share) => (
          <li>{share} {secrets["share"] ? "✓" : "???"}</li>
        ))}
      </ul>
    </div>
  );
}

useServerSettings

Returns the server + a callback to add a server + a callback to remove a server from SharedSettings' servers setting. Uses the SharedSettingsContext.

function KnownShares() {
  const [servers, addServer, removeServer] = useAuthorSettings();

  return (
    <div>
      <h1>My servers</h1>
      <ul>
        {servers.map((share) => <li>{server}</li>)}
      </ul>
    </div>
  );
}

Components

<SharedSettingsContext.Provider>

If you're going to use any of the settings hooks, you should provide a single instance of SharedSettings as a context for them:

const settings = new Earthstar.SharedSettings();

function App() {
  return (
    <SharedSettings.Provider value={settings}>
      // Components which use useAuthorSettings, useShareSettings etc.
      <TheRestOfTheApp />
    </SharedSettings.Provider>
  );
}

<ShareLabel>

Renders the human readable portion of a share address, and omits the public key: e.g. '+gardening.bhyux4opeug2ieqcy36exrf4qymc56adwll4zeazm42oamxtr7heq' becomes '+gardening'. Also renders an identicon next to the address to make it harder for shares to be mistaken for one another. Good for making sure you don't disclose share addresses to people looking over your users' shoulders.

<ShareLabel address="+potatoes.b34ou9e8">

Also takes an iconSize prop (side size in px), and a viewingAuthorSecret prop. The latter prop is used as a salt for generating the identicon, making it so that each user has their own identicon for each share, making them harder for an attacker to imitate. If available, pass the current user's keypair secret to this prop.

<IdentityLabel>

Renders the shortname portion of an identity's address, omitting the public key, e.g. @cinn.euu8euheuigoe... just becomes @cinn.

<IdentityLabel address="@devy.a234gue9Juhxo9eu...">

Also takes an iconSize prop (side size in px), and a viewingAuthorSecret prop. The latter prop is used as a salt for generating the identicon, making it so that each user has their own identicon for each share, making them harder for an attacker to imitate. If available, pass the current user's keypair secret to this prop.