-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from podium-lib/podium-store-bridge-browser
docs: the bridge, browser, and store that's WIP
- Loading branch information
Showing
8 changed files
with
501 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
--- | ||
title: "@podium/bridge" | ||
--- | ||
|
||
This package is a bridge designed to pass [JSON-RPC 2.0](https://www.jsonrpc.org/specification) messages between a web application and a native web view. | ||
|
||
## Usage | ||
|
||
To install: | ||
|
||
```sh | ||
npm install @podium/bridge | ||
``` | ||
|
||
Import the bridge in your client-side bundle: | ||
|
||
```js | ||
import "@podium/bridge"; | ||
``` | ||
|
||
You should probably send messages via [@podium/browser]. That said, the bridge is available on `window['@podium'].bridge`. | ||
|
||
```js | ||
/** @type {import("@podium/bridge").PodiumBridge} */ | ||
const bridge = window["@podium"].bridge; | ||
|
||
// You can listen for incoming messages, which can either be RpcRequest or RpcResponse | ||
bridge.on("global/authentication", (message) => { | ||
const request = | ||
/** @type {import("@podium/bridge").RpcRequest<{ token?: string }>} */ ( | ||
message | ||
); | ||
|
||
if (typeof request.token === "string") { | ||
// logged in | ||
} else { | ||
// logged out | ||
} | ||
}); | ||
|
||
// You can trigger notifications (one-way messages) | ||
bridge.notification({ | ||
method: "global/authentication", | ||
params: { token: null }, | ||
}); | ||
|
||
// And you can call methods and await the response | ||
/** @type {import("@podium/bridge").RpcResponse<{ c: string }>} */ | ||
const response = await bridge.call({ | ||
method: "document/native-feature", | ||
params: { a: "foo", b: "bar" }, | ||
}); | ||
``` | ||
|
||
## API | ||
|
||
### `bridge.on` | ||
|
||
Add a listener for incoming messages for a given method name. | ||
|
||
```js | ||
import "@podium/bridge"; | ||
|
||
/** @type {import("@podium/bridge").PodiumBridge} */ | ||
const bridge = window["@podium"].bridge; | ||
|
||
bridge.on("global/authentication", (message) => { | ||
const request = | ||
/** @type {import("@podium/bridge").RpcRequest<{ token?: string }>} */ ( | ||
message | ||
); | ||
|
||
if (typeof request.token === "string") { | ||
// logged in | ||
} else { | ||
// logged out | ||
} | ||
}); | ||
``` | ||
|
||
### `bridge.notification` | ||
|
||
Send a [notification](https://www.jsonrpc.org/specification#notification) (one-way message). | ||
|
||
```js | ||
import "@podium/bridge"; | ||
|
||
/** @type {import("@podium/bridge").PodiumBridge} */ | ||
const bridge = window["@podium"].bridge; | ||
|
||
bridge.notification({ | ||
method: "global/authentication", | ||
params: { token: null }, | ||
}); | ||
``` | ||
|
||
### `bridge.call` | ||
|
||
Send a [request](https://www.jsonrpc.org/specification#request_object) and await a [response](https://www.jsonrpc.org/specification#response_object). | ||
|
||
```js | ||
import "@podium/bridge"; | ||
|
||
/** @type {import("@podium/bridge").PodiumBridge} */ | ||
const bridge = window["@podium"].bridge; | ||
|
||
/** @type {import("@podium/bridge").RpcResponse<{ c: string }>} */ | ||
const response = await bridge.call({ | ||
method: "document/native-feature", | ||
params: { a: "foo", b: "bar" }, | ||
}); | ||
``` | ||
|
||
[@podium/browser]: /docs/api/browser | ||
[@podium/store]: /docs/api/store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
--- | ||
title: "@podium/store" | ||
--- | ||
|
||
This is a client-side library that provides a reactive data store using [nanostores](https://github.com/stores/stores) on top of [@podium/browser](https://github.com/podium-lib/browser)'s `MessageBus`. It includes some ready-made stores and helpers for you to make reactive stores for your own events. | ||
|
||
By using reactive state backed by `MessageBus` you can seamlessly share state between different parts of a Podium application. If a podlet changes the value of shared state, all other podlets using that value can update. | ||
|
||
<!-- TODO: look into an existing nanostores plugin so we make something that fits the ecosystem --> | ||
|
||
## Usage | ||
|
||
To install: | ||
|
||
```sh | ||
npm install @podium/store | ||
``` | ||
|
||
Use an [included store](#included-stores) in your client-side application: | ||
|
||
```js | ||
// store/user.js | ||
import { $authentication } from "@podium/store"; | ||
import { computed } from "nanostores"; | ||
|
||
// You can optionally make a computed value based on the included store | ||
export const $loggedIn = computed($authentication, (authentication) => | ||
Boolean(authentication.token) | ||
); | ||
|
||
// Colocating actions with the store makes it easier to | ||
// see what can trigger updates. | ||
export function logIn(token) { | ||
$authentication.set({ token }); | ||
} | ||
|
||
export function logOut() { | ||
$authentication.set({ token: null }); | ||
} | ||
``` | ||
|
||
Use the reactive store to do minimal updates of your UI when state changes. Nanostores supports multiple view frameworks: | ||
|
||
- [React and Preact](https://github.com/stores/stores?tab=readme-ov-file#react--preact) | ||
- [Lit](https://github.com/stores/stores?tab=readme-ov-file#lit) | ||
- [Vue](https://github.com/stores/stores?tab=readme-ov-file#vue) | ||
- [Vanilla JS](https://github.com/stores/stores?tab=readme-ov-file#vanilla-js) | ||
|
||
This example shows a React component: | ||
|
||
```js | ||
// components/user.jsx | ||
import { useStore } from "@nanostores/react"; | ||
import { $loggedIn } from "../stores/user.js"; | ||
|
||
export const User = () => { | ||
const loggedIn = useStore($loggedIn); | ||
return <p>{loggedIn ? "Welcome!" : "Please log in"}</p>; | ||
}; | ||
``` | ||
|
||
This is the same component in Lit: | ||
|
||
```js | ||
// components/user.js | ||
import { StoreController } from "@nanostores/lit"; | ||
import { $loggedIn } from "../stores/user.js"; | ||
|
||
class User extends LitElement { | ||
loggedInController = new StoreController(this, $loggedIn); | ||
|
||
render() { | ||
return html`<p> | ||
${this.loggedInController.value ? "Welcome!" : "Please log in"} | ||
</p>`; | ||
} | ||
} | ||
|
||
customElements.define("a-user", User); | ||
``` | ||
|
||
### Create your own reactive state | ||
|
||
By using the [included helper](#mapchannel-topic-initialvalue) you can make your reactive state sync between the different parts of a Podium application. | ||
|
||
## API | ||
|
||
### `$authentication` | ||
|
||
Type: [`map`](https://github.com/stores/stores?tab=readme-ov-file#maps) | ||
|
||
```js | ||
import { $authentication } from "@podium/store"; | ||
``` | ||
|
||
### `atom(channel, topic, initialValue)` | ||
|
||
Create your own [`atom`](https://github.com/nanostores/nanostores?tab=readme-ov-file#atoms) that syncs between parts of a Podium application using the [MessageBus](https://github.com/podium-lib/browser). | ||
|
||
This method requires the following arguments: | ||
|
||
| option | type | details | | ||
| ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| channel | `string` | Name of the channel | | ||
| topic | `string` | Name of the topic | | ||
| payload | `object` | The initial value. Replaced if [`peek(channel, topic)`](https://github.com/podium-lib/browser?tab=readme-ov-file#peekchannel-topic) returns a value. | | ||
|
||
```js | ||
import { atom } from "@podium/store"; | ||
|
||
const $reminders = atom("reminders", "list", []); | ||
``` | ||
|
||
### `map(channel, topic, initialValue)` | ||
|
||
Create your own [`map`](https://github.com/nanostores/nanostores?tab=readme-ov-file#maps) that syncs between parts of a Podium application using the [MessageBus](https://github.com/podium-lib/browser). | ||
|
||
This method requires the following arguments: | ||
|
||
| option | type | details | | ||
| ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| channel | `string` | Name of the channel | | ||
| topic | `string` | Name of the topic | | ||
| payload | `object` | The initial value. Replaced if [`peek(channel, topic)`](https://github.com/podium-lib/browser?tab=readme-ov-file#peekchannel-topic) returns a value. | | ||
|
||
```js | ||
import { map } from "@podium/store"; | ||
|
||
const $user = map("user", "profile", { displayName: "foobar" }); | ||
``` | ||
|
||
### `deepMap(channel, topic, initialValue)` | ||
|
||
Create your own [`deepMap`](https://github.com/nanostores/nanostores?tab=readme-ov-file#deep-maps) that syncs between parts of a Podium application using the [MessageBus](https://github.com/podium-lib/browser). | ||
|
||
This method requires the following arguments: | ||
|
||
| option | type | details | | ||
| ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| channel | `string` | Name of the channel | | ||
| topic | `string` | Name of the topic | | ||
| payload | `object` | The initial value. Replaced if [`peek(channel, topic)`](https://github.com/podium-lib/browser?tab=readme-ov-file#peekchannel-topic) returns a value. | | ||
|
||
```js | ||
import { deepMap, listenKeys } from "@podium/store"; | ||
|
||
export const $profile = deepMap({ | ||
hobbies: [ | ||
{ | ||
name: "woodworking", | ||
friends: [{ id: 123, name: "Ron Swanson" }], | ||
}, | ||
], | ||
skills: [["Carpentry", "Sanding"], ["Varnishing"]], | ||
}); | ||
|
||
listenKeys($profile, ["hobbies[0].friends[0].name", "skills[0][0]"]); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.