Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
EjembiEmmanuel authored Oct 23, 2024
2 parents e468071 + f0e4048 commit c223632
Show file tree
Hide file tree
Showing 57 changed files with 2,714 additions and 8,605 deletions.
13 changes: 12 additions & 1 deletion apps/mobile/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,15 @@ EXPO_PUBLIC_EKUBO_API="https://mainnet-api.ekubo.org"
EXPO_PUBLIC_EKUBO_ROUTE_API="https://quoter-mainnet-api.ekubo.org"

# AVNU API
EXPO_PUBLIC_AVNU_API="https://starknet.api.avnu.fi"
EXPO_PUBLIC_AVNU_API="https://starknet.api.avnu.fi"

#PINATA
EXPO_PUBLIC_PINATA_JWT=""
EXPO_PUBLIC_PINATA_UPLOAD_GATEWAY_URL=''
EXPO_PUBLIC_PINATA_PINATA_SIGN_URL='https://api.pinata.cloud/v3/files/sign'
EXPO_PUBLIC_PINATA_UPLOAD_URL='https://uploads.pinata.cloud/v3/files'

# Onramp/Offramp

EXPO_PUBLIC_APP_ID_ONRAMP_MONEY=

19 changes: 18 additions & 1 deletion apps/mobile/src/assets/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,24 @@ export const GalleryIcon: React.FC<SvgProps> = (props) => {
</Svg>
);
};

export const VideoIcon: React.FC<SvgProps> = (props) => {
return (
<Svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<Path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
<Rect x="2" y="6" width="14" height="12" rx="2" />
</Svg>
);
};
export const GifIcon: React.FC<SvgProps> = (props) => {
return (
<Svg viewBox="0 0 24 24" fill="none" {...props}>
Expand Down
66 changes: 64 additions & 2 deletions apps/mobile/src/components/Loading/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import React from 'react';
import {View, ActivityIndicator, Text, StyleSheet} from 'react-native';
import React, {useEffect} from 'react';
import {ActivityIndicator, Easing, StyleSheet, View} from 'react-native';
import Animated, {
cancelAnimation,
useAnimatedStyle,
useSharedValue,
withRepeat,
withTiming,
} from 'react-native-reanimated';

interface LoadingAnimationProps {
size?: number;
color?: string;
}

const Loading = () => {
return (
Expand All @@ -19,3 +31,53 @@ const styles = StyleSheet.create({
});

export default Loading;

interface ThemedLoadingSpinnerProps {
size?: number;
color?: string;
borderWidth?: number;
}

export const LoadingSpinner: React.FC<ThemedLoadingSpinnerProps> = ({
size = 22,
color = 'white',
borderWidth = 3,
}) => {
const rotation = useSharedValue(0);

const animatedStyles = useAnimatedStyle(() => {
return {
transform: [
{
rotateZ: `${rotation.value}deg`,
},
],
};
}, [rotation.value]);

const styles = StyleSheet.create({
spinner: {
height: size,
width: size,
borderRadius: size / 2,
borderWidth,
borderTopColor: 'transparent',
borderRightColor: 'transparent',
borderBottomColor: 'transparent',
borderLeftColor: color,
},
});

useEffect(() => {
rotation.value = withRepeat(
withTiming(360, {
duration: 1000,
easing: Easing.linear,
}),
200,
);
return () => cancelAnimation(rotation);
}, []);

return <Animated.View style={[styles.spinner, animatedStyles]} />;
};
189 changes: 189 additions & 0 deletions apps/mobile/src/components/VideoPlayer/MiniVideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import {Ionicons} from '@expo/vector-icons';
import {AVPlaybackStatus, ResizeMode, Video} from 'expo-av';
import React, {useEffect, useRef, useState} from 'react';
import {Modal, Pressable, SafeAreaView, StyleSheet, View, ViewStyle} from 'react-native';

interface VideoPlayerProps {
uri: string;
customStyle?: ViewStyle;
}

export default function MiniVideoPlayer({uri, customStyle}: VideoPlayerProps) {
const miniVideoRef = useRef<Video>(null);
const modalVideoRef = useRef<Video>(null);
const [isMiniPlaying, setIsMiniPlaying] = useState(false);
const [isModalPlaying, setIsModalPlaying] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);

const handleMiniPlayPause = async () => {
if (miniVideoRef.current) {
if (isMiniPlaying) {
await miniVideoRef.current.pauseAsync();
} else {
await miniVideoRef.current.playAsync();
}
setIsMiniPlaying(!isMiniPlaying);
}
};

const handleModalPlayPause = async () => {
if (modalVideoRef.current) {
if (isModalPlaying) {
await modalVideoRef.current.pauseAsync();
} else {
await modalVideoRef.current.playAsync();
}
setIsModalPlaying(!isModalPlaying);
}
};

const handlePreview = async () => {
if (miniVideoRef.current && isMiniPlaying) {
await miniVideoRef.current.pauseAsync();
setIsMiniPlaying(false);
}
setIsModalVisible(true);
};

const handleCloseModal = async () => {
if (modalVideoRef.current && isModalPlaying) {
await modalVideoRef.current.pauseAsync();
setIsModalPlaying(false);
}
setIsModalVisible(false);
};

useEffect(() => {
if (isModalVisible && modalVideoRef.current) {
modalVideoRef.current.playFromPositionAsync(0);
}
}, [isModalVisible]);

const onMiniPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status.isLoaded) {
setIsMiniPlaying(status.isPlaying);
}
};

const onModalPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status.isLoaded) {
setIsModalPlaying(status.isPlaying);
}
};

