SwipeableRow.tsx 4.5 KB

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