feat: show custom pwa install notice

This commit is contained in:
thecodrr
2022-01-05 19:23:33 +05:00
parent e0fd39aaf8
commit ded47d3827
8 changed files with 232 additions and 42 deletions

View File

@@ -11,7 +11,7 @@
"@mdi/react": "^1.4.0",
"@notesnook/desktop": "file:desktop",
"@rebass/forms": "^4.0.6",
"@streetwritersco/tinymce-plugins": "^1.5.3",
"@streetwritersco/tinymce-plugins": "^1.5.5",
"@tinymce/tinymce-react": "^3.13.0",
"async-mutex": "^0.3.2",
"axios": "^0.21.4",

View File

@@ -260,6 +260,7 @@
</svg>
</div>
<div id="dialogContainer"></div>
<div id="floatingViewContainer"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

View File

@@ -1,23 +0,0 @@
import React, { useMemo } from "react";
import { Flex, Text } from "rebass";
import { getPlatform } from "../../utils/platform";
function Banner() {
const link = useMemo(() => {
const os = getPlatform();
if (os === "Android")
return "https://play.google.com/store/apps/details?id=com.streetwriters.notesnook";
if (os === "iOS")
return "https://apps.apple.com/pk/app/notesnook-take-private-notes/id1544027013";
return null;
}, []);
if (link === null) return null;
return (
<Flex alignItems="center" justifyContent="center" bg="primary" py={1}>
<Text color="static" textAlign="center" fontSize="title">
Use our <a href={link}>mobile app</a> for a better experience.
</Text>
</Flex>
);
}
export default Banner;

View File

@@ -0,0 +1,32 @@
import { Button, Flex } from "rebass";
import { useOpenContextMenu } from "../../utils/useContextMenu";
import { ChevronDown } from "../icons";
export default function DropdownButton({ title, options }) {
const openContextMenu = useOpenContextMenu();
if (!options || !options.length) return null;
return (
<Flex>
<Button
sx={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
onClick={options[0].onClick}
>
{options[0].title()}
</Button>
{options.length > 1 && (
<Button
px={1}
sx={{
borderBottomLeftRadius: 0,
borderTopLeftRadius: 0,
}}
onClick={(event) =>
openContextMenu(event, options.slice(1), { title })
}
>
<ChevronDown color="static" size={18} />
</Button>
)}
</Flex>
);
}

View File

@@ -0,0 +1,127 @@
import ReactDOM from "react-dom";
import { Box, Button, Flex, Text } from "rebass";
import { closeOpenedDialog } from "../../common/dialog-controller";
import Config from "../../utils/config";
import { getDownloadLink, getPlatform } from "../../utils/platform";
import DropdownButton from "../dropdown-button";
import ThemeProvider from "../theme-provider";
const nativeFeatures = [
"Native high-performance encryption",
"Automatic backups",
"Pin notes in notifications drawer",
"Share & append to notes from anywhere",
"Quick note widgets",
"App lock",
];
const platform = getPlatform();
const isMobile = platform === "Android" || platform === "iOS";
const storeName = platform === "Android" ? "Play Store" : "App Store";
function getOptions(onClose) {
return getDownloadLink(platform).map((item) => ({
key: item.type || item.link,
title: () => {
switch (platform) {
case "iOS":
case "Android":
return `Install from ${storeName}`;
default:
return `Download (${item.type})`;
}
},
onClick: () => {
window.open(item.link, "_blank");
onClose();
Config.set("installNotice", false);
},
}));
}
export default function InstallNotice({ onClose }) {
return (
<Flex
flexDirection={"column"}
sx={{
position: "absolute",
top: ["initial", 2],
right: [0, 2],
left: [2, "initial"],
bottom: [2, "initial"],
zIndex: 2,
bg: "background",
borderRadius: "default",
border: "1px solid var(--border)",
width: ["95%", 400],
}}
p={2}
>
<Text variant={"title"}>Install Notesnook</Text>
<Text variant={"body"}>
For a more integrated user experience, try out Notesnook for {platform}.
</Text>
{isMobile && (
<Box
sx={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
columnGap: 1,
rowGap: 1,
mt: 1,
}}
>
{nativeFeatures.map((feature) => (
<Flex
p={1}
sx={{
borderRadius: "default",
border: "1px solid var(--border)",
}}
>
<Text variant="body" ml={1}>
{feature}
</Text>
</Flex>
))}
</Box>
)}
<Flex mt={[4, 1]} alignItems={"center"}>
<DropdownButton title={"Options"} options={getOptions(onClose)} />
<Button
variant={"secondary"}
ml={1}
alignSelf={"start"}
onClick={() => {
onClose();
Config.set("installNotice", false);
}}
>
Don't show again
</Button>
</Flex>
</Flex>
);
}
export function showInstallNotice() {
if (!Config.get("installNotice", true)) return;
const root = document.getElementById("floatingViewContainer");
if (root) {
return new Promise((resolve) => {
const perform = (result) => {
ReactDOM.unmountComponentAtNode(root);
resolve(result);
};
ReactDOM.render(
<ThemeProvider>
<InstallNotice onClose={perform} />
</ThemeProvider>,
root
);
});
}
return Promise.reject("No element with id 'floatingViewContainer'");
}

