mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-02 04:01:43 +02:00
255 lines
7.7 KiB
TypeScript
255 lines
7.7 KiB
TypeScript
/*
|
|
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/>.
|
|
*/
|
|
|
|
import React, { useEffect } from "react";
|
|
import { useStore } from "./stores/app-store";
|
|
import { useStore as useUserStore } from "./stores/user-store";
|
|
import { useEditorStore } from "./stores/editor-store";
|
|
import { useStore as useAnnouncementStore } from "./stores/announcement-store";
|
|
import { useStore as useSettingStore } from "./stores/setting-store";
|
|
import { scheduleBackups, scheduleFullBackups } from "./common/notices";
|
|
import {
|
|
handleInternalLink,
|
|
introduceFeatures,
|
|
resetFeatures,
|
|
scheduleExpiredNotesDeletion
|
|
} from "./common";
|
|
import { AppEventManager, AppEvents } from "./common/app-events";
|
|
import { db } from "./common/db";
|
|
import { EVENTS, parseInternalLink } from "@notesnook/core";
|
|
import { registerKeyMap } from "./common/key-map";
|
|
import { updateStatus, removeStatus, getStatus } from "./hooks/use-status";
|
|
import { hashNavigate, navigate } from "./navigation";
|
|
import { desktop } from "./common/desktop-bridge";
|
|
import { FeatureDialog } from "./dialogs/feature-dialog";
|
|
import { AnnouncementDialog } from "./dialogs/announcement-dialog";
|
|
import { logger } from "./utils/logger";
|
|
import { showToast } from "./utils/toast";
|
|
import { strings } from "@notesnook/intl";
|
|
|
|
export default function AppEffects() {
|
|
const refreshNavItems = useStore((store) => store.refreshNavItems);
|
|
const toggleListPane = useStore((store) => store.toggleListPane);
|
|
const updateLastSynced = useStore((store) => store.updateLastSynced);
|
|
const isFocusMode = useStore((store) => store.isFocusMode);
|
|
const initUser = useUserStore((store) => store.init);
|
|
const initStore = useStore((store) => store.init);
|
|
const initSettingStore = useSettingStore((store) => store.init);
|
|
const setIsVaultCreated = useStore((store) => store.setIsVaultCreated);
|
|
const initEditorStore = useEditorStore((store) => store.init);
|
|
const dialogAnnouncements = useAnnouncementStore(
|
|
(store) => store.dialogAnnouncements
|
|
);
|
|
|
|
useEffect(
|
|
function initializeApp() {
|
|
initStore();
|
|
initSettingStore();
|
|
|
|
(async function () {
|
|
await initEditorStore();
|
|
await attachDesktopListeners();
|
|
await resetFeatures();
|
|
await refreshNavItems();
|
|
await updateLastSynced();
|
|
await initUser();
|
|
// await resetNotices();
|
|
|
|
await FeatureDialog.show({ featureName: "highlights" });
|
|
await scheduleBackups();
|
|
await scheduleFullBackups();
|
|
await scheduleExpiredNotesDeletion();
|
|
|
|
if (useSettingStore.getState().isFullOfflineMode)
|
|
// NOTE: we deliberately don't await here because we don't want to pause execution.
|
|
db.attachments.cacheAttachments().catch(logger.error);
|
|
})();
|
|
},
|
|
[
|
|
initEditorStore,
|
|
initStore,
|
|
updateLastSynced,
|
|
refreshNavItems,
|
|
initUser,
|
|
setIsVaultCreated
|
|
]
|
|
);
|
|
|
|
useEffect(() => {
|
|
// const systemTimeInvalidEvent = EV.subscribe(
|
|
// EVENTS.systemTimeInvalid,
|
|
// async ({ serverTime, localTime }) => {
|
|
// await showInvalidSystemTimeDialog({ serverTime, localTime });
|
|
// }
|
|
// );
|
|
|
|
function handleDownloadUploadProgress(
|
|
type: ProcessingType,
|
|
total: number,
|
|
current: number
|
|
) {
|
|
const [key, status] = getProcessingStatusFromType(type);
|
|
|
|
if (current === total) {
|
|
removeStatus(key);
|
|
} else {
|
|
updateStatus({
|
|
key,
|
|
status: `${status} attachments`,
|
|
current,
|
|
total,
|
|
progress: 0
|
|
});
|
|
}
|
|
}
|
|
|
|
const fileDownloadEvents = db.eventManager.subscribeMulti(
|
|
[EVENTS.fileDownloaded, EVENTS.fileDownload],
|
|
({ total, current }: { total: number; current: number }) => {
|
|
handleDownloadUploadProgress("download", total, current);
|
|
},
|
|
null
|
|
);
|
|
|
|
const fileUploadEvents = db.eventManager.subscribeMulti(
|
|
[EVENTS.fileUploaded, EVENTS.fileUpload],
|
|
({ total, current }: { total: number; current: number }) => {
|
|
handleDownloadUploadProgress("upload", total, current);
|
|
},
|
|
null
|
|
);
|
|
|
|
const fileEncrypted = AppEventManager.subscribe(
|
|
AppEvents.fileEncrypted,
|
|
({ total, current }: { total: number; current: number }) => {
|
|
handleDownloadUploadProgress("encrypt", total, current);
|
|
}
|
|
);
|
|
|
|
const progressEvent = AppEventManager.subscribe(
|
|
AppEvents.UPDATE_ATTACHMENT_PROGRESS,
|
|
({
|
|
type,
|
|
total,
|
|
loaded
|
|
}: {
|
|
type: ProcessingType;
|
|
total: number;
|
|
loaded: number;
|
|
}) => {
|
|
const [key, status] = getProcessingStatusFromType(type);
|
|
if (!key) return;
|
|
|
|
const percent = Math.round((loaded / total) * 100);
|
|
const oldStatus = getStatus(key);
|
|
const text = oldStatus?.status || `${status} attachment`;
|
|
|
|
if (
|
|
(!oldStatus ||
|
|
(oldStatus.total === undefined &&
|
|
oldStatus.current === undefined) ||
|
|
oldStatus.total === oldStatus.current) &&
|
|
loaded === total
|
|
) {
|
|
removeStatus(key);
|
|
} else {
|
|
updateStatus({
|
|
...oldStatus,
|
|
key,
|
|
status: text,
|
|
progress: loaded === total ? 100 : percent
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
const vaultEvent = db.eventManager.subscribe(EVENTS.vaultAutoLocked, () => {
|
|
showToast("success", strings.vaultLocked());
|
|
});
|
|
|
|
registerKeyMap();
|
|
return () => {
|
|
[
|
|
...fileDownloadEvents,
|
|
...fileUploadEvents,
|
|
progressEvent,
|
|
fileEncrypted,
|
|
vaultEvent
|
|
].forEach((e) => e.unsubscribe());
|
|
// systemTimeInvalidEvent.unsubscribe();
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
toggleListPane(!isFocusMode);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isFocusMode]);
|
|
|
|
useEffect(() => {
|
|
introduceFeatures();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!dialogAnnouncements.length || IS_TESTING) return;
|
|
(async () => {
|
|
await AnnouncementDialog.show({ announcement: dialogAnnouncements[0] });
|
|
})();
|
|
}, [dialogAnnouncements]);
|
|
|
|
return <React.Fragment />;
|
|
}
|
|
|
|
type ProcessingType = "download" | "upload" | "encrypt";
|
|
function getProcessingStatusFromType(type: ProcessingType) {
|
|
switch (type) {
|
|
case "download":
|
|
return ["downloadingAttachments", "Downloading"];
|
|
case "upload":
|
|
return ["uploadingAttachments", "Uploading"];
|
|
case "encrypt":
|
|
return ["encryptingAttachments", "Encrypting"];
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function attachDesktopListeners() {
|
|
const handlers = [
|
|
AppEventManager.subscribe(AppEvents.onCreateItem, onCreateItem),
|
|
AppEventManager.subscribe(AppEvents.onOpenLink, handleInternalLink)
|
|
];
|
|
|
|
await desktop?.bridge.ready.query();
|
|
return handlers;
|
|
}
|
|
|
|
async function onCreateItem(itemType: string) {
|
|
switch (itemType) {
|
|
case "note":
|
|
useEditorStore.getState().newSession();
|
|
break;
|
|
case "notebook":
|
|
hashNavigate("/notebooks/create", { replace: true });
|
|
break;
|
|
case "reminder":
|
|
hashNavigate("/reminders/create", { replace: true });
|
|
break;
|
|
}
|
|
}
|