'init'
This commit is contained in:
198
app/providers/AttachmentProvider/useAttachment.ts
Normal file
198
app/providers/AttachmentProvider/useAttachment.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useDownloadStatus } from "../TransportProvider/useDownloadStatus";
|
||||
import { useUploadStatus } from "../TransportProvider/useUploadStatus";
|
||||
import { useFileStorage } from "../../hooks/useFileStorage";
|
||||
import { usePublicKey } from "../AccountProvider/usePublicKey";
|
||||
import { usePrivatePlain } from "../AccountProvider/usePrivatePlain";
|
||||
import { decodeWithPassword, encodeWithPassword, generateMd5 } from "../../crypto/crypto";
|
||||
import { useTransport } from "../TransportProvider/useTransport";
|
||||
import { useDialogsCache } from "../DialogProvider/useDialogsCache";
|
||||
import { useConsoleLogger } from "../../hooks/useConsoleLogger";
|
||||
import { Attachment, AttachmentType } from "../ProtocolProvider/protocol/packets/packet.message";
|
||||
import { useMemory } from "../MemoryProvider/useMemory";
|
||||
import { DialogContext } from "../DialogProvider/DialogProvider";
|
||||
import { useSaveAvatar } from "../AvatarProvider/useSaveAvatar";
|
||||
import { AVATAR_PASSWORD_TO_ENCODE } from "@/app/constants";
|
||||
import { useDialog } from "../DialogProvider/useDialog";
|
||||
|
||||
export enum DownloadStatus {
|
||||
DOWNLOADED,
|
||||
NOT_DOWNLOADED,
|
||||
PENDING,
|
||||
DECRYPTING,
|
||||
DOWNLOADING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
export function useAttachment(attachment: Attachment, keyPlain: string) {
|
||||
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
const uploadedPercentage = useUploadStatus(attachment.id);
|
||||
const downloadPercentage = useDownloadStatus(attachment.id);
|
||||
const [downloadStatus, setDownloadStatus] = useMemory("attachment-downloaded-status-" + attachment.id, DownloadStatus.PENDING, true);
|
||||
const [downloadTag, setDownloadTag] = useState("");
|
||||
const {readFile, writeFile} = useFileStorage();
|
||||
const { downloadFile } = useTransport();
|
||||
const publicKey = usePublicKey();
|
||||
const privatePlain = usePrivatePlain();
|
||||
const {updateAttachmentInDialogCache} = useDialogsCache();
|
||||
const {info} = useConsoleLogger('useAttachment');
|
||||
const {updateAttachmentsInMessagesByAttachmentId} = useDialog();
|
||||
|
||||
|
||||
const context = useContext(DialogContext);
|
||||
if(!context) {
|
||||
throw new Error("useAttachment must be used within a DialogProvider");
|
||||
}
|
||||
const {dialog} = context;
|
||||
const saveAvatar = useSaveAvatar();
|
||||
|
||||
useEffect(() => {
|
||||
calcDownloadStatus();
|
||||
}, []);
|
||||
|
||||
const getPreview = () => {
|
||||
if(attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Это тег загрузки
|
||||
*/
|
||||
return attachment.preview.split("::").splice(1).join("::");
|
||||
}
|
||||
return attachment.preview;
|
||||
}
|
||||
|
||||
const calcDownloadStatus = async () => {
|
||||
if(attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Это тег загрузки
|
||||
*/
|
||||
setDownloadTag(attachment.preview.split("::")[0]);
|
||||
}
|
||||
if(!attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Там не тег загрузки, значит это наш файл
|
||||
*/
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
if (downloadStatus == DownloadStatus.DOWNLOADED) {
|
||||
return;
|
||||
}
|
||||
if(attachment.type == AttachmentType.FILE){
|
||||
/**
|
||||
* Если это файл, то он хранится не в папке медиа,
|
||||
* а в загрузках
|
||||
*/
|
||||
const preview = getPreview();
|
||||
const filename = preview.split("::")[1];
|
||||
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
|
||||
const fileData = await readFile(pathInDownloads, false);
|
||||
if(fileData){
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
if(attachment.type == AttachmentType.AVATAR){
|
||||
/**
|
||||
* Если это аватар, то он хранится не в папке медиа,
|
||||
* а в папке аватарок
|
||||
*/
|
||||
const fileData = await readFile(`a/${await generateMd5(attachment.id + publicKey)}`);
|
||||
if(fileData){
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileData = await readFile(`m/${await generateMd5(attachment.id + publicKey)}`);
|
||||
if(fileData){
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
setDownloadStatus(DownloadStatus.NOT_DOWNLOADED);
|
||||
}
|
||||
|
||||
const getBlob = async () => {
|
||||
if(attachment.blob && attachment.blob != ""){
|
||||
return attachment.blob;
|
||||
}
|
||||
const folder = (attachment.type == AttachmentType.AVATAR) ? "a" : "m";
|
||||
const fileData = await readFile(`${folder}/${await generateMd5(attachment.id + publicKey)}`);
|
||||
if (!fileData) {
|
||||
return "";
|
||||
}
|
||||
const password = (attachment.type == AttachmentType.AVATAR) ? AVATAR_PASSWORD_TO_ENCODE : privatePlain;
|
||||
const decryptedData = await decodeWithPassword(password, Buffer.from(fileData, 'binary').toString());
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
const download = async () => {
|
||||
if(downloadStatus == DownloadStatus.DOWNLOADED){
|
||||
return;
|
||||
}
|
||||
if (downloadTag == "") {
|
||||
return;
|
||||
}
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADING);
|
||||
info("Downloading attachment: " + downloadTag);
|
||||
let downloadedBlob = '';
|
||||
try {
|
||||
downloadedBlob = await downloadFile(attachment.id,
|
||||
downloadTag);
|
||||
} catch (e) {
|
||||
console.info(e);
|
||||
info("Error downloading attachment: " + attachment.id);
|
||||
setDownloadStatus(DownloadStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
setDownloadStatus(DownloadStatus.DECRYPTING);
|
||||
//console.info("Decrypted attachment ", Buffer.from(keyPlain, 'binary').toString('hex'));
|
||||
const decrypted = await decodeWithPassword(keyPlain, downloadedBlob);
|
||||
setDownloadTag("");
|
||||
if(attachment.type == AttachmentType.FILE) {
|
||||
/**
|
||||
* Если это файл то шифрованную копию не пишем,
|
||||
* пишем его сразу в загрузки
|
||||
*/
|
||||
const preview = getPreview();
|
||||
const filename = preview.split("::")[1];
|
||||
let buffer = Buffer.from(decrypted.split(",")[1], 'base64');
|
||||
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
|
||||
await writeFile(pathInDownloads, buffer, false);
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
if(attachment.type == AttachmentType.AVATAR) {
|
||||
/**
|
||||
* Аватарки, пишем их в папку аватарок
|
||||
*/
|
||||
const avatarPath = `a/${await generateMd5(attachment.id + publicKey)}`;
|
||||
await writeFile(avatarPath,
|
||||
Buffer.from(await encodeWithPassword(AVATAR_PASSWORD_TO_ENCODE, decrypted)));
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
saveAvatar(dialog, avatarPath, decrypted);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Если это не файл, то обновляем состояние кэша,
|
||||
* и пишем шифрованную копию
|
||||
*/
|
||||
updateAttachmentInDialogCache(attachment.id, decrypted);
|
||||
updateAttachmentsInMessagesByAttachmentId(attachment.id, decrypted);
|
||||
await writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decrypted)).toString('binary'));
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
}
|
||||
|
||||
return {
|
||||
uploadedPercentage,
|
||||
downloadPercentage,
|
||||
downloadStatus,
|
||||
getPreview,
|
||||
getBlob,
|
||||
download
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user