View File

@@ -1,6 +1,7 @@
import { EVENTS } from "@notesnook/desktop/events";
import { render } from "react-dom";
import { AppEventManager } from "./common/app-events";
import { showInstallNotice } from "./components/installnotice";
import { updateStatus } from "./hooks/use-status";
import { getCurrentHash, getCurrentPath, makeURL } from "./navigation";
import * as serviceWorker from "./serviceWorkerRegistration";
@@ -96,6 +97,7 @@ if (process.env.REACT_APP_PLATFORM !== "desktop") {
});
},
});
window.addEventListener("beforeinstallprompt", () => showInstallNotice());
} else serviceWorker.unregister();
function shouldSkipInitiation() {

View File

@@ -7,20 +7,74 @@ export function getPlatform() {
os = null;
if (macosPlatforms.indexOf(platform) !== -1) {
os = "macOS";
return "macOS";
} else if (iosPlatforms.indexOf(platform) !== -1) {
os = "iOS";
return "iOS";
} else if (windowsPlatforms.indexOf(platform) !== -1) {
os = "Windows";
return "Windows";
} else if (/Android/.test(userAgent)) {
os = "Android";
return "Android";
} else if (!os && /Linux/.test(platform)) {
os = "Linux";
return "Linux";
}
return os;
}
export function getDownloadLink(platform) {
switch (platform) {
case "iOS":
return [
{
link: "https://apps.apple.com/pk/app/notesnook-take-private-notes/id1544027013",
},
];
case "Android":
return [
{
link: "https://play.google.com/store/apps/details?id=com.streetwriters.notesnook",
},
];
case "macOS":
return [
{
type: "x64",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_x64.dmg",
},
{
type: "arm64",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_amd64.dmg",
},
];
case "Windows":
return [
{
type: ".exe",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_x64.exe",
},
];
case "Linux":
return [
{
type: ".AppImage",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_x86_64.AppImage",
},
{
type: ".deb",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_x86_64.deb",
},
{
type: ".rpm",
link: "https://github.com/streetwriters/notesnook/releases/latest/download/notesnook_x86_64.rpm",
},
];
default:
return [
{ link: "https://github.com/streetwriters/notesnook/releases/latest/" },
];
}
}
export function isDesktop() {
return "api" in window;
}

View File

@@ -1,12 +1,7 @@
import { useCallback, useEffect, useState } from "react";
var isOpening = false;
var currentEvent = undefined;
function closeMenu() {
if (isOpening) {
isOpening = false;
return;
}
window.dispatchEvent(
new CustomEvent("globalcontextmenu", { detail: { state: "close" } })
);
@@ -23,7 +18,13 @@ function onKeyDown(event) {
if (event.keyCode === 27) closeMenu();
}
function onWindowClick() {
function onWindowClick(event) {
if (
event === currentEvent.nativeEvent ||
event.target === currentEvent.target
)
return;
closeMenu();
}
@@ -35,9 +36,7 @@ function openMenu(e) {
closeMenu();
return;
}
if (e.type === "click") {
isOpening = true;
}
currentEvent = e;
menu.style.display = "block";
@@ -66,7 +65,6 @@ function openMenu(e) {
window.addEventListener("keydown", onKeyDown);
window.addEventListener("click", onWindowClick);
window.addEventListener("blur", onWindowClick);
isOpening = false;
}
function getPosition(e) {
@@ -119,7 +117,7 @@ function useContextMenu() {
}
export function useOpenContextMenu() {
return useCallback((event, items, data, withClick) => {
return useCallback((event, items, data) => {
window.dispatchEvent(
new CustomEvent("globalcontextmenu", {
detail: {
@@ -127,7 +125,6 @@ export function useOpenContextMenu() {
items,
data,
internalEvent: event,
withClick,
},
})
);