Compare commits

...

27 Commits

Author SHA1 Message Date
ammarahm-ed
57a3b73ada mobile: sync quick notes only when online 2023-05-03 10:55:35 +05:00
ammarahm-ed
5f6a38c8b1 mobile: release 2.4.15 2023-04-29 09:07:46 +05:00
Muhammad Ali
3496e6bfa9 editor: increased hit Area 2023-04-29 08:33:20 +05:00
ammarahm-ed
fc5b62ba61 mobile: remove editor flicker on app launch 2023-04-29 08:29:48 +05:00
ammarahm-ed
9d0a206240 mobile: fix editor not rendered when enter foreground 2023-04-29 08:29:48 +05:00
ammarahm-ed
ee6bbca18c mobile: only call verify biometrics once on focus 2023-04-29 05:47:05 +05:00
ammarahm-ed
e6d5472514 mobile: fix rerender crash 2023-04-29 05:46:06 +05:00
Abdullah Atta
1e5f13087b web: try to reconnect sync on window focus 2023-04-28 10:39:59 +05:00
Abdullah Atta
abb89ecbc3 web: bump version to 2.4.10 2023-04-27 15:15:46 +05:00
Abdullah Atta
aa168bec5e desktop: revert "desktop: remove custom protocol interception for https requests" 2023-04-27 15:14:30 +05:00
Abdullah Atta
f9116b22db web: bump version to 2.4.9 2023-04-27 13:16:01 +05:00
Abdullah Atta
5b957f5f0d web: fix Can't resolve './network.worker' error 2023-04-27 13:08:13 +05:00
Abdullah Atta
a578aae3dc web: wait for internet before running sync on device wake 2023-04-27 13:08:13 +05:00
Abdullah Atta
43e5d2fa2d editor: fix table properties tooltip text 2023-04-27 13:08:03 +05:00
Abdullah Atta
b58d034716 theme: use outline instead of boxShadow for input border 2023-04-27 12:59:26 +05:00
Abdullah Atta
3b77d79997 web: prevent pro users from resubscribing 2023-04-27 12:59:26 +05:00
Abdullah Atta
ef4c9af7dc web: rename setters from onXYZ to xyz 2023-04-27 12:59:26 +05:00
ammarahm-ed
8ba3cf172e mobile: release v2.4.14 2023-04-27 12:49:02 +05:00
ammarahm-ed
3e446ce9dd mobile: fix crash on get search items 2023-04-27 11:27:48 +05:00
ammarahm-ed
394aa98713 mobile: remove usage of lazy imports 2023-04-27 10:59:04 +05:00
Muhammad Ali
a2ac882a05 desktop: remove custom protocol interception for https requests (#2426) 2023-04-27 10:13:37 +05:00
ammarahm-ed
cae36487f9 mobile: fix notebook not updated on note 2023-04-27 09:38:44 +05:00
ammarahm-ed
fade5eddbf mobile: replace newline with paragraph in generated html 2023-04-27 08:32:09 +05:00
ammarahm-ed
66b2ffce31 mobile: fix webpack config 2023-04-27 08:32:09 +05:00
ammarahm-ed
13cd2d4daa mobile: refactor 2023-04-27 08:18:25 +05:00
ammarahm-ed
8442faeeb1 mobile: close editor on navigate to topic 2023-04-26 20:06:40 +05:00
Ammar Ahmed
6d18383b22 mobile: sync notes created from quick note/notification notes (#2428)
* mobile: sync quick notes

* Update apps/mobile/app/services/notifications.ts

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>

* mobile: ensure full db init when sync is required

---------

Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-04-26 15:15:08 +05:00
60 changed files with 367 additions and 312 deletions

View File

@@ -31,14 +31,6 @@ import { TipManager } from "./services/tip-manager";
import { useUserStore } from "./stores/use-user-store";
import { View } from "react-native";
import { useState } from "react";
import NetInfo from "@react-native-community/netinfo";
NetInfo.configure({
reachabilityUrl: "https://notesnook.com",
reachabilityTest: (response) => {
if (!response) return false;
return response?.status >= 200 && response?.status < 300;
}
});
SettingsService.init();
SettingsService.checkOrientation();

View File

@@ -148,7 +148,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
<Button
key={item.title}
title={item.title}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
type="gray"
onPress={() => onPress(item)}
width={null}

View File

@@ -67,7 +67,7 @@ export const Title = ({ text, style = {}, inline }) => {
right: 0
}}
iconSize={24}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
borderRadius: 100,
paddingVertical: 0,

View File

@@ -209,7 +209,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
>
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
marginRight: 10
}}
@@ -221,7 +221,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{formatBytes(attachment.length)}
@@ -232,7 +232,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{attachment.noteIds.length} note
@@ -248,7 +248,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
context: "local"
});
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{attachment.metadata.hash}
@@ -304,7 +304,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
key={item.id}
>
<Paragraph size={SIZE.xs + 1}>{item.title}</Paragraph>
<Paragraph size={SIZE.xs}>{item.title}</Paragraph>
</PressableButton>
))}
</>

View File

