rendering asset carousel

This commit is contained in:
Roland Osborne 2022-10-05 15:11:29 -07:00
parent 2cc2419bf9
commit 633ca9248e
13 changed files with 5253 additions and 5105 deletions

View File

@ -30,6 +30,7 @@
"react-native-reanimated": "^2.10.0",
"react-native-safe-area-context": "^4.3.3",
"react-native-safe-area-view": "^1.1.1",
"react-native-snap-carousel": "4.0.0-beta.6",
"react-native-sqlite-storage": "^6.0.1",
"react-native-video": "^5.2.1",
"react-native-web": "~0.18.7",

View File

@ -1,4 +1,4 @@
import { FlatList, View, Text, TouchableOpacity } from 'react-native';
import { FlatList, View, Text, TouchableOpacity, Modal } from 'react-native';
import { useTopicItem } from './useTopicItem.hook';
import { styles } from './TopicItem.styled';
import { Logo } from 'utils/Logo';
@ -6,24 +6,47 @@ import Colors from 'constants/Colors';
import { VideoThumb } from './videoThumb/VideoThumb';
import { AudioThumb } from './audioThumb/AudioThumb';
import { ImageThumb } from './imageThumb/ImageThumb';
import { ImageAsset } from './imageAsset/ImageAsset';
import { AudioAsset } from './audioAsset/AudioAsset';
import { VideoAsset } from './videoAsset/VideoAsset';
import AntIcons from '@expo/vector-icons/AntDesign';
import Carousel from 'react-native-snap-carousel';
export function TopicItem({ item }) {
const { state, actions } = useTopicItem(item);
const renderThumb = (asset) => {
if (asset.item.image) {
return <ImageThumb topicId={item.topicId} asset={asset.item.image} />
}
if (asset.item.video) {
return <VideoThumb topicId={item.topicId} asset={asset.item.video} />
}
if (asset.item.audio) {
return <AudioThumb topicId={item.topicId} asset={asset.item.audio} />
}
return <></>
};
const renderAsset = (asset) => {
return (
<View style={styles.frame}>
{ asset.item.image && (
<ImageAsset topicId={item.topicId} asset={asset.item.image} onClearCarousel={() => actions.hideCarousel()} />
)}
{ asset.item.video && (
<VideoAsset topicId={item.topicId} asset={asset.item.video} onClearCarousel={() => actions.hideCarousel()} />
)}
{ asset.item.audio && (
<AudioAsset topicId={item.topicId} asset={asset.item.audio} onClearCarousel={() => actions.hideCarousel()} />
)}
</View>
)
}
const renderThumb = (thumb) => {
return (
<View>
{ thumb.item.image && (
<ImageThumb topicId={item.topicId} asset={thumb.item.image} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
{ thumb.item.video && (
<VideoThumb topicId={item.topicId} asset={thumb.item.video} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
{ thumb.item.audio && (
<AudioThumb topicId={item.topicId} asset={thumb.item.audio} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
</View>
);
}
return (
<View style={styles.item}>
@ -55,6 +78,23 @@ export function TopicItem({ item }) {
{ state.status !== 'confirmed' && (
<AntIcons name="cloudo" size={32} color={Colors.divider} />
)}
<Modal
animationType="fade"
transparent={true}
visible={state.carousel}
supportedOrientations={['portrait', 'landscape']}
onRequestClose={actions.hideCarousel}
>
<View style={styles.modal}>
<Carousel
data={state.assets}
firstItem={state.carouselIndex}
renderItem={renderAsset}
sliderWidth={state.width}
itemWidth={state.width}
/>
</View>
</Modal>
</View>
);
}

View File

@ -30,5 +30,17 @@ export const styles = StyleSheet.create({
marginTop: 4,
marginBottom: 4,
},
modal: {
width: '100%',
height: '100%',
backgroundColor: 'rgba(52, 52, 52, 0.8)',
},
frame: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
},
})

View File

@ -0,0 +1,10 @@
import { Image, View, TouchableOpacity } from 'react-native';
import Colors from 'constants/Colors';
export function AudioAsset({ topicId, asset, onClearCarousel }) {
return (
<TouchableOpacity onPress={onClearCarousel} activeOpacity={1} style={{ width: 100, height: 100, backgroundColor: 'yellow' }} onPress={onClearCarousel} />
);
}

View File

@ -3,10 +3,10 @@ import { styles } from './AudioThumb.styled';
import Colors from 'constants/Colors';
import audio from 'images/audio.png';
export function AudioThumb({ topicId, asset }) {
export function AudioThumb({ topicId, asset, onAssetView }) {
return (
<TouchableOpacity activeOpacity={1}>
<TouchableOpacity activeOpacity={1} onPress={onAssetView}>
<Image source={audio} style={{ borderRadius: 4, width: 92, height: 92, marginRight: 16, backgroundColor: Colors.lightgrey }} resizeMode={'cover'} />
{ asset.label && (
<View style={styles.overlay}>

View File

@ -0,0 +1,15 @@
import { Image, View, TouchableOpacity } from 'react-native';
import { useImageAsset } from './useImageAsset.hook';
import { styles } from './ImageAsset.styled';
import Colors from 'constants/Colors';
export function ImageAsset({ topicId, asset, onClearCarousel }) {
const { state, actions } = useImageAsset(topicId, asset);
return (
<TouchableOpacity onPress={onClearCarousel} activeOpacity={1}>
<Image source={{ uri: state.url }} style={{ borderRadius: 4, width: state.imageWidth, height: state.imageHeight }} resizeMode={'cover'} />
</TouchableOpacity>
);
}

View File

@ -0,0 +1,17 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({
overlay: {
marginRight: 16,
position: 'absolute',
bottom: 0,
right: 0,
padding: 2,
borderTopLeftRadius: 4,
backgroundColor: Colors.white,
borderWidth: 1,
borderColor: Colors.divider,
},
})

View File

@ -0,0 +1,58 @@
import { useState, useRef, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
import { Image } from 'react-native';
import { useWindowDimensions } from 'react-native';
export function useImageAsset(topicId, asset) {
const [state, setState] = useState({
frameWidth: 1,
frameHeight: 1,
imageRatio: 1,
imageWidth: 1,
imageHeight: 1,
url: null,
});
const conversation = useContext(ConversationContext);
const dimensions = useWindowDimensions();
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
const frameRatio = state.frameWidth / state.frameHeight;
if (frameRatio > state.imageRatio) {
//height constrained
const height = 0.8 * state.frameHeight;
const width = height * state.imageRatio;
updateState({ imageWidth: width, imageHeight: height });
}
else {
//width constrained
const width = 0.8 * state.frameWidth;
const height = width / state.imageRatio;
updateState({ imageWidth: width, imageHeight: height });
}
}, [state.frameWidth, state.frameHeight, state.imageRatio]);
useEffect(() => {
updateState({ frameWidth: dimensions.width, frameHeight: dimensions.height });
}, [dimensions]);
useEffect(() => {
const url = conversation.actions.getTopicAssetUrl(topicId, asset.full);
if (url) {
Image.getSize(url, (width, height) => {
updateState({ url, imageRatio: width / height });
});
}
}, [topicId, conversation, asset]);
const actions = {
};
return { state, actions };
}

View File

@ -3,11 +3,11 @@ import { useImageThumb } from './useImageThumb.hook';
import { styles } from './ImageThumb.styled';
import Colors from 'constants/Colors';
export function ImageThumb({ topicId, asset }) {
export function ImageThumb({ topicId, asset, onAssetView }) {
const { state, actions } = useImageThumb(topicId, asset);
return (
<TouchableOpacity activeOpacity={1}>
<TouchableOpacity activeOpacity={1} onPress={onAssetView}>
<Image source={{ uri: state.url }} style={{ borderRadius: 4, width: 92 * state.ratio, height: 92, marginRight: 16 }} resizeMode={'cover'} />
</TouchableOpacity>
);

View File

@ -2,6 +2,7 @@ import { useState, useEffect, useContext } from 'react';
import { CardContext } from 'context/CardContext';
import { ProfileContext } from 'context/ProfileContext';
import moment from 'moment';
import { useWindowDimensions } from 'react-native';
export function useTopicItem(item) {
@ -11,15 +12,24 @@ export function useTopicItem(item) {
logo: null,
timestamp: null,
message: null,
carousel: false,
carouselIndex: 0,
width: null,
height: null,
});
const profile = useContext(ProfileContext);
const card = useContext(CardContext);
const dimensions = useWindowDimensions();
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
updateState({ width: dimensions.width, height: dimensions.height });
}, [dimensions]);
useEffect(() => {
const { topicId, detail } = item;
const { guid, data, status, transform } = detail;
@ -95,6 +105,12 @@ export function useTopicItem(item) {
}, [card, item]);
const actions = {
showCarousel: (index) => {
updateState({ carousel: true, carouselIndex: index });
},
hideCarousel: () => {
updateState({ carousel: false });
},
};
return { state, actions };

View File

@ -0,0 +1,10 @@
import { Image, View, TouchableOpacity } from 'react-native';
import Colors from 'constants/Colors';
export function VideoAsset({ topicId, asset, onClearCarousel }) {
return (
<TouchableOpacity onPress={onClearCarousel} activeOpacity={1} style={{ width: 100, height: 100, backgroundColor: 'yellow' }} onPress={onClearCarousel} />
);
}

View File

@ -4,11 +4,11 @@ import { styles } from './VideoThumb.styled';
import Colors from 'constants/Colors';
import Ionicons from '@expo/vector-icons/AntDesign';
export function VideoThumb({ topicId, asset }) {
export function VideoThumb({ topicId, asset, onAssetView }) {
const { state, actions } = useVideoThumb(topicId, asset);
return (
<TouchableOpacity activeOpacity={1}>
<TouchableOpacity activeOpacity={1} onPress={onAssetView}>
<Image source={{ uri: state.url }} style={{ borderRadius: 4, width: 92 * state.ratio, height: 92, marginRight: 16 }} resizeMode={'cover'} />
<View style={styles.overlay}>
<Ionicons name="caretright" size={20} color={Colors.white} />

File diff suppressed because it is too large Load Diff