From 39b4a98c39232dedc409165b7817b5e2a99aaae2 Mon Sep 17 00:00:00 2001 From: balzack Date: Sun, 5 Jan 2025 15:52:16 -0800 Subject: [PATCH] supporting binary download --- app/client/mobile/ios/Podfile.lock | 6 ++++ app/client/mobile/package.json | 3 +- .../message/binaryAsset/BinaryAsset.styled.ts | 31 +++++++++++++++-- .../src/message/binaryAsset/BinaryAsset.tsx | 33 ++++++++++++++++--- .../binaryAsset/useBinaryAsset.hook.ts | 5 ++- app/client/mobile/yarn.lock | 27 ++++++++++++++- 6 files changed, 95 insertions(+), 10 deletions(-) diff --git a/app/client/mobile/ios/Podfile.lock b/app/client/mobile/ios/Podfile.lock index 611e69be..6aec585c 100644 --- a/app/client/mobile/ios/Podfile.lock +++ b/app/client/mobile/ios/Podfile.lock @@ -1259,6 +1259,8 @@ PODS: - React-logger (= 0.74.3) - React-perflogger (= 0.74.3) - React-utils (= 0.74.3) + - rn-fetch-blob (0.12.0): + - React-Core - RNCClipboard (1.15.0): - React-Core - RNFS (2.20.0): @@ -1493,6 +1495,7 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - rn-fetch-blob (from `../node_modules/rn-fetch-blob`) - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) @@ -1632,6 +1635,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + rn-fetch-blob: + :path: "../node_modules/rn-fetch-blob" RNCClipboard: :path: "../node_modules/@react-native-clipboard/clipboard" RNFS: @@ -1713,6 +1718,7 @@ SPEC CHECKSUMS: React-runtimescheduler: 0c80752bceb80924cb8a4babc2a8e3ed70d41e87 React-utils: a06061b3887c702235d2dac92dacbd93e1ea079e ReactCommon: f00e436b3925a7ae44dfa294b43ef360fbd8ccc4 + rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba RNCClipboard: 69ab8e51324d5b351f6ba72bbdb72478087a2c64 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 6fee3422fd8c81c5ee756fa72e3d1780e9943d9d diff --git a/app/client/mobile/package.json b/app/client/mobile/package.json index 769d9a8e..e83e1540 100644 --- a/app/client/mobile/package.json +++ b/app/client/mobile/package.json @@ -43,7 +43,8 @@ "react-native-video": "^6.8.2", "react-native-wheel-color-picker": "^1.3.1", "react-router-dom": "^6.26.0", - "react-router-native": "^6.26.0" + "react-router-native": "^6.26.0", + "rn-fetch-blob": "^0.12.0" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/app/client/mobile/src/message/binaryAsset/BinaryAsset.styled.ts b/app/client/mobile/src/message/binaryAsset/BinaryAsset.styled.ts index 85e66acb..de5b5619 100644 --- a/app/client/mobile/src/message/binaryAsset/BinaryAsset.styled.ts +++ b/app/client/mobile/src/message/binaryAsset/BinaryAsset.styled.ts @@ -1,5 +1,5 @@ import {StyleSheet} from 'react-native'; -import {Colors} from '../constants/Colors'; +import {Colors} from '../../constants/Colors'; export const styles = StyleSheet.create({ modal: { @@ -14,6 +14,9 @@ export const styles = StyleSheet.create({ borderRadius: 4, backgroundColor: '#444444', }, + control: { + backgroundColor: 'transparent', + }, container: { position: 'relative', width: 92, @@ -30,6 +33,11 @@ export const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + info: { + fontSize: 12, + position: 'absolute', + top: 0, + }, blur: { position: 'absolute', top: 0, @@ -52,10 +60,29 @@ export const styles = StyleSheet.create({ width: '100%', height: '100%', }, + label: { + flexGrow: 1, + fontSize: 32, + paddingLeft: 16, + minWidth: 0, + textOverflow: 'ellipsis', + flexShrink: 1, + }, close: { + display: 'flex', + flexDirection: 'row', position: 'absolute', + alignItems: 'center', top: 0, - right: 0, + minWidth: 0, + width: '100%', + }, + alert: { + position: 'absolute', + bottom: 0, + }, + alertLabel: { + color: Colors.offsync, }, closeIcon: { backgroundColor: 'transparent', diff --git a/app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx b/app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx index 625377f2..cebfe05c 100644 --- a/app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx +++ b/app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx @@ -1,17 +1,19 @@ import React, { useState, useEffect, useRef } from 'react'; -import { SafeAreaView, Modal, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native' -import { Icon, ProgressBar, IconButton } from 'react-native-paper' +import { SafeAreaView, Modal, Share, Pressable, View, Image, Animated, useAnimatedValue } from 'react-native' +import { Text, Icon, ProgressBar, IconButton } from 'react-native-paper' import { useBinaryAsset } from './useBinaryAsset.hook'; import { MediaAsset } from '../../conversation/Conversation'; import { styles } from './BinaryAsset.styled' import {BlurView} from '@react-native-community/blur'; import Video from 'react-native-video' import thumb from '../../images/binary.png'; +import RNFetchBlob from 'rn-fetch-blob'; export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string, asset: MediaAsset, loaded: ()=>void, show: boolean }) { const { state, actions } = useBinaryAsset(topicId, asset); const [modal, setModal] = useState(false); const opacity = useAnimatedValue(0); + const [alert, setAlert] = useState(''); useEffect(() => { if (show) { @@ -23,7 +25,21 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string, } }, [show]); + const share = async () => { + try { + setAlert(''); + const extension = asset.binary?.extension || asset.encrypted?.extension; + const options = { fileCache: true, appendExt: extension.toLowerCase() }; + const download = await RNFetchBlob.config(options).fetch("GET", state.dataUrl); + await Share.share({ url: download.path() }); + } catch (err) { + console.log(err); + setAlert(state.strings.operationFailed) + } + } + const showBinary = () => { + setAlert(''); setModal(true); actions.loadBinary(); }; @@ -48,6 +64,7 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string, + { asset.binary?.label || asset.encrypted?.label } @@ -59,16 +76,22 @@ export function BinaryAsset({ topicId, asset, loaded, show }: { topicId: string, resizeMode="contain" source={thumb} /> - - - + { state.dataUrl && ( + + + + )} { state.loading && ( )} + + { alert } + + { asset.binary?.label || asset.encrypted?.label } diff --git a/app/client/mobile/src/message/binaryAsset/useBinaryAsset.hook.ts b/app/client/mobile/src/message/binaryAsset/useBinaryAsset.hook.ts index 36d11607..1c4e398d 100644 --- a/app/client/mobile/src/message/binaryAsset/useBinaryAsset.hook.ts +++ b/app/client/mobile/src/message/binaryAsset/useBinaryAsset.hook.ts @@ -1,12 +1,15 @@ import { useState, useContext, useEffect, useRef } from 'react' import { AppContext } from '../../context/AppContext' +import { DisplayContext } from '../../context/DisplayContext'; import { Focus } from 'databag-client-sdk' import { ContextType } from '../../context/ContextType' import { MediaAsset } from '../../conversation/Conversation'; export function useBinaryAsset(topicId: string, asset: MediaAsset) { const app = useContext(AppContext) as ContextType + const display = useContext(DisplayContext) as ContextType const [state, setState] = useState({ + strings: display.state.strings, dataUrl: null, loading: false, loaded: false, @@ -25,7 +28,7 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) { }, loadBinary: async () => { const { focus } = app.state; - const assetId = asset.audio ? asset.audio.full : asset.encrypted ? asset.encrypted.parts : null; + const assetId = asset.binary ? asset.binary.data : asset.encrypted ? asset.encrypted.parts : null; if (focus && assetId != null && !state.loading && !state.dataUrl) { cancelled.current = false; updateState({ loading: true, loadPercent: 0 }); diff --git a/app/client/mobile/yarn.lock b/app/client/mobile/yarn.lock index b77c4125..9d70c613 100644 --- a/app/client/mobile/yarn.lock +++ b/app/client/mobile/yarn.lock @@ -3471,6 +3471,7 @@ __metadata: react-router-dom: ^6.26.0 react-router-native: ^6.26.0 react-test-renderer: 18.2.0 + rn-fetch-blob: ^0.12.0 typescript: 5.0.4 languageName: unknown linkType: soft @@ -3946,7 +3947,7 @@ __metadata: languageName: node linkType: hard -"base-64@npm:^0.1.0": +"base-64@npm:0.1.0, base-64@npm:^0.1.0": version: 0.1.0 resolution: "base-64@npm:0.1.0" checksum: 5a42938f82372ab5392cbacc85a5a78115cbbd9dbef9f7540fa47d78763a3a8bd7d598475f0d92341f66285afd377509851a9bb5c67bbecb89686e9255d5b3eb @@ -5734,6 +5735,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:7.0.6": + version: 7.0.6 + resolution: "glob@npm:7.0.6" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.2 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 6ad065f51982f9a76f7052984121c95bca376ea02060b21200ad62b400422b05f0dc331f72da89a73c21a2451cbe9bec16bb17dcf37a516dc51bbbb6efe462a1 + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": version: 10.4.5 resolution: "glob@npm:10.4.5" @@ -9598,6 +9613,16 @@ __metadata: languageName: node linkType: hard +"rn-fetch-blob@npm:^0.12.0": + version: 0.12.0 + resolution: "rn-fetch-blob@npm:0.12.0" + dependencies: + base-64: 0.1.0 + glob: 7.0.6 + checksum: 56e5832be583e97d58f955c0c4f6dcb0eb62c97dde331182c6effb726253731cb555d4a5f6c81079ed2fcb957f81633cbe95b37d326d063405f912506de20ff4 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0"