Skip to content

Commit 343b803

Browse files
committed
Merge branch 'develop'
2 parents c60f0c1 + b25b4b7 commit 343b803

File tree

327 files changed

+4931
-982
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

327 files changed

+4931
-982
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = {
1919
'eol-last': [1, 'always'],
2020
'multiline-ternary': [1, 'always-multiline'],
2121
'no-nested-ternary': 1,
22+
'no-empty': [1, { 'allowEmptyCatch': true }],
2223
'comma-dangle': [1, 'always-multiline'],
2324
'@typescript-eslint/ban-types': 0,
2425
'@typescript-eslint/ban-ts-comment': 0,

.github/pull_request_template.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
## External Contributions
3+
4+
This project is not yet set up to accept pull requests from external contributors.
5+
6+
If you have a pull request that you believe should be accepted, please contact
7+
the Developer Relations team <developer-advocates@sendbird.com> with details
8+
and we'll evaluate if we can setup a [CLA](https://en.wikipedia.org/wiki/Contributor_License_Agreement) to allow for the contribution.
9+
10+
## For Internal Contributors
11+
12+
[TICKET_ID]
13+
14+
## Description Of Changes
15+
16+
Add a brief description of the changes that you have involved in this PR
17+
18+
## Types Of Changes
19+
20+
What types of changes does your code introduce to this project?
21+
Put an `x` in the boxes that apply_
22+
23+
- [ ] Bugfix
24+
- [ ] New feature
25+
- [ ] Documentation (correction or otherwise)
26+
- [ ] Cosmetics (whitespace, appearance (ex) Prettier)
27+
- [ ] Build configuration
28+
- [ ] Improvement (refactor code)
29+
- [ ] Test
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
it.todo('write a test');
1+
it.todo('@sendbird/chat-react-hooks');
Lines changed: 10 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,17 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2-
import type Sendbird from 'sendbird';
1+
import type { SendbirdChatSDK } from '@sendbird/uikit-utils';
32

4-
import { arrayToMap } from '@sendbird/uikit-utils';
5-
6-
import useChannelHandler from '../handler/useChannelHandler';
73
import type { UseGroupChannelList, UseGroupChannelListOptions } from '../types';
4+
import { useGroupChannelListWithCollection } from './useGroupChannelListWithCollection';
5+
import { useGroupChannelListWithQuery } from './useGroupChannelListWithQuery';
86

9-
type GroupChannelMap = Record<string, Sendbird.GroupChannel>;
10-
11-
const createGroupChannelListQuery = (
12-
sdk: Sendbird.SendBirdInstance,
13-
queryCreator: UseGroupChannelListOptions['queryFactory'],
14-
) => {
15-
const passedQuery = queryCreator?.();
16-
if (passedQuery) return passedQuery;
17-
18-
const defaultQuery = sdk.GroupChannel.createMyGroupChannelListQuery();
19-
defaultQuery.memberStateFilter = 'all';
20-
defaultQuery.order = 'latest_last_message';
21-
defaultQuery.includeEmpty = true;
22-
defaultQuery.limit = 10;
23-
return defaultQuery;
24-
};
25-
26-
const useGroupChannelList = (
27-
sdk: Sendbird.SendBirdInstance,
7+
export const useGroupChannelList = (
8+
sdk: SendbirdChatSDK,
289
userId?: string,
2910
options?: UseGroupChannelListOptions,
3011
): UseGroupChannelList => {
31-
const queryRef = useRef<Sendbird.GroupChannelListQuery>();
32-
const [groupChannelMap, setGroupChannelMap] = useState<GroupChannelMap>({});
33-
const [refreshing, setRefreshing] = useState(false);
34-
35-
const init = useCallback(
36-
async (uid?: string) => {
37-
if (uid) {
38-
queryRef.current = createGroupChannelListQuery(sdk, options?.queryFactory);
39-
40-
const channels: Sendbird.GroupChannel[] = await queryRef.current.next();
41-
setGroupChannelMap((prev) => ({ ...prev, ...arrayToMap(channels, 'url') }));
42-
channels.forEach((channel) => sdk.markAsDelivered(channel.url));
43-
} else {
44-
setGroupChannelMap({});
45-
}
46-
},
47-
[sdk, options?.queryFactory],
48-
);
49-
50-
const updateChannel = (channel: Sendbird.OpenChannel | Sendbird.GroupChannel) => {
51-
if (channel.isGroupChannel()) update(channel);
52-
};
53-
54-
useChannelHandler(
55-
sdk,
56-
'useGroupChannelList',
57-
{
58-
onChannelChanged: updateChannel,
59-
onChannelFrozen: updateChannel,
60-
onChannelUnfrozen: updateChannel,
61-
onChannelDeleted(channelUrl: string) {
62-
if (!groupChannelMap[channelUrl]) return;
63-
setGroupChannelMap((prevState) => {
64-
delete prevState[channelUrl];
65-
return { ...prevState };
66-
});
67-
},
68-
onChannelMemberCountChanged(channels: Array<Sendbird.GroupChannel>) {
69-
const validChannels = channels.filter((channel) => channel.isGroupChannel() && groupChannelMap[channel.url]);
70-
setGroupChannelMap((prevState) => {
71-
validChannels.forEach((channel) => (prevState[channel.url] = channel));
72-
return { ...prevState };
73-
});
74-
},
75-
},
76-
[groupChannelMap],
77-
);
78-
79-
useEffect(() => {
80-
init(userId);
81-
}, [init, userId]);
82-
83-
const groupChannels = useMemo(() => {
84-
const channels = Object.values(groupChannelMap);
85-
if (options?.queryFactory) return channels.sort(options?.sortComparator);
86-
return channels;
87-
}, [groupChannelMap, options?.sortComparator]);
88-
89-
const refresh = useCallback(async () => {
90-
setRefreshing(true);
91-
await init(userId);
92-
setRefreshing(false);
93-
}, [init, userId]);
94-
95-
const update = useCallback(
96-
(channel: Sendbird.GroupChannel) => {
97-
sdk.markAsDelivered(channel.url);
98-
setGroupChannelMap((prev) => ({ ...prev, [channel.url]: channel }));
99-
},
100-
[sdk],
101-
);
102-
103-
const loadMore = useCallback(async () => {
104-
if (queryRef.current?.hasNext) {
105-
const channels = await queryRef.current.next();
106-
setGroupChannelMap((prev) => ({ ...prev, ...arrayToMap(channels, 'url') }));
107-
channels.forEach((channel) => sdk.markAsDelivered(channel.url));
108-
}
109-
}, [sdk]);
110-
111-
return { groupChannels, update, refresh, refreshing, loadMore };
12+
if (sdk.isCacheEnabled) {
13+
return useGroupChannelListWithCollection(sdk, userId, options);
14+
} else {
15+
return useGroupChannelListWithQuery(sdk, userId, options);
16+
}
11217
};
113-
114-
export default useGroupChannelList;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2+
import type Sendbird from 'sendbird';
3+
4+
import type { SendbirdChannel, SendbirdChatSDK } from '@sendbird/uikit-utils';
5+
import { Logger, arrayToMap, useAsyncEffect, useUniqId } from '@sendbird/uikit-utils';
6+
7+
import useInternalPubSub from '../common/useInternalPubSub';
8+
import useChannelHandler from '../handler/useChannelHandler';
9+
import type { UseGroupChannelList, UseGroupChannelListOptions } from '../types';
10+
11+
type GroupChannelMap = Record<string, Sendbird.GroupChannel>;
12+
13+
const createGroupChannelListCollection = (
14+
sdk: SendbirdChatSDK,
15+
collectionCreator: UseGroupChannelListOptions['collectionCreator'],
16+
) => {
17+
const passedCollection = collectionCreator?.();
18+
if (passedCollection) return passedCollection;
19+
20+
const defaultCollection = sdk.GroupChannel.createGroupChannelCollection();
21+
const filter = new sdk.GroupChannelFilter();
22+
filter.includeEmpty = true;
23+
filter.memberStateFilter = sdk.GroupChannelFilter.MemberStateFilter.ALL;
24+
return defaultCollection
25+
.setLimit(10)
26+
.setFilter(filter)
27+
.setOrder(sdk.GroupChannelCollection.GroupChannelOrder.LATEST_LAST_MESSAGE)
28+
.build();
29+
};
30+
31+
const HOOK_NAME = 'useGroupChannelListWithCollection';
32+
33+
export const useGroupChannelListWithCollection = (
34+
sdk: SendbirdChatSDK,
35+
userId?: string,
36+
options?: UseGroupChannelListOptions,
37+
): UseGroupChannelList => {
38+
const id = useUniqId(HOOK_NAME);
39+
const { events, subscribe } = useInternalPubSub();
40+
41+
const collectionRef = useRef<Sendbird.GroupChannelCollection>();
42+
const [loading, setLoading] = useState(false);
43+
const [refreshing, setRefreshing] = useState(false);
44+
45+
const [groupChannelMap, setGroupChannelMap] = useState<GroupChannelMap>({});
46+
const groupChannels = useMemo(() => {
47+
const channels = Object.values(groupChannelMap);
48+
if (options?.sortComparator) return channels.sort(options?.sortComparator);
49+
return channels;
50+
}, [groupChannelMap, options?.sortComparator]);
51+
52+
// ---------- internal methods ---------- //
53+
const updateChannels = (channels: SendbirdChannel[], clearPrev: boolean) => {
54+
const groupChannels = channels.filter((c): c is Sendbird.GroupChannel => c.isGroupChannel());
55+
if (clearPrev) setGroupChannelMap(arrayToMap(groupChannels, 'url'));
56+
else setGroupChannelMap((prev) => ({ ...prev, ...arrayToMap(groupChannels, 'url') }));
57+
groupChannels.forEach((channel) => sdk.markAsDelivered(channel.url));
58+
};
59+
const deleteChannels = (channelUrls: string[]) => {
60+
setGroupChannelMap(({ ...draft }) => {
61+
channelUrls.forEach((url) => delete draft[url]);
62+
return draft;
63+
});
64+
};
65+
const init = useCallback(
66+
async (uid?: string) => {
67+
if (collectionRef.current) collectionRef.current?.dispose();
68+
69+
if (uid) {
70+
collectionRef.current = createGroupChannelListCollection(sdk, options?.collectionCreator);
71+
if (collectionRef.current?.hasMore) {
72+
updateChannels(await collectionRef.current?.loadMore(), true);
73+
}
74+
75+
collectionRef.current?.setGroupChannelCollectionHandler({
76+
onChannelsAdded(_, channels) {
77+
updateChannels(channels, false);
78+
},
79+
onChannelsUpdated(_, channels) {
80+
updateChannels(channels, false);
81+
},
82+
onChannelsDeleted(_, channelUrls) {
83+
deleteChannels(channelUrls);
84+
},
85+
});
86+
}
87+
},
88+
[sdk, options?.collectionCreator],
89+
);
90+
// ---------- internal methods ends ---------- //
91+
92+
// ---------- internal hooks ---------- //
93+
useEffect(() => {
94+
return () => {
95+
if (collectionRef.current) collectionRef.current?.dispose();
96+
};
97+
}, []);
98+
useAsyncEffect(async () => {
99+
setLoading(true);
100+
await init(userId);
101+
setLoading(false);
102+
}, [init, userId]);
103+
104+
useEffect(() => {
105+
const unsubscribes = [
106+
subscribe(
107+
events.ChannelUpdated,
108+
({ channel }, err) => {
109+
if (err) Logger.warn(HOOK_NAME, 'Cannot update channels', err);
110+
else updateChannels([channel], false);
111+
},
112+
HOOK_NAME,
113+
),
114+
subscribe(
115+
events.ChannelDeleted,
116+
({ channelUrl }, err) => {
117+
if (err) Logger.warn(HOOK_NAME, 'Cannot delete channels', err);
118+
else deleteChannels([channelUrl]);
119+
},
120+
HOOK_NAME,
121+
),
122+
];
123+
124+
return () => {
125+
unsubscribes.forEach((fn) => fn());
126+
};
127+
}, []);
128+
129+
useChannelHandler(
130+
sdk,
131+
HOOK_NAME + id,
132+
{
133+
onChannelChanged: (channel) => updateChannels([channel], false),
134+
onChannelFrozen: (channel) => updateChannels([channel], false),
135+
onChannelUnfrozen: (channel) => updateChannels([channel], false),
136+
onChannelMemberCountChanged: (channels) => updateChannels(channels, false),
137+
onChannelDeleted: (url) => deleteChannels([url]),
138+
onUserJoined: (channel) => updateChannels([channel], false),
139+
onUserLeft: (channel, user) => {
140+
const isMe = user.userId === userId;
141+
if (isMe) deleteChannels([channel.url]);
142+
else updateChannels([channel], false);
143+
},
144+
},
145+
[sdk, userId],
146+
);
147+
// ---------- internal hooks ends ---------- //
148+
149+
// ---------- returns methods ---------- //
150+
const refresh = useCallback(async () => {
151+
setRefreshing(true);
152+
await init(userId);
153+
setRefreshing(false);
154+
}, [init, userId]);
155+
156+
const update = useCallback(
157+
(channel: Sendbird.GroupChannel) => {
158+
sdk.markAsDelivered(channel.url);
159+
setGroupChannelMap((prev) => ({ ...prev, [channel.url]: channel }));
160+
},
161+
[sdk],
162+
);
163+
164+
const next = useCallback(async () => {
165+
if (collectionRef.current?.hasMore) {
166+
const channels = await collectionRef.current?.loadMore();
167+
setGroupChannelMap((prev) => ({ ...prev, ...arrayToMap(channels, 'url') }));
168+
channels.forEach((channel) => sdk.markAsDelivered(channel.url));
169+
}
170+
}, [sdk]);
171+
// ---------- returns methods ends ---------- //
172+
173+
return {
174+
loading,
175+
groupChannels,
176+
refresh,
177+
refreshing,
178+
next,
179+
update,
180+
};
181+
};

0 commit comments

Comments
 (0)