jazz signals
A library to help use Jazz with preact signals.
install
npm i -S @nichoth/jazz-signals
develop
Start a local vite server
npm start
API
localAuth
Import a function localAuth
that helps with authentication, and has a property createState
that returns observable state. This will create and return a signal of a localNode
, the object used for persistence/telepathicState
.
import { localAuth } from '@nichoth/jazz-signals'
localAuth.createState
Create a state object that includes signals.
export interface LocalAuthState {
authStatus:Signal<AuthStatus>;
localNode:Signal<LocalNode|null>;
logoutCount:Signal<number>;
syncAddress?:string;
}
localAuth.createState = function ():LocalAuthState
The returned state object should be passed into the localAuth
function. See an example in the example app.
The signals returned are plain signals; you would want to wrap them in a call to useMemo
if you use them in a view component.
function localAuth (
appName:string,
appHostname:string|undefined,
opts:LocalAuthState
):() => void
This will create a new BrowserLocalAuth, and mutate the signals passed in as opts:LocalAuthState
. The return value is a function that will unsubscribe from BrowserLocalAuth
. See the example for a demonstration of how the unsubscribe function can be used.
To check if you are logged in, look for the authStatus.value.logout
property. If .logout
exists, then you are logged in. Call authStatus.value.signUp
or authStatus.value.signIn
to handle creating an account and logging in, respectively. See an example of handling auth.
telepathicSignal
function telepathicSignal<T extends CoValueImpl> ({
id,
localNode
}:{
id?:CoID<T>,
localNode:Signal<LocalNode|null>
}):Signal<[ T|null, (()=>void)|null ]> {
example
localAuth
An example of an application that consumes this package is in the example directory.
Create a localNode by mutating the signals that are passed in. Signals are created by localNode.createState
.
You should create a state object first with localAuth.createState
, then pass the state to localAuth
.
import { localAuth } from '@nichoth/jazz-signals'
import { useEffect, useMemo } from 'preact/hooks'
function MyComponent ({ appHostName, syncAddress, appName }) {
const state = useMemo(() => localAuth.createState(), [])
const { localNode } = state // <-- a signal for our localNode
/**
* Handle auth, create a node
*/
useEffect(() => {
let unlisten:()=>void = () => null
localAuth(appName, appHostName, { ...state }).then(_unlisten => {
unlisten = _unlisten
})
return unlisten
}, [appName, appHostName, syncAddress, logoutCount.value])
}
telepathicSignal
Create a new signal that is subscribed to any changes from the cojson
object referenced by the given id
.
import { telepathicSignal } from '@nichoth/jazz-signals'
const mySignal = telepathicSignal({
id: 'co_zPLDBXZD5UuZtYGzpqAvgAAHhs4',
localNode
})
import { telepathicSignal } from '@nichoth/jazz-signals'
import { useMemo } from 'preact/hooks'
function Component () {
const projectSignal = useMemo(() => {
return telepathicSignal({
// get the `id` from the URL or something
id: 'co_zPLDBXZD5UuZtYGzpqAvgAAHhs4',
localNode // <-- here we consume the localNode we created earlier
})
}, [params.id, localNode.value])
const [project] = projectSignal.value
// get tasks (the list of things to do)
// this is where we subscribe to task changes
const tasksSignal = useMemo(() => {
if (!project) return signal([])
// we depend on the 'tasks' key existing.
const tasksId = project.get('tasks')
return telepathicSignal({ id: tasksId, localNode })
}, [project, localNode.value])
const [tasks] = tasksSignal.value
return (<div>
<h2>{project.get('title')}</h2>
<ul className="todo-list">
{tasks?.map((taskId: CoID<Task>) => {
// subscribe to each TODO list item
const [task] = useMemo(
() => telepathicSignal<Task>({ id: taskId, localNode }),
[taskId, localNode.value]
).value
// The view will re-render when the task updates.
// This is magically in sync with multiple devices.
// You can create an invitation for a second device, and changes
// will automatically be visible in both places.
return (<li key={taskId}>
{task?.get('done') ?
(<s>{task.get('text')}</s>) :
(<span>{task.get('text')}</span>)
}
</li>)
})}
</ul>
</div>)
}