adding video and image to add topic carousel

This commit is contained in:
balzack 2022-10-01 13:18:37 -07:00
parent b6b18aaaaa
commit 769ce6805f
12 changed files with 248 additions and 9 deletions

View File

@ -246,6 +246,11 @@ PODS:
- ReactCommon/turbomodule/core
- react-native-sqlite-storage (6.0.1):
- React-Core
- react-native-video (5.2.1):
- React-Core
- react-native-video/Video (= 5.2.1)
- react-native-video/Video (5.2.1):
- React-Core
- React-perflogger (0.69.5)
- React-RCTActionSheet (0.69.5):
- React-Core/RCTActionSheetHeaders (= 0.69.5)
@ -384,6 +389,7 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- react-native-video (from `../node_modules/react-native-video`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
@ -465,6 +471,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-safe-area-context"
react-native-sqlite-storage:
:path: "../node_modules/react-native-sqlite-storage"
react-native-video:
:path: "../node_modules/react-native-video"
React-perflogger:
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
React-RCTActionSheet:
@ -529,6 +537,7 @@ SPEC CHECKSUMS:
React-logger: 15c734997c06fe9c9b88e528fb7757601e7a56df
react-native-safe-area-context: b456e1c40ec86f5593d58b275bd0e9603169daca
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
React-perflogger: 367418425c5e4a9f0f80385ee1eaacd2a7348f8e
React-RCTActionSheet: e4885e7136f98ded1137cd3daccc05eaed97d5a6
React-RCTAnimation: 7c5a74f301c9b763343ba98a3dd776ed2676993f

View File

@ -27,6 +27,7 @@
"react-native-safe-area-context": "^4.3.3",
"react-native-safe-area-view": "^1.1.1",
"react-native-sqlite-storage": "^6.0.1",
"react-native-video": "^5.2.1",
"react-native-web": "~0.18.7",
"react-router-dom": "6",
"react-router-native": "^6.3.0"

View File

@ -7,6 +7,8 @@ import MaterialIcons from '@expo/vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors';
import { SafeAreaView } from 'react-native-safe-area-context';
import ImagePicker from 'react-native-image-crop-picker'
import { VideoFile } from './videoFile/VideoFile';
import { ImageFile } from './imageFile/ImageFile';
export function AddTopic() {
@ -14,7 +16,7 @@ export function AddTopic() {
const addImage = async () => {
try {
const full = await ImagePicker.openPicker({ mediaType: 'photo', includeBase64: true });
const full = await ImagePicker.openPicker({ mediaType: 'photo' });
actions.addImage(full.path);
}
catch (err) {
@ -22,6 +24,16 @@ export function AddTopic() {
}
}
const addVideo = async () => {
try {
const full = await ImagePicker.openPicker({ mediaType: 'video' });
actions.addVideo(full.path);
}
catch (err) {
console.log(err);
}
}
const remove = (item) => {
Alert.alert(
`Removing ${item.type} from message.`,
@ -40,11 +52,17 @@ export function AddTopic() {
const renderAsset = ({ item }) => {
if (item.type === 'image') {
return (
<TouchableOpacity onPress={() => remove(item)}>
<Image source={{ uri: item.data }} style={{ width: 92 * item.ratio, height: 92, marginRight: 16 }}resizeMode={'contain'} />
</TouchableOpacity>
<ImageFile path={item.data} remove={() => remove(item)} />
);
}
if (item.type === 'video') {
return (
<VideoFile path={item.data}
remove={() => remove(item)}
setPosition={(position) => actions.setVideoPosition(item.key, position)}
/>
)
}
else {
return (
<View style={styles.asset}></View>
@ -67,7 +85,7 @@ export function AddTopic() {
<TouchableOpacity style={styles.addButton} onPress={addImage}>
<AntIcons name="picture" size={20} color={Colors.text} />
</TouchableOpacity>
<TouchableOpacity style={styles.addButton}>
<TouchableOpacity style={styles.addButton} onPress={addVideo}>
<MaterialIcons name="video-outline" size={24} color={Colors.text} />
</TouchableOpacity>
<TouchableOpacity style={styles.addButton}>

View File

@ -60,8 +60,8 @@ export const styles = StyleSheet.create({
},
carousel: {
paddingTop: 8,
paddingLeft: 16,
paddingRight: 16,
paddingLeft: 16,
},
})

View File

@ -0,0 +1,24 @@
import { useRef, useEffect } from 'react';
import { TouchableOpacity, View, Image } from 'react-native';
import { useImageFile } from './useImageFile.hook';
import { styles } from './ImageFile.styled';
import Icons from '@expo/vector-icons/AntDesign';
import Colors from 'constants/Colors';
export function ImageFile({ path, setPosition, remove }) {
const { state, actions } = useImageFile();
useEffect(() => {
Image.getSize(path, actions.setInfo);
}, [path]);
return (
<TouchableOpacity onLongPress={remove}>
<Image source={{ uri: path }} style={{ width: 92 * state.ratio, height: 92, marginRight: 16 }} resizeMode={'contain'} />
<View style={styles.overlay}>
<Icons name="picture" size={20} color={Colors.grey} />
</View>
</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,22 @@
import { useState, useRef, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
export function useImageFile() {
const [state, setState] = useState({
ratio: 1,
});
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const actions = {
setInfo: (width, height) => {
updateState({ ratio: width / height });
},
};
return { state, actions };
}

View File

@ -27,6 +27,38 @@ export function useAddTopic(cardId, channelId) {
updateState({ assets: [ ...state.assets, asset ] });
});
},
addVideo: (data) => {
assetId.current++;
const asset = { key: assetId.current, type: 'video', data: data, ratio: 1, duration: 0, position: 0, ref: null };
updateState({ assets: [ ...state.assets, asset ] });
},
setVideoInfo: (key, width, height, duration) => {
updateState({ assets: state.assets.map((item) => {
if(item.key === key) {
return { ...item, ratio: width / height, duration };
}
return item;
})
});
},
setVideoRef: (key, ref) => {
updateState({ assets: state.assets.map((item) => {
if(item.key === key) {
return { ...item, ref };
}
return item;
})
});
},
setVideoPosition: (key, position) => {
updateState({ assets: state.assets.map((item) => {
if(item.key === key) {
return { ...item, position };
}
return item;
})
});
},
removeAsset: (key) => {
updateState({ assets: state.assets.filter(item => (item.key !== key))});
},

View File

@ -0,0 +1,33 @@
import { useRef, useEffect } from 'react';
import { TouchableOpacity, View } from 'react-native';
import Video from 'react-native-video';
import { useVideoFile } from './useVideoFile.hook';
import { styles } from './VideoFile.styled';
import Icons from '@expo/vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors';
export function VideoFile({ path, setPosition, remove }) {
const { state, actions } = useVideoFile();
const video = useRef();
useEffect(() => {
if (video.current) {
video.current.seek(state.position);
setPosition(state.position);
}
}, [state.position]);
return (
<TouchableOpacity onPress={actions.setNextPosition} onLongPress={remove}>
<Video source={{ uri: path }} style={{ width: 92 * state.ratio, height: 92, marginRight: 16 }} resizeMode={'cover'} paused={true}
onLoad={({ naturalSize, duration }) => actions.setInfo(naturalSize.width, naturalSize.height, duration)}
ref={(ref) => video.current = ref}
/>
<View style={styles.overlay}>
<Icons name="video-outline" size={20} color={Colors.grey} />
</View>
</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,30 @@
import { useState, useRef, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
export function useVideoFile() {
const [state, setState] = useState({
duration: 0,
position: 0,
ratio: 1,
});
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const actions = {
setInfo: (width, height, duration) => {
updateState({ ratio: width / height, duration: Math.floor(duration) });
},
setNextPosition: () => {
if (state.duration) {
const position = (state.position + 1) % state.duration;
updateState({ position });
}
},
};
return { state, actions };
}

View File

@ -1686,7 +1686,7 @@
resolved "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz"
integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ==
"@react-native/normalize-color@2.0.0", "@react-native/normalize-color@^2.0.0":
"@react-native/normalize-color@*", "@react-native/normalize-color@2.0.0", "@react-native/normalize-color@^2.0.0":
version "2.0.0"
resolved "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.0.0.tgz"
integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw==
@ -2911,6 +2911,15 @@ depd@~1.1.2:
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
deprecated-react-native-prop-types@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz#c10c6ee75ff2b6de94bb127f142b814e6e08d9ab"
integrity sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==
dependencies:
"@react-native/normalize-color" "*"
invariant "*"
prop-types "*"
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
@ -2933,6 +2942,11 @@ electron-to-chromium@^1.4.202:
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.242.tgz"
integrity sha512-nPdgMWtjjWGCtreW/2adkrB2jyHjClo9PtVhR6rW+oxa4E4Wom642Tn+5LslHP3XPL5MCpkn5/UEY60EXylNeQ==
eme-encryption-scheme-polyfill@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.1.1.tgz#91c823ed584e8ec5a9f03a6a676def8f80c57a4c"
integrity sha512-njD17wcUrbqCj0ArpLu5zWXtaiupHb/2fIUQGdInf83GlI+Q6mmqaPGLdrke4savKAu15J/z1Tg/ivDgl14g0g==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
@ -3783,7 +3797,7 @@ internal-ip@4.3.0:
default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
invariant@^2.2.4:
invariant@*, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@ -4252,6 +4266,11 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
keymirror@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35"
integrity sha512-vIkZAFWoDijgQT/Nvl2AHCMmnegN2ehgTPYuyy2hWQkQSntI0S7ESYqdLkoSe1HyEBFHHkCgSIvVdSEiWwKvCg==
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz"
@ -5383,7 +5402,7 @@ prompts@^2.3.2, prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.7.2:
prop-types@*, prop-types@^15.7.2:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -5552,6 +5571,16 @@ react-native-sqlite-storage@^6.0.1:
resolved "https://registry.npmjs.org/react-native-sqlite-storage/-/react-native-sqlite-storage-6.0.1.tgz"
integrity sha512-1tDFjrint6X6qSYKf3gDyz+XB+X79jfiL6xTugKHPRtF0WvqMtVgdLuNqZunIXjNEvNtNVEbXaeZ6MsguFu00A==
react-native-video@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-5.2.1.tgz#a17e856759d7e17eee9cbd9df0d05ba22e88d457"
integrity sha512-aJlr9MeTuQ0LpZ4n+EC9RvhoKeiPbLtI2Rxy8u7zo/wzGevbRpWHSBj9xZ5YDBXnAVXzuqyNIkGhdw7bfdIBZw==
dependencies:
deprecated-react-native-prop-types "^2.2.0"
keymirror "^0.1.1"
prop-types "^15.7.2"
shaka-player "^2.5.9"
react-native-web@~0.18.7:
version "0.18.9"
resolved "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.9.tgz"
@ -6024,6 +6053,13 @@ setprototypeof@1.2.0:
resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shaka-player@^2.5.9:
version "2.5.23"
resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.23.tgz#db92d1c6cf2314f0180a2cec11b0e2f2560336f5"
integrity sha512-3MC9k0OXJGw8AZ4n/ZNCZS2yDxx+3as5KgH6Tx4Q5TRboTBBCu6dYPI5vp1DxKeyU12MBN1Zcbs7AKzXv2EnCg==
dependencies:
eme-encryption-scheme-polyfill "^2.0.1"
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz"