Skip to content

Commit b122691

Browse files
committed
fix(uikit): support compatibility for removing AppState listener under 0.65
1 parent 0aa38d1 commit b122691

File tree

6 files changed

+71
-52
lines changed

6 files changed

+71
-52
lines changed

packages/uikit-react-native/src/contexts/SendbirdChatCtx.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import React, { useCallback, useEffect, useState } from 'react';
2-
import { AppState, AppStateStatus } from 'react-native';
1+
import React, { useCallback, useState } from 'react';
32

43
import { useAppFeatures } from '@sendbird/uikit-chat-hooks';
54
import type {
@@ -8,7 +7,7 @@ import type {
87
SendbirdUser,
98
SendbirdUserUpdateParams,
109
} from '@sendbird/uikit-utils';
11-
import { confirmAndMarkAsDelivered, useForceUpdate } from '@sendbird/uikit-utils';
10+
import { confirmAndMarkAsDelivered, useAppState, useForceUpdate } from '@sendbird/uikit-utils';
1211

1312
import type EmojiManager from '../libs/EmojiManager';
1413
import type ImageCompressionConfig from '../libs/ImageCompressionConfig';
@@ -123,16 +122,11 @@ export const SendbirdChatProvider = ({
123122
[sdkInstance, appFeatures.deliveryReceiptEnabled],
124123
);
125124

126-
useEffect(() => {
127-
const listener = (status: AppStateStatus) => {
128-
// 'active' | 'background' | 'inactive' | 'unknown' | 'extension';
129-
if (status === 'active') sdkInstance.connectionState === 'CLOSED' && sdkInstance.setForegroundState();
130-
else if (status === 'background') sdkInstance.connectionState === 'OPEN' && sdkInstance.setBackgroundState();
131-
};
132-
133-
const subscriber = AppState.addEventListener('change', listener);
134-
return () => subscriber.remove();
135-
}, [sdkInstance]);
125+
useAppState('change', (status) => {
126+
// 'active' | 'background' | 'inactive' | 'unknown' | 'extension';
127+
if (status === 'active') sdkInstance.connectionState === 'CLOSED' && sdkInstance.setForegroundState();
128+
else if (status === 'background') sdkInstance.connectionState === 'OPEN' && sdkInstance.setBackgroundState();
129+
});
136130

137131
const value: Context = {
138132
sdk: sdkInstance,

packages/uikit-react-native/src/fragments/createGroupChannelListFragment.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import React, { useEffect } from 'react';
2-
import { AppState } from 'react-native';
1+
import React from 'react';
32

43
import { useGroupChannelList } from '@sendbird/uikit-chat-hooks';
5-
import { PASS, useFreshCallback } from '@sendbird/uikit-utils';
4+
import { PASS, useAppState, useFreshCallback } from '@sendbird/uikit-utils';
65

76
import StatusComposition from '../components/StatusComposition';
87
import GroupChannelPreviewContainer from '../containers/GroupChannelPreviewContainer';
@@ -34,12 +33,9 @@ const createGroupChannelListFragment = (initModule?: Partial<GroupChannelListMod
3433
});
3534

3635
if (features.deliveryReceiptEnabled) {
37-
useEffect(() => {
38-
const listener = AppState.addEventListener('change', (status) => {
39-
if (status === 'active') groupChannels.forEach(markAsDeliveredWithChannel);
40-
});
41-
return () => listener.remove();
42-
}, []);
36+
useAppState('change', (status) => {
37+
if (status === 'active') groupChannels.forEach(markAsDeliveredWithChannel);
38+
});
4339
}
4440

4541
const _renderGroupChannelPreview: GroupChannelListProps['List']['renderGroupChannelPreview'] = useFreshCallback(

packages/uikit-utils/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
},
4444
"devDependencies": {
4545
"@types/react": "*",
46+
"@types/react-native": "*",
4647
"react": "17.0.2",
4748
"react-native": "0.67.4",
4849
"react-native-builder-bob": "^0.18.0",

packages/uikit-utils/src/hooks/index.ts

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import type { DependencyList } from 'react';
3-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
4-
import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context';
3+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
54

65
type Destructor = () => void;
76
type AsyncEffectCallback = () => void | Destructor | Promise<void> | Promise<Destructor>;
@@ -73,31 +72,3 @@ export const useFreshCallback = <T extends (...args: any[]) => any>(callback: T)
7372
ref.current = callback;
7473
return useCallback(((...args) => ref.current(...args)) as T, []);
7574
};
76-
77-
type EdgePaddingMap = {
78-
left: 'paddingLeft';
79-
right: 'paddingRight';
80-
top: 'paddingTop';
81-
bottom: 'paddingBottom';
82-
};
83-
const edgePaddingMap: EdgePaddingMap = {
84-
left: 'paddingLeft',
85-
right: 'paddingRight',
86-
top: 'paddingTop',
87-
bottom: 'paddingBottom',
88-
};
89-
export const useSafeAreaPadding = <
90-
T extends keyof EdgeInsets,
91-
Result extends { [key in EdgePaddingMap[T]]: EdgeInsets[T] },
92-
>(
93-
edges: T[],
94-
): Result => {
95-
const safeAreaInsets = useSafeAreaInsets();
96-
return useMemo(() => {
97-
return edges.reduce((map, edge) => {
98-
const paddingKey = edgePaddingMap[edge];
99-
map[paddingKey] = safeAreaInsets[edge];
100-
return map;
101-
}, {} as { [key in EdgePaddingMap[T]]: EdgeInsets[T] });
102-
}, edges) as Result;
103-
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useEffect, useMemo, useRef } from 'react';
2+
import { AppState, AppStateEvent, AppStateStatus } from 'react-native';
3+
import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context';
4+
5+
type EdgePaddingMap = {
6+
left: 'paddingLeft';
7+
right: 'paddingRight';
8+
top: 'paddingTop';
9+
bottom: 'paddingBottom';
10+
};
11+
const edgePaddingMap: EdgePaddingMap = {
12+
left: 'paddingLeft',
13+
right: 'paddingRight',
14+
top: 'paddingTop',
15+
bottom: 'paddingBottom',
16+
};
17+
18+
export const useSafeAreaPadding = <
19+
T extends keyof EdgeInsets,
20+
Result extends { [key in EdgePaddingMap[T]]: EdgeInsets[T] },
21+
>(
22+
edges: T[],
23+
): Result => {
24+
const safeAreaInsets = useSafeAreaInsets();
25+
return useMemo(() => {
26+
return edges.reduce((map, edge) => {
27+
const paddingKey = edgePaddingMap[edge];
28+
map[paddingKey] = safeAreaInsets[edge];
29+
return map;
30+
}, {} as { [key in EdgePaddingMap[T]]: EdgeInsets[T] });
31+
}, edges) as Result;
32+
};
33+
34+
type AppStateListener = (status: AppStateStatus) => void;
35+
36+
export const useAppState = (type: AppStateEvent, listener: AppStateListener) => {
37+
const callbackRef = useRef<AppStateListener>(listener);
38+
callbackRef.current = listener;
39+
40+
useEffect(() => {
41+
const eventListener = (state: AppStateStatus) => callbackRef.current(state);
42+
const subscriber = AppState.addEventListener(type, eventListener);
43+
44+
return () => {
45+
// @ts-ignore
46+
if (subscriber?.remove) {
47+
subscriber.remove();
48+
}
49+
// @ts-ignore
50+
else if (AppState.removeEventListener) {
51+
// @ts-ignore
52+
AppState.removeEventListener(type, eventListener);
53+
}
54+
};
55+
}, []);
56+
};

packages/uikit-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export { BufferedRequest } from './shared/bufferedRequest';
2222
export * from './shared';
2323

2424
export * from './hooks';
25+
export * from './hooks/react-native';
2526
export * from './ui-format/groupChannel';
2627
export * from './ui-format/common';
2728
export * from './sendbird/channel';

0 commit comments

Comments
 (0)