@seamapi/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +20 -0
- package/README.md +157 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/lib/components/DeviceManager/DeviceManager.d.ts +7 -0
- package/lib/components/DeviceManager/DeviceManager.js +24 -0
- package/lib/components/DeviceManager/DeviceManager.js.map +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +2 -0
- package/lib/components/index.js.map +1 -0
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.js +2 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/use-devices.d.ts +10 -0
- package/lib/hooks/use-devices.js +17 -0
- package/lib/hooks/use-devices.js.map +1 -0
- package/lib/hooks/use-seam.d.ts +7 -0
- package/lib/hooks/use-seam.js +62 -0
- package/lib/hooks/use-seam.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -0
- package/lib/provider.d.ts +20 -0
- package/lib/provider.js +53 -0
- package/lib/provider.js.map +1 -0
- package/package.json +116 -0
- package/src/index.ts +1 -0
- package/src/lib/components/DeviceManager/DeviceManager.tsx +45 -0
- package/src/lib/components/index.ts +1 -0
- package/src/lib/hooks/index.ts +1 -0
- package/src/lib/hooks/use-devices.ts +31 -0
- package/src/lib/hooks/use-seam.ts +88 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/provider.tsx +102 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-2023 Seam Labs, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Seam React Components
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@seamapi/react)
|
|
4
|
+
[](https://github.com/seamapi/react/actions/workflows/check.yml)
|
|
5
|
+
|
|
6
|
+
Seam React component library.
|
|
7
|
+
|
|
8
|
+
## Description
|
|
9
|
+
|
|
10
|
+
Zero-backend beautiful drop-in pre-built components with everything you need to get started with Seam.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Add this as a dependency to your project using [npm] with
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
$ npm install @seamapi/react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
[npm]: https://www.npmjs.com/
|
|
21
|
+
|
|
22
|
+
## Development and Testing
|
|
23
|
+
|
|
24
|
+
### Quickstart
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ git clone https://github.com/seamapi/react.git
|
|
28
|
+
$ cd react
|
|
29
|
+
$ nvm install
|
|
30
|
+
$ npm install
|
|
31
|
+
$ npm run test:watch
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Primary development tasks are defined under `scripts` in `package.json`
|
|
35
|
+
and available via `npm run`.
|
|
36
|
+
View them with
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
$ npm run
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Source code
|
|
43
|
+
|
|
44
|
+
The [source code] is hosted on GitHub.
|
|
45
|
+
Clone the project with
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
$ git clone git@github.com:seamapi/react.git
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
[source code]: https://github.com/seamapi/react
|
|
52
|
+
|
|
53
|
+
### Requirements
|
|
54
|
+
|
|
55
|
+
You will need [Node.js] with [npm] and a [Node.js debugging] client.
|
|
56
|
+
|
|
57
|
+
Be sure that all commands run under the correct Node version, e.g.,
|
|
58
|
+
if using [nvm], install the correct version with
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
$ nvm install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Set the active version for each shell session with
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
$ nvm use
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Install the development dependencies with
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
$ npm install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
[Node.js]: https://nodejs.org/
|
|
77
|
+
[Node.js debugging]: https://nodejs.org/en/docs/guides/debugging-getting-started/
|
|
78
|
+
[npm]: https://www.npmjs.com/
|
|
79
|
+
[nvm]: https://github.com/creationix/nvm
|
|
80
|
+
|
|
81
|
+
### Publishing
|
|
82
|
+
|
|
83
|
+
#### Automatic
|
|
84
|
+
|
|
85
|
+
New versions are released automatically with [semantic-release].
|
|
86
|
+
as long as commits follow the [Angular Commit Message Conventions].
|
|
87
|
+
|
|
88
|
+
[Angular Commit Message Conventions]: https://semantic-release.gitbook.io/semantic-release/#commit-message-format
|
|
89
|
+
[semantic-release]: https://semantic-release.gitbook.io/
|
|
90
|
+
|
|
91
|
+
#### Manual
|
|
92
|
+
|
|
93
|
+
Publish a new version by triggering a [version workflow_dispatch on GitHub Actions].
|
|
94
|
+
The `version` input will be passed as the first argument to [npm-version].
|
|
95
|
+
|
|
96
|
+
This may be done on the web or using the [GitHub CLI] with
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
$ gh workflow run version.yml --raw-field version=<version>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
[GitHub CLI]: https://cli.github.com/
|
|
103
|
+
[npm-version]: https://docs.npmjs.com/cli/version
|
|
104
|
+
[version workflow_dispatch on GitHub Actions]: https://github.com/seamapi/react/actions?query=workflow%3Aversion
|
|
105
|
+
|
|
106
|
+
## GitHub Actions
|
|
107
|
+
|
|
108
|
+
_GitHub Actions should already be configured: this section is for reference only._
|
|
109
|
+
|
|
110
|
+
The following repository secrets must be set on [GitHub Actions]:
|
|
111
|
+
|
|
112
|
+
- `NPM_TOKEN`: npm token for installing and publishing packages.
|
|
113
|
+
- `GH_TOKEN`: A personal access token for the bot user with
|
|
114
|
+
`packages:write` and `contents:write` permission.
|
|
115
|
+
- `GIT_USER_NAME`: The GitHub bot user's real name.
|
|
116
|
+
- `GIT_USER_EMAIL`: The GitHub bot user's email.
|
|
117
|
+
- `GPG_PRIVATE_KEY`: The GitHub bot user's [GPG private key].
|
|
118
|
+
- `GPG_PASSPHRASE`: The GitHub bot user's GPG passphrase.
|
|
119
|
+
|
|
120
|
+
[GitHub Actions]: https://github.com/features/actions
|
|
121
|
+
[GPG private key]: https://github.com/marketplace/actions/import-gpg#prerequisites
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
> If using squash merge, edit and ensure the commit message follows the [Angular Commit Message Conventions] specification.
|
|
126
|
+
> Otherwise, each individual commit must follow the [Angular Commit Message Conventions] specification.
|
|
127
|
+
|
|
128
|
+
1. Create your feature branch (`git checkout -b my-new-feature`).
|
|
129
|
+
2. Make changes.
|
|
130
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
131
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
|
132
|
+
5. Create a new draft pull request.
|
|
133
|
+
6. Ensure all checks pass.
|
|
134
|
+
7. Mark your pull request ready for review.
|
|
135
|
+
8. Wait for the required approval from the code owners.
|
|
136
|
+
9. Merge when ready.
|
|
137
|
+
|
|
138
|
+
[Angular Commit Message Conventions]: https://semantic-release.gitbook.io/semantic-release/#commit-message-format
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
This npm package is licensed under the [MIT license].
|
|
143
|
+
|
|
144
|
+
[MIT license]: https://github.com/seamapi/react/blob/main/LICENSE.txt
|
|
145
|
+
|
|
146
|
+
## Warranty
|
|
147
|
+
|
|
148
|
+
This software is provided by the copyright holders and contributors "as is" and
|
|
149
|
+
any express or implied warranties, including, but not limited to, the implied
|
|
150
|
+
warranties of merchantability and fitness for a particular purpose are
|
|
151
|
+
disclaimed. In no event shall the copyright holder or contributors be liable for
|
|
152
|
+
any direct, indirect, incidental, special, exemplary, or consequential damages
|
|
153
|
+
(including, but not limited to, procurement of substitute goods or services;
|
|
154
|
+
loss of use, data, or profits; or business interruption) however caused and on
|
|
155
|
+
any theory of liability, whether in contract, strict liability, or tort
|
|
156
|
+
(including negligence or otherwise) arising in any way out of the use of this
|
|
157
|
+
software, even if advised of the possibility of such damage.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/index.js';
|
package/index.js
ADDED
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type UseDevicesParams } from '../../../lib/hooks/use-devices.js';
|
|
3
|
+
export type DeviceManagerProps = UseDevicesParams;
|
|
4
|
+
/**
|
|
5
|
+
* Fetch, list, and manage devices.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DeviceManager: (props: DeviceManagerProps) => ReactElement;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import {} from 'react';
|
|
3
|
+
import {} from 'seamapi';
|
|
4
|
+
import { useDevices } from '../../../lib/hooks/use-devices.js';
|
|
5
|
+
/**
|
|
6
|
+
* Fetch, list, and manage devices.
|
|
7
|
+
*/
|
|
8
|
+
export const DeviceManager = (props) => {
|
|
9
|
+
const { devices, isLoading, isError, error } = useDevices(props);
|
|
10
|
+
if (isLoading)
|
|
11
|
+
return _jsx("p", { role: 'loading', children: t.loading });
|
|
12
|
+
if (isError)
|
|
13
|
+
return _jsx("p", { children: error?.message });
|
|
14
|
+
return (_jsx("ul", { children: devices?.map((device) => (_jsx(DeviceManagerItem, { ...device }, device.device_id))) }));
|
|
15
|
+
};
|
|
16
|
+
const DeviceManagerItem = (props) => {
|
|
17
|
+
return (_jsxs("li", { children: [_jsxs("p", { children: [t.device_id, ": ", props.device_id] }), _jsxs("p", { children: [t.name, ": ", props.properties.name] })] }));
|
|
18
|
+
};
|
|
19
|
+
const t = {
|
|
20
|
+
loading: 'Loading devices',
|
|
21
|
+
device_id: 'Device ID',
|
|
22
|
+
name: 'Device name',
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=DeviceManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeviceManager.js","sourceRoot":"","sources":["../../../src/lib/components/DeviceManager/DeviceManager.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAqB,MAAM,OAAO,CAAA;AACzC,OAAO,EAA4C,MAAM,SAAS,CAAA;AAElE,OAAO,EAAE,UAAU,EAAyB,MAAM,0BAA0B,CAAA;AAI5E;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAyB,EAAgB,EAAE;IACvE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAEhE,IAAI,SAAS;QAAE,OAAO,YAAG,IAAI,EAAC,SAAS,YAAE,CAAC,CAAC,OAAO,GAAK,CAAA;IACvD,IAAI,OAAO;QAAE,OAAO,sBAAI,KAAK,EAAE,OAAO,GAAK,CAAA;IAE3C,OAAO,CACL,uBACG,OAAO,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACxB,KAAC,iBAAiB,OAA4B,MAAM,IAA5B,MAAM,CAAC,SAAS,CAAgB,CACzD,CAAC,GACC,CACN,CAAA;AACH,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,CACxB,KAAqC,EACvB,EAAE;IAChB,OAAO,CACL,yBACE,wBACG,CAAC,CAAC,SAAS,QAAI,KAAK,CAAC,SAAS,IAC7B,EACJ,wBACG,CAAC,CAAC,IAAI,QAAI,KAAK,CAAC,UAAU,CAAC,IAAI,IAC9B,IACD,CACN,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,GAAG;IACR,OAAO,EAAE,iBAAiB;IAC1B,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,aAAa;CACpB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DeviceManager/DeviceManager.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-seam.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { type DevicesListRequest, type DevicesListResponse, type SeamError } from 'seamapi';
|
|
3
|
+
export type UseDevicesParams = DevicesListRequest;
|
|
4
|
+
export type UseDevicesData = DevicesListResponse['devices'];
|
|
5
|
+
type Result = UseQueryResult<UseDevicesData, SeamError>;
|
|
6
|
+
type UseDevicesResult = Omit<Result, 'data'> & {
|
|
7
|
+
devices: Result['data'];
|
|
8
|
+
};
|
|
9
|
+
export declare function useDevices(params: DevicesListRequest): UseDevicesResult;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import {} from 'seamapi';
|
|
3
|
+
import { useSeam } from './use-seam.js';
|
|
4
|
+
export function useDevices(params) {
|
|
5
|
+
const { client } = useSeam();
|
|
6
|
+
const { data, ...rest } = useQuery({
|
|
7
|
+
enabled: client != null,
|
|
8
|
+
queryKey: ['list', 'devices', params],
|
|
9
|
+
queryFn: async () => {
|
|
10
|
+
if (client == null)
|
|
11
|
+
return [];
|
|
12
|
+
return await client?.devices.list(params);
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
return { ...rest, devices: data };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=use-devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-devices.js","sourceRoot":"","sources":["../../src/lib/hooks/use-devices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAIN,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AASvC,MAAM,UAAU,UAAU,CAAC,MAA0B;IACnD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,CAChC;QACE,OAAO,EAAE,MAAM,IAAI,IAAI;QACvB,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC;QACrC,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,IAAI,MAAM,IAAI,IAAI;gBAAE,OAAO,EAAE,CAAA;YAC7B,OAAO,MAAM,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC3C,CAAC;KACF,CACF,CAAA;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AACnC,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { Seam } from 'seamapi';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { seamContext } from '../../lib/provider.js';
|
|
6
|
+
export function useSeam() {
|
|
7
|
+
const { client, clientOptions, publishableKey, ...context } = useContext(seamContext);
|
|
8
|
+
const [clientSession, setClientSession] = useClientSession();
|
|
9
|
+
const userIdentifierKey = useUserIdentifierKey(context.userIdentifierKey);
|
|
10
|
+
const { isLoading, isError, error, data } = useQuery({
|
|
11
|
+
queryKey: ['client', { client, clientSession, userIdentifierKey }],
|
|
12
|
+
queryFn: async () => {
|
|
13
|
+
if (client != null)
|
|
14
|
+
return client;
|
|
15
|
+
if (publishableKey == null) {
|
|
16
|
+
throw new Error('Missing publishableKey');
|
|
17
|
+
}
|
|
18
|
+
// TODO: Check if client session is still valid before resuming it.
|
|
19
|
+
if (clientSession != null) {
|
|
20
|
+
return new Seam({
|
|
21
|
+
...clientOptions,
|
|
22
|
+
clientSessionToken: clientSession.token,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const res = await Seam.getClientSessionToken({
|
|
26
|
+
...clientOptions,
|
|
27
|
+
publishableKey,
|
|
28
|
+
userIdentifierKey,
|
|
29
|
+
});
|
|
30
|
+
if (!res.ok || res.client_session?.token == null) {
|
|
31
|
+
throw new Error('Failed to get client access token');
|
|
32
|
+
}
|
|
33
|
+
setClientSession(res.client_session);
|
|
34
|
+
return new Seam({
|
|
35
|
+
...clientOptions,
|
|
36
|
+
clientSessionToken: res.client_session.token,
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return { client: data ?? null, isLoading, isError, error };
|
|
41
|
+
}
|
|
42
|
+
function useClientSession() {
|
|
43
|
+
const localStorageKey = 'seam_client_session';
|
|
44
|
+
const setClientSession = (clientSession) => {
|
|
45
|
+
globalThis?.localStorage?.setItem(localStorageKey, JSON.stringify(clientSession));
|
|
46
|
+
};
|
|
47
|
+
const cachedClientSession = globalThis?.localStorage?.getItem(localStorageKey);
|
|
48
|
+
return [
|
|
49
|
+
cachedClientSession != null ? JSON.parse(cachedClientSession) : null,
|
|
50
|
+
setClientSession,
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
function useUserIdentifierKey(userIdentifierKey) {
|
|
54
|
+
if (userIdentifierKey != null) {
|
|
55
|
+
return userIdentifierKey;
|
|
56
|
+
}
|
|
57
|
+
const localStorageKey = 'seam_user_identifier_key';
|
|
58
|
+
const key = globalThis?.localStorage?.getItem(localStorageKey) ?? uuidv4();
|
|
59
|
+
globalThis?.localStorage?.setItem(localStorageKey, key);
|
|
60
|
+
return key;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=use-seam.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-seam.js","sourceRoot":"","sources":["../../src/lib/hooks/use-seam.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { type Seam, type SeamClientOptions } from 'seamapi';
|
|
3
|
+
declare global {
|
|
4
|
+
var seam: Omit<SeamProviderProps, 'children'> | undefined;
|
|
5
|
+
}
|
|
6
|
+
export interface SeamContext {
|
|
7
|
+
client: Seam | null;
|
|
8
|
+
clientOptions?: SeamClientOptions | undefined;
|
|
9
|
+
publishableKey?: string | undefined;
|
|
10
|
+
userIdentifierKey?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface SeamProviderProps {
|
|
13
|
+
client?: Seam;
|
|
14
|
+
publishableKey?: string;
|
|
15
|
+
userIdentifierKey?: string;
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare function SeamProvider({ children, ...props }: SeamProviderProps): ReactElement;
|
|
20
|
+
export declare const seamContext: import("react").Context<SeamContext>;
|
package/lib/provider.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
3
|
+
import { createContext, useRef } from 'react';
|
|
4
|
+
import {} from 'seamapi';
|
|
5
|
+
export function SeamProvider({ children, ...props }) {
|
|
6
|
+
const { Provider } = seamContext;
|
|
7
|
+
const queryClientRef = useRef(new QueryClient());
|
|
8
|
+
const contextRef = useRef(createSeamContextValue(props));
|
|
9
|
+
if (contextRef.current.client == null &&
|
|
10
|
+
contextRef.current.publishableKey == null) {
|
|
11
|
+
contextRef.current = defaultSeamContextValue;
|
|
12
|
+
}
|
|
13
|
+
if (contextRef.current.client == null &&
|
|
14
|
+
contextRef.current.publishableKey == null) {
|
|
15
|
+
throw new Error('Must provide either a Seam client or a publishableKey.');
|
|
16
|
+
}
|
|
17
|
+
return (_jsx(QueryClientProvider, { client: queryClientRef.current, children: _jsx(Provider, { value: { ...contextRef.current }, children: children }) }));
|
|
18
|
+
}
|
|
19
|
+
const createDefaultSeamContextValue = () => {
|
|
20
|
+
if (globalThis.seam?.client == null &&
|
|
21
|
+
globalThis.seam?.publishableKey == null) {
|
|
22
|
+
return { client: null };
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return createSeamContextValue(globalThis.seam);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.warn(err);
|
|
30
|
+
return { client: null };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const createSeamContextValue = ({ client, ...options }) => {
|
|
34
|
+
if (client == null && Object.keys(options).length === 0) {
|
|
35
|
+
return { client: null };
|
|
36
|
+
}
|
|
37
|
+
if (client != null && Object.values(options).some((v) => v == null)) {
|
|
38
|
+
throw new Error('Cannot provide both a Seam client along with other options.');
|
|
39
|
+
}
|
|
40
|
+
const { publishableKey, userIdentifierKey, ...clientOptions } = options;
|
|
41
|
+
if (client != null) {
|
|
42
|
+
return { client };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
client: null,
|
|
46
|
+
publishableKey,
|
|
47
|
+
userIdentifierKey,
|
|
48
|
+
clientOptions,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const defaultSeamContextValue = createDefaultSeamContextValue();
|
|
52
|
+
export const seamContext = createContext(defaultSeamContextValue);
|
|
53
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/lib/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,aAAa,EAAqC,MAAM,EAAE,MAAM,OAAO,CAAA;AAChF,OAAO,EAAqC,MAAM,SAAS,CAAA;AAsB3D,MAAM,UAAU,YAAY,CAAC,EAC3B,QAAQ,EACR,GAAG,KAAK,EACU;IAClB,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAA;IAEhC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,CAAA;IAEhD,MAAM,UAAU,GAAG,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAA;IACxD,IACE,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI;QACjC,UAAU,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,EACzC;QACA,UAAU,CAAC,OAAO,GAAG,uBAAuB,CAAA;KAC7C;IAED,IACE,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI;QACjC,UAAU,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,EACzC;QACA,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;KAC1E;IAED,OAAO,CACL,KAAC,mBAAmB,IAAC,MAAM,EAAE,cAAc,CAAC,OAAO,YACjD,KAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,YAAG,QAAQ,GAAY,GAC7C,CACvB,CAAA;AACH,CAAC;AAED,MAAM,6BAA6B,GAAG,GAAgB,EAAE;IACtD,IACE,UAAU,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI;QAC/B,UAAU,CAAC,IAAI,EAAE,cAAc,IAAI,IAAI,EACvC;QACA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;KACxB;IAED,IAAI;QACF,OAAO,sBAAsB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;KAC/C;IAAC,OAAO,GAAG,EAAE;QACZ,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;KACxB;AACH,CAAC,CAAA;AAED,MAAM,sBAAsB,GAAG,CAAC,EAC9B,MAAM,EACN,GAAG,OAAO,EAC0B,EAAe,EAAE;IACrD,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QACvD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;KACxB;IAED,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE;QACnE,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAA;KACF;IAED,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAA;IAEvE,IAAI,MAAM,IAAI,IAAI,EAAE;QAClB,OAAO,EAAE,MAAM,EAAE,CAAA;KAClB;IAED,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,cAAc;QACd,iBAAiB;QACjB,aAAa;KACd,CAAA;AACH,CAAC,CAAA;AAED,MAAM,uBAAuB,GAAG,6BAA6B,EAAE,CAAA;AAE/D,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAc,uBAAuB,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seamapi/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Seam React component library.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"typings": "index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": "./index.js"
|
|
10
|
+
},
|
|
11
|
+
"module": "index.js",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"keywords": [
|
|
14
|
+
"node"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/seamapi/react",
|
|
17
|
+
"bugs": "https://github.com/seamapi/react/issues",
|
|
18
|
+
"repository": "seamapi/react",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Seam Labs, Inc.",
|
|
22
|
+
"email": "devops@getseam.com"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"index.js",
|
|
26
|
+
"index.js.map",
|
|
27
|
+
"index.d.ts",
|
|
28
|
+
"lib",
|
|
29
|
+
"src",
|
|
30
|
+
"!test",
|
|
31
|
+
"!**/*.test.ts",
|
|
32
|
+
"!**/*.test.tsx",
|
|
33
|
+
"!**/*.stories.ts",
|
|
34
|
+
"!**/*.stories.tsx",
|
|
35
|
+
"!src/stories"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"start": "storybook dev --port 6006",
|
|
39
|
+
"build": "npm run build:ts",
|
|
40
|
+
"postbuild": "node ./index.js",
|
|
41
|
+
"build:ts": "tsc --project tsconfig.build.json",
|
|
42
|
+
"prebuild:ts": "del 'index.*' lib",
|
|
43
|
+
"postbuild:ts": "tsc-alias --project tsconfig.build.json",
|
|
44
|
+
"docs:build": "storybook build --docs --output-dir docs",
|
|
45
|
+
"docs:start": "storybook dev --docs --port 6007",
|
|
46
|
+
"typecheck": "tsc",
|
|
47
|
+
"dependencies": "dpdm --exit-code circular:1 src/index.ts",
|
|
48
|
+
"test": "jest --collect-coverage",
|
|
49
|
+
"test:update": "jest --updateSnapshot",
|
|
50
|
+
"test:watch": "jest --watch",
|
|
51
|
+
"test:inspect": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand",
|
|
52
|
+
"lint": "eslint --ignore-path .gitignore --ext .js,.cjs,.mjs,.ts,.tsx .",
|
|
53
|
+
"prelint": "prettier --check --ignore-path .gitignore .",
|
|
54
|
+
"postversion": "git push --follow-tags",
|
|
55
|
+
"example:basic": "vite --config examples/basic/vite.config.js",
|
|
56
|
+
"format": "eslint --ignore-path .gitignore --fix --ext .js,.cjs,.mjs,.ts,.tsx .",
|
|
57
|
+
"preformat": "prettier --write --ignore-path .gitignore ."
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=16.13.0",
|
|
61
|
+
"npm": ">= 8.1.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"react": ">=18.0.0",
|
|
65
|
+
"react-dom": ">=18.0.0",
|
|
66
|
+
"seamapi": ">=5.15.0-alpha.0"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@tanstack/react-query": "^4.28.0",
|
|
70
|
+
"uuid": "^9.0.0"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@babel/preset-env": "^7.21.4",
|
|
74
|
+
"@babel/preset-react": "^7.18.6",
|
|
75
|
+
"@babel/preset-typescript": "^7.21.4",
|
|
76
|
+
"@storybook/addon-essentials": "^7.0.2",
|
|
77
|
+
"@storybook/addon-interactions": "^7.0.2",
|
|
78
|
+
"@storybook/addon-links": "^7.0.2",
|
|
79
|
+
"@storybook/blocks": "^7.0.2",
|
|
80
|
+
"@storybook/react": "^7.0.2",
|
|
81
|
+
"@storybook/react-webpack5": "^7.0.2",
|
|
82
|
+
"@storybook/testing-library": "^0.1.0",
|
|
83
|
+
"@testing-library/react": "^14.0.0",
|
|
84
|
+
"@types/jest": "^29.5.0",
|
|
85
|
+
"@types/node": "^18.11.18",
|
|
86
|
+
"@types/react": "^18.0.33",
|
|
87
|
+
"@types/react-dom": "^18.0.11",
|
|
88
|
+
"@types/uuid": "^9.0.1",
|
|
89
|
+
"@vitejs/plugin-react": "^3.1.0",
|
|
90
|
+
"del-cli": "^5.0.0",
|
|
91
|
+
"dpdm": "^3.13.0",
|
|
92
|
+
"eslint": "^8.9.0",
|
|
93
|
+
"eslint-config-prettier": "^8.4.0",
|
|
94
|
+
"eslint-config-standard-jsx": "^11.0.0",
|
|
95
|
+
"eslint-config-standard-react": "^13.0.0",
|
|
96
|
+
"eslint-config-standard-with-typescript": "^34.0.0",
|
|
97
|
+
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
98
|
+
"eslint-plugin-storybook": "^0.6.11",
|
|
99
|
+
"eslint-plugin-unused-imports": "^2.0.0",
|
|
100
|
+
"http-proxy-middleware": "^2.0.6",
|
|
101
|
+
"jest": "^29.5.0",
|
|
102
|
+
"jest-environment-jsdom": "^29.5.0",
|
|
103
|
+
"prettier": "^2.3.0",
|
|
104
|
+
"prop-types": "^15.8.1",
|
|
105
|
+
"react": "^18.2.0",
|
|
106
|
+
"react-dom": "^18.2.0",
|
|
107
|
+
"seamapi": "^5.15.0-alpha.0",
|
|
108
|
+
"storybook": "^7.0.2",
|
|
109
|
+
"ts-jest": "^29.1.0",
|
|
110
|
+
"tsc-alias": "^1.8.5",
|
|
111
|
+
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
|
112
|
+
"typescript": "^5.0.2",
|
|
113
|
+
"vite": "^4.2.1",
|
|
114
|
+
"vite-tsconfig-paths": "^4.0.8"
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'lib/index.js'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ReactElement } from 'react'
|
|
2
|
+
import { type CommonDeviceProperties, type Device } from 'seamapi'
|
|
3
|
+
|
|
4
|
+
import { useDevices, type UseDevicesParams } from 'lib/hooks/use-devices.js'
|
|
5
|
+
|
|
6
|
+
export type DeviceManagerProps = UseDevicesParams
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetch, list, and manage devices.
|
|
10
|
+
*/
|
|
11
|
+
export const DeviceManager = (props: DeviceManagerProps): ReactElement => {
|
|
12
|
+
const { devices, isLoading, isError, error } = useDevices(props)
|
|
13
|
+
|
|
14
|
+
if (isLoading) return <p role='loading'>{t.loading}</p>
|
|
15
|
+
if (isError) return <p>{error?.message}</p>
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<ul>
|
|
19
|
+
{devices?.map((device) => (
|
|
20
|
+
<DeviceManagerItem key={device.device_id} {...device} />
|
|
21
|
+
))}
|
|
22
|
+
</ul>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DeviceManagerItem = (
|
|
27
|
+
props: Device<CommonDeviceProperties>
|
|
28
|
+
): ReactElement => {
|
|
29
|
+
return (
|
|
30
|
+
<li>
|
|
31
|
+
<p>
|
|
32
|
+
{t.device_id}: {props.device_id}
|
|
33
|
+
</p>
|
|
34
|
+
<p>
|
|
35
|
+
{t.name}: {props.properties.name}
|
|
36
|
+
</p>
|
|
37
|
+
</li>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const t = {
|
|
42
|
+
loading: 'Loading devices',
|
|
43
|
+
device_id: 'Device ID',
|
|
44
|
+
name: 'Device name',
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DeviceManager/DeviceManager.js'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-seam.js'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query'
|
|
2
|
+
import {
|
|
3
|
+
type DevicesListRequest,
|
|
4
|
+
type DevicesListResponse,
|
|
5
|
+
type SeamError,
|
|
6
|
+
} from 'seamapi'
|
|
7
|
+
|
|
8
|
+
import { useSeam } from './use-seam.js'
|
|
9
|
+
|
|
10
|
+
export type UseDevicesParams = DevicesListRequest
|
|
11
|
+
export type UseDevicesData = DevicesListResponse['devices']
|
|
12
|
+
|
|
13
|
+
type Result = UseQueryResult<UseDevicesData, SeamError>
|
|
14
|
+
|
|
15
|
+
type UseDevicesResult = Omit<Result, 'data'> & { devices: Result['data'] }
|
|
16
|
+
|
|
17
|
+
export function useDevices(params: DevicesListRequest): UseDevicesResult {
|
|
18
|
+
const { client } = useSeam()
|
|
19
|
+
const { data, ...rest } = useQuery<DevicesListResponse['devices'], SeamError>(
|
|
20
|
+
{
|
|
21
|
+
enabled: client != null,
|
|
22
|
+
queryKey: ['list', 'devices', params],
|
|
23
|
+
queryFn: async () => {
|
|
24
|
+
if (client == null) return []
|
|
25
|
+
return await client?.devices.list(params)
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return { ...rest, devices: data }
|
|
31
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { useContext } from 'react'
|
|
3
|
+
import { type ClientSession, Seam } from 'seamapi'
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
5
|
+
|
|
6
|
+
import { seamContext } from 'lib/provider.js'
|
|
7
|
+
|
|
8
|
+
export function useSeam(): {
|
|
9
|
+
client: Seam | null
|
|
10
|
+
isLoading: boolean
|
|
11
|
+
isError: boolean
|
|
12
|
+
error: unknown
|
|
13
|
+
} {
|
|
14
|
+
const { client, clientOptions, publishableKey, ...context } =
|
|
15
|
+
useContext(seamContext)
|
|
16
|
+
const [clientSession, setClientSession] = useClientSession()
|
|
17
|
+
const userIdentifierKey = useUserIdentifierKey(context.userIdentifierKey)
|
|
18
|
+
|
|
19
|
+
const { isLoading, isError, error, data } = useQuery<Seam>({
|
|
20
|
+
queryKey: ['client', { client, clientSession, userIdentifierKey }],
|
|
21
|
+
queryFn: async () => {
|
|
22
|
+
if (client != null) return client
|
|
23
|
+
|
|
24
|
+
if (publishableKey == null) {
|
|
25
|
+
throw new Error('Missing publishableKey')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// TODO: Check if client session is still valid before resuming it.
|
|
29
|
+
if (clientSession != null) {
|
|
30
|
+
return new Seam({
|
|
31
|
+
...clientOptions,
|
|
32
|
+
clientSessionToken: clientSession.token,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const res = await Seam.getClientSessionToken({
|
|
37
|
+
...clientOptions,
|
|
38
|
+
publishableKey,
|
|
39
|
+
userIdentifierKey,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!res.ok || res.client_session?.token == null) {
|
|
43
|
+
throw new Error('Failed to get client access token')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setClientSession(res.client_session)
|
|
47
|
+
|
|
48
|
+
return new Seam({
|
|
49
|
+
...clientOptions,
|
|
50
|
+
clientSessionToken: res.client_session.token,
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return { client: data ?? null, isLoading, isError, error }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function useClientSession(): [
|
|
59
|
+
clientSession: ClientSession | null,
|
|
60
|
+
setClientSession: (clientSession: ClientSession) => void
|
|
61
|
+
] {
|
|
62
|
+
const localStorageKey = 'seam_client_session'
|
|
63
|
+
|
|
64
|
+
const setClientSession = (clientSession: ClientSession): void => {
|
|
65
|
+
globalThis?.localStorage?.setItem(
|
|
66
|
+
localStorageKey,
|
|
67
|
+
JSON.stringify(clientSession)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cachedClientSession = globalThis?.localStorage?.getItem(localStorageKey)
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
cachedClientSession != null ? JSON.parse(cachedClientSession) : null,
|
|
75
|
+
setClientSession,
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function useUserIdentifierKey(userIdentifierKey: string | undefined): string {
|
|
80
|
+
if (userIdentifierKey != null) {
|
|
81
|
+
return userIdentifierKey
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const localStorageKey = 'seam_user_identifier_key'
|
|
85
|
+
const key = globalThis?.localStorage?.getItem(localStorageKey) ?? uuidv4()
|
|
86
|
+
globalThis?.localStorage?.setItem(localStorageKey, key)
|
|
87
|
+
return key
|
|
88
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
import { createContext, type ReactElement, type ReactNode, useRef } from 'react'
|
|
3
|
+
import { type Seam, type SeamClientOptions } from 'seamapi'
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
// eslint-disable-next-line no-var
|
|
7
|
+
var seam: Omit<SeamProviderProps, 'children'> | undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SeamContext {
|
|
11
|
+
client: Seam | null
|
|
12
|
+
clientOptions?: SeamClientOptions | undefined
|
|
13
|
+
publishableKey?: string | undefined
|
|
14
|
+
userIdentifierKey?: string | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SeamProviderProps {
|
|
18
|
+
client?: Seam
|
|
19
|
+
publishableKey?: string
|
|
20
|
+
userIdentifierKey?: string
|
|
21
|
+
endpoint?: string
|
|
22
|
+
children?: ReactNode
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function SeamProvider({
|
|
26
|
+
children,
|
|
27
|
+
...props
|
|
28
|
+
}: SeamProviderProps): ReactElement {
|
|
29
|
+
const { Provider } = seamContext
|
|
30
|
+
|
|
31
|
+
const queryClientRef = useRef(new QueryClient())
|
|
32
|
+
|
|
33
|
+
const contextRef = useRef(createSeamContextValue(props))
|
|
34
|
+
if (
|
|
35
|
+
contextRef.current.client == null &&
|
|
36
|
+
contextRef.current.publishableKey == null
|
|
37
|
+
) {
|
|
38
|
+
contextRef.current = defaultSeamContextValue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (
|
|
42
|
+
contextRef.current.client == null &&
|
|
43
|
+
contextRef.current.publishableKey == null
|
|
44
|
+
) {
|
|
45
|
+
throw new Error('Must provide either a Seam client or a publishableKey.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<QueryClientProvider client={queryClientRef.current}>
|
|
50
|
+
<Provider value={{ ...contextRef.current }}>{children}</Provider>
|
|
51
|
+
</QueryClientProvider>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const createDefaultSeamContextValue = (): SeamContext => {
|
|
56
|
+
if (
|
|
57
|
+
globalThis.seam?.client == null &&
|
|
58
|
+
globalThis.seam?.publishableKey == null
|
|
59
|
+
) {
|
|
60
|
+
return { client: null }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
return createSeamContextValue(globalThis.seam)
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// eslint-disable-next-line no-console
|
|
67
|
+
console.warn(err)
|
|
68
|
+
return { client: null }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const createSeamContextValue = ({
|
|
73
|
+
client,
|
|
74
|
+
...options
|
|
75
|
+
}: Omit<SeamProviderProps, 'children'>): SeamContext => {
|
|
76
|
+
if (client == null && Object.keys(options).length === 0) {
|
|
77
|
+
return { client: null }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (client != null && Object.values(options).some((v) => v == null)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'Cannot provide both a Seam client along with other options.'
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { publishableKey, userIdentifierKey, ...clientOptions } = options
|
|
87
|
+
|
|
88
|
+
if (client != null) {
|
|
89
|
+
return { client }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
client: null,
|
|
94
|
+
publishableKey,
|
|
95
|
+
userIdentifierKey,
|
|
96
|
+
clientOptions,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const defaultSeamContextValue = createDefaultSeamContextValue()
|
|
101
|
+
|
|
102
|
+
export const seamContext = createContext<SeamContext>(defaultSeamContextValue)
|