updated pwa

This commit is contained in:
PhilReact 2025-03-05 18:39:03 +02:00
parent 299a42ca99
commit 2c006c12f9
5 changed files with 110 additions and 18 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" /> <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
<title>Qortal Go</title> <title>Qortal Go</title>

View File

@ -147,6 +147,7 @@ import { useBlockedAddresses } from "./components/Chat/useBlockUsers";
import { UserLookup } from "./components/UserLookup.tsx/UserLookup"; import { UserLookup } from "./components/UserLookup.tsx/UserLookup";
import { RegisterName } from "./components/RegisterName"; import { RegisterName } from "./components/RegisterName";
import { BuyQortInformation } from "./components/BuyQortInformation"; import { BuyQortInformation } from "./components/BuyQortInformation";
import { InstallPWA } from "./components/InstallPWA";
type extStates = type extStates =
@ -1839,6 +1840,13 @@ function App() {
backgroundRepeat: desktopViewMode === "apps" && "no-repeat", backgroundRepeat: desktopViewMode === "apps" && "no-repeat",
}} }}
> >
<div style={{
display: !isNative && extState === "not-authenticated" ? 'block' : 'none'
}}>
<InstallPWA />
</div>
<GlobalContext.Provider value={{ <GlobalContext.Provider value={{
showTutorial, showTutorial,
openTutorialModal, openTutorialModal,

View File

@ -114,22 +114,31 @@ import { LocalNotifications } from '@capacitor/local-notifications';
import { executeEvent } from "./utils/events"; import { executeEvent } from "./utils/events";
import TradeBotRespondRequest from "./transactions/TradeBotRespondRequest"; import TradeBotRespondRequest from "./transactions/TradeBotRespondRequest";
export const isNative = Capacitor.isNativePlatform();
const uid = new ShortUniqueId({ length: 9, dictionary: 'number' }); const uid = new ShortUniqueId({ length: 9, dictionary: 'number' });
const generateId = ()=> { const generateId = ()=> {
return parseInt(uid.rnd()) return parseInt(uid.rnd())
} }
LocalNotifications.requestPermissions().then(permission => {
if (permission.display === 'granted') {
console.log("Notifications enabled");
}
}).catch((error)=> console.error(error));
FilePicker.requestPermissions().then(permission => { if(isNative){
if (permission?.publicStorage === 'granted') {
console.log("File access permission granted"); LocalNotifications.requestPermissions().then(permission => {
} if (permission.display === 'granted') {
}).catch((error)=> console.error(error));; console.log("Notifications enabled");
}
}).catch((error)=> console.error(error));
FilePicker.requestPermissions().then(permission => {
if (permission?.publicStorage === 'granted') {
console.log("File access permission granted");
}
}).catch((error)=> console.error(error));
}
export let groupSecretkeys = {} export let groupSecretkeys = {}
@ -435,7 +444,6 @@ export async function performPowTaskWeb(chatBytes, 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 = isNative ? await NativePOW.computeProofOfWork({ chatBytes, difficulty }) : await performPowTaskWeb(chatBytes, difficulty); const result = isNative ? await NativePOW.computeProofOfWork({ chatBytes, difficulty }) : await performPowTaskWeb(chatBytes, difficulty);
return {nonce: result.nonce, chatBytesArray} return {nonce: result.nonce, chatBytesArray}
@ -3375,7 +3383,9 @@ export const checkThreads = async (bringBack) => {
} }
}; };
// Configure Background Fetch if(isNative){
// Configure Background Fetch
BackgroundFetch.configure({ BackgroundFetch.configure({
minimumFetchInterval: 15, // Minimum 15-minute interval minimumFetchInterval: 15, // Minimum 15-minute interval
enableHeadless: true, // Enable headless mode for Android enableHeadless: true, // Enable headless mode for Android
@ -3400,7 +3410,9 @@ BackgroundFetch.configure({
BackgroundFetch.finish(taskId); BackgroundFetch.finish(taskId);
}); });
}
if(isNative){
LocalNotifications.addListener('localNotificationActionPerformed', async (event) => { LocalNotifications.addListener('localNotificationActionPerformed', async (event) => {
@ -3430,6 +3442,9 @@ LocalNotifications.addListener('localNotificationActionPerformed', async (event)
} }
}); });
}
const initializeBackButton = () => { const initializeBackButton = () => {
@ -3443,5 +3458,6 @@ const initializeBackButton = () => {
}; };
// Call this function on app startup if(isNative){
initializeBackButton(); initializeBackButton();
}

View File

@ -0,0 +1,64 @@
import { Button } from '@mui/material';
import { useEffect, useState } from 'react';
export const InstallPWA = () => {
const [deferredPrompt, setDeferredPrompt] = useState(null);
const [showInstallButton, setShowInstallButton] = useState(false);
const [isStandalone, setIsStandalone] = useState(false);
useEffect(() => {
// Check if app is already installed
if (window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone) {
setIsStandalone(true);
return;
}
// Restore install prompt if previously stored
const wasPromptAvailable = localStorage.getItem("pwaPromptAvailable");
if (wasPromptAvailable === "true") {
setShowInstallButton(true);
}
// Listen for beforeinstallprompt event
const handleBeforeInstallPrompt = (e) => {
e.preventDefault(); // Prevent automatic prompt
setDeferredPrompt(e);
setShowInstallButton(true);
localStorage.setItem("pwaPromptAvailable", "true"); // Remember install prompt was available
};
window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
return () => {
window.removeEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
};
}, []);
const handleInstallClick = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === "accepted") {
console.log("User accepted the install prompt");
localStorage.removeItem("pwaPromptAvailable"); // Remove stored prompt
setShowInstallButton(false);
} else {
console.log("User dismissed the install prompt");
setShowInstallButton(true); // Keep showing button
}
setDeferredPrompt(null);
});
}
};
return (
<div>
{!isStandalone && showInstallButton && (
<Button size="small" sx={{
position: 'fixed',
top: '10px',
left: '10px'
}} variant="contained" onClick={handleInstallClick}>Install App</Button>
)}
</div>
);
};

View File

@ -12,15 +12,17 @@ export default defineConfig({
assetsInclude: ['**/*.wasm'], assetsInclude: ['**/*.wasm'],
plugins: [react(), wasm(), topLevelAwait(), VitePWA({ plugins: [react(), wasm(), topLevelAwait(), VitePWA({
registerType: 'prompt', registerType: "autoUpdate",
manifest: { manifest: {
name: 'Qortal Go', name: 'Qortal Go',
short_name: 'Go', short_name: 'Go',
description: 'Your easy access to the Qortal blockchain', description: 'Your easy access to the Qortal blockchain',
start_url: '/', start_url: '/',
display: 'standalone', display: 'standalone',
theme_color: '#ffffff',
background_color: '#ffffff', theme_color: '#1f2023',
background_color: '#1f2023',
icons: [ icons: [
{ {
src: '/qortal192.png', src: '/qortal192.png',
@ -37,6 +39,8 @@ export default defineConfig({
workbox: { workbox: {
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB limit maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB limit
disableDevLogs: true, // Suppresses logs in development disableDevLogs: true, // Suppresses logs in development
skipWaiting: true, // Forces the new version to activate immediately
clientsClaim: true // Makes the new service worker take control
}, },
})], })],
build: { build: {