show decrypt progress

This commit is contained in:
Roland Osborne 2024-12-18 11:43:10 -08:00
parent f2bf282026
commit ae2f4f7813
6 changed files with 5394 additions and 8028 deletions

View File

@ -21,6 +21,12 @@
z-index: 1; z-index: 1;
transition: all 400ms; transition: all 400ms;
.progress {
position: absolute;
bottom: 16px;
width: 25%;
}
.close { .close {
position: absolute; position: absolute;
top: 16px; top: 16px;

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { MediaAsset } from '../../conversation/Conversation'; import { MediaAsset } from '../../conversation/Conversation';
import { useImageAsset } from './useImageAsset.hook'; import { useImageAsset } from './useImageAsset.hook';
import { ActionIcon, Image } from '@mantine/core' import { Progress, ActionIcon, Image } from '@mantine/core'
import classes from './ImageAsset.module.css' import classes from './ImageAsset.module.css'
import { IconX } from '@tabler/icons-react' import { IconX } from '@tabler/icons-react'
@ -46,6 +46,9 @@ export function ImageAsset({ topicId, asset }: { topicId: string, asset: MediaAs
<img className={classes.image} src={state.dataUrl} /> <img className={classes.image} src={state.dataUrl} />
</div> </div>
)} )}
{ state.loading && state.loadPercent > 0 && (
<Progress className={classes.progress} value={state.loadPercent} />
)}
<ActionIcon className={classes.close} variant="subtle" size="lg" onClick={hide}> <ActionIcon className={classes.close} variant="subtle" size="lg" onClick={hide}>
<IconX size="lg" /> <IconX size="lg" />
</ActionIcon> </ActionIcon>

View File

@ -9,6 +9,8 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
const [state, setState] = useState({ const [state, setState] = useState({
thumbUrl: null, thumbUrl: null,
dataUrl: null, dataUrl: null,
loading: false,
loadPercent: 0,
}) })
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -40,13 +42,15 @@ export function useImageAsset(topicId: string, asset: MediaAsset) {
loadImage: async () => { loadImage: async () => {
const { focus } = app.state; const { focus } = app.state;
const assetId = asset.image ? asset.image.full : asset.encrypted ? asset.encrypted.parts : null; const assetId = asset.image ? asset.image.full : asset.encrypted ? asset.encrypted.parts : null;
if (focus && assetId != null) { if (focus && assetId != null && !state.loading) {
updateState({ loading: true, loadPercent: 0 });
try { try {
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId); const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) });
updateState({ dataUrl }); updateState({ dataUrl });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
updateState({ loading: false });
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -131,7 +131,7 @@ export interface Focus {
viewMoreTopics(): Promise<void>; viewMoreTopics(): Promise<void>;
getTopicAssetUrl(topicId: string, assetId: string, progress: (percent: number) => boolean): Promise<string>; getTopicAssetUrl(topicId: string, assetId: string, progress?: (percent: number) => boolean|void): Promise<string>;
flagTopic(topicId: string): Promise<void>; flagTopic(topicId: string): Promise<void>;
setBlockTopic(topicId: string): Promise<void>; setBlockTopic(topicId: string): Promise<void>;

View File

@ -241,7 +241,7 @@ export class FocusModule implements Focus {
} }
} }
private downloadBlock(topicId: string, blockId: string): Promise<string> { private downloadBlock(topicId: string, blockId: string, progress: (percent: number)=>void): Promise<string> {
const { cardId, channelId, connection } = this; const { cardId, channelId, connection } = this;
if (!connection) { if (!connection) {
throw new Error('disconnected from channel'); throw new Error('disconnected from channel');
@ -253,6 +253,7 @@ export class FocusModule implements Focus {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.onprogress = (ev: ProgressEvent<EventTarget>)=>{ progress((ev.loaded * 100) / ev.total) };
xhr.setRequestHeader('Content-Type', 'text/plain'); xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onload = () => { xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) { if (xhr.status >= 200 && xhr.status < 300) {
@ -761,7 +762,7 @@ export class FocusModule implements Focus {
await this.removeRemoteChannelTopic(topicId); await this.removeRemoteChannelTopic(topicId);
} }
public async getTopicAssetUrl(topicId: string, assetId: string, progress: null | ((percent: number) => boolean)): Promise<string> { public async getTopicAssetUrl(topicId: string, assetId: string, progress?: ((percent: number) => boolean|void)): Promise<string> {
const entry = this.topicEntries.get(topicId); const entry = this.topicEntries.get(topicId);
if (!entry) { if (!entry) {
throw new Error('topic entry not found'); throw new Error('topic entry not found');
@ -783,7 +784,21 @@ export class FocusModule implements Focus {
const write = await media.write(); const write = await media.write();
this.closeMedia.push(write.close); this.closeMedia.push(write.close);
for (let i = 0; i < asset.split.length; i++) { for (let i = 0; i < asset.split.length; i++) {
const block = await this.downloadBlock(topicId, asset.split[i].partId); let download = true;
const block = await this.downloadBlock(topicId, asset.split[i].partId, (percent: number)=>{
if (progress && download !== false) {
download = progress(Math.floor((i * 100 + percent) / asset.split.length));
}
});
if (download === false) {
throw new Error('aborted asset load');
}
if (progress) {
download = progress(Math.floor((i * 100) / asset.split.length));
}
if (download === false) {
throw new Error('aborted asset load');
}
const { data } = await crypto.aesDecrypt(block, asset.split[i].blockIv, channelKey); const { data } = await crypto.aesDecrypt(block, asset.split[i].blockIv, channelKey);
await write.setData(data); await write.setData(data);
} }