SwipeableRow.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import React, { PropsWithChildren, useRef } from 'react';
  2. import { Animated, StyleSheet, Text, View } from 'react-native';
  3. import { RectButton, Swipeable } from 'react-native-gesture-handler';
  4. import { Colors } from 'src/theme';
  5. import { getFontSize } from 'src/utils';
  6. import { SheetManager } from 'react-native-actions-sheet';
  7. import PinIcon from 'assets/icons/messages/pin.svg';
  8. import ArchiveIcon from 'assets/icons/messages/archive.svg';
  9. import DotsIcon from 'assets/icons/messages/dots.svg';
  10. import UnpinIcon from 'assets/icons/messages/unpin.svg';
  11. import { ChatProps } from '../types';
  12. import { usePostSetArchiveMutation, usePostSetPinMutation } from '@api/chat';
  13. import { useChatStore } from 'src/stores/chatStore';
  14. interface AppleStyleSwipeableRowProps extends PropsWithChildren<unknown> {
  15. chat: ChatProps;
  16. token: string;
  17. onRowOpen: (ref: any) => void;
  18. refetch: () => void;
  19. refetchBlocked: () => void;
  20. }
  21. const SwipeableRow: React.FC<AppleStyleSwipeableRowProps> = ({
  22. children,
  23. chat,
  24. token,
  25. onRowOpen,
  26. refetch,
  27. refetchBlocked
  28. }) => {
  29. const swipeableRow = useRef<Swipeable>(null);
  30. const { setSelectedChat } = useChatStore();
  31. const { mutateAsync: pinChat } = usePostSetPinMutation();
  32. const { mutateAsync: archiveChat } = usePostSetArchiveMutation();
  33. const close = () => {
  34. swipeableRow.current?.close();
  35. };
  36. const renderRightAction = (
  37. text: string,
  38. color: string,
  39. x: number,
  40. progress: Animated.AnimatedInterpolation<number>
  41. ) => {
  42. const trans = progress.interpolate({
  43. inputRange: [0, 1],
  44. outputRange: [x, 0]
  45. });
  46. const pressHandler = async () => {
  47. close();
  48. if (text === 'More') {
  49. setSelectedChat(chat);
  50. SheetManager.show('more-modal', {
  51. payload: {
  52. uid: chat.uid,
  53. name: chat.name,
  54. avatar: chat.avatar,
  55. muted: chat.muted,
  56. token: token,
  57. refetch,
  58. refetchBlocked
  59. } as any
  60. });
  61. } else {
  62. await archiveChat({
  63. token,
  64. value: chat.archive === 1 ? 0 : 1,
  65. conversation_with_user: chat.uid
  66. });
  67. refetch();
  68. }
  69. };
  70. return (
  71. <Animated.View style={{ flex: 1, transform: [{ translateX: trans }] }}>
  72. <RectButton style={[styles.rightAction, { backgroundColor: color }]} onPress={pressHandler}>
  73. {text === 'More' ? (
  74. <DotsIcon height={18} width={18} />
  75. ) : (
  76. <ArchiveIcon height={18} width={18} />
  77. )}
  78. <Text style={styles.actionText}>{text}</Text>
  79. </RectButton>
  80. </Animated.View>
  81. );
  82. };
  83. const renderLeftAction = (
  84. text: string,
  85. color: string,
  86. x: number,
  87. progress: Animated.AnimatedInterpolation<number>
  88. ) => {
  89. const trans = progress.interpolate({
  90. inputRange: [0, 1],
  91. outputRange: [-x, 0]
  92. });
  93. const pressHandler = async () => {
  94. close();
  95. await pinChat({
  96. token,
  97. value: chat.pin === 1 ? 0 : 1,
  98. conversation_with_user: chat.uid
  99. });
  100. refetch();
  101. };
  102. return (
  103. <Animated.View style={{ flex: 1, transform: [{ translateX: trans }] }}>
  104. <RectButton style={[styles.rightAction, { backgroundColor: color }]} onPress={pressHandler}>
  105. {chat.pin === 1 ? <UnpinIcon width={18} /> : <PinIcon width={18} />}
  106. <Text style={styles.actionText}>{text}</Text>
  107. </RectButton>
  108. </Animated.View>
  109. );
  110. };
  111. const renderRightActions = (progress: Animated.AnimatedInterpolation<number>) => (
  112. <View
  113. style={{
  114. width: 168,
  115. flexDirection: 'row'
  116. }}
  117. >
  118. {renderRightAction(
  119. chat.archive === 0 ? 'Archive' : 'Unarchive',
  120. Colors.BORDER_LIGHT,
  121. 168,
  122. progress
  123. )}
  124. {renderRightAction('More', Colors.FILL_LIGHT, 112, progress)}
  125. </View>
  126. );
  127. const renderLeftActions = (progress: Animated.AnimatedInterpolation<number>) => (
  128. <View
  129. style={{
  130. width: 84,
  131. flexDirection: 'row'
  132. }}
  133. >
  134. {renderLeftAction(chat.pin === 1 ? 'Unpin' : 'Pin', Colors.FILL_LIGHT, 84, progress)}
  135. </View>
  136. );
  137. return (
  138. <Swipeable
  139. ref={swipeableRow}
  140. friction={2}
  141. enableTrackpadTwoFingerGesture
  142. rightThreshold={40}
  143. renderRightActions={renderRightActions}
  144. renderLeftActions={renderLeftActions}
  145. onSwipeableOpenStartDrag={() => {
  146. onRowOpen(swipeableRow.current);
  147. }}
  148. >
  149. {children}
  150. </Swipeable>
  151. );
  152. };
  153. const styles = StyleSheet.create({
  154. actionText: {
  155. color: Colors.DARK_BLUE,
  156. fontSize: getFontSize(12),
  157. fontWeight: '600',
  158. backgroundColor: 'transparent'
  159. },
  160. rightAction: {
  161. alignItems: 'center',
  162. flex: 1,
  163. justifyContent: 'center',
  164. gap: 12
  165. }
  166. });
  167. export default SwipeableRow;