Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 15 additions & 40 deletions src/components/feed/OtherModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { videoDelete } from '@/utils/requests';
import { Ionicons } from '@expo/vector-icons';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'expo-router';
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Alert, Dimensions, Modal, Pressable, Text, TouchableOpacity, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import tw from 'twrnc';
import BottomSheet from '@/components/ui/bottomSheet';

const { height: SCREEN_HEIGHT, width: SCREEN_WIDTH } = Dimensions.get('window');
const TAB_BAR_HEIGHT = 60;
Expand All @@ -19,7 +19,6 @@ export default function OtherModal({
onPlaybackSpeedChange,
currentPlaybackRate = 1.0,
}) {
const insets = useSafeAreaInsets();
const router = useRouter();
const { colorScheme } = useTheme();
const [showPlaybackSpeed, setShowPlaybackSpeed] = useState(false);
Expand Down Expand Up @@ -112,24 +111,13 @@ export default function OtherModal({

if (showPlaybackSpeed) {
return (
<Modal
visible={visible}
animationType="slide"
transparent={true}
onRequestClose={() => setShowPlaybackSpeed(false)}>
<View style={tw`flex-1 justify-end`}>
<Pressable
style={tw`absolute inset-0`}
onPress={() => setShowPlaybackSpeed(false)}
/>
<View
style={[
tw`bg-white dark:bg-black rounded-t-[20px] pt-3`,
{ paddingBottom: insets.bottom + 20 },
]}>
<View
style={tw`w-10 h-1 bg-gray-300 dark:bg-gray-700 rounded-sm self-center mb-5`}
/>
<BottomSheet
visible={visible}
onClose={() => {
setShowPlaybackSpeed(false);
onClose();
}}
>
<Text
style={tw`text-lg font-bold text-center mb-6 px-4 text-black dark:text-white`}>
Playback Speed
Expand All @@ -156,12 +144,12 @@ export default function OtherModal({

<TouchableOpacity
style={tw`mt-3 py-4 items-center border-t border-gray-100 dark:border-gray-800`}
onPress={() => setShowPlaybackSpeed(false)}>
onPress={() => {
setShowPlaybackSpeed(false);
}}>
<Text style={tw`text-base font-semibold text-[#007AFF]`}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</BottomSheet>
);
}

Expand Down Expand Up @@ -216,18 +204,7 @@ export default function OtherModal({
}

return (
<Modal visible={visible} animationType="slide" transparent={true} onRequestClose={onClose}>
<View style={tw`flex-1 justify-end`}>
<Pressable style={tw`absolute inset-0`} onPress={onClose} />

<View
style={[
tw`bg-white dark:bg-black rounded-t-[20px] pt-3`,
{ paddingBottom: insets.bottom + 20 },
]}>
<View
style={tw`w-10 h-1 bg-gray-300 dark:bg-gray-700 rounded-sm self-center mb-5`}
/>
<BottomSheet visible={visible} onClose={onClose}>

<View style={tw`flex-row justify-around px-4 mb-5`}>
{options.map((option, index) => (
Expand Down Expand Up @@ -261,8 +238,6 @@ export default function OtherModal({
onPress={onClose}>
<Text style={tw`text-base font-semibold text-[#007AFF]`}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</BottomSheet>
);
}
82 changes: 38 additions & 44 deletions src/components/feed/ShareModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useTheme } from '@/contexts/ThemeContext';
import { shareContent } from '@/utils/sharer';
import { Ionicons } from '@expo/vector-icons';
import React from 'react';
import React, { useEffect } from 'react';
import { Dimensions, Modal, Pressable, Share, Text, TouchableOpacity, View } from 'react-native';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { scheduleOnRN } from 'react-native-worklets';
import tw from 'twrnc';
import BottomSheet from '@/components/ui/bottomSheet';

const { height: SCREEN_HEIGHT, width: SCREEN_WIDTH } = Dimensions.get('window');
const TAB_BAR_HEIGHT = 60;
Expand Down Expand Up @@ -47,7 +51,6 @@ type CommentReplyLikePayload = {
};

export default function ShareModal({ visible, item, onClose }) {
const insets = useSafeAreaInsets();
const { colorScheme } = useTheme();

if (!item) return null;
Expand Down Expand Up @@ -86,50 +89,41 @@ export default function ShareModal({ visible, item, onClose }) {
];

return (
<Modal visible={visible} animationType="slide" transparent={true} onRequestClose={onClose}>
<View style={tw`flex-1 justify-end`}>
<Pressable style={tw`absolute inset-0`} onPress={onClose} />
<View
style={[
tw`bg-white dark:bg-gray-900 rounded-t-[20px] pt-3`,
{ paddingBottom: insets.bottom + 20 },
]}>
<View
style={tw`w-10 h-1 bg-gray-300 dark:bg-gray-700 rounded-sm self-center mb-5`}
/>
<Text
style={tw`text-lg font-bold text-center mb-6 px-4 text-black dark:text-white`}>
Share to
</Text>

<View style={tw`flex-row justify-around px-4 mb-5`}>
{shareOptions.map((option, index) => (
<BottomSheet visible={visible} onClose={onClose}>
<Text
style={tw`text-lg font-bold text-center mb-6 px-4 text-black dark:text-white`}>
Share to
</Text>

<View style={tw`flex-row justify-around px-4 mb-5`}>
{shareOptions.map((option, index) => (
<TouchableOpacity
key={index}
style={tw`items-center w-20`}
onPress={option.onPress}>
<View
style={tw`w-15 h-15 rounded-full bg-gray-100 dark:bg-gray-800 justify-center items-center mb-2`}>
<Ionicons
name={option.icon}
size={28}
color={colorScheme === 'dark' ? '#fff' : '#000'}
/>
</View>
<Text
style={tw`text-xs text-black dark:text-white text-center`}>
{option.label}
</Text>
</TouchableOpacity>
))}
</View>

<TouchableOpacity
key={index}
style={tw`items-center w-20`}
onPress={option.onPress}>
<View
style={tw`w-15 h-15 rounded-full bg-gray-100 dark:bg-gray-800 justify-center items-center mb-2`}>
<Ionicons
name={option.icon}
size={28}
color={colorScheme === 'dark' ? '#fff' : '#000'}
/>
</View>
<Text style={tw`text-xs text-black dark:text-white text-center`}>
{option.label}
style={tw`mt-3 py-4 items-center border-t border-gray-100 dark:border-gray-800`}
onPress={onClose}>
<Text style={tw`text-base font-semibold text-[#007AFF]`}>
Cancel
</Text>
</TouchableOpacity>
))}
</View>

<TouchableOpacity
style={tw`mt-3 py-4 items-center border-t border-gray-100 dark:border-gray-800`}
onPress={onClose}>
<Text style={tw`text-base font-semibold text-[#007AFF]`}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</BottomSheet>
);
}
75 changes: 75 additions & 0 deletions src/components/ui/bottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useTheme } from '@/contexts/ThemeContext';
import React, { useEffect, useState } from 'react';
import { Dimensions, Modal, Pressable, Share, Text, TouchableOpacity, View } from 'react-native';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { scheduleOnRN } from 'react-native-worklets';
import tw from 'twrnc';

const { height: SCREEN_HEIGHT, width: SCREEN_WIDTH } = Dimensions.get('window');
const TAB_BAR_HEIGHT = 60;

const BottomSheet = ({ visible, onClose, children }) => {
const insets = useSafeAreaInsets();
const { colorScheme } = useTheme();

const translateY = useSharedValue(0);
const [contentHeight, setContentHeight] = useState(0);
const closeTranslateY = contentHeight || 900;

const swipeStyle = useAnimatedStyle(() => {
return {
transform: [{ translateY: translateY.value }],
};
});

const gesture = Gesture.Pan()
.onUpdate((event) => {
translateY.value = Math.max(Math.min(event.translationY, closeTranslateY), 0);
})
.onEnd((event) => {
if (event.velocityY > 100) {
translateY.value = withTiming(closeTranslateY, { duration: 150 }, () => {
// Closes after the animation, scheduleOnRN necessary or else it crashes the app
scheduleOnRN(onClose);
});
} else {
translateY.value = withTiming(0, { duration: 150 });
}
});

// Reset ShareModal position at each opening
useEffect(() => {
translateY.value = 0;
});

return (
<Modal visible={visible} animationType="slide" transparent={true} onRequestClose={onClose}>
<GestureHandlerRootView style={{ flex: 1 }}>
<GestureDetector gesture={gesture}>
<Animated.View style={[tw`flex-1 justify-end`, swipeStyle]}>
<Pressable style={tw`absolute inset-0`} onPress={onClose} />
<View
style={[
tw`bg-white dark:bg-gray-900 rounded-t-[20px] pt-3 max-h-[85%]`,
{ paddingBottom: insets.bottom },
]}
// Detects View's height
onLayout={(event) => {
const { height } = event.nativeEvent.layout;
setContentHeight(height);
}}>
<View
style={tw`w-10 h-1 bg-gray-300 dark:bg-gray-700 rounded-sm self-center mb-5`}
/>
{children}
</View>
</Animated.View>
</GestureDetector>
</GestureHandlerRootView>
</Modal>
);
}

export default BottomSheet;