Skip to content

Commit 0949395

Browse files
committed
refactor: move PWC concern in a separate hook
1 parent 678cceb commit 0949395

File tree

3 files changed

+88
-50
lines changed

3 files changed

+88
-50
lines changed

package/src/components/Message/hooks/useMessageActionHandlers.ts

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useEffect, useMemo, useRef } from 'react';
2-
import { Alert, EventSubscription, Keyboard, Platform } from 'react-native';
1+
import { useMemo } from 'react';
2+
import { Alert } from 'react-native';
33

44
import { UserResponse } from 'stream-chat';
55

@@ -9,15 +9,12 @@ import type { ChannelContextValue } from '../../../contexts/channelContext/Chann
99
import type { ChatContextValue } from '../../../contexts/chatContext/ChatContext';
1010
import { MessageComposerAPIContextValue } from '../../../contexts/messageComposerContext/MessageComposerAPIContext';
1111
import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
12-
import { useMessageInputContext } from '../../../contexts/messageInputContext/MessageInputContext';
1312
import type { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
1413

1514
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
16-
import { useStableCallback } from '../../../hooks';
17-
import { useKeyboardVisibility } from '../../../hooks/useKeyboardVisibility';
15+
import { usePortalClosingKeyboardSafeCallback, useStableCallback } from '../../../hooks';
1816
import { useTranslatedMessage } from '../../../hooks/useTranslatedMessage';
1917
import { NativeHandlers } from '../../../native';
20-
import { KeyboardControllerPackage } from '../../KeyboardCompatibleView/KeyboardControllerAvoidingView';
2118

2219
export const useMessageActionHandlers = ({
2320
channel,
@@ -40,19 +37,9 @@ export const useMessageActionHandlers = ({
4037
const { t } = useTranslationContext();
4138
const handleResendMessage = useStableCallback(() => retrySendMessage(message));
4239
const translatedMessage = useTranslatedMessage(message);
43-
const { inputBoxRef } = useMessageInputContext();
44-
const isKeyboardVisible = useKeyboardVisibility();
45-
const keyboardDidShowSubscriptionRef = useRef<EventSubscription | undefined>(undefined);
4640

4741
const isMuted = useUserMuteActive(message.user);
4842

49-
const clearKeyboardDidShowSubscription = useStableCallback(() => {
50-
keyboardDidShowSubscriptionRef.current?.remove();
51-
keyboardDidShowSubscriptionRef.current = undefined;
52-
});
53-
54-
useEffect(() => clearKeyboardDidShowSubscription, [clearKeyboardDidShowSubscription]);
55-
5643
const handleQuotedReplyMessage = useStableCallback(() => {
5744
setQuotedMessage(message);
5845
});
@@ -127,40 +114,7 @@ export const useMessageActionHandlers = ({
127114
}
128115
});
129116

130-
const handleEditMessage = useStableCallback(() => {
131-
requestAnimationFrame(() =>
132-
requestAnimationFrame(() => {
133-
clearKeyboardDidShowSubscription();
134-
135-
const openEditingState = () => {
136-
clearKeyboardDidShowSubscription();
137-
setEditingState(message);
138-
};
139-
140-
if (!inputBoxRef.current) {
141-
openEditingState();
142-
return;
143-
}
144-
145-
if (isKeyboardVisible) {
146-
inputBoxRef.current?.focus();
147-
openEditingState();
148-
return;
149-
}
150-
151-
const event = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
152-
153-
keyboardDidShowSubscriptionRef.current = KeyboardControllerPackage?.KeyboardEvents
154-
? KeyboardControllerPackage.KeyboardEvents.addListener(
155-
'keyboardDidShow',
156-
openEditingState,
157-
)
158-
: Keyboard.addListener(event, openEditingState);
159-
160-
inputBoxRef.current?.focus();
161-
}),
162-
);
163-
});
117+
const handleEditMessage = usePortalClosingKeyboardSafeCallback(() => setEditingState(message));
164118

165119
const handleFlagMessage = useStableCallback(() => {
166120
if (!message.id) {

package/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './useMessageReminder';
99
export * from './useQueryReminders';
1010
export * from './useClientNotifications';
1111
export * from './useInAppNotificationsState';
12+
export * from './usePortalClosingKeyboardSafeCallback';
1213
export * from './useRAFCoalescedValue';
1314
export * from './useAudioPlayerControl';
1415
export * from './useAttachmentPickerState';
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useEffect, useRef } from 'react';
2+
import { EventSubscription, Keyboard, Platform } from 'react-native';
3+
4+
import { useKeyboardVisibility } from './useKeyboardVisibility';
5+
6+
import { useStableCallback } from './useStableCallback';
7+
8+
import { KeyboardControllerPackage } from '../components/KeyboardCompatibleView/KeyboardControllerAvoidingView';
9+
import { useMessageInputContext } from '../contexts/messageInputContext/MessageInputContext';
10+
11+
const SETTLE_FRAMES = Platform.OS === 'android' ? 2 : 0;
12+
13+
const scheduleAfterFrames = (callback: () => void, frames: number, rafIds: number[]) => {
14+
if (frames <= 0) {
15+
callback();
16+
return;
17+
}
18+
19+
const rafId = requestAnimationFrame(() => scheduleAfterFrames(callback, frames - 1, rafIds));
20+
rafIds.push(rafId);
21+
};
22+
23+
export const usePortalClosingKeyboardSafeCallback = <T extends unknown[]>(
24+
callback: (...args: T) => void,
25+
) => {
26+
const isKeyboardVisible = useKeyboardVisibility();
27+
const { inputBoxRef } = useMessageInputContext();
28+
const keyboardSubscriptionRef = useRef<EventSubscription | undefined>(undefined);
29+
const rafIdsRef = useRef<number[]>([]);
30+
const stableCallback = useStableCallback(callback);
31+
32+
const clearKeyboardSubscription = useStableCallback(() => {
33+
keyboardSubscriptionRef.current?.remove();
34+
keyboardSubscriptionRef.current = undefined;
35+
});
36+
37+
const clearScheduledFrames = useStableCallback(() => {
38+
rafIdsRef.current.forEach((rafId) => cancelAnimationFrame(rafId));
39+
rafIdsRef.current = [];
40+
});
41+
42+
useEffect(() => {
43+
return () => {
44+
clearKeyboardSubscription();
45+
clearScheduledFrames();
46+
};
47+
}, [clearKeyboardSubscription, clearScheduledFrames]);
48+
49+
return useStableCallback((...args: T) => {
50+
clearKeyboardSubscription();
51+
clearScheduledFrames();
52+
53+
scheduleAfterFrames(
54+
() => {
55+
const runCallback = () => {
56+
clearKeyboardSubscription();
57+
stableCallback(...args);
58+
};
59+
60+
if (!inputBoxRef.current) {
61+
runCallback();
62+
return;
63+
}
64+
65+
if (isKeyboardVisible) {
66+
inputBoxRef.current.focus();
67+
runCallback();
68+
return;
69+
}
70+
71+
const keyboardEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
72+
73+
keyboardSubscriptionRef.current = KeyboardControllerPackage?.KeyboardEvents
74+
? KeyboardControllerPackage.KeyboardEvents.addListener(keyboardEvent, runCallback)
75+
: Keyboard.addListener(keyboardEvent, runCallback);
76+
77+
inputBoxRef.current.focus();
78+
},
79+
SETTLE_FRAMES,
80+
rafIdsRef.current,
81+
);
82+
});
83+
};

0 commit comments

Comments
 (0)