mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-30 18:48:27 +02:00
Compare commits
14 Commits
fix/io-err
...
3.0.20-and
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3750dd38dc | ||
|
|
1506cc257d | ||
|
|
fe7c7889e8 | ||
|
|
3f54eee11d | ||
|
|
11b5922c2c | ||
|
|
71b85d2a4d | ||
|
|
289832d166 | ||
|
|
a88e612c35 | ||
|
|
bda456362b | ||
|
|
f6f644c55d | ||
|
|
08c92333d6 | ||
|
|
4d72b77f29 | ||
|
|
4f9d06737f | ||
|
|
bfc494a3d4 |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Notesnook Discord Community
|
||||
url: https://discord.gg/6mHHyncHJE
|
||||
url: https://go.notesnook.com/discord
|
||||
about: Reach out to us directly & discuss your issues, suggestions & other feedback!
|
||||
|
||||
@@ -126,9 +126,13 @@ export async function clearFileStorage() {
|
||||
}
|
||||
|
||||
export async function createCacheDir() {
|
||||
if (!(await RNFetchBlob.fs.exists(cacheDir))) {
|
||||
await RNFetchBlob.fs.mkdir(cacheDir);
|
||||
DatabaseLogger.log("Cache directory created");
|
||||
try {
|
||||
if (!(await RNFetchBlob.fs.exists(cacheDir))) {
|
||||
await RNFetchBlob.fs.mkdir(cacheDir);
|
||||
DatabaseLogger.log("Cache directory created");
|
||||
}
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,22 +235,30 @@ export async function exists(filename) {
|
||||
}
|
||||
|
||||
export async function bulkExists(files) {
|
||||
const cacheFiles = await RNFetchBlob.fs.ls(cacheDir);
|
||||
let missingFiles = files.filter((file) => !cacheFiles.includes(file));
|
||||
try {
|
||||
await createCacheDir();
|
||||
const cacheFiles = await RNFetchBlob.fs.ls(cacheDir);
|
||||
let missingFiles = files.filter((file) => !cacheFiles.includes(file));
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupFiles = await RNFetchBlob.fs.ls(iosAppGroup);
|
||||
missingFiles = missingFiles.filter((file) => !appGroupFiles.includes(file));
|
||||
if (Platform.OS === "ios") {
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupFiles = await RNFetchBlob.fs.ls(iosAppGroup);
|
||||
missingFiles = missingFiles.filter(
|
||||
(file) => !appGroupFiles.includes(file)
|
||||
);
|
||||
}
|
||||
return missingFiles;
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
return [];
|
||||
}
|
||||
|
||||
return missingFiles;
|
||||
}
|
||||
|
||||
export async function getCacheSize() {
|
||||
await createCacheDir();
|
||||
const stat = await RNFetchBlob.fs.lstat(`file://` + cacheDir);
|
||||
let total = 0;
|
||||
console.log("Total files", stat.length);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { IOS_APPGROUPID } from "../../utils/constants";
|
||||
import { DatabaseLogger, db } from "../database";
|
||||
import { createCacheDir } from "./io";
|
||||
import { cacheDir, getUploadedFileSize } from "./utils";
|
||||
import { cacheDir, checkUpload, getUploadedFileSize } from "./utils";
|
||||
|
||||
export async function uploadFile(filename, requestOptions, cancelToken) {
|
||||
if (!requestOptions) return false;
|
||||
@@ -33,24 +33,42 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
|
||||
DatabaseLogger.info(`Preparing to upload file: ${filename}`);
|
||||
|
||||
try {
|
||||
const uploadedFileSize = await getUploadedFileSize(filename);
|
||||
|
||||
if (uploadedFileSize === -1) {
|
||||
const error = `Uploaded file verification failed. (File hash: ${filename})`;
|
||||
throw new Error(error);
|
||||
let filePath = `${cacheDir}/${filename}`;
|
||||
let exists = await RNFetchBlob.fs.exists(filePath);
|
||||
// Check for file in appGroupPath if it doesn't exist in cacheDir
|
||||
if (!exists && Platform.OS === "ios") {
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupPath = `${iosAppGroup}/${filename}`;
|
||||
filePath = appGroupPath;
|
||||
exists = await RNFetchBlob.fs.exists(filePath);
|
||||
}
|
||||
|
||||
if (uploadedFileSize !== 0) {
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
`Trying to upload file at path ${filePath} that doest not exist.`
|
||||
);
|
||||
}
|
||||
|
||||
const fileSize = (await RNFetchBlob.fs.stat(filePath)).size;
|
||||
|
||||
let remoteFileSize = await getUploadedFileSize(filename);
|
||||
if (remoteFileSize === -1) return false;
|
||||
if (remoteFileSize > 0 && remoteFileSize === fileSize) {
|
||||
DatabaseLogger.log(`File ${filename} is already uploaded.`);
|
||||
return true;
|
||||
}
|
||||
|
||||
let res = await fetch(url, {
|
||||
let uploadUrlResponse = await fetch(url, {
|
||||
method: "PUT",
|
||||
headers
|
||||
});
|
||||
|
||||
const uploadUrl = res.ok ? await res.text() : await res.json();
|
||||
const uploadUrl = uploadUrlResponse.ok
|
||||
? await uploadUrlResponse.text()
|
||||
: await uploadUrlResponse.json();
|
||||
|
||||
if (typeof uploadUrl !== "string") {
|
||||
throw new Error(
|
||||
@@ -58,28 +76,9 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
|
||||
);
|
||||
}
|
||||
|
||||
let uploadFilePath = `${cacheDir}/${filename}`;
|
||||
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupPath = `${iosAppGroup}/${filename}`;
|
||||
let exists = await RNFetchBlob.fs.exists(uploadFilePath);
|
||||
if (!exists && Platform.OS === "ios") {
|
||||
uploadFilePath = appGroupPath;
|
||||
exists = await RNFetchBlob.fs.exists(uploadFilePath);
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
`Trying to upload file at path ${uploadFilePath} that doest not exist.`
|
||||
);
|
||||
}
|
||||
|
||||
DatabaseLogger.info(`Starting upload: ${filename}`);
|
||||
|
||||
let request = RNFetchBlob.config({
|
||||
let uploadRequest = RNFetchBlob.config({
|
||||
IOSBackgroundTask: !globalThis["IS_SHARE_EXTENSION"]
|
||||
})
|
||||
.fetch(
|
||||
@@ -88,7 +87,7 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
|
||||
{
|
||||
"content-type": ""
|
||||
},
|
||||
RNFetchBlob.wrap(uploadFilePath)
|
||||
RNFetchBlob.wrap(filePath)
|
||||
)
|
||||
.uploadProgress((sent, total) => {
|
||||
useAttachmentStore
|
||||
@@ -101,30 +100,27 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
|
||||
|
||||
cancelToken.cancel = () => {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
request.cancel();
|
||||
uploadRequest.cancel();
|
||||
};
|
||||
let response = await request;
|
||||
|
||||
let status = response.info().status;
|
||||
let text = await response.text();
|
||||
let result = status >= 200 && status < 300 && text.length === 0;
|
||||
let uploadResponse = await uploadRequest;
|
||||
let status = uploadResponse.info().status;
|
||||
let uploaded = status >= 200 && status < 300;
|
||||
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
if (result) {
|
||||
DatabaseLogger.info(
|
||||
`File upload status: ${filename}, ${status}, ${text}`
|
||||
);
|
||||
let attachment = await db.attachments.attachment(filename);
|
||||
if (!attachment) return result;
|
||||
} else {
|
||||
const fileInfo = await RNFetchBlob.fs.stat(uploadFilePath);
|
||||
|
||||
if (!uploaded) {
|
||||
const fileInfo = await RNFetchBlob.fs.stat(filePath);
|
||||
throw new Error(
|
||||
`${status}, ${text}, name: ${fileInfo.filename}, length: ${
|
||||
`${status}, name: ${fileInfo.filename}, length: ${
|
||||
fileInfo.size
|
||||
}, info: ${JSON.stringify(response.info())}`
|
||||
}, info: ${JSON.stringify(uploadResponse.info())}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
const attachment = await db.attachments.attachment(filename);
|
||||
await checkUpload(filename, requestOptions.chunkSize, attachment.size);
|
||||
DatabaseLogger.info(`File upload status: ${filename}, ${status}`);
|
||||
return uploaded;
|
||||
} catch (e) {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
ToastManager.error(e, "File upload failed");
|
||||
|
||||
@@ -123,3 +123,18 @@ export async function getUploadedFileSize(hash) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkUpload(filename, chunkSize, expectedSize) {
|
||||
const size = await getUploadedFileSize(filename);
|
||||
const totalChunks = Math.ceil(size / chunkSize);
|
||||
const decryptedLength = size - totalChunks * ABYTES;
|
||||
const error =
|
||||
size === 0
|
||||
? `File size is 0.`
|
||||
: size === -1
|
||||
? `File verification check failed.`
|
||||
: expectedSize !== decryptedLength
|
||||
? `File size mismatch. Expected ${size} bytes but got ${decryptedLength} bytes.`
|
||||
: undefined;
|
||||
if (error) throw new Error(error);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { GroupHeader, GroupOptions, ItemType } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
@@ -26,13 +27,11 @@ import { presentSheet } from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { RouteName } from "../../../stores/use-navigation-store";
|
||||
import { getContainerBorder } from "../../../utils/colors";
|
||||
import { GROUP } from "../../../utils/constants";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import Sort from "../../sheets/sort";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type SectionHeaderProps = {
|
||||
item: GroupHeader;
|
||||
@@ -58,17 +57,14 @@ export const SectionHeader = React.memo<
|
||||
}: SectionHeaderProps) {
|
||||
const { colors } = useThemeColors();
|
||||
const { fontScale } = useWindowDimensions();
|
||||
let groupBy = Object.keys(GROUP).find(
|
||||
(key) => GROUP[key as keyof typeof GROUP] === groupOptions.groupBy
|
||||
);
|
||||
const groupBy =
|
||||
strings.groupByStrings[
|
||||
groupOptions.groupBy as keyof typeof strings.groupByStrings
|
||||
]?.();
|
||||
const isCompactModeEnabled = useIsCompactModeEnabled(
|
||||
dataType as "note" | "notebook"
|
||||
);
|
||||
|
||||
groupBy = !groupBy
|
||||
? "Default"
|
||||
: groupBy.slice(0, 1).toUpperCase() + groupBy.slice(1, groupBy.length);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
@@ -118,12 +114,10 @@ export const SectionHeader = React.memo<
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
console.log("Opening Sort sheet", screen, dataType);
|
||||
presentSheet({
|
||||
component: <Sort screen={screen} type={dataType} />
|
||||
});
|
||||
}}
|
||||
tooltipText="Change sorting of items in list"
|
||||
title={groupBy}
|
||||
icon={
|
||||
groupOptions.sortDirection === "asc"
|
||||
@@ -157,11 +151,6 @@ export const SectionHeader = React.memo<
|
||||
screen !== "Notes"
|
||||
}
|
||||
testID="icon-compact-mode"
|
||||
tooltipText={
|
||||
isCompactModeEnabled
|
||||
? "Switch to normal mode"
|
||||
: "Switch to compact mode"
|
||||
}
|
||||
color={colors.secondary.icon}
|
||||
name={isCompactModeEnabled ? "view-list" : "view-list-outline"}
|
||||
onPress={() => {
|
||||
|
||||
@@ -32,7 +32,7 @@ import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { InteractionManager, Platform } from "react-native";
|
||||
import Share from "react-native-share";
|
||||
import { db } from "../common/database";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
import { AttachmentDialog } from "../components/attachments";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import NoteHistory from "../components/note-history";
|
||||
@@ -650,35 +650,40 @@ export const useActions = ({
|
||||
}
|
||||
|
||||
async function shareNote() {
|
||||
if (item.type !== "note") return;
|
||||
|
||||
if (processingId.current === "shareNote") {
|
||||
ToastManager.show({
|
||||
heading: strings.pleaseWait() + "...",
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!checkItemSynced()) return;
|
||||
if (locked) {
|
||||
close();
|
||||
await sleep(300);
|
||||
openVault({
|
||||
item: item,
|
||||
novault: true,
|
||||
locked: true,
|
||||
share: true,
|
||||
title: strings.shareNote()
|
||||
});
|
||||
} else {
|
||||
processingId.current = "shareNote";
|
||||
const convertedText = await convertNoteToText(item);
|
||||
try {
|
||||
if (item.type !== "note") return;
|
||||
if (processingId.current === "shareNote") {
|
||||
ToastManager.show({
|
||||
heading: strings.pleaseWait() + "...",
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!checkItemSynced()) return;
|
||||
if (locked) {
|
||||
close();
|
||||
await sleep(300);
|
||||
openVault({
|
||||
item: item,
|
||||
novault: true,
|
||||
locked: true,
|
||||
share: true,
|
||||
title: strings.shareNote()
|
||||
});
|
||||
} else {
|
||||
processingId.current = "shareNote";
|
||||
const convertedText = await convertNoteToText(item);
|
||||
processingId.current = undefined;
|
||||
Share.open({
|
||||
title: strings.shareNote(),
|
||||
failOnCancel: false,
|
||||
message: convertedText || ""
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error);
|
||||
DatabaseLogger.error(e);
|
||||
processingId.current = undefined;
|
||||
Share.open({
|
||||
title: strings.shareNote(),
|
||||
failOnCancel: false,
|
||||
message: convertedText || ""
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,7 +766,8 @@ export const useActions = ({
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
processingId.current = undefined;
|
||||
DatabaseLogger.error(e);
|
||||
ToastManager.error(e as Error);
|
||||
}
|
||||
}
|
||||
@@ -769,7 +775,7 @@ export const useActions = ({
|
||||
actions.push(
|
||||
{
|
||||
id: "favorite",
|
||||
title: item.favorite ? "Unfav" : "Fav",
|
||||
title: item.favorite ? strings.favorite() : strings.unfavorite(),
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
@@ -949,8 +955,8 @@ export const useActions = ({
|
||||
id: "trash",
|
||||
title:
|
||||
item.type !== "notebook" && item.type !== "note"
|
||||
? "Delete " + item.type
|
||||
: "Move to trash",
|
||||
? strings.doAction(item.type, 1, "delete")
|
||||
: strings.moveToTrash(),
|
||||
icon: "delete-outline",
|
||||
type: "error",
|
||||
func: deleteItem
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
} from "@notesnook/common";
|
||||
import { Note } from "@notesnook/core";
|
||||
import { FilteredSelector } from "@notesnook/core";
|
||||
import { basename, dirname, join } from "pathe";
|
||||
import { basename, dirname, join, extname } from "pathe";
|
||||
import downloadAttachment from "../common/filesystem/download-attachment";
|
||||
import { cacheDir } from "../common/filesystem/utils";
|
||||
import { unlockVault } from "../utils/unlock-vault";
|
||||
@@ -326,10 +326,18 @@ async function createFile(
|
||||
await copyFileAsync("file://" + exportedFile, file.uri);
|
||||
filePath = file.uri;
|
||||
} else {
|
||||
filePath = join(path, basename(noteItem.path));
|
||||
const originalPath = join(path, basename(noteItem.path));
|
||||
filePath = originalPath;
|
||||
const ext = extname(originalPath);
|
||||
let id = 1;
|
||||
while (await RNFetchBlob.fs.exists(filePath)) {
|
||||
filePath = originalPath.replace(`${ext}`, "") + "_" + id + ext;
|
||||
id++;
|
||||
}
|
||||
console.log("path", filePath);
|
||||
await RNFetchBlob.fs.mv(exportedFile, filePath);
|
||||
}
|
||||
|
||||
console.log("file moved...");
|
||||
return {
|
||||
filePath: filePath,
|
||||
fileDir: path,
|
||||
|
||||
@@ -15,13 +15,39 @@ import { DOMParser } from './worker.js';
|
||||
global.DOMParser = DOMParser;
|
||||
import {setI18nGlobal } from "@notesnook/intl";
|
||||
import { i18n } from "@lingui/core";
|
||||
import { ScriptManager, Script } from '@callstack/repack/client';
|
||||
import {
|
||||
messages as $en
|
||||
} from "@notesnook/intl/locales/$en.json";
|
||||
import {
|
||||
messages as $pseudo
|
||||
} from "@notesnook/intl/locales/$pseudo-LOCALE.json";
|
||||
|
||||
i18n.load({
|
||||
en: $en,
|
||||
en: __DEV__ ? $pseudo : $en
|
||||
});
|
||||
setI18nGlobal(i18n);
|
||||
i18n.activate("en");
|
||||
setI18nGlobal(i18n);
|
||||
|
||||
|
||||
try {
|
||||
ScriptManager.shared.addResolver(async (scriptId) => {
|
||||
// `scriptId` will be either 'student' or 'teacher'
|
||||
|
||||
// In dev mode, resolve script location to dev server.
|
||||
if (__DEV__) {
|
||||
return {
|
||||
url: Script.getDevServerURL(scriptId),
|
||||
cache: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
url: Script.getFileSystemURL(scriptId)
|
||||
};
|
||||
});
|
||||
|
||||
} catch(e) {
|
||||
/** ignore error when running with metro bundler */
|
||||
}
|
||||
@@ -121,6 +121,7 @@
|
||||
"ts-jest": "^29.1.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack": "^5.88.2",
|
||||
"metro-react-native-babel-preset": "0.77.0"
|
||||
"metro-react-native-babel-preset": "0.77.0",
|
||||
"acorn-import-attributes": "1.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
14
apps/mobile/package-lock.json
generated
14
apps/mobile/package-lock.json
generated
@@ -26,8 +26,7 @@
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5",
|
||||
"react-native-actions-sheet": "^0.9.7",
|
||||
"react-native-mmkv-storage": "^0.10.2",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot"
|
||||
"react-native-mmkv-storage": "^0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fonteditor-core": "^2.1.11",
|
||||
@@ -29521,6 +29520,7 @@
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
|
||||
"react-native-pdf": "6.6.2",
|
||||
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
"react-native-reanimated": "3.14.0",
|
||||
"react-native-safe-area-context": "^4.10.8",
|
||||
@@ -29558,6 +29558,7 @@
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||
"@typescript-eslint/parser": "^5.14.0",
|
||||
"acorn-import-attributes": "1.9.5",
|
||||
"babel-jest": "^29.6.3",
|
||||
"babel-loader": "^8.2.5",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
@@ -35397,6 +35398,15 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-attributes": {
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
|
||||
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"dev": true,
|
||||
|
||||
@@ -230,7 +230,7 @@ export const SettingsDialog = DialogManager.register(function SettingsDialog(
|
||||
flexDirection: "column",
|
||||
padding: 20,
|
||||
gap: 20,
|
||||
minHeight: "min-content",
|
||||
minHeight: "auto",
|
||||
overflow: "auto"
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
import {
|
||||
EventTypes,
|
||||
getRoot,
|
||||
isReactNative,
|
||||
post,
|
||||
postAsyncWithTimeout,
|
||||
saveTheme
|
||||
@@ -157,6 +158,7 @@ export function useEditorController({
|
||||
const selectionChange = useCallback((_editor: Editor) => {}, []);
|
||||
|
||||
const titleChange = useCallback(async (title: string) => {
|
||||
if (!isReactNative()) return;
|
||||
const currentSessionId = globalThis.sessionId;
|
||||
post(
|
||||
EventTypes.contentchange,
|
||||
@@ -215,6 +217,7 @@ export function useEditorController({
|
||||
|
||||
const contentChange = useCallback(
|
||||
(editor: Editor, ignoreEdit?: boolean) => {
|
||||
if (!isReactNative()) return;
|
||||
if (editorControllers[tabRef.current.id]?.loading) {
|
||||
logger("info", "Edit skipped, tab is in loading state");
|
||||
return;
|
||||
|
||||
@@ -44,11 +44,9 @@ export class PortalProviderAPI extends EventDispatcher<Portals> {
|
||||
}
|
||||
|
||||
render(Component: FunctionComponent, container: HTMLElement) {
|
||||
queueMicrotask(() => {
|
||||
const root = this.roots.get(container) || createRoot(container);
|
||||
flushSync(() => root.render(<Component />));
|
||||
this.roots.set(container, root);
|
||||
});
|
||||
const root = this.roots.get(container) || createRoot(container);
|
||||
flushSync(() => root.render(<Component />));
|
||||
this.roots.set(container, root);
|
||||
}
|
||||
|
||||
remove(container: HTMLElement) {
|
||||
|
||||
@@ -150,11 +150,12 @@ export function TablePopup(props: TablePopupProps) {
|
||||
display: ["flex", "none", "none"],
|
||||
mt: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
justifyContent: "center",
|
||||
maxWidth: "100%"
|
||||
}}
|
||||
>
|
||||
<InlineInput
|
||||
containerProps={{ sx: { mr: 1 } }}
|
||||
containerProps={{ sx: { mr: 1, flexShrink: 1 } }}
|
||||
label="columns"
|
||||
placeholder={`${cellLocation.column} columns`}
|
||||
type="number"
|
||||
@@ -167,6 +168,7 @@ export function TablePopup(props: TablePopupProps) {
|
||||
}}
|
||||
/>
|
||||
<InlineInput
|
||||
containerProps={{ sx: { flexShrink: 1 } }}
|
||||
label="rows"
|
||||
placeholder={`${cellLocation.row} rows`}
|
||||
type="number"
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.ProseMirror span.math-inline {
|
||||
font-family: KaTeX_Main, Times New Roman, serif;
|
||||
}
|
||||
|
||||
.ProseMirror {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -495,7 +495,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
},
|
||||
regenerate: () => t`Regenerate`,
|
||||
redo: () => t`Redo`,
|
||||
createYourAccount: () => t`Create your {"\n"}account`,
|
||||
createYourAccount: () => t`Create your account`,
|
||||
pinned: () => t`Pinned`,
|
||||
editNotebook: () => t`Edit notebook`,
|
||||
newNotebook: () => t`New notebook`,
|
||||
@@ -513,7 +513,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
appliedDark: () => t`Applied as dark theme`,
|
||||
appliedLight: () => t`Applied as light theme`,
|
||||
basic: () => t`Basic`,
|
||||
loginToYourAccount: () => t`Login to your {"\n"}account`,
|
||||
loginToYourAccount: () => t`Login to your account`,
|
||||
continue: () => t`Continue`,
|
||||
unlockWithBiometrics: () => t`Unlock with biometrics`,
|
||||
fileCheck: () => t`Run file check`,
|
||||
@@ -525,7 +525,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
changePasswordConfirm: () => t`I understand, change my password`,
|
||||
next: () => t`Next`,
|
||||
forgotPassword: () => t`Forgot password?`,
|
||||
cancelLogin: "Cancel login",
|
||||
cancelLogin: () => t`Cancel login`,
|
||||
logoutFromDevice: () => t`Logout from this device`,
|
||||
useAccountPassword: () => t`Use account password`,
|
||||
addColor: () => t`Add color`,
|
||||
@@ -1069,12 +1069,20 @@ $headline$: Use starting line of the note as title.`,
|
||||
t`Sync your notes in the background even when the app is closed. This is an experimental feature. If you face any issues, please turn it off.`,
|
||||
forcePullChanges: () => t`Force pull changes`,
|
||||
forcePullChangesDesc: () =>
|
||||
t`Use this if changes from other devices are not appearing on this device. This will overwrite the data on this device with the latest data from the server.\n\nThis must only be used for troubleshooting. Using it regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co.`,
|
||||
[
|
||||
t`Use this if changes from other devices are not appearing on this device. This will overwrite the data on this device with the latest data from the server.`,
|
||||
"",
|
||||
t`This must only be used for troubleshooting. Using it regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co.`
|
||||
].join("\n"),
|
||||
forceSyncNotice: () =>
|
||||
`This must only be used for troubleshooting. Using this regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co.`,
|
||||
forcePushChanges: () => t`Force push changes`,
|
||||
forcePushChangesDesc: () =>
|
||||
t`Use this if changes made on this device are not appearing on other devices. This will overwrite the data on the server with the data from this device.\n\nThis must only be used for troubleshooting. Using it regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co.`,
|
||||
[
|
||||
t`Use this if changes made on this device are not appearing on other devices. This will overwrite the data on the server with the data from this device.`,
|
||||
"",
|
||||
t`This must only be used for troubleshooting. Using it regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co.`
|
||||
].join("\n"),
|
||||
start: () => t`Start`,
|
||||
customization: () => t`Customization`,
|
||||
appearance: () => t`Appearance`,
|
||||
|
||||
Reference in New Issue
Block a user