@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
package/src/useDm.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { DmModel } from '@towns-labs/sdk'
|
|
3
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
4
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to get the data of a DM.
|
|
8
|
+
* You can use this hook to get DM metadata and if the user has joined the DM.
|
|
9
|
+
* @param streamId - The id of the DM to get the data of.
|
|
10
|
+
* @param config - Configuration options for the observable.
|
|
11
|
+
* @returns The DmModel of the DM.
|
|
12
|
+
*/
|
|
13
|
+
export const useDm = (streamId: string, config?: ObservableConfig.FromData<DmModel>) => {
|
|
14
|
+
const sync = useSyncAgent()
|
|
15
|
+
const dm = useMemo(() => sync.dms.getDm(streamId), [streamId, sync])
|
|
16
|
+
return useObservable(dm, config)
|
|
17
|
+
}
|
package/src/useGdm.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { GdmModel } from '@towns-labs/sdk'
|
|
3
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
4
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to get the data of a Group DM.
|
|
8
|
+
* You can use this hook to get Group DM metadata and if the user has joined the Group DM.
|
|
9
|
+
* @param streamId - The id of the Group DM to get the data of.
|
|
10
|
+
* @param config - Configuration options for the observable.
|
|
11
|
+
* @returns The GdmModel of the Group DM.
|
|
12
|
+
*/
|
|
13
|
+
export const useGdm = (streamId: string, config?: ObservableConfig.FromData<GdmModel>) => {
|
|
14
|
+
const sync = useSyncAgent()
|
|
15
|
+
const gdm = useMemo(() => sync.gdms.getGdm(streamId), [streamId, sync])
|
|
16
|
+
return useObservable(gdm, config)
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 join a space.
|
|
9
|
+
* @param config - Configuration options for the action.
|
|
10
|
+
* @returns The joinSpace action and the status of the action.
|
|
11
|
+
*/
|
|
12
|
+
export const useJoinSpace = (config?: ActionConfig<Spaces['joinSpace']>) => {
|
|
13
|
+
const sync = useSyncAgent()
|
|
14
|
+
const { action: joinSpace, ...rest } = useAction(sync.spaces, 'joinSpace', config)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
/**
|
|
18
|
+
* Action to join a space.
|
|
19
|
+
* @param spaceId - The id of the space to join.
|
|
20
|
+
* @param signer - The signer to use to join the space.
|
|
21
|
+
* @param opts - Options for the join action.
|
|
22
|
+
*/
|
|
23
|
+
joinSpace,
|
|
24
|
+
...rest,
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/useMember.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Member } from '@towns-labs/sdk'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { useObservable } from './useObservable'
|
|
4
|
+
|
|
5
|
+
import type { ObservableConfig } from './useObservable'
|
|
6
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
7
|
+
import { getRoom } from './utils'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to get data from a specific member of a Space, GDM, Channel, or DM.
|
|
11
|
+
* @param props - The streamId and userId of the member to get data from.
|
|
12
|
+
* @param config - Configuration options for the observable.
|
|
13
|
+
* @returns The Member data.
|
|
14
|
+
*/
|
|
15
|
+
export const useMember = (
|
|
16
|
+
props: { streamId: string; userId: string },
|
|
17
|
+
config?: ObservableConfig.FromObservable<Member>,
|
|
18
|
+
) => {
|
|
19
|
+
const sync = useSyncAgent()
|
|
20
|
+
const member = useMemo(
|
|
21
|
+
() => getRoom(sync, props.streamId).members.get(props.userId),
|
|
22
|
+
[sync, props],
|
|
23
|
+
)
|
|
24
|
+
const { data, ...rest } = useObservable(member, config)
|
|
25
|
+
return {
|
|
26
|
+
// Excluding `Member.id` property from the return value, since its a internal store id and can lead to confusion
|
|
27
|
+
userId: data.userId,
|
|
28
|
+
streamId: data.streamId,
|
|
29
|
+
initialized: data.initialized,
|
|
30
|
+
// username
|
|
31
|
+
username: data.username,
|
|
32
|
+
isUsernameConfirmed: data.isUsernameConfirmed,
|
|
33
|
+
isUsernameEncrypted: data.isUsernameEncrypted,
|
|
34
|
+
// displayName
|
|
35
|
+
displayName: data.displayName,
|
|
36
|
+
isDisplayNameEncrypted: data.isDisplayNameEncrypted,
|
|
37
|
+
// ensAddress
|
|
38
|
+
ensAddress: data.ensAddress,
|
|
39
|
+
// nft
|
|
40
|
+
nft: data.nft,
|
|
41
|
+
// membership
|
|
42
|
+
membership: data.membership,
|
|
43
|
+
...rest,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { MembersModel } from '@towns-labs/sdk'
|
|
3
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
4
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
5
|
+
import { getRoom } from './utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to get the members userIds of a Space, GDM, Channel, or DM.
|
|
9
|
+
* Used with useMember to get data from a specific member.
|
|
10
|
+
* @param streamId - The id of the stream to get the members of.
|
|
11
|
+
* @param config - Configuration options for the observable.
|
|
12
|
+
* @returns The MembersModel of the stream, containing the userIds of the members.
|
|
13
|
+
*/
|
|
14
|
+
export const useMemberList = (
|
|
15
|
+
streamId: string,
|
|
16
|
+
config?: ObservableConfig.FromData<MembersModel>,
|
|
17
|
+
) => {
|
|
18
|
+
const sync = useSyncAgent()
|
|
19
|
+
const members = useMemo(() => getRoom(sync, streamId).members, [sync, streamId])
|
|
20
|
+
return useObservable(members, config)
|
|
21
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Member, Myself, SyncAgent } from '@towns-labs/sdk'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
4
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
import { getRoom } from './utils'
|
|
7
|
+
|
|
8
|
+
const getMyMember = (sync: SyncAgent, streamId: string) => getRoom(sync, streamId).members.myself
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook to get the data of the current user in a stream.
|
|
12
|
+
* @param streamId - The id of the stream to get the current user of.
|
|
13
|
+
* @param config - Configuration options for the observable.
|
|
14
|
+
* @returns The MemberModel of the current user.
|
|
15
|
+
*/
|
|
16
|
+
export const useMyMember = (streamId: string, config?: ObservableConfig.FromObservable<Member>) => {
|
|
17
|
+
const sync = useSyncAgent()
|
|
18
|
+
const myself = useMemo(() => getMyMember(sync, streamId), [sync, streamId])
|
|
19
|
+
const { data } = useObservable(myself.member, config)
|
|
20
|
+
return {
|
|
21
|
+
...data,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to set the ENS address of the current user in a stream.
|
|
27
|
+
* You should be validating if the ENS address belongs to the user before setting it.
|
|
28
|
+
* @param streamId - The id of the stream to set the ENS address of.
|
|
29
|
+
* @param config - Configuration options for the action.
|
|
30
|
+
* @returns The `setEnsAddress` action and its loading state.
|
|
31
|
+
*/
|
|
32
|
+
export const useSetEnsAddress = (
|
|
33
|
+
streamId: string,
|
|
34
|
+
config?: ActionConfig<Myself['setEnsAddress']>,
|
|
35
|
+
) => {
|
|
36
|
+
const sync = useSyncAgent()
|
|
37
|
+
const member = useMemo(() => getMyMember(sync, streamId), [sync, streamId])
|
|
38
|
+
const { action: setEnsAddress, ...rest } = useAction(member, 'setEnsAddress', config)
|
|
39
|
+
return { setEnsAddress, ...rest }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Hook to set the username of the current user in a stream.
|
|
44
|
+
* @param streamId - The id of the stream to set the username of.
|
|
45
|
+
* @param config - Configuration options for the action.
|
|
46
|
+
* @returns The `setUsername` action and its loading state.
|
|
47
|
+
*/
|
|
48
|
+
export const useSetUsername = (streamId: string, config?: ActionConfig<Myself['setUsername']>) => {
|
|
49
|
+
const sync = useSyncAgent()
|
|
50
|
+
const member = useMemo(() => getMyMember(sync, streamId), [sync, streamId])
|
|
51
|
+
const { action: setUsername, ...rest } = useAction(member, 'setUsername', config)
|
|
52
|
+
return { setUsername, ...rest }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hook to set the display name of the current user in a stream.
|
|
57
|
+
* @param streamId - The id of the stream to set the display name of.
|
|
58
|
+
* @param config - Configuration options for the action.
|
|
59
|
+
* @returns The `setDisplayName` action and its loading state.
|
|
60
|
+
*/
|
|
61
|
+
export const useSetDisplayName = (
|
|
62
|
+
streamId: string,
|
|
63
|
+
config?: ActionConfig<Myself['setDisplayName']>,
|
|
64
|
+
) => {
|
|
65
|
+
const sync = useSyncAgent()
|
|
66
|
+
const member = useMemo(() => getMyMember(sync, streamId), [sync, streamId])
|
|
67
|
+
const { action: setDisplayName, ...rest } = useAction(member, 'setDisplayName', config)
|
|
68
|
+
return { setDisplayName, ...rest }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Hook to set the NFT of the current user in a stream.
|
|
73
|
+
* You should be validating if the NFT belongs to the user before setting it.
|
|
74
|
+
* @param streamId - The id of the stream to set the NFT of.
|
|
75
|
+
* @param config - Configuration options for the action.
|
|
76
|
+
* @returns The `setNft` action and its loading state.
|
|
77
|
+
*/
|
|
78
|
+
export const useSetNft = (streamId: string, config?: ActionConfig<Myself['setNft']>) => {
|
|
79
|
+
const sync = useSyncAgent()
|
|
80
|
+
const member = useMemo(() => getMyMember(sync, streamId), [sync, streamId])
|
|
81
|
+
const { action: setNft, ...rest } = useAction(member, 'setNft', config)
|
|
82
|
+
return { setNft, ...rest }
|
|
83
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react'
|
|
3
|
+
import { type Observable, type PersistedModel } from '@towns-labs/sdk'
|
|
4
|
+
import { isPersistedModel } from './internals/utils'
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
7
|
+
export namespace ObservableConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for an observable.
|
|
10
|
+
* It can be used to configure the behavior of the `useObservable` hook.
|
|
11
|
+
*/
|
|
12
|
+
export type FromObservable<Observable_> =
|
|
13
|
+
Observable_ extends Observable<infer Data> ? FromData<Data> : never
|
|
14
|
+
|
|
15
|
+
// TODO: Some util props:
|
|
16
|
+
// - select: select a subset of the data, or transform it
|
|
17
|
+
// - remove onError is is not a persisted model data
|
|
18
|
+
/**
|
|
19
|
+
* Create configuration options for an observable from the data type.
|
|
20
|
+
* It can be used to configure the behavior of the `useObservable` hook.
|
|
21
|
+
*/
|
|
22
|
+
export type FromData<Data> =
|
|
23
|
+
Data extends PersistedModel<infer UnwrappedData>
|
|
24
|
+
? {
|
|
25
|
+
/**
|
|
26
|
+
* Trigger the update immediately, without waiting for the first update.
|
|
27
|
+
* @defaultValue true
|
|
28
|
+
*/
|
|
29
|
+
fireImmediately?: boolean
|
|
30
|
+
/** Callback function to be called when the data is updated. */
|
|
31
|
+
onUpdate?: (data: UnwrappedData) => void
|
|
32
|
+
// TODO: when an error occurs? store errors? RPC error?
|
|
33
|
+
/** Callback function to be called when an error occurs. */
|
|
34
|
+
onError?: (error: Error) => void
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
/**
|
|
38
|
+
* Trigger the update immediately, without waiting for the first update.
|
|
39
|
+
* @defaultValue true
|
|
40
|
+
*/
|
|
41
|
+
fireImmediately?: boolean
|
|
42
|
+
/** Callback function to be called when the data is updated. */
|
|
43
|
+
onUpdate?: (data: Data) => void
|
|
44
|
+
// TODO: when an error occurs? store errors? RPC error?
|
|
45
|
+
/** Callback function to be called when an error occurs. */
|
|
46
|
+
onError?: (error: Error) => void
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The value returned by the useObservable hook.
|
|
52
|
+
* If the observable is a PersistedModel, it will include error and status information.
|
|
53
|
+
*/
|
|
54
|
+
export type ObservableValue<T> = {
|
|
55
|
+
/** The data of the model. */
|
|
56
|
+
data: T
|
|
57
|
+
/** If the model is in an error state, this will be the error. */
|
|
58
|
+
error: Error | undefined
|
|
59
|
+
/** The status of the model. */
|
|
60
|
+
status: 'loading' | 'loaded' | 'error'
|
|
61
|
+
/** True if the model is in a loading state. */
|
|
62
|
+
isLoading: boolean
|
|
63
|
+
/** True if the model is in an error state. */
|
|
64
|
+
isError: boolean
|
|
65
|
+
/** True if the data is loaded. */
|
|
66
|
+
isLoaded: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* This hook subscribes to an observable and returns the value of the observable.
|
|
71
|
+
* @param observable - The observable to subscribe to.
|
|
72
|
+
* @param config - Configuration options for the observable.
|
|
73
|
+
* @returns The value of the observable.
|
|
74
|
+
*/
|
|
75
|
+
export function useObservable<
|
|
76
|
+
Model,
|
|
77
|
+
Data = Model extends PersistedModel<infer UnwrappedData> ? UnwrappedData : Model,
|
|
78
|
+
>(observable: Observable<Model>, config?: ObservableConfig.FromData<Model>): ObservableValue<Data> {
|
|
79
|
+
const opts = useMemo(() => ({ fireImmediately: true, ...config }), [config])
|
|
80
|
+
|
|
81
|
+
const subscribeFn = useCallback(
|
|
82
|
+
(subFn: () => void) => {
|
|
83
|
+
return observable.subscribe(subFn, {
|
|
84
|
+
fireImediately: opts?.fireImmediately,
|
|
85
|
+
})
|
|
86
|
+
},
|
|
87
|
+
[observable, opts?.fireImmediately],
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const value = useSyncExternalStore(subscribeFn, () => observable.value)
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (isPersistedModel(value)) {
|
|
94
|
+
if (value.status === 'loaded') {
|
|
95
|
+
opts.onUpdate?.(value.data)
|
|
96
|
+
}
|
|
97
|
+
if (value.status === 'error') {
|
|
98
|
+
opts.onError?.(value.error)
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
opts.onUpdate?.(value)
|
|
102
|
+
}
|
|
103
|
+
}, [opts, value])
|
|
104
|
+
|
|
105
|
+
const data = useMemo(() => {
|
|
106
|
+
if (isPersistedModel(value)) {
|
|
107
|
+
const { data, status } = value
|
|
108
|
+
return {
|
|
109
|
+
data: data as unknown as Data,
|
|
110
|
+
error: status === 'error' ? value.error : undefined,
|
|
111
|
+
status,
|
|
112
|
+
isLoading: status === 'loading',
|
|
113
|
+
isError: status === 'error',
|
|
114
|
+
isLoaded: status === 'loaded',
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
return {
|
|
118
|
+
data: value as unknown as Data,
|
|
119
|
+
error: undefined,
|
|
120
|
+
status: 'loaded' as const,
|
|
121
|
+
isLoading: false,
|
|
122
|
+
isError: false,
|
|
123
|
+
isLoaded: true,
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}, [value]) satisfies ObservableValue<Data>
|
|
127
|
+
|
|
128
|
+
return data
|
|
129
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MessageReactions, Space, assert } from '@towns-labs/sdk'
|
|
2
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
3
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
4
|
+
import { getRoom } from './utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to get the reactions of a specific stream.
|
|
8
|
+
* @param streamId - The id of the stream to get the reactions of.
|
|
9
|
+
* @param config - Configuration options for the observable.
|
|
10
|
+
* @returns The reactions of the stream as a map from the message eventId to the reaction.
|
|
11
|
+
*/
|
|
12
|
+
export const useReactions = (
|
|
13
|
+
streamId: string,
|
|
14
|
+
config?: ObservableConfig.FromData<Record<string, MessageReactions>>,
|
|
15
|
+
) => {
|
|
16
|
+
const sync = useSyncAgent()
|
|
17
|
+
const room = getRoom(sync, streamId)
|
|
18
|
+
assert(!(room instanceof Space), 'Space does not have reactions')
|
|
19
|
+
|
|
20
|
+
return useObservable(room.timeline.reactions, config)
|
|
21
|
+
}
|
package/src/useRedact.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type 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 your own message in a channel stream.
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ### Redact a message
|
|
13
|
+
*
|
|
14
|
+
* You can use `redact` to redact a message in a stream.
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { useRedact } from '@towns-labs/react-sdk'
|
|
17
|
+
*
|
|
18
|
+
* const { redact } = useRedact(streamId)
|
|
19
|
+
* redact({ 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 useRedact = (streamId: string, config?: ActionConfig<Channel['redact']>) => {
|
|
36
|
+
const sync = useSyncAgent()
|
|
37
|
+
const room = getRoom(sync, streamId)
|
|
38
|
+
assert(!(room instanceof Space), 'Space does not have reactions')
|
|
39
|
+
const { action: redact, ...rest } = useAction(room, 'redact', config)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
redact,
|
|
43
|
+
...rest,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type MessageTimeline, Space, assert } from '@towns-labs/sdk'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
6
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
7
|
+
import { getRoom } from './utils'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to get the scrollback action for a stream.
|
|
11
|
+
*
|
|
12
|
+
* Scrollback is the action of getting miniblocks from a stream before a certain point in time.
|
|
13
|
+
* Getting miniblocks means that new events that are possibly new messages, reactions and so on are fetched.
|
|
14
|
+
*
|
|
15
|
+
* @param streamId - The id of the stream to get the scrollback action for.
|
|
16
|
+
* @param config - Configuration options for the action.
|
|
17
|
+
* @returns The `scrollback` action and its loading state.
|
|
18
|
+
*/
|
|
19
|
+
export const useScrollback = (
|
|
20
|
+
streamId: string,
|
|
21
|
+
config?: ActionConfig<MessageTimeline['scrollback']>,
|
|
22
|
+
) => {
|
|
23
|
+
const sync = useSyncAgent()
|
|
24
|
+
const room = useMemo(() => getRoom(sync, streamId), [sync, streamId])
|
|
25
|
+
assert(!(room instanceof Space), 'cant scrollback spaces')
|
|
26
|
+
const { action: scrollback, ...rest } = useAction(room.timeline, 'scrollback', config)
|
|
27
|
+
return {
|
|
28
|
+
scrollback,
|
|
29
|
+
...rest,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Channel, Space, assert } from '@towns-labs/sdk'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { type ActionConfig, useAction } from './internals/useAction'
|
|
6
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
7
|
+
import { getRoom } from './utils'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to send a message to a stream. Can be used to send a message to a channel or a dm/group dm.
|
|
11
|
+
* @param streamId - The id of the stream to send the message to.
|
|
12
|
+
* @param config - Configuration options for the action.
|
|
13
|
+
* @returns The sendMessage action and the status of the action.
|
|
14
|
+
*/
|
|
15
|
+
export const useSendMessage = (
|
|
16
|
+
streamId: string,
|
|
17
|
+
// TODO: now that we're using runtime check for room type, Gdm/Dm will have the same config as Channel.
|
|
18
|
+
// Its not a problem, both should be the same, but we need more abstractions around this on the SyncAgent
|
|
19
|
+
config?: ActionConfig<Channel['sendMessage']>,
|
|
20
|
+
) => {
|
|
21
|
+
const sync = useSyncAgent()
|
|
22
|
+
const room = useMemo(() => getRoom(sync, streamId), [streamId, sync])
|
|
23
|
+
assert(!(room instanceof Space), 'room cant be a space')
|
|
24
|
+
const { action: sendMessage, ...rest } = useAction(room, 'sendMessage', config)
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
/** Sends a message to the stream.
|
|
28
|
+
* @param message - The message to send.
|
|
29
|
+
* @param options - Additional options for the message.
|
|
30
|
+
* @returns The event id of the message.
|
|
31
|
+
*/
|
|
32
|
+
sendMessage,
|
|
33
|
+
...rest,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type 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 send a reaction to a message in a stream.
|
|
10
|
+
*
|
|
11
|
+
* Reaction can be any string value, including emojis.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { useSendReaction } from '@towns-labs/react-sdk'
|
|
16
|
+
*
|
|
17
|
+
* const { sendReaction } = useSendReaction('stream-id')
|
|
18
|
+
* sendReaction(messageEventId, '🔥')
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @param streamId - The id of the stream to send the reaction to.
|
|
22
|
+
* @param config - Configuration options for the action.
|
|
23
|
+
* @returns The `sendReaction` action and its loading state.
|
|
24
|
+
*/
|
|
25
|
+
export const useSendReaction = (
|
|
26
|
+
streamId: string,
|
|
27
|
+
config?: ActionConfig<Channel['sendReaction']>,
|
|
28
|
+
) => {
|
|
29
|
+
const sync = useSyncAgent()
|
|
30
|
+
const room = getRoom(sync, streamId)
|
|
31
|
+
assert(!(room instanceof Space), 'Space does not have reactions')
|
|
32
|
+
|
|
33
|
+
const { action: sendReaction, ...rest } = useAction(room, 'sendReaction', config)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
sendReaction,
|
|
37
|
+
...rest,
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/useSpace.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import type { Space } 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 space.
|
|
10
|
+
* You can use this hook to get space metadata and ids of channels in the space.
|
|
11
|
+
* @param spaceId - The id of the space to get data about.
|
|
12
|
+
* @param config - Configuration options for the observable.
|
|
13
|
+
* @returns The SpaceModel data.
|
|
14
|
+
* @example
|
|
15
|
+
* You can use this hook to display the data about a space:
|
|
16
|
+
*
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { useSpace } from '@towns-labs/react-sdk'
|
|
19
|
+
*
|
|
20
|
+
* const Space = ({ spaceId }: { spaceId: string }) => {
|
|
21
|
+
* const { data: space } = useSpace(spaceId)
|
|
22
|
+
* return <div>{space.metadata?.name || 'Unnamed Space'}</div>
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const useSpace = (spaceId: string, config?: ObservableConfig.FromObservable<Space>) => {
|
|
27
|
+
const sync = useSyncAgent()
|
|
28
|
+
const observable = useMemo(() => sync.spaces.getSpace(spaceId), [sync, spaceId])
|
|
29
|
+
return useObservable(observable, config)
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { Space, TimelinesMap, assert } from '@towns-labs/sdk'
|
|
3
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
4
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
5
|
+
import { getRoom } from './utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to get the threads from a stream.
|
|
9
|
+
*
|
|
10
|
+
* @param streamId - The id of the stream to get the threads from.
|
|
11
|
+
* @param config - Configuration options for the observable.
|
|
12
|
+
* @returns The threads of the stream as a map from the message eventId to a thread.
|
|
13
|
+
*/
|
|
14
|
+
export const useThreads = (
|
|
15
|
+
streamId: string,
|
|
16
|
+
config?: ObservableConfig.FromObservable<TimelinesMap>,
|
|
17
|
+
) => {
|
|
18
|
+
const sync = useSyncAgent()
|
|
19
|
+
const room = useMemo(() => getRoom(sync, streamId), [streamId, sync])
|
|
20
|
+
assert(!(room instanceof Space), 'room cant be a space')
|
|
21
|
+
return useObservable(room.timeline.threads, config)
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Space, type TimelineEvent, assert } from '@towns-labs/sdk'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
4
|
+
import { getRoom } from './utils'
|
|
5
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to get the timeline events from a stream.
|
|
9
|
+
*
|
|
10
|
+
* You can use the `useTimeline` hook to get the timeline events from a channel stream, dm stream or group dm stream
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { useTimeline } from '@towns-labs/react-sdk'
|
|
15
|
+
* import { RiverTimelineEvent } from '@towns-labs/sdk'
|
|
16
|
+
*
|
|
17
|
+
* const { data: events } = useTimeline(streamId)
|
|
18
|
+
*
|
|
19
|
+
* // You can filter the events by their kind
|
|
20
|
+
* const messages = events.filter((event) => event.content?.kind === RiverTimelineEvent.ChannelMessage)
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param streamId - The id of the stream to get the timeline events from.
|
|
24
|
+
* @param config - Configuration options for the observable.
|
|
25
|
+
* @returns The timeline events of the stream as an observable.
|
|
26
|
+
*/
|
|
27
|
+
export const useTimeline = (
|
|
28
|
+
streamId: string,
|
|
29
|
+
config?: ObservableConfig.FromObservable<TimelineEvent[]>,
|
|
30
|
+
) => {
|
|
31
|
+
const sync = useSyncAgent()
|
|
32
|
+
const room = useMemo(() => getRoom(sync, streamId), [streamId, sync])
|
|
33
|
+
assert(!(room instanceof Space), 'Space does not have timeline')
|
|
34
|
+
return useObservable(room.timeline.events, config)
|
|
35
|
+
}
|
package/src/useTowns.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import type { Observable, SyncAgent } from '@towns-labs/sdk'
|
|
3
|
+
import { type ObservableConfig, useObservable } from './useObservable'
|
|
4
|
+
import { useSyncAgent } from './useSyncAgent'
|
|
5
|
+
|
|
6
|
+
type SyncSelector = SyncAgent['observables']
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to get an observable from the sync agent.
|
|
10
|
+
*
|
|
11
|
+
* An alternative of our premade hooks, allowing the creation of custom abstractions.
|
|
12
|
+
* @param selector - A selector function to get a observable from the sync agent.
|
|
13
|
+
* @param config - Configuration options for the observable.
|
|
14
|
+
* @returns The data from the selected observable.
|
|
15
|
+
*/
|
|
16
|
+
export function useTowns<T>(
|
|
17
|
+
selector: (sync: SyncSelector) => Observable<T>,
|
|
18
|
+
config?: ObservableConfig.FromData<T>,
|
|
19
|
+
) {
|
|
20
|
+
const syncAgent = useSyncAgent()
|
|
21
|
+
return useObservable(selector(syncAgent.observables), config)
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { AuthStatus } from '@towns-labs/sdk'
|
|
4
|
+
import { useTowns } from './useTowns'
|
|
5
|
+
import type { ObservableConfig } from './useObservable'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to get the auth status of the user connection with the Towns network.
|
|
9
|
+
* @param config - Configuration options for the observable.
|
|
10
|
+
* @returns An object containing the current AuthStatus status and boolean flags for each possible status.
|
|
11
|
+
*/
|
|
12
|
+
export const useTownsAuthStatus = (config?: ObservableConfig.FromObservable<AuthStatus>) => {
|
|
13
|
+
const { data: status } = useTowns((s) => s.riverAuthStatus, config)
|
|
14
|
+
return {
|
|
15
|
+
/** The current AuthStatus of the user connection with the Towns network. */
|
|
16
|
+
status,
|
|
17
|
+
/** Whether the user connection with the Towns network is initializing. */
|
|
18
|
+
isInitializing: status === AuthStatus.Initializing,
|
|
19
|
+
/** Whether the user connection with the Towns network is evaluating credentials. */
|
|
20
|
+
isEvaluatingCredentials: status === AuthStatus.EvaluatingCredentials,
|
|
21
|
+
/** Whether the user connection with the Towns network is credentialed. */
|
|
22
|
+
isCredentialed: status === AuthStatus.Credentialed,
|
|
23
|
+
/** Whether the user connection with the Towns network is connecting to Towns. */
|
|
24
|
+
isConnectingToTowns: status === AuthStatus.ConnectingToRiver,
|
|
25
|
+
/** Whether the user connection with the Towns network is connected to Towns. */
|
|
26
|
+
isConnectedToTowns: status === AuthStatus.ConnectedToRiver,
|
|
27
|
+
/** Whether the user connection with the Towns network is disconnected. */
|
|
28
|
+
isDisconnected: status === AuthStatus.Disconnected,
|
|
29
|
+
/** Whether the user connection with the Towns network is in an error state. */
|
|
30
|
+
isError: status === AuthStatus.Error,
|
|
31
|
+
}
|
|
32
|
+
}
|