return (
<View style={[styles.videoContainer, customStyle]}>
<Video
ref={miniVideoRef}
source={{uri}}
style={styles.video}
videoStyle={styles.video}
resizeMode={ResizeMode.COVER}
shouldPlay={false}
isLooping
onPlaybackStatusUpdate={onMiniPlaybackStatusUpdate}
/>
<View style={styles.controlsOverlay}>
<Pressable onPress={handleMiniPlayPause} style={styles.controlButton}>
<Ionicons name={isMiniPlaying ? 'pause' : 'play'} size={20} color="white" />
</Pressable>
</View>
<Pressable onPress={handlePreview} style={styles.previewButton}>
<Ionicons name="expand" size={12} color="white" />
</Pressable>
<Modal
animationType="fade"
transparent={true}
visible={isModalVisible}
onRequestClose={handleCloseModal}
>
<SafeAreaView style={styles.modalContainer}>
<Pressable onPress={handleModalPlayPause} style={styles.modalContent}>
<Video
ref={modalVideoRef}
source={{uri}}
videoStyle={styles.video}
style={styles.fullScreenVideo}
resizeMode={ResizeMode.CONTAIN}
shouldPlay={false}
isLooping
onPlaybackStatusUpdate={onModalPlaybackStatusUpdate}
/>
<Pressable onPress={handleCloseModal} style={styles.closeButton}>
<Ionicons name="close" size={24} color="white" />
</Pressable>
</Pressable>
</SafeAreaView>
</Modal>
</View>
);
}

const styles = StyleSheet.create({
videoContainer: {
backgroundColor: 'black',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
overflow: 'hidden',
width: '100%',
height: '100%',
},
video: {
width: '100%',
height: '100%',
},
controlsOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
},
controlButton: {
padding: 10,
},
previewButton: {
position: 'absolute',
top: 6,
right: 6,
padding: 5,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 10,
},
modalContainer: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.9)',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
maxWidth: 700,
width: '100%',
aspectRatio: 16 / 9,
backgroundColor: 'black',
borderRadius: 10,
overflow: 'hidden',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.1)',
},
fullScreenVideo: {
width: '100%',
height: '100%',
},
modalControls: {
position: 'absolute',
bottom: 20,
left: 0,
right: 0,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
closeButton: {
position: 'absolute',
top: 10,
right: 10,
padding: 10,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 20,
},
});
54 changes: 54 additions & 0 deletions apps/mobile/src/components/VideoPlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {ResizeMode, Video} from 'expo-av';
import React, {useRef, useState} from 'react';
import {Dimensions, Pressable, StyleSheet} from 'react-native';

const VideoPlayer = ({uri}: {uri: string}) => {
const videoRef = useRef<Video>(null);
const [isPlaying, setIsPlaying] = useState(false);

const handlePlayPause = async () => {
if (videoRef.current) {
if (isPlaying) {
await videoRef.current.pauseAsync();
} else {
await videoRef.current.playAsync();
}
setIsPlaying(!isPlaying);
}
};

return (
<Pressable onPress={handlePlayPause} style={styles.videoContainer}>
<Video
ref={videoRef}
source={{
uri,
}}
videoStyle={styles.video}
style={styles.video}
resizeMode={ResizeMode.COVER}
shouldPlay={false} // By default, video won't play
isLooping
/>
</Pressable>
);
};

const styles = StyleSheet.create({
videoContainer: {
width: '100%',
height: Dimensions.get('window').width * (9 / 16),
backgroundColor: 'black',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 20,
},
video: {
width: '100%',
height: '100%',
borderRadius: 20,
overflow: 'hidden',
},
});

export default VideoPlayer;
Loading

0 comments on commit c223632

Please sign in to comment.