Skip to content

Commit 23a179c

Browse files
committed
feat: implement swipable registry
1 parent 3c61a9d commit 23a179c

4 files changed

Lines changed: 98 additions & 7 deletions

File tree

package/src/components/ChannelList/ChannelList.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
ChannelsProvider,
2727
} from '../../contexts/channelsContext/ChannelsContext';
2828
import { useChatContext } from '../../contexts/chatContext/ChatContext';
29+
import { SwipeRegistryProvider } from '../../contexts/swipeableContext/SwipeRegistryContext';
2930
import type { ChannelListEventListenerOptions } from '../../types/types';
3031
import { ChannelPreview } from '../ChannelPreview/ChannelPreview';
3132
import { EmptyStateIndicator as EmptyStateIndicatorDefault } from '../Indicators/EmptyStateIndicator';
@@ -423,7 +424,9 @@ export const ChannelList = (props: ChannelListProps) => {
423424

424425
return (
425426
<ChannelsProvider value={channelsContext}>
426-
<List />
427+
<SwipeRegistryProvider>
428+
<List />
429+
</SwipeRegistryProvider>
427430
</ChannelsProvider>
428431
);
429432
};

package/src/components/ChannelPreview/ChannelPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
3636
const message = translatedLastMessage ? translatedLastMessage : lastMessage;
3737

3838
return (
39-
<SwipableWrapper>
39+
<SwipableWrapper swipeableId={channel.id}>
4040
<Preview channel={channel} muted={muted} unread={unread} lastMessage={message} />
4141
</SwipableWrapper>
4242
);

package/src/components/UIComponents/SwipableWrapper.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { PropsWithChildren, useCallback } from 'react';
1+
import React, { PropsWithChildren, useCallback, useEffect, useRef } from 'react';
22
import { StyleSheet } from 'react-native';
33

44
import Animated, {
@@ -9,9 +9,13 @@ import Animated, {
99
} from 'react-native-reanimated';
1010

1111
import ReanimatedSwipeable, {
12+
SwipeDirection,
13+
SwipeableMethods,
1214
SwipeableProps,
1315
} from 'react-native-gesture-handler/ReanimatedSwipeable';
1416

17+
import { useSwipeRegistryContext } from '../../contexts/swipeableContext/SwipeRegistryContext';
18+
1519
const ACTION_WIDTH = 50;
1620
const DEFAULT_RIGHT_ACTIONS_WIDTH = ACTION_WIDTH * 2;
1721

@@ -23,6 +27,7 @@ const animationOptions = {
2327
};
2428

2529
type SwipableWrapperProps = PropsWithChildren<{
30+
swipeableId?: string;
2631
swipableProps?: SwipeableProps;
2732
}>;
2833

@@ -44,24 +49,55 @@ const DefaultRightActions = ({ translation }: { translation: SharedValue<number>
4449
);
4550
};
4651

47-
export const SwipableWrapper = ({ children, swipableProps }: SwipableWrapperProps) => {
52+
export const SwipableWrapper = ({ children, swipeableId, swipableProps }: SwipableWrapperProps) => {
53+
const swipeRegistry = useSwipeRegistryContext();
54+
const swipeableRef = useRef<SwipeableMethods | null>(null);
55+
56+
const {
57+
onSwipeableWillOpen: onSwipeableWillOpenFromProps,
58+
renderRightActions: renderRightActionsFromProps,
59+
...restSwipableProps
60+
} = swipableProps ?? {};
61+
4862
const defaultRenderRightActions = useCallback(
4963
(_progress: SharedValue<number>, translation: SharedValue<number>) => (
5064
<DefaultRightActions translation={translation} />
5165
),
5266
[],
5367
);
5468

69+
useEffect(() => {
70+
if (!swipeRegistry || !swipeableId) {
71+
return;
72+
}
73+
74+
return swipeRegistry.registerSwipeable(swipeableId, () => {
75+
swipeableRef.current?.close();
76+
});
77+
}, [swipeRegistry, swipeableId]);
78+
79+
const onSwipeableWillOpen = useCallback(
80+
(direction: SwipeDirection.LEFT | SwipeDirection.RIGHT) => {
81+
if (swipeRegistry && swipeableId) {
82+
swipeRegistry.notifyWillOpen(swipeableId);
83+
}
84+
onSwipeableWillOpenFromProps?.(direction);
85+
},
86+
[onSwipeableWillOpenFromProps, swipeRegistry, swipeableId],
87+
);
88+
5589
return (
5690
<ReanimatedSwipeable
91+
ref={swipeableRef}
5792
animationOptions={animationOptions}
93+
friction={2}
94+
onSwipeableWillOpen={onSwipeableWillOpen}
5895
overshootLeft={false}
5996
overshootRight={true}
6097
overshootFriction={16}
61-
friction={2}
6298
renderLeftActions={undefined}
63-
renderRightActions={defaultRenderRightActions}
64-
{...swipableProps}
99+
renderRightActions={renderRightActionsFromProps ?? defaultRenderRightActions}
100+
{...restSwipableProps}
65101
>
66102
{children}
67103
</ReanimatedSwipeable>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, {
2+
PropsWithChildren,
3+
createContext,
4+
useCallback,
5+
useContext,
6+
useMemo,
7+
useRef,
8+
} from 'react';
9+
10+
type CloseSwipeable = () => void;
11+
12+
type SwipeRegistryContextValue = {
13+
notifyWillOpen: (id: string) => void;
14+
registerSwipeable: (id: string, close: CloseSwipeable) => () => void;
15+
};
16+
17+
const SwipeRegistryContext = createContext<SwipeRegistryContextValue | undefined>(undefined);
18+
19+
export const SwipeRegistryProvider = ({ children }: PropsWithChildren) => {
20+
const swipeablesRef = useRef<Map<string, CloseSwipeable>>(new Map());
21+
22+
const registerSwipeable = useCallback((id: string, close: CloseSwipeable) => {
23+
swipeablesRef.current.set(id, close);
24+
25+
return () => {
26+
const registered = swipeablesRef.current.get(id);
27+
if (registered === close) {
28+
swipeablesRef.current.delete(id);
29+
}
30+
};
31+
}, []);
32+
33+
const notifyWillOpen = useCallback((id: string) => {
34+
for (const [registeredId, close] of swipeablesRef.current.entries()) {
35+
if (registeredId !== id) {
36+
close();
37+
}
38+
}
39+
}, []);
40+
41+
const value = useMemo(
42+
() => ({
43+
notifyWillOpen,
44+
registerSwipeable,
45+
}),
46+
[notifyWillOpen, registerSwipeable],
47+
);
48+
49+
return <SwipeRegistryContext.Provider value={value}>{children}</SwipeRegistryContext.Provider>;
50+
};
51+
52+
export const useSwipeRegistryContext = () => useContext(SwipeRegistryContext);

0 commit comments

Comments
 (0)