improved asset rendering

This commit is contained in:
balzack 2022-10-18 22:38:01 -07:00
parent 92a967210d
commit 55957aeacb
11 changed files with 143 additions and 67 deletions

View File

@ -310,7 +310,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 7; CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_TEAM = 3P65PQ7SUR; DEVELOPMENT_TEAM = 3P65PQ7SUR;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@ -348,7 +348,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 7; CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_TEAM = 3P65PQ7SUR; DEVELOPMENT_TEAM = 3P65PQ7SUR;
INFOPLIST_FILE = Databag/Info.plist; INFOPLIST_FILE = Databag/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Databag; INFOPLIST_KEY_CFBundleDisplayName = Databag;

View File

@ -97,8 +97,7 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
<VideoAsset topicId={item.topicId} asset={asset.item.video} dismiss={actions.hideCarousel} /> <VideoAsset topicId={item.topicId} asset={asset.item.video} dismiss={actions.hideCarousel} />
)} )}
{ asset.item.audio && ( { asset.item.audio && (
<AudioAsset topicId={item.topicId} asset={asset.item.audio} active={state.activeId == asset.dataIndex} <AudioAsset topicId={item.topicId} asset={asset.item.audio} dismiss={actions.hideCarousel} />
setActive={() => actions.setActive(asset.dataIndex)} dismiss={actions.hideCarousel} />
)} )}
</View> </View>
) )

View File

