mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
web: convert more code to typescript
This commit is contained in:
@@ -23,7 +23,16 @@ import useSlider from "./hooks/use-slider";
|
||||
import useMobile from "./hooks/use-mobile";
|
||||
import useTablet from "./hooks/use-tablet";
|
||||
|
||||
export default function MobileAppEffects({ sliderId, overlayId, setShow }) {
|
||||
type MobileAppEffectsProps = {
|
||||
sliderId: string;
|
||||
overlayId: string;
|
||||
setShow: (show: boolean) => void;
|
||||
};
|
||||
export default function MobileAppEffects({
|
||||
sliderId,
|
||||
overlayId,
|
||||
setShow
|
||||
}: MobileAppEffectsProps) {
|
||||
const isMobile = useMobile();
|
||||
const isTablet = useTablet();
|
||||
const toggleSideMenu = useStore((store) => store.toggleSideMenu);
|
||||
@@ -40,6 +49,7 @@ export default function MobileAppEffects({ sliderId, overlayId, setShow }) {
|
||||
|
||||
const percent = offset - (position / width) * offset;
|
||||
const overlay = document.getElementById("overlay");
|
||||
if (!overlay) return;
|
||||
if (percent > 0) {
|
||||
overlay.style.opacity = `${percent}%`;
|
||||
overlay.style.pointerEvents = "all";
|
||||
@@ -74,6 +84,7 @@ export default function MobileAppEffects({ sliderId, overlayId, setShow }) {
|
||||
useEffect(() => {
|
||||
if (!overlayId) return;
|
||||
const overlay = document.getElementById(overlayId);
|
||||
if (!overlay) return;
|
||||
overlay.onclick = () => toggleSideMenu(false);
|
||||
return () => {
|
||||
overlay.onclick = null;
|
||||
@@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useEffect } from "react";
|
||||
import { useStore } from "./stores/app-store";
|
||||
import { useStore as useUserStore } from "./stores/user-store";
|
||||
import { useStore as useNotesStore } from "./stores/note-store";
|
||||
import { useStore as useThemeStore } from "./stores/theme-store";
|
||||
import { useStore as useAttachmentStore } from "./stores/attachment-store";
|
||||
import { useStore as useEditorStore } from "./stores/editor-store";
|
||||
@@ -47,11 +46,13 @@ import { interruptedOnboarding } from "./dialogs/onboarding-dialog";
|
||||
import { hashNavigate } from "./navigation";
|
||||
import { desktop } from "./common/desktop-bridge";
|
||||
|
||||
export default function AppEffects({ setShow }) {
|
||||
type AppEffectsProps = {
|
||||
setShow: (show: boolean) => void;
|
||||
};
|
||||
export default function AppEffects({ setShow }: AppEffectsProps) {
|
||||
const refreshNavItems = useStore((store) => store.refreshNavItems);
|
||||
const updateLastSynced = useStore((store) => store.updateLastSynced);
|
||||
const isFocusMode = useStore((store) => store.isFocusMode);
|
||||
const addReminder = useStore((store) => store.addReminder);
|
||||
const initUser = useUserStore((store) => store.init);
|
||||
const initStore = useStore((store) => store.init);
|
||||
const initAttachments = useAttachmentStore((store) => store.init);
|
||||
@@ -68,7 +69,7 @@ export default function AppEffects({ setShow }) {
|
||||
function initializeApp() {
|
||||
const userCheckStatusEvent = EV.subscribe(
|
||||
EVENTS.userCheckStatus,
|
||||
async (type) => {
|
||||
async (type: string) => {
|
||||
if (isUserPremium()) {
|
||||
return { type, result: true };
|
||||
} else {
|
||||
@@ -93,7 +94,7 @@ export default function AppEffects({ setShow }) {
|
||||
showUpgradeReminderDialogs();
|
||||
}
|
||||
await resetReminders();
|
||||
setIsVaultCreated(await db.vault.exists());
|
||||
setIsVaultCreated(await db.vault?.exists());
|
||||
|
||||
await showOnboardingDialog(interruptedOnboarding());
|
||||
await showFeatureDialog("highlights");
|
||||
@@ -111,23 +112,29 @@ export default function AppEffects({ setShow }) {
|
||||
updateLastSynced,
|
||||
refreshNavItems,
|
||||
initUser,
|
||||
initNotes,
|
||||
addReminder,
|
||||
setIsVaultCreated
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const systemTimeInvalidEvent = EV.subscribe(
|
||||
EVENTS.systemTimeInvalid,
|
||||
async ({ serverTime, localTime }) => {
|
||||
await showInvalidSystemTimeDialog({ serverTime, localTime });
|
||||
}
|
||||
);
|
||||
// const systemTimeInvalidEvent = EV.subscribe(
|
||||
// EVENTS.systemTimeInvalid,
|
||||
// async ({ serverTime, localTime }) => {
|
||||
// await showInvalidSystemTimeDialog({ serverTime, localTime });
|
||||
// }
|
||||
// );
|
||||
|
||||
const attachmentsLoadingEvent = EV.subscribe(
|
||||
EVENTS.attachmentsLoading,
|
||||
({ type, total, current }) => {
|
||||
({
|
||||
type,
|
||||
total,
|
||||
current
|
||||
}: {
|
||||
type: ProcessingType;
|
||||
total: number;
|
||||
current: number;
|
||||
}) => {
|
||||
const [key, status] = getProcessingStatusFromType(type);
|
||||
|
||||
if (current === total) {
|
||||
@@ -144,7 +151,15 @@ export default function AppEffects({ setShow }) {
|
||||
|
||||
const progressEvent = AppEventManager.subscribe(
|
||||
AppEvents.UPDATE_ATTACHMENT_PROGRESS,
|
||||
({ type, total, loaded }) => {
|
||||
({
|
||||
type,
|
||||
total,
|
||||
loaded
|
||||
}: {
|
||||
type: ProcessingType;
|
||||
total: number;
|
||||
loaded: number;
|
||||
}) => {
|
||||
const [key, status] = getProcessingStatusFromType(type);
|
||||
if (!key) return;
|
||||
|
||||
@@ -167,7 +182,7 @@ export default function AppEffects({ setShow }) {
|
||||
return () => {
|
||||
attachmentsLoadingEvent.unsubscribe();
|
||||
progressEvent.unsubscribe();
|
||||
systemTimeInvalidEvent.unsubscribe();
|
||||
// systemTimeInvalidEvent.unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -221,7 +236,8 @@ export default function AppEffects({ setShow }) {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
||||
function getProcessingStatusFromType(type) {
|
||||
type ProcessingType = "download" | "upload" | "encrypt";
|
||||
function getProcessingStatusFromType(type: ProcessingType) {
|
||||
switch (type) {
|
||||
case "download":
|
||||
return ["downloadingAttachments", "Downloading"];
|
||||
@@ -230,6 +246,6 @@ function getProcessingStatusFromType(type) {
|
||||
case "encrypt":
|
||||
return ["encryptingAttachments", "Encrypting"];
|
||||
default:
|
||||
return undefined;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import useMobile from "./hooks/use-mobile";
|
||||
import useTablet from "./hooks/use-tablet";
|
||||
import { LazyMotion, domAnimation } from "framer-motion";
|
||||
import useDatabase from "./hooks/use-database";
|
||||
import { Allotment, LayoutPriority } from "allotment";
|
||||
import { Allotment, AllotmentHandle, LayoutPriority } from "allotment";
|
||||
import { useStore } from "./stores/app-store";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import { ViewLoader } from "./components/loaders/view-loader";
|
||||
@@ -38,8 +38,8 @@ import { usePersistentState } from "./hooks/use-persistent-state";
|
||||
|
||||
new WebExtensionRelay();
|
||||
|
||||
const GlobalMenuWrapper = React.lazy(() =>
|
||||
import("./components/global-menu-wrapper")
|
||||
const GlobalMenuWrapper = React.lazy(
|
||||
() => import("./components/global-menu-wrapper")
|
||||
);
|
||||
const AppEffects = React.lazy(() => import("./app-effects"));
|
||||
const MobileAppEffects = React.lazy(() => import("./app-effects.mobile"));
|
||||
@@ -91,9 +91,25 @@ function App() {
|
||||
|
||||
export default App;
|
||||
|
||||
function SuspenseLoader({ condition, props, component: Component, fallback }) {
|
||||
type SuspenseLoaderProps<TComponent extends React.JSXElementConstructor<any>> =
|
||||
{
|
||||
condition: boolean;
|
||||
props?: React.ComponentProps<TComponent>;
|
||||
component: TComponent;
|
||||
fallback: JSX.Element;
|
||||
};
|
||||
|
||||
function SuspenseLoader<TComponent extends React.JSXElementConstructor<any>>({
|
||||
condition,
|
||||
props,
|
||||
component,
|
||||
fallback
|
||||
}: SuspenseLoaderProps<TComponent>) {
|
||||
if (!condition) return fallback;
|
||||
|
||||
const Component = component as (
|
||||
props: any
|
||||
) => React.ReactComponentElement<any, any>;
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
@@ -105,14 +121,23 @@ function SuspenseLoader({ condition, props, component: Component, fallback }) {
|
||||
);
|
||||
}
|
||||
|
||||
function DesktopAppContents({ isAppLoaded, show, setShow }) {
|
||||
type DesktopAppContentsProps = {
|
||||
isAppLoaded: boolean;
|
||||
show: boolean;
|
||||
setShow: (show: boolean) => void;
|
||||
};
|
||||
function DesktopAppContents({
|
||||
isAppLoaded,
|
||||
show,
|
||||
setShow
|
||||
}: DesktopAppContentsProps) {
|
||||
const isFocusMode = useStore((store) => store.isFocusMode);
|
||||
const isTablet = useTablet();
|
||||
const [paneSizes, setPaneSizes] = usePersistentState("paneSizes", [
|
||||
isTablet ? 60 : 180,
|
||||
isTablet ? 240 : 380
|
||||
]);
|
||||
const panesRef = useRef();
|
||||
const panesRef = useRef<AllotmentHandle>(null);
|
||||
const [isNarrow, setIsNarrow] = useState(paneSizes[0] <= 55);
|
||||
|
||||
return (
|
||||
@@ -184,7 +209,7 @@ function DesktopAppContents({ isAppLoaded, show, setShow }) {
|
||||
);
|
||||
}
|
||||
|
||||
function MobileAppContents({ isAppLoaded }) {
|
||||
function MobileAppContents({ isAppLoaded }: { isAppLoaded: boolean }) {
|
||||
return (
|
||||
<FlexScrollContainer
|
||||
id="slider"
|
||||
@@ -648,7 +648,7 @@ export function showSettings() {
|
||||
));
|
||||
}
|
||||
|
||||
export function showOnboardingDialog(type: string) {
|
||||
export function showOnboardingDialog(type?: string) {
|
||||
if (!type) return;
|
||||
return showDialog("OnboardingDialog", (Dialog, perform) => (
|
||||
<Dialog type={type} onClose={(res: boolean) => perform(res)} />
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const SINGLE_LINE_HEIGHT = 1.4;
|
||||
const DEFAULT_FONT_SIZE = document.getElementById("p").clientHeight - 1;
|
||||
|
||||
const MAX_HEIGHTS = {
|
||||
note: SINGLE_LINE_HEIGHT * 7 * DEFAULT_FONT_SIZE,
|
||||
notebook: SINGLE_LINE_HEIGHT * 7 * DEFAULT_FONT_SIZE,
|
||||
generic: SINGLE_LINE_HEIGHT * 4 * DEFAULT_FONT_SIZE
|
||||
};
|
||||
|
||||
function getNoteHeight(item) {
|
||||
const { notebooks, headline } = item;
|
||||
let height = SINGLE_LINE_HEIGHT * 3;
|
||||
//if (title.length > 35) height += SINGLE_LINE_HEIGHT;
|
||||
if (headline?.length > 0) height += SINGLE_LINE_HEIGHT * 2;
|
||||
else height += SINGLE_LINE_HEIGHT;
|
||||
if (notebooks?.length) height += SINGLE_LINE_HEIGHT;
|
||||
|
||||
return height * DEFAULT_FONT_SIZE;
|
||||
}
|
||||
|
||||
function getNotebookHeight(item) {
|
||||
const { topics, description, title } = item;
|
||||
// at the minimum we will have a title and the info text
|
||||
let height = SINGLE_LINE_HEIGHT * 3; // 2.8 = 2 lines
|
||||
|
||||
if (title.length > 35) {
|
||||
height += SINGLE_LINE_HEIGHT; // title has become multiline
|
||||
}
|
||||
|
||||
if (topics.length > 0) {
|
||||
height += SINGLE_LINE_HEIGHT;
|
||||
}
|
||||
|
||||
if (description?.length > 0) {
|
||||
height += SINGLE_LINE_HEIGHT * 2;
|
||||
}
|
||||
|
||||
return height * DEFAULT_FONT_SIZE;
|
||||
}
|
||||
|
||||
function getItemHeight(item) {
|
||||
const { title } = item;
|
||||
// at the minimum we will have a title and the info text
|
||||
let height = SINGLE_LINE_HEIGHT * 3; // 2.8 = 2 lines
|
||||
|
||||
if (title.length > 30) {
|
||||
height += SINGLE_LINE_HEIGHT; // title has become multiline
|
||||
}
|
||||
|
||||
return height * DEFAULT_FONT_SIZE;
|
||||
}
|
||||
|
||||
export { getNoteHeight, getNotebookHeight, getItemHeight, MAX_HEIGHTS };
|
||||
@@ -33,11 +33,6 @@ const SINGLE_LINE_HEIGHT = 1.4;
|
||||
const DEFAULT_LINE_HEIGHT =
|
||||
(document.getElementById("p")?.clientHeight || 16) - 1;
|
||||
export const DEFAULT_ITEM_HEIGHT = SINGLE_LINE_HEIGHT * 2 * DEFAULT_LINE_HEIGHT;
|
||||
// const MAX_HEIGHTS = {
|
||||
// note: SINGLE_LINE_HEIGHT * 7 * DEFAULT_LINE_HEIGHT,
|
||||
// notebook: SINGLE_LINE_HEIGHT * 7 * DEFAULT_LINE_HEIGHT,
|
||||
// generic: SINGLE_LINE_HEIGHT * 4 * DEFAULT_LINE_HEIGHT
|
||||
// };
|
||||
|
||||
export type Item = { id: string; type: string; title: string } & Record<
|
||||
string,
|
||||
|
||||
@@ -20,14 +20,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useEffect } from "react";
|
||||
import { NavigationEvents } from "../navigation";
|
||||
|
||||
function useNavigate(routeKey, onNavigation) {
|
||||
function useNavigate(routeKey: string, onNavigation: () => void) {
|
||||
useEffect(() => {
|
||||
onNavigation();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function onNavigate(route) {
|
||||
function onNavigate(route: { key: string }) {
|
||||
if (route?.key === routeKey) {
|
||||
onNavigation();
|
||||
}
|
||||
@@ -72,9 +72,6 @@ class AnnouncementStore extends BaseStore {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {[import("zustand").UseStore<AnnouncementStore>, AnnouncementStore]}
|
||||
*/
|
||||
const [useStore, store] = createStore(AnnouncementStore);
|
||||
export { useStore, store };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user