Skip to content

Commit

Permalink
adding generic file attachment to mobile app
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandosborne committed Aug 12, 2023
1 parent 25f5a55 commit f2919bf
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 3 deletions.
10 changes: 9 additions & 1 deletion app/mobile/src/session/conversation/topicItem/TopicItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import avatar from 'images/avatar.png';
import { VideoThumb } from './videoThumb/VideoThumb';
import { AudioThumb } from './audioThumb/AudioThumb';
import { ImageThumb } from './imageThumb/ImageThumb';
import { BinaryThumb } from './binaryThumb/BinaryThumb';
import { ImageAsset } from './imageAsset/ImageAsset';
import { AudioAsset } from './audioAsset/AudioAsset';
import { VideoAsset } from './videoAsset/VideoAsset';
import { BinaryAsset } from './binaryAsset/BinaryAsset';
import Carousel from 'react-native-reanimated-carousel';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaView, SafeAreaProvider } from 'react-native-safe-area-context';
Expand Down Expand Up @@ -120,7 +122,10 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
<VideoThumb url={thumb.item.thumb} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
{ thumb.item.type === 'audio' && (
<AudioThumb labe={thumb.item.label} onAssetView={() => actions.showCarousel(thumb.index)} />
<AudioThumb label={thumb.item.label} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
{ thumb.item.type === 'binary' && (
<BinaryThumb label={thumb.item.label} extension={thumb.item.extension} onAssetView={() => actions.showCarousel(thumb.index)} />
)}
</View>
);
Expand Down Expand Up @@ -234,6 +239,9 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
{ state.assets[index].type === 'audio' && (
<AudioAsset asset={state.assets[index]} dismiss={actions.hideCarousel} />
)}
{ state.assets[index].type === 'binary' && (
<BinaryAsset asset={state.assets[index]} dismiss={actions.hideCarousel} />
)}
</View>
)} />
</GestureHandlerRootView>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ActivityIndicator, Alert, View, Text, TouchableOpacity } from 'react-native';
import { useEffect, useRef } from 'react';
import Colors from 'constants/Colors';
import { useBinaryAsset } from './useBinaryAsset.hook';
import { styles } from './BinaryAsset.styled';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import AntIcons from 'react-native-vector-icons/AntDesign';

