mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
web: fix many type errors
This commit is contained in:
@@ -15,6 +15,8 @@ apps/mobile/e2e/
|
|||||||
|
|
||||||
# web
|
# web
|
||||||
apps/web/public/an.js
|
apps/web/public/an.js
|
||||||
|
apps/web/src/common/sqlite/wa-sqlite-async.js
|
||||||
|
apps/web/src/common/sqlite/wa-sqlite.js
|
||||||
|
|
||||||
# editor
|
# editor
|
||||||
packages/editor/styles/
|
packages/editor/styles/
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export class AppModel {
|
|||||||
|
|
||||||
async goToTags() {
|
async goToTags() {
|
||||||
await this.navigateTo("Tags");
|
await this.navigateTo("Tags");
|
||||||
return new ItemsViewModel(this.page, "tags");
|
return new ItemsViewModel(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToColor(color: string) {
|
async goToColor(color: string) {
|
||||||
|
|||||||
@@ -18,19 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { lazify } from "../utils/lazify";
|
import { lazify } from "../utils/lazify";
|
||||||
import { Attachment } from "@notesnook/core";
|
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
|
|
||||||
async function download(hash: string, groupId?: string) {
|
async function download(hash: string, groupId?: string) {
|
||||||
const attachment = db.attachments.attachment(hash);
|
const attachment = await db.attachments.attachment(hash);
|
||||||
if (!attachment) return;
|
if (!attachment) return;
|
||||||
const downloadResult = await db
|
const downloadResult = await db
|
||||||
.fs()
|
.fs()
|
||||||
.downloadFile(
|
.downloadFile(
|
||||||
groupId || attachment.metadata.hash,
|
groupId || attachment.hash,
|
||||||
attachment.metadata.hash,
|
attachment.hash,
|
||||||
attachment.chunkSize,
|
attachment.chunkSize
|
||||||
attachment.metadata
|
|
||||||
);
|
);
|
||||||
if (!downloadResult) throw new Error("Failed to download file.");
|
if (!downloadResult) throw new Error("Failed to download file.");
|
||||||
|
|
||||||
@@ -46,7 +44,7 @@ export async function saveAttachment(hash: string) {
|
|||||||
|
|
||||||
const { attachment, key } = response;
|
const { attachment, key } = response;
|
||||||
await lazify(import("../interfaces/fs"), ({ saveFile }) =>
|
await lazify(import("../interfaces/fs"), ({ saveFile }) =>
|
||||||
saveFile(attachment.metadata.hash, {
|
saveFile(attachment.hash, {
|
||||||
key,
|
key,
|
||||||
iv: attachment.iv,
|
iv: attachment.iv,
|
||||||
name: attachment.filename,
|
name: attachment.filename,
|
||||||
@@ -77,7 +75,7 @@ export async function downloadAttachment<
|
|||||||
return (await db.attachments.read(hash, type)) as TOutputType;
|
return (await db.attachments.read(hash, type)) as TOutputType;
|
||||||
|
|
||||||
const blob = await lazify(import("../interfaces/fs"), ({ decryptFile }) =>
|
const blob = await lazify(import("../interfaces/fs"), ({ decryptFile }) =>
|
||||||
decryptFile(attachment.metadata.hash, {
|
decryptFile(attachment.hash, {
|
||||||
key,
|
key,
|
||||||
iv: attachment.iv,
|
iv: attachment.iv,
|
||||||
name: attachment.filename,
|
name: attachment.filename,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dialogs } from "../dialogs";
|
import { Dialogs } from "../dialogs";
|
||||||
import { store as notebookStore } from "../stores/notebook-store";
|
|
||||||
import { store as tagStore } from "../stores/tag-store";
|
import { store as tagStore } from "../stores/tag-store";
|
||||||
import { store as appStore } from "../stores/app-store";
|
import { store as appStore } from "../stores/app-store";
|
||||||
import { store as editorStore } from "../stores/editor-store";
|
import { store as editorStore } from "../stores/editor-store";
|
||||||
@@ -26,7 +25,6 @@ import { store as noteStore } from "../stores/note-store";
|
|||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { showToast } from "../utils/toast";
|
import { showToast } from "../utils/toast";
|
||||||
import { Text } from "@theme-ui/components";
|
import { Text } from "@theme-ui/components";
|
||||||
import { Topic as TopicIcon } from "../components/icons";
|
|
||||||
import Config from "../utils/config";
|
import Config from "../utils/config";
|
||||||
import { AppVersion, getChangelog } from "../utils/version";
|
import { AppVersion, getChangelog } from "../utils/version";
|
||||||
import { Period } from "../dialogs/buy-dialog/types";
|
import { Period } from "../dialogs/buy-dialog/types";
|
||||||
@@ -36,7 +34,7 @@ import { ConfirmDialogProps } from "../dialogs/confirm";
|
|||||||
import { getFormattedDate } from "@notesnook/common";
|
import { getFormattedDate } from "@notesnook/common";
|
||||||
import { downloadUpdate } from "../utils/updater";
|
import { downloadUpdate } from "../utils/updater";
|
||||||
import { ThemeMetadata } from "@notesnook/themes-server";
|
import { ThemeMetadata } from "@notesnook/themes-server";
|
||||||
import { Reminder } from "@notesnook/core";
|
import { Color, Reminder, Tag } from "@notesnook/core";
|
||||||
import { AuthenticatorType } from "@notesnook/core/dist/api/user-manager";
|
import { AuthenticatorType } from "@notesnook/core/dist/api/user-manager";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
@@ -405,10 +403,13 @@ export function showPasswordDialog(
|
|||||||
password?: string;
|
password?: string;
|
||||||
oldPassword?: string;
|
oldPassword?: string;
|
||||||
newPassword?: string;
|
newPassword?: string;
|
||||||
|
deleteAllLockedNotes?: boolean;
|
||||||
}) => boolean | Promise<boolean>
|
}) => boolean | Promise<boolean>
|
||||||
) {
|
) {
|
||||||
const { title, subtitle, positiveButtonText, checks } = getDialogData(type);
|
const { title, subtitle, positiveButtonText, checks } = getDialogData(type);
|
||||||
return showDialog("PasswordDialog", (Dialog, perform) => (
|
return showDialog<"PasswordDialog", boolean>(
|
||||||
|
"PasswordDialog",
|
||||||
|
(Dialog, perform) => (
|
||||||
<Dialog
|
<Dialog
|
||||||
type={type}
|
type={type}
|
||||||
title={title}
|
title={title}
|
||||||
@@ -419,7 +420,8 @@ export function showPasswordDialog(
|
|||||||
onClose={() => perform(false)}
|
onClose={() => perform(false)}
|
||||||
onDone={() => perform(true)}
|
onDone={() => perform(true)}
|
||||||
/>
|
/>
|
||||||
));
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showBackupPasswordDialog(
|
export function showBackupPasswordDialog(
|
||||||
@@ -462,9 +464,7 @@ export function showCreateTagDialog() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showEditTagDialog(tagId: string) {
|
export function showEditTagDialog(tag: Tag) {
|
||||||
const tag = db.tags.tag(tagId);
|
|
||||||
if (!tag) return;
|
|
||||||
return showDialog("ItemDialog", (Dialog, perform) => (
|
return showDialog("ItemDialog", (Dialog, perform) => (
|
||||||
<Dialog
|
<Dialog
|
||||||
title={"Edit tag"}
|
title={"Edit tag"}
|
||||||
@@ -474,7 +474,7 @@ export function showEditTagDialog(tagId: string) {
|
|||||||
onClose={() => perform(false)}
|
onClose={() => perform(false)}
|
||||||
onAction={async (title: string) => {
|
onAction={async (title: string) => {
|
||||||
if (!title) return;
|
if (!title) return;
|
||||||
await db.tags.add({ id: tagId, title });
|
await db.tags.add({ id: tag.id, title });
|
||||||
showToast("success", "Tag edited!");
|
showToast("success", "Tag edited!");
|
||||||
tagStore.refresh();
|
tagStore.refresh();
|
||||||
editorStore.refreshTags();
|
editorStore.refreshTags();
|
||||||
@@ -486,9 +486,7 @@ export function showEditTagDialog(tagId: string) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showRenameColorDialog(colorId: string) {
|
export function showRenameColorDialog(color: Color) {
|
||||||
const color = db.colors.color(colorId);
|
|
||||||
if (!color) return;
|
|
||||||
return showDialog("ItemDialog", (Dialog, perform) => (
|
return showDialog("ItemDialog", (Dialog, perform) => (
|
||||||
<Dialog
|
<Dialog
|
||||||
title={"Rename color"}
|
title={"Rename color"}
|
||||||
@@ -498,7 +496,7 @@ export function showRenameColorDialog(colorId: string) {
|
|||||||
onClose={() => perform(false)}
|
onClose={() => perform(false)}
|
||||||
onAction={async (title: string) => {
|
onAction={async (title: string) => {
|
||||||
if (!title) return;
|
if (!title) return;
|
||||||
await db.tags.add({ id: colorId, title });
|
await db.tags.add({ id: color.id, title });
|
||||||
showToast("success", "Color renamed!");
|
showToast("success", "Color renamed!");
|
||||||
appStore.refreshNavItems();
|
appStore.refreshNavItems();
|
||||||
perform(true);
|
perform(true);
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import { createWritableStream } from "./desktop-bridge";
|
|||||||
import { createZipStream } from "../utils/streams/zip-stream";
|
import { createZipStream } from "../utils/streams/zip-stream";
|
||||||
import { FeatureKeys } from "../dialogs/feature-dialog";
|
import { FeatureKeys } from "../dialogs/feature-dialog";
|
||||||
import { ZipEntry, createUnzipIterator } from "../utils/streams/unzip-stream";
|
import { ZipEntry, createUnzipIterator } from "../utils/streams/unzip-stream";
|
||||||
|
import { User } from "@notesnook/core/dist/api/user-manager";
|
||||||
|
import { LegacyBackupFile } from "@notesnook/core";
|
||||||
|
|
||||||
export const CREATE_BUTTON_MAP = {
|
export const CREATE_BUTTON_MAP = {
|
||||||
notes: {
|
notes: {
|
||||||
@@ -52,10 +54,6 @@ export const CREATE_BUTTON_MAP = {
|
|||||||
title: "Create a notebook",
|
title: "Create a notebook",
|
||||||
onClick: () => hashNavigate("/notebooks/create", { replace: true })
|
onClick: () => hashNavigate("/notebooks/create", { replace: true })
|
||||||
},
|
},
|
||||||
topics: {
|
|
||||||
title: "Create a topic",
|
|
||||||
onClick: () => hashNavigate(`/topics/create`, { replace: true })
|
|
||||||
},
|
|
||||||
tags: {
|
tags: {
|
||||||
title: "Create a tag",
|
title: "Create a tag",
|
||||||
onClick: () => hashNavigate(`/tags/create`, { replace: true })
|
onClick: () => hashNavigate(`/tags/create`, { replace: true })
|
||||||
@@ -227,7 +225,7 @@ export async function restoreBackupFile(backupFile: File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function restoreWithProgress(
|
async function restoreWithProgress(
|
||||||
backup: Record<string, unknown>,
|
backup: LegacyBackupFile,
|
||||||
password?: string,
|
password?: string,
|
||||||
key?: string
|
key?: string
|
||||||
) {
|
) {
|
||||||
@@ -263,8 +261,8 @@ async function restoreWithProgress(
|
|||||||
|
|
||||||
export async function verifyAccount() {
|
export async function verifyAccount() {
|
||||||
if (!(await db.user?.getUser())) return true;
|
if (!(await db.user?.getUser())) return true;
|
||||||
return showPasswordDialog("verify_account", ({ password }) => {
|
return showPasswordDialog("verify_account", async ({ password }) => {
|
||||||
return db.user?.verifyPassword(password) || false;
|
return !!password && (await db.user?.verifyPassword(password));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +295,7 @@ export async function showUpgradeReminderDialogs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function restore(
|
async function restore(
|
||||||
backup: Record<string, unknown>,
|
backup: LegacyBackupFile,
|
||||||
password?: string,
|
password?: string,
|
||||||
key?: string
|
key?: string
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { showToast } from "../utils/toast";
|
|||||||
import Vault from "./vault";
|
import Vault from "./vault";
|
||||||
import { TaskManager } from "./task-manager";
|
import { TaskManager } from "./task-manager";
|
||||||
import { pluralize } from "@notesnook/common";
|
import { pluralize } from "@notesnook/common";
|
||||||
import { Reminder } from "@notesnook/core";
|
|
||||||
|
|
||||||
async function moveNotesToTrash(ids: string[], confirm = true) {
|
async function moveNotesToTrash(ids: string[], confirm = true) {
|
||||||
if (confirm && !(await showMultiDeleteConfirmation(ids.length))) return;
|
if (confirm && !(await showMultiDeleteConfirmation(ids.length))) return;
|
||||||
@@ -103,10 +102,10 @@ async function deleteAttachments(ids: string[]) {
|
|||||||
showToast("success", `${pluralize(ids.length, "attachment")} deleted`);
|
showToast("success", `${pluralize(ids.length, "attachment")} deleted`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveRemindersToTrash(reminders: Reminder[]) {
|
async function moveRemindersToTrash(ids: string[]) {
|
||||||
const isMultiselect = reminders.length > 1;
|
const isMultiselect = ids.length > 1;
|
||||||
if (isMultiselect) {
|
if (isMultiselect) {
|
||||||
if (!(await showMultiDeleteConfirmation(reminders.length))) return;
|
if (!(await showMultiDeleteConfirmation(ids.length))) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await TaskManager.startTask({
|
await TaskManager.startTask({
|
||||||
@@ -114,13 +113,13 @@ async function moveRemindersToTrash(reminders: Reminder[]) {
|
|||||||
id: "deleteReminders",
|
id: "deleteReminders",
|
||||||
action: async (report) => {
|
action: async (report) => {
|
||||||
report({
|
report({
|
||||||
text: `Deleting ${pluralize(reminders.length, "reminder")}...`
|
text: `Deleting ${pluralize(ids.length, "reminder")}...`
|
||||||
});
|
});
|
||||||
await reminderStore.delete(...reminders.map((i) => i.id));
|
await reminderStore.delete(...ids);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
showToast("success", `${pluralize(reminders.length, "reminder")} deleted.`);
|
showToast("success", `${pluralize(ids.length, "reminder")} deleted.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Multiselect = {
|
export const Multiselect = {
|
||||||
|
|||||||
@@ -288,10 +288,11 @@ export function Factory(Module) {
|
|||||||
return sqlite3.column_blob(stmt, iCol);
|
return sqlite3.column_blob(stmt, iCol);
|
||||||
case SQLite.SQLITE_FLOAT:
|
case SQLite.SQLITE_FLOAT:
|
||||||
return sqlite3.column_double(stmt, iCol);
|
return sqlite3.column_double(stmt, iCol);
|
||||||
case SQLite.SQLITE_INTEGER:
|
case SQLite.SQLITE_INTEGER: {
|
||||||
const lo32 = sqlite3.column_int(stmt, iCol);
|
const lo32 = sqlite3.column_int(stmt, iCol);
|
||||||
const hi32 = Module.getTempRet0();
|
const hi32 = Module.getTempRet0();
|
||||||
return cvt32x2AsSafe(lo32, hi32);
|
return cvt32x2AsSafe(lo32, hi32);
|
||||||
|
}
|
||||||
case SQLite.SQLITE_NULL:
|
case SQLite.SQLITE_NULL:
|
||||||
return null;
|
return null;
|
||||||
case SQLite.SQLITE_TEXT:
|
case SQLite.SQLITE_TEXT:
|
||||||
@@ -813,10 +814,11 @@ export function Factory(Module) {
|
|||||||
return sqlite3.value_blob(pValue);
|
return sqlite3.value_blob(pValue);
|
||||||
case SQLite.SQLITE_FLOAT:
|
case SQLite.SQLITE_FLOAT:
|
||||||
return sqlite3.value_double(pValue);
|
return sqlite3.value_double(pValue);
|
||||||
case SQLite.SQLITE_INTEGER:
|
case SQLite.SQLITE_INTEGER: {
|
||||||
const lo32 = sqlite3.value_int(pValue);
|
const lo32 = sqlite3.value_int(pValue);
|
||||||
const hi32 = Module.getTempRet0();
|
const hi32 = Module.getTempRet0();
|
||||||
return cvt32x2AsSafe(lo32, hi32);
|
return cvt32x2AsSafe(lo32, hi32);
|
||||||
|
}
|
||||||
case SQLite.SQLITE_NULL:
|
case SQLite.SQLITE_NULL:
|
||||||
return null;
|
return null;
|
||||||
case SQLite.SQLITE_TEXT:
|
case SQLite.SQLITE_TEXT:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class Vault {
|
|||||||
static async createVault() {
|
static async createVault() {
|
||||||
if (await db.vault.exists()) return false;
|
if (await db.vault.exists()) return false;
|
||||||
return await showPasswordDialog("create_vault", async ({ password }) => {
|
return await showPasswordDialog("create_vault", async ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
await db.vault.create(password);
|
await db.vault.create(password);
|
||||||
showToast("success", "Vault created.");
|
showToast("success", "Vault created.");
|
||||||
return true;
|
return true;
|
||||||
@@ -35,6 +36,7 @@ class Vault {
|
|||||||
static async clearVault() {
|
static async clearVault() {
|
||||||
if (!(await db.vault.exists())) return false;
|
if (!(await db.vault.exists())) return false;
|
||||||
return await showPasswordDialog("clear_vault", async ({ password }) => {
|
return await showPasswordDialog("clear_vault", async ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
try {
|
try {
|
||||||
await db.vault.clear(password);
|
await db.vault.clear(password);
|
||||||
return true;
|
return true;
|
||||||
@@ -49,6 +51,7 @@ class Vault {
|
|||||||
return await showPasswordDialog(
|
return await showPasswordDialog(
|
||||||
"delete_vault",
|
"delete_vault",
|
||||||
async ({ password, deleteAllLockedNotes }) => {
|
async ({ password, deleteAllLockedNotes }) => {
|
||||||
|
if (!password) return false;
|
||||||
if (!(await db.user.verifyPassword(password))) return false;
|
if (!(await db.user.verifyPassword(password))) return false;
|
||||||
await db.vault.delete(!!deleteAllLockedNotes);
|
await db.vault.delete(!!deleteAllLockedNotes);
|
||||||
return true;
|
return true;
|
||||||
@@ -62,6 +65,7 @@ class Vault {
|
|||||||
*/
|
*/
|
||||||
static unlockVault() {
|
static unlockVault() {
|
||||||
return showPasswordDialog("ask_vault_password", ({ password }) => {
|
return showPasswordDialog("ask_vault_password", ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
return db.vault
|
return db.vault
|
||||||
.unlock(password)
|
.unlock(password)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
@@ -73,6 +77,7 @@ class Vault {
|
|||||||
return showPasswordDialog(
|
return showPasswordDialog(
|
||||||
"change_password",
|
"change_password",
|
||||||
async ({ oldPassword, newPassword }) => {
|
async ({ oldPassword, newPassword }) => {
|
||||||
|
if (!oldPassword || !newPassword) return false;
|
||||||
await db.vault.changePassword(oldPassword, newPassword);
|
await db.vault.changePassword(oldPassword, newPassword);
|
||||||
showToast("success", "Vault password changed.");
|
showToast("success", "Vault password changed.");
|
||||||
return true;
|
return true;
|
||||||
@@ -80,9 +85,9 @@ class Vault {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unlockNote(id, type = "unlock_note") {
|
static unlockNote(id: string, type = "unlock_note") {
|
||||||
return new Promise((resolve) => {
|
|
||||||
return showPasswordDialog(type, ({ password }) => {
|
return showPasswordDialog(type, ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
return db.vault
|
return db.vault
|
||||||
.remove(id, password)
|
.remove(id, password)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
@@ -90,19 +95,18 @@ class Vault {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}).then(resolve);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static lockNote(id) {
|
static lockNote(id: string): Promise<boolean> {
|
||||||
return db.vault
|
return db.vault
|
||||||
.add(id)
|
.add(id)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
.catch(({ message }) => {
|
.catch(({ message }) => {
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case db.vault.ERRORS.noVault:
|
case VAULT_ERRORS.noVault:
|
||||||
return Vault.createVault().then(() => Vault.lockNote(id));
|
return Vault.createVault().then(() => Vault.lockNote(id));
|
||||||
case db.vault.ERRORS.vaultLocked:
|
case VAULT_ERRORS.vaultLocked:
|
||||||
return Vault.unlockVault().then(() => Vault.lockNote(id));
|
return Vault.unlockVault().then(() => Vault.lockNote(id));
|
||||||
default:
|
default:
|
||||||
showToast("error", message);
|
showToast("error", message);
|
||||||
@@ -112,8 +116,9 @@ class Vault {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static askPassword(action) {
|
static askPassword(action: (password: string) => Promise<boolean>) {
|
||||||
return showPasswordDialog("ask_vault_password", ({ password }) => {
|
return showPasswordDialog("ask_vault_password", ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
return action(password);
|
return action(password);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ type DialogProps = SxProp & {
|
|||||||
description?: string;
|
description?: string;
|
||||||
positiveButton?: DialogButtonProps | null;
|
positiveButton?: DialogButtonProps | null;
|
||||||
negativeButton?: DialogButtonProps | null;
|
negativeButton?: DialogButtonProps | null;
|
||||||
footer?: React.Component;
|
footer?: React.ReactNode;
|
||||||
noScroll?: boolean;
|
noScroll?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -157,13 +157,15 @@ async function addAttachment(
|
|||||||
file: File,
|
file: File,
|
||||||
options: AddAttachmentOptions = {}
|
options: AddAttachmentOptions = {}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const { default: FS } = await import("../../interfaces/fs");
|
const { getUploadedFileSize, hashStream, writeEncryptedFile } = await import(
|
||||||
|
"../../interfaces/fs"
|
||||||
|
);
|
||||||
const { expectedFileHash, showProgress = true } = options;
|
const { expectedFileHash, showProgress = true } = options;
|
||||||
let forceWrite = options.forceWrite;
|
let forceWrite = options.forceWrite;
|
||||||
|
|
||||||
const action = async () => {
|
const action = async () => {
|
||||||
const reader = file.stream().getReader();
|
const reader = file.stream().getReader();
|
||||||
const { hash, type: hashType } = await FS.hashStream(reader);
|
const { hash, type: hashType } = await hashStream(reader);
|
||||||
reader.releaseLock();
|
reader.releaseLock();
|
||||||
|
|
||||||
if (expectedFileHash && hash !== expectedFileHash)
|
if (expectedFileHash && hash !== expectedFileHash)
|
||||||
@@ -171,15 +173,15 @@ async function addAttachment(
|
|||||||
`Please select the same file for reuploading. Expected hash ${expectedFileHash} but got ${hash}.`
|
`Please select the same file for reuploading. Expected hash ${expectedFileHash} but got ${hash}.`
|
||||||
);
|
);
|
||||||
|
|
||||||
const exists = db.attachments?.attachment(hash);
|
const exists = await db.attachments.attachment(hash);
|
||||||
if (!forceWrite && exists) {
|
if (!forceWrite && exists) {
|
||||||
forceWrite = (await FS.getUploadedFileSize(hash)) <= 0;
|
forceWrite = (await getUploadedFileSize(hash)) <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceWrite || !exists) {
|
if (forceWrite || !exists) {
|
||||||
const key: SerializedKey = await getEncryptionKey();
|
const key: SerializedKey = await getEncryptionKey();
|
||||||
|
|
||||||
const output = await FS.writeEncryptedFile(file, key, hash);
|
const output = await writeEncryptedFile(file, key, hash);
|
||||||
if (!output) throw new Error("Could not encrypt file.");
|
if (!output) throw new Error("Could not encrypt file.");
|
||||||
|
|
||||||
if (forceWrite && exists) await db.attachments.reset(hash);
|
if (forceWrite && exists) await db.attachments.reset(hash);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ function TitleBox(props: TitleBoxProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updateFontSize = useCallback(
|
const updateFontSize = useCallback(
|
||||||
(length) => {
|
(length: number) => {
|
||||||
if (!inputRef.current) return;
|
if (!inputRef.current) return;
|
||||||
const fontSize = textLengthToFontSize(
|
const fontSize = textLengthToFontSize(
|
||||||
length,
|
length,
|
||||||
|
|||||||
@@ -143,7 +143,11 @@ function Field(props: FieldProps) {
|
|||||||
}}
|
}}
|
||||||
disabled={action.disabled}
|
disabled={action.disabled}
|
||||||
>
|
>
|
||||||
{action.component ? action.component : <action.icon size={20} />}
|
{action.component ? (
|
||||||
|
action.component
|
||||||
|
) : action.icon ? (
|
||||||
|
<action.icon size={20} />
|
||||||
|
) : null}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type FilteredListProps<T> = {
|
|||||||
placeholders: { filter: string; empty: string };
|
placeholders: { filter: string; empty: string };
|
||||||
filter: (query: string) => Promise<T[]>;
|
filter: (query: string) => Promise<T[]>;
|
||||||
onCreateNewItem: (title: string) => Promise<void>;
|
onCreateNewItem: (title: string) => Promise<void>;
|
||||||
} & VirtualizedListProps<T>;
|
} & VirtualizedListProps<T, unknown>;
|
||||||
|
|
||||||
export function FilteredList<T>(props: FilteredListProps<T>) {
|
export function FilteredList<T>(props: FilteredListProps<T>) {
|
||||||
const { items, filter, onCreateNewItem, placeholders, ...listProps } = props;
|
const { items, filter, onCreateNewItem, placeholders, ...listProps } = props;
|
||||||
@@ -38,7 +38,7 @@ export function FilteredList<T>(props: FilteredListProps<T>) {
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const _filter = useCallback(
|
const _filter = useCallback(
|
||||||
async (query) => {
|
async (query = "") => {
|
||||||
setFilteredItems(query ? await filter(query) : []);
|
setFilteredItems(query ? await filter(query) : []);
|
||||||
setQuery(query);
|
setQuery(query);
|
||||||
},
|
},
|
||||||
@@ -46,7 +46,7 @@ export function FilteredList<T>(props: FilteredListProps<T>) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const _createNewItem = useCallback(
|
const _createNewItem = useCallback(
|
||||||
async (title) => {
|
async (title: string) => {
|
||||||
await onCreateNewItem(title);
|
await onCreateNewItem(title);
|
||||||
setQuery(undefined);
|
setQuery(undefined);
|
||||||
if (inputRef.current) inputRef.current.value = "";
|
if (inputRef.current) inputRef.current.value = "";
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ function NavigationMenu(props: NavigationMenuProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const _navigate = useCallback(
|
const _navigate = useCallback(
|
||||||
(path) => {
|
(path: string) => {
|
||||||
toggleNavigationContainer(true);
|
toggleNavigationContainer(true);
|
||||||
const nestedRoute = findNestedRoute(path);
|
const nestedRoute = findNestedRoute(path);
|
||||||
navigate(!nestedRoute || nestedRoute === location ? path : nestedRoute);
|
navigate(!nestedRoute || nestedRoute === location ? path : nestedRoute);
|
||||||
@@ -218,7 +218,7 @@ function NavigationMenu(props: NavigationMenuProps) {
|
|||||||
key: "rename",
|
key: "rename",
|
||||||
title: "Rename color",
|
title: "Rename color",
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await showRenameColorDialog(color.id);
|
await showRenameColorDialog(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ import {
|
|||||||
NotebooksWithDateEdited,
|
NotebooksWithDateEdited,
|
||||||
TagsWithDateEdited
|
TagsWithDateEdited
|
||||||
} from "../list-container/types";
|
} from "../list-container/types";
|
||||||
import { SchemeColors, StaticColors } from "@notesnook/theme";
|
import { SchemeColors } from "@notesnook/theme";
|
||||||
import Vault from "../../common/vault";
|
import Vault from "../../common/vault";
|
||||||
|
|
||||||
type NoteProps = {
|
type NoteProps = {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import { ResolvedItem } from "../list-container/resolved-item";
|
|||||||
import { SessionItem } from "../session-item";
|
import { SessionItem } from "../session-item";
|
||||||
import { COLORS } from "../../common/constants";
|
import { COLORS } from "../../common/constants";
|
||||||
import { DefaultColors } from "@notesnook/core";
|
import { DefaultColors } from "@notesnook/core";
|
||||||
|
import { VirtualizedTable } from "../virtualized-table";
|
||||||
|
|
||||||
const tools = [
|
const tools = [
|
||||||
{ key: "pin", property: "pinned", icon: Pin, label: "Pin" },
|
{ key: "pin", property: "pinned", icon: Pin, label: "Pin" },
|
||||||
@@ -272,10 +273,14 @@ function Notebooks({ noteId }: { noteId: string }) {
|
|||||||
<VirtualizedList
|
<VirtualizedList
|
||||||
mode="fixed"
|
mode="fixed"
|
||||||
estimatedSize={50}
|
estimatedSize={50}
|
||||||
getItemKey={(index) => result.value.getKey(index)}
|
getItemKey={(index) => result.value.key(index)}
|
||||||
items={result.value.ungrouped}
|
items={result.value.ids}
|
||||||
renderItem={({ item: id }) => (
|
renderItem={({ item: index }) => (
|
||||||
<ListItemWrapper id={id} items={result.value} simplified />
|
<ResolvedItem index={index} items={result.value} type="notebook">
|
||||||
|
{({ item, data }) => (
|
||||||
|
<ListItemWrapper item={item} data={data} simplified />
|
||||||
|
)}
|
||||||
|
</ResolvedItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
@@ -295,10 +300,14 @@ function Reminders({ noteId }: { noteId: string }) {
|
|||||||
<VirtualizedList
|
<VirtualizedList
|
||||||
mode="fixed"
|
mode="fixed"
|
||||||
estimatedSize={54}
|
estimatedSize={54}
|
||||||
getItemKey={(index) => result.value.getKey(index)}
|
getItemKey={(index) => result.value.key(index)}
|
||||||
items={result.value.ungrouped}
|
items={result.value.ids}
|
||||||
renderItem={({ item: id }) => (
|
renderItem={({ item: index }) => (
|
||||||
<ListItemWrapper id={id} items={result.value} simplified />
|
<ResolvedItem index={index} items={result.value} type="reminder">
|
||||||
|
{({ item, data }) => (
|
||||||
|
<ListItemWrapper item={item} data={data} simplified />
|
||||||
|
)}
|
||||||
|
</ResolvedItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
@@ -315,14 +324,17 @@ function Attachments({ noteId }: { noteId: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title="Attachments">
|
<Section title="Attachments">
|
||||||
{result.value.ids.map((id, index) => (
|
<VirtualizedTable
|
||||||
<ListItemWrapper
|
estimatedSize={30}
|
||||||
key={result.value.getKey(index)}
|
getItemKey={(index) => result.value.key(index)}
|
||||||
id={id as string}
|
items={result.value.ids}
|
||||||
items={result.value}
|
header={<></>}
|
||||||
compact
|
renderRow={({ item: index }) => (
|
||||||
|
<ResolvedItem index={index} type="attachment" items={result.value}>
|
||||||
|
{({ item }) => <ListItemWrapper item={item} compact />}
|
||||||
|
</ResolvedItem>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -353,10 +365,10 @@ function SessionHistory({
|
|||||||
<VirtualizedList
|
<VirtualizedList
|
||||||
mode="fixed"
|
mode="fixed"
|
||||||
estimatedSize={28}
|
estimatedSize={28}
|
||||||
getItemKey={(index) => result.value.getKey(index)}
|
getItemKey={(index) => result.value.key(index)}
|
||||||
items={result.value.ungrouped}
|
items={result.value.ids}
|
||||||
renderItem={({ item: id }) => (
|
renderItem={({ item: index }) => (
|
||||||
<ResolvedItem type="session" id={id} items={result.value}>
|
<ResolvedItem type="session" index={index} items={result.value}>
|
||||||
{({ item }) => (
|
{({ item }) => (
|
||||||
<SessionItem
|
<SessionItem
|
||||||
noteId={noteId}
|
noteId={noteId}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { showToast } from "../../utils/toast";
|
|||||||
import { pluralize } from "@notesnook/common";
|
import { pluralize } from "@notesnook/common";
|
||||||
import { MenuItem } from "@notesnook/ui";
|
import { MenuItem } from "@notesnook/ui";
|
||||||
import { Tag } from "@notesnook/core/dist/types";
|
import { Tag } from "@notesnook/core/dist/types";
|
||||||
|
import { showEditTagDialog } from "../../common/dialog-controller";
|
||||||
|
|
||||||
type TagProps = { item: Tag; totalNotes: number };
|
type TagProps = { item: Tag; totalNotes: number };
|
||||||
function Tag(props: TagProps) {
|
function Tag(props: TagProps) {
|
||||||
@@ -70,7 +71,7 @@ const menuItems: (tag: Tag, ids?: string[]) => MenuItem[] = (tag, ids = []) => {
|
|||||||
title: "Rename tag",
|
title: "Rename tag",
|
||||||
icon: Edit.path,
|
icon: Edit.path,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
hashNavigate(`/tags/${tag.id}/edit`);
|
showEditTagDialog(tag);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export function ThemePreview(props: ThemePreviewProps) {
|
|||||||
theme.previewColors.background
|
theme.previewColors.background
|
||||||
].map((color) => (
|
].map((color) => (
|
||||||
<Circle
|
<Circle
|
||||||
|
key={color}
|
||||||
color={color}
|
color={color}
|
||||||
size={18}
|
size={18}
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function Unlock(props: UnlockProps) {
|
|||||||
try {
|
try {
|
||||||
if (!password) return;
|
if (!password) return;
|
||||||
const note = await db.vault.open(noteId, password);
|
const note = await db.vault.open(noteId, password);
|
||||||
console.log(note);
|
if (!note) return;
|
||||||
openLockedSession(note);
|
openLockedSession(note);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { store as notestore } from "../stores/note-store";
|
|||||||
import { store as editorStore } from "../stores/editor-store";
|
import { store as editorStore } from "../stores/editor-store";
|
||||||
import { Perform } from "../common/dialog-controller";
|
import { Perform } from "../common/dialog-controller";
|
||||||
import { FilteredList } from "../components/filtered-list";
|
import { FilteredList } from "../components/filtered-list";
|
||||||
import { ItemReference, Tag, isGroupHeader } from "@notesnook/core/dist/types";
|
import { ItemReference, Tag } from "@notesnook/core/dist/types";
|
||||||
import { ResolvedItem } from "../components/list-container/resolved-item";
|
import { ResolvedItem } from "../components/list-container/resolved-item";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
@@ -66,23 +66,17 @@ function AddTagsDialog(props: AddTagsDialogProps) {
|
|||||||
await useStore.getState().refresh();
|
await useStore.getState().refresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selected: SelectedReference[] = [];
|
const selected: SelectedReference[] = [];
|
||||||
for (const tag of tags.ids) {
|
const selectedTags = await db.relations
|
||||||
if (isGroupHeader(tag)) continue;
|
.to({ type: "note", ids: noteIds }, "tag")
|
||||||
if (selected.findIndex((a) => a.id === tag) > -1) continue;
|
.get();
|
||||||
|
selectedTags.forEach((r) => {
|
||||||
if (await tagHasNotes(tag, noteIds)) {
|
if (selected.findIndex((a) => a.id === r.fromId) > -1) return;
|
||||||
selected.push({
|
selected.push({ id: r.fromId, op: "add", new: false });
|
||||||
id: tag,
|
|
||||||
op: "add",
|
|
||||||
new: false
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
useSelectionStore.getState().setSelected(selected);
|
useSelectionStore.getState().setSelected(selected);
|
||||||
})();
|
})();
|
||||||
}, [tags]);
|
}, [tags, noteIds]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -115,10 +109,10 @@ function AddTagsDialog(props: AddTagsDialogProps) {
|
|||||||
>
|
>
|
||||||
{tags && (
|
{tags && (
|
||||||
<FilteredList
|
<FilteredList
|
||||||
getItemKey={(index, items) => items[index]}
|
getItemKey={(index) => tags.key(index)}
|
||||||
mode="fixed"
|
mode="fixed"
|
||||||
estimatedSize={30}
|
estimatedSize={30}
|
||||||
items={tags.ungrouped}
|
items={tags.ids}
|
||||||
sx={{ mt: 2 }}
|
sx={{ mt: 2 }}
|
||||||
itemGap={5}
|
itemGap={5}
|
||||||
placeholders={{
|
placeholders={{
|
||||||
@@ -132,10 +126,10 @@ function AddTagsDialog(props: AddTagsDialogProps) {
|
|||||||
const { selected, setSelected } = useSelectionStore.getState();
|
const { selected, setSelected } = useSelectionStore.getState();
|
||||||
setSelected([...selected, { id: tagId, new: true, op: "add" }]);
|
setSelected([...selected, { id: tagId, new: true, op: "add" }]);
|
||||||
}}
|
}}
|
||||||
renderItem={({ item: tagId }) => {
|
renderItem={({ item: index }) => {
|
||||||
return (
|
return (
|
||||||
<ResolvedItem key={tagId} type="tag" items={tags} id={tagId}>
|
<ResolvedItem key={index} type="tag" items={tags} index={index}>
|
||||||
{({ item }) => <TagItem tag={item} key={tagId} />}
|
{({ item }) => <TagItem tag={item} />}
|
||||||
</ResolvedItem>
|
</ResolvedItem>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ function AttachmentsDialog({ onClose }: AttachmentsDialogProps) {
|
|||||||
<Sidebar
|
<Sidebar
|
||||||
onDownloadAll={() => download(allAttachments?.ungrouped || [])}
|
onDownloadAll={() => download(allAttachments?.ungrouped || [])}
|
||||||
filter={async (query) => {
|
filter={async (query) => {
|
||||||
setAttachments(await db.lookup.attachments(query));
|
setAttachments(await db.lookup.attachments(query).sorted());
|
||||||
}}
|
}}
|
||||||
counts={counts}
|
counts={counts}
|
||||||
onRouteChange={async (route) => {
|
onRouteChange={async (route) => {
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ function SetupSMS(props: SetupAuthenticatorProps) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
action={{
|
action={{
|
||||||
disabled: error || isSending || !enabled,
|
disabled: !!error || isSending || !enabled,
|
||||||
component: (
|
component: (
|
||||||
<Text variant={"body"}>
|
<Text variant={"body"}>
|
||||||
{isSending ? (
|
{isSending ? (
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { Perform, showAddNotebookDialog } from "../common/dialog-controller";
|
|||||||
import { showToast } from "../utils/toast";
|
import { showToast } from "../utils/toast";
|
||||||
import { isMac } from "../utils/platform";
|
import { isMac } from "../utils/platform";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { Notebook, isGroupHeader } from "@notesnook/core/dist/types";
|
import { Notebook } from "@notesnook/core/dist/types";
|
||||||
import {
|
import {
|
||||||
UncontrolledTreeEnvironment,
|
UncontrolledTreeEnvironment,
|
||||||
Tree,
|
Tree,
|
||||||
@@ -47,6 +47,7 @@ import {
|
|||||||
} from "react-complex-tree";
|
} from "react-complex-tree";
|
||||||
import { FlexScrollContainer } from "../components/scroll-container";
|
import { FlexScrollContainer } from "../components/scroll-container";
|
||||||
import { pluralize } from "@notesnook/common";
|
import { pluralize } from "@notesnook/common";
|
||||||
|
import usePromise from "../hooks/use-promise";
|
||||||
|
|
||||||
type MoveDialogProps = { onClose: Perform; noteIds: string[] };
|
type MoveDialogProps = { onClose: Perform; noteIds: string[] };
|
||||||
type NotebookReference = {
|
type NotebookReference = {
|
||||||
@@ -73,18 +74,14 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
const setIsMultiselect = useSelectionStore((store) => store.setIsMultiselect);
|
const setIsMultiselect = useSelectionStore((store) => store.setIsMultiselect);
|
||||||
const isMultiselect = useSelectionStore((store) => store.isMultiselect);
|
const isMultiselect = useSelectionStore((store) => store.isMultiselect);
|
||||||
const refreshNotebooks = useStore((store) => store.refresh);
|
const refreshNotebooks = useStore((store) => store.refresh);
|
||||||
const notebooks = useStore((store) => store.notebooks);
|
// const notebooks = useStore((store) => store.notebooks);
|
||||||
const reloadItem = useRef<(changedItemIds: TreeItemIndex[]) => void>();
|
const reloadItem = useRef<(changedItemIds: TreeItemIndex[]) => void>();
|
||||||
const treeRef = useRef<TreeEnvironmentRef>(null);
|
const treeRef = useRef<TreeEnvironmentRef>(null);
|
||||||
|
const rootNotebooks = usePromise(() =>
|
||||||
|
db.notebooks.roots.ids(db.settings.getGroupOptions("notebooks"))
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!notebooks) {
|
|
||||||
(async function () {
|
|
||||||
await refreshNotebooks();
|
|
||||||
})();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for (const notebook of notebooks.ids) {
|
// for (const notebook of notebooks.ids) {
|
||||||
// if (isGroupHeader(notebook)) continue;
|
// if (isGroupHeader(notebook)) continue;
|
||||||
// // for (const topic of notebook.topics) {
|
// // for (const topic of notebook.topics) {
|
||||||
@@ -141,7 +138,7 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
// new: false
|
// new: false
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
}, [noteIds, notebooks, refreshNotebooks, setSelected, setIsMultiselect]);
|
}, [noteIds, refreshNotebooks, setSelected, setIsMultiselect]);
|
||||||
|
|
||||||
const _onClose = useCallback(
|
const _onClose = useCallback(
|
||||||
(result: boolean) => {
|
(result: boolean) => {
|
||||||
@@ -214,7 +211,8 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
Reset selection
|
Reset selection
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{notebooks && notebooks.ids.length > 0 ? (
|
{rootNotebooks.status === "fulfilled" &&
|
||||||
|
rootNotebooks.value.length > 0 ? (
|
||||||
<FlexScrollContainer>
|
<FlexScrollContainer>
|
||||||
<UncontrolledTreeEnvironment
|
<UncontrolledTreeEnvironment
|
||||||
ref={treeRef}
|
ref={treeRef}
|
||||||
@@ -235,9 +233,7 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
isFolder: true,
|
isFolder: true,
|
||||||
canMove: false,
|
canMove: false,
|
||||||
canRename: false,
|
canRename: false,
|
||||||
children: notebooks.ids.filter(
|
children: rootNotebooks.value
|
||||||
(t) => !isGroupHeader(t)
|
|
||||||
) as string[]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +261,6 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
"notebook"
|
"notebook"
|
||||||
)
|
)
|
||||||
.get();
|
.get();
|
||||||
console.log(itemIds, notebooks?.ids);
|
|
||||||
return itemIds.filter(Boolean).map((id) => {
|
return itemIds.filter(Boolean).map((id) => {
|
||||||
if (id === "root") {
|
if (id === "root") {
|
||||||
return {
|
return {
|
||||||
@@ -274,9 +269,7 @@ function MoveDialog({ onClose, noteIds }: MoveDialogProps) {
|
|||||||
isFolder: true,
|
isFolder: true,
|
||||||
canMove: false,
|
canMove: false,
|
||||||
canRename: false,
|
canRename: false,
|
||||||
children: notebooks.ids.filter(
|
children: rootNotebooks.value
|
||||||
(t) => !isGroupHeader(t)
|
|
||||||
) as string[]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import IconTag from "../components/icon-tag";
|
|||||||
import { Clock, Refresh } from "../components/icons";
|
import { Clock, Refresh } from "../components/icons";
|
||||||
import Note from "../components/note";
|
import Note from "../components/note";
|
||||||
import { getFormattedReminderTime } from "@notesnook/common";
|
import { getFormattedReminderTime } from "@notesnook/common";
|
||||||
|
import usePromise from "../hooks/use-promise";
|
||||||
|
|
||||||
export type ReminderPreviewDialogProps = {
|
export type ReminderPreviewDialogProps = {
|
||||||
onClose: Perform;
|
onClose: Perform;
|
||||||
@@ -57,9 +58,11 @@ export default function ReminderPreviewDialog(
|
|||||||
props: ReminderPreviewDialogProps
|
props: ReminderPreviewDialogProps
|
||||||
) {
|
) {
|
||||||
const { reminder } = props;
|
const { reminder } = props;
|
||||||
const referencedNotes = db.relations
|
const referencedNotes = usePromise(
|
||||||
.to({ id: reminder.id, type: "reminder" }, "note")
|
() =>
|
||||||
.resolved();
|
db.relations.to({ id: reminder.id, type: "reminder" }, "note").resolve(),
|
||||||
|
[reminder.id]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -117,17 +120,13 @@ export default function ReminderPreviewDialog(
|
|||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
{referencedNotes && referencedNotes.length > 0 && (
|
{referencedNotes &&
|
||||||
|
referencedNotes.status === "fulfilled" &&
|
||||||
|
referencedNotes.value.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Text variant="body">References:</Text>
|
<Text variant="body">References:</Text>
|
||||||
{referencedNotes.map((item, index) => (
|
{referencedNotes.value.map((item, index) => (
|
||||||
<Note
|
<Note key={item.id} item={item} date={item.dateCreated} compact />
|
||||||
key={item.id}
|
|
||||||
item={item}
|
|
||||||
date={item.dateCreated}
|
|
||||||
tags={[]}
|
|
||||||
compact
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -49,13 +49,12 @@ export const AuthenticationSettings: SettingsGroup[] = [
|
|||||||
await createBackup();
|
await createBackup();
|
||||||
const result = await showPasswordDialog(
|
const result = await showPasswordDialog(
|
||||||
"change_account_password",
|
"change_account_password",
|
||||||
async (data) => {
|
async ({ newPassword, oldPassword }) => {
|
||||||
|
if (!newPassword || !oldPassword) return false;
|
||||||
await db.user.clearSessions();
|
await db.user.clearSessions();
|
||||||
return (
|
return (
|
||||||
db.user.changePassword(
|
(await db.user.changePassword(oldPassword, newPassword)) ||
|
||||||
data.oldPassword,
|
false
|
||||||
data.newPassword
|
|
||||||
) || false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function Importer() {
|
|||||||
const notesCounter = useRef<HTMLSpanElement>(null);
|
const notesCounter = useRef<HTMLSpanElement>(null);
|
||||||
const importProgress = useRef<HTMLDivElement>(null);
|
const importProgress = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const onDrop = useCallback((acceptedFiles) => {
|
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||||
setFiles((files) => {
|
setFiles((files) => {
|
||||||
const newFiles = [...acceptedFiles, ...files];
|
const newFiles = [...acceptedFiles, ...files];
|
||||||
return newFiles;
|
return newFiles;
|
||||||
@@ -43,7 +43,9 @@ export function Importer() {
|
|||||||
|
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||||
onDrop,
|
onDrop,
|
||||||
accept: [".zip"]
|
accept: {
|
||||||
|
"application/zip": [".zip"]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function SpellCheckerLanguages() {
|
|||||||
}, [spellChecker.enabledLanguages, spellChecker.languages]);
|
}, [spellChecker.enabledLanguages, spellChecker.languages]);
|
||||||
|
|
||||||
const filter = useCallback(
|
const filter = useCallback(
|
||||||
async (query) => {
|
async (query: string) => {
|
||||||
if (!spellChecker.languages) return;
|
if (!spellChecker.languages) return;
|
||||||
setLanguages(
|
setLanguages(
|
||||||
spellChecker.languages.filter(
|
spellChecker.languages.filter(
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export const ProfileSettings: SettingsGroup[] = [
|
|||||||
title: "Delete account",
|
title: "Delete account",
|
||||||
action: async () =>
|
action: async () =>
|
||||||
showPasswordDialog("delete_account", async ({ password }) => {
|
showPasswordDialog("delete_account", async ({ password }) => {
|
||||||
|
if (!password) return false;
|
||||||
await db.user.deleteUser(password);
|
await db.user.deleteUser(password);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export function useIsUserPremium() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isUserPremium(user?: User) {
|
export function isUserPremium(user?: User) {
|
||||||
return true;
|
|
||||||
if (IS_TESTING) return true;
|
if (IS_TESTING) return true;
|
||||||
if (!user) user = userstore.get().user;
|
if (!user) user = userstore.get().user;
|
||||||
if (!user) return false;
|
if (!user) return false;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default function useSlider(
|
|||||||
}, [ref, slides, onSliding, onChange]);
|
}, [ref, slides, onSliding, onChange]);
|
||||||
|
|
||||||
const slideToIndex = useCallback(
|
const slideToIndex = useCallback(
|
||||||
(index) => {
|
(index: number) => {
|
||||||
if (!slides || !ref.current || index >= slides.length) return;
|
if (!slides || !ref.current || index >= slides.length) return;
|
||||||
console.log(slides[index].offset, slides[index].width);
|
console.log(slides[index].offset, slides[index].width);
|
||||||
const slider = ref.current;
|
const slider = ref.current;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { ProgressStream } from "../utils/streams/progress-stream";
|
|||||||
import { consumeReadableStream } from "../utils/stream";
|
import { consumeReadableStream } from "../utils/stream";
|
||||||
import { Base64DecoderStream } from "../utils/streams/base64-decoder-stream";
|
import { Base64DecoderStream } from "../utils/streams/base64-decoder-stream";
|
||||||
import { toBlob } from "@notesnook-importer/core/dist/src/utils/stream";
|
import { toBlob } from "@notesnook-importer/core/dist/src/utils/stream";
|
||||||
import { Cipher, DataFormat, SerializedKey } from "@notesnook/crypto";
|
import { DataFormat, SerializedKey } from "@notesnook/crypto";
|
||||||
import { IDataType } from "hash-wasm/dist/lib/util";
|
import { IDataType } from "hash-wasm/dist/lib/util";
|
||||||
import { IndexedDBKVStore } from "./key-value";
|
import { IndexedDBKVStore } from "./key-value";
|
||||||
import FileHandle from "@notesnook/streamable-fs/dist/src/filehandle";
|
import FileHandle from "@notesnook/streamable-fs/dist/src/filehandle";
|
||||||
@@ -44,6 +44,7 @@ import {
|
|||||||
} from "./file-store";
|
} from "./file-store";
|
||||||
import { isFeatureSupported } from "../utils/feature-check";
|
import { isFeatureSupported } from "../utils/feature-check";
|
||||||
import {
|
import {
|
||||||
|
FileEncryptionMetadataWithHash,
|
||||||
FileEncryptionMetadataWithOutputType,
|
FileEncryptionMetadataWithOutputType,
|
||||||
IFileStorage,
|
IFileStorage,
|
||||||
Output,
|
Output,
|
||||||
@@ -117,7 +118,7 @@ export async function writeEncryptedFile(
|
|||||||
return {
|
return {
|
||||||
chunkSize: CHUNK_SIZE,
|
chunkSize: CHUNK_SIZE,
|
||||||
iv: iv,
|
iv: iv,
|
||||||
length: file.size,
|
size: file.size,
|
||||||
salt: key.salt!,
|
salt: key.salt!,
|
||||||
alg: "xcha-stream"
|
alg: "xcha-stream"
|
||||||
};
|
};
|
||||||
@@ -134,15 +135,15 @@ async function writeEncryptedBase64(
|
|||||||
data: string,
|
data: string,
|
||||||
key: SerializedKey,
|
key: SerializedKey,
|
||||||
mimeType: string
|
mimeType: string
|
||||||
) {
|
): Promise<FileEncryptionMetadataWithHash> {
|
||||||
const bytes = new Uint8Array(Buffer.from(data, "base64"));
|
const bytes = new Uint8Array(Buffer.from(data, "base64"));
|
||||||
|
|
||||||
const { hash, type: hashType } = await hashBuffer(bytes);
|
const { hash, type: hashType } = await hashBuffer(bytes);
|
||||||
|
|
||||||
const attachment = db.attachments.attachment(hash);
|
const attachment = await db.attachments.attachment(hash);
|
||||||
|
|
||||||
const file = new File([bytes.buffer], hash, {
|
const file = new File([bytes.buffer], hash, {
|
||||||
type: attachment?.metadata.type || mimeType || "application/octet-stream"
|
type: attachment?.mimeType || mimeType || "application/octet-stream"
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await writeEncryptedFile(file, key, hash);
|
const result = await writeEncryptedFile(file, key, hash);
|
||||||
@@ -180,7 +181,7 @@ export async function hashStream(
|
|||||||
return { type: "xxh64", hash: hasher.digest("hex") };
|
return { type: "xxh64", hash: hasher.digest("hex") };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readEncrypted<TOutputFormat extends OutputFormat>(
|
async function readEncrypted<TOutputFormat extends DataFormat>(
|
||||||
filename: string,
|
filename: string,
|
||||||
key: SerializedKey,
|
key: SerializedKey,
|
||||||
cipherData: FileEncryptionMetadataWithOutputType<TOutputFormat>
|
cipherData: FileEncryptionMetadataWithOutputType<TOutputFormat>
|
||||||
@@ -468,7 +469,7 @@ async function downloadFile(
|
|||||||
return true;
|
return true;
|
||||||
else if (handle) await handle.delete();
|
else if (handle) await handle.delete();
|
||||||
|
|
||||||
const attachment = db.attachments?.attachment(filename);
|
const attachment = await db.attachments.attachment(filename);
|
||||||
|
|
||||||
reportProgress(
|
reportProgress(
|
||||||
{ total: 100, loaded: 0 },
|
{ total: 100, loaded: 0 },
|
||||||
@@ -510,7 +511,7 @@ async function downloadFile(
|
|||||||
|
|
||||||
const totalChunks = Math.ceil(contentLength / chunkSize);
|
const totalChunks = Math.ceil(contentLength / chunkSize);
|
||||||
const decryptedLength = contentLength - totalChunks * ABYTES;
|
const decryptedLength = contentLength - totalChunks * ABYTES;
|
||||||
if (attachment && attachment.length !== decryptedLength) {
|
if (attachment && attachment.size !== decryptedLength) {
|
||||||
const error = `File length mismatch. Please upload this file again from the attachment manager. (File hash: ${filename})`;
|
const error = `File length mismatch. Please upload this file again from the attachment manager. (File hash: ${filename})`;
|
||||||
await db.attachments.markAsFailed(filename, error);
|
await db.attachments.markAsFailed(filename, error);
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
@@ -519,7 +520,7 @@ async function downloadFile(
|
|||||||
const fileHandle = await streamablefs.createFile(
|
const fileHandle = await streamablefs.createFile(
|
||||||
filename,
|
filename,
|
||||||
decryptedLength,
|
decryptedLength,
|
||||||
attachment?.metadata.type || "application/octet-stream"
|
attachment?.mimeType || "application/octet-stream"
|
||||||
);
|
);
|
||||||
|
|
||||||
await response.body
|
await response.body
|
||||||
@@ -554,7 +555,7 @@ async function downloadFile(
|
|||||||
async function exists(filename: string) {
|
async function exists(filename: string) {
|
||||||
const handle = await streamablefs.readFile(filename);
|
const handle = await streamablefs.readFile(filename);
|
||||||
return (
|
return (
|
||||||
handle &&
|
!!handle &&
|
||||||
handle.file.size === (await handle.size()) - handle.file.chunks * ABYTES
|
handle.file.size === (await handle.size()) - handle.file.chunks * ABYTES
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
showBuyDialog,
|
showBuyDialog,
|
||||||
showCreateTagDialog,
|
showCreateTagDialog,
|
||||||
showEditReminderDialog,
|
showEditReminderDialog,
|
||||||
showEditTagDialog,
|
|
||||||
showEmailVerificationDialog,
|
showEmailVerificationDialog,
|
||||||
showFeatureDialog,
|
showFeatureDialog,
|
||||||
showOnboardingDialog,
|
showOnboardingDialog,
|
||||||
@@ -64,9 +63,6 @@ const hashroutes = defineRoutes({
|
|||||||
"/tags/create": () => {
|
"/tags/create": () => {
|
||||||
showCreateTagDialog().then(afterAction);
|
showCreateTagDialog().then(afterAction);
|
||||||
},
|
},
|
||||||
"/tags/:tagId/edit": ({ tagId }) => {
|
|
||||||
showEditTagDialog(tagId)?.then(afterAction);
|
|
||||||
},
|
|
||||||
"/notes/create": () => {
|
"/notes/create": () => {
|
||||||
closeOpenedDialog();
|
closeOpenedDialog();
|
||||||
hashNavigate("/notes/create", { addNonce: true, replace: true });
|
hashNavigate("/notes/create", { addNonce: true, replace: true });
|
||||||
|
|||||||
@@ -293,8 +293,7 @@ class AppStore extends BaseStore<AppStore> {
|
|||||||
try {
|
try {
|
||||||
const result = await db.sync({
|
const result = await db.sync({
|
||||||
type: full ? "full" : "send",
|
type: full ? "full" : "send",
|
||||||
force,
|
force
|
||||||
serverLastSynced: lastSynced
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) return this.updateSyncStatus("failed");
|
if (!result) return this.updateSyncStatus("failed");
|
||||||
|
|||||||
@@ -124,10 +124,7 @@ async function processNote(entry: ZipEntry, attachments: Record<string, any>) {
|
|||||||
for (const nb of notebooks) {
|
for (const nb of notebooks) {
|
||||||
const notebook = await importNotebook(nb).catch(() => ({ id: undefined }));
|
const notebook = await importNotebook(nb).catch(() => ({ id: undefined }));
|
||||||
if (!notebook.id) continue;
|
if (!notebook.id) continue;
|
||||||
await db.notes.addToNotebook(
|
await db.notes.addToNotebook(notebook.id, noteId);
|
||||||
{ id: notebook.id, topic: notebook.topic },
|
|
||||||
noteId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
|||||||
const file = await lazify(
|
const file = await lazify(
|
||||||
import("../../interfaces/fs"),
|
import("../../interfaces/fs"),
|
||||||
({ decryptFile }) =>
|
({ decryptFile }) =>
|
||||||
decryptFile(attachment.metadata.hash, {
|
decryptFile(attachment.hash, {
|
||||||
key,
|
key,
|
||||||
iv: attachment.iv,
|
iv: attachment.iv,
|
||||||
name: attachment.filename,
|
name: attachment.filename,
|
||||||
|
|||||||
@@ -43,27 +43,32 @@ export class WebExtensionServer implements Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getNotes(): Promise<ItemReference[] | undefined> {
|
async getNotes(): Promise<ItemReference[] | undefined> {
|
||||||
return db.notes.all
|
const notes = await db.notes.all
|
||||||
.filter((n) => !n.locked)
|
.where((eb) => eb("notes.locked", "==", false))
|
||||||
.map((note) => ({ id: note.id, title: note.title }));
|
.fields(["notes.id", "notes.title"])
|
||||||
|
.items(undefined, db.settings.getGroupOptions("notes"));
|
||||||
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNotebooks(): Promise<NotebookReference[] | undefined> {
|
async getNotebooks(
|
||||||
return db.notebooks.all.map((nb) => ({
|
parentId?: string
|
||||||
id: nb.id,
|
): Promise<NotebookReference[] | undefined> {
|
||||||
title: nb.title,
|
if (!parentId)
|
||||||
topics: nb.topics.map((topic: ItemReference) => ({
|
return await db.notebooks.roots
|
||||||
id: topic.id,
|
.fields(["notebooks.id", "notebooks.title"])
|
||||||
title: topic.title
|
.items();
|
||||||
}))
|
|
||||||
}));
|
return await db.relations
|
||||||
|
.from({ type: "notebook", id: parentId as string }, "notebook")
|
||||||
|
.selector.fields(["notebooks.id", "notebooks.title"])
|
||||||
|
.items();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTags(): Promise<ItemReference[] | undefined> {
|
async getTags(): Promise<ItemReference[] | undefined> {
|
||||||
return db.tags.all.map((tag) => ({
|
const tags = await db.tags.all
|
||||||
id: tag.id,
|
.fields(["notes.id", "notes.title"])
|
||||||
title: tag.title
|
.items(undefined, db.settings.getGroupOptions("tags"));
|
||||||
}));
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveClip(clip: Clip) {
|
async saveClip(clip: Clip) {
|
||||||
@@ -94,9 +99,10 @@ export class WebExtensionServer implements Server {
|
|||||||
}).outerHTML;
|
}).outerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
const note = clip.note?.id ? db.notes.note(clip.note?.id) : null;
|
const note = clip.note?.id ? await db.notes.note(clip.note?.id) : null;
|
||||||
|
|
||||||
let content = (await note?.content()) || "";
|
let content =
|
||||||
|
(!!note?.contentId && (await db.content.get(note.contentId))?.data) || "";
|
||||||
if (isCipher(content)) return;
|
if (isCipher(content)) return;
|
||||||
|
|
||||||
content += clipContent;
|
content += clipContent;
|
||||||
@@ -113,12 +119,9 @@ export class WebExtensionServer implements Server {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (id && clip.tags) {
|
if (id && clip.tags) {
|
||||||
for (const title of clip.tags) {
|
for (const id of clip.tags) {
|
||||||
const tagId = db.tags.tag(title)?.id || (await db.tags.add({ title }));
|
if (!(await db.tags.exists(id))) continue;
|
||||||
await db.relations.add(
|
await db.relations.add({ id: id, type: "tag" }, { id, type: "note" });
|
||||||
{ id: tagId, type: "tag" },
|
|
||||||
{ id, type: "note" }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,13 +129,7 @@ export class WebExtensionServer implements Server {
|
|||||||
for (const ref of clip.refs) {
|
for (const ref of clip.refs) {
|
||||||
switch (ref.type) {
|
switch (ref.type) {
|
||||||
case "notebook":
|
case "notebook":
|
||||||
await db.notes.addToNotebook({ id: ref.id }, id);
|
await db.notes.addToNotebook(ref.id, id);
|
||||||
break;
|
|
||||||
case "topic":
|
|
||||||
await db.notes.addToNotebook(
|
|
||||||
{ id: ref.parentId, topic: ref.id },
|
|
||||||
id
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ import {
|
|||||||
import { pluralize } from "@notesnook/common";
|
import { pluralize } from "@notesnook/common";
|
||||||
import { Allotment, AllotmentHandle } from "allotment";
|
import { Allotment, AllotmentHandle } from "allotment";
|
||||||
import { Plus } from "../components/icons";
|
import { Plus } from "../components/icons";
|
||||||
import { useStore as useNotesStore } from "../stores/note-store";
|
import {
|
||||||
|
notesFromContext,
|
||||||
|
useStore as useNotesStore
|
||||||
|
} from "../stores/note-store";
|
||||||
import { useStore as useNotebookStore } from "../stores/notebook-store";
|
import { useStore as useNotebookStore } from "../stores/notebook-store";
|
||||||
import Placeholder from "../components/placeholders";
|
import Placeholder from "../components/placeholders";
|
||||||
import { db } from "../common/db";
|
import { db } from "../common/db";
|
||||||
@@ -74,8 +77,9 @@ function Notebook(props: NotebookProps) {
|
|||||||
const filteredItems = useSearch(
|
const filteredItems = useSearch(
|
||||||
"notes",
|
"notes",
|
||||||
(query) => {
|
(query) => {
|
||||||
if (!context || !notes || context.type !== "notebook") return;
|
if (!context || context.type !== "notebook") return;
|
||||||
return db.lookup.notes(query, notes.ungrouped).sorted();
|
const notes = notesFromContext(context);
|
||||||
|
return db.lookup.notes(query, notes).sorted();
|
||||||
},
|
},
|
||||||
[context, notes]
|
[context, notes]
|
||||||
);
|
);
|
||||||
@@ -95,7 +99,7 @@ function Notebook(props: NotebookProps) {
|
|||||||
setContext({ type: "notebook", id: notebookId || rootId });
|
setContext({ type: "notebook", id: notebookId || rootId });
|
||||||
}, [rootId, notebookId]);
|
}, [rootId, notebookId]);
|
||||||
|
|
||||||
const toggleCollapse = useCallback((isCollapsed) => {
|
const toggleCollapse = useCallback((isCollapsed: boolean) => {
|
||||||
if (!paneRef.current || !sizes.current) return;
|
if (!paneRef.current || !sizes.current) return;
|
||||||
|
|
||||||
if (!isCollapsed) {
|
if (!isCollapsed) {
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ export type ItemReference = {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NotebookReference = ItemReference & {
|
export type NotebookReference = ItemReference;
|
||||||
topics: ItemReference[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ClientMetadata = {
|
export type ClientMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -76,7 +74,7 @@ export type Clip = {
|
|||||||
export interface Server {
|
export interface Server {
|
||||||
login(): Promise<User | null>;
|
login(): Promise<User | null>;
|
||||||
getNotes(): Promise<ItemReference[] | undefined>;
|
getNotes(): Promise<ItemReference[] | undefined>;
|
||||||
getNotebooks(): Promise<NotebookReference[] | undefined>;
|
getNotebooks(parentId?: string): Promise<NotebookReference[] | undefined>;
|
||||||
getTags(): Promise<ItemReference[] | undefined>;
|
getTags(): Promise<ItemReference[] | undefined>;
|
||||||
saveClip(clip: Clip): Promise<void>;
|
saveClip(clip: Clip): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ function Notebook(props: NotebookProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FilteredList
|
{/* <FilteredList
|
||||||
getAll={() => notebook.topics}
|
getAll={() => notebook.topics}
|
||||||
itemName="topic"
|
itemName="topic"
|
||||||
placeholder={"Search for a topic"}
|
placeholder={"Search for a topic"}
|
||||||
@@ -188,7 +188,7 @@ function Notebook(props: NotebookProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/> */}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export default class Vault {
|
|||||||
|
|
||||||
async exists(vaultKey?: Cipher<"base64">) {
|
async exists(vaultKey?: Cipher<"base64">) {
|
||||||
if (!vaultKey) vaultKey = await this.getKey();
|
if (!vaultKey) vaultKey = await this.getKey();
|
||||||
return vaultKey && isCipher(vaultKey);
|
return !!vaultKey && isCipher(vaultKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private & internal methods
|
// Private & internal methods
|
||||||
|
|||||||
@@ -62,8 +62,10 @@ type EncryptedBackupFile = BaseBackupFile & {
|
|||||||
encrypted: true;
|
encrypted: true;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BackupFile = UnencryptedBackupFile | EncryptedBackupFile;
|
export type BackupFile = UnencryptedBackupFile | EncryptedBackupFile;
|
||||||
type LegacyBackupFile = LegacyUnencryptedBackupFile | LegacyEncryptedBackupFile;
|
export type LegacyBackupFile =
|
||||||
|
| LegacyUnencryptedBackupFile
|
||||||
|
| LegacyEncryptedBackupFile;
|
||||||
|
|
||||||
type BackupState = {
|
type BackupState = {
|
||||||
buffer: string[];
|
buffer: string[];
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
export * from "./types";
|
export * from "./types";
|
||||||
export { VirtualizedGrouping } from "./utils/virtualized-grouping";
|
export { VirtualizedGrouping } from "./utils/virtualized-grouping";
|
||||||
export { DefaultColors } from "./collections/colors";
|
export { DefaultColors } from "./collections/colors";
|
||||||
|
export { type BackupFile, type LegacyBackupFile } from "./database/backup";
|
||||||
|
|||||||
Reference in New Issue
Block a user