@towns-labs/react-sdk 2.0.1
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/README.md +69 -0
- package/dist/esm/TownsSyncProvider.jsx +39 -0
- package/dist/esm/TownsSyncProvider.jsx.map +1 -0
- package/dist/esm/connectTowns.js +45 -0
- package/dist/esm/connectTowns.js.map +1 -0
- package/dist/esm/index.js +35 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internals/TownsSyncContext.js +4 -0
- package/dist/esm/internals/TownsSyncContext.js.map +1 -0
- package/dist/esm/internals/useAction.js +58 -0
- package/dist/esm/internals/useAction.js.map +1 -0
- package/dist/esm/internals/useTownsSync.js +5 -0
- package/dist/esm/internals/useTownsSync.js.map +1 -0
- package/dist/esm/internals/utils.js +10 -0
- package/dist/esm/internals/utils.js.map +1 -0
- package/dist/esm/useAdminRedact.js +43 -0
- package/dist/esm/useAdminRedact.js.map +1 -0
- package/dist/esm/useAgentConnection.js +128 -0
- package/dist/esm/useAgentConnection.js.map +1 -0
- package/dist/esm/useChannel.js +18 -0
- package/dist/esm/useChannel.js.map +1 -0
- package/dist/esm/useCreateChannel.js +24 -0
- package/dist/esm/useCreateChannel.js.map +1 -0
- package/dist/esm/useCreateDm.js +22 -0
- package/dist/esm/useCreateDm.js.map +1 -0
- package/dist/esm/useCreateGdm.js +22 -0
- package/dist/esm/useCreateGdm.js.map +1 -0
- package/dist/esm/useCreateSpace.js +22 -0
- package/dist/esm/useCreateSpace.js.map +1 -0
- package/dist/esm/useDm.js +16 -0
- package/dist/esm/useDm.js.map +1 -0
- package/dist/esm/useGdm.js +16 -0
- package/dist/esm/useGdm.js.map +1 -0
- package/dist/esm/useJoinSpace.js +23 -0
- package/dist/esm/useJoinSpace.js.map +1 -0
- package/dist/esm/useMember.js +36 -0
- package/dist/esm/useMember.js.map +1 -0
- package/dist/esm/useMemberList.js +17 -0
- package/dist/esm/useMemberList.js.map +1 -0
- package/dist/esm/useMyMember.js +71 -0
- package/dist/esm/useMyMember.js.map +1 -0
- package/dist/esm/useObservable.js +56 -0
- package/dist/esm/useObservable.js.map +1 -0
- package/dist/esm/useReactions.js +17 -0
- package/dist/esm/useReactions.js.map +1 -0
- package/dist/esm/useRedact.js +43 -0
- package/dist/esm/useRedact.js.map +1 -0
- package/dist/esm/useScrollback.js +27 -0
- package/dist/esm/useScrollback.js.map +1 -0
- package/dist/esm/useSendMessage.js +31 -0
- package/dist/esm/useSendMessage.js.map +1 -0
- package/dist/esm/useSendReaction.js +33 -0
- package/dist/esm/useSendReaction.js.map +1 -0
- package/dist/esm/useSpace.js +28 -0
- package/dist/esm/useSpace.js.map +1 -0
- package/dist/esm/useSyncAgent.jsx +20 -0
- package/dist/esm/useSyncAgent.jsx.map +1 -0
- package/dist/esm/useThreads.js +19 -0
- package/dist/esm/useThreads.js.map +1 -0
- package/dist/esm/useTimeline.js +32 -0
- package/dist/esm/useTimeline.js.map +1 -0
- package/dist/esm/useTowns.js +16 -0
- package/dist/esm/useTowns.js.map +1 -0
- package/dist/esm/useTownsAuthStatus.js +30 -0
- package/dist/esm/useTownsAuthStatus.js.map +1 -0
- package/dist/esm/useUserDms.js +36 -0
- package/dist/esm/useUserDms.js.map +1 -0
- package/dist/esm/useUserGdms.js +27 -0
- package/dist/esm/useUserGdms.js.map +1 -0
- package/dist/esm/useUserSpaces.js +27 -0
- package/dist/esm/useUserSpaces.js.map +1 -0
- package/dist/esm/utils.js +17 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/types/TownsSyncProvider.d.ts +23 -0
- package/dist/types/TownsSyncProvider.d.ts.map +1 -0
- package/dist/types/connectTowns.d.ts +29 -0
- package/dist/types/connectTowns.d.ts.map +1 -0
- package/dist/types/index.d.ts +35 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/internals/TownsSyncContext.d.ts +11 -0
- package/dist/types/internals/TownsSyncContext.d.ts.map +1 -0
- package/dist/types/internals/useAction.d.ts +38 -0
- package/dist/types/internals/useAction.d.ts.map +1 -0
- package/dist/types/internals/useTownsSync.d.ts +8 -0
- package/dist/types/internals/useTownsSync.d.ts.map +1 -0
- package/dist/types/internals/utils.d.ts +3 -0
- package/dist/types/internals/utils.d.ts.map +1 -0
- package/dist/types/useAdminRedact.d.ts +42 -0
- package/dist/types/useAdminRedact.d.ts.map +1 -0
- package/dist/types/useAgentConnection.d.ts +83 -0
- package/dist/types/useAgentConnection.d.ts.map +1 -0
- package/dist/types/useChannel.d.ts +12 -0
- package/dist/types/useChannel.d.ts.map +1 -0
- package/dist/types/useCreateChannel.d.ts +23 -0
- package/dist/types/useCreateChannel.d.ts.map +1 -0
- package/dist/types/useCreateDm.d.ts +28 -0
- package/dist/types/useCreateDm.d.ts.map +1 -0
- package/dist/types/useCreateGdm.d.ts +31 -0
- package/dist/types/useCreateGdm.d.ts.map +1 -0
- package/dist/types/useCreateSpace.d.ts +29 -0
- package/dist/types/useCreateSpace.d.ts.map +1 -0
- package/dist/types/useDm.d.ts +11 -0
- package/dist/types/useDm.d.ts.map +1 -0
- package/dist/types/useGdm.d.ts +11 -0
- package/dist/types/useGdm.d.ts.map +1 -0
- package/dist/types/useJoinSpace.d.ts +24 -0
- package/dist/types/useJoinSpace.d.ts.map +1 -0
- package/dist/types/useMember.d.ts +30 -0
- package/dist/types/useMember.d.ts.map +1 -0
- package/dist/types/useMemberList.d.ts +11 -0
- package/dist/types/useMemberList.d.ts.map +1 -0
- package/dist/types/useMyMember.d.ts +83 -0
- package/dist/types/useMyMember.d.ts.map +1 -0
- package/dist/types/useObservable.d.ts +59 -0
- package/dist/types/useObservable.d.ts.map +1 -0
- package/dist/types/useReactions.d.ts +10 -0
- package/dist/types/useReactions.d.ts.map +1 -0
- package/dist/types/useRedact.d.ts +42 -0
- package/dist/types/useRedact.d.ts.map +1 -0
- package/dist/types/useScrollback.d.ts +27 -0
- package/dist/types/useScrollback.d.ts.map +1 -0
- package/dist/types/useSendMessage.d.ts +32 -0
- package/dist/types/useSendMessage.d.ts.map +1 -0
- package/dist/types/useSendReaction.d.ts +32 -0
- package/dist/types/useSendReaction.d.ts.map +1 -0
- package/dist/types/useSpace.d.ts +22 -0
- package/dist/types/useSpace.d.ts.map +1 -0
- package/dist/types/useSyncAgent.d.ts +12 -0
- package/dist/types/useSyncAgent.d.ts.map +1 -0
- package/dist/types/useThreads.d.ts +11 -0
- package/dist/types/useThreads.d.ts.map +1 -0
- package/dist/types/useTimeline.d.ts +24 -0
- package/dist/types/useTimeline.d.ts.map +1 -0
- package/dist/types/useTowns.d.ts +14 -0
- package/dist/types/useTowns.d.ts.map +1 -0
- package/dist/types/useTownsAuthStatus.d.ts +26 -0
- package/dist/types/useTownsAuthStatus.d.ts.map +1 -0
- package/dist/types/useUserDms.d.ts +41 -0
- package/dist/types/useUserDms.d.ts.map +1 -0
- package/dist/types/useUserGdms.d.ts +32 -0
- package/dist/types/useUserGdms.d.ts.map +1 -0
- package/dist/types/useUserSpaces.d.ts +32 -0
- package/dist/types/useUserSpaces.d.ts.map +1 -0
- package/dist/types/utils.d.ts +3 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +75 -0
- package/src/connectTowns.ts +63 -0
- package/src/index.ts +34 -0
- package/src/internals/TownsSyncContext.ts +12 -0
- package/src/internals/useAction.ts +84 -0
- package/src/internals/useTownsSync.ts +4 -0
- package/src/internals/utils.ts +11 -0
- package/src/useAdminRedact.ts +45 -0
- package/src/useAgentConnection.ts +143 -0
- package/src/useChannel.ts +27 -0
- package/src/useCreateChannel.ts +30 -0
- package/src/useCreateDm.ts +25 -0
- package/src/useCreateGdm.ts +25 -0
- package/src/useCreateSpace.ts +25 -0
- package/src/useDm.ts +17 -0
- package/src/useGdm.ts +17 -0
- package/src/useJoinSpace.ts +26 -0
- package/src/useMember.ts +45 -0
- package/src/useMemberList.ts +21 -0
- package/src/useMyMember.ts +83 -0
- package/src/useObservable.ts +129 -0
- package/src/useReactions.ts +21 -0
- package/src/useRedact.ts +45 -0
- package/src/useScrollback.ts +31 -0
- package/src/useSendMessage.ts +35 -0
- package/src/useSendReaction.ts +39 -0
- package/src/useSpace.ts +30 -0
- package/src/useThreads.ts +22 -0
- package/src/useTimeline.ts +35 -0
- package/src/useTowns.ts +22 -0
- package/src/useTownsAuthStatus.ts +32 -0
- package/src/useUserDms.ts +38 -0
- package/src/useUserGdms.ts +29 -0
- package/src/useUserSpaces.ts +29 -0
- package/src/utils.ts +28 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Spaces } from '@towns-labs/sdk';
|
|
2
|
+
import type { ObservableConfig } from './useObservable';
|
|
3
|
+
/**
|
|
4
|
+
* Hook to get the spaces of the current user.
|
|
5
|
+
* @param config - Configuration options for the observable.
|
|
6
|
+
* @returns The list of all space ids of the current user.
|
|
7
|
+
* @example
|
|
8
|
+
* You can combine this hook with the `useSpace` hook to get all spaces of the current user and render them:
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useUserSpaces, useSpace } from '@towns-labs/react-sdk'
|
|
12
|
+
*
|
|
13
|
+
* const AllSpaces = () => {
|
|
14
|
+
* const { spaceIds } = useUserSpaces()
|
|
15
|
+
* return <>{spaceIds.map((spaceId) => <Space key={spaceId} spaceId={spaceId} />)}</>
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* const Space = ({ spaceId }: { spaceId: string }) => {
|
|
19
|
+
* const { data: space } = useSpace(spaceId)
|
|
20
|
+
* return <div>{space.metadata?.name || 'Unnamed Space'}</div>
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const useUserSpaces: (config?: ObservableConfig.FromObservable<Spaces>) => {
|
|
25
|
+
error: Error | undefined;
|
|
26
|
+
status: "loading" | "loaded" | "error";
|
|
27
|
+
isLoading: boolean;
|
|
28
|
+
isError: boolean;
|
|
29
|
+
isLoaded: boolean;
|
|
30
|
+
spaceIds: string[];
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=useUserSpaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useUserSpaces.d.ts","sourceRoot":"","sources":["../../src/useUserSpaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAGvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,gBAAgB,CAAC,cAAc,CAAC,MAAM,CAAC;;;;;;;CAG7E,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,EAAE,EACP,KAAK,GAAG,EACR,KAAK,EACL,KAAK,SAAS,EAMjB,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,OAAO,GAAI,MAAM,SAAS,EAAE,UAAU,MAAM,KAAG,GAAG,GAAG,OAAO,GAAG,EAAE,GAAG,KAchF,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@towns-labs/react-sdk",
|
|
3
|
+
"description": "React Hooks for Towns Protocol SDK",
|
|
4
|
+
"version": "2.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/esm/index.js",
|
|
7
|
+
"types": "./dist/types/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "bun run clean && bun run build:esm+types",
|
|
10
|
+
"build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types",
|
|
11
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
12
|
+
"gen": "bun run gen:extractor && bun run gen:docs",
|
|
13
|
+
"gen:docs": "tsx ./scripts/docgen/generate.ts",
|
|
14
|
+
"gen:extractor": "api-extractor run --local",
|
|
15
|
+
"lint": "eslint ./src --max-warnings=0",
|
|
16
|
+
"test:build": "publint --strict && attw --pack --ignore-rules cjs-resolves-to-esm",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"watch": "bun run build -w"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@towns-labs/proto": "^1.0.1",
|
|
22
|
+
"@towns-labs/sdk": "^1.0.1",
|
|
23
|
+
"@towns-labs/web3": "^1.0.1",
|
|
24
|
+
"ethers": "^5.8.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@microsoft/api-extractor": "^7.52.2",
|
|
28
|
+
"@microsoft/api-extractor-model": "^7.30.5",
|
|
29
|
+
"@microsoft/tsdoc": "^0.15.0",
|
|
30
|
+
"@microsoft/tsdoc-config": "^0.17.0",
|
|
31
|
+
"@types/node": "^20.14.8",
|
|
32
|
+
"@types/react": "^19.1.16",
|
|
33
|
+
"@types/react-dom": "^19.1.7",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
|
35
|
+
"@typescript-eslint/parser": "^8.29.0",
|
|
36
|
+
"eslint": "^8.57.1",
|
|
37
|
+
"eslint-plugin-import-x": "^4.10.2",
|
|
38
|
+
"eslint-plugin-react": "^7.32.2",
|
|
39
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
40
|
+
"eslint-plugin-tsdoc": "^0.3.0",
|
|
41
|
+
"react": "^19.1.0",
|
|
42
|
+
"react-dom": "^19.1.0",
|
|
43
|
+
"ts-morph": "^24.0.0",
|
|
44
|
+
"tsx": "^4.7.1",
|
|
45
|
+
"typescript": "~5.8.3"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist/**",
|
|
49
|
+
"!dist/**/*.tsbuildinfo",
|
|
50
|
+
"src/**/*.ts",
|
|
51
|
+
"!src/**/*.test.ts",
|
|
52
|
+
"!src/**/*.test-d.ts"
|
|
53
|
+
],
|
|
54
|
+
"keywords": [
|
|
55
|
+
"hooks",
|
|
56
|
+
"react",
|
|
57
|
+
"river",
|
|
58
|
+
"sdk",
|
|
59
|
+
"web3"
|
|
60
|
+
],
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"react": "^18.2 || ^19.0",
|
|
63
|
+
"typescript": "^5"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"repository": {
|
|
69
|
+
"type": "git",
|
|
70
|
+
"url": "https://github.com/towns-protocol/towns.git",
|
|
71
|
+
"directory": "packages/react"
|
|
72
|
+
},
|
|
73
|
+
"sideEffects": false,
|
|
74
|
+
"typings": "./dist/types/index.d.ts"
|
|
75
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/// This file can be used on server side to create a Towns Protocol Client
|
|
2
|
+
/// We don't want a 'use client' directive here
|
|
3
|
+
import {
|
|
4
|
+
type SignerContext,
|
|
5
|
+
SyncAgent,
|
|
6
|
+
type SyncAgentConfig,
|
|
7
|
+
makeSignerContext,
|
|
8
|
+
makeSignerContextFromBearerToken,
|
|
9
|
+
} from '@towns-labs/sdk'
|
|
10
|
+
import { ethers } from 'ethers'
|
|
11
|
+
|
|
12
|
+
const defaultConfig: Partial<SyncAgentConfig> = {
|
|
13
|
+
unpackEnvelopeOpts: {
|
|
14
|
+
disableSignatureValidation: true,
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Sign and connect to Towns using a Signer and a random delegate wallet every time
|
|
20
|
+
* @param signer - The signer to use
|
|
21
|
+
* @param config - The configuration for the sync agent
|
|
22
|
+
* @returns The sync agent
|
|
23
|
+
*/
|
|
24
|
+
export const signAndConnect = async (
|
|
25
|
+
signer: ethers.Signer,
|
|
26
|
+
config: Omit<SyncAgentConfig, 'context'>,
|
|
27
|
+
): Promise<SyncAgent> => {
|
|
28
|
+
const delegateWallet = ethers.Wallet.createRandom()
|
|
29
|
+
const signerContext = await makeSignerContext(signer, delegateWallet)
|
|
30
|
+
return new SyncAgent({ context: signerContext, ...defaultConfig, ...config })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Connect to Towns using a SignerContext
|
|
35
|
+
*
|
|
36
|
+
* Useful for server side code, allowing you to persist the signer context and use it to auth with Towns later
|
|
37
|
+
* @param signerContext - The signer context to use
|
|
38
|
+
* @param config - The configuration for the sync agent
|
|
39
|
+
* @returns The sync agent
|
|
40
|
+
*/
|
|
41
|
+
export const connectTowns = async (
|
|
42
|
+
signerContext: SignerContext,
|
|
43
|
+
config: Omit<SyncAgentConfig, 'context'>,
|
|
44
|
+
): Promise<SyncAgent> => {
|
|
45
|
+
return new SyncAgent({ context: signerContext, ...defaultConfig, ...config })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Connect to Towns using a Bearer Token
|
|
50
|
+
* Towns clients can use this to connect to Towns Protocol on behalf of a user
|
|
51
|
+
*
|
|
52
|
+
* Useful for server side code, allowing you to persist the signer context and use it to auth with Towns later
|
|
53
|
+
* @param token - The bearer token to use
|
|
54
|
+
* @param config - The configuration for the sync agent
|
|
55
|
+
* @returns The sync agent
|
|
56
|
+
*/
|
|
57
|
+
export const connectTownsWithBearerToken = async (
|
|
58
|
+
token: string,
|
|
59
|
+
config: Omit<SyncAgentConfig, 'context'>,
|
|
60
|
+
): Promise<SyncAgent> => {
|
|
61
|
+
const signerContext = await makeSignerContextFromBearerToken(token)
|
|
62
|
+
return new SyncAgent({ context: signerContext, ...defaultConfig, ...config })
|
|
63
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* This file is generated by running 🏕️ scripts/generate_sdk_index.sh 🏕️ *
|
|
3
|
+
**************************************************************************/
|
|
4
|
+
export * from './TownsSyncProvider'
|
|
5
|
+
export * from './connectTowns'
|
|
6
|
+
export * from './useAdminRedact'
|
|
7
|
+
export * from './useAgentConnection'
|
|
8
|
+
export * from './useChannel'
|
|
9
|
+
export * from './useCreateChannel'
|
|
10
|
+
export * from './useCreateDm'
|
|
11
|
+
export * from './useCreateGdm'
|
|
12
|
+
export * from './useCreateSpace'
|
|
13
|
+
export * from './useDm'
|
|
14
|
+
export * from './useGdm'
|
|
15
|
+
export * from './useJoinSpace'
|
|
16
|
+
export * from './useMember'
|
|
17
|
+
export * from './useMemberList'
|
|
18
|
+
export * from './useMyMember'
|
|
19
|
+
export * from './useObservable'
|
|
20
|
+
export * from './useReactions'
|
|
21
|
+
export * from './useRedact'
|
|
22
|
+
export * from './useScrollback'
|
|
23
|
+
export * from './useSendMessage'
|
|
24
|
+
export * from './useSendReaction'
|
|
25
|
+
export * from './useSpace'
|
|
26
|
+
export * from './useSyncAgent'
|
|
27
|
+
export * from './useThreads'
|
|
28
|
+
export * from './useTimeline'
|
|
29
|
+
export * from './useTowns'
|
|
30
|
+
export * from './useTownsAuthStatus'
|
|
31
|
+
export * from './useUserDms'
|
|
32
|
+
export * from './useUserGdms'
|
|
33
|
+
export * from './useUserSpaces'
|
|
34
|
+
export * from './utils'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { SyncAgent } from '@towns-labs/sdk'
|
|
3
|
+
import { createContext } from 'react'
|
|
4
|
+
|
|
5
|
+
type TownsSyncContextProps = {
|
|
6
|
+
syncAgent: SyncAgent | undefined
|
|
7
|
+
setSyncAgent: (syncAgent: SyncAgent | undefined) => void
|
|
8
|
+
config?: {
|
|
9
|
+
onTokenExpired?: () => void
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const TownsSyncContext = createContext<TownsSyncContextProps | undefined>(undefined)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for an action.
|
|
5
|
+
* It can be used to configure the behavior of the useAction hook.
|
|
6
|
+
*/
|
|
7
|
+
export type ActionConfig<Action> = {
|
|
8
|
+
/** Callback function to be called when an error occurs while executing the action. */
|
|
9
|
+
onError?: (err: Error) => void
|
|
10
|
+
/** Callback function to be called when the action is successful. */
|
|
11
|
+
onSuccess?: (data: ReturnOf<Action>) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type MultipleParams<T> = T extends unknown[] ? T : [T]
|
|
15
|
+
|
|
16
|
+
type ActionFn<T> = T extends (...args: infer Args) => Promise<infer Return>
|
|
17
|
+
? (...args: Args) => Promise<Return>
|
|
18
|
+
: never
|
|
19
|
+
|
|
20
|
+
type ParamsOf<T> = Parameters<ActionFn<T>>
|
|
21
|
+
type ReturnOf<T> = Awaited<ReturnType<ActionFn<T>>>
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Hook to create an action from a namespace.
|
|
25
|
+
* @internal
|
|
26
|
+
* @param namespace - The namespace to create the action from.
|
|
27
|
+
* @param fnName - The name of the action to create. Example: `Namespace.fnName`
|
|
28
|
+
* @param config - Configuration options for the action.
|
|
29
|
+
* @returns The action and its loading state.
|
|
30
|
+
*/
|
|
31
|
+
export const useAction = <Namespace, Key extends keyof Namespace, Fn extends Namespace[Key]>(
|
|
32
|
+
namespace: Namespace | undefined,
|
|
33
|
+
fnName: Key & string,
|
|
34
|
+
config?: ActionConfig<Fn>,
|
|
35
|
+
) => {
|
|
36
|
+
const [status, setStatus] = useState<'loading' | 'error' | 'success' | 'idle'>('idle')
|
|
37
|
+
const [error, setError] = useState<Error | undefined>()
|
|
38
|
+
const [data, setData] = useState<ReturnOf<Fn> | undefined>()
|
|
39
|
+
|
|
40
|
+
const action = useCallback(
|
|
41
|
+
async (...args: MultipleParams<ParamsOf<Fn>>): Promise<ReturnOf<Fn>> => {
|
|
42
|
+
if (!namespace) {
|
|
43
|
+
throw new Error(`useAction: namespace is undefined`)
|
|
44
|
+
}
|
|
45
|
+
const fn = namespace[fnName] as ActionFn<Fn>
|
|
46
|
+
if (typeof fn !== 'function') {
|
|
47
|
+
throw new Error(`useAction: fn ${fnName} is not a function`)
|
|
48
|
+
}
|
|
49
|
+
setStatus('loading')
|
|
50
|
+
try {
|
|
51
|
+
const data = (await fn.apply(namespace, args)) as ReturnOf<Fn>
|
|
52
|
+
setData(data)
|
|
53
|
+
setStatus('success')
|
|
54
|
+
config?.onSuccess?.(data)
|
|
55
|
+
return data as ReturnOf<Fn>
|
|
56
|
+
} catch (error: unknown) {
|
|
57
|
+
setStatus('error')
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
setError(error)
|
|
60
|
+
config?.onError?.(error)
|
|
61
|
+
}
|
|
62
|
+
// Let the caller handle the error
|
|
63
|
+
throw error
|
|
64
|
+
} finally {
|
|
65
|
+
setStatus('idle')
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[config, fnName, namespace],
|
|
69
|
+
)
|
|
70
|
+
return {
|
|
71
|
+
/** The action to execute. */
|
|
72
|
+
action,
|
|
73
|
+
/** The data returned by the action. */
|
|
74
|
+
data,
|
|
75
|
+
/** The error that occurred while executing the action. */
|
|
76
|
+
error,
|
|
77
|
+
/** Whether the action is pending. */
|
|
78
|
+
isPending: status === 'loading',
|
|
79
|
+
/** Whether the action is successful. */
|
|
80
|
+
isSuccess: status === 'success',
|
|
81
|
+
/** Whether the action is in error. */
|
|
82
|
+
isError: status === 'error',
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PersistedModel } from '@towns-labs/sdk'
|
|
2
|
+
|
|
3
|
+
export const isPersistedModel = <T>(value: T | PersistedModel<T>): value is PersistedModel<T> => {
|
|
4
|
+
if (typeof value !== 'object') {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
if (value === null) {
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
return 'status' in value && 'data' in value
|
|
11
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Channel, Space, assert } from '@towns-labs/sdk'
|
|
4
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
import { getRoom } from './utils'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to redact any message in a channel if you're an admin.
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ### Redact a message
|
|
13
|
+
*
|
|
14
|
+
* You can use `adminRedact` to redact a message in a stream.
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { useAdminRedact } from '@towns-labs/react-sdk'
|
|
17
|
+
*
|
|
18
|
+
* const { adminRedact } = useAdminRedact(streamId)
|
|
19
|
+
* adminRedact({ eventId: messageEventId })
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ### Redact a message reaction
|
|
23
|
+
*
|
|
24
|
+
* You can also use `redact` to redact a message reaction in a stream.
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { useRedact } from '@towns-labs/react-sdk'
|
|
27
|
+
*
|
|
28
|
+
* const { redact } = useRedact(streamId)
|
|
29
|
+
* redact({ eventId: reactionEventId })
|
|
30
|
+
* ```
|
|
31
|
+
* @param streamId - The id of the stream to redact the message in.
|
|
32
|
+
* @param config - Configuration options for the action.
|
|
33
|
+
* @returns The `redact` action and its loading state.
|
|
34
|
+
*/
|
|
35
|
+
export const useAdminRedact = (streamId: string, config?: ActionConfig<Channel['adminRedact']>) => {
|
|
36
|
+
const sync = useSyncAgent()
|
|
37
|
+
const room = getRoom(sync, streamId)
|
|
38
|
+
assert(!(room instanceof Space), 'Spaces dont have timeline to redact')
|
|
39
|
+
const { action: adminRedact, ...rest } = useAction(room, 'adminRedact', config)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
adminRedact,
|
|
43
|
+
...rest,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { SyncAgentConfig } from '@towns-labs/sdk'
|
|
2
|
+
import { useCallback, useMemo, useState } from 'react'
|
|
3
|
+
import type { ethers } from 'ethers'
|
|
4
|
+
import { connectTownsWithBearerToken, signAndConnect } from './connectTowns'
|
|
5
|
+
import { useTownsSync } from './internals/useTownsSync'
|
|
6
|
+
|
|
7
|
+
type AgentConnectConfig = Omit<SyncAgentConfig, 'context' | 'onTokenExpired'>
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook for managing the connection to the sync agent
|
|
11
|
+
*
|
|
12
|
+
* @example You can connect the Sync Agent to Towns Protocol using a Bearer Token or using a Signer.
|
|
13
|
+
*
|
|
14
|
+
* ### Bearer Token
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { useAgentConnection } from '@towns-labs/react-sdk'
|
|
17
|
+
* import { townsEnv } from '@towns-labs/sdk'
|
|
18
|
+
* import { useState } from 'react'
|
|
19
|
+
*
|
|
20
|
+
* const townsConfig = townsEnv().makeTownsConfig('beta')
|
|
21
|
+
*
|
|
22
|
+
* const Login = () => {
|
|
23
|
+
* const { connectUsingBearerToken, isAgentConnecting, isAgentConnected } = useAgentConnection()
|
|
24
|
+
* const [bearerToken, setBearerToken] = useState('')
|
|
25
|
+
*
|
|
26
|
+
* return (
|
|
27
|
+
* <>
|
|
28
|
+
* <input value={bearerToken} onChange={(e) => setBearerToken(e.target.value)} />
|
|
29
|
+
* <button onClick={() => connectUsingBearerToken(bearerToken, { townsConfig })}>
|
|
30
|
+
* Login
|
|
31
|
+
* </button>
|
|
32
|
+
* {isAgentConnecting && <span>Connecting... ⏳</span>}
|
|
33
|
+
* {isAgentConnected && <span>Connected ✅</span>}
|
|
34
|
+
* </>
|
|
35
|
+
* )
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* ### Signer
|
|
40
|
+
*
|
|
41
|
+
* If you're using Wagmi and Viem, you can use the [`useEthersSigner`](https://wagmi.sh/react/guides/ethers#usage-1) hook to get an ethers.js v5 Signer from a Viem Wallet Client.
|
|
42
|
+
*
|
|
43
|
+
* ```tsx
|
|
44
|
+
* import { useAgentConnection } from '@towns-labs/react-sdk'
|
|
45
|
+
* import { townsEnv } from '@towns-labs/sdk'
|
|
46
|
+
* import { useEthersSigner } from './utils/viem-to-ethers'
|
|
47
|
+
*
|
|
48
|
+
* const townsConfig = townsEnv().makeTownsConfig('beta')
|
|
49
|
+
*
|
|
50
|
+
* const Login = () => {
|
|
51
|
+
* const { connect, isAgentConnecting, isAgentConnected } = useAgentConnection()
|
|
52
|
+
* const signer = useEthersSigner()
|
|
53
|
+
*
|
|
54
|
+
* return (
|
|
55
|
+
* <>
|
|
56
|
+
* <button onClick={async () => {
|
|
57
|
+
* if (!signer) {
|
|
58
|
+
* return
|
|
59
|
+
* }
|
|
60
|
+
* connect(signer, { townsConfig })
|
|
61
|
+
* }}>
|
|
62
|
+
* Login
|
|
63
|
+
* </button>
|
|
64
|
+
* {isAgentConnecting && <span>Connecting... ⏳</span>}
|
|
65
|
+
* {isAgentConnected && <span>Connected ✅</span>}
|
|
66
|
+
* </>
|
|
67
|
+
* )
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @returns The connection state and methods (connect, connectUsingBearerToken, disconnect)
|
|
72
|
+
*/
|
|
73
|
+
export const useAgentConnection = () => {
|
|
74
|
+
const [isAgentConnecting, setConnecting] = useState(false)
|
|
75
|
+
const towns = useTownsSync()
|
|
76
|
+
|
|
77
|
+
const connect = useCallback(
|
|
78
|
+
async (signer: ethers.Signer, config: AgentConnectConfig) => {
|
|
79
|
+
if (towns?.syncAgent) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
const mergedConfig = {
|
|
83
|
+
...config,
|
|
84
|
+
...towns?.config,
|
|
85
|
+
onTokenExpired: () => {
|
|
86
|
+
towns?.config?.onTokenExpired?.()
|
|
87
|
+
towns?.setSyncAgent(undefined)
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
setConnecting(true)
|
|
91
|
+
return signAndConnect(signer, mergedConfig)
|
|
92
|
+
.then((syncAgent) => {
|
|
93
|
+
towns?.setSyncAgent(syncAgent)
|
|
94
|
+
return syncAgent
|
|
95
|
+
})
|
|
96
|
+
.finally(() => setConnecting(false))
|
|
97
|
+
},
|
|
98
|
+
[towns],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
const connectUsingBearerToken = useCallback(
|
|
102
|
+
async (bearerToken: string, config: AgentConnectConfig) => {
|
|
103
|
+
if (towns?.syncAgent) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
const mergedConfig = {
|
|
107
|
+
...config,
|
|
108
|
+
...towns?.config,
|
|
109
|
+
onTokenExpired: () => {
|
|
110
|
+
towns?.config?.onTokenExpired?.()
|
|
111
|
+
towns?.setSyncAgent(undefined)
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
setConnecting(true)
|
|
115
|
+
return connectTownsWithBearerToken(bearerToken, mergedConfig)
|
|
116
|
+
.then((syncAgent) => {
|
|
117
|
+
towns?.setSyncAgent(syncAgent)
|
|
118
|
+
return syncAgent
|
|
119
|
+
})
|
|
120
|
+
.finally(() => setConnecting(false))
|
|
121
|
+
},
|
|
122
|
+
[towns],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const disconnect = useCallback(() => towns?.setSyncAgent(undefined), [towns])
|
|
126
|
+
|
|
127
|
+
const isAgentConnected = useMemo(() => !!towns?.syncAgent, [towns])
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
/** Connect to Towns Protocol using a Signer */
|
|
131
|
+
connect,
|
|
132
|
+
/** Connect to Towns Protocol using a Bearer Token */
|
|
133
|
+
connectUsingBearerToken,
|
|
134
|
+
/** Disconnect from Towns Protocol */
|
|
135
|
+
disconnect,
|
|
136
|
+
/** Whether the agent is currently connecting */
|
|
137
|
+
isAgentConnecting,
|
|
138
|
+
/** Whether the agent is connected */
|
|
139
|
+
isAgentConnected,
|
|
140
|
+
/** The environment of the current connection (beta, omega, alpha, local_dev, etc.) */
|
|
141
|
+
env: towns?.syncAgent?.config.townsConfig.environmentId,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import type { Channel } from '@towns-labs/sdk'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to get data about a channel.
|
|
10
|
+
* You can use this hook to get channel metadata and if the user has joined the channel.
|
|
11
|
+
* @param spaceId - The id of the space the channel belongs to.
|
|
12
|
+
* @param channelId - The id of the channel to get data about.
|
|
13
|
+
* @param config - Configuration options for the observable.
|
|
14
|
+
* @returns The ChannelModel data.
|
|
15
|
+
*/
|
|
16
|
+
export const useChannel = (
|
|
17
|
+
spaceId: string,
|
|
18
|
+
channelId: string,
|
|
19
|
+
config?: ObservableConfig.FromObservable<Channel>,
|
|
20
|
+
) => {
|
|
21
|
+
const sync = useSyncAgent()
|
|
22
|
+
const channel = useMemo(
|
|
23
|
+
() => sync.spaces.getSpace(spaceId).getChannel(channelId),
|
|
24
|
+
[sync.spaces, spaceId, channelId],
|
|
25
|
+
)
|
|
26
|
+
return useObservable(channel, config)
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { Space } from '@towns-labs/sdk'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
6
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to create a channel.
|
|
10
|
+
* @param config - Configuration options for the action.
|
|
11
|
+
* @returns The `createChannel` action and its loading state.
|
|
12
|
+
*/
|
|
13
|
+
export const useCreateChannel = (
|
|
14
|
+
spaceId: string,
|
|
15
|
+
config?: ActionConfig<Space['createChannel']>,
|
|
16
|
+
) => {
|
|
17
|
+
const sync = useSyncAgent()
|
|
18
|
+
const space = useMemo(() => sync.spaces.getSpace(spaceId), [spaceId, sync.spaces])
|
|
19
|
+
const { action: createChannel, ...rest } = useAction(space, 'createChannel', config)
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
/**
|
|
23
|
+
* Action to create a channel.
|
|
24
|
+
* @param name - The name of the channel to create.
|
|
25
|
+
* @param signer - The signer to use to create the channel.
|
|
26
|
+
*/
|
|
27
|
+
createChannel,
|
|
28
|
+
...rest,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { Dms } from '@towns-labs/sdk'
|
|
4
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A hook that allows you to create a new direct message (DM).
|
|
9
|
+
* @param config - The action config.
|
|
10
|
+
* @returns An object containing the `createDM` action and the rest of the action result.
|
|
11
|
+
*/
|
|
12
|
+
export const useCreateDm = (config?: ActionConfig<Dms['createDM']>) => {
|
|
13
|
+
const sync = useSyncAgent()
|
|
14
|
+
const { action: createDM, ...rest } = useAction(sync.dms, 'createDM', config)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new DM.
|
|
19
|
+
* @param userId - The `userId` of the user to create a DM with.
|
|
20
|
+
* @returns A promise that resolves to the result of the create operation.
|
|
21
|
+
*/
|
|
22
|
+
createDM,
|
|
23
|
+
...rest,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { Gdms } from '@towns-labs/sdk'
|
|
4
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A hook that allows you to create a new group direct message (GDM).
|
|
9
|
+
* @param config - The action config.
|
|
10
|
+
* @returns An object containing the `createGDM` action and the rest of the action result.
|
|
11
|
+
*/
|
|
12
|
+
export const useCreateGdm = (config?: ActionConfig<Gdms['createGDM']>) => {
|
|
13
|
+
const sync = useSyncAgent()
|
|
14
|
+
const { action: createGDM, ...rest } = useAction(sync.gdms, 'createGDM', config)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new GDM.
|
|
19
|
+
* @param userIds - The `userIds` of the users to invite to the GDM.
|
|
20
|
+
* @returns A promise that resolves to the result of the create operation.
|
|
21
|
+
*/
|
|
22
|
+
createGDM,
|
|
23
|
+
...rest,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { Spaces } from '@towns-labs/sdk'
|
|
4
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to create a space.
|
|
9
|
+
* @param config - Configuration options for the action.
|
|
10
|
+
* @returns The `createSpace` action and its loading state.
|
|
11
|
+
*/
|
|
12
|
+
export const useCreateSpace = (config: ActionConfig<Spaces['createSpace']> = {}) => {
|
|
13
|
+
const sync = useSyncAgent()
|
|
14
|
+
const { action: createSpace, ...rest } = useAction(sync.spaces, 'createSpace', config)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* Action to create a space.
|
|
19
|
+
* @param opts - Options for the create space action.
|
|
20
|
+
* @param signer - The signer used to create the space.
|
|
21
|
+
*/
|
|
22
|
+
createSpace,
|
|
23
|
+
...rest,
|
|
24
|
+
}
|
|
25
|
+
}
|