@ -1,36 +1,38 @@
import { Text, Image, View, TouchableOpacity } from 'react-native'; import { Image, View, Text, TouchableOpacity } from 'react-native';
import { useRef } from 'react';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
import { Video, AVPlaybackStatus } from 'expo-av';
import { useAudioAsset } from './useAudioAsset.hook'; import { useAudioAsset } from './useAudioAsset.hook';
import GestureRecognizer from 'react-native-swipe-gestures';
import { styles } from './AudioAsset.styled'; import { styles } from './AudioAsset.styled';
import audio from 'images/audio.png';
import Icons from '@expo/vector-icons/MaterialCommunityIcons'; import Icons from '@expo/vector-icons/MaterialCommunityIcons';
import audio from 'images/audio.png';
export function AudioAsset({ topicId, asset, active, setActive, dismiss }) { export function AudioAsset({ topicId, asset, dismiss }) {
const { state, actions } = useAudioAsset(topicId, asset); const { state, actions } = useAudioAsset(topicId, asset);
const play = () => { const player = useRef(null);
actions.play();
setActive();
}
return ( return (
<View style={styles.background}> <View style={styles.container}>
<Image source={audio} style={{ width: state.length, height: state.length }} resizeMode={'cover'} /> <Image source={audio} style={{ width: state.width, height: state.height }} resizeMode={'contain'} />
<Text style={styles.label}>{ asset.label }</Text> <Text style={styles.label}>{ asset.label }</Text>
{ state.playing && active && ( { !state.playing && state.loaded && (
<TouchableOpacity style={styles.control} onPress={actions.pause}> <TouchableOpacity style={styles.control} onPress={actions.play}>
<Icons name="stop-circle-outline" size={92} color={Colors.white} /> <Icons name="play-circle-outline" size={92} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{ (!state.playing || !active) && ( { state.playing && state.loaded && (
<TouchableOpacity style={styles.control} onPress={play}> <TouchableOpacity style={styles.control} onPress={actions.pause}>
<Icons name="play-circle-outline" size={92} color={Colors.white} /> <Icons name="pause-circle-outline" size={92} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
)} )}
<TouchableOpacity style={styles.close} onPress={dismiss}> <TouchableOpacity style={styles.close} onPress={dismiss}>
<Icons name="close" size={24} color={Colors.white} /> <Icons name="window-close" size={32} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
<Video ref={player} source={{ uri: state.url }} isLooping={true}
shouldPlay={state.playing} onLoad={actions.loaded} style={styles.player} />
</View> </View>
); );
} }

View File

@ -2,13 +2,19 @@ import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
background: { container: {
backgroundColor: Colors.lightgrey, position: 'relative',
borderRadius: 4, borderRadius: 8,
backgroundColor: Colors.formBackground,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
control: {
position: 'absolute',
paddingRight: 8,
paddingTop: 4,
},
label: { label: {
position: 'absolute', position: 'absolute',
textAlign: 'center', textAlign: 'center',
@ -18,13 +24,17 @@ export const styles = StyleSheet.create({
paddingLeft: 20, paddingLeft: 20,
paddingRight: 20, paddingRight: 20,
}, },
control: {
position: 'absolute',
},
close: { close: {
position: 'absolute', position: 'absolute',
top: 0, top: 0,
right: 0, right: 0,
paddingTop: 4,
paddingBottom: 4,
paddingLeft: 8,
paddingRight: 8,
},
player: {
display: 'none',
}, },
}) })

View File

@ -2,15 +2,18 @@ import { useState, useRef, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext'; import { ConversationContext } from 'context/ConversationContext';
import { Image } from 'react-native'; import { Image } from 'react-native';
import { useWindowDimensions } from 'react-native'; import { useWindowDimensions } from 'react-native';
import SoundPlayer from 'react-native-sound-player'
export function useAudioAsset(topicId, asset) { export function useAudioAsset(topicId, asset) {
const [state, setState] = useState({ const [state, setState] = useState({
length: null, width: 1,
height: 1,
url: null,
playing: false, playing: false,
loaded: false,
}); });
const closing = useRef(null);
const conversation = useContext(ConversationContext); const conversation = useContext(ConversationContext);
const dimensions = useWindowDimensions(); const dimensions = useWindowDimensions();
@ -19,29 +22,36 @@ export function useAudioAsset(topicId, asset) {
} }
useEffect(() => { useEffect(() => {
if (dimensions.width < dimensions.height) { const frameRatio = dimensions.width / dimensions.height;
updateState({ length: 0.8 * dimensions.width }); if (frameRatio > 1) {
//height constrained
const height = 0.9 * dimensions.height;
const width = height;
updateState({ width, height });
} }
else { else {
updateState({ length: 0.8 * dimensions.height }); //width constrained
const width = 0.9 * dimensions.width;
const height = width;
updateState({ width, height });
} }
}, [dimensions]); }, [dimensions]);
useEffect(() => { useEffect(() => {
const url = conversation.actions.getTopicAssetUrl(topicId, asset.full); const url = conversation.actions.getTopicAssetUrl(topicId, asset.full);
updateState({ url, playing: false }); updateState({ url });
return () => { SoundPlayer.stop() }
}, [topicId, conversation, asset]); }, [topicId, conversation, asset]);
const actions = { const actions = {
play: () => { play: () => {
SoundPlayer.playUrl(state.url);
updateState({ playing: true }); updateState({ playing: true });
}, },
pause: () => { pause: () => {
SoundPlayer.stop();
updateState({ playing: false }); updateState({ playing: false });
}, },
loaded: () => {
updateState({ loaded: true });
}
}; };
return { state, actions }; return { state, actions };

View File

@ -1,4 +1,4 @@
import { Image, TouchableOpacity } from 'react-native'; import { View, Image, ActivityIndicator, TouchableOpacity } from 'react-native';
import { useImageAsset } from './useImageAsset.hook'; import { useImageAsset } from './useImageAsset.hook';
import { styles } from './ImageAsset.styled'; import { styles } from './ImageAsset.styled';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
@ -7,8 +7,13 @@ export function ImageAsset({ topicId, asset, dismiss }) {
const { state, actions } = useImageAsset(topicId, asset); const { state, actions } = useImageAsset(topicId, asset);
return ( return (
<TouchableOpacity activeOpacity={1} onPress={dismiss}> <TouchableOpacity style={styles.container} activeOpacity={1} onPress={dismiss}>
<Image source={{ uri: state.url }} style={{ borderRadius: 4, width: state.imageWidth, height: state.imageHeight }} resizeMode={'cover'} /> <Image source={{ uri: state.url }} onLoad={actions.loaded} style={{ borderRadius: 4, width: state.imageWidth, height: state.imageHeight }} resizeMode={'cover'} />
{ !state.loaded && (
<View style={styles.loading}>
<ActivityIndicator color={Colors.white} size="large" />
</View>
)}
</TouchableOpacity> </TouchableOpacity>
); );
} }

View File

@ -2,6 +2,14 @@ import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
container: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
loading: {
position: 'absolute',
},
overlay: { overlay: {
marginRight: 16, marginRight: 16,
position: 'absolute', position: 'absolute',

View File

@ -12,6 +12,7 @@ export function useImageAsset(topicId, asset) {
imageWidth: 1, imageWidth: 1,
imageHeight: 1, imageHeight: 1,
url: null, url: null,
loaded: false,
}); });
const conversation = useContext(ConversationContext); const conversation = useContext(ConversationContext);
@ -51,6 +52,9 @@ export function useImageAsset(topicId, asset) {
}, [topicId, conversation, asset]); }, [topicId, conversation, asset]);
const actions = { const actions = {
loaded: () => {
updateState({ loaded: true });
}
}; };
return { state, actions }; return { state, actions };

View File

@ -1,8 +1,7 @@
import { Image, View, TouchableOpacity } from 'react-native'; import { ActivityIndicator, Image, View, TouchableOpacity } from 'react-native';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
import { Video, AVPlaybackStatus } from 'expo-av'; import { Video, AVPlaybackStatus } from 'expo-av';
import { useVideoAsset } from './useVideoAsset.hook'; import { useVideoAsset } from './useVideoAsset.hook';
import GestureRecognizer from 'react-native-swipe-gestures';
import { styles } from './VideoAsset.styled'; import { styles } from './VideoAsset.styled';
import Icons from '@expo/vector-icons/MaterialCommunityIcons'; import Icons from '@expo/vector-icons/MaterialCommunityIcons';
@ -11,18 +10,36 @@ export function VideoAsset({ topicId, asset, dismiss }) {
const { state, actions } = useVideoAsset(topicId, asset); const { state, actions } = useVideoAsset(topicId, asset);
return ( return (
<TouchableOpacity activeOpacity={1} onPress={actions.showClose}> <View style={styles.container}>
{ state.url && ( { !state.loaded && (
<Video source={{ uri: state.url }} style={{ width: state.width, height: state.height }} resizeMode={'cover'} <TouchableOpacity onPress={dismiss}>
onReadyForDisplay={(e) => actions.setResolution(e.naturalSize.width, e.naturalSize.height)} <ActivityIndicator color={Colors.white} size="large" />
useNativeControls={state.controls} resizeMode="contain" />
)}
{ state.closing && (
<TouchableOpacity style={styles.close} onPress={dismiss}>
<Icons name="close" size={24} color={Colors.white} />
</TouchableOpacity> </TouchableOpacity>
)} )}
</TouchableOpacity> <TouchableOpacity activeOpacity={1} style={{...styles.container, ...state.display}} onPress={actions.showControls}>
<Video source={{ uri: state.url }} style={{ width: state.width, height: state.height }} resizeMode={'cover'}
onReadyForDisplay={(e) => actions.setResolution(e.naturalSize.width, e.naturalSize.height)}
isLooping={true} shouldPlay={state.playing} resizeMode="contain" />
{ (!state.playing || state.controls) && (
<View style={{ ...styles.overlay, width: state.width, height: state.height }} />
)}
{ !state.playing && state.loaded && (
<TouchableOpacity style={styles.control} onPress={actions.play}>
<Icons name="play-circle-outline" size={92} color={Colors.white} />
</TouchableOpacity>
)}
{ state.controls && state.playing && state.loaded && (
<TouchableOpacity style={styles.control} onPress={actions.pause}>
<Icons name="pause-circle-outline" size={92} color={Colors.white} />
</TouchableOpacity>
)}
{ (state.controls || !state.playing) && (
<TouchableOpacity style={styles.close} onPress={dismiss}>
<Icons name="window-close" size={32} color={Colors.white} />
</TouchableOpacity>
)}
</TouchableOpacity>
</View>
); );
} }