@@ -22,7 +22,6 @@ import { TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils";
import { SIZE } from "../../utils/size";
@@ -47,9 +46,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
attachment,
encryption
);
const encryptionProgress = useAttachmentStore(
(state) => state.encryptionProgress
);
const onPress = () => {
Actions.present(attachment, setAttachments, attachment.metadata.hash);
};
@@ -125,9 +122,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
</View>
</View>
{currentProgress ||
(encryptionProgress && encryptionProgress !== "0.00") ||
encryption ? (
{currentProgress ? (
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
@@ -144,13 +139,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
>
<ProgressCircleComponent
size={SIZE.xxl}
progress={
encryptionProgress
? encryptionProgress
: currentProgress?.value
? currentProgress?.value / 100
: 0
}
progress={currentProgress?.value ? currentProgress?.value / 100 : 0}
showsText
textStyle={{
fontSize: 10

View File

@@ -86,7 +86,7 @@ export const ProFeatures = ({ count = 6 }) => {
await sleep(300);
eSendEvent(eOpenPremiumDialog);
}}
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
textDecorationLine: "underline",
color: colors.icon

View File

@@ -70,6 +70,7 @@ const Launcher = React.memo(
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const verifying = useRef(false);
const loadNotes = useCallback(async () => {
if (verifyUser) {
@@ -206,6 +207,9 @@ const Launcher = React.memo(
setVerifyUser(false);
enabled(false);
password.current = null;
setTimeout(() => {
verifying.current = false;
}, 1);
}
}, [setVerifyUser]);
@@ -214,7 +218,9 @@ const Launcher = React.memo(
}, [init, verifyUser]);
useEffect(() => {
if (verifying.current) return;
if (verifyUser && appState === "active") {
verifying.current = true;
onUnlockBiometrics();
}
}, [appState, onUnlockBiometrics, verifyUser]);

View File

@@ -108,7 +108,7 @@ const ReminderItem = React.memo(
color={colors.errorText}
/>
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -133,7 +133,7 @@ const ReminderItem = React.memo(
>
<Icon name="reload" size={SIZE.md} color={colors.accent} />
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -147,7 +147,7 @@ const ReminderItem = React.memo(
<ReminderTime
reminder={item}
checkIsActive={false}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
justifyContent: "flex-start",
borderWidth: 0,

View File

@@ -143,7 +143,7 @@ export const Expiring = () => {
await sleep(300);
eSendEvent(eOpenPremiumDialog, promo);
}}
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
textDecorationLine: "underline",
color: colors.icon,

View File

@@ -62,10 +62,10 @@ export const DateMeta = ({ item }) => {
paddingVertical: 3
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{getNameFromKey(key)}
</Paragraph>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{timeConverter(item[key])}
</Paragraph>
</View>

View File

@@ -108,7 +108,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
backgroundColor: "transparent",
paddingHorizontal: 0
}}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
/>
) : null}
</View>

View File

@@ -115,12 +115,12 @@ export const Items = ({ item, buttons, close }) => {
onPress={item.func}
key={item.id}
testID={"icon-" + item.id}
activeOpacity={1}
customStyle={{
alignSelf: "flex-start",
width: topBarItemWidth,
marginRight: isLast ? 0 : 10,
backgroundColor: "transparent",
paddingHorizontal: 0
paddingHorizontal: 0,
width: topBarItemWidth
}}
>
<PressableButton
@@ -183,7 +183,7 @@ export const Items = ({ item, buttons, close }) => {
horizontal
style={{
paddingHorizontal: 12,
paddingTop: 12
paddingVertical: 12
}}
>
{topBarItems.map(renderTopBarItem)}

View File

@@ -23,13 +23,18 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import Notebook from "../../screens/notebook";
import { TopicNotes } from "../../screens/notes/topic-notes";
import { presentSheet, ToastEvent } from "../../services/event-manager";
import {
eSendEvent,
presentSheet,
ToastEvent
} from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useNotebookStore } from "../../stores/use-notebook-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import { eClearEditor } from "../../utils/events";
export default function Notebooks({ note, close, full }) {
const colors = useThemeStore((state) => state.colors);
@@ -112,6 +117,7 @@ export default function Notebooks({ note, close, full }) {
size={SIZE.sm}
onPress={() => {
navigateNotebook(item.id);
eSendEvent(eClearEditor);
close();
}}
>
@@ -134,6 +140,7 @@ export default function Notebooks({ note, close, full }) {
key={topic.id}
onPress={() => {
navigateTopic(topic.id, item.id);
eSendEvent(eClearEditor);
close();
}}
onLongPress={async () => {
@@ -155,7 +162,7 @@ export default function Notebooks({ note, close, full }) {
title={topic.title}
type="gray"
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
icon="bookmark-outline"
style={{
marginRight: 5,
@@ -184,7 +191,7 @@ export default function Notebooks({ note, close, full }) {
{noteNotebooks.length > 1 && !full ? (
<Button
title={`See all linked notebooks`}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
alignSelf: "flex-end",
marginRight: 12,

View File

@@ -91,7 +91,7 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
title="Learn more"
height={30}
type="grayAccent"

View File

@@ -57,7 +57,7 @@ export const Tags = ({ item, close }) => {
icon="plus"
iconPosition="right"
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
marginRight: 5,
borderRadius: 100,
@@ -107,7 +107,7 @@ const TagItem = ({ tag, close }) => {
title={"#" + tag}
type="grayBg"
height={20}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={style}
textStyle={{
textDecorationLine: "underline"

View File

@@ -39,7 +39,7 @@ export const Topics = ({ item, close }) => {
height={30}
onPress={() => open(topic)}
icon="bookmark-outline"
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
marginRight: 5,
paddingHorizontal: 8,

View File

@@ -42,6 +42,7 @@ import { SelectionProvider } from "./context";
import { FilteredList } from "./filtered-list";
import { ListItem } from "./list-item";
import { useItemSelectionStore } from "./store";
import { useRelationStore } from "../../../stores/use-relation-store";
const MoveNoteSheet = ({ note, actionSheetRef }) => {
const colors = useThemeStore((state) => state.colors);
@@ -263,7 +264,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
if (itemState[id] === "selected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
db.relations.add(item, { id: noteId, type: "note" });
await db.relations.add(item, { id: noteId, type: "note" });
}
} else {
await db.notes.addToNotebook(
@@ -278,7 +279,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
} else if (itemState[id] === "deselected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
db.relations.unlink(item, { id: noteId, type: "note" });
await db.relations.unlink(item, { id: noteId, type: "note" });
}
} else {
await db.notes.removeFromNotebook(
@@ -296,6 +297,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
setNotebooks();
eSendEvent(eOnTopicSheetUpdate);
SearchService.updateAndSearch();
useRelationStore.getState().update();
actionSheetRef.current?.hide();
};

View File

@@ -158,11 +158,7 @@ export const MoveNotes = ({
{item.title}
</Paragraph>
{item.type == "note" && item.headline ? (
<Paragraph
numberOfLines={1}
color={colors.icon}
size={SIZE.xs + 1}
>
<Paragraph numberOfLines={1} color={colors.icon} size={SIZE.xs}>
{item.headline}
</Paragraph>
) : null}

View File

@@ -43,8 +43,7 @@ import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import { QRCode } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
let RNFetchBlob;
import RNFetchBlob from "rn-fetch-blob";
class RecoveryKeySheet extends React.Component {
constructor(props) {
@@ -115,7 +114,6 @@ class RecoveryKeySheet extends React.Component {
this.svg.current?.toDataURL(async (data) => {
try {
let path;
RNFetchBlob = (await import("rn-fetch-blob")).default;
let fileName = "nn_" + this.user.email + "_recovery_key_qrcode";
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".png";
@@ -151,7 +149,6 @@ class RecoveryKeySheet extends React.Component {
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".txt";
RNFetchBlob = (await import("rn-fetch-blob")).default;
if (Platform.OS === "android") {
let file = await ScopedStorage.createDocument(
fileName,

View File

@@ -119,7 +119,7 @@ export default function ReminderNotify({
marginTop: 10
}}
>
<Paragraph size={SIZE.xs + 1}>Remind me in:</Paragraph>
<Paragraph size={SIZE.xs}>Remind me in:</Paragraph>
{QuickActions.map((item) => {
return (
<Button
@@ -127,7 +127,7 @@ export default function ReminderNotify({
key={item.title}
title={item.title}
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{ marginLeft: 10, borderRadius: 100 }}
onPress={() => onSnooze(item.time)}
/>
@@ -150,7 +150,7 @@ export default function ReminderNotify({
<Paragraph
style={{
color: colors.icon,
fontSize: SIZE.xs + 1,
fontSize: SIZE.xs,
marginBottom: 10
}}
>

View File

@@ -550,7 +550,7 @@ export default function ReminderSheet({
}}
>
<>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{recurringMode === RecurringModes.Daily
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
: selectedDays.length === 7 &&

View File

@@ -44,7 +44,8 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph";
let RNFetchBlob;
import RNFetchBlob from "rn-fetch-blob";
const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false);
const [restoring, setRestoring] = useState(false);
@@ -213,7 +214,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
return;
}
} else {
RNFetchBlob = (await import("rn-fetch-blob")).default;
let path = await storage.checkAndCreateDir("/backups/");
files = await RNFetchBlob.fs.lstat(path);
}

View File

@@ -40,6 +40,8 @@ import { getElevation } from "../../../utils";
import { SIZE } from "../../../utils/size";
import { IconButton } from "../icon-button";
import Paragraph from "../typography/paragraph";
import phone from "phone";
interface InputProps extends TextInputProps {
fwdRef?: RefObject<TextInput>;
validationType?:
@@ -149,7 +151,6 @@ const Input = ({
isError = customValidator && value === customValidator();
break;
case "phonenumber": {
const { default: phone } = await import("phone");
const result = phone(value, {
strictDetection: true,
validateMobilePrefix: true

View File

@@ -65,7 +65,7 @@ export const Notice = ({
flexShrink: 1
}}
selectable={selectable}
size={isSmall ? SIZE.xs + 1 : SIZE.sm}
size={isSmall ? SIZE.xs : SIZE.sm}
>
{text}
</Paragraph>

View File

@@ -19,9 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureType } from "./components/sheets/new-feature";
export const features: FeatureType[] = [
{
title: "Default font size & font family",
body: "Now you can set default font size and font family in editor that will be used across all your new and old notes."
}
];
export const features: FeatureType[] = [];

View File

@@ -520,7 +520,7 @@ export const useActions = ({ close = () => null, item }) => {
}
async function showAttachments() {
AttachmentDialog.present();
AttachmentDialog.present(item);
}
async function exportNote() {
@@ -613,7 +613,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "pin",
title: "Pin",
title: item.pinned ? "Unpin" : "Pin",
icon: item.pinned ? "pin-off-outline" : "pin-outline",
func: pinItem,
close: false,
@@ -623,7 +623,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "favorite",
title: "Favorite",
title: item.favorite ? "Unfavorite" : "Favorite",
icon: item.favorite ? "star-off" : "star-outline",
func: addToFavorites,
close: false,
@@ -701,7 +701,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "read-only",
title: "Read only",
title: "Readonly",
icon: "pencil-lock",
func: toggleReadyOnlyMode,
on: item.readonly

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useState } from "react";
import { useEffect, useState } from "react";
import { useAttachmentStore } from "../stores/use-attachment-store";
type AttachmentProgress = {
@@ -41,22 +41,24 @@ export const useAttachmentProgress = (
: undefined
);
const attachmentProgress = progress?.[attachment.metadata.hash];
if (attachmentProgress) {
const type = attachmentProgress.type;
const loaded =
attachmentProgress.type === "download"
? attachmentProgress.recieved
: attachmentProgress.sent;
const value = loaded / attachmentProgress.total;
setCurrentProgress({
value: value * 100,
percent: (value * 100).toFixed(0) + "%",
type: type
});
} else {
setCurrentProgress(undefined);
}
useEffect(() => {
const attachmentProgress = progress?.[attachment.metadata.hash];
if (attachmentProgress) {
const type = attachmentProgress.type;
const loaded =
attachmentProgress.type === "download"
? attachmentProgress.recieved
: attachmentProgress.sent;
const value = loaded / attachmentProgress.total;
setCurrentProgress({
value: value * 100,
percent: (value * 100).toFixed(0) + "%",
type: type
});
} else {
setCurrentProgress(undefined);
}
}, [attachment.metadata.hash, progress]);
return [currentProgress, setCurrentProgress];
};

View File

@@ -128,7 +128,6 @@ const Editor = React.memo(
renderKey.current =
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
editor.setLoading(true);
setTimeout(() => editor.setLoading(false), 10);
}, [editor]);
useEffect(() => {
@@ -167,6 +166,7 @@ const Editor = React.memo(
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
onContentProcessDidTerminate={onError}
setSupportMultipleWindows={false}
overScrollMode="never"
scrollEnabled={false}

View File

@@ -101,8 +101,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
setTimeout(() => {
if (!loadingState.current.startTime) {
translateValue.value = 6000;
opacity.value = 0;
}
}, 1000);
}, 3000);
eSubscribeEvent("loadingNote" + editorId, load);
return () => {
clearTimers();

View File

@@ -80,8 +80,8 @@ export const useEditor = (
const lockedSessionId = useRef<string>();
const postMessage = useCallback(
async <T>(type: string, data: T) =>
await post(editorRef, sessionIdRef.current, type, data),
async <T>(type: string, data: T, waitFor = 300) =>
await post(editorRef, sessionIdRef.current, type, data, waitFor),
[sessionIdRef]
);
@@ -120,9 +120,13 @@ export const useEditor = (
[editorId]
);
if (loading) {
setLoading(false);
}
useEffect(() => {
if (loading) {
setLoading(false);
} else {
state.current.ready = false;
}
}, [loading]);
const withTimer = useCallback(
(id: string, fn: () => void, duration: number) => {
@@ -336,7 +340,6 @@ export const useEditor = (
) => {
state.current.currentlyEditing = true;
const editorState = useEditorStore.getState();
if (item && item.type === "new") {
currentNote.current && (await reset());
const nextSessionId = makeSessionId(item as NoteType);
@@ -344,7 +347,7 @@ export const useEditor = (
sessionIdRef.current = nextSessionId;
sessionHistoryId.current = Date.now();
await commands.setSessionId(nextSessionId);
await commands.focus();
if (state.current?.ready) await commands.focus();
lastContentChangeTime.current = 0;
useEditorStore.getState().setReadonly(false);
} else {
@@ -358,7 +361,7 @@ export const useEditor = (
!currentContent.current?.data ||
currentContent.current?.data.length < 50000
) {
overlay(false);
if (state.current.ready) overlay(false);
} else {
overlay(true);
}
@@ -372,7 +375,11 @@ export const useEditor = (
currentNote.current = item as NoteType;
await commands.setStatus(timeConverter(item.dateEdited), "Saved");
await postMessage(EditorEvents.title, item.title);
await postMessage(EditorEvents.html, currentContent.current?.data);
await postMessage(
EditorEvents.html,
currentContent.current?.data,
10000
);
useEditorStore.getState().setReadonly(item.readonly);
await commands.setTags(currentNote.current);
commands.setSettings();
@@ -578,6 +585,10 @@ export const useEditor = (
lastContentChangeTime.current = Date.now();
};
useEffect(() => {
state.current.saveCount = 0;
}, [sessionId, loading]);
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
eSendEvent("webview_reset");
@@ -586,30 +597,24 @@ export const useEditor = (
}
}, [isDefaultEditor, restoreEditorState]);
useEffect(() => {
state.current.saveCount = 0;
(async () => {
await commands.setSessionId(sessionIdRef.current);
if (sessionIdRef.current) {
if (!state.current?.ready) return;
await onReady();
}
})();
}, [sessionId, loading, commands, onReady]);
const onLoad = useCallback(async () => {
state.current.ready = true;
onReady();
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
commands.setInsets(
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
);
if (currentNote.current) {
loadNote({ ...currentNote.current, forced: true });
} else {
await commands.setPlaceholder(placeholderTip.current);
}
commands.setSettings();
if (currentNote.current) overlay(true);
clearTimeout(timers.current["editor:loaded"]);
timers.current["editor:loaded"] = setTimeout(async () => {
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
commands.setInsets(
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
);
await commands.setSessionId(sessionIdRef.current);
await onReady();
await commands.setSettings();
if (currentNote.current) {
loadNote({ ...currentNote.current, forced: true });
} else {
await commands.setPlaceholder(placeholderTip.current);
}
state.current.ready = true;
}, 300);
}, [
onReady,
postMessage,
@@ -617,7 +622,8 @@ export const useEditor = (
commands,
isDefaultEditor,
insets,
loadNote
loadNote,
overlay
]);
return {

View File

@@ -73,7 +73,8 @@ export async function post<T>(
ref: RefObject<WebView>,
sessionId: string,
type: string,
value: T | null = null
value: T | null = null,
waitFor = 300
) {
if (!sessionId) {
console.warn("post called without sessionId of type:", type);
@@ -85,7 +86,7 @@ export async function post<T>(
sessionId: sessionId
};
setImmediate(() => ref.current?.postMessage(JSON.stringify(message)));
const response = await getResponse(type);
const response = await getResponse(type, waitFor);
return response;
}
@@ -97,7 +98,8 @@ type WebviewResponseData = {
};
export const getResponse = async (
type: string
type: string,
waitFor = 300
): Promise<WebviewResponseData | false> => {
return new Promise((resolve) => {
const callback = (data: WebviewResponseData) => {
@@ -107,7 +109,7 @@ export const getResponse = async (
eSubscribeEvent(type, callback);
setTimeout(() => {
resolve(false);
}, 5000);
}, waitFor);
});
};

View File

@@ -118,6 +118,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
get: () => {
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
?.data as NotebookType;
if (!notebook) return [];
return db.relations?.from(notebook, "note");
}
});

View File

@@ -109,7 +109,7 @@ const SettingsUserSection = ({ item }) => {
flexGrow: 1
}}
>
<Heading color={colors.accent} size={SIZE.xs + 1}>
<Heading color={colors.accent} size={SIZE.xs}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}

View File

@@ -45,6 +45,7 @@ import { sleep } from "../utils/time";
import { useRelationStore } from "../stores/use-relation-store";
import { useReminderStore } from "../stores/use-reminder-store";
import { presentDialog } from "../components/dialog/functions";
import NetInfo from "@react-native-community/netinfo";
export type Reminder = {
id: string;
@@ -193,7 +194,7 @@ const onEvent = async ({ type, detail }: Event) => {
case "Hide":
unpinQuickNote();
break;
case "ReplyInput":
case "ReplyInput": {
displayNotification({
title: "Quick note",
message: 'Tap on "Take note" to add a note.',
@@ -203,16 +204,25 @@ const onEvent = async ({ type, detail }: Event) => {
reply_button_text: "Take note",
reply_placeholder_text: "Write something..."
});
await initDatabase(false);
if (!db.isInitialized) await db.init();
await db.notes?.init();
await db.notes?.add({
content: {
type: "tiptap",
data: `<p>${input} </p>`
}
});
await db.notes?.init();
const status = await NetInfo.fetch();
if (status.isInternetReachable) {
try {
await db.sync(false, false);
} catch (e) {
console.log(e, (e as Error).stack);
}
}
useNoteStore.getState().setNotes();
break;
}
}
}
};

View File

@@ -164,7 +164,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 2044
versionCode 2046
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -1,10 +1,5 @@
- Added default font size & font family settings
- Redesign reminder/notebook sheets to improve UX
- Fixed an issue causing sync to fail
- Fix tag suggestions in share extension
- Fix app font sizes too small in some places
- Improved mobile editor performance on low-end devices
- Fix pin to notifications not working
- Bug fixes and performance improvements
- Fix a crash when tryin to upload attachments
- Fix editor not rendered when enter foreground after a long time on iOS
- Increased hit area for outline list to make toggling outline list items easier
Thank you for using Notesnook!

View File

@@ -7,6 +7,15 @@ import Config from 'react-native-config';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import appJson from './app.json';
import Notifications from "../app/services/notifications";
import NetInfo from "@react-native-community/netinfo";
NetInfo.configure({
reachabilityUrl: "https://notesnook.com",
reachabilityTest: (response) => {
if (!response) return false;
return response?.status >= 200 && response?.status < 300;
}
});
Notifications.init();
const appName = appJson.name;

View File

@@ -993,7 +993,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1067,7 +1067,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1097,7 +1097,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1170,7 +1170,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1328,7 +1328,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1340,7 +1340,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1370,7 +1370,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1382,7 +1382,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1411,7 +1411,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1485,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1515,7 +1515,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2038;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1589,7 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.15;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,10 +1,5 @@
- Added default font size & font family settings
- Redesign reminder/notebook sheets to improve UX
- Fixed an issue causing sync to fail
- Fix tag suggestions in share extension
- Fix app font sizes too small in some places
- Improved mobile editor performance on low-end devices
- Fix pin to notifications not working
- Bug fixes and performance improvements
- Fix a crash when tryin to upload attachments
- Fix editor not rendered when enter foreground after a long time on iOS
- Increased hit area for outline list to make toggling outline list items easier
Thank you for using Notesnook!

View File

@@ -168,6 +168,7 @@ module.exports = (env) => {
/node_modules(.*[/\\])+@microsoft/,
/node_modules(.*[/\\])+@msgpack/,
/node_modules(.*[/\\])+liqe/,
/node_modules(.*[/\\])+leac/,
/node_modules(.*[/\\])+selderee/,
/node_modules(.*[/\\])+html-to-text/,
/node_modules(.*[/\\])+buffer/,

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.15",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -1,89 +0,0 @@
diff --git a/node_modules/leac/lib/leac.cjs b/node_modules/leac/lib/leac.cjs
index 13123da..9d490e2 100644
--- a/node_modules/leac/lib/leac.cjs
+++ b/node_modules/leac/lib/leac.cjs
@@ -1 +1,83 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=/\n/g;function t(t){const o=[...t.matchAll(e)].map((e=>e.index||0));o.unshift(-1);const s=n(o,0,o.length);return e=>r(s,e)}function n(e,t,r){if(r-t==1)return{offset:e[t],index:t+1};const o=Math.ceil((t+r)/2),s=n(e,t,o),l=n(e,o,r);return{offset:s.offset,low:s,high:l}}function r(e,t){return function(e){return Object.prototype.hasOwnProperty.call(e,"index")}(e)?{line:e.index,column:t-e.offset}:r(e.high.offset<t?e.high:e.low,t)}function o(e,t){return{...e,regex:s(e,t)}}function s(e,t){if(0===e.name.length)throw new Error(`Rule #${t} has empty name, which is not allowed.`);if(function(e){return Object.prototype.hasOwnProperty.call(e,"regex")}(e))return function(e){if(e.global)throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);return e.sticky?e:new RegExp(e.source,e.flags+"y")}(e.regex);if(function(e){return Object.prototype.hasOwnProperty.call(e,"str")}(e)){if(0===e.str.length)throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);return new RegExp(l(e.str),"y")}return new RegExp(l(e.name),"y")}function l(e){return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g,"\\$&")}exports.createLexer=function(e,n="",r={}){const s="string"!=typeof n?n:r,l="string"==typeof n?n:"",c=e.map(o),i=!!s.lineNumbers;return function(e,n=0){const r=i?t(e):()=>({line:0,column:0});let o=n;const s=[];e:for(;o<e.length;){let t=!1;for(const n of c){n.regex.lastIndex=o;const c=n.regex.exec(e);if(c&&c[0].length>0){if(!n.discard){const e=r(o),t="string"==typeof n.replace?c[0].replace(new RegExp(n.regex.source,n.regex.flags),n.replace):c[0];s.push({state:l,name:n.name,text:t,offset:o,len:c[0].length,line:e.line,column:e.column})}if(o=n.regex.lastIndex,t=!0,n.push){const t=n.push(e,o);s.push(...t.tokens),o=t.offset}if(n.pop)break e;break}}if(!t)break}return{tokens:s,offset:o,complete:e.length<=o}}};
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: !0 });
+const e = /\n/g;
+function t(t) {
+ const o = [...t.matchAll(e)].map((e) => e.index || 0);
+ o.unshift(-1);
+ const s = n(o, 0, o.length);
+ return (e) => r(s, e);
+}
+function n(e, t, r) {
+ if (r - t == 1) return { offset: e[t], index: t + 1 };
+ const o = Math.ceil((t + r) / 2),
+ s = n(e, t, o),
+ l = n(e, o, r);
+ return { offset: s.offset, low: s, high: l };
+}
+function r(e, t) {
+ return (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "index");
+ })(e)
+ ? { line: e.index, column: t - e.offset }
+ : r(e.high.offset < t ? e.high : e.low, t);
+}
+function _o(e, t) {
+ return { ...e, regex: s(e, t) };
+}
+function s(e, t) {
+ if (0 === e.name.length) throw new Error(`Rule #${t} has empty name, which is not allowed.`);
+ if (
+ (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "regex");
+ })(e)
+ )
+ return (function (e) {
+ if (e.global) throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);
+ return e.sticky ? e : new RegExp(e.source, e.flags + "y");
+ })(e.regex);
+ if (
+ (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "str");
+ })(e)
+ ) {
+ if (0 === e.str.length) throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);
+ return new RegExp(l(e.str), "y");
+ }
+ return new RegExp(l(e.name), "y");
+}
+function l(e) {
+ return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, "\\$&");
+}
+exports.createLexer = function (_e, n = "", r = {}) {
+ const s = "string" != typeof n ? n : r,
+ l = "string" == typeof n ? n : "",
+ i = !!s.lineNumbers;
+ return function (e, n = 0) {
+ const r = i ? t(e) : () => ({ line: 0, column: 0 });
+ let o = n;
+ const s = [];
+ e: for (; o < e.length; ) {
+ let t = !1;
+ let z = _e.map(_o);
+ for (const n of z) {
+ n.regex.lastIndex = o;
+ const c = n.regex.exec(e);
+ if (c && c[0].length > 0) {
+ if (!n.discard) {
+ const e = r(o),
+ t = "string" == typeof n.replace ? c[0].replace(new RegExp(n.regex.source, n.regex.flags), n.replace) : c[0];
+ s.push({ state: l, name: n.name, text: t, offset: o, len: c[0].length, line: e.line, column: e.column });
+ }
+ if (((o = n.regex.lastIndex), (t = !0), n.push)) {
+ const t = n.push(e, o);
+ s.push(...t.tokens), (o = t.offset);
+ }
+ if (n.pop) break e;
+ break;
+ }
+ }
+ if (!t) break;
+ }
+ return { tokens: s, offset: o, complete: true };
+ };
+};

View File

@@ -26,12 +26,12 @@ import {
Keyboard,
Platform,
SafeAreaView,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
useWindowDimensions,
View,
ScrollView
useWindowDimensions
} from "react-native";
import {
SafeAreaProvider,
@@ -45,10 +45,10 @@ import Storage from "../app/common/database/storage";
import { eSendEvent } from "../app/services/event-manager";
import { getElevation } from "../app/utils";
import { eOnLoadNote } from "../app/utils/events";
import { sleep } from "../app/utils/time";
import { Editor } from "./editor";
import { Search } from "./search";
import { initDatabase, useShareStore } from "./store";
import { Editor } from "./editor";
import NetInfo from "@react-native-community/netinfo";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);
};
@@ -63,16 +63,15 @@ async function sanitizeHtml(site) {
}
function makeHtmlFromUrl(url) {
return `<a style="overflow-wrap:anywhere;white-space:pre-wrap" href='${url}' target='_blank'>${url}</a>`;
return `<a href='${url}' target='_blank'>${url}</a>`;
}
function makeHtmlFromPlainText(text) {
if (!text) return "";
return `<p style="overflow-wrap:anywhere;white-space:pre-wrap" >${text.replace(
/(?:\r\n|\r|\n)/g,
"<br>"
)}</p>`;
return `<p>${text
.replace(/[\n]+/g, "\n")
.replace(/(?:\r\n|\r|\n)/g, "</p><p>")}</p>`;
}
function getBaseUrl(site) {
@@ -315,7 +314,6 @@ const ShareView = ({ quicknote = false }) => {
const onPress = async () => {
setLoading(true);
await initDatabase();
await sleep(1500);
if (!noteContent.current) return;
if (appendNote && !db.notes.note(appendNote.id)) {
useShareStore.getState().setAppendNote(null);
@@ -358,6 +356,14 @@ const ShareView = ({ quicknote = false }) => {
}
}
}
const status = await NetInfo.fetch();
if (status.isInternetReachable) {
try {
await db.sync(false, false);
} catch (e) {
console.log(e, e.stack);
}
}
await Storage.write("notesAddedFromIntent", "added");
close();
setLoading(false);

View File

@@ -29,8 +29,7 @@ import { db } from "../app/common/database";
export async function initDatabase() {
if (!db.isInitialized) {
// Only load collections in database.
await db.initCollections();
await db.init();
}
await db.notes.init();
}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.4.8",
"version": "2.4.10",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.4.8",
"version": "2.4.10",
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.4.8",
"version": "2.4.10",
"appAppleId": "1544027013",
"private": true,
"main": "./build/electron.js",

View File

@@ -22,8 +22,8 @@ import { isDevelopment, getPath } from "./utils";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
import { logger } from "./logger";
import { Blob } from "buffer";
import { URL } from "url";
import { Blob } from "buffer";
const FILE_NOT_FOUND = -6;
const BASE_PATH = isDevelopment() ? "../public" : "";
@@ -75,9 +75,10 @@ function registerProtocol() {
...request,
body,
headers: {
...request.headers,
origin: `${PROTOCOL}://${HOSTNAME}/`
...request.headers
// origin: `${PROTOCOL}://${HOSTNAME}/`
},
referrer: request.referrer,
redirect: "manual"
});
} catch (e) {
@@ -103,7 +104,7 @@ function registerProtocol() {
);
}
const bypassedRoutes = ["/notes/index_v14.json", "/notes/welcome-web"];
const bypassedRoutes = [];
function shouldInterceptRequest(url) {
let shouldIntercept = url.hostname === HOSTNAME;
return shouldIntercept && !bypassedRoutes.includes(url.pathname);
@@ -117,14 +118,14 @@ async function getBody(request) {
/**
* @type {Electron.Session}
*/
const session = globalThis.window.webContents.session;
const session = globalThis?.window?.webContents?.session;
const blobParts = [];
if (!request.uploadData || !request.uploadData.length) return null;
for (let data of request.uploadData) {
if (data.type === "rawData") {
blobParts.push(new Uint8Array(data.bytes));
} else if (data.type === "blob") {
} else if (session && data.type === "blob") {
const buffer = await session.getBlobData(data.blobUUID);
blobParts.push(new Uint8Array(buffer));
}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.4.8",
"version": "2.4.10",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.4.8",
"version": "2.4.10",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "2.4.8",
"version": "2.4.10",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -41,6 +41,8 @@ import { useCheckoutStore } from "./store";
import { getCurrencySymbol } from "./helpers";
import { Theme } from "@notesnook/theme";
import { isMacStoreApp } from "../../../utils/platform";
import { isUserSubscribed } from "../../../hooks/use-is-user-premium";
import { SUBSCRIPTION_STATUS } from "../../../common/constants";
type BuyDialogProps = {
couponCode?: string;
@@ -52,7 +54,7 @@ export function BuyDialog(props: BuyDialogProps) {
const { onClose, couponCode, plan } = props;
const theme = useTheme() as Theme;
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
useEffect(() => {
return () => {
useCheckoutStore.getState().reset();
@@ -156,13 +158,13 @@ type SideBarProps = {
};
function SideBar(props: SideBarProps) {
const { initialPlan, onClose } = props;
const [showPlans, setShowPlans] = useState(!!initialPlan);
const onPlanSelected = useCheckoutStore((state) => state.onPlanSelected);
const [showPlans, setShowPlans] = useState(false);
const onPlanSelected = useCheckoutStore((state) => state.selectPlan);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const pricingInfo = useCheckoutStore((state) => state.pricingInfo);
const user = useUserStore((store) => store.user);
const couponCode = useCheckoutStore((store) => store.couponCode);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
if (user && selectedPlan)
return (
@@ -176,7 +178,13 @@ function SideBar(props: SideBarProps) {
/>
);
if (user && showPlans)
if (user && !showPlans && isUserSubscribed(user)) {
return (
<AlreadyPremium user={user} onShowPlans={() => setShowPlans(true)} />
);
}
if (user && (showPlans || !!initialPlan))
return (
<PlansList
onPlansLoaded={(plans) => {
@@ -221,7 +229,7 @@ function SideBar(props: SideBarProps) {
function Details() {
const user = useUserStore((store) => store.user);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const onPriceUpdated = useCheckoutStore((state) => state.onPriceUpdated);
const onPriceUpdated = useCheckoutStore((state) => state.updatePrice);
const couponCode = useCheckoutStore((store) => store.couponCode);
const setIsApplyingCoupon = useCheckoutStore(
(store) => store.setIsApplyingCoupon
@@ -347,6 +355,47 @@ function TrialOrUpgrade(props: TrialOrUpgradeProps) {
);
}
type AlreadyPremiumProps = {
user: User | undefined;
onShowPlans: () => void;
};
function AlreadyPremium(props: AlreadyPremiumProps) {
const { user, onShowPlans } = props;
const isCanceled =
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELED;
return (
<>
<Rocket width={200} />
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
Notesnook Pro
</Text>
{isCanceled ? (
<>
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
Resubscribing to Notesnook Pro will replace your existing
subscription.
</Text>
<Button
variant="primary"
mt={2}
sx={{ borderRadius: 100, px: 6 }}
onClick={onShowPlans}
data-test-id="see-all-plans"
>
Continue
</Button>
</>
) : (
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
You are already subscribed to Notesnook Pro.
</Text>
)}
</>
);
}
type SelectedPlanProps = {
plan: Plan;
pricingInfo: PricingInfo | undefined;
@@ -360,7 +409,7 @@ function SelectedPlan(props: SelectedPlanProps) {
store.setIsApplyingCoupon
]);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const couponInputRef = useRef<HTMLInputElement>(null);
const applyCoupon = useCallback(() => {

View File

@@ -23,13 +23,13 @@ import produce from "immer";
interface ICheckoutStore {
selectedPlan?: Plan;
onPlanSelected: (plan?: Plan) => void;
selectPlan: (plan?: Plan) => void;
pricingInfo?: PricingInfo;
onPriceUpdated: (pricingInfo?: PricingInfo) => void;
updatePrice: (pricingInfo?: PricingInfo) => void;
isApplyingCoupon: boolean;
setIsApplyingCoupon: (isApplyingCoupon: boolean) => void;
couponCode?: string;
onApplyCoupon: (couponCode?: string) => void;
applyCoupon: (couponCode?: string) => void;
reset: () => void;
}
export const useCheckoutStore = create<ICheckoutStore>((set) => ({
@@ -37,20 +37,20 @@ export const useCheckoutStore = create<ICheckoutStore>((set) => ({
pricingInfo: undefined,
couponCode: undefined,
isApplyingCoupon: false,
onPlanSelected: (plan) =>
selectPlan: (plan) =>
set(
produce((state: ICheckoutStore) => {
state.selectedPlan = plan;
state.pricingInfo = undefined;
})
),
onPriceUpdated: (pricingInfo) =>
updatePrice: (pricingInfo) =>
set(
produce((state: ICheckoutStore) => {
state.pricingInfo = pricingInfo;
})
),
onApplyCoupon: (couponCode) =>
applyCoupon: (couponCode) =>
set(
produce((state: ICheckoutStore) => {
state.couponCode = couponCode;

View File

@@ -90,13 +90,7 @@ const features: Record<FeatureKeys, Feature> = {
}
]
: isDesktop()
? [
{
title: "Default font size & font family",
subtitle:
"You can now change the default font family and size used by the editor across all your older & newer notes. Go into Settings > Editor settings to change these settings."
}
]
? []
: [],
cta: {
title: "Got it",

View File

@@ -41,3 +41,13 @@ export function isUserPremium(user?: User) {
subStatus === SUBSCRIPTION_STATUS.TRIAL
);
}
export function isUserSubscribed(user?: User) {
if (!user) user = userstore.get().user;
const subStatus = user?.subscription?.type;
return (
subStatus === SUBSCRIPTION_STATUS.PREMIUM ||
subStatus === SUBSCRIPTION_STATUS.PREMIUM_CANCELED
);
}

View File

@@ -35,7 +35,9 @@ import { EV, EVENTS, SYNC_CHECK_IDS } from "@notesnook/core/common";
import { logger } from "../utils/logger";
import Config from "../utils/config";
import { onPageVisibilityChanged } from "../utils/page-visibility";
import { NetworkCheck } from "../utils/network-check";
const networkCheck = new NetworkCheck();
var syncStatusTimeout = 0;
const BATCH_SIZE = 50;
class AppStore extends BaseStore {
@@ -128,7 +130,7 @@ class AppStore extends BaseStore {
if (type === "online") {
// a slight delay to make sure sockets are open and can be connected
// to. Otherwise, this fails miserably.
await new Promise((resolve) => setTimeout(resolve, 2000));
await networkCheck.waitForInternet();
}
await db.connectSSE({ force: type === "online" }).catch(logger.error);
} else if (type === "offline") {

View File

@@ -0,0 +1,38 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import Worker from "worker-loader?filename=static/workers/network-check.worker.[contenthash].js!./network-check.worker";
import type { NetworkCheck as NetworkWorker } from "./network-check.worker";
import { wrap, Remote } from "comlink";
export class NetworkCheck {
private worker!: globalThis.Worker;
private network!: Remote<NetworkWorker>;
constructor() {
this.worker = new Worker();
this.network = wrap<NetworkWorker>(this.worker);
}
waitForInternet() {
return this.network.waitForInternet();
}
}

View File

@@ -0,0 +1,40 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { expose } from "comlink";
const module = {
async waitForInternet() {
let retries = 10;
while (retries-- > 0) {
try {
const response = await fetch("https://api.notesnook.com/health");
if (response.ok) return true;
} catch {
// ignore
}
// wait a bit before trying again.
await new Promise((resolve) => setTimeout(resolve, 2500));
}
return false;
}
};
expose(module);
export type NetworkCheck = typeof module;

View File

@@ -35,7 +35,7 @@ function isDocumentHidden() {
export function onPageVisibilityChanged(
handler: (
status: "online" | "offline" | "visibilitychange",
status: "online" | "offline" | "visibilitychange" | "focus",
bool: boolean
) => void
) {
@@ -46,6 +46,8 @@ export function onPageVisibilityChanged(
document.addEventListener(visibilityChange(), () =>
handler("visibilitychange", isDocumentHidden())
);
window.addEventListener("focus", () => handler("focus", false));
}
function onDeviceOnline(handler: () => void) {

View File

@@ -154,7 +154,7 @@ export const OutlineListItem = Node.create<ListItemOptions>({
const clientY =
e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
const hitArea = { width: 26, height: 24 };
const hitArea = { width: 40, height: 40 };
const selection = editor.state.selection;
const parent = findParentNode((node) => !!node.attrs.textDirection)(

View File

@@ -18,10 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Flex } from "@theme-ui/components";
import {
ReactNodeView,
ReactNodeViewProps,
} from "../react";
import { ReactNodeView, ReactNodeViewProps } from "../react";
import { Node as ProsemirrorNode } from "prosemirror-model";
import { Editor } from "../../types";
import { Editor as TiptapEditor } from "@tiptap/core";
@@ -263,7 +260,7 @@ function TableColumnToolbar(props: TableToolbarProps) {
>
<TableProperties
editor={editor}
title="tableProperties"
title="Table properties"
icon="more"
variant={"small"}
/>

View File

@@ -54,13 +54,12 @@ const clean: ThemeUIStyleObject = {
const error: ThemeUIStyleObject = {
variant: "forms.input",
boxShadow: "0px 0px 0px 1px var(--error) inset",
outline: "none",
outline: "1.5px solid var(--error)",
":focus": {
boxShadow: "0px 0px 0px 1.5px var(--error) inset"
outline: "2px solid var(--error)"
},
":hover:not(:focus)": {
boxShadow: "0px 0px 0px 1px var(--error) inset"
outline: "1.5px solid var(--error)"
}
};