feat: PWA prompt

This commit is contained in:
zerosoul
2022-05-11 22:25:07 +08:00
parent 5728cae112
commit 360575bc08
6 changed files with 157 additions and 8 deletions
+58
View File
@@ -0,0 +1,58 @@
// import React from "react";
import styled from "styled-components";
import Modal from "../Modal";
import Button from "../../component/styled/Button";
const Styled = styled.div`
margin-top: 15px;
pointer-events: all;
width: 300px;
padding: 16px 32px;
border-radius: 6px;
background: #fff;
box-shadow: 0px 25px 50px rgba(31, 41, 55, 0.25);
display: flex;
flex-direction: column;
gap: 12px;
.tip {
line-height: 1.4;
display: flex;
flex-direction: column;
gap: 5px;
color: #333;
.title {
font-size: 18px;
font-weight: bold;
}
.desc {
font-size: 12px;
color: #666;
}
}
.btns {
width: 100%;
display: flex;
justify-content: flex-end;
gap: 12px;
}
`;
export default function Prompt({ handleInstall, closePrompt }) {
return (
<Modal mask={false}>
<Styled>
<div className="tip">
<h2 className="title">Add this Web APP</h2>
<p className="desc">Add to your PC and open like native APP</p>
</div>
<div className="btns">
<Button className="main small" onClick={handleInstall}>
Install
</Button>
<Button className="ghost small" onClick={closePrompt}>
Cancel
</Button>
</div>
</Styled>
</Modal>
);
}
+63
View File
@@ -0,0 +1,63 @@
import { useEffect, useState, useRef } from "react";
// import { useGetServerQuery } from "../../../app/services/server";
// import manifest from "./manifest.json";
import Prompt from "./Prompt";
export default function Manifest() {
const deferredPromptRef = useRef(null);
const [popup, setPopup] = useState(false);
// const { data, isSuccess } = useGetServerQuery();
useEffect(() => {
const handleInstallPromotion = (e) => {
// Prevent the mini-infobar from appearing on mobile
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPromptRef.current = e;
// Update UI notify the user they can install the PWA
setPopup(true);
// Optionally, send analytics event that PWA install promo was shown.
console.log(`'beforeinstallprompt' event was fired.`);
};
const handleInstalled = () => {
deferredPromptRef.current = null;
setPopup(false);
};
// if (isSuccess && data) {
// console.log("server", data);
// manifest.name = `${data.name}'s Chat`;
// // const stringManifest = JSON.stringify(manifest);
// // const blob = new Blob([stringManifest], { type: "application/json" });
// // const manifestURL = URL.createObjectURL(blob);
// let content = encodeURIComponent(JSON.stringify(manifest));
// let manifestURL = "data:application/manifest+json," + content;
// const manifestEle = document.querySelector("#my-manifest-placeholder");
// if (manifestEle) {
// manifestEle.setAttribute("href", manifestURL);
// }
// }
window.addEventListener("beforeinstallprompt", handleInstallPromotion);
window.addEventListener("appinstalled", handleInstalled);
return () => {
window.removeEventListener("beforeinstallprompt", handleInstallPromotion);
window.removeEventListener("appinstalled", handleInstalled);
};
}, []);
const handleInstall = async () => {
// Hide the app provided install promotion
setPopup(false);
if (!deferredPromptRef.current) return;
// Show the install prompt
deferredPromptRef.current.prompt();
// Wait for the user to respond to the prompt
const { outcome } = await deferredPromptRef.current.userChoice;
// Optionally, send analytics event with outcome of user choice
console.log(`User response to the install prompt: ${outcome}`);
// We've used the prompt, and can't use it again, throw it away
deferredPromptRef.current = null;
};
const handleClose = async () => {
setPopup(false);
};
if (!popup) return null;
return <Prompt handleInstall={handleInstall} closePrompt={handleClose} />;
}
@@ -0,0 +1,20 @@
{
"name": "RustChat",
"short_name": "Your private chat APP",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
+5 -2
View File
@@ -1,11 +1,14 @@
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
export default function Modal({ id = "root-modal", children }) {
export default function Modal({ id = "root-modal", mask = true, children }) {
const [wrapper, setWrapper] = useState(null);
// let eleRef = useRef(null);
useEffect(() => {
const modalRoot = document.getElementById(id);
if (mask) {
modalRoot.classList.add("mask");
}
const wrapper = document.createElement("div");
wrapper.classList.add("wrapper");
modalRoot.appendChild(wrapper);
@@ -13,7 +16,7 @@ export default function Modal({ id = "root-modal", children }) {
return () => {
modalRoot.removeChild(wrapper);
};
}, [id]);
}, [id, mask]);
if (!wrapper) return null;
console.log("create portal");
return createPortal(children, wrapper);
+2 -2
View File
@@ -23,6 +23,7 @@ import SettingPage from "./setting";
import SettingChannelPage from "./settingChannel";
import toast from "react-hot-toast";
import ResourceManagement from "./resources";
import Manifest from "../common/component/Manifest";
const PageRoutes = () => {
const {
@@ -128,10 +129,9 @@ export default function ReduxRoutes() {
// };
return (
<Provider store={store}>
{/* <PersistGate loading={null} persistor={persistStore(store)}> */}
<Manifest />
<Meta />
<PageRoutes />
{/* </PersistGate> */}
</Provider>
);
}