mirror of
https://github.com/Qortal/qortal-mobile.git
synced 2025-04-24 20:07:52 +00:00
added pwa
This commit is contained in:
parent
e3d7d71c52
commit
299a42ca99
2249
package-lock.json
generated
2249
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -88,6 +88,7 @@
|
|||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"tiptap-extension-resize-image": "^1.1.8",
|
"tiptap-extension-resize-image": "^1.1.8",
|
||||||
"ts-key-enum": "^2.0.12",
|
"ts-key-enum": "^2.0.12",
|
||||||
|
"vite-plugin-pwa": "^0.21.1",
|
||||||
"vite-plugin-top-level-await": "^1.4.4",
|
"vite-plugin-top-level-await": "^1.4.4",
|
||||||
"vite-plugin-wasm": "^3.3.0"
|
"vite-plugin-wasm": "^3.3.0"
|
||||||
},
|
},
|
||||||
|
BIN
public/qortal.png
Normal file
BIN
public/qortal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
BIN
public/qortal192.png
Normal file
BIN
public/qortal192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
12
src/App.tsx
12
src/App.tsx
@ -132,7 +132,7 @@ import {
|
|||||||
} from "./atoms/global";
|
} from "./atoms/global";
|
||||||
import { useAppFullScreen } from "./useAppFullscreen";
|
import { useAppFullScreen } from "./useAppFullscreen";
|
||||||
import { NotAuthenticated, manifestData } from "./ExtStates/NotAuthenticated";
|
import { NotAuthenticated, manifestData } from "./ExtStates/NotAuthenticated";
|
||||||
import { openIndexedDB, showSaveFilePicker } from "./components/Apps/useQortalMessageListener";
|
import { isNative, openIndexedDB, showSaveFilePicker } from "./components/Apps/useQortalMessageListener";
|
||||||
import { fileToBase64 } from "./utils/fileReading";
|
import { fileToBase64 } from "./utils/fileReading";
|
||||||
import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
|
import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
|
||||||
import { Wallets } from "./Wallets";
|
import { Wallets } from "./Wallets";
|
||||||
@ -499,7 +499,7 @@ function App() {
|
|||||||
const seedPhrase = generatorRef.current.parsedString
|
const seedPhrase = generatorRef.current.parsedString
|
||||||
saveSeedPhraseToDisk(seedPhrase)
|
saveSeedPhraseToDisk(seedPhrase)
|
||||||
await showInfo({
|
await showInfo({
|
||||||
message: `Your seed phrase was saved to INTERNAL storage, in the document folder. Keep that file secure.`,
|
message: isNative ? `Your seed phrase was downloaded by your browser.. Keep that file secure.` : `Your seed phrase was saved to INTERNAL storage, in the document folder. Keep that file secure.`,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
@ -2705,7 +2705,7 @@ function App() {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Download Wallet
|
Download Account
|
||||||
</TextP>
|
</TextP>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="35px" />
|
<Spacer height="35px" />
|
||||||
@ -2735,10 +2735,10 @@ function App() {
|
|||||||
<CustomButton onClick={async ()=> {
|
<CustomButton onClick={async ()=> {
|
||||||
await saveFileToDiskFunc()
|
await saveFileToDiskFunc()
|
||||||
await showInfo({
|
await showInfo({
|
||||||
message: `Your wallet file was saved to internal storage, in the document folder. Keep that file secure.`,
|
message: isNative ? `Your account file was saved to internal storage, in the document folder. Keep that file secure.` : `Your account file was downloaded by your browser. Keep that file secure.` ,
|
||||||
})
|
})
|
||||||
}}>
|
}}>
|
||||||
Download wallet
|
Download account
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -2972,7 +2972,7 @@ await showInfo({
|
|||||||
await saveFileToDiskFunc();
|
await saveFileToDiskFunc();
|
||||||
returnToMain();
|
returnToMain();
|
||||||
await showInfo({
|
await showInfo({
|
||||||
message: `Your wallet file was saved to internal storage, in the document folder. Keep that file secure.`,
|
message: isNative ? `Your account file was saved to internal storage, in the document folder. Keep that file secure.` : `Your account file was downloaded by your browser. Keep that file secure.`
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -30,6 +30,7 @@ import { crypto } from "./constants/decryptWallet";
|
|||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from "@mui/lab";
|
||||||
import { PasswordField } from "./components";
|
import { PasswordField } from "./components";
|
||||||
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
||||||
|
import { isNative } from "./components/Apps/useQortalMessageListener";
|
||||||
|
|
||||||
const parsefilenameQortal = (filename) => {
|
const parsefilenameQortal = (filename) => {
|
||||||
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
||||||
@ -119,6 +120,8 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
accept: {
|
accept: {
|
||||||
"application/json": [".json"], // Only accept JSON files
|
"application/json": [".json"], // Only accept JSON files
|
||||||
@ -267,7 +270,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography>Your saved wallets</Typography>
|
<Typography>Your saved accounts</Typography>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@ -328,15 +331,25 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
>
|
>
|
||||||
Add seed-phrase
|
Add seed-phrase
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
<CustomButton
|
{isNative ? (
|
||||||
|
<CustomButton
|
||||||
sx={{
|
sx={{
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
}}
|
}}
|
||||||
onClick={handleFilePick}
|
onClick={handleFilePick}
|
||||||
>
|
>
|
||||||
|
|
||||||
Add wallets
|
Add account
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
|
) : (
|
||||||
|
<CustomButton sx={{
|
||||||
|
padding: '10px'
|
||||||
|
}} {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
Add account
|
||||||
|
</CustomButton>
|
||||||
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -13,6 +13,8 @@ import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes";
|
|||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { App as CapacitorApp } from '@capacitor/app';
|
import { App as CapacitorApp } from '@capacitor/app';
|
||||||
import Base58 from "./deps/Base58";
|
import Base58 from "./deps/Base58";
|
||||||
|
import ChatComputePowWorker from './chatComputePow.worker.js?worker';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
base64ToUint8Array,
|
base64ToUint8Array,
|
||||||
decryptSingle,
|
decryptSingle,
|
||||||
@ -33,6 +35,8 @@ import NativePOW from './utils/nativepow'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest";
|
import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest";
|
||||||
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes";
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes";
|
||||||
|
import { Capacitor } from '@capacitor/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addDataPublishesCase,
|
addDataPublishesCase,
|
||||||
addEnteredQmailTimestampCase,
|
addEnteredQmailTimestampCase,
|
||||||
@ -405,11 +409,35 @@ function playNotificationSound() {
|
|||||||
// chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" });
|
// chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// const worker = new ChatComputePowWorker()
|
const worker = new ChatComputePowWorker()
|
||||||
|
|
||||||
|
export async function performPowTaskWeb(chatBytes, difficulty) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
worker.onmessage = (e) => {
|
||||||
|
if (e.data.error) {
|
||||||
|
reject(new Error(e.data.error));
|
||||||
|
} else {
|
||||||
|
resolve(e.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (err) => {
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the task to the worker
|
||||||
|
worker.postMessage({
|
||||||
|
chatBytes,
|
||||||
|
path: `${import.meta.env.BASE_URL}memory-pow.wasm.full`,
|
||||||
|
difficulty,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function performPowTask(chatBytes, difficulty) {
|
export async function performPowTask(chatBytes, difficulty) {
|
||||||
|
const isNative = Capacitor.isNativePlatform();
|
||||||
const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
|
const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
|
||||||
const result = await NativePOW.computeProofOfWork({ chatBytes, difficulty });
|
const result = isNative ? await NativePOW.computeProofOfWork({ chatBytes, difficulty }) : await performPowTaskWeb(chatBytes, difficulty);
|
||||||
return {nonce: result.nonce, chatBytesArray}
|
return {nonce: result.nonce, chatBytesArray}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Sha256 } from 'asmcrypto.js';
|
import { Sha256 } from 'asmcrypto.js';
|
||||||
import wasmInit from './memory-pow.wasm?init';
|
import wasmInit from './memory-pow.wasm?init';
|
||||||
import NativePOW from './utils/nativepow'
|
|
||||||
let compute; // Exported compute function from Wasm
|
let compute; // Exported compute function from Wasm
|
||||||
let memory; // WebAssembly.Memory instance
|
let memory; // WebAssembly.Memory instance
|
||||||
let heap; // Uint8Array view of the memory buffer
|
let heap; // Uint8Array view of the memory buffer
|
||||||
@ -24,7 +24,9 @@ async function loadWasm() {
|
|||||||
|
|
||||||
const wasmModule = await wasmInit(importObject);
|
const wasmModule = await wasmInit(importObject);
|
||||||
compute = wasmModule.exports.compute2;
|
compute = wasmModule.exports.compute2;
|
||||||
|
console.log('Wasm loaded successfully:', compute);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Error loading Wasm:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,24 +60,24 @@ function sbrk(size) {
|
|||||||
|
|
||||||
// Proof-of-Work computation function
|
// Proof-of-Work computation function
|
||||||
async function computePow(chatBytes, difficulty) {
|
async function computePow(chatBytes, difficulty) {
|
||||||
// if (!compute) {
|
if (!compute) {
|
||||||
// throw new Error('WebAssembly module not initialized. Call loadWasm first.');
|
throw new Error('WebAssembly module not initialized. Call loadWasm first.');
|
||||||
// }
|
}
|
||||||
|
|
||||||
// const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
|
const chatBytesArray = Uint8Array.from(Object.values(chatBytes));
|
||||||
// const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
|
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
|
||||||
|
|
||||||
// // Allocate memory for the hash
|
// Allocate memory for the hash
|
||||||
// const hashPtr = sbrk(32);
|
const hashPtr = sbrk(32);
|
||||||
// const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
|
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
|
||||||
// hashAry.set(chatBytesHash);
|
hashAry.set(chatBytesHash);
|
||||||
|
|
||||||
// // Reuse the work buffer if already allocated
|
// Reuse the work buffer if already allocated
|
||||||
// if (!workBufferPtr) {
|
if (!workBufferPtr) {
|
||||||
// workBufferPtr = sbrk(workBufferLength);
|
workBufferPtr = sbrk(workBufferLength);
|
||||||
// }
|
}
|
||||||
const nonce = await NativePOW.computeProofOfWork({ chatBytes, difficulty });
|
|
||||||
(hashPtr, workBufferPtr, workBufferLength, difficulty);
|
const nonce = compute(hashPtr, workBufferPtr, workBufferLength, difficulty);
|
||||||
|
|
||||||
return { nonce, chatBytesArray };
|
return { nonce, chatBytesArray };
|
||||||
}
|
}
|
||||||
@ -86,9 +88,9 @@ self.addEventListener('message', async (e) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize Wasm if not already done
|
// Initialize Wasm if not already done
|
||||||
// if (!compute) {
|
if (!compute) {
|
||||||
// await loadWasm();
|
await loadWasm();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Perform the POW computation
|
// Perform the POW computation
|
||||||
const result = await computePow(chatBytes, difficulty);
|
const result = await computePow(chatBytes, difficulty);
|
||||||
|
@ -7,8 +7,11 @@ import { Browser } from '@capacitor/browser';
|
|||||||
import { saveFile } from '../../qortalRequests/get';
|
import { saveFile } from '../../qortalRequests/get';
|
||||||
import { mimeToExtensionMap } from '../../utils/memeTypes';
|
import { mimeToExtensionMap } from '../../utils/memeTypes';
|
||||||
import { MyContext } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
|
import FileSaver from 'file-saver';
|
||||||
|
|
||||||
|
import { Capacitor } from '@capacitor/core';
|
||||||
|
|
||||||
|
export const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
|
||||||
export const saveFileInChunks = async (
|
export const saveFileInChunks = async (
|
||||||
@ -16,7 +19,7 @@ export const saveFileInChunks = async (
|
|||||||
fileName: string,
|
fileName: string,
|
||||||
chunkSize = 1024 * 1024
|
chunkSize = 1024 * 1024
|
||||||
) => {
|
) => {
|
||||||
try {
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let isFirstChunk = true;
|
let isFirstChunk = true;
|
||||||
|
|
||||||
@ -77,9 +80,7 @@ export const saveFileInChunks = async (
|
|||||||
isFirstChunk = false;
|
isFirstChunk = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -317,9 +318,11 @@ const UIQortalRequests = [
|
|||||||
|
|
||||||
|
|
||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
if(isNative){
|
||||||
await saveFileInChunks(blob, filename)
|
await saveFileInChunks(blob, filename)
|
||||||
|
} else {
|
||||||
|
FileSaver.saveAs(blob, filename)
|
||||||
|
}
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "success",
|
type: "success",
|
||||||
message:
|
message:
|
||||||
|
@ -29,6 +29,7 @@ import SaveIcon from '@mui/icons-material/Save';
|
|||||||
import { useSetRecoilState } from "recoil";
|
import { useSetRecoilState } from "recoil";
|
||||||
import { blobControllerAtom } from "../../atoms/global";
|
import { blobControllerAtom } from "../../atoms/global";
|
||||||
import { decodeIfEncoded } from "../../utils/decode";
|
import { decodeIfEncoded } from "../../utils/decode";
|
||||||
|
import { isNative } from "../Apps/useQortalMessageListener";
|
||||||
|
|
||||||
|
|
||||||
export const AttachmentCard = ({
|
export const AttachmentCard = ({
|
||||||
@ -67,7 +68,7 @@ export const AttachmentCard = ({
|
|||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: "success",
|
||||||
message:
|
message:
|
||||||
"File saved in INTERNAL STORAGE, DOCUMENT folder.",
|
isNative ? "File saved in INTERNAL STORAGE, DOCUMENT folder." : "File downloaded",
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -126,7 +127,7 @@ export const AttachmentCard = ({
|
|||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: "success",
|
||||||
message:
|
message:
|
||||||
"File saved in INTERNAL STORAGE, DOCUMENT folder.",
|
isNative ? "File saved in INTERNAL STORAGE, DOCUMENT folder." : "File downloaded",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -383,7 +383,6 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: "0px 20px",
|
padding: "0px 20px",
|
||||||
|
@ -23,6 +23,7 @@ import { saveToLocalStorage } from "../Apps/AppsNavBar";
|
|||||||
import { decryptData, encryptData } from "../../qortalRequests/get";
|
import { decryptData, encryptData } from "../../qortalRequests/get";
|
||||||
import { saveFileToDiskGeneric } from "../../utils/generateWallet/generateWallet";
|
import { saveFileToDiskGeneric } from "../../utils/generateWallet/generateWallet";
|
||||||
import { base64ToUint8Array, uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
import { base64ToUint8Array, uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
||||||
|
import { isNative } from "../Apps/useQortalMessageListener";
|
||||||
|
|
||||||
export const handleImportClick = async () => {
|
export const handleImportClick = async () => {
|
||||||
const fileInput = document.createElement('input');
|
const fileInput = document.createElement('input');
|
||||||
@ -544,7 +545,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
await saveFileToDiskGeneric(blob, filename)
|
await saveFileToDiskGeneric(blob, filename)
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: "saved in INTERNAL storage under DOCUMENTS",
|
message: isNative ? "saved in INTERNAL storage under DOCUMENTS" : "file saved by your browser",
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
|
|
||||||
|
@ -9,6 +9,11 @@ import * as WORDLISTS from './wordlists';
|
|||||||
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
import FileSaver from 'file-saver';
|
||||||
|
import { Capacitor } from '@capacitor/core';
|
||||||
|
|
||||||
|
const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
|
||||||
export function generateRandomSentence(template = 'adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun', maxWordLength = 0, capitalize = true) {
|
export function generateRandomSentence(template = 'adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun', maxWordLength = 0, capitalize = true) {
|
||||||
const partsOfSpeechMap = {
|
const partsOfSpeechMap = {
|
||||||
@ -89,22 +94,30 @@ export const createAccount = async(generatedSeedPhrase)=> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const saveFileToDisk = async (data: any, qortAddress: string) => {
|
export const saveFileToDisk = async (data: any, qortAddress: string) => {
|
||||||
|
if(isNative){
|
||||||
|
const dataString = JSON.stringify(data);
|
||||||
|
const fileName = `qortal_backup_${qortAddress}_${uid.rnd()}.json`;
|
||||||
|
|
||||||
const dataString = JSON.stringify(data);
|
// Write the file to the Filesystem
|
||||||
const fileName = `qortal_backup_${qortAddress}_${uid.rnd()}.json`;
|
await Filesystem.writeFile({
|
||||||
|
path: fileName,
|
||||||
|
data: dataString,
|
||||||
|
directory: Directory.Documents, // Save in the Documents folder
|
||||||
|
encoding: Encoding.UTF8,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const dataString = JSON.stringify(data);
|
||||||
|
const blob = new Blob([dataString], { type: 'application/json' });
|
||||||
|
const fileName = "qortal_backup_" + qortAddress + ".json";
|
||||||
|
|
||||||
|
await FileSaver.saveAs(blob, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
// Write the file to the Filesystem
|
|
||||||
await Filesystem.writeFile({
|
|
||||||
path: fileName,
|
|
||||||
data: dataString,
|
|
||||||
directory: Directory.Documents, // Save in the Documents folder
|
|
||||||
encoding: Encoding.UTF8,
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveSeedPhraseToDisk = async (data) => {
|
export const saveSeedPhraseToDisk = async (data) => {
|
||||||
|
if(isNative){
|
||||||
const fileName = `qortal_seedphrase_${uid.rnd()}.txt`
|
const fileName = `qortal_seedphrase_${uid.rnd()}.txt`
|
||||||
|
|
||||||
await Filesystem.writeFile({
|
await Filesystem.writeFile({
|
||||||
@ -113,7 +126,12 @@ export const saveSeedPhraseToDisk = async (data) => {
|
|||||||
directory: Directory.Documents, // Save in the Documents folder
|
directory: Directory.Documents, // Save in the Documents folder
|
||||||
encoding: Encoding.UTF8,
|
encoding: Encoding.UTF8,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
|
||||||
|
const fileName = "qortal_seedphrase.txt"
|
||||||
|
|
||||||
|
await FileSaver.saveAs(blob, fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasExtension = (filename) => {
|
const hasExtension = (filename) => {
|
||||||
@ -122,13 +140,25 @@ const hasExtension = (filename) => {
|
|||||||
|
|
||||||
|
|
||||||
export const saveFileToDiskGeneric = async (blob, filename) => {
|
export const saveFileToDiskGeneric = async (blob, filename) => {
|
||||||
const timestamp = new Date()
|
if(isNative){
|
||||||
.toISOString()
|
const timestamp = new Date()
|
||||||
.replace(/:/g, "-"); // Safe timestamp for filenames
|
.toISOString()
|
||||||
|
.replace(/:/g, "-"); // Safe timestamp for filenames
|
||||||
|
|
||||||
const fileExtension = mimeToExtensionMap[blob.type]
|
const fileExtension = mimeToExtensionMap[blob.type]
|
||||||
let fileName = filename || "qortal_file_" + timestamp + "." + fileExtension;
|
let fileName = filename || "qortal_file_" + timestamp + "." + fileExtension;
|
||||||
fileName = hasExtension(fileName) ? fileName : fileName + "." + fileExtension;
|
fileName = hasExtension(fileName) ? fileName : fileName + "." + fileExtension;
|
||||||
await saveFileInChunks(blob, fileName)
|
await saveFileInChunks(blob, fileName)
|
||||||
// await FileSaver.saveAs(blob, fileName);
|
} else {
|
||||||
|
const timestamp = new Date()
|
||||||
|
.toISOString()
|
||||||
|
.replace(/:/g, "-"); // Safe timestamp for filenames
|
||||||
|
|
||||||
|
const fileExtension = mimeToExtensionMap[blob.type]
|
||||||
|
let fileName = filename || "qortal_file_" + timestamp + "." + fileExtension;
|
||||||
|
fileName = hasExtension(fileName) ? fileName : fileName + "." + fileExtension;
|
||||||
|
|
||||||
|
await FileSaver.saveAs(blob, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -6,10 +6,39 @@ import { resolve } from 'path';
|
|||||||
import fixReactVirtualized from 'esbuild-plugin-react-virtualized'
|
import fixReactVirtualized from 'esbuild-plugin-react-virtualized'
|
||||||
import wasm from 'vite-plugin-wasm';
|
import wasm from 'vite-plugin-wasm';
|
||||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||||
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
|
||||||
assetsInclude: ['**/*.wasm'],
|
assetsInclude: ['**/*.wasm'],
|
||||||
plugins: [react(), wasm(), topLevelAwait()],
|
plugins: [react(), wasm(), topLevelAwait(), VitePWA({
|
||||||
|
registerType: 'prompt',
|
||||||
|
manifest: {
|
||||||
|
name: 'Qortal Go',
|
||||||
|
short_name: 'Go',
|
||||||
|
description: 'Your easy access to the Qortal blockchain',
|
||||||
|
start_url: '/',
|
||||||
|
display: 'standalone',
|
||||||
|
theme_color: '#ffffff',
|
||||||
|
background_color: '#ffffff',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: '/qortal192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/qortal.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
workbox: {
|
||||||
|
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB limit
|
||||||
|
disableDevLogs: true, // Suppresses logs in development
|
||||||
|
},
|
||||||
|
})],
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// Specify multiple entry points for Rollup
|
// Specify multiple entry points for Rollup
|
||||||
|
Loading…
x
Reference in New Issue
Block a user