From 51b44eb2581ad239b8faa213e54d03fec46ef40a Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Thu, 19 Dec 2024 13:33:56 -0800 Subject: [PATCH] added binary download from message --- .../conversation/binaryFile/BinaryFile.tsx | 8 +- .../message/audioAsset/AudioAsset.module.css | 1 + .../message/audioAsset/useAudioAsset.hook.ts | 18 --- .../binaryAsset/BinaryAsset.module.css | 105 +++++++++++++++--- .../src/message/binaryAsset/BinaryAsset.tsx | 75 +++++++++++-- .../binaryAsset/useBinaryAsset.hook.ts | 25 ++++- 6 files changed, 179 insertions(+), 53 deletions(-) diff --git a/app/client/web/src/conversation/binaryFile/BinaryFile.tsx b/app/client/web/src/conversation/binaryFile/BinaryFile.tsx index 2dabf872..4758bddb 100644 --- a/app/client/web/src/conversation/binaryFile/BinaryFile.tsx +++ b/app/client/web/src/conversation/binaryFile/BinaryFile.tsx @@ -13,9 +13,11 @@ export function BinaryFile({ source, disabled, remove }: {source: File, disabled { state.name } { state.extension } - - - + { !state.disabled && ( + + + + )} ); } diff --git a/app/client/web/src/message/audioAsset/AudioAsset.module.css b/app/client/web/src/message/audioAsset/AudioAsset.module.css index 08da65ac..574e7fd4 100644 --- a/app/client/web/src/message/audioAsset/AudioAsset.module.css +++ b/app/client/web/src/message/audioAsset/AudioAsset.module.css @@ -60,6 +60,7 @@ .play { position: absolute; + cursor: pointer; } .frame { diff --git a/app/client/web/src/message/audioAsset/useAudioAsset.hook.ts b/app/client/web/src/message/audioAsset/useAudioAsset.hook.ts index 226d3a51..2b35a733 100644 --- a/app/client/web/src/message/audioAsset/useAudioAsset.hook.ts +++ b/app/client/web/src/message/audioAsset/useAudioAsset.hook.ts @@ -7,7 +7,6 @@ import { MediaAsset } from '../../conversation/Conversation'; export function useAudioAsset(topicId: string, asset: MediaAsset) { const app = useContext(AppContext) as ContextType const [state, setState] = useState({ - thumbUrl: null, dataUrl: null, loading: false, loadPercent: 0, @@ -18,23 +17,6 @@ export function useAudioAsset(topicId: string, asset: MediaAsset) { setState((s) => ({ ...s, ...value })) } - const setThumb = async () => { - const { focus } = app.state; - const assetId = asset.audio ? asset.audio.thumb : asset.encrypted ? asset.encrypted.thumb : null; - if (focus && assetId != null) { - try { - const thumbUrl = await focus.getTopicAssetUrl(topicId, assetId); - updateState({ thumbUrl }); - } catch (err) { - console.log(err); - } - } - }; - - useEffect(() => { - setThumb(); - }, [asset]); - const actions = { unloadAudio: () => { updateState({ dataUrl: null }); diff --git a/app/client/web/src/message/binaryAsset/BinaryAsset.module.css b/app/client/web/src/message/binaryAsset/BinaryAsset.module.css index 80262f17..8dd35532 100644 --- a/app/client/web/src/message/binaryAsset/BinaryAsset.module.css +++ b/app/client/web/src/message/binaryAsset/BinaryAsset.module.css @@ -1,34 +1,107 @@ .asset { - position: relative; + cursor: pointer; display: flex; align-items: center; justify-content: center; + position: relative; - .label { + .detail { position: absolute; top: 0; - display: flex; - align-items: center; - flex-direction: column; width: 100%; - overflow: hidden; + + .label { + width: 100%; + text-align: center; + font-size: 12px; + } + + .extension { + width: 100%; + text-align: center; + font-size: 12px; + font-weight: bold; + } } - .name { - text-align: center; - width: 100%; - text-overflow: ellipsis; - overflow: hidden; - } - - .extension { - font-weight: bold; + .download { + position: absolute; } .thumb { width: auto; height: 128px; - border-radius: 4px; } } +.modal { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(8,8,8, .5); + backdrop-filter: blur(33px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1; + transition: all 400ms; + + .progress { + position: absolute; + bottom: 16px; + width: 25%; + } + + .detail { + position: absolute; + top: 0; + width: 100%; + + .label { + width: 100%; + text-align: center; + font-size: 16px; + } + + .extension { + width: 100%; + text-align: center; + font-size: 16px; + font-weight: bold; + } + } + + .close { + position: absolute; + top: 16px; + right: 16px; + } + + .download { + position: absolute; + cursor: pointer; + } + + .frame { + position: absolute; + width: 80vw; + height: 80vh; + max-width: 300px; + max-height: 300px; + display: flex; + align-items: center; + justify-content: center; + + .image { + width: 80vw; + height: 80vh; + max-width: 300px; + max-height: 300px; + background-color: transparent; + object-fit: contain; + } + + } +} diff --git a/app/client/web/src/message/binaryAsset/BinaryAsset.tsx b/app/client/web/src/message/binaryAsset/BinaryAsset.tsx index 2bc02e44..feaf8bfd 100644 --- a/app/client/web/src/message/binaryAsset/BinaryAsset.tsx +++ b/app/client/web/src/message/binaryAsset/BinaryAsset.tsx @@ -1,21 +1,76 @@ -import React from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { MediaAsset } from '../../conversation/Conversation'; import { useBinaryAsset } from './useBinaryAsset.hook'; -import binary from '../../images/binary.png' +import { Progress, ActionIcon, Image } from '@mantine/core' import classes from './BinaryAsset.module.css' -import { Image } from '@mantine/core' +import { IconDownload, IconX } from '@tabler/icons-react' +import binary from '../../images/binary.png' export function BinaryAsset({ topicId, asset }: { topicId: string, asset: MediaAsset }) { const { state, actions } = useBinaryAsset(topicId, asset); - const { label, extension } = asset.encrypted || asset.binary || { label: '', extension: '' }; + const [showModal, setShowModal] = useState(false); + const [showBinary, setShowBinary] = useState(false); + const { label, extension } = asset.encrypted || asset.binary || { label: 'asset', extension: 'dat' }; + + const show = () => { + setShowModal(true); + } + + const hide = () => { + setShowModal(false); + } + + const download = () => { + console.log("DOWNLOAD"); + const link = document.createElement("a"); + link.download = `${label}.${extension.toLowerCase()}` + link.href = state.dataUrl; + link.click(); + link.remove(); + } + + useEffect(() => { + if (showModal) { + setShowBinary(true); + actions.loadBinary(); + } else { + setShowBinary(false); + actions.unloadBinary(); + } + }, [showModal]); + return ( -
- -
-
{ label }
-
{ extension }
+
+
+ +
+
{ label }
+
{ extension }
+
+
+ + { showModal && ( +
+
+ +
+
{ label }
+
{ extension }
+
+ { state.dataUrl && ( + + )} +
+ { state.loading && state.loadPercent > 0 && ( + + )} + + + +
+ )}
- ) + ); } diff --git a/app/client/web/src/message/binaryAsset/useBinaryAsset.hook.ts b/app/client/web/src/message/binaryAsset/useBinaryAsset.hook.ts index 04651e11..5c58d044 100644 --- a/app/client/web/src/message/binaryAsset/useBinaryAsset.hook.ts +++ b/app/client/web/src/message/binaryAsset/useBinaryAsset.hook.ts @@ -7,6 +7,9 @@ import { MediaAsset } from '../../conversation/Conversation'; export function useBinaryAsset(topicId: string, asset: MediaAsset) { const app = useContext(AppContext) as ContextType const [state, setState] = useState({ + dataUrl: null, + loading: false, + loadPercent: 0, }) // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -15,13 +18,23 @@ export function useBinaryAsset(topicId: string, asset: MediaAsset) { } const actions = { - getAssetUrl: async (topicId: string, assetId: string) => { - const { focus } = app.state; - if (!focus) { - throw new Error('no channel in focus'); - } - return await focus.getTopicAssetUrl(topicId, assetId, (percent: number)=>true); + unloadBinary: () => { + updateState({ dataUrl: null }); }, + loadBinary: async () => { + const { focus } = app.state; + const assetId = asset.audio ? asset.audio.hd : asset.encrypted ? asset.encrypted.parts : null; + if (focus && assetId != null && !state.loading) { + updateState({ loading: true, loadPercent: 0 }); + try { + const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) }); + updateState({ dataUrl, loading: false }); + } catch (err) { + updateState({ loading: false }); + console.log(err); + } + } + } } return { state, actions }