mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 18:15:19 +00:00
added binary download from message
This commit is contained in:
parent
990f01c9e5
commit
51b44eb258
@ -13,9 +13,11 @@ export function BinaryFile({ source, disabled, remove }: {source: File, disabled
|
||||
<Image radius="sm" className={classes.thumb} src={binary} />
|
||||
<Text className={classes.name}>{ state.name }</Text>
|
||||
<Text className={classes.extension}>{ state.extension }</Text>
|
||||
<ActionIcon className={classes.close} variant="subtle" disabled={disabled} onClick={remove}>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
{ !state.disabled && (
|
||||
<ActionIcon className={classes.close} variant="subtle" onClick={remove}>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -60,6 +60,7 @@
|
||||
|
||||
.play {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.frame {
|
||||
|
@ -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 });
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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 (
|
||||
<div className={classes.asset}>
|
||||
<Image className={classes.thumb} src={binary} fit="contain" />
|
||||
<div className={classes.label}>
|
||||
<div className={classes.name}>{ label }</div>
|
||||
<div className={classes.extension}>{ extension }</div>
|
||||
<div>
|
||||
<div className={classes.asset} onClick={show}>
|
||||
<Image radius="sm" className={classes.thumb} src={binary} fit="contain" />
|
||||
<div className={classes.detail}>
|
||||
<div className={classes.label}>{ label }</div>
|
||||
<div className={classes.extension}>{ extension }</div>
|
||||
</div>
|
||||
<IconDownload className={classes.download} size={32} />
|
||||
</div>
|
||||
|
||||
{ showModal && (
|
||||
<div className={classes.modal} style={ showBinary ? { opacity: 1} : { opacity: 0 }}>
|
||||
<div className={classes.frame}>
|
||||
<Image radius="sm" className={classes.image} src={binary} fit="contain" />
|
||||
<div className={classes.detail}>
|
||||
<div className={classes.label}>{ label }</div>
|
||||
<div className={classes.extension}>{ extension }</div>
|
||||
</div>
|
||||
{ state.dataUrl && (
|
||||
<IconDownload className={classes.download} size={64} onClick={download} />
|
||||
)}
|
||||
</div>
|
||||
{ state.loading && state.loadPercent > 0 && (
|
||||
<Progress className={classes.progress} value={state.loadPercent} />
|
||||
)}
|
||||
<ActionIcon className={classes.close} variant="subtle" size="lg" onClick={hide}>
|
||||
<IconX size="lg" />
|
||||
</ActionIcon>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 }
|
||||
|
Loading…
x
Reference in New Issue
Block a user