diff --git a/app/mobile/ios/Podfile.lock b/app/mobile/ios/Podfile.lock
index e4f6a2d6..53838531 100644
--- a/app/mobile/ios/Podfile.lock
+++ b/app/mobile/ios/Podfile.lock
@@ -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
diff --git a/app/mobile/package.json b/app/mobile/package.json
index ba60b4be..81af89f3 100644
--- a/app/mobile/package.json
+++ b/app/mobile/package.json
@@ -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"
diff --git a/app/mobile/src/session/conversation/addTopic/AddTopic.jsx b/app/mobile/src/session/conversation/addTopic/AddTopic.jsx
index 7cc24200..8ec89f9d 100644
--- a/app/mobile/src/session/conversation/addTopic/AddTopic.jsx
+++ b/app/mobile/src/session/conversation/addTopic/AddTopic.jsx
@@ -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 (
- remove(item)}>
-
-
+ remove(item)} />
);
}
+ if (item.type === 'video') {
+ return (
+ remove(item)}
+ setPosition={(position) => actions.setVideoPosition(item.key, position)}
+ />
+ )
+ }
else {
return (
@@ -67,7 +85,7 @@ export function AddTopic() {
-
+
diff --git a/app/mobile/src/session/conversation/addTopic/AddTopic.styled.js b/app/mobile/src/session/conversation/addTopic/AddTopic.styled.js
index 5ce2051c..6c4c7d82 100644
--- a/app/mobile/src/session/conversation/addTopic/AddTopic.styled.js
+++ b/app/mobile/src/session/conversation/addTopic/AddTopic.styled.js
@@ -60,8 +60,8 @@ export const styles = StyleSheet.create({
},
carousel: {
paddingTop: 8,
- paddingLeft: 16,
paddingRight: 16,
+ paddingLeft: 16,
},
})
diff --git a/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.jsx b/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.jsx
new file mode 100644
index 00000000..e1320c88
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.jsx
@@ -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 (
+
+
+
+
+
+
+ );
+}
diff --git a/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.styled.js b/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.styled.js
new file mode 100644
index 00000000..9c879c72
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/imageFile/ImageFile.styled.js
@@ -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,
+ },
+})
+
diff --git a/app/mobile/src/session/conversation/addTopic/imageFile/useImageFile.hook.js b/app/mobile/src/session/conversation/addTopic/imageFile/useImageFile.hook.js
new file mode 100644
index 00000000..c82d5c5c
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/imageFile/useImageFile.hook.js
@@ -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 };
+}
+
diff --git a/app/mobile/src/session/conversation/addTopic/useAddTopic.hook.js b/app/mobile/src/session/conversation/addTopic/useAddTopic.hook.js
index 459559bf..49f51236 100644
--- a/app/mobile/src/session/conversation/addTopic/useAddTopic.hook.js
+++ b/app/mobile/src/session/conversation/addTopic/useAddTopic.hook.js
@@ -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))});
},
diff --git a/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.jsx b/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.jsx
new file mode 100644
index 00000000..3cc7303b
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.jsx
@@ -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 (
+
+
+ );
+}
diff --git a/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.styled.js b/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.styled.js
new file mode 100644
index 00000000..9c879c72
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/videoFile/VideoFile.styled.js
@@ -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,
+ },
+})
+
diff --git a/app/mobile/src/session/conversation/addTopic/videoFile/useVideoFile.hook.js b/app/mobile/src/session/conversation/addTopic/videoFile/useVideoFile.hook.js
new file mode 100644
index 00000000..1f0eb273
--- /dev/null
+++ b/app/mobile/src/session/conversation/addTopic/videoFile/useVideoFile.hook.js
@@ -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 };
+}
+
diff --git a/app/mobile/yarn.lock b/app/mobile/yarn.lock
index 1872e022..50973132 100644
--- a/app/mobile/yarn.lock
+++ b/app/mobile/yarn.lock
@@ -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"