web: improve app startup

This commit is contained in:
Abdullah Atta
2024-11-12 14:09:04 +05:00
committed by Abdullah Atta
parent ea9b10ff23
commit 1f3e7574f5
12 changed files with 1308 additions and 738 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -37,13 +37,14 @@ import {
ImperativePanelHandle
} from "react-resizable-panels";
import GlobalMenuWrapper from "./components/global-menu-wrapper";
import AppEffects from "./app-effects";
new WebExtensionRelay();
// const GlobalMenuWrapper = React.lazy(
// () => import("./components/global-menu-wrapper")
// );
const AppEffects = React.lazy(() => import("./app-effects"));
// const AppEffects = React.lazy(() => import("./app-effects"));
const MobileAppEffects = React.lazy(() => import("./app-effects.mobile"));
const HashRouter = React.lazy(() => import("./components/hash-router"));
@@ -51,6 +52,7 @@ function App() {
const isMobile = useMobile();
const [show, setShow] = useState(true);
const isFocusMode = useStore((store) => store.isFocusMode);
console.timeEnd("loading app");
return (
<>
@@ -58,7 +60,6 @@ function App() {
<div id="menu-wrapper">
<GlobalMenuWrapper />
</div>
<AppEffects setShow={setShow} />
{isMobile && (
<MobileAppEffects
sliderId="slider"
@@ -67,6 +68,7 @@ function App() {
/>
)}
</Suspense>
<AppEffects setShow={setShow} />
<Flex
id="app"
@@ -126,7 +128,7 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
useEffect(() => {
const size = navPane.current?.getSize();
// Toggle `isNarrow` to true if panel size isn't set to narrow by user
// Toggle `isNarrow` to true if panel size isn't set to narrow by user
setIsNarrow((size && size <= 5) || isTablet);
}, [isTablet]);
@@ -224,7 +226,9 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
bg: "background"
}}
>
{<HashRouter />}
<Suspense fallback={<EditorLoader />}>
<HashRouter />
</Suspense>
</Flex>
</Panel>
</PanelGroup>

View File

@@ -19,10 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { getCurrentHash, getCurrentPath, makeURL } from "./navigation";
import Config from "./utils/config";
// import { initializeLogger, logger } from "./utils/logger";
import type { AuthProps } from "./views/auth";
import { initializeFeatureChecks } from "./utils/feature-check";
import { initializeLogger } from "./utils/logger";
type Route<TProps = null> = {
component: () => Promise<{
@@ -146,12 +145,14 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
export async function init() {
await initializeFeatureChecks();
await import("./utils/logger").then(({ initializeLogger }) =>
initializeLogger()
);
const { path, route } = getRoute();
return { ...route, path };
const [{ default: Component }] = await Promise.all([
route.component(),
initializeLogger()
]);
return { Component, path, props: route.props };
}
function shouldSkipInitiation() {

View File

@@ -29,6 +29,9 @@ import {
import { useKeyStore } from "../../interfaces/key-store";
import { isFeatureSupported } from "../../utils/feature-check";
import { strings } from "@notesnook/intl";
import { db } from "../../common/db";
import { createDialect } from "../../common/sqlite";
import { getDeviceInfo } from "../../utils/platform";
export function GlobalErrorHandler(props: PropsWithChildren) {
const { showBoundary } = useErrorBoundary();
@@ -164,9 +167,6 @@ export function ErrorComponent({ error, resetErrorBoundary }: FallbackProps) {
variant="secondary"
sx={{ alignSelf: "start", px: 30, mt: 1 }}
onClick={async () => {
const { getDeviceInfo } = await import(
"../../dialogs/issue-dialog"
);
const mailto = new URL("mailto:support@streetwriters.co");
mailto.searchParams.set(
"body",
@@ -223,7 +223,6 @@ function getErrorHelp(props: FallbackProps) {
explanation: strings.searchIndexCorrupt(),
action: strings.searchIndexCorruptFix(),
fix: async () => {
const { db } = await import("../../common/db");
await db.lookup.rebuild();
resetErrorBoundary();
}
@@ -240,7 +239,6 @@ function errorToString(error: unknown) {
}
async function resetDatabase() {
const { createDialect } = await import("../../common/sqlite");
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
await useKeyStore.getState().clear();
const dialect = createDialect({

View File

@@ -18,12 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Flex, Link, Text } from "@theme-ui/components";
import { appVersion } from "../utils/version";
import Field from "../components/field";
import Dialog from "../components/dialog";
import platform from "platform";
import { useState } from "react";
import { isUserPremium } from "../hooks/use-is-user-premium";
import { writeText } from "clipboard-polyfill";
import { store as userstore } from "../stores/user-store";
@@ -32,6 +29,8 @@ import { Debug } from "@notesnook/core";
import { ConfirmDialog } from "./confirm";
import { BaseDialogProps, DialogManager } from "../common/dialog-manager";
import { strings } from "@notesnook/intl";
import { getDeviceInfo } from "../utils/platform";
import { isUserPremium } from "../hooks/use-is-user-premium";
const PLACEHOLDERS = {
title: strings.issueTitlePlaceholder(),
@@ -136,7 +135,7 @@ export const IssueDialog = DialogManager.register(function IssueDialog(
/>
</Text>
<Text variant="subBody" mt={1}>
{getDeviceInfo()
{getDeviceInfo([`Pro: ${isUserPremium()}`])
.split("\n")
.map((t) => (
<>
@@ -161,19 +160,10 @@ function showIssueReportedDialog({ url }: { url: string }) {
});
}
export function getDeviceInfo() {
const version = appVersion.formatted;
const os = platform.os;
const browser = `${platform.name} ${platform.version}`;
return `App version: ${version}
OS: ${os}
Browser: ${browser}
Pro: ${isUserPremium()}`;
}
const BODY_TEMPLATE = (body: string) => {
const info = `**Device information:**\n${getDeviceInfo()}`;
const info = `**Device information:**\n${getDeviceInfo([
`Pro: ${isUserPremium()}`
])}`;
if (!body) return info;
return `${body}\n\n${info}`;
};

View File

@@ -19,12 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { initializeDatabase } from "../common/db";
const memory = {
isDatabaseLoaded: false
};
export async function loadDatabase(persistence: "db" | "memory" = "db") {
if (memory.isDatabaseLoaded) return;
await initializeDatabase(persistence);
memory.isDatabaseLoaded = true;
let isDatabaseLoaded: false | Promise<any> = false;
export function loadDatabase(persistence: "db" | "memory" = "db") {
isDatabaseLoaded = isDatabaseLoaded || initializeDatabase(persistence);
return isDatabaseLoaded;
}

View File

@@ -20,11 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useEffect, useState } from "react";
import config from "../utils/config";
export function usePersistentState<T>(key: string, def: T) {
const defState = config.get<T>(key, def);
export function usePersistentState<T>(key: string | undefined, def: T) {
const defState = key ? config.get<T>(key, def) : def;
const [value, setValue] = useState(defState);
useEffect(() => {
if (!key) return;
config.set<T>(key, value);
}, [key, value]);

View File

@@ -176,6 +176,16 @@
<svg style="height: 120px">
<use href="#themed-logo" />
</svg>
<span
style="
margin-top: 10px;
font-family: monospace;
color: var(--paragraph);
font-size: 0.8rem;
"
>
Decrypting your notes
</span>
</div>
<div id="dialogContainer"></div>
<div id="floatingViewContainer"></div>

View File

@@ -28,17 +28,6 @@ import Config from "./utils/config";
import { setI18nGlobal, Messages } from "@notesnook/intl";
import { i18n } from "@lingui/core";
const locale = import.meta.env.DEV
? import("@notesnook/intl/locales/$pseudo-LOCALE.json")
: import("@notesnook/intl/locales/$en.json");
locale.then(({ default: locale }) => {
i18n.load({
en: locale.messages as unknown as Messages
});
i18n.activate("en");
});
setI18nGlobal(i18n);
const colorScheme = JSON.parse(
window.localStorage.getItem("colorScheme") || '"light"'
);
@@ -55,6 +44,21 @@ if (theme) {
if (stylesheet) stylesheet.innerHTML = css;
} else stylesheet?.remove();
const locale = import.meta.env.DEV
? import("@notesnook/intl/locales/$pseudo-LOCALE.json")
: import("@notesnook/intl/locales/$en.json");
locale.then(({ default: locale }) => {
i18n.load({
en: locale.messages as unknown as Messages
});
i18n.activate("en");
import("./root").then(({ startApp }) => {
startApp();
});
});
setI18nGlobal(i18n);
if (!IS_DESKTOP_APP && !IS_TESTING) {
// logger.info("Initializing service worker...");
@@ -76,7 +80,3 @@ if (!IS_DESKTOP_APP && !IS_TESTING) {
// window.addEventListener("beforeinstallprompt", () => showInstallNotice());
}
import("./root").then(({ startApp }) => {
startApp();
});

View File

@@ -25,12 +25,14 @@ import {
ErrorComponent,
GlobalErrorHandler
} from "./components/error-boundary";
import { TitleBar } from "./components/title-bar";
import { desktop } from "./common/desktop-bridge";
import { useKeyStore } from "./interfaces/key-store";
import Config from "./utils/config";
import { usePromise } from "@notesnook/common";
import { AuthProps } from "./views/auth";
import { loadDatabase } from "./hooks/use-database";
import AppLock from "./views/app-lock";
import { Text } from "@theme-ui/components";
export async function startApp() {
const rootElement = document.getElementById("root");
@@ -43,16 +45,24 @@ export async function startApp() {
.query()
?.then((s) => s.nativeTitlebar));
const TitleBar = window.hasNativeTitlebar
? () => <></>
: await import("./components/title-bar").then((m) => m.TitleBar);
try {
const { component, props, path } = await init();
const { Component, props, path } = await init();
await useKeyStore.getState().init();
const { default: AppLock } = await import("./views/app-lock");
loadDatabase(
path !== "/sessionexpired" || Config.get("sessionExpired", false)
? "db"
: "memory"
);
root.render(
<>
{hasNativeTitlebar ? null : <TitleBar />}
<TitleBar />
<ErrorBoundary>
<GlobalErrorHandler>
<BaseThemeProvider
@@ -61,7 +71,7 @@ export async function startApp() {
>
<AppLock>
<RouteWrapper
component={component}
Component={Component}
path={path}
routeProps={props}
/>
@@ -75,7 +85,7 @@ export async function startApp() {
console.error(e);
root.render(
<>
{hasNativeTitlebar ? null : <TitleBar />}
<TitleBar />
<ErrorComponent
error={e}
resetErrorBoundary={() => window.location.reload()}
@@ -86,22 +96,18 @@ export async function startApp() {
}
function RouteWrapper(props: {
component: () => Promise<{
default: (props: AuthProps) => JSX.Element;
}>;
Component: (props: AuthProps) => JSX.Element;
path: Routes;
routeProps: AuthProps | null;
}) {
const { component, path, routeProps } = props;
const { Component, path, routeProps } = props;
const result = usePromise(async () => {
const { loadDatabase } = await import("./hooks/use-database");
await loadDatabase(
path !== "/sessionexpired" || Config.get("sessionExpired", false)
? "db"
: "memory"
);
return (await component()).default;
}, [component, path]);
}, [path]);
if (result.status === "rejected") {
throw result.reason instanceof Error
@@ -124,12 +130,22 @@ function RouteWrapper(props: {
alignItems: "center"
}}
>
<svg style={{ height: 120 }}>
<svg
style={{
height: 120,
transform: "scale(1)",
animation: "pulse 2s infinite",
marginBottom: 10
}}
>
<use href="#themed-logo" />
</svg>
<Text variant="body" sx={{ fontFamily: "monospace" }}>
Decrypting your notes
</Text>
</div>
);
return <result.value route={routeProps?.route || "login:email"} />;
return <Component route={routeProps?.route || "login:email"} />;
}
if (import.meta.hot) import.meta.hot.accept();

View File

@@ -17,6 +17,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import platform from "platform";
import { appVersion } from "../utils/version";
export function getPlatform() {
if (window.os) return window.os();
@@ -131,3 +134,14 @@ export function isMac() {
export function isMacStoreApp() {
return window.os ? window.os() === "mas" : false;
}
export function getDeviceInfo(extras: string[] = []) {
const version = appVersion.formatted;
const os = platform.os;
const browser = `${platform.name} ${platform.version}`;
return `App version: ${version}
OS: ${os}
Browser: ${browser}
${extras.join("\n")}`;
}

View File

@@ -92,7 +92,10 @@ export default defineConfig({
"@mdi/js",
"@mdi/react",
"@emotion/react",
"katex"
"katex",
"react-modal",
"dayjs",
"@streetwriters/kysely"
],
alias: [