View File

@ -2,15 +2,28 @@ import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
close: { container: {
backgroundColor: 'rgba(0, 0, 0, 0.6)', display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
overlay: {
position: 'absolute', position: 'absolute',
alignSelf: 'center', backgroundColor: 'rgba(0, 0, 0, 0.4)',
justifySelf: 'center', },
borderBottomLeftRadius: 4, control: {
borderBottomRightRadius: 4, position: 'absolute',
paddingLeft: 4, paddingRight: 8,
paddingRight: 4, paddingTop: 4,
},
close: {
position: 'absolute',
top: 0,
right: 0,
paddingTop: 4,
paddingBottom: 4,
paddingLeft: 8,
paddingRight: 8,
}, },
}) })

View File

@ -10,13 +10,14 @@ export function useVideoAsset(topicId, asset) {
frameHeight: 1, frameHeight: 1,
videoRatio: 1, videoRatio: 1,
width: 1, width: 1,
weight: 1,
url: null, url: null,
playing: false,
loaded: false,
controls: false, controls: false,
closing: false, display: { display: 'none' },
}); });
const closing = useRef(null); const controls = useRef(null);
const conversation = useContext(ConversationContext); const conversation = useContext(ConversationContext);
const dimensions = useWindowDimensions(); const dimensions = useWindowDimensions();
@ -51,13 +52,20 @@ export function useVideoAsset(topicId, asset) {
const actions = { const actions = {
setResolution: (width, height) => { setResolution: (width, height) => {
updateState({ controls: true, videoRatio: width / height }); updateState({ loaded: true, display: {}, videoRatio: width / height });
}, },
showClose: () => { play: () => {
clearTimeout(closing.current); actions.showControls();
updateState({ closing: true }); updateState({ playing: true });
closing.current = setTimeout(() => { },
updateState({ closing: false }); pause: () => {
updateState({ playing: false });
},
showControls: () => {
clearTimeout(controls.current);
updateState({ controls: true });
controls.current = setTimeout(() => {
updateState({ controls: false });
}, 2000); }, 2000);
}, },
}; };