export function BinaryAsset({ asset, dismiss }) {

const { state, actions } = useBinaryAsset();


const download = async () => {
try {
const url = asset.encrypted ? `file://${asset.decrypted}` : asset.data;
await actions.download(asset.label, asset.extension, url);
}
catch (err) {
Alert.alert(
'Download Failed',
'Please try again.'
)
}
};

return (
<View style={{ ...styles.container, width: state.width, height: state.height }}>
<Text style={styles.label}>{ asset.label }</Text>
<TouchableOpacity style={styles.close} onPress={dismiss}>
<MatIcons name="window-close" size={32} color={Colors.white} />
</TouchableOpacity>
<View style={styles.action}>
{ asset.encrypted && !asset.decrypted && (
<TouchableOpacity style={styles.loading} onPress={dismiss}>
<ActivityIndicator color={Colors.white} size="large" />
{ asset.total > 1 && (
<Text style={styles.decrypting}>{ asset.block } / { asset.total }</Text>
)}
</TouchableOpacity>
)}
{ !state.downloading && (!asset.encrypted || asset.decrypted) && (
<TouchableOpacity onPress={download}>
<AntIcons name="download" size={64} color={Colors.white} />
</TouchableOpacity>
)}
{ state.downloading && (
<ActivityIndicator color={Colors.white} size="large" />
)}
</View>
<Text style={styles.extension}>{ asset.extension }</Text>
</View>
);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';

export const styles = StyleSheet.create({
container: {
position: 'relative',
borderRadius: 8,
backgroundColor: Colors.grey,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
},
label: {
textAlign: 'center',
fontSize: 20,
paddingTop: 8,
paddingLeft: 48,
paddingRight: 48,
color: Colors.white,
},
action: {
display: 'flex',
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
},
extension: {
textAlign: 'center',
fontSize: 48,
paddingBottom: 8,
paddingLeft: 48,
paddingRight: 48,
color: Colors.white,
},
close: {
position: 'absolute',
top: 0,
right: 0,
paddingTop: 4,
paddingBottom: 4,
paddingLeft: 8,
paddingRight: 8,
},
decrypting: {
fontVariant: ["tabular-nums"],
paddingTop: 16,
fontSize: 12,
color: '#888888',
},
})

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useState, useRef, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
import { Image } from 'react-native';
import { useWindowDimensions } from 'react-native';
import { Platform } from 'react-native';
import RNFetchBlob from "rn-fetch-blob";
import Share from 'react-native-share';

export function useBinaryAsset() {

const [state, setState] = useState({
width: 1,
height: 1,
downloading: false,
});

const dimensions = useWindowDimensions();

const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}

useEffect(() => {
const { width, height } = dimensions;
if (width < height) {
updateState({ width, height: width });
}
else {
updateState({ widht: height, height });
}
}, [dimensions]);

const actions = {
download: async (label, extension, url) => {
if (!state.downloading) {
try {
updateState({ downloading: true });

const blob = await RNFetchBlob.config({ fileCache: true }).fetch("GET", url);
const src = blob.path();
if (Platform.OS === 'ios') {
const path = `${RNFetchBlob.fs.dirs.DocumentDir}`
const dst = `${path}/${label}.${extension.toLowerCase()}`
if (RNFetchBlob.fs.exists(dst)) {
RNFetchBlob.fs.unlink(dst);
}
await RNFetchBlob.fs.mv(src, dst);
try {
await Share.open({ url: dst, message: `${label}.${extension}`, subject: `${label}.${extension}` })
}
catch (err) {
console.log(err);
}
RNFetchBlob.fs.unlink(dst);
}
else {
const path = `${RNFetchBlob.fs.dirs.DownloadDir}`
const dst = `${path}/${label}.${extension.toLowerCase()}`
if (RNFetchBlob.fs.exists(dst)) {
RNFetchBlob.fs.unlink(dst);
}
await RNFetchBlob.fs.mv(src, dst);
RNFetchBlob.fs.unlink(dst);
}

updateState({ downloading: false });
}
catch (err) {
console.log(err);
updateState({ downloading: false });
throw new Error('download failed');
}
}
}
};

return { state, actions };
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { View, Text, Image } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { styles } from './BinaryThumb.styled';
import Colors from 'constants/Colors';
import AntIcons from 'react-native-vector-icons/AntDesign';

export function BinaryThumb({ label, extension, onAssetView }) {

return (
<TouchableOpacity activeOpacity={1} style={styles.canvas} onPress={onAssetView}>
<Text style={styles.label}>{ label }</Text>
<View style={styles.action}>
<AntIcons name="download" size={28} color={Colors.white} />
</View>
<Text style={styles.extension}>{ extension }</Text>
</TouchableOpacity>
);

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';

export const styles = StyleSheet.create({
canvas: {
borderRadius: 4,
width: 92,
height: 92,
marginRight: 16,
backgroundColor: Colors.grey,
display: 'flex',
alignItems: 'center',
},
action: {
flexGrow: 1,
},
label: {
textAlign: 'center',
color: Colors.white,
padding: 4,
fontSize: 14,
},
extension: {
textAlign: 'center',
color: Colors.white,
fontSize: 20,
}
})

Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export function useTopicItem(item, hosting, remove, contentKey) {
const asset = parsed[i];
if (asset.encrypted) {
const encrypted = true;
const { type, thumb, label, parts } = asset.encrypted;
assets.push({ type, thumb, label, encrypted, decrypted: null, parts });
const { type, thumb, label, extension, parts } = asset.encrypted;
assets.push({ type, thumb, label, extension, encrypted, decrypted: null, parts });
}
else {
const encrypted = false
Expand All @@ -85,6 +85,12 @@ export function useTopicItem(item, hosting, remove, contentKey) {
const full = conversation.actions.getTopicAssetUrl(item.topicId, asset.audio.full);
assets.push({ type, label, encrypted, full });
}
else if (asset.binary) {
const type = 'binary';
const { label, extension } = asset.binary;
const data = conversation.actions.getTopicAssetUrl(item.topicId, asset.binary.data);
assets.push({ type, label, extension, data });
}
}
};
}
Expand Down

0 comments on commit f2919bf

Please sign in to comment.