mirror of
https://github.com/Qortal/qortal-mobile.git
synced 2025-03-15 04:12:32 +00:00
changed logic for file save qortalrequest
This commit is contained in:
parent
72281c7fad
commit
d57ab6e9d3
12
src/App.tsx
12
src/App.tsx
@ -716,10 +716,8 @@ function App() {
|
||||
message?.isFromExtension
|
||||
) {
|
||||
qortalRequestPermissonFromExtension(message, event);
|
||||
} else if(message?.action === 'SHOW_SAVE_FILE_PICKER'){
|
||||
showSaveFilePicker(message?.payload)
|
||||
|
||||
} else if(message?.action === 'getFileFromIndexedDB'){
|
||||
}
|
||||
else if(message?.action === 'getFileFromIndexedDB'){
|
||||
handleGetFileFromIndexedDB(event);
|
||||
}
|
||||
};
|
||||
@ -1521,7 +1519,11 @@ function App() {
|
||||
show,
|
||||
message,
|
||||
rootHeight,
|
||||
showInfo
|
||||
showInfo,
|
||||
openSnackGlobal: openSnack,
|
||||
setOpenSnackGlobal: setOpenSnack,
|
||||
infoSnackCustom: infoSnack,
|
||||
setInfoSnackCustom: setInfoSnack
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
@ -1,19 +1,60 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { navigationControllerAtom } from '../../atoms/global';
|
||||
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import { saveFile } from '../../qortalRequests/get';
|
||||
import { mimeToExtensionMap } from '../../utils/memeTypes';
|
||||
import { MyContext } from '../../App';
|
||||
|
||||
|
||||
|
||||
|
||||
export const saveFileInChunks = async (blob: Blob, fileName: string, chunkSize = 1024 * 1024) => {
|
||||
const base64Prefix = 'data:video/mp4;base64,';
|
||||
export const saveFileInChunks = async (
|
||||
blob: Blob,
|
||||
fileName: string,
|
||||
chunkSize = 1024 * 1024
|
||||
) => {
|
||||
try {
|
||||
let offset = 0;
|
||||
let isFirstChunk = true;
|
||||
const fullFileName = fileName + Date.now() + '.mp4'
|
||||
|
||||
// Extract the MIME type from the blob
|
||||
const mimeType = blob.type || 'application/octet-stream';
|
||||
|
||||
// Create the dynamic base64 prefix
|
||||
const base64Prefix = `data:${mimeType};base64,`;
|
||||
|
||||
// Function to extract extension from fileName
|
||||
const getExtensionFromFileName = (name: string): string => {
|
||||
const lastDotIndex = name.lastIndexOf('.');
|
||||
if (lastDotIndex !== -1) {
|
||||
return name.substring(lastDotIndex); // includes the dot
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// Extract existing extension from fileName
|
||||
const existingExtension = getExtensionFromFileName(fileName);
|
||||
|
||||
// Remove existing extension from fileName to avoid duplication
|
||||
if (existingExtension) {
|
||||
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
// Map MIME type to file extension
|
||||
const mimeTypeToExtension = (mimeType: string): string => {
|
||||
|
||||
return mimeToExtensionMap[mimeType] || existingExtension || ''; // Use existing extension if MIME type not found
|
||||
};
|
||||
|
||||
// Determine the final extension to use
|
||||
const extension = mimeTypeToExtension(mimeType);
|
||||
|
||||
// Construct the full file name with timestamp and extension
|
||||
const fullFileName = `${fileName}_${Date.now()}${extension}`;
|
||||
|
||||
// Read the blob in chunks
|
||||
while (offset < blob.size) {
|
||||
// Extract the current chunk
|
||||
@ -28,7 +69,7 @@ export const saveFileInChunks = async (blob: Blob, fileName: string, chunkSize =
|
||||
data: isFirstChunk ? base64Prefix + base64Chunk : base64Chunk,
|
||||
directory: Directory.Documents,
|
||||
recursive: true,
|
||||
append: !isFirstChunk // Append after the first chunk
|
||||
append: !isFirstChunk, // Append after the first chunk
|
||||
});
|
||||
|
||||
// Update offset and flag
|
||||
@ -36,13 +77,14 @@ export const saveFileInChunks = async (blob: Blob, fileName: string, chunkSize =
|
||||
isFirstChunk = false;
|
||||
}
|
||||
|
||||
console.log("File saved successfully in chunks:", fileName);
|
||||
console.log('File saved successfully in chunks:', fullFileName);
|
||||
} catch (error) {
|
||||
console.error("Error saving file in chunks:", error);
|
||||
console.error('Error saving file in chunks:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Helper function to convert a Blob to a Base64 string
|
||||
const blobToBase64 = (blob: Blob): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -220,19 +262,42 @@ const UIQortalRequests = [
|
||||
|
||||
|
||||
|
||||
export const showSaveFilePicker = async (data) => {
|
||||
let blob;
|
||||
let fileName;
|
||||
export const showSaveFilePicker = async (data, {openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom}) => {
|
||||
|
||||
|
||||
try {
|
||||
const { filename, mimeType, fileId } = data;
|
||||
const { filename, mimeType, blob } = data;
|
||||
|
||||
// Retrieve file from IndexedDB or any other source
|
||||
blob = await retrieveFileFromIndexedDB(fileId);
|
||||
fileName = filename;
|
||||
setInfoSnackCustom({
|
||||
type: "info",
|
||||
message:
|
||||
"Saving file...",
|
||||
});
|
||||
|
||||
|
||||
setOpenSnackGlobal(true);
|
||||
|
||||
await saveFileInChunks(blob, filename)
|
||||
setInfoSnackCustom({
|
||||
type: "success",
|
||||
message:
|
||||
"Saving file success!",
|
||||
});
|
||||
|
||||
|
||||
await saveFileInChunks(blob, fileName)
|
||||
setOpenSnackGlobal(true);
|
||||
} catch (error) {
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message:
|
||||
error?.message ? `Error saving file: ${error?.message}` : 'Error saving file',
|
||||
});
|
||||
|
||||
|
||||
setOpenSnackGlobal(true);
|
||||
console.error("Error saving file:", error);
|
||||
|
||||
}
|
||||
@ -323,6 +388,10 @@ currentIndex: -1,
|
||||
isDOMContentLoaded: false
|
||||
})
|
||||
const setHasSettingsChangedAtom = useSetRecoilState(navigationControllerAtom);
|
||||
const { openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom } = useContext(MyContext);
|
||||
|
||||
|
||||
useEffect(()=> {
|
||||
@ -391,10 +460,23 @@ isDOMContentLoaded: false
|
||||
{ action: event.data.action, type: 'qortalRequest', payload: event.data, isExtension: true },
|
||||
event.ports[0]
|
||||
);
|
||||
} else if(event?.data?.action === 'SAVE_FILE'
|
||||
){
|
||||
try {
|
||||
const res = await saveFile( event.data, null, true, {
|
||||
openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} else if (
|
||||
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
|
||||
event?.data?.action === 'PUBLISH_QDN_RESOURCE' ||
|
||||
event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'SAVE_FILE'
|
||||
event?.data?.action === 'ENCRYPT_DATA'
|
||||
|
||||
) {
|
||||
let data;
|
||||
|
@ -303,25 +303,7 @@ function setLocalStorage(key, data) {
|
||||
break;
|
||||
}
|
||||
|
||||
case "SAVE_FILE": {
|
||||
try {
|
||||
const res = await saveFile(request.payload, event.source, isFromExtension);
|
||||
event.source.postMessage({
|
||||
requestId: request.requestId,
|
||||
action: request.action,
|
||||
payload: res,
|
||||
type: "backgroundMessageResponse",
|
||||
}, event.origin);
|
||||
} catch (error) {
|
||||
event.source.postMessage({
|
||||
requestId: request.requestId,
|
||||
action: request.action,
|
||||
error: error.message,
|
||||
type: "backgroundMessageResponse",
|
||||
}, event.origin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case "DEPLOY_AT": {
|
||||
try {
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
isUsingLocal
|
||||
} from "../background";
|
||||
import { getNameInfo } from "../backgroundFunctions/encryption";
|
||||
import { showSaveFilePicker } from "../components/Apps/useQortalMessageListener";
|
||||
import { QORT_DECIMALS } from "../constants/constants";
|
||||
import Base58 from "../deps/Base58";
|
||||
import {
|
||||
@ -223,12 +224,12 @@ function getFileFromContentScript(fileId) {
|
||||
}
|
||||
|
||||
|
||||
function sendToSaveFilePicker(data) {
|
||||
window.postMessage({
|
||||
action: "SHOW_SAVE_FILE_PICKER",
|
||||
payload: data,
|
||||
}, "*");
|
||||
}
|
||||
// function sendToSaveFilePicker(data) {
|
||||
// window.postMessage({
|
||||
// action: "SHOW_SAVE_FILE_PICKER",
|
||||
// payload: data,
|
||||
// }, "*");
|
||||
// }
|
||||
|
||||
|
||||
const responseResolvers = new Map();
|
||||
@ -1149,9 +1150,9 @@ export const joinGroup = async (data, isFromExtension) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const saveFile = async (data, sender, isFromExtension) => {
|
||||
export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
|
||||
try {
|
||||
const requiredFields = ["filename", "fileId"];
|
||||
const requiredFields = ['filename', 'blob']
|
||||
const missingFields: string[] = [];
|
||||
requiredFields.forEach((field) => {
|
||||
if (!data[field]) {
|
||||
@ -1194,15 +1195,20 @@ export const saveFile = async (data, sender, isFromExtension) => {
|
||||
},
|
||||
};
|
||||
}
|
||||
sendToSaveFilePicker(
|
||||
{
|
||||
filename,
|
||||
mimeType,
|
||||
blob,
|
||||
fileId,
|
||||
fileHandleOptions,
|
||||
}
|
||||
);
|
||||
|
||||
showSaveFilePicker( {
|
||||
filename,
|
||||
mimeType,
|
||||
blob
|
||||
}, snackMethods)
|
||||
// sendToSaveFilePicker(
|
||||
// {
|
||||
// filename,
|
||||
// mimeType,
|
||||
// blob,
|
||||
// fileId
|
||||
// }
|
||||
// );
|
||||
return true;
|
||||
} else {
|
||||
throw new Error("User declined to save file");
|
||||
|
@ -12,10 +12,13 @@ export const mimeToExtensionMap = {
|
||||
"application/vnd.oasis.opendocument.presentation": ".odp",
|
||||
"text/plain": ".txt",
|
||||
"text/csv": ".csv",
|
||||
"text/html": ".html",
|
||||
"application/xhtml+xml": ".xhtml",
|
||||
"application/xml": ".xml",
|
||||
"application/json": ".json",
|
||||
"application/rtf": ".rtf",
|
||||
"application/vnd.apple.pages": ".pages",
|
||||
"application/vnd.google-apps.document": ".gdoc",
|
||||
"application/vnd.google-apps.spreadsheet": ".gsheet",
|
||||
"application/vnd.google-apps.presentation": ".gslides",
|
||||
|
||||
// Images
|
||||
"image/jpeg": ".jpg",
|
||||
@ -25,6 +28,11 @@ export const mimeToExtensionMap = {
|
||||
"image/svg+xml": ".svg",
|
||||
"image/tiff": ".tif",
|
||||
"image/bmp": ".bmp",
|
||||
"image/x-icon": ".ico",
|
||||
"image/heic": ".heic",
|
||||
"image/heif": ".heif",
|
||||
"image/apng": ".apng",
|
||||
"image/avif": ".avif",
|
||||
|
||||
// Audio
|
||||
"audio/mpeg": ".mp3",
|
||||
@ -32,6 +40,11 @@ export const mimeToExtensionMap = {
|
||||
"audio/wav": ".wav",
|
||||
"audio/webm": ".weba",
|
||||
"audio/aac": ".aac",
|
||||
"audio/flac": ".flac",
|
||||
"audio/x-m4a": ".m4a",
|
||||
"audio/x-ms-wma": ".wma",
|
||||
"audio/midi": ".midi",
|
||||
"audio/x-midi": ".mid",
|
||||
|
||||
// Video
|
||||
"video/mp4": ".mp4",
|
||||
@ -45,6 +58,7 @@ export const mimeToExtensionMap = {
|
||||
"video/3gpp2": ".3g2",
|
||||
"video/x-matroska": ".mkv",
|
||||
"video/x-flv": ".flv",
|
||||
"video/x-ms-asf": ".asf",
|
||||
|
||||
// Archives
|
||||
"application/zip": ".zip",
|
||||
@ -53,4 +67,57 @@ export const mimeToExtensionMap = {
|
||||
"application/x-7z-compressed": ".7z",
|
||||
"application/x-gzip": ".gz",
|
||||
"application/x-bzip2": ".bz2",
|
||||
}
|
||||
"application/x-apple-diskimage": ".dmg",
|
||||
"application/vnd.android.package-archive": ".apk",
|
||||
"application/x-iso9660-image": ".iso",
|
||||
|
||||
// Code Files
|
||||
"text/javascript": ".js",
|
||||
"text/css": ".css",
|
||||
"text/html": ".html",
|
||||
"application/json": ".json",
|
||||
"text/xml": ".xml",
|
||||
"application/x-sh": ".sh",
|
||||
"application/x-csh": ".csh",
|
||||
"text/x-python": ".py",
|
||||
"text/x-java-source": ".java",
|
||||
"application/java-archive": ".jar",
|
||||
"application/vnd.microsoft.portable-executable": ".exe",
|
||||
"application/x-msdownload": ".msi",
|
||||
"text/x-c": ".c",
|
||||
"text/x-c++": ".cpp",
|
||||
"text/x-go": ".go",
|
||||
"application/x-perl": ".pl",
|
||||
"text/x-php": ".php",
|
||||
"text/x-ruby": ".rb",
|
||||
"text/x-sql": ".sql",
|
||||
"application/x-httpd-php": ".php",
|
||||
"application/x-python-code": ".pyc",
|
||||
|
||||
// ROM Files
|
||||
"application/x-nintendo-nes-rom": ".nes",
|
||||
"application/x-snes-rom": ".smc",
|
||||
"application/x-gameboy-rom": ".gb",
|
||||
"application/x-gameboy-advance-rom": ".gba",
|
||||
"application/x-n64-rom": ".n64",
|
||||
"application/x-sega-genesis-rom": ".gen",
|
||||
"application/x-sega-master-system-rom": ".sms",
|
||||
"application/x-psx-rom": ".iso", // PlayStation ROMs
|
||||
"application/x-bios-rom": ".rom",
|
||||
"application/x-flash-rom": ".bin",
|
||||
"application/x-eeprom": ".eep",
|
||||
"application/x-c64-rom": ".prg",
|
||||
|
||||
// Miscellaneous
|
||||
"application/octet-stream": ".bin", // General binary files
|
||||
"application/x-shockwave-flash": ".swf",
|
||||
"application/x-silverlight-app": ".xap",
|
||||
"application/x-ms-shortcut": ".lnk",
|
||||
"application/vnd.ms-fontobject": ".eot",
|
||||
"font/woff": ".woff",
|
||||
"font/woff2": ".woff2",
|
||||
"font/ttf": ".ttf",
|
||||
"font/otf": ".otf",
|
||||
"application/vnd.visio": ".vsd",
|
||||
"application/vnd.ms-project": ".mpp",
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user