Compare commits

...

42 Commits

Author SHA1 Message Date
Abdullah Atta
e221882f8e clipper: change version to 0.2.0 2023-05-17 11:35:52 +05:00
Abdullah Atta
153ee4bce3 clipper: load content scripts on demand to reduce host permissions 2023-05-17 11:35:52 +05:00
ammarahm-ed
a2ecdab61c mobile: release 2.4.17 2023-05-17 11:24:29 +05:00
Ammar Ahmed
eb089e6211 Merge pull request #2543 from streetwriters/feat/in-app-review
mobile: add in-app-review flow
2023-05-17 11:18:22 +05:00
ammarahm-ed
1981c3a2f8 mobile: add in-app-review flow 2023-05-17 11:14:49 +05:00
Abdullah Atta
68b3ebe318 clipper: get rid of <all_urls> permission 2023-05-17 10:11:47 +05:00
ammarahm-ed
8a0b6264d2 mobile: fix pro features locked for pro users 2023-05-17 10:09:37 +05:00
Abdullah Atta
239174c3e7 web: bump version to 2.4.11 2023-05-17 09:29:16 +05:00
Abdullah Atta
db1503d828 clipper: remove contextMenus permission 2023-05-16 18:05:20 +05:00
Abdullah Atta
7b1cbba9e7 clipper: bump version to 0.2 2023-05-16 17:39:21 +05:00
Abdullah Atta
2c3c193629 clipper: default to simplified mode if user is no longer pro 2023-05-16 17:35:59 +05:00
Abdullah Atta
899e56e9cd clipper(core): escape % & ' during css stringify 2023-05-16 17:35:59 +05:00
Abdullah Atta
9c9582ae12 clipper: improve error messages 2023-05-16 17:35:59 +05:00
Abdullah Atta
b918667b63 clipper: adjust icon size in inline tag 2023-05-16 17:35:59 +05:00
Abdullah Atta
878c901e06 clipper: add support for assigning multiple notebooks 2023-05-16 17:35:59 +05:00
Muhammad Ali
fdd3897a60 web: close tag dropdown on tab press (#2508) 2023-05-16 17:32:00 +05:00
ammarahm-ed
c958657b01 mobile: release 2.4.16 2023-05-16 12:11:43 +05:00
Abdullah Atta
e7175f48a0 clipper: do not exclude localhost tabs in production mode 2023-05-13 17:51:10 +05:00
Abdullah Atta
4e1862d653 clipper: improve connection reliability
this adds support for working with multiple Notesnook tabs
even though it is not currently recommended that you
open multiple Notesnook tabs
2023-05-13 17:51:10 +05:00
Abdullah Atta
1d5ac37222 clipper: always clip tab from current active window 2023-05-13 17:50:53 +05:00
Abdullah Atta
32d027fff6 clipper(core): don't clip images in simplified mode 2023-05-13 17:50:41 +05:00
ammarahm-ed
9434993373 mobile: remove usesCleartextTraffic in release builds 2023-05-08 18:30:36 +05:00
Abdullah Atta
2bd0f1a304 web: allow users to view their billing history 2023-05-08 15:25:20 +05:00
Abdullah Atta
7f873f240e web: allow requesting refunds from settings 2023-05-08 15:25:20 +05:00
Abdullah Atta
a0229729b7 web: use sandbox checkout during development 2023-05-08 15:25:20 +05:00
Abdullah Atta
dd368a4170 web: show success on checkout completion 2023-05-08 15:25:20 +05:00
ammarahm-ed
6d3e985c9d mobile: fix failing build 2023-05-08 09:27:55 +05:00
ammarahm-ed
15858db21b mobile: sync quick notes only when online 2023-05-08 08:19:17 +05:00
Ammar Ahmed
b69a94a96f mobile: fix premium toast is not shown when using premium features on mobile (#2504) 2023-05-08 08:18:38 +05:00
Muhammad Ali
73b965eb7c web: close properties pane when note is switched (#2486) 2023-05-06 16:16:01 +05:00
Abdullah Atta
5738f48360 core: expire session if key is not found 2023-05-06 15:27:58 +05:00
Muhammad Ali
de74b8c2cd editor: position search box correctly in readonly mode (#2478) 2023-05-03 13:22:25 +05:00
Muhammad Ali
2d07d79057 web: fix issue where notes directly in a notebook could not be unlinked (#2466)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-05-02 12:49:24 +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
79 changed files with 1682 additions and 786 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

@@ -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

@@ -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

@@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useRef, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { View } from "react-native";
import Animated, { FadeInUp, FadeOutUp } from "react-native-reanimated";
import { editorState } from "../../screens/editor/tiptap/utils";
import useKeyboard from "../../hooks/use-keyboard";
import { DDS } from "../../services/device-detection";
import {
eSendEvent,
@@ -31,6 +31,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils";
import {
eCloseActionSheet,
eCloseSheet,
eOpenPremiumDialog,
eShowGetPremium
} from "../../utils/events";
@@ -39,12 +40,12 @@ import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
export const PremiumToast = ({ context = "global", offset = 0 }) => {
const colors = useThemeStore((state) => state.colors);
const [msg, setMsg] = useState(null);
const timer = useRef();
const keyboard = useKeyboard();
const open = useCallback(
(event) => {
@@ -72,7 +73,6 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
useEffect(() => {
eSubscribeEvent(eShowGetPremium, open);
return () => {
clearTimeout(timer.current);
eUnSubscribeEvent(eShowGetPremium, open);
};
}, [open]);
@@ -80,9 +80,7 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
const onPress = async () => {
open(null);
eSendEvent(eCloseActionSheet);
if (editorState().isFocused) {
//tiny.call(EditorWebView, tiny.blur);
}
eSendEvent(eCloseSheet);
await sleep(300);
eSendEvent(eOpenPremiumDialog);
};
@@ -102,9 +100,13 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
flexDirection: "row",
alignSelf: "center",
justifyContent: "space-between",
top: offset,
top: offset + keyboard.keyboardHeight,
maxWidth: DDS.isLargeTablet() ? 400 : "98%"
}}
onTouchEnd={() => {
setMsg(null);
clearTimeout(timer.current);
}}
>
<View
style={{

View File

@@ -42,6 +42,7 @@ import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { eSendEvent } from "../../../services/event-manager";
import { eCloseSheet } from "../../../utils/events";
import { requestInAppReview } from "../../../services/app-review";
const ExportNotesSheet = ({ notes, update }) => {
const colors = useThemeStore((state) => state.colors);
@@ -72,6 +73,7 @@ const ExportNotesSheet = ({ notes, update }) => {
update({ disableClosing: false });
setComplete(true);
setExporting(false);
requestInAppReview();
};
const actions = [

View File

@@ -22,7 +22,7 @@ import { View } from "react-native";
import { ScrollView } from "react-native-actions-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
import { presentSheet, ToastEvent } from "../../../services/event-manager";
import { ToastEvent, presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useTagStore } from "../../../stores/use-tag-store";
import { useThemeStore } from "../../../stores/use-theme-store";

View File

@@ -35,6 +35,7 @@ import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { requestInAppReview } from "../../../services/app-review";
const PublishNoteSheet = ({ note: item, update }) => {
const colors = useThemeStore((state) => state.colors);
@@ -66,6 +67,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
Navigation.queueRoutesForUpdate();
setPublishLoading(false);
}
requestInAppReview();
} catch (e) {
ToastEvent.show({
heading: "Could not publish note",

View File

@@ -520,7 +520,7 @@ export const useActions = ({ close = () => null, item }) => {
}
async function showAttachments() {
AttachmentDialog.present();
AttachmentDialog.present(item);
}
async function exportNote() {

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

@@ -39,6 +39,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { getElevation } from "../../utils";
import { SIZE } from "../../utils/size";
import { requestInAppReview } from "../../services/app-review";
const AppLock = ({ route }) => {
const colors = useThemeStore((state) => state.colors);
const appLockMode = useSettingStore((state) => state.settings.appLockMode);
@@ -151,6 +152,9 @@ const AppLock = ({ route }) => {
return;
}
SettingsService.set({ appLockMode: item.value });
if (!welcome) {
requestInAppReview();
}
}}
customStyle={{
justifyContent: "flex-start",

View File

@@ -0,0 +1,44 @@
/*
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 InAppReview from "react-native-in-app-review";
import { DatabaseLogger } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import Config from "react-native-config";
const day_ms = 86400000;
export function requestInAppReview() {
if (Config.GITHUB_RELEASE === "true") return;
const time = MMKV.getMap<{ timestamp: number }>("requestInAppReview");
if (time?.timestamp && time?.timestamp + day_ms * 7 > Date.now()) {
return;
}
if (InAppReview.isAvailable()) {
InAppReview.RequestInAppReview()
.then(() => {})
.catch((error) => {
DatabaseLogger.error(error);
});
MMKV.setMap("requestInAppReview", { timestamp: Date.now() });
} else {
DatabaseLogger.error(new Error("In App Review not available"));
}
}

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.',
@@ -211,9 +212,17 @@ const onEvent = async ({ type, detail }: Event) => {
data: `<p>${input} </p>`
}
});
await db.sync(false, false);
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

@@ -98,7 +98,7 @@ async function getProducts() {
}
function get() {
if (__DEV__ || Config.isTesting) return true;
if (__DEV__ || Config.isTesting === "true") return true;
return SUBSCRIPTION_STATUS.BASIC !== premiumStatus;
}

View File

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

View File

@@ -51,8 +51,7 @@
android:theme="@style/BootTheme"
android:largeHeap="true"
android:supportsRtl="false"
tools:replace="android:supportsRtl"
android:usesCleartextTraffic="true">
tools:replace="android:supportsRtl">
<receiver android:exported="false" android:name=".NoteWidget">
<intent-filter>

View File

@@ -1,6 +1,4 @@
- Sync notes created from share extension & notification quick notes without requiring to launch the app
- Fix formatting is lost when sharing notes to other apps
- Fix editor would be blank after entering the app from background
- Small bug fixes and ux improvements
- Fix pro features locked accidentally for pro users
- Added in app review
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1067,7 +1067,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.14;
MARKETING_VERSION = 2.4.17;
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
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.14;
MARKETING_VERSION = 2.4.17;
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
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.14;
MARKETING_VERSION = 2.4.17;
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
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.14;
MARKETING_VERSION = 2.4.17;
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
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.14;
MARKETING_VERSION = 2.4.17;
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 = 2037;
CURRENT_PROJECT_VERSION = 2040;
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.14;
MARKETING_VERSION = 2.4.17;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -256,6 +256,8 @@ PODS:
- React-Core
- react-native-image-resizer (3.0.5):
- React-Core
- react-native-in-app-review (4.3.3):
- React-Core
- react-native-keep-awake (1.1.0):
- React-Core
- react-native-mmkv-storage (0.8.0):
@@ -460,6 +462,7 @@ DEPENDENCIES:
- react-native-html-to-pdf-lite (from `../../node_modules/react-native-html-to-pdf-lite`)
- react-native-image-picker (from `../../node_modules/react-native-image-picker`)
- "react-native-image-resizer (from `../../node_modules/@bam.tech/react-native-image-resizer`)"
- react-native-in-app-review (from `../../node_modules/react-native-in-app-review`)
- "react-native-keep-awake (from `../../node_modules/@sayem314/react-native-keep-awake`)"
- react-native-mmkv-storage (from `../../node_modules/react-native-mmkv-storage`)
- "react-native-netinfo (from `../../node_modules/@react-native-community/netinfo`)"
@@ -580,6 +583,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-image-picker"
react-native-image-resizer:
:path: "../../node_modules/@bam.tech/react-native-image-resizer"
react-native-in-app-review:
:path: "../../node_modules/react-native-in-app-review"
react-native-keep-awake:
:path: "../../node_modules/@sayem314/react-native-keep-awake"
react-native-mmkv-storage:
@@ -715,6 +720,7 @@ SPEC CHECKSUMS:
react-native-html-to-pdf-lite: 21bfb169bf4cbcd7bec9f736975ee1b3f5292d4a
react-native-image-picker: 9c8a2687b69300ad9e95cec5d38f35ab9d32467d
react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4
react-native-keep-awake: acbee258db16483744910f0da3ace39eb9ab47fd
react-native-mmkv-storage: 8ba3c0216a6df283ece11205b442a3e435aec4e5
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983

View File

@@ -59,7 +59,8 @@
"react-native-notification-sounds": "0.5.5",
"@bam.tech/react-native-image-resizer": "3.0.5",
"react-native-navigation-bar-color": "2.0.2",
"react-native-actions-shortcuts": "^1.0.1"
"react-native-actions-shortcuts": "^1.0.1",
"react-native-in-app-review": "4.3.3"
},
"devDependencies": {
"@babel/core": "^7.12.9",

View File

@@ -22,6 +22,11 @@ if (isGithubRelease) {
android:null
}
}
config.dependencies["react-native-in-app-review"] = {
platforms: {
android: null
}
}
}
module.exports = config;

View File

@@ -1,6 +1,4 @@
- Sync notes created from share extension & notification quick notes in background
- Fix formatting is lost when sharing notes to other apps
- Fix editor would be blank after entering the app from background sometimes
- Small bug fixes and UX improvements
- Fix pro features locked accidentally for pro users
- Added in app review
Thank you for using Notesnook!

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.16",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.16",
"license": "GPL-3.0-or-later",
"workspaces": [
"native/",
@@ -14,7 +14,8 @@
],
"dependencies": {
"react": "18.0.0",
"react-native": "0.69.7"
"react-native": "0.69.7",
"react-native-in-app-review": "4.3.3"
},
"devDependencies": {
"otplib": "12.0.1",
@@ -94,6 +95,7 @@
"react-native-html-to-pdf-lite": "^0.9.1",
"react-native-iap": "7.5.6",
"react-native-image-picker": "4.1.2",
"react-native-in-app-review": "4.3.3",
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.8.0",
"react-native-modal-datetime-picker": "14.0.0",
@@ -18292,6 +18294,11 @@
"react-native": "*"
}
},
"node_modules/react-native-in-app-review": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/react-native-in-app-review/-/react-native-in-app-review-4.3.3.tgz",
"integrity": "sha512-Q9sXBtK8tCBYFPCGmMgeMlkxXC5e6vAyPZK26OC1oOKUuKUd0QORnryk/qbwnuN4fLshHQN8UKlLgyHLGRuK3A=="
},
"node_modules/react-native-keychain": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-4.0.5.tgz",
@@ -24303,6 +24310,7 @@
"react-native-html-to-pdf-lite": "^0.9.1",
"react-native-iap": "7.5.6",
"react-native-image-picker": "4.1.2",
"react-native-in-app-review": "4.3.3",
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.8.0",
"react-native-modal-datetime-picker": "14.0.0",
@@ -34988,6 +34996,11 @@
"react-native-image-pan-zoom": "^2.1.12"
}
},
"react-native-in-app-review": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/react-native-in-app-review/-/react-native-in-app-review-4.3.3.tgz",
"integrity": "sha512-Q9sXBtK8tCBYFPCGmMgeMlkxXC5e6vAyPZK26OC1oOKUuKUd0QORnryk/qbwnuN4fLshHQN8UKlLgyHLGRuK3A=="
},
"react-native-keychain": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-4.0.5.tgz",

View File

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

View File

@@ -48,6 +48,7 @@ import { eOnLoadNote } from "../app/utils/events";
import { Editor } from "./editor";
import { Search } from "./search";
import { initDatabase, useShareStore } from "./store";
import NetInfo from "@react-native-community/netinfo";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);
};
@@ -355,7 +356,14 @@ const ShareView = ({ quicknote = false }) => {
}
}
}
await db.sync(false, 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

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.4.9",
"version": "2.4.11",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.4.9",
"version": "2.4.11",
"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.9",
"version": "2.4.11",
"appAppleId": "1544027013",
"private": true,
"main": "./build/electron.js",

View File

@@ -23,6 +23,7 @@ import { createReadStream } from "fs";
import { extname, normalize } from "path";
import { logger } from "./logger";
import { URL } from "url";
import { Blob } from "buffer";
const FILE_NOT_FOUND = -6;
const BASE_PATH = isDevelopment() ? "../public" : "";
@@ -67,7 +68,31 @@ function registerProtocol() {
mimeType: extensionToMimeType[fileExtension]
});
} else {
protocol.uninterceptProtocol(PROTOCOL);
var response;
try {
const body = await getBody(request);
response = await fetch(request.url, {
...request,
body,
headers: {
...request.headers
// origin: `${PROTOCOL}://${HOSTNAME}/`
},
referrer: request.referrer,
redirect: "manual"
});
} catch (e) {
console.error(e);
logger.error(`Error sending request to `, request.url, "Error: ", e);
callback({ statusCode: 400 });
return;
}
callback({
statusCode: response.status,
data: response.body,
headers: Object.fromEntries(response.headers.entries()),
mimeType: response.headers.get("Content-Type")
});
}
}
);
@@ -85,5 +110,29 @@ function shouldInterceptRequest(url) {
return shouldIntercept && !bypassedRoutes.includes(url.pathname);
}
/**
*
* @param {Electron.ProtocolRequest} request
*/
async function getBody(request) {
/**
* @type {Electron.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 (session && data.type === "blob") {
const buffer = await session.getBlobData(data.blobUUID);
blobParts.push(new Uint8Array(buffer));
}
}
const blob = new Blob(blobParts);
return await blob.arrayBuffer();
}
const PROTOCOL_URL = `${PROTOCOL}://${HOSTNAME}/`;
export { registerProtocol, PROTOCOL_URL };

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.4.9",
"version": "2.4.11",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.4.9",
"version": "2.4.11",
"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.9",
"version": "2.4.11",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -45,11 +45,8 @@ import { isTesting } from "./utils/platform";
import { updateStatus, removeStatus, getStatus } from "./hooks/use-status";
import { showToast } from "./utils/toast";
import { interruptedOnboarding } from "./components/dialogs/onboarding-dialog";
import { WebExtensionRelay } from "./utils/web-extension-relay";
import { hashNavigate } from "./navigation";
const relay = new WebExtensionRelay();
export default function AppEffects({ setShow }) {
const refreshNavItems = useStore((store) => store.refreshNavItems);
const updateLastSynced = useStore((store) => store.updateLastSynced);
@@ -103,7 +100,6 @@ export default function AppEffects({ setShow }) {
await showOnboardingDialog(interruptedOnboarding());
await showFeatureDialog("highlights");
await scheduleBackups();
relay.connect();
})();
return () => {

View File

@@ -35,6 +35,9 @@ import StatusBar from "./components/status-bar";
import { EditorLoader } from "./components/loaders/editor-loader";
import { FlexScrollContainer } from "./components/scroll-container";
import CachedRouter from "./components/cached-router";
import { WebExtensionRelay } from "./utils/web-extension-relay";
new WebExtensionRelay();
const GlobalMenuWrapper = React.lazy(() =>
import("./components/global-menu-wrapper")

View File

@@ -211,8 +211,9 @@ export function showError(title: string, message: string) {
export function showMultiDeleteConfirmation(length: number) {
return confirm({
title: `Delete ${length} items?`,
message: `These items will be **kept in your Trash for ${db.settings?.getTrashCleanupInterval() || 7
} days** after which they will be permanently deleted.`,
message: `These items will be **kept in your Trash for ${
db.settings?.getTrashCleanupInterval() || 7
} days** after which they will be permanently deleted.`,
positiveButtonText: "Yes",
negativeButtonText: "No"
});
@@ -331,6 +332,12 @@ export function showMoveNoteDialog(noteIds: string[]) {
));
}
export function showBillingHistoryDialog() {
return showDialog("BillingHistoryDialog", (Dialog, perform) => (
<Dialog onClose={(res: boolean) => perform(res)} />
));
}
function getDialogData(type: string) {
switch (type) {
case "create_vault":

View File

@@ -0,0 +1,145 @@
/*
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 { useEffect, useState } from "react";
import { Perform } from "../../common/dialog-controller";
import Dialog from "./dialog";
import { db } from "../../common/db";
import { Loading } from "../icons";
import { Flex, Link, Text } from "@theme-ui/components";
import { formatDate } from "@notesnook/core/utils/date";
type Transaction = {
order_id: string;
checkout_id: string;
amount: string;
currency: string;
status: keyof typeof TransactionStatusToText;
created_at: Date;
passthrough: null;
product_id: number;
is_subscription: boolean;
is_one_off: boolean;
subscription: Subscription;
user: User;
receipt_url: string;
};
type Subscription = {
subscription_id: number;
status: string;
};
type User = {
user_id: number;
email: string;
marketing_consent: boolean;
};
const TransactionStatusToText = {
completed: "Completed",
refunded: "Refunded",
partially_refunded: "Partially refunded",
disputed: "Disputed"
};
export type BillingHistoryDialogProps = {
onClose: Perform;
};
export default function BillingHistoryDialog(props: BillingHistoryDialogProps) {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | undefined>();
useEffect(() => {
(async function () {
try {
setError(undefined);
setIsLoading(true);
const transactions = await db.subscriptions?.transactions();
if (!transactions) return;
setTransactions(transactions);
} catch (e) {
if (e instanceof Error) setError(e);
} finally {
setIsLoading(false);
}
})();
}, []);
return (
<Dialog
isOpen={true}
title={"Billing history"}
description={"View all the transactions you have made with Notesnook."}
onClose={() => props.onClose(false)}
negativeButton={{ text: "Close", onClick: () => props.onClose(false) }}
width={600}
>
{isLoading ? (
<Loading sx={{ mt: 2 }} />
) : error ? (
<Flex sx={{ bg: "errorBg", p: 1, borderRadius: "default" }}>
<Text variant="error">
{error.message}
<br />
{error.stack}
</Text>
</Flex>
) : (
<Flex sx={{ flexDirection: "column", gap: 1, mt: 1 }}>
{transactions.map((transaction) => (
<Flex
key={transaction.order_id}
sx={{
justifyContent: "space-between",
borderBottom: "1px solid var(--border)",
pb: 1
}}
>
<Flex sx={{ flexDirection: "column" }}>
<Text variant="subtitle">Order #{transaction.order_id}</Text>
<Text variant="body" sx={{ color: "fontTertiary" }}>
{formatDate(new Date(transaction.created_at).getTime())} {" "}
{TransactionStatusToText[transaction.status]}
</Text>
</Flex>
<Flex sx={{ flexDirection: "column", alignItems: "end" }}>
<Text variant="body">
{transaction.amount} {transaction.currency}
</Text>
<Link
href={transaction.receipt_url}
target="_blank"
rel="noreferer nofollow"
variant="text.subBody"
sx={{ color: "primary" }}
>
View receipt
</Link>
</Flex>
</Flex>
))}
</Flex>
)}
</Dialog>
);
}

View File

@@ -55,6 +55,8 @@ export function BuyDialog(props: BuyDialogProps) {
const theme = useTheme() as Theme;
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
useEffect(() => {
return () => {
useCheckoutStore.getState().reset();
@@ -118,7 +120,7 @@ export function BuyDialog(props: BuyDialogProps) {
boxShadow: "4px 5px 18px 2px #00000038",
borderRadius: "dialog",
flexDirection: ["column", "column", "row"],
width: ["95%", "80%", "60%"],
width: ["95%", "80%", isCheckoutCompleted ? "400px" : "60%"],
maxHeight: ["95%", "80%", "80%"],
alignSelf: "center",
overflowY: ["scroll", "scroll", "hidden"]
@@ -139,7 +141,7 @@ export function BuyDialog(props: BuyDialogProps) {
flexShrink: 0,
alignItems: "center",
justifyContent: "center",
width: ["100%", "100%", 350]
width: ["100%", "100%", isCheckoutCompleted ? "100%" : 350]
}}
p={4}
py={50}
@@ -165,6 +167,9 @@ function SideBar(props: SideBarProps) {
const user = useUserStore((store) => store.user);
const couponCode = useCheckoutStore((store) => store.couponCode);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
if (isCheckoutCompleted) return <CheckoutCompleted onClose={onClose} />;
if (user && selectedPlan)
return (
@@ -230,12 +235,16 @@ function Details() {
const user = useUserStore((store) => store.user);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const onPriceUpdated = useCheckoutStore((state) => state.updatePrice);
const completeCheckout = useCheckoutStore((state) => state.completeCheckout);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
const couponCode = useCheckoutStore((store) => store.couponCode);
const setIsApplyingCoupon = useCheckoutStore(
(store) => store.setIsApplyingCoupon
);
const theme = useThemeStore((store) => store.theme);
if (isCheckoutCompleted) return null;
if (selectedPlan && user)
return (
<PaddleCheckout
@@ -243,6 +252,7 @@ function Details() {
theme={theme}
user={user}
coupon={couponCode}
onCompleted={completeCheckout}
onCouponApplied={() => setIsApplyingCoupon(true)}
onPriceUpdated={(pricingInfo) => {
onPriceUpdated(pricingInfo);
@@ -396,6 +406,31 @@ function AlreadyPremium(props: AlreadyPremiumProps) {
);
}
function CheckoutCompleted(props: { onClose: () => void }) {
const { onClose } = props;
return (
<>
<Rocket width={200} />
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
Thank you!
</Text>
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
You have successfully subscribed to Notesnook Pro.
</Text>
<Button
variant="primary"
mt={2}
sx={{ borderRadius: 100, px: 6 }}
onClick={onClose}
data-test-id="see-all-plans"
>
Continue
</Button>
</>
);
}
type SelectedPlanProps = {
plan: Plan;
pricingInfo: PricingInfo | undefined;

View File

@@ -33,13 +33,25 @@ import {
// const isDev = false; // process.env.NODE_ENV === "development";
// const VENDOR_ID = isDev ? 1506 : 128190;
const PADDLE_ORIGIN = "https://buy.paddle.com";
const PADDLE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-buy.paddle.com"
: "https://buy.paddle.com";
const CHECKOUT_CREATE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-create-checkout.paddle.com"
: "https://create-checkout.paddle.com";
const CHECKOUT_SERVICE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-checkout-service.paddle.com"
: "https://checkout-service.paddle.com";
const SUBSCRIBED_EVENTS: PaddleEvents[] = [
PaddleEvents["Checkout.Loaded"],
PaddleEvents["Checkout.Coupon.Applied"],
PaddleEvents["Checkout.Coupon.Remove"],
PaddleEvents["Checkout.Location.Submit"]
PaddleEvents["Checkout.Location.Submit"],
PaddleEvents["Checkout.Complete"]
];
type PaddleCheckoutProps = {
@@ -48,10 +60,11 @@ type PaddleCheckoutProps = {
plan: Plan;
onPriceUpdated?: (pricingInfo: PricingInfo) => void;
onCouponApplied?: () => void;
onCompleted?: () => void;
coupon?: string;
};
export function PaddleCheckout(props: PaddleCheckoutProps) {
const { plan, onPriceUpdated, coupon, onCouponApplied } = props;
const { plan, onPriceUpdated, coupon, onCouponApplied, onCompleted } = props;
const [sourceUrl, setSourceUrl] = useState<string>();
const [isLoading, setIsLoading] = useState(true);
const [checkoutId, setCheckoutId] = useState<string>();
@@ -61,7 +74,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
const reloadCheckout = useCallback(() => {
if (!checkoutRef.current) return;
setIsLoading(true);
checkoutRef.current.src = `https://buy.paddle.com/checkout/?checkout_id=${checkoutId}&display_mode=inline&apple_pay_enabled=false`;
checkoutRef.current.src = `${PADDLE_ORIGIN}/checkout/?checkout_id=${checkoutId}&display_mode=inline&apple_pay_enabled=false`;
}, [checkoutId]);
const updatePrice = useCallback(
@@ -96,6 +109,10 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
)
return;
if (event_name === PaddleEvents["Checkout.Complete"]) {
onCompleted && onCompleted();
return;
}
if (event_name === PaddleEvents["Checkout.Loaded"]) setIsLoading(false);
const pricingInfo = await updatePrice(checkout.id);
@@ -108,7 +125,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
return () => {
window.removeEventListener("message", onMessage);
};
}, [onPriceUpdated, updatePrice, plan]);
}, [onPriceUpdated, updatePrice, plan, onCompleted]);
useEffect(() => {
if (
@@ -173,7 +190,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
async function getCheckoutURL(params: PaddleCheckoutProps) {
const { plan, theme, user } = params;
const BASE_URL = `https://create-checkout.paddle.com/checkout/product/${plan.id}`;
const BASE_URL = `${CHECKOUT_CREATE_ORIGIN}/checkout/product/${plan.id}`;
const queryParams = new URLSearchParams();
queryParams.set("product", plan.id);
queryParams.set("passthrough", JSON.stringify({ userId: user.id }));
@@ -227,7 +244,7 @@ async function applyCoupon(
checkoutId: string,
couponCode: string
): Promise<CheckoutData | false> {
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/coupon`;
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/coupon`;
const body = { data: { coupon_code: couponCode } };
const headers = new Headers();
headers.set("content-type", "application/json");
@@ -246,7 +263,7 @@ async function applyCoupon(
}
async function removeCoupon(checkoutId: string): Promise<CheckoutData | false> {
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/coupon`;
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/coupon`;
const response = await fetch(url, {
method: "DELETE"
@@ -260,7 +277,7 @@ async function removeCoupon(checkoutId: string): Promise<CheckoutData | false> {
}
async function sendCheckoutEvent(checkoutId: string, eventName: PaddleEvents) {
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/event`;
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/event`;
const body = { data: { event_name: eventName } };
const headers = new Headers();
headers.set("content-type", "application/json");
@@ -275,7 +292,7 @@ async function sendCheckoutEvent(checkoutId: string, eventName: PaddleEvents) {
async function getCheckoutData(
checkoutId: string
): Promise<CheckoutData | undefined> {
const url = `https://checkout-service.paddle.com/checkout/${checkoutId}`;
const url = `${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}`;
const response = await fetch(url);
if (!response.ok) return undefined;
const json = (await response.json()) as CheckoutDataResponse;

View File

@@ -32,7 +32,7 @@ export const DEFAULT_PLANS: Plan[] = [
country: "PK",
currency: "USD",
discount: 0,
id: "648884",
id: process.env.NODE_ENV === "development" ? "9822" : "648884",
price: { gross: 4.49, net: 0, tax: 0 }
},
{
@@ -40,7 +40,7 @@ export const DEFAULT_PLANS: Plan[] = [
country: "PK",
currency: "USD",
discount: 0,
id: "658759",
id: process.env.NODE_ENV === "development" ? "50305" : "658759",
price: { gross: 49.99, net: 0, tax: 0 }
}
];
@@ -52,7 +52,8 @@ export const PLAN_METADATA: Record<Period, PlanMetadata> = {
let CACHED_PLANS: Plan[];
export async function getPlans(): Promise<Plan[] | null> {
if (isTesting()) return DEFAULT_PLANS;
if (isTesting() || process.env.NODE_ENV === "development")
return DEFAULT_PLANS;
if (CACHED_PLANS) return CACHED_PLANS;
const url = `https://notesnook.com/api/v1/prices/products/web`;

View File

@@ -22,6 +22,8 @@ import create from "zustand";
import produce from "immer";
interface ICheckoutStore {
isCompleted: boolean;
completeCheckout: () => void;
selectedPlan?: Plan;
selectPlan: (plan?: Plan) => void;
pricingInfo?: PricingInfo;
@@ -33,10 +35,17 @@ interface ICheckoutStore {
reset: () => void;
}
export const useCheckoutStore = create<ICheckoutStore>((set) => ({
isCompleted: false,
selectedPlan: undefined,
pricingInfo: undefined,
couponCode: undefined,
isApplyingCoupon: false,
completeCheckout: () =>
set(
produce((state: ICheckoutStore) => {
state.isCompleted = true;
})
),
selectPlan: (plan) =>
set(
produce((state: ICheckoutStore) => {

View File

@@ -22,7 +22,7 @@ import Dialog from "./dialog";
import { getHomeRoute, hardNavigate } from "../../navigation";
import { appVersion } from "../../utils/version";
import Config from "../../utils/config";
import { isDesktop, isTesting } from "../../utils/platform";
import { isTesting } from "../../utils/platform";
import { useEffect } from "react";
import { ArrowRight, Checkmark, Icon, Warn } from "../icons";
@@ -89,9 +89,18 @@ const features: Record<FeatureKeys, Feature> = {
)
}
]
: isDesktop()
? []
: [],
: [
{
title: "Billing history",
subtitle:
"You can now get a list of all the transactions you have made, their amount and when they were made."
},
{
title: "Request refund",
subtitle:
"You can now request refunds directly from inside the app. No need for emails etc. Go to Settings > Request refund to send your request for a refund."
}
],
cta: {
title: "Got it",
icon: Checkmark,

View File

@@ -57,6 +57,9 @@ const EmailChangeDialog = React.lazy(() => import("./email-change-dialog"));
const LanguageSelectorDialog = React.lazy(
() => import("./language-selector-dialog")
);
const BillingHistoryDialog = React.lazy(
() => import("./billing-history-dialog")
);
export const Dialogs = {
AddNotebookDialog,
@@ -85,5 +88,6 @@ export const Dialogs = {
AddReminderDialog,
ReminderPreviewDialog,
EmailChangeDialog,
LanguageSelectorDialog
LanguageSelectorDialog,
BillingHistoryDialog
};

View File

@@ -200,6 +200,8 @@ function Autosuggest({
if (e.key === "ArrowDown" && !text) onOpenMenu(defaultItems.slice());
e.preventDefault();
} else if (e.key === "Tab") {
closeMenu();
}
}}
/>

View File

@@ -156,6 +156,7 @@ class EditorStore extends BaseStore {
}
});
appStore.setIsEditorOpen(true);
this.toggleProperties(false);
};
saveSession = async (sessionId, session) => {

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

@@ -19,22 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { expose, Remote, wrap } from "comlink";
import { updateStatus } from "../hooks/use-status";
import { db } from "../common/db";
import {
Gateway,
ItemReference,
NotebookReference,
Server,
Clip,
WEB_EXTENSION_CHANNEL_EVENTS
} from "@notesnook/web-clipper/dist/common/bridge";
import { isUserPremium } from "../hooks/use-is-user-premium";
import { store as themestore } from "../stores/theme-store";
import { store as appstore } from "../stores/app-store";
import { formatDate } from "@notesnook/core/utils/date";
import { h } from "./html";
import { sanitizeFilename } from "./filename";
import { attachFile } from "../components/editor/picker";
export class WebExtensionRelay {
private gateway?: Remote<Gateway>;
@@ -52,6 +40,8 @@ export class WebExtensionRelay {
async connect(): Promise<boolean> {
if (this.gateway) return true;
const { WebExtensionServer } = await import("./web-extension-server");
const channel = new MessageChannel();
channel.port1.start();
channel.port2.start();
@@ -76,96 +66,3 @@ export class WebExtensionRelay {
return true;
}
}
class WebExtensionServer implements Server {
async login() {
const user = await db.user?.getUser();
if (!user) return null;
const { theme, accent } = themestore.get();
return { email: user.email, pro: isUserPremium(user), accent, theme };
}
async getNotes(): Promise<ItemReference[] | undefined> {
await db.notes?.init();
return db.notes?.all
.filter((n) => !n.locked)
.map((note) => ({ id: note.id, title: note.title }));
}
async getNotebooks(): Promise<NotebookReference[] | undefined> {
return db.notebooks?.all.map((nb) => ({
id: nb.id,
title: nb.title,
topics: nb.topics.map((topic: ItemReference) => ({
id: topic.id,
title: topic.title
}))
}));
}
async getTags(): Promise<ItemReference[] | undefined> {
return db.tags?.all.map((tag) => ({
id: tag.id,
title: tag.title
}));
}
async saveClip(clip: Clip) {
let clipContent = "";
if (clip.mode === "simplified" || clip.mode === "screenshot") {
clipContent += clip.data;
} else {
const clippedFile = new File(
[new TextEncoder().encode(clip.data).buffer],
`${sanitizeFilename(clip.title)}.clip`,
{
type: "application/vnd.notesnook.web-clip"
}
);
const attachment = await attachFile(clippedFile);
if (!attachment) return;
clipContent += h("iframe", [], {
"data-hash": attachment.hash,
"data-mime": attachment.type,
src: clip.url,
title: clip.pageTitle || clip.title,
width: clip.width ? `${clip.width}` : undefined,
height: clip.height ? `${clip.height}` : undefined,
class: "web-clip"
}).outerHTML;
}
const note =
(clip.note?.id && db.notes?.note(clip.note?.id)) ||
db.notes?.note(await db.notes?.add({ title: clip.title }));
let content = (await note?.content()) || "";
content += clipContent;
content += h("div", [
h("hr"),
h("p", ["Clipped from ", h("a", [clip.title], { href: clip.url })]),
h("p", [`Date clipped: ${formatDate(Date.now())}`])
]).innerHTML;
await db.notes?.add({
id: note?.id,
content: { type: "tiptap", data: content }
});
if (clip.notebook && note) {
await db.notes?.addToNotebook(
{ id: clip.notebook.id, topic: clip.notebook.topic.id },
note.id
);
}
if (clip.tags && note) {
for (const tag of clip.tags) {
await db.tags?.add(tag, note.id);
}
}
await appstore.refresh();
}
}

View File

@@ -0,0 +1,131 @@
/*
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 { db } from "../common/db";
import {
ItemReference,
NotebookReference,
Server,
Clip
} from "@notesnook/web-clipper/dist/common/bridge";
import { isUserPremium } from "../hooks/use-is-user-premium";
import { store as themestore } from "../stores/theme-store";
import { store as appstore } from "../stores/app-store";
import { formatDate } from "@notesnook/core/utils/date";
import { h } from "./html";
import { sanitizeFilename } from "./filename";
import { attachFile } from "../components/editor/picker";
export class WebExtensionServer implements Server {
async login() {
const user = await db.user?.getUser();
const { theme, accent } = themestore.get();
if (!user) return { pro: false, theme, accent };
return { email: user.email, pro: isUserPremium(user), theme, accent };
}
async getNotes(): Promise<ItemReference[] | undefined> {
await db.notes?.init();
return db.notes?.all
.filter((n) => !n.locked)
.map((note) => ({ id: note.id, title: note.title }));
}
async getNotebooks(): Promise<NotebookReference[] | undefined> {
return db.notebooks?.all.map((nb) => ({
id: nb.id,
title: nb.title,
topics: nb.topics.map((topic: ItemReference) => ({
id: topic.id,
title: topic.title
}))
}));
}
async getTags(): Promise<ItemReference[] | undefined> {
return db.tags?.all.map((tag) => ({
id: tag.id,
title: tag.title
}));
}
async saveClip(clip: Clip) {
let clipContent = "";
if (clip.mode === "simplified" || clip.mode === "screenshot") {
clipContent += clip.data;
} else {
const clippedFile = new File(
[new TextEncoder().encode(clip.data).buffer],
`${sanitizeFilename(clip.title)}.clip`,
{
type: "application/vnd.notesnook.web-clip"
}
);
const attachment = await attachFile(clippedFile);
if (!attachment) return;
clipContent += h("iframe", [], {
"data-hash": attachment.hash,
"data-mime": attachment.type,
src: clip.url,
title: clip.pageTitle || clip.title,
width: clip.width ? `${clip.width}` : undefined,
height: clip.height ? `${clip.height}` : undefined,
class: "web-clip"
}).outerHTML;
}
const note = clip.note?.id ? db.notes?.note(clip.note?.id) : null;
let content = (await note?.content()) || "";
content += clipContent;
content += h("div", [
h("hr"),
h("p", ["Clipped from ", h("a", [clip.title], { href: clip.url })]),
h("p", [`Date clipped: ${formatDate(Date.now())}`])
]).innerHTML;
const id = await db.notes?.add({
id: note?.id,
title: note ? note.title : clip.title,
content: { type: "tiptap", data: content },
tags: note ? note.tags : clip.tags
});
if (clip.refs && id && !clip.note) {
for (const ref of clip.refs) {
switch (ref.type) {
case "notebook":
await db.notes?.addToNotebook({ id: ref.id }, id);
break;
case "topic":
await db.notes?.addToNotebook(
{ id: ref.parentId, topic: ref.id },
id
);
break;
}
}
}
await appstore.refresh();
}
}

View File

@@ -44,7 +44,8 @@ import {
showPromptDialog,
showEmailChangeDialog,
showLanguageSelectorDialog,
confirm
confirm,
showBillingHistoryDialog
} from "../common/dialog-controller";
import { TaskManager } from "../common/task-manager";
import { SUBSCRIPTION_STATUS } from "../common/constants";
@@ -1357,6 +1358,46 @@ function AccountStatus(props) {
tip="Update the payment method you used to purchase this subscription."
/>
</Button>
<Button
variant="list"
onClick={async () => {
const refundSubscription = await confirm({
title: "Request refund?",
message:
"You will only be issued a refund if you are eligible as per our refund policy. Your account will be immediately downgraded to Basic and your funds will be transferred to your account within 24 hours.",
negativeButtonText: "No",
positiveButtonText: "Yes"
});
if (refundSubscription) {
const error = await TaskManager.startTask({
id: "refund-subscription",
type: "modal",
title: "Requesting refund for your subscription",
subtitle: "Please wait...",
action: () => db.subscriptions.refund()
});
if (error instanceof Error) {
showToast("error", error.message);
} else {
showToast(
"success",
"Your refund has been issued. Please wait 24 hours before reaching out to us in case you do not receive your funds."
);
}
}
}}
>
<Tip
text="Request refund"
tip="If you are eligible for a refund, your account will be immediately downgraded to Basic and your funds will be transferred to your account within 24 hours."
/>
</Button>
<Button variant="list" onClick={() => showBillingHistoryDialog()}>
<Tip
text="Billing history"
tip="View all the transactions you have made with Notesnook."
/>
</Button>
<Button
variant="list"
sx={{ ":hover": { borderColor: "error" } }}
@@ -1379,11 +1420,8 @@ function AccountStatus(props) {
});
showToast(
"success",
"Your subscription has been cancelled."
"Your subscription has been canceled."
);
setTimeout(() => {
window.location.reload();
}, 5000);
}
} catch (e) {
showToast("error", e.message);

View File

@@ -31,39 +31,30 @@ const ACTION = {
default_popup: "popup.html"
};
const nnHost =
process.env.NODE_ENV === "production"
? "*://app.notesnook.com/*"
: "*://localhost/*";
const corsHost = "https://cors.notesnook.com/*";
const common = {
name: "Notesnook Web Clipper",
version: "0.1",
description: "Clip web pages.",
permissions: [
"activeTab",
"tabs",
"storage",
"contextMenus",
"notifications",
"<all_urls>"
],
content_scripts: [
{
js: ["nnContentScript.bundle.js"],
matches: ["*://app.notesnook.com/*", "*://localhost/*"]
},
{
js: ["contentScript.bundle.js"],
matches: ["http://*/*", "https://*/*"],
exclude_matches: ["*://app.notesnook.com/*", "*://localhost/*"]
}
],
browser_specific_settings: {
gecko: {
strict_min_version: "105.0"
}
},
version: "0.2.0",
description:
"Clip web pages & save interesting things you find on the web directly into Notesnook in a private & secure way.",
permissions: ["activeTab", "tabs", "storage", "notifications"],
icons: ICONS
};
const v2 = {
...common,
permissions: [...common.permissions, corsHost, nnHost],
optional_permissions: ["http://*/*", "https://*/*"],
browser_specific_settings: {
gecko: {
id: "notesnook-web-clipper-unlisted@notesnook.com",
strict_min_version: "105.0"
}
},
manifest_version: 2,
background: {
scripts: [BACKGROUND_SCRIPT]
@@ -73,6 +64,8 @@ const v2 = {
const v3 = {
...common,
host_permissions: [corsHost, nnHost],
optional_host_permissions: ["http://*/*", "https://*/*"],
manifest_version: 3,
background: {
service_worker: BACKGROUND_SCRIPT

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/web-clipper",
"version": "0.1.0",
"version": "0.2.0",
"private": true,
"main": "./dist/index.js",
"dependencies": {

View File

@@ -16,7 +16,7 @@ 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 { browser, Runtime } from "webextension-polyfill-ts";
import { browser, Runtime, Tabs } from "webextension-polyfill-ts";
import { Remote, wrap } from "comlink";
import { createEndpoint } from "./utils/comlink-extension";
import { Server } from "./common/bridge";
@@ -28,19 +28,48 @@ let api: Remote<Server> | undefined;
export async function connectApi(openNew = false, onDisconnect?: () => void) {
if (api) return api;
const tab = await getTab(openNew);
if (!tab) return false;
const tabs = await findNotesnookTabs(openNew);
for (const tab of tabs) {
try {
const api = await Promise.race([
connectToTab(tab, onDisconnect),
timeout(5000)
]);
if (!api) continue;
return api as Remote<Server>;
} catch (e) {
console.error(e);
}
}
return await new Promise<Remote<Server> | false>(function connect(resolve) {
if (!tab.id) return resolve(false);
return false;
}
const port = browser.tabs.connect(tab.id);
function timeout(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms, false));
}
port.onDisconnect.addListener(() => {
api = undefined;
onDisconnect?.();
async function connectToTab(tab: Tabs.Tab, onDisconnect?: () => void) {
if (!tab.id) return false;
if (browser.scripting) {
await browser.scripting.executeScript({
files: ["nnContentScript.bundle.js"],
target: { tabId: tab.id }
});
} else {
await browser.tabs.executeScript(tab.id, {
file: "nnContentScript.bundle.js"
});
}
const port = browser.tabs.connect(tab.id);
port.onDisconnect.addListener(() => {
api = undefined;
onDisconnect?.();
});
return new Promise<Remote<Server> | false>(function connect(resolve) {
async function onMessage(
message: WebExtensionChannelMessage,
port: Runtime.Port
@@ -58,23 +87,26 @@ export async function connectApi(openNew = false, onDisconnect?: () => void) {
});
}
async function getTab(openNew = false) {
export async function findNotesnookTabs(openNew = false) {
const tabs = await browser.tabs.query({
url: APP_URL_FILTER
url: APP_URL_FILTER,
discarded: false,
status: "complete"
});
if (tabs.length) return tabs[0];
if (tabs.length) return tabs;
if (openNew) {
const [tab] = await Promise.all([
browser.tabs.create({ url: APP_URL, active: false }),
new Promise((resolve) => {
browser.runtime.onMessage.addListener((message) => {
if (message.type === "start_connection") resolve(true);
});
const tab = await browser.tabs.create({ url: APP_URL, active: false });
await new Promise<void>((resolve) =>
browser.tabs.onUpdated.addListener(function onUpdated(id, info) {
if (id === tab.id && info.status === "complete") {
browser.tabs.onUpdated.removeListener(onUpdated);
resolve();
}
})
]);
return tab;
);
return [tab];
}
return undefined;
return [];
}

View File

@@ -22,7 +22,7 @@ export type ClipArea = "full-page" | "visible" | "selection" | "article";
export type ClipMode = "simplified" | "screenshot" | "complete";
export type User = {
email: string;
email?: string;
pro: boolean;
accent: string;
theme: "dark" | "light";
@@ -45,7 +45,18 @@ export interface Gateway {
connect(): ClientMetadata;
}
export type SelectedNotebook = ItemReference & { topic: ItemReference };
type SelectedTopicReference = ItemReference & {
type: "topic";
parentId: string;
};
type SelectedNotebookReference = ItemReference & {
type: "notebook";
};
export type SelectedReference =
| SelectedTopicReference
| SelectedNotebookReference;
export type Clip = {
url: string;
@@ -58,7 +69,7 @@ export type Clip = {
pageTitle?: string;
tags?: string[];
note?: ItemReference;
notebook?: SelectedNotebook;
refs?: SelectedReference[];
};
export interface Server {

View File

@@ -0,0 +1,62 @@
/*
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 { Button, Text } from "@theme-ui/components";
import { Icon } from "../icons/icon";
import { Icons } from "../icons";
type CheckListItemProps = {
title: string;
isSelected: boolean;
onSelected: () => void;
indentLevel?: number;
};
export function CheckListItem(props: CheckListItemProps) {
const { title, onSelected, isSelected, indentLevel = 0 } = props;
return (
<Button
variant="list"
onClick={onSelected}
sx={{
display: "flex",
alignItems: "center",
py: 1,
px: 1,
ml: indentLevel * 2
}}
>
<Icon
path={isSelected ? Icons.checkCircle : Icons.circle}
color={isSelected ? "primary" : "text"}
size={16}
/>
<Text
sx={{
fontSize: "13px",
ml: 1,
fontWeight: 400,
color: "text"
}}
>
{title}
</Text>
</Button>
);
}

View File

@@ -25,7 +25,7 @@ import { FlexScrollContainer } from "../scroll-container";
type FilteredListT<T> = {
placeholder: string;
getAll: () => Array<T>;
filter: (items: T[], query: string) => T[];
filter?: (items: T[], query: string) => T[];
onCreateNewItem?: (title: string) => void;
renderItem: (item: T, index: number) => JSX.Element;
itemName: string;
@@ -57,6 +57,7 @@ export function FilteredList<T>({
const _filter = useCallback(
(query) => {
if (!filter) return;
setItems(() => {
const items = getAll();
if (!query) {
@@ -83,27 +84,32 @@ export function FilteredList<T>({
return (
<>
<Input
sx={{
mt: 1,
mb: 1,
mr: 1,
fontSize: "body",
py: "7px",
color: "text"
}}
ref={inputRef}
autoFocus
placeholder={items.length <= 0 ? `Add a ${itemName}` : placeholder}
onChange={(e) => {
_filter(e.target.value);
}}
onKeyUp={async (e) => {
if (e.key === "Enter" && noItemsFound) {
_createNewItem(query);
{(onCreateNewItem || filter) && (
<Input
sx={{
m: 1,
width: "auto",
fontSize: "body",
py: "7px",
color: "text"
}}
ref={inputRef}
autoFocus
placeholder={
items.length <= 0 && onCreateNewItem
? `Add a ${itemName}`
: placeholder
}
}}
/>
onChange={(e) => {
_filter(e.target.value);
}}
onKeyUp={async (e) => {
if (e.key === "Enter" && noItemsFound && onCreateNewItem) {
_createNewItem(query);
}
}}
/>
)}
<FlexScrollContainer>
<Flex

View File

@@ -38,7 +38,12 @@ import {
mdiMagnify,
mdiViewDayOutline,
mdiViewDashboardOutline,
mdiArrowLeft
mdiArrowLeft,
mdiCheckCircleOutline,
mdiCircleOutline,
mdiBookOutline,
mdiBookmarkOutline,
mdiPound
} from "@mdi/js";
export const Icons = {
@@ -67,6 +72,12 @@ export const Icons = {
chevronUp: mdiChevronUp,
chevronRight: mdiChevronRight,
checkCircle: mdiCheckCircleOutline,
circle: mdiCircleOutline,
notebook: mdiBookOutline,
topic: mdiBookmarkOutline,
tag: mdiPound,
none: "",
back: mdiArrowLeft
};

View File

@@ -0,0 +1,55 @@
/*
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 { Flex, Text } from "@theme-ui/components";
import { Icon } from "../icons/icon";
import { SchemeColors } from "@notesnook/theme/dist/theme/colorscheme";
export function InlineTag(props: {
title: string;
icon: string;
iconColor?: keyof SchemeColors;
onClick: () => void;
}) {
const { title, icon, onClick, iconColor = "icon" } = props;
return (
<Flex
onClick={onClick}
sx={{
alignItems: "center",
justifyContent: "center",
bg: "bgSecondary",
border: "1px solid var(--border)",
borderRadius: "small",
p: "3px",
pr: 1,
cursor: "pointer",
":hover": {
bg: "hover"
}
}}
>
<Icon path={icon} color={iconColor} size={14} />
<Text variant="subBody" sx={{ color: "icon", ml: 1 }}>
{title}
</Text>
</Flex>
);
}

View File

@@ -24,6 +24,7 @@ import { Icon } from "../icons/icon";
import { Icons } from "../icons";
import { useAppStore } from "../../stores/app-store";
import { Picker } from "../picker";
import { CheckListItem } from "../check-list-item";
type NotePickerProps = {
selectedNote?: ItemReference;
@@ -95,7 +96,7 @@ export const NotePicker = (props: NotePickerProps) => {
)}
</Flex>
<Picker onClose={close} isOpen={modalVisible}>
<Picker onClose={close} onDone={() => {}} isOpen={modalVisible}>
<FilteredList
getAll={() => notes}
filter={(items, query) =>
@@ -105,12 +106,13 @@ export const NotePicker = (props: NotePickerProps) => {
placeholder={"Search for a note"}
refreshItems={() => notes}
renderItem={(note) => (
<Note
note={note}
<CheckListItem
title={note.title}
onSelected={() => {
onSelected(note);
close();
}}
isSelected={selectedNote?.id === note.id}
/>
)}
/>
@@ -118,17 +120,3 @@ export const NotePicker = (props: NotePickerProps) => {
</>
);
};
function Note({
note,
onSelected
}: {
note: ItemReference;
onSelected: () => void;
}) {
return (
<Button onClick={onSelected} variant="list" sx={{ py: "7px" }}>
<Text variant="body">{note.title}</Text>
</Button>
);
}

View File

@@ -17,100 +17,120 @@ 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 { Button, Flex, Text } from "@theme-ui/components";
import { Flex } from "@theme-ui/components";
import { FilteredList } from "../filtered-list";
import {
NotebookReference,
ItemReference,
SelectedNotebook
SelectedReference
} from "../../common/bridge";
import { Icon } from "../icons/icon";
import { Icons } from "../icons";
import { useAppStore } from "../../stores/app-store";
import { Picker } from "../picker";
import { InlineTag } from "../inline-tag";
import { CheckListItem } from "../check-list-item";
type NotebookPickerProps = {
selectedNotebook?: SelectedNotebook;
onSelected: (notebook?: SelectedNotebook) => void;
selectedItems: SelectedReference[];
onSelected: (items?: SelectedReference[]) => void;
};
export const NotebookPicker = (props: NotebookPickerProps) => {
const { selectedNotebook, onSelected } = props;
const { onSelected } = props;
const [modalVisible, setModalVisible] = useState(false);
const [expanded, setExpanded] = useState<string | null>(null);
const notebooks = useAppStore((s) => s.notebooks);
const [selectedItems, setSelectedItems] = useState<SelectedReference[]>(
props.selectedItems
);
const close = () => {
setExpanded(null);
setModalVisible(false);
};
const open = () => setModalVisible(true);
const open = () => {
setSelectedItems(props.selectedItems);
setModalVisible(true);
};
return (
<>
<Flex sx={{ alignItems: "center" }}>
<Button
variant="tool"
<Flex
sx={{
border: "1px solid var(--border)",
p: 1,
gap: 1,
borderRadius: "default",
flexWrap: "wrap"
}}
>
{props.selectedItems && props.selectedItems.length
? props.selectedItems.map((item) => (
<InlineTag
key={item.id}
title={item.title}
icon={item.type === "topic" ? Icons.topic : Icons.notebook}
onClick={() =>
setSelectedItems((items) => {
const copy = items.slice();
const index = copy.indexOf(item);
if (index > -1) copy.splice(index, 1);
onSelected(copy);
return copy;
})
}
/>
))
: null}
<InlineTag
title={selectedItems.length ? "Add more" : "Add to notebook"}
icon={Icons.plus}
iconColor="primary"
onClick={open}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flex: 1,
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
height: 33
}}
title={
selectedNotebook
? `${selectedNotebook.title} > ${selectedNotebook.topic.title}`
: `Select a notebook`
}
>
<Text variant="text">
{selectedNotebook
? `${selectedNotebook.title} > ${selectedNotebook.topic.title}`
: `Select a notebook`}
</Text>
<Icon path={Icons.chevronDown} color="text" size={18} />
</Button>
{selectedNotebook && (
<Button
variant="tool"
onClick={() => onSelected(undefined)}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexShrink: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
height: 33
}}
title={"Clear selection"}
>
<Icon path={Icons.close} color="text" size={16} />
</Button>
)}
/>
</Flex>
<Picker onClose={close} isOpen={modalVisible}>
<Picker
onClose={close}
onDone={() => {
onSelected(selectedItems);
close();
}}
isOpen={modalVisible}
>
<FilteredList
getAll={() => notebooks}
getAll={() => useAppStore.getState().notebooks}
filter={(items, query) =>
items.filter((item) => item.title.toLowerCase().indexOf(query) > -1)
}
itemName="notebook"
placeholder={"Search for a notebook"}
refreshItems={() => notebooks}
refreshItems={() => useAppStore.getState().notebooks}
renderItem={(item) => (
<Notebook
notebook={item}
isExpanded={expanded === item.id}
onExpand={(id) => setExpanded(id || null)}
onSelected={(notebook) => {
onSelected(notebook);
close();
isTopicSelected={(topic) =>
!!selectedItems.find(
(n) =>
n.id === topic.id &&
n.type === "topic" &&
n.parentId === item.id
)
}
isSelected={
!!selectedItems.find(
(n) => n.id === item.id && n.type === "notebook"
)
}
onSelected={(ref) => {
setSelectedItems((items) => {
const copy = items.slice();
const index = copy.findIndex(
(n) => n.id === ref.id && n.type === ref.type
);
if (index > -1) {
copy.splice(index, 1);
} else {
copy.push(ref);
}
return copy;
});
}}
/>
)}
@@ -122,12 +142,12 @@ export const NotebookPicker = (props: NotebookPickerProps) => {
type NotebookProps = {
notebook: NotebookReference;
isExpanded: boolean;
onExpand: (notebookId?: string) => void;
onSelected: (notebook: SelectedNotebook) => void;
isSelected: boolean;
onSelected: (notebook: SelectedReference) => void;
isTopicSelected: (topic: ItemReference) => boolean;
};
function Notebook(props: NotebookProps) {
const { notebook, isExpanded, onExpand, onSelected } = props;
const { notebook, isSelected, onSelected, isTopicSelected } = props;
return (
<Flex
@@ -136,69 +156,39 @@ function Notebook(props: NotebookProps) {
overflow: "hidden"
}}
>
<Button
variant="list"
onClick={() => {
if (isExpanded) return onExpand();
onExpand(notebook.id);
<CheckListItem
title={notebook.title}
isSelected={isSelected}
onSelected={() => {
onSelected({
id: notebook.id,
title: notebook.title,
type: "notebook"
});
}}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: isExpanded ? "border" : "transparent",
py: "7px",
px: 1
}}
>
<Text
sx={{
fontSize: "13px",
fontWeight: 400,
color: "var(--paragraph)"
}}
>
{notebook.title}
</Text>
<Icon
path={isExpanded ? Icons.chevronUp : Icons.chevronDown}
color="text"
size={18}
/>
</Button>
/>
{isExpanded ? (
<FilteredList
getAll={() => notebook.topics}
filter={(items, query) =>
items.filter((item) => item.title.toLowerCase().indexOf(query) > -1)
}
itemName="topic"
placeholder={"Search for a topic"}
refreshItems={() => notebook.topics}
renderItem={(topic) => (
<Topic
topic={topic}
onSelected={() => {
onSelected({ id: notebook.id, title: notebook.title, topic });
}}
/>
)}
/>
) : null}
<FilteredList
getAll={() => notebook.topics}
itemName="topic"
placeholder={"Search for a topic"}
refreshItems={() => notebook.topics}
renderItem={(topic) => (
<CheckListItem
title={topic.title}
isSelected={isTopicSelected(topic)}
indentLevel={1}
onSelected={() => {
onSelected({
id: topic.id,
title: topic.title,
type: "topic",
parentId: notebook.id
});
}}
/>
)}
/>
</Flex>
);
}
type TopicProps = {
topic: ItemReference;
onSelected: () => void;
};
function Topic(props: TopicProps) {
const { topic, onSelected } = props;
return (
<Button variant="list" onClick={onSelected} sx={{ pl: 3, py: "7px" }}>
<Text variant="text">{topic.title}</Text>
</Button>
);
}

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { PropsWithChildren } from "react";
import Modal from "react-modal";
import { Flex } from "@theme-ui/components";
import { Button, Flex } from "@theme-ui/components";
import { ThemeProvider } from "../theme-provider";
Modal.setAppElement("#root");
@@ -30,26 +30,27 @@ const customStyles = {
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
boxShadow: "0px 1px 10px var(--info)",
boxShadow: "0px 1px 10px #00000055",
border: "none",
borderRadius: 5,
backgroundColor: "var(--background)",
padding: "10px",
height: "80vh",
width: "85vw",
display: "flex",
flexDirection: "column",
overflow: "hidden"
overflow: "hidden",
padding: 0
} as const
};
type PickerProps = {
isOpen: boolean;
onClose: () => void;
onDone: () => void;
};
export const Picker = (props: PropsWithChildren<PickerProps>) => {
const { children, isOpen, onClose } = props;
const { children, isOpen, onClose, onDone } = props;
return (
<Modal
@@ -66,10 +67,25 @@ export const Picker = (props: PropsWithChildren<PickerProps>) => {
<Flex
sx={{
flexDirection: "column",
overflow: "hidden"
overflow: "hidden",
height: "100%"
}}
>
{children}
<Flex
sx={{
flexDirection: "column",
overflow: "hidden",
flex: 1,
padding: 2
}}
>
{children}
</Flex>
<Flex sx={{ bg: "bgSecondary", p: 1, justifyContent: "end" }}>
<Button variant="dialog" onClick={onDone}>
Done
</Button>
</Flex>
</Flex>
</ThemeProvider>
</Modal>

View File

@@ -17,29 +17,34 @@ 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 { Button, Flex, Text } from "@theme-ui/components";
import { Flex } from "@theme-ui/components";
import { FilteredList } from "../filtered-list";
import { ItemReference } from "../../common/bridge";
import { Icon } from "../icons/icon";
import { Icons } from "../icons";
import { useAppStore } from "../../stores/app-store";
import { Picker } from "../picker";
import { InlineTag } from "../inline-tag";
import { CheckListItem } from "../check-list-item";
type TagPickerProps = {
selectedTags: string[];
onSelected: (tag: string) => void;
onDeselected: (tag: string) => void;
onSelected: (tags: string[]) => void;
};
export const TagPicker = (props: TagPickerProps) => {
const { selectedTags, onSelected, onDeselected } = props;
const { onSelected } = props;
const [modalVisible, setModalVisible] = useState(false);
const tags = useAppStore((s) => s.tags);
const [selectedTags, setSelectedTags] = useState<string[]>(
props.selectedTags || []
);
const close = () => {
setModalVisible(false);
};
const open = () => setModalVisible(true);
const open = () => {
setSelectedTags(props.selectedTags || []);
setModalVisible(true);
};
return (
<>
@@ -47,59 +52,66 @@ export const TagPicker = (props: TagPickerProps) => {
sx={{
border: "2px solid var(--border)",
p: 1,
pb: 0,
borderRadius: "default",
flexWrap: "wrap"
flexWrap: "wrap",
gap: 1
}}
>
{selectedTags.length
? selectedTags.map((tag) => (
{props.selectedTags.length
? props.selectedTags.map((tag) => (
<InlineTag
key={tag}
tag={tag}
onDeselected={() => {
onDeselected(tag);
icon={Icons.tag}
title={tag}
onClick={() => {
setSelectedTags((items) => {
const copy = items.slice();
const index = copy.indexOf(tag);
if (index > -1) copy.splice(index, 1);
onSelected(copy);
return copy;
});
}}
/>
))
: null}
<Flex
<InlineTag
icon={Icons.plus}
title={"Assign a tag"}
iconColor="primary"
onClick={open}
sx={{
bg: "bgSecondary",
border: "1px solid var(--border)",
borderRadius: "small",
mr: 1,
px: 1,
mb: 1,
cursor: "pointer",
":hover": {
bg: "hover"
}
}}
title="Click to assign more tags"
>
<Icon path={Icons.plus} size={12} color="fontTertiary" />
<Text variant="subBody" sx={{ ml: "2px", color: "icon" }}>
Assign a tag
</Text>
</Flex>
/>
</Flex>
<Picker onClose={close} isOpen={modalVisible}>
<Picker
onClose={close}
onDone={() => {
onSelected(selectedTags);
close();
}}
isOpen={modalVisible}
>
<FilteredList
getAll={() => tags}
getAll={() => useAppStore.getState().tags}
filter={(items, query) =>
items.filter((item) => item.title.toLowerCase().indexOf(query) > -1)
}
itemName="tag"
placeholder={"Search for a tag"}
onCreateNewItem={(tag) => onSelected(tag)}
refreshItems={() => tags}
refreshItems={() => useAppStore.getState().tags}
renderItem={(tag) => (
<Tag
tag={tag}
<CheckListItem
title={`#${tag.title}`}
onSelected={() => {
onSelected(tag.title);
setSelectedTags((items) => {
const copy = items.slice();
const index = copy.indexOf(tag.title);
if (index > -1) {
copy.splice(index, 1);
} else {
copy.push(tag.title);
}
return copy;
});
}}
isSelected={selectedTags.indexOf(tag.title) > -1}
/>
@@ -109,64 +121,3 @@ export const TagPicker = (props: TagPickerProps) => {
</>
);
};
type TagProps = {
tag: ItemReference;
isSelected: boolean;
onSelected: () => void;
};
function Tag(props: TagProps) {
const { tag, onSelected, isSelected } = props;
return (
<Button
variant="list"
onClick={onSelected}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
py: "7px",
px: 1
}}
>
<Text
sx={{
fontSize: "13px",
fontWeight: 400,
color: "var(--paragraph)"
}}
>
#{tag.title}
</Text>
{isSelected ? <Icon path={Icons.check} color="text" size={14} /> : null}
</Button>
);
}
function InlineTag(props: { tag: string; onDeselected: () => void }) {
const { tag, onDeselected } = props;
return (
<Flex
onClick={onDeselected}
sx={{
bg: "bgSecondary",
border: "1px solid var(--border)",
borderRadius: "small",
mr: 1,
px: 1,
mb: 1,
cursor: "pointer",
":hover": {
bg: "hover"
}
}}
title="Click to remove"
>
<Text variant="subBody" sx={{ color: "icon" }}>
#{tag}
</Text>
</Flex>
);
}

View File

@@ -26,43 +26,38 @@ import {
WEB_EXTENSION_CHANNEL_EVENTS
} from "../common/bridge";
let mainPort: MessagePort | undefined;
browser.runtime.onConnect.addListener((port) => {
if (mainPort) {
const server: Remote<Server> = wrap<Server>(mainPort);
expose(
{
login: () => server.login(),
getNotes: () => server.getNotes(),
getNotebooks: () => server.getNotebooks(),
getTags: () => server.getTags(),
saveClip: (clip: Clip) => server.saveClip(clip)
},
createEndpoint(port)
);
port.postMessage({ success: true });
} else {
port.postMessage({ success: false });
}
});
window.addEventListener("message", (ev) => {
const { type } = ev.data;
switch (type) {
case WEB_EXTENSION_CHANNEL_EVENTS.ON_CREATED:
if (ev.ports.length) {
const mainPort = ev.ports.at(0);
if (mainPort) {
expose(new BackgroundGateway(), mainPort);
const server: Remote<Server> = wrap<Server>(mainPort);
expose(
{
login: () => server.login(),
getNotes: () => server.getNotes(),
getNotebooks: () => server.getNotebooks(),
getTags: () => server.getTags(),
saveClip: (clip: Clip) => server.saveClip(clip)
},
createEndpoint(port)
);
port.postMessage({ success: true });
} else {
port.postMessage({ success: false });
}
}
break;
}
});
window.addEventListener("message", (ev) => {
const { type } = ev.data;
switch (type) {
case WEB_EXTENSION_CHANNEL_EVENTS.ON_CREATED:
if (ev.ports.length) {
const [port] = ev.ports;
mainPort = port;
expose(new BackgroundGateway(), port);
browser.runtime.sendMessage(undefined, { type: "start_connection" });
}
break;
}
window.postMessage({ type: WEB_EXTENSION_CHANNEL_EVENTS.ON_READY }, "*");
});
window.postMessage({ type: WEB_EXTENSION_CHANNEL_EVENTS.ON_READY }, "*");
class BackgroundGateway implements Gateway {
connect() {
return {

View File

@@ -61,7 +61,9 @@ export const useAppStore = create<AppStore>((set) => ({
if (!notesnook) {
set({ isLoggingIn: false });
return;
throw new Error(
"Please refresh the Notesnook web app to connect with the Web Clipper."
);
}
const user = await notesnook.login();

View File

@@ -26,7 +26,7 @@ import { NotebookPicker } from "../components/notebook-picker";
import { TagPicker } from "../components/tag-picker";
import {
ItemReference,
SelectedNotebook,
SelectedReference,
ClipArea,
ClipMode,
ClipData
@@ -39,6 +39,10 @@ import { FlexScrollContainer } from "../components/scroll-container";
import { DEFAULT_SETTINGS, SETTINGS_KEY } from "./settings";
import type { Config } from "@notesnook/clipper/dist/types";
const ERROR_MAP: Record<string, string> = {
"Could not establish connection. Receiving end does not exist.":
"Please refresh this web page and try again. If the error persists, it means you don't have the required permission."
};
const clipAreas: { name: string; id: ClipArea; icon: string }[] = [
{
name: "Full page",
@@ -92,6 +96,7 @@ export function Main() {
const [settings] = usePersistentState<Config>(SETTINGS_KEY, DEFAULT_SETTINGS);
const [title, setTitle] = useState<string>();
const [hasPermission, setHasPermission] = useState<boolean>(false);
const [url, setUrl] = useState<string>();
const [clipNonce, setClipNonce] = useState(0);
const [clipMode, setClipMode] = usePersistentState<ClipMode>(
@@ -104,15 +109,18 @@ export function Main() {
);
const [isClipping, setIsClipping] = useState(false);
const [note, setNote] = usePersistentState<ItemReference>("note");
const [notebook, setNotebook] =
usePersistentState<SelectedNotebook>("notebook");
const [refs, setRefs] = usePersistentState<SelectedReference[]>("refs", []);
const [tags, setTags] = usePersistentState<string[]>("tags", []);
const [clipData, setClipData] = useState<ClipData>();
const pageTitle = useRef<string>();
useEffect(() => {
(async () => {
const [tab] = await browser.tabs.query({ active: true });
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true,
windowType: "normal"
});
setTitle(tab?.title ? tab.title : "Untitled");
setUrl(tab?.url);
@@ -123,6 +131,13 @@ export function Main() {
useEffect(() => {
(async () => {
if (!clipArea || !clipMode) return;
if (
!isPremium &&
(clipMode === "complete" || clipMode === "screenshot")
) {
setClipMode("simplified");
return;
}
try {
setIsClipping(true);
@@ -134,7 +149,58 @@ export function Main() {
setIsClipping(false);
}
})();
}, [clipArea, clipMode, clipNonce]);
}, [isPremium, clipArea, clipMode, clipNonce]);
useEffect(() => {
(async () => {
if (!settings || !settings.corsProxy) return;
setHasPermission(
await browser.permissions.contains({
origins: [`${settings.corsProxy}/*`]
})
);
})();
}, [settings]);
if (!hasPermission && !!settings?.corsProxy) {
return (
<FlexScrollContainer style={{ maxHeight: 560 }}>
<Flex
sx={{
flexDirection: "column",
p: 2,
width: 320,
backgroundColor: "background"
}}
>
<Text
variant="subtitle"
sx={{ mt: 2, mb: 1, color: "icon", fontSize: "body" }}
>
Permission required
</Text>
<Text
variant="body"
sx={{ mt: 2, mb: 1, color: "icon", fontSize: "body" }}
>
Permission is required to use the CORS proxy: {settings.corsProxy}
</Text>
<Button
onClick={async () => {
if (!settings.corsProxy) return;
setHasPermission(
await browser.permissions.request({
origins: [`${settings.corsProxy}/*`]
})
);
}}
>
Grant permission
</Button>
</Flex>
</FlexScrollContainer>
);
}
return (
<FlexScrollContainer style={{ maxHeight: 560 }}>
@@ -290,9 +356,11 @@ export function Main() {
setClipNonce((s) => ++s);
}}
>
{error}
{ERROR_MAP[error] || error}
<br />
Click here to retry.
<Text sx={{ fontSize: 11, color: "error" }}>
Click here to try again.
</Text>
</Text>
)}
@@ -303,7 +371,7 @@ export function Main() {
Organization
</Text>
{notebook || tags?.length ? null : (
{refs?.length || tags?.length ? null : (
<NotePicker
selectedNote={note}
onSelected={(note) => setNote(note)}
@@ -311,34 +379,19 @@ export function Main() {
)}
{note ? null : (
<>
{notebook || tags?.length ? null : (
{refs?.length || tags?.length ? null : (
<Text variant="subBody" sx={{ my: 1, textAlign: "center" }}>
or
</Text>
)}
<NotebookPicker
selectedNotebook={notebook}
onSelected={(notebook) => setNotebook(notebook)}
selectedItems={refs || []}
onSelected={(items) => setRefs(items)}
/>
<Box sx={{ mt: 1 }} />
<TagPicker
selectedTags={tags || []}
onDeselected={(tag) => {
setTags((tags) => {
const copy = tags?.slice() || [];
copy.splice(copy.indexOf(tag), 1);
return copy;
});
}}
onSelected={(tag) => {
setTags((tags) => {
const copy = tags?.slice() || [];
if (copy.indexOf(tag) > -1) {
copy.splice(copy.indexOf(tag), 1);
} else copy.push(tag);
return copy;
});
}}
onSelected={(tags) => setTags(tags)}
/>
</>
)}
@@ -360,12 +413,21 @@ export function Main() {
mode: clipMode,
tags,
note,
notebook,
refs,
pageTitle: pageTitle.current,
...clipData
});
setClipData(undefined);
await browser.notifications?.create({
title: "Clip saved!",
message: "Open the Notesnook app to view the result.",
type: "basic",
iconUrl: browser.runtime.getURL("256x256.png"),
isClickable: false
});
window.close();
}}
>
@@ -406,9 +468,24 @@ export async function clip(
return { data: clipData };
}
const [tab] = await browser.tabs.query({ active: true });
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true,
windowType: "normal"
});
if (!tab || !tab.id) return;
if (browser.scripting) {
await browser.scripting.executeScript({
files: ["contentScript.bundle.js"],
target: { tabId: tab.id }
});
} else {
await browser.tabs.executeScript(tab.id, {
file: "contentScript.bundle.js"
});
}
if (area === "visible" && mode === "screenshot") {
if (!tab.height || !tab.width) return;

View File

@@ -45,7 +45,7 @@ export function Settings() {
backgroundColor: "background"
}}
as="form"
onSubmit={(e) => {
onSubmit={async (e) => {
e.preventDefault();
const form = new FormData(e.target as HTMLFormElement);
@@ -56,6 +56,10 @@ export function Settings() {
corsProxy = `${url.protocol}//${url.hostname}`;
}
await browser.permissions.request({
origins: [`${corsProxy}/*`]
});
saveSettings({
corsProxy
});

View File

@@ -0,0 +1,5 @@
- 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

@@ -29,7 +29,8 @@
},
"scripts": {
"build": "tsc",
"test": "playwright test"
"test": "playwright test",
"postinstall": "patch-package"
},
"bugs": {
"url": "https://github.com/streetwriters/notesnook/issues"

View File

@@ -0,0 +1,24 @@
diff --git a/node_modules/css-what/lib/commonjs/stringify.js b/node_modules/css-what/lib/commonjs/stringify.js
index 158b180..89b5b6c 100644
--- a/node_modules/css-what/lib/commonjs/stringify.js
+++ b/node_modules/css-what/lib/commonjs/stringify.js
@@ -11,7 +11,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringify = void 0;
var types_1 = require("./types");
-var attribValChars = ["\\", '"'];
+var attribValChars = ["\\", '"', "%", "'"];
var pseudoValChars = __spreadArray(__spreadArray([], attribValChars, true), ["(", ")"], false);
var charsToEscapeInAttributeValue = new Set(attribValChars.map(function (c) { return c.charCodeAt(0); }));
var charsToEscapeInPseudoValue = new Set(pseudoValChars.map(function (c) { return c.charCodeAt(0); }));
diff --git a/node_modules/css-what/lib/es/stringify.js b/node_modules/css-what/lib/es/stringify.js
index 5f3b8ef..0ec34f7 100644
--- a/node_modules/css-what/lib/es/stringify.js
+++ b/node_modules/css-what/lib/es/stringify.js
@@ -1,5 +1,5 @@
import { SelectorType, AttributeAction } from "./types";
-const attribValChars = ["\\", '"'];
+const attribValChars = ["\\", '"', "%", "'"];
const pseudoValChars = [...attribValChars, "(", ")"];
const charsToEscapeInAttributeValue = new Set(attribValChars.map((c) => c.charCodeAt(0)));
const charsToEscapeInPseudoValue = new Set(pseudoValChars.map((c) => c.charCodeAt(0)));

View File

@@ -463,7 +463,7 @@ async function getPage(
fetchOptions: resolveFetchOptions(config),
inlineOptions: {
fonts: false,
images: true,
images: styles,
stylesheets: styles
},
styles,

View File

@@ -34,11 +34,30 @@ export default class Subscriptions {
await http.delete(`${hosts.SUBSCRIPTIONS_HOST}/subscriptions`, token);
}
async refund() {
const token = await this._tokenManager.getAccessToken();
if (!token) return;
await http.post(
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/refund`,
null,
token
);
}
async transactions() {
const token = await this._tokenManager.getAccessToken();
if (!token) return;
return await http.get(
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/transactions`,
token
);
}
async updateUrl() {
const token = await this._tokenManager.getAccessToken();
if (!token) return;
return await http.get(
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/update_url`,
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/update`,
token
);
}

View File

@@ -21,6 +21,7 @@ import { migrateItem } from "../../migrations";
import setManipulator from "../../utils/set";
import { logger } from "../../logger";
import { isHTMLEqual } from "../../utils/html-diff";
import { EV, EVENTS } from "../../common";
class Merger {
/**
@@ -225,8 +226,8 @@ class Merger {
if (!type || !item || !definition) return;
if (!this.key) this.key = await this._db.user.getEncryptionKey();
if (!this.key.key || !this.key.salt) {
await this._db.user.logout(true, "User encryption key not generated.");
if (!this.key || !this.key.key || !this.key.salt) {
EV.publish(EVENTS.userSessionExpired);
throw new Error("User encryption key not generated. Please relogin.");
}

View File

@@ -347,11 +347,12 @@ export default class Notes extends Collection {
for (const noteId of noteIds) {
const note = this.note(noteId);
if (!note || note.deleted || !note.notebooks) {
if (!note || note.deleted) {
continue;
}
if (topicId) {
if (!note.notebooks) continue;
const { notebooks } = note;
const notebook = findById(notebooks, notebookId);

View File

@@ -8,6 +8,7 @@
"name": "@notesnook/editor-mobile",
"version": "1.0.0",
"dependencies": {
"@emotion/react": "^11.10.0",
"@mdi/js": "^6.9.96",
"@mdi/react": "^1.6.0",
"framer-motion": "^6.5.1",
@@ -85,7 +86,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.18.6"
},
@@ -370,7 +370,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.6"
},
@@ -492,7 +491,6 @@
"version": "7.19.4",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -501,7 +499,6 @@
"version": "7.19.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -548,7 +545,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
@@ -562,7 +558,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -574,7 +569,6 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -588,7 +582,6 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
@@ -596,14 +589,12 @@
"node_modules/@babel/highlight/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -612,7 +603,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -621,7 +611,6 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
@@ -2023,7 +2012,6 @@
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.11"
},
@@ -2083,7 +2071,6 @@
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.0.tgz",
"integrity": "sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
@@ -2386,6 +2373,59 @@
"postcss-selector-parser": "^6.0.10"
}
},
"node_modules/@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0",
"source-map": "^0.5.7",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@emotion/cache": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
"integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
"dependencies": {
"@emotion/memoize": "^0.8.1",
"@emotion/sheet": "^1.2.2",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/cache/node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/hash": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
},
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
@@ -2401,6 +2441,74 @@
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"node_modules/@emotion/react": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.0.tgz",
"integrity": "sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"hoist-non-react-statics": "^3.3.1"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"dependencies": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.1",
"csstype": "^3.0.2"
}
},
"node_modules/@emotion/serialize/node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/sheet": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
},
"node_modules/@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
},
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@emotion/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
},
"node_modules/@emotion/weak-memoize": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"node_modules/@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
@@ -4235,8 +4343,7 @@
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"node_modules/@types/prettier": {
"version": "2.7.2",
@@ -5520,7 +5627,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
@@ -5895,7 +6001,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -6357,8 +6462,7 @@
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/cookie": {
"version": "0.5.0",
@@ -6420,7 +6524,6 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
"dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -6906,8 +7009,7 @@
"node_modules/csstype": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
"dev": true
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -7497,7 +7599,6 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@@ -7626,7 +7727,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -8878,6 +8978,11 @@
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -9249,8 +9354,7 @@
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/function.prototype.name": {
"version": "1.1.5",
@@ -9511,7 +9615,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -9590,6 +9693,19 @@
"resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz",
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -9901,7 +10017,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -9917,7 +10032,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -10037,8 +10151,7 @@
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
},
"node_modules/is-bigint": {
"version": "1.0.4",
@@ -10108,7 +10221,6 @@
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
@@ -12766,8 +12878,7 @@
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema": {
"version": "0.4.0",
@@ -12915,8 +13026,7 @@
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/loader-runner": {
"version": "4.3.0",
@@ -13783,7 +13893,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -13795,7 +13904,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -13962,8 +14070,7 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
@@ -13975,7 +14082,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -16125,8 +16231,7 @@
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"dev": true
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/regenerator-transform": {
"version": "0.15.0",
@@ -16266,7 +16371,6 @@
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
@@ -17237,6 +17341,11 @@
"postcss": "^8.2.15"
}
},
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -17266,7 +17375,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -17661,7 +17769,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -19055,7 +19162,6 @@
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -19160,7 +19266,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"dev": true,
"requires": {
"@babel/highlight": "^7.18.6"
}
@@ -19373,7 +19478,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true,
"requires": {
"@babel/types": "^7.18.6"
}
@@ -19464,14 +19568,12 @@
"@babel/helper-string-parser": {
"version": "7.19.4",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
"dev": true
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw=="
},
"@babel/helper-validator-identifier": {
"version": "7.19.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"dev": true
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
},
"@babel/helper-validator-option": {
"version": "7.18.6",
@@ -19506,7 +19608,6 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
@@ -19517,7 +19618,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
@@ -19526,7 +19626,6 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -19537,7 +19636,6 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -19545,26 +19643,22 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
@@ -20501,7 +20595,6 @@
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.11"
}
@@ -20549,7 +20642,6 @@
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.0.tgz",
"integrity": "sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==",
"dev": true,
"requires": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
@@ -20702,6 +20794,60 @@
"integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==",
"dev": true
},
"@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"requires": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0",
"source-map": "^0.5.7",
"stylis": "4.2.0"
},
"dependencies": {
"@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
}
}
},
"@emotion/cache": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
"integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
"requires": {
"@emotion/memoize": "^0.8.1",
"@emotion/sheet": "^1.2.2",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"stylis": "4.2.0"
},
"dependencies": {
"@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
}
}
},
"@emotion/hash": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
},
"@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
@@ -20717,6 +20863,65 @@
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"@emotion/react": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.0.tgz",
"integrity": "sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==",
"requires": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"hoist-non-react-statics": "^3.3.1"
}
},
"@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"requires": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.1",
"csstype": "^3.0.2"
},
"dependencies": {
"@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
}
}
},
"@emotion/sheet": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
},
"@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
},
"@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw=="
},
"@emotion/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
},
"@emotion/weak-memoize": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
@@ -22141,8 +22346,7 @@
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"@types/prettier": {
"version": "2.7.2",
@@ -23132,7 +23336,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
@@ -23432,8 +23635,7 @@
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"camel-case": {
"version": "4.1.2",
@@ -23800,8 +24002,7 @@
"convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"cookie": {
"version": "0.5.0",
@@ -23846,7 +24047,6 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
"dev": true,
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -24181,8 +24381,7 @@
"csstype": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
"dev": true
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
},
"damerau-levenshtein": {
"version": "1.0.8",
@@ -24630,7 +24829,6 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -24740,8 +24938,7 @@
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"escodegen": {
"version": "2.0.0",
@@ -25691,6 +25888,11 @@
"pkg-dir": "^4.1.0"
}
},
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -25951,8 +26153,7 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"function.prototype.name": {
"version": "1.1.5",
@@ -26146,7 +26347,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@@ -26198,6 +26398,21 @@
"resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz",
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
},
"dependencies": {
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -26437,7 +26652,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -26446,8 +26660,7 @@
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
}
}
},
@@ -26536,8 +26749,7 @@
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
},
"is-bigint": {
"version": "1.0.4",
@@ -26586,7 +26798,6 @@
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
@@ -28613,8 +28824,7 @@
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"json-schema": {
"version": "0.4.0",
@@ -28732,8 +28942,7 @@
"lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"loader-runner": {
"version": "4.3.0",
@@ -29384,7 +29593,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
}
@@ -29393,7 +29601,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -29523,8 +29730,7 @@
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-to-regexp": {
"version": "0.1.7",
@@ -29535,8 +29741,7 @@
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
},
"performance-now": {
"version": "2.1.0",
@@ -30947,8 +31152,7 @@
"regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"dev": true
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"regenerator-transform": {
"version": "0.15.0",
@@ -31060,7 +31264,6 @@
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"requires": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
@@ -31785,6 +31988,11 @@
"postcss-selector-parser": "^6.0.4"
}
},
"stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -31807,8 +32015,7 @@
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"svg-parser": {
"version": "2.0.4",
@@ -32109,8 +32316,7 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
},
"to-regex-range": {
"version": "5.0.1",
@@ -33202,8 +33408,7 @@
"yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
},
"yargs": {
"version": "16.2.0",

View File

@@ -11,7 +11,8 @@
"mdi-react": "9.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"zustand": "^3.6.8"
"zustand": "^3.6.8",
"@emotion/react": "^11.10.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.0",

View File

@@ -1,5 +1,5 @@
diff --git a/node_modules/react-scripts/config/webpack.config.js b/node_modules/react-scripts/config/webpack.config.js
index e465d8e..18b7317 100644
index e465d8e..5e3771c 100644
--- a/node_modules/react-scripts/config/webpack.config.js
+++ b/node_modules/react-scripts/config/webpack.config.js
@@ -106,6 +106,9 @@ module.exports = function (webpackEnv) {
@@ -35,7 +35,7 @@ index e465d8e..18b7317 100644
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
@@ -320,13 +330,17 @@ module.exports = function (webpackEnv) {
@@ -320,13 +330,19 @@ module.exports = function (webpackEnv) {
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
@@ -43,6 +43,8 @@ index e465d8e..18b7317 100644
+ "react-dom": path.resolve(path.join(__dirname,'../../react-dom')),
+ "@mdi/js": path.resolve(path.join(__dirname,'../../@mdi/js')),
+ "@mdi/react": path.resolve(path.join(__dirname,'../../@mdi/react')),
+ "@emotion/react": path.resolve(path.join(__dirname,'../../@emotion/react')),
+
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
@@ -54,7 +56,7 @@ index e465d8e..18b7317 100644
}),
...(modules.webpackAliases || {}),
},
@@ -336,14 +350,14 @@ module.exports = function (webpackEnv) {
@@ -336,14 +352,14 @@ module.exports = function (webpackEnv) {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
@@ -77,7 +79,7 @@ index e465d8e..18b7317 100644
],
},
module: {
@@ -353,7 +367,7 @@ module.exports = function (webpackEnv) {
@@ -353,7 +369,7 @@ module.exports = function (webpackEnv) {
shouldUseSourceMap && {
enforce: 'pre',
exclude: /@babel(?:\/|\\{1,2})runtime/,
@@ -86,7 +88,7 @@ index e465d8e..18b7317 100644
loader: require.resolve('source-map-loader'),
},
{
@@ -408,13 +422,13 @@ module.exports = function (webpackEnv) {
@@ -408,13 +424,13 @@ module.exports = function (webpackEnv) {
},
],
issuer: {
@@ -102,7 +104,7 @@ index e465d8e..18b7317 100644
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
@@ -452,7 +466,7 @@ module.exports = function (webpackEnv) {
@@ -452,7 +468,7 @@ module.exports = function (webpackEnv) {
plugins: [
isEnvDevelopment &&
shouldUseReactRefresh &&
@@ -111,7 +113,7 @@ index e465d8e..18b7317 100644
].filter(Boolean),
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
@@ -460,13 +474,13 @@ module.exports = function (webpackEnv) {
@@ -460,13 +476,13 @@ module.exports = function (webpackEnv) {
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
@@ -127,7 +129,7 @@ index e465d8e..18b7317 100644
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
@@ -594,7 +608,7 @@ module.exports = function (webpackEnv) {
@@ -594,7 +610,7 @@ module.exports = function (webpackEnv) {
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/editor",
"version": "1.3.1",
"version": "1.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/editor",
"version": "1.3.1",
"version": "1.4.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"@emotion/react": "^11.10.0",

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

@@ -21,7 +21,7 @@ import { SearchStorage } from "../../extensions/search-replace";
import { FloatingMenuProps } from "./types";
import { SearchReplacePopup } from "../popups/search-replace";
import { ResponsivePresenter } from "../../components/responsive";
import { getToolbarElement } from "../utils/dom";
import { getEditorContainer, getToolbarElement } from "../utils/dom";
export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
const { editor } = props;
@@ -34,11 +34,11 @@ export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
isOpen={isSearching}
onClose={() => editor.commands.endSearch()}
position={{
target: getToolbarElement(),
target: editor.isEditable ? getToolbarElement() : getEditorContainer(),
isTargetAbsolute: true,
location: "below",
location: editor.isEditable ? "below" : "top",
align: "end",
yOffset: 5
yOffset: editor.isEditable ? 5 : -50
}}
blocking={false}
focusOnRender={false}