mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 18:19:38 +02:00
Compare commits
55 Commits
fix-db-ini
...
fix/sync-q
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2febd09613 | ||
|
|
91138f4911 | ||
|
|
f9bcfe2f7a | ||
|
|
3621f95dbc | ||
|
|
536dead44d | ||
|
|
39d74e80a9 | ||
|
|
46e03dd248 | ||
|
|
152fee2c29 | ||
|
|
492a4d16b2 | ||
|
|
3d7c1fa1e7 | ||
|
|
8a138d960d | ||
|
|
69bb0e3656 | ||
|
|
4875cfa108 | ||
|
|
a28beb62c6 | ||
|
|
21c8960146 | ||
|
|
59442b9107 | ||
|
|
2ba638b123 | ||
|
|
8548940cb9 | ||
|
|
909bdfa0cf | ||
|
|
bfb3c56abe | ||
|
|
e06836f701 | ||
|
|
bc16cb79f2 | ||
|
|
176c46e3f2 | ||
|
|
af65055ce9 | ||
|
|
ad23ff19de | ||
|
|
cf65fc4f57 | ||
|
|
9ab83e7fbb | ||
|
|
1acf2aadba | ||
|
|
06833bcac9 | ||
|
|
3bf6b5293a | ||
|
|
c40d33df37 | ||
|
|
fe609f8275 | ||
|
|
3566f47f2d | ||
|
|
4b5526dffd | ||
|
|
4977390394 | ||
|
|
87804de452 | ||
|
|
bc55d2b476 | ||
|
|
3ff34a5c82 | ||
|
|
7e01e90e22 | ||
|
|
d568be6536 | ||
|
|
734e79604f | ||
|
|
227a962042 | ||
|
|
a1640175e5 | ||
|
|
7cf61570ef | ||
|
|
83762c668e | ||
|
|
ecd4a08b1c | ||
|
|
7b66462300 | ||
|
|
41639fd325 | ||
|
|
2fe82841ef | ||
|
|
9b7af87611 | ||
|
|
9744679550 | ||
|
|
9cdc502ce5 | ||
|
|
300a104949 | ||
|
|
df94f31998 | ||
|
|
aec1d601a3 |
@@ -8,9 +8,9 @@ Thank you so much for considering to contribute to Notesnook! If you have no ide
|
||||
|
||||
Ugh! Bugs!
|
||||
|
||||
> A bug is when software behaves in a way that you didn't expect, and the developer didn't intend.
|
||||
> A bug is when software behaves in a way you didn't expect, which the developer didn't intend.
|
||||
|
||||
To help us understand what's going on, we first want to **make sure you're using the latest version of Notesnook**.
|
||||
To help us understand what's happening, we first want to **make sure you're using the latest version of Notesnook**.
|
||||
|
||||
Once you've **confirmed that the bug still exists in the latest version**, you'll want to check to make sure it's not something we already know about in the [opened GitHub issues](https://github.com/streetwriters/notesnook/issues).
|
||||
|
||||
@@ -30,7 +30,7 @@ Before you open a new feature request, please make sure it's not a duplicate. **
|
||||
|
||||
### Helping out in the issue tracker
|
||||
|
||||
There are always new issues getting opened that need to be triaged, sorted & organized, so the developers can easily find the most critical and/or relevant bugs to fix. Any help in this regard is appreciated.
|
||||
New issues are always opened that need to be triaged, sorted & organized, so the developers can easily find the most critical and/or relevant bugs to fix. Any help in this regard is appreciated.
|
||||
|
||||
In addition to this, you can help out in the following ways:
|
||||
|
||||
@@ -62,7 +62,7 @@ Once you are done, [open a new pull request](https://docs.github.com/en/pull-req
|
||||
|
||||
1. Fork [the repository](https://github.com/streetwriters/notesnook) and create your branch from `master` (you can name your branch anything).
|
||||
2. Run `npm run bootstrap` in the repository root.
|
||||
3. If you’ve fixed a bug or added code that should be tested, add tests!
|
||||
3. If you've fixed a bug or added code that should be tested, add tests!
|
||||
4. Ensure the test suite passes (`npm run test`).
|
||||
5. Format your code with prettier (`npm run prettier`).
|
||||
6. Make sure your code lints (`npm run lint`). Tip: `npm run linc` to only check changed files.
|
||||
@@ -131,11 +131,11 @@ We use an automatic code formatter called [Prettier](https://prettier.io/). Run
|
||||
|
||||
Then, our linter will catch most issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`.
|
||||
|
||||
However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at [Airbnb’s Style Guide](https://github.com/airbnb/javascript) will guide you in the right direction.
|
||||
However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at [Airbnb's Style Guide](https://github.com/airbnb/javascript) will guide you in the right direction.
|
||||
|
||||
## Git Branch Organization
|
||||
|
||||
Submit all changes directly to the [`master branch`](https://github.com/streetwriters/notesnook). We don’t use separate branches for development or for upcoming releases. This requires us to always keep the `master` branch in a deployable state which means:
|
||||
Submit all changes directly to the [`master branch`](https://github.com/streetwriters/notesnook). We don't use separate branches for development or for upcoming releases. This requires us to always keep the `master` branch in a deployable state which means:
|
||||
|
||||
1. All tests must be passing at all times
|
||||
2. There should be as few breaking changes as possible
|
||||
|
||||
@@ -68,14 +68,3 @@ db.host(
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
}
|
||||
);
|
||||
|
||||
export async function loadDatabase() {
|
||||
// if (!DB) {
|
||||
// let module = await import(/* webpackChunkName: "notes-core" */ 'notes-core/api/index');
|
||||
// DB = module.default;
|
||||
// }
|
||||
// db = new DB(Storage, Platform.OS === 'ios' ? EventSource : AndroidEventSource, filesystem);
|
||||
// if (DOMParser) {
|
||||
// await DOMParser.prepare();
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -36,7 +36,11 @@ function getFileExtension(filename) {
|
||||
var ext = /^.+\.([^.]+)$/.exec(filename);
|
||||
return ext == null ? "" : ext[1];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} param0
|
||||
* @returns
|
||||
*/
|
||||
export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
|
||||
|
||||
@@ -44,8 +44,10 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
import { LoginSteps, useLogin } from "./use-login";
|
||||
|
||||
function getObfuscatedEmail(email) {
|
||||
if (!email) return null;
|
||||
return email.replace(/(.{2})(.*)(?=@)/, function (gp1, gp2, gp3) {
|
||||
if (!email) return "";
|
||||
const [username, provider] = email.split("@");
|
||||
if (username.length === 1) return `****@${provider}`;
|
||||
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
|
||||
for (let i = 0; i < gp3.length; i++) {
|
||||
gp2 += "*";
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ 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 notifee from "@notifee/react-native";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import RNBootSplash from "react-native-bootsplash";
|
||||
import { checkVersion } from "react-native-check-version";
|
||||
import Config from "react-native-config";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { DatabaseLogger, db, loadDatabase } from "../../common/database";
|
||||
import { DatabaseLogger, db } from "../../common/database";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
import BiometricService from "../../services/biometrics";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
@@ -35,7 +37,9 @@ import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { AndroidModule } from "../../utils";
|
||||
import { eOpenAnnouncementDialog } from "../../utils/events";
|
||||
import { getGithubVersion } from "../../utils/github-version";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { SVG } from "../auth/background";
|
||||
@@ -50,9 +54,6 @@ import { SvgView } from "../ui/svg";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Walkthrough } from "../walkthroughs";
|
||||
import Config from "react-native-config";
|
||||
import { getGithubVersion } from "../../utils/github-version";
|
||||
import notifee from "@notifee/react-native";
|
||||
|
||||
const Launcher = React.memo(
|
||||
function Launcher() {
|
||||
@@ -69,7 +70,7 @@ const Launcher = React.memo(
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const dbInitCompleted = useRef(false);
|
||||
|
||||
const loadNotes = useCallback(async () => {
|
||||
if (verifyUser) {
|
||||
return;
|
||||
@@ -80,28 +81,25 @@ const Launcher = React.memo(
|
||||
initialize();
|
||||
setImmediate(() => {
|
||||
setLoading(false);
|
||||
if (!dbInitCompleted.current) {
|
||||
setImmediate(() => doAppLoadActions());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}, [doAppLoadActions, setLoading, verifyUser]);
|
||||
}, [setLoading, verifyUser]);
|
||||
|
||||
const init = useCallback(async () => {
|
||||
if (!dbInitCompleted.current) {
|
||||
if (!db.isInitialized) {
|
||||
await RNBootSplash.hide({ fade: true });
|
||||
await loadDatabase();
|
||||
DatabaseLogger.info("Initializing database");
|
||||
await db.init();
|
||||
dbInitCompleted.current = true;
|
||||
}
|
||||
|
||||
if (db.migrations.required() && !verifyUser) {
|
||||
presentSheet({
|
||||
component: <Migrate />,
|
||||
onClose: async () => {
|
||||
await db.init();
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
}
|
||||
loadNotes();
|
||||
},
|
||||
disableClosing: true
|
||||
@@ -120,9 +118,6 @@ const Launcher = React.memo(
|
||||
if (!loading) {
|
||||
doAppLoadActions();
|
||||
}
|
||||
return () => {
|
||||
dbInitCompleted.current = false;
|
||||
};
|
||||
}, [doAppLoadActions, loading]);
|
||||
|
||||
const doAppLoadActions = useCallback(async () => {
|
||||
@@ -198,6 +193,11 @@ const Launcher = React.memo(
|
||||
|
||||
const onUnlockBiometrics = useCallback(async () => {
|
||||
if (!(await BiometricService.isBiometryAvailable())) return;
|
||||
if (Platform.OS === "android") {
|
||||
const activityName = await AndroidModule.getActivityName();
|
||||
if (activityName !== "MainActivity") return;
|
||||
}
|
||||
|
||||
let verified = await BiometricService.validateUser(
|
||||
"Unlock to access your notes",
|
||||
""
|
||||
|
||||
@@ -116,11 +116,11 @@ export const Items = ({ item, buttons, close }) => {
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
customStyle={{
|
||||
alignItems: "center",
|
||||
alignSelf: "flex-start",
|
||||
width: topBarItemWidth,
|
||||
marginBottom: 10,
|
||||
marginRight: isLast ? 0 : 10,
|
||||
backgroundColor: "transparent"
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
@@ -150,11 +150,7 @@ export const Items = ({ item, buttons, close }) => {
|
||||
/>
|
||||
</PressableButton>
|
||||
|
||||
<Paragraph
|
||||
size={SIZE.xxs + 1}
|
||||
style={{ textAlign: "center" }}
|
||||
textBreakStrategy="simple"
|
||||
>
|
||||
<Paragraph size={SIZE.xxs + 1} style={{ textAlign: "center" }}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
|
||||
@@ -200,7 +200,8 @@ export default function ReminderSheet({
|
||||
|
||||
if (!_reminder) {
|
||||
ToastEvent.show({
|
||||
heading: "Failed to add a new reminder"
|
||||
heading: "Failed to add a new reminder",
|
||||
context: "local"
|
||||
});
|
||||
}
|
||||
if (reference) {
|
||||
@@ -214,7 +215,7 @@ export default function ReminderSheet({
|
||||
useRelationStore.getState().update();
|
||||
close?.();
|
||||
} catch (e) {
|
||||
ToastEvent.error(e as Error);
|
||||
ToastEvent.error(e as Error, undefined, "local");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +310,16 @@ export default function ReminderSheet({
|
||||
mode as keyof typeof ReminderModes
|
||||
] as Reminder["mode"]
|
||||
);
|
||||
if (mode === "Repeat") {
|
||||
setSelectedDays((days) => {
|
||||
if (days.length > 0) return days;
|
||||
if (days.indexOf(date.getDay()) > -1) {
|
||||
return days;
|
||||
}
|
||||
days.push(date.getDay());
|
||||
return [...days];
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -25,7 +25,6 @@ import Navigation from "../../../services/navigation";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { GROUP, SORT } from "../../../utils/constants";
|
||||
import { refreshNotesPage } from "../../../utils/events";
|
||||
import layoutmanager from "../../../utils/layout-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
@@ -39,7 +38,6 @@ const Sort = ({ type, screen }) => {
|
||||
const updateGroupOptions = async (_groupOptions) => {
|
||||
await db.settings.setGroupOptions(type, _groupOptions);
|
||||
|
||||
layoutmanager.withSpringAnimation(600);
|
||||
setGroupOptions(_groupOptions);
|
||||
setTimeout(() => {
|
||||
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
@@ -34,24 +34,25 @@ import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
let toastMessages = [];
|
||||
export const Toast = ({ context = "global" }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [keyboard, setKeyboard] = useState(false);
|
||||
const [data, setData] = useState({});
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const hideTimeout = useRef();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const toastMessages = useRef([]);
|
||||
|
||||
const showToastFunc = useCallback(
|
||||
const show = useCallback(
|
||||
async (data) => {
|
||||
if (!data) return;
|
||||
if (data.context !== context) return;
|
||||
if (toastMessages.findIndex((m) => m.message === data.message) >= 0) {
|
||||
if (
|
||||
toastMessages.current.findIndex((m) => m.message === data.message) >= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
toastMessages.push(data);
|
||||
if (toastMessages?.length > 1) return;
|
||||
toastMessages.current.push(data);
|
||||
if (toastMessages.current?.length > 1) return;
|
||||
setData(data);
|
||||
|
||||
setVisible(true);
|
||||
@@ -59,16 +60,16 @@ export const Toast = ({ context = "global" }) => {
|
||||
clearTimeout(hideTimeout.current);
|
||||
}
|
||||
hideTimeout.current = setTimeout(() => {
|
||||
hideToastFunc();
|
||||
hide();
|
||||
}, data.duration);
|
||||
},
|
||||
[context, hideToastFunc]
|
||||
[context, hide]
|
||||
);
|
||||
|
||||
const showNext = useCallback(
|
||||
const next = useCallback(
|
||||
(data) => {
|
||||
if (!data) {
|
||||
hideToastFunc();
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
setData(data);
|
||||
@@ -76,27 +77,28 @@ export const Toast = ({ context = "global" }) => {
|
||||
clearTimeout(hideTimeout.current);
|
||||
}
|
||||
hideTimeout.current = setTimeout(() => {
|
||||
hideToastFunc();
|
||||
hide();
|
||||
}, data?.duration);
|
||||
},
|
||||
[hideToastFunc]
|
||||
[hide]
|
||||
);
|
||||
|
||||
const hideToastFunc = useCallback(() => {
|
||||
const hide = useCallback(() => {
|
||||
if (hideTimeout.current) {
|
||||
clearTimeout(hideTimeout.current);
|
||||
}
|
||||
let msg = toastMessages.length > 1 ? toastMessages.shift() : null;
|
||||
let msg =
|
||||
toastMessages.current.length > 1 ? toastMessages.current.shift() : null;
|
||||
|
||||
if (msg) {
|
||||
setVisible(false);
|
||||
showNext(msg);
|
||||
next(msg);
|
||||
setTimeout(() => {
|
||||
setVisible(true);
|
||||
}, 300);
|
||||
} else {
|
||||
setVisible(false);
|
||||
toastMessages.shift();
|
||||
toastMessages.current.shift();
|
||||
setTimeout(() => {
|
||||
setData({});
|
||||
if (hideTimeout.current) {
|
||||
@@ -104,44 +106,22 @@ export const Toast = ({ context = "global" }) => {
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}, [showNext]);
|
||||
|
||||
const _onKeyboardShow = () => {
|
||||
setKeyboard(true);
|
||||
};
|
||||
|
||||
const _onKeyboardHide = () => {
|
||||
setKeyboard(false);
|
||||
};
|
||||
}, [next]);
|
||||
|
||||
useEffect(() => {
|
||||
toastMessages = [];
|
||||
let sub1 = Keyboard.addListener("keyboardDidShow", _onKeyboardShow);
|
||||
let sub2 = Keyboard.addListener("keyboardDidHide", _onKeyboardHide);
|
||||
eSubscribeEvent(eShowToast, showToastFunc);
|
||||
eSubscribeEvent(eHideToast, hideToastFunc);
|
||||
eSubscribeEvent(eShowToast, show);
|
||||
eSubscribeEvent(eHideToast, hide);
|
||||
return () => {
|
||||
if (hideTimeout.current) {
|
||||
clearTimeout(hideTimeout.current);
|
||||
}
|
||||
|
||||
toastMessages = [];
|
||||
sub1?.remove();
|
||||
sub2?.remove();
|
||||
eUnSubscribeEvent(eShowToast, showToastFunc);
|
||||
eUnSubscribeEvent(eHideToast, hideToastFunc);
|
||||
toastMessages.current = [];
|
||||
eUnSubscribeEvent(eShowToast, show);
|
||||
eUnSubscribeEvent(eHideToast, hide);
|
||||
};
|
||||
}, [hideToastFunc, keyboard, showToastFunc]);
|
||||
}, [hide, show]);
|
||||
|
||||
return (
|
||||
visible && (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (hideTimeout.current) {
|
||||
clearTimeout(hideTimeout.current);
|
||||
}
|
||||
hideToastFunc();
|
||||
}}
|
||||
onPress={hide}
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
width: DDS.isTab ? 400 : "100%",
|
||||
@@ -209,7 +189,7 @@ export const Toast = ({ context = "global" }) => {
|
||||
color={colors.pri}
|
||||
size={SIZE.md}
|
||||
onPress={() => {
|
||||
hideToastFunc();
|
||||
hide();
|
||||
}}
|
||||
>
|
||||
{data.heading}
|
||||
@@ -224,7 +204,7 @@ export const Toast = ({ context = "global" }) => {
|
||||
paddingRight: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
hideToastFunc();
|
||||
hide();
|
||||
}}
|
||||
>
|
||||
{data.message}
|
||||
|
||||
@@ -19,4 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { FeatureType } from "./components/sheets/new-feature";
|
||||
|
||||
export const features: FeatureType[] = [];
|
||||
export const features: FeatureType[] = [
|
||||
{
|
||||
title: "Default font size & font family",
|
||||
body: "Now you can set default font size and font family in editor that will be used across all your new and old notes."
|
||||
}
|
||||
];
|
||||
|
||||
@@ -613,7 +613,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "pin",
|
||||
title: item.pinned ? "Unpin" : "Pin",
|
||||
title: "Pin",
|
||||
icon: item.pinned ? "pin-off-outline" : "pin-outline",
|
||||
func: pinItem,
|
||||
close: false,
|
||||
@@ -623,7 +623,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "favorite",
|
||||
title: !item.favorite ? "Favorite" : "Unfavorite",
|
||||
title: "Favorite",
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
|
||||
@@ -558,7 +558,7 @@ export const useAppEvents = () => {
|
||||
let shareExtensionOpened = MMKV.getString("shareExtensionOpened");
|
||||
if (notesAddedFromIntent) {
|
||||
if (Platform.OS === "ios") {
|
||||
await db.init();
|
||||
await db.initCollections();
|
||||
await db.notes.init();
|
||||
}
|
||||
useNoteStore.getState().setNotes();
|
||||
@@ -571,8 +571,8 @@ export const useAppEvents = () => {
|
||||
if (notesAddedFromIntent || shareExtensionOpened) {
|
||||
let id = useEditorStore.getState().currentEditingNote;
|
||||
let note = id && db.notes.note(id).data;
|
||||
eSendEvent("loadingNote", note);
|
||||
eSendEvent("webview_reset");
|
||||
setTimeout(() => eSendEvent("loadingNote", note), 1);
|
||||
MMKV.removeItem("shareExtensionOpened");
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -17,35 +17,46 @@ 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 { useState } from "react";
|
||||
import { useAttachmentStore } from "../stores/use-attachment-store";
|
||||
|
||||
export const useAttachmentProgress = (attachment, encryption) => {
|
||||
type AttachmentProgress = {
|
||||
type: string;
|
||||
value?: number;
|
||||
percent?: string;
|
||||
};
|
||||
|
||||
export const useAttachmentProgress = (
|
||||
attachment: any,
|
||||
encryption?: boolean
|
||||
) => {
|
||||
const progress = useAttachmentStore((state) => state.progress);
|
||||
const [currentProgress, setCurrentProgress] = useState(
|
||||
const [currentProgress, setCurrentProgress] = useState<
|
||||
AttachmentProgress | undefined
|
||||
>(
|
||||
encryption
|
||||
? {
|
||||
type: "encrypt"
|
||||
}
|
||||
: null
|
||||
: undefined
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let prog = progress[attachment.metadata.hash];
|
||||
if (prog) {
|
||||
let type = prog.type;
|
||||
let loaded = prog.type === "download" ? prog.recieved : prog.sent;
|
||||
prog = loaded / prog.total;
|
||||
prog = (prog * 100).toFixed(0);
|
||||
setCurrentProgress({
|
||||
value: prog,
|
||||
percent: prog + "%",
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
setCurrentProgress(null);
|
||||
}
|
||||
}, [attachment.metadata.hash, progress]);
|
||||
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);
|
||||
}
|
||||
|
||||
return [currentProgress, setCurrentProgress];
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import BiometicService from "../services/biometrics";
|
||||
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
|
||||
import { db } from "../common/database";
|
||||
|
||||
const VaultStatusCache = {
|
||||
exists: false,
|
||||
biometryEnrolled: false,
|
||||
isBiometryAvailable: false
|
||||
};
|
||||
|
||||
export const useVaultStatus = () => {
|
||||
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusCache);
|
||||
|
||||
const checkVaultStatus = useCallback(() => {
|
||||
db.vault.exists().then(async (exists) => {
|
||||
let available = await BiometicService.isBiometryAvailable();
|
||||
let fingerprint = await BiometicService.hasInternetCredentials();
|
||||
if (
|
||||
VaultStatusCache.exists === exists &&
|
||||
VaultStatusCache.biometryEnrolled === fingerprint &&
|
||||
VaultStatusCache.isBiometryAvailable === available
|
||||
)
|
||||
return;
|
||||
setVaultStatus({
|
||||
exists: exists,
|
||||
biometryEnrolled: fingerprint,
|
||||
isBiometryAvailable: available ? true : false
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
checkVaultStatus();
|
||||
eSubscribeEvent("vaultUpdated", () => checkVaultStatus());
|
||||
return () => {
|
||||
eUnSubscribeEvent("vaultUpdated", () => checkVaultStatus());
|
||||
};
|
||||
}, [checkVaultStatus]);
|
||||
|
||||
return vaultStatus;
|
||||
};
|
||||
@@ -10,7 +10,7 @@
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.10.4",
|
||||
"entities": "^3.0.1",
|
||||
"html-to-text": "8.1.0",
|
||||
"html-to-text": "9.0.5",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
|
||||
@@ -24,6 +24,7 @@ import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Platform, ViewStyle } from "react-native";
|
||||
@@ -89,7 +90,7 @@ const Editor = React.memo(
|
||||
noToolbar,
|
||||
noHeader
|
||||
});
|
||||
|
||||
const renderKey = useRef(`editor-0`);
|
||||
useImperativeHandle(ref, () => ({
|
||||
get: () => editor
|
||||
}));
|
||||
@@ -124,6 +125,8 @@ const Editor = React.memo(
|
||||
);
|
||||
|
||||
const onError = useCallback(() => {
|
||||
renderKey.current =
|
||||
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
|
||||
editor.setLoading(true);
|
||||
setTimeout(() => editor.setLoading(false), 10);
|
||||
}, [editor]);
|
||||
@@ -152,6 +155,7 @@ const Editor = React.memo(
|
||||
testID={notesnook.editor.id}
|
||||
ref={editor.ref}
|
||||
onLoad={editor.onLoad}
|
||||
key={renderKey.current}
|
||||
onRenderProcessGone={onError}
|
||||
nestedScrollEnabled
|
||||
onError={onError}
|
||||
|
||||
@@ -106,6 +106,7 @@ typeof globalThis.editorTitle !== "undefined" && editorTitle.current && editorTi
|
||||
if (editorController.content) editorController.content.current = null;
|
||||
editorController.onUpdate();
|
||||
editorController.setTitle(null);
|
||||
editorController.countWords(0);
|
||||
typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",saved:""});
|
||||
`
|
||||
);
|
||||
@@ -237,6 +238,10 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
|
||||
'response = window.dispatchEvent(new Event("handleBackPress",{cancelable:true}));'
|
||||
);
|
||||
};
|
||||
|
||||
keyboardShown = async (keyboardShown: boolean) => {
|
||||
return this.doAsync(`globalThis['keyboardShown']=${keyboardShown};`);
|
||||
};
|
||||
//todo add replace image function
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ export type Settings = {
|
||||
keyboardShown?: boolean;
|
||||
doubleSpacedLines?: boolean;
|
||||
corsProxy: string;
|
||||
fontSize: string;
|
||||
fontFamily: string;
|
||||
};
|
||||
|
||||
export type EditorProps = {
|
||||
|
||||
@@ -25,6 +25,8 @@ import { useCallback, useEffect, useRef } from "react";
|
||||
import {
|
||||
BackHandler,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
KeyboardEventListener,
|
||||
NativeEventSubscription
|
||||
} from "react-native";
|
||||
import { WebViewMessageEvent } from "react-native-webview";
|
||||
@@ -32,13 +34,12 @@ import { db } from "../../../common/database";
|
||||
import ManageTagsSheet from "../../../components/sheets/manage-tags";
|
||||
import { RelationsList } from "../../../components/sheets/relations-list";
|
||||
import ReminderSheet from "../../../components/sheets/reminder";
|
||||
import useKeyboard from "../../../hooks/use-keyboard";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
ToastEvent,
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent,
|
||||
ToastEvent
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
@@ -135,8 +136,30 @@ export const useEditorEvents = (
|
||||
const doubleSpacedLines = useSettingStore(
|
||||
(state) => state.settings?.doubleSpacedLines
|
||||
);
|
||||
const defaultFontSize = useSettingStore(
|
||||
(state) => state.settings.defaultFontSize
|
||||
);
|
||||
const defaultFontFamily = useSettingStore(
|
||||
(state) => state.settings.defaultFontFamily
|
||||
);
|
||||
|
||||
const tools = useDragState((state) => state.data);
|
||||
const { keyboardShown } = useKeyboard();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyboardDidShow: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(true);
|
||||
};
|
||||
const handleKeyboardDidHide: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(false);
|
||||
};
|
||||
const subscriptions = [
|
||||
Keyboard.addListener("keyboardDidShow", handleKeyboardDidShow),
|
||||
Keyboard.addListener("keyboardDidHide", handleKeyboardDidHide)
|
||||
];
|
||||
return () => {
|
||||
subscriptions.forEach((subscription) => subscription.remove());
|
||||
};
|
||||
}, [editor.commands]);
|
||||
|
||||
useEffect(() => {
|
||||
editor.commands.setSettings({
|
||||
@@ -147,9 +170,10 @@ export const useEditorEvents = (
|
||||
tools: tools || getDefaultPresets().default,
|
||||
noHeader: noHeader,
|
||||
noToolbar: readonly || editorPropReadonly || noToolbar,
|
||||
keyboardShown: keyboardShown || false,
|
||||
doubleSpacedLines: doubleSpacedLines,
|
||||
corsProxy: corsProxy
|
||||
corsProxy: corsProxy,
|
||||
fontSize: defaultFontSize,
|
||||
fontFamily: defaultFontFamily
|
||||
});
|
||||
}, [
|
||||
fullscreen,
|
||||
@@ -160,12 +184,13 @@ export const useEditorEvents = (
|
||||
deviceMode,
|
||||
tools,
|
||||
editor.commands,
|
||||
keyboardShown,
|
||||
doubleSpacedLines,
|
||||
editorPropReadonly,
|
||||
noHeader,
|
||||
noToolbar,
|
||||
corsProxy
|
||||
corsProxy,
|
||||
defaultFontSize,
|
||||
defaultFontFamily
|
||||
]);
|
||||
|
||||
const onBackPress = useCallback(async () => {
|
||||
|
||||
@@ -43,9 +43,9 @@ import { NoteType } from "../../../utils/types";
|
||||
import Commands from "./commands";
|
||||
import { Content, EditorState, Note, SavePayload } from "./types";
|
||||
import {
|
||||
EditorEvents,
|
||||
clearAppState,
|
||||
defaultState,
|
||||
EditorEvents,
|
||||
getAppState,
|
||||
isContentInvalid,
|
||||
isEditorLoaded,
|
||||
@@ -120,29 +120,9 @@ export const useEditor = (
|
||||
[editorId]
|
||||
);
|
||||
|
||||
const onReady = useCallback(async () => {
|
||||
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
|
||||
overlay(true);
|
||||
setLoading(true);
|
||||
}
|
||||
}, [overlay]);
|
||||
|
||||
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]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [loading]);
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
const withTimer = useCallback(
|
||||
(id: string, fn: () => void, duration: number) => {
|
||||
@@ -567,6 +547,7 @@ export const useEditor = (
|
||||
state.current.isRestoringState = true;
|
||||
state.current.currentlyEditing = true;
|
||||
state.current.movedAway = false;
|
||||
|
||||
if (!DDS.isTab) {
|
||||
tabBarRef.current?.goToPage(1, false);
|
||||
}
|
||||
@@ -586,10 +567,6 @@ export const useEditor = (
|
||||
state.current.isRestoringState = false;
|
||||
}, [loadNote, overlay]);
|
||||
|
||||
useEffect(() => {
|
||||
isDefaultEditor && restoreEditorState();
|
||||
}, [isDefaultEditor, restoreEditorState]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent(eOnLoadNote + editorId, loadNote);
|
||||
return () => {
|
||||
@@ -597,6 +574,29 @@ export const useEditor = (
|
||||
};
|
||||
}, [editorId, loadNote, restoreEditorState, isDefaultEditor]);
|
||||
|
||||
const onContentChanged = () => {
|
||||
lastContentChangeTime.current = Date.now();
|
||||
};
|
||||
|
||||
const onReady = useCallback(async () => {
|
||||
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
|
||||
eSendEvent("webview_reset");
|
||||
} else {
|
||||
isDefaultEditor && restoreEditorState();
|
||||
}
|
||||
}, [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();
|
||||
@@ -620,10 +620,6 @@ export const useEditor = (
|
||||
loadNote
|
||||
]);
|
||||
|
||||
const onContentChanged = () => {
|
||||
lastContentChangeTime.current = Date.now();
|
||||
};
|
||||
|
||||
return {
|
||||
ref: editorRef,
|
||||
onLoad,
|
||||
@@ -639,6 +635,7 @@ export const useEditor = (
|
||||
saveContent,
|
||||
onContentChanged,
|
||||
editorId: editorId,
|
||||
markImageLoaded
|
||||
markImageLoaded,
|
||||
overlay
|
||||
};
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ export const EditorWrapper = ({ width }) => {
|
||||
if (editorState().movedAway) return;
|
||||
if (state === "active") {
|
||||
editorController.current.onReady();
|
||||
editorController.current.overlay(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import { Licenses } from "./licenses";
|
||||
import SoundPicker from "./sound-picker";
|
||||
import { Subscription } from "./subscription";
|
||||
import { TrashIntervalSelector } from "./trash-interval-selector";
|
||||
import { FontSelector } from "./font-selector";
|
||||
export const components: { [name: string]: ReactElement } = {
|
||||
colorpicker: <AccentColorPicker />,
|
||||
homeselector: <HomagePageSelector />,
|
||||
@@ -35,5 +36,6 @@ export const components: { [name: string]: ReactElement } = {
|
||||
"debug-logs": <DebugLogs />,
|
||||
"sound-picker": <SoundPicker />,
|
||||
licenses: <Licenses />,
|
||||
"trash-interval-selector": <TrashIntervalSelector />
|
||||
"trash-interval-selector": <TrashIntervalSelector />,
|
||||
"font-selector": <FontSelector />
|
||||
};
|
||||
|
||||
@@ -20,8 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { ToolId } from "@notesnook/editor/dist/toolbar";
|
||||
import React, { RefObject } from "react";
|
||||
import { View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { PressableButton } from "../../../components/ui/pressable";
|
||||
import { SvgView } from "../../../components/ui/svg";
|
||||
import Paragraph from "../../../components/ui/typography/paragraph";
|
||||
@@ -34,13 +32,14 @@ import {
|
||||
getToolIcon,
|
||||
getUngroupedTools
|
||||
} from "./toolbar-definition";
|
||||
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
|
||||
|
||||
export default function ToolSheet({
|
||||
group,
|
||||
fwdRef
|
||||
}: {
|
||||
group: DraggableItem;
|
||||
fwdRef: RefObject<ActionSheet>;
|
||||
fwdRef: RefObject<ActionSheetRef>;
|
||||
}) {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const data = useDragState((state) => state.data);
|
||||
@@ -50,6 +49,7 @@ export default function ToolSheet({
|
||||
(item: ToolId) => {
|
||||
const tool = findToolById(item);
|
||||
const iconSvgString = tool ? getToolIcon(tool.icon as ToolId) : null;
|
||||
if (item === "none") return;
|
||||
return (
|
||||
<PressableButton
|
||||
key={item}
|
||||
@@ -109,12 +109,7 @@ export default function ToolSheet({
|
||||
padding: 12
|
||||
}}
|
||||
>
|
||||
<ScrollView
|
||||
onMomentumScrollEnd={() => {
|
||||
fwdRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
nestedScrollEnabled={true}
|
||||
>
|
||||
<ScrollView nestedScrollEnabled={true}>
|
||||
{!ungrouped || ungrouped.length === 0 ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
|
||||
110
apps/mobile/app/screens/settings/font-selector.jsx
Normal file
110
apps/mobile/app/screens/settings/font-selector.jsx
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { PressableButton } from "../../components/ui/pressable";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
|
||||
|
||||
export const FontSelector = () => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const defaultFontFamily = useSettingStore(
|
||||
(state) => state.settings.defaultFontFamily
|
||||
);
|
||||
const menuRef = useRef();
|
||||
const [width, setWidth] = useState(0);
|
||||
|
||||
const onChange = (item) => {
|
||||
menuRef.current?.hide();
|
||||
SettingsService.set({
|
||||
defaultFontFamily: item
|
||||
});
|
||||
};
|
||||
return (
|
||||
<View
|
||||
onLayout={(event) => {
|
||||
setWidth(event.nativeEvent.layout.width);
|
||||
}}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
ref={menuRef}
|
||||
animationDuration={200}
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: colors.bg,
|
||||
width: width,
|
||||
marginTop: 60
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<PressableButton
|
||||
onPress={async () => {
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
type="grayBg"
|
||||
customStyle={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginTop: 10,
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
padding: 12
|
||||
}}
|
||||
>
|
||||
<Paragraph>{getFontById(defaultFontFamily).title}</Paragraph>
|
||||
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
|
||||
</PressableButton>
|
||||
}
|
||||
>
|
||||
{getFonts().map((item) => (
|
||||
<MenuItem
|
||||
key={item.id}
|
||||
onPress={async () => {
|
||||
onChange(item.id);
|
||||
}}
|
||||
style={{
|
||||
backgroundColor:
|
||||
defaultFontFamily === item.id ? colors.nav : "transparent",
|
||||
width: "100%",
|
||||
maxWidth: width
|
||||
}}
|
||||
textStyle={{
|
||||
fontSize: SIZE.md,
|
||||
color: defaultFontFamily === item.id ? colors.accent : colors.pri
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -37,6 +37,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { components } from "./components";
|
||||
import { RouteParams, SettingSection } from "./types";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
|
||||
const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -64,6 +65,12 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
backgroundColor: colors.errorBg
|
||||
}
|
||||
: {};
|
||||
|
||||
const updateInput = (value: any) => {
|
||||
inputRef?.current?.setNativeProps({
|
||||
text: value + ""
|
||||
});
|
||||
};
|
||||
return isHidden ? null : (
|
||||
<PressableButton
|
||||
disabled={item.type === "component"}
|
||||
@@ -190,6 +197,95 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
defaultValue={item.inputProperties?.defaultValue}
|
||||
/>
|
||||
)}
|
||||
|
||||
{item.type === "input-selector" && (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginTop: 12
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="minus"
|
||||
color={colors.pri}
|
||||
onPress={() => {
|
||||
const rawValue = SettingsService.get()[
|
||||
item.property as keyof SettingStore["settings"]
|
||||
] as string;
|
||||
if (rawValue) {
|
||||
const currentValue = parseInt(rawValue);
|
||||
if (currentValue <= 0) return;
|
||||
const nextValue = currentValue - 1;
|
||||
SettingsService.set({
|
||||
[item.property as string]: nextValue
|
||||
});
|
||||
updateInput(nextValue);
|
||||
}
|
||||
}}
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
<Input
|
||||
{...item.inputProperties}
|
||||
onSubmit={(e) => {
|
||||
if (e.nativeEvent.text) {
|
||||
SettingsService.set({
|
||||
[item.property as string]: e.nativeEvent.text
|
||||
});
|
||||
}
|
||||
item.inputProperties?.onSubmitEditing?.(e);
|
||||
}}
|
||||
onChangeText={(text) => {
|
||||
if (text) {
|
||||
if (item.minInputValue) {
|
||||
text =
|
||||
parseInt(text) < item.minInputValue
|
||||
? item.minInputValue + ""
|
||||
: text;
|
||||
}
|
||||
SettingsService.set({
|
||||
[item.property as string]: text
|
||||
});
|
||||
}
|
||||
item.inputProperties?.onSubmitEditing?.(text as any);
|
||||
}}
|
||||
keyboardType="decimal-pad"
|
||||
containerStyle={{
|
||||
width: 45
|
||||
}}
|
||||
wrapperStyle={{
|
||||
maxWidth: 45,
|
||||
marginBottom: 0,
|
||||
marginHorizontal: 6
|
||||
}}
|
||||
fwdRef={inputRef}
|
||||
onLayout={() => {
|
||||
if (item.property) {
|
||||
updateInput(SettingsService.get()[item.property]);
|
||||
}
|
||||
}}
|
||||
defaultValue={item.inputProperties?.defaultValue}
|
||||
/>
|
||||
<IconButton
|
||||
name="plus"
|
||||
color={colors.pri}
|
||||
onPress={() => {
|
||||
const rawValue = SettingsService.get()[
|
||||
item.property as keyof SettingStore["settings"]
|
||||
] as string;
|
||||
if (rawValue) {
|
||||
const currentValue = parseInt(rawValue);
|
||||
const nextValue = currentValue + 1;
|
||||
SettingsService.set({
|
||||
[item.property as string]: nextValue
|
||||
});
|
||||
updateInput(nextValue);
|
||||
}
|
||||
}}
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import notifee from "@notifee/react-native";
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import React from "react";
|
||||
import { Linking, Platform } from "react-native";
|
||||
@@ -317,7 +318,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
await PremiumService.setPremiumStatus();
|
||||
await BiometicService.resetCredentials();
|
||||
MMKV.clearStore();
|
||||
await db.init();
|
||||
await clearAllStores();
|
||||
SettingsService.init();
|
||||
setTimeout(() => {
|
||||
@@ -451,13 +451,13 @@ export const settingsGroups: SettingSection[] = [
|
||||
},
|
||||
{
|
||||
id: "customize",
|
||||
name: "Customize",
|
||||
name: "Customization",
|
||||
sections: [
|
||||
{
|
||||
id: "personalization",
|
||||
type: "screen",
|
||||
name: "Theme",
|
||||
description: "Change app look and feel",
|
||||
description: "Change app look and feel with color themes",
|
||||
icon: "shape",
|
||||
sections: [
|
||||
{
|
||||
@@ -515,7 +515,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
id: "behaviour",
|
||||
type: "screen",
|
||||
name: "Behaviour",
|
||||
description: "Change app homepage",
|
||||
description: "Change how the app behaves in different situations",
|
||||
sections: [
|
||||
{
|
||||
id: "default-home",
|
||||
@@ -533,6 +533,63 @@ export const settingsGroups: SettingSection[] = [
|
||||
component: "trash-interval-selector"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "editor",
|
||||
name: "Editor",
|
||||
type: "screen",
|
||||
icon: "note-edit-outline",
|
||||
description: "Customize the editor to fit your needs",
|
||||
sections: [
|
||||
{
|
||||
id: "configure-toolbar",
|
||||
type: "screen",
|
||||
name: "Configure toolbar",
|
||||
description: "Make the toolbar adaptable to your needs.",
|
||||
component: "configuretoolbar"
|
||||
},
|
||||
{
|
||||
id: "reset-toolbar",
|
||||
name: "Reset toolbar",
|
||||
description: "Reset toolbar configuration to default",
|
||||
modifer: () => {
|
||||
useDragState.getState().setPreset("default");
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "double-spaced-lines",
|
||||
name: "Use double spaced lines",
|
||||
description:
|
||||
"New lines will be double spaced (old ones won't be affected).",
|
||||
type: "switch",
|
||||
property: "doubleSpacedLines",
|
||||
icon: "format-line-spacing",
|
||||
onChange: () => {
|
||||
ToastEvent.show({
|
||||
heading: "Line spacing changed",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "default-font-size",
|
||||
name: "Default font size",
|
||||
description: "Set the default font size in editor",
|
||||
type: "input-selector",
|
||||
minInputValue: 8,
|
||||
icon: "format-size",
|
||||
property: "defaultFontSize"
|
||||
},
|
||||
{
|
||||
id: "default-font-family",
|
||||
name: "Default font family",
|
||||
description: "Set the default font family in editor",
|
||||
type: "component",
|
||||
icon: "format-font",
|
||||
property: "defaultFontFamily",
|
||||
component: "font-selector"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -929,42 +986,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "editor",
|
||||
name: "Editor",
|
||||
sections: [
|
||||
{
|
||||
id: "configure-toolbar",
|
||||
type: "screen",
|
||||
name: "Configure toolbar",
|
||||
description: "Make the toolbar adaptable to your needs.",
|
||||
component: "configuretoolbar"
|
||||
},
|
||||
{
|
||||
id: "reset-toolbar",
|
||||
name: "Reset toolbar",
|
||||
description: "Reset toolbar configuration to default",
|
||||
modifer: () => {
|
||||
useDragState.getState().setPreset("default");
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "double-spaced-lines",
|
||||
name: "Use double spaced lines",
|
||||
description:
|
||||
"New lines will be double spaced (old ones won't be affected).",
|
||||
type: "switch",
|
||||
property: "doubleSpacedLines",
|
||||
icon: "format-line-spacing",
|
||||
onChange: () => {
|
||||
ToastEvent.show({
|
||||
heading: "Line spacing changed",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "help-support",
|
||||
name: "Help and support",
|
||||
|
||||
@@ -22,7 +22,14 @@ import { Settings } from "../../stores/use-setting-store";
|
||||
|
||||
export type SettingSection = {
|
||||
id: string;
|
||||
type?: "screen" | "switch" | "component" | "danger" | "input";
|
||||
type?:
|
||||
| "screen"
|
||||
| "switch"
|
||||
| "component"
|
||||
| "danger"
|
||||
| "input"
|
||||
| "input-selector"
|
||||
| "dropdown-selector";
|
||||
name?: string | ((current?: unknown) => string);
|
||||
description?: string | ((current: unknown) => string);
|
||||
icon?: string;
|
||||
@@ -35,6 +42,8 @@ export type SettingSection = {
|
||||
hidden?: (current: unknown) => boolean;
|
||||
onChange?: (property: boolean) => void;
|
||||
inputProperties?: TextInput["props"];
|
||||
options?: any[];
|
||||
minInputValue?: number;
|
||||
};
|
||||
|
||||
export type SettingsGroup = {
|
||||
|
||||
@@ -85,13 +85,21 @@ async function getNextMonthlyReminderDate(
|
||||
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
|
||||
}
|
||||
|
||||
async function initDatabase(notes = true) {
|
||||
if (!db.isInitialized) {
|
||||
await db.initCollections();
|
||||
}
|
||||
if (notes) {
|
||||
await db.notes?.init();
|
||||
}
|
||||
}
|
||||
|
||||
const onEvent = async ({ type, detail }: Event) => {
|
||||
const { notification, pressAction, input } = detail;
|
||||
if (type === EventType.DELIVERED && Platform.OS === "android") {
|
||||
const reminder = db.reminders?.reminder(notification?.id?.split("_")[0]);
|
||||
if (reminder && reminder.recurringMode === "month") {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
await scheduleNotification(reminder);
|
||||
}
|
||||
return;
|
||||
@@ -100,10 +108,10 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
notifee.decrementBadgeCount();
|
||||
if (notification?.data?.type === "quickNote") return;
|
||||
MMKV.removeItem("appState");
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
if (notification?.data?.type === "reminder") {
|
||||
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
|
||||
if (!reminder) return;
|
||||
await sleep(1000);
|
||||
const ReminderNotify =
|
||||
require("../components/sheets/reminder-notify").default;
|
||||
@@ -131,8 +139,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
notifee.decrementBadgeCount();
|
||||
switch (pressAction?.id) {
|
||||
case "REMINDER_SNOOZE": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
);
|
||||
@@ -151,8 +158,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
break;
|
||||
}
|
||||
case "REMINDER_DISABLE": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
);
|
||||
@@ -168,8 +174,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
break;
|
||||
}
|
||||
case "UNPIN": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
remove(notification?.id as string);
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
@@ -198,14 +203,15 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
reply_button_text: "Take note",
|
||||
reply_placeholder_text: "Write something..."
|
||||
});
|
||||
await db.init();
|
||||
if (!db.isInitialized) await db.init();
|
||||
await db.notes?.init();
|
||||
await db.notes?.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>${input} </p>`
|
||||
}
|
||||
});
|
||||
await db.notes?.init();
|
||||
await db.sync(false, false);
|
||||
useNoteStore.getState().setNotes();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,14 +27,6 @@ import { DatabaseLogger } from "../common/database/index";
|
||||
import { ToastEvent } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
|
||||
NetInfo.configure({
|
||||
reachabilityUrl: "https://notesnook.com",
|
||||
reachabilityTest: (response) => {
|
||||
if (!response) return false;
|
||||
return response?.status >= 200 && response?.status < 300;
|
||||
}
|
||||
});
|
||||
|
||||
export const ignoredMessages = [
|
||||
"Sync already running",
|
||||
"Not allowed to start service intent",
|
||||
|
||||
@@ -24,7 +24,6 @@ import { db } from "../common/database";
|
||||
import { MMKV } from "../common/database/mmkv";
|
||||
import PremiumService from "../services/premium";
|
||||
import { SUBSCRIPTION_STATUS } from "../utils/constants";
|
||||
import layoutmanager from "../utils/layout-manager";
|
||||
export interface MessageStore extends State {
|
||||
message: Message;
|
||||
setMessage: (message: Message) => void;
|
||||
@@ -96,13 +95,7 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
|
||||
icon: "account-outline"
|
||||
},
|
||||
setMessage: (message) => {
|
||||
setTimeout(() => {
|
||||
if (get().message.visible || message.visible) {
|
||||
layoutmanager.withAnimation();
|
||||
}
|
||||
|
||||
set({ message: { ...message } });
|
||||
}, 1);
|
||||
set({ message: { ...message } });
|
||||
},
|
||||
announcements: [],
|
||||
remove: async (id) => {
|
||||
|
||||
@@ -69,6 +69,7 @@ export type RouteParams = {
|
||||
AppLock: AppLockRouteParams;
|
||||
Auth: AuthParams;
|
||||
Reminders: GenericRouteParam;
|
||||
SettingsGroup: GenericRouteParam;
|
||||
};
|
||||
|
||||
export type RouteName = keyof RouteParams;
|
||||
|
||||
@@ -73,6 +73,8 @@ export type Settings = {
|
||||
corsProxy: string;
|
||||
disableRealtimeSync?: boolean;
|
||||
notificationSound?: Sound & { platform: PlatformOSType };
|
||||
defaultFontSize: string;
|
||||
defaultFontFamily: string;
|
||||
};
|
||||
|
||||
type DimensionsType = {
|
||||
@@ -149,7 +151,9 @@ export const useSettingStore = create<SettingStore>((set) => ({
|
||||
defaultSnoozeTime: "5",
|
||||
corsProxy: "https://cors.notesnook.com",
|
||||
reminderNotificationMode: "urgent",
|
||||
notificationSound: undefined
|
||||
notificationSound: undefined,
|
||||
defaultFontFamily: "sans-serif",
|
||||
defaultFontSize: "16"
|
||||
},
|
||||
sheetKeyboardHandler: true,
|
||||
fullscreen: false,
|
||||
|
||||
@@ -27,7 +27,6 @@ import { db } from "../common/database";
|
||||
import { tabBarRef } from "./global-refs";
|
||||
|
||||
let prevTarget = null;
|
||||
let htmlToText;
|
||||
|
||||
export const TOOLTIP_POSITIONS = {
|
||||
LEFT: 1,
|
||||
@@ -171,3 +170,8 @@ export function showTooltip(event, text, position = 2) {
|
||||
clickToHide: true
|
||||
});
|
||||
}
|
||||
|
||||
export function toTitleCase(value) {
|
||||
if (!value) return;
|
||||
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
@@ -46,18 +46,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* @return {String} Sanitized filename
|
||||
*/
|
||||
|
||||
var illegalRe = /[/?<>\\:*|"]/g;
|
||||
const illegalRe = /[/?<>\\:*|"]/g;
|
||||
//var controlRe = /[x00-x1f\x80-\x9f]/g;
|
||||
var reservedRe = /^\.+$/;
|
||||
var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
||||
var windowsTrailingRe = /[. ]+$/;
|
||||
var whitespace = /\s+/g;
|
||||
const reservedRe = /^\.+$/;
|
||||
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
||||
const windowsTrailingRe = /[. ]+$/;
|
||||
const whitespace = /\s+/g;
|
||||
|
||||
function sanitize(input, replacement) {
|
||||
function sanitize(input: string, replacement: string) {
|
||||
if (typeof input !== "string") {
|
||||
throw new Error("Input must be string");
|
||||
}
|
||||
var sanitized = input
|
||||
const sanitized = input
|
||||
.replace(whitespace, replacement)
|
||||
.replace(illegalRe, replacement)
|
||||
.replace(reservedRe, replacement)
|
||||
@@ -67,7 +67,10 @@ function sanitize(input, replacement) {
|
||||
return sanitized.slice(0, 254).toLowerCase();
|
||||
}
|
||||
|
||||
export function sanitizeFilename(input, options) {
|
||||
var replacement = (options && options.replacement) || "";
|
||||
export function sanitizeFilename(
|
||||
input: string,
|
||||
options: { replacement: string }
|
||||
) {
|
||||
const replacement = (options && options.replacement) || "";
|
||||
return sanitize(input, replacement);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await visibleByText(note.body);
|
||||
await visibleByText("Created at:");
|
||||
});
|
||||
|
||||
it("Favorite and unfavorite a note", async () => {
|
||||
@@ -124,8 +124,9 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-delete");
|
||||
await tapById("icon-trash");
|
||||
await navigate("Trash");
|
||||
await sleep(500);
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Restore note");
|
||||
await device.pressBack();
|
||||
|
||||
@@ -56,7 +56,7 @@ async function createNotebook(
|
||||
await tapById("topic-add-button");
|
||||
}
|
||||
}
|
||||
await tapByText("Create notebook");
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ describe("NOTEBOOKS", () => {
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
|
||||
it("Add new topic to notebook", async () => {
|
||||
it.only("Add new topic to notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
@@ -133,7 +133,7 @@ describe("NOTEBOOKS", () => {
|
||||
await elementById("input-title").typeText("Topic");
|
||||
await tapByText("Add");
|
||||
await sleep(500);
|
||||
await visibleById("topic-sheet-item-0");
|
||||
await tapByText("Topic");
|
||||
});
|
||||
|
||||
it("Edit topic", async () => {
|
||||
@@ -146,7 +146,6 @@ describe("NOTEBOOKS", () => {
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(300);
|
||||
await visibleById("topic-sheet-item-0");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Edit topic");
|
||||
await elementById("input-title").typeText(" (edited)");
|
||||
@@ -254,7 +253,7 @@ describe("NOTEBOOKS", () => {
|
||||
"Topic 2"
|
||||
);
|
||||
await tapById("topic-add-button");
|
||||
await tapByText("Save changes");
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1 (Edited)");
|
||||
await visibleByText("Description of Notebook 1 (Edited)");
|
||||
@@ -274,7 +273,7 @@ describe("NOTEBOOKS", () => {
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Move to trash");
|
||||
await sleep(2000);
|
||||
await tapByText("No");
|
||||
await tapByText("Delete");
|
||||
await sleep(4000);
|
||||
await navigate("Trash");
|
||||
await visibleByText("Notebook 1");
|
||||
@@ -296,7 +295,9 @@ describe("NOTEBOOKS", () => {
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Move to trash");
|
||||
await sleep(2000);
|
||||
await tapByText("Yes");
|
||||
await tapByText("Move all notes in this notebook to trash");
|
||||
await sleep(500);
|
||||
await tapByText("Delete");
|
||||
await sleep(4000);
|
||||
await navigate("Trash");
|
||||
await visibleByText("Notebook 1");
|
||||
@@ -319,7 +320,9 @@ describe("NOTEBOOKS", () => {
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Delete topic");
|
||||
await sleep(2000);
|
||||
await tapByText("Yes");
|
||||
await tapByText("Move all notes in this topic to trash");
|
||||
await sleep(500);
|
||||
await tapByText("Delete");
|
||||
await device.pressBack();
|
||||
await sleep(4000);
|
||||
await navigate("Trash");
|
||||
|
||||
@@ -164,7 +164,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2043
|
||||
versionCode 2044
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.streetwriters.notesnook;
|
||||
import android.graphics.Color;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
@@ -36,6 +37,16 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getActivityName(Promise promise) {
|
||||
try {
|
||||
promise.resolve(getCurrentActivity().getClass().getSimpleName());
|
||||
} catch (Exception e) {
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ReactMethod
|
||||
public void setSecureMode(final boolean mode) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
- Fixed text exports not following line spacing settings
|
||||
- Fixed tags are not updated in list after renaming
|
||||
- Fixed a sync issue where notes marked as local only would get deleted sometimes
|
||||
- Fixed a bug where opening a note from search then closing it would cause a crash
|
||||
- Added default font size & font family settings
|
||||
- Redesign reminder/notebook sheets to improve UX
|
||||
- Fixed an issue causing sync to fail
|
||||
- Fix tag suggestions in share extension
|
||||
- Fix app font sizes too small in some places
|
||||
- Improved mobile editor performance on low-end devices
|
||||
- Fix pin to notifications not working
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -993,7 +993,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1067,7 +1067,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
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 = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
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.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
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 = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
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.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
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 = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
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.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
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 = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
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.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
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 = 2035;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
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.12;
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
- Fixed text exports not following line spacing settings
|
||||
- Fixed tags are not updated in list after renaming
|
||||
- Fixed a sync issue where notes marked as local only would get deleted sometimes
|
||||
- Fixed a bug where opening a note from search then closing it would cause a crash
|
||||
- Added default font size & font family settings
|
||||
- Redesign reminder/notebook sheets to improve UX
|
||||
- Fixed an issue causing sync to fail
|
||||
- Fix tag suggestions in share extension
|
||||
- Fix app font sizes too small in some places
|
||||
- Improved mobile editor performance on low-end devices
|
||||
- Fix pin to notifications not working
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -86,7 +86,17 @@ module.exports = (env) => {
|
||||
"@notesnook": path.join(__dirname, "../../../packages"),
|
||||
"@streetwriters/showdown": path.join(__dirname, "../node_modules/@streetwriters/showdown"),
|
||||
"qclone": path.join(__dirname, "../node_modules/qclone"),
|
||||
"@notifee/react-native": path.join(__dirname, "../node_modules/@ammarahmed/notifee-react-native")
|
||||
"@notifee/react-native": path.join(__dirname, "../node_modules/@ammarahmed/notifee-react-native"),
|
||||
"html-to-text": path.join(__dirname, "../node_modules/html-to-text"),
|
||||
"leac": path.join(__dirname, "../node_modules/leac"),
|
||||
"parseley": path.join(__dirname, "../node_modules/parseley"),
|
||||
"htmlparser2": path.join(__dirname, "../node_modules/htmlparser2"),
|
||||
"selderee": path.join(__dirname, "../node_modules/selderee"),
|
||||
"minimist": path.join(__dirname, "../node_modules/minimist"),
|
||||
"entities": path.join(__dirname, "../node_modules/entities"),
|
||||
"deepmerge": path.join(__dirname, "../node_modules/deepmerge"),
|
||||
"@selderee/plugin-htmlparser2": path.join(__dirname, "../node_modules/@selderee/plugin-htmlparser2"),
|
||||
"peberminta": path.join(__dirname, "../node_modules/peberminta"),
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
||||
460
apps/mobile/package-lock.json
generated
460
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.12",
|
||||
"version": "2.4.13",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.12",
|
||||
"version": "2.4.13",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
"native/",
|
||||
@@ -35,7 +35,7 @@
|
||||
"dayjs": "^1.10.4",
|
||||
"entities": "^3.0.1",
|
||||
"fflate": "^0.7.3",
|
||||
"html-to-text": "8.1.0",
|
||||
"html-to-text": "9.0.5",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
@@ -5620,12 +5620,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@selderee/plugin-htmlparser2": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz",
|
||||
"integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
|
||||
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
|
||||
"dependencies": {
|
||||
"domhandler": "^4.2.0",
|
||||
"selderee": "^0.6.0"
|
||||
"domhandler": "^5.0.3",
|
||||
"selderee": "^0.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -8142,57 +8142,6 @@
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/domutils": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/entities": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
|
||||
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
|
||||
@@ -8288,9 +8237,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -8573,11 +8522,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/discontinuous-range": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
||||
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ=="
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -8591,22 +8535,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.2.0",
|
||||
"entities": "^2.0.0"
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
@@ -8628,11 +8575,11 @@
|
||||
]
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@@ -8642,13 +8589,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||
"dependencies": {
|
||||
"dom-serializer": "^1.0.1",
|
||||
"domelementtype": "^2.2.0",
|
||||
"domhandler": "^4.2.0"
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
@@ -10967,14 +10914,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"bin": {
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/hermes-engine": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
|
||||
@@ -11024,28 +10963,24 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/html-to-text": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.1.0.tgz",
|
||||
"integrity": "sha512-Z9iYAqYK2c18GswSbnxJSeMs7lyJgwR2oIkDOyOHGBbYsPsG4HvT379jj3Lcbfko8A5ceyyMHAfkmp/BiXA9/Q==",
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
|
||||
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
|
||||
"dependencies": {
|
||||
"@selderee/plugin-htmlparser2": "^0.6.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"he": "^1.2.0",
|
||||
"htmlparser2": "^6.1.0",
|
||||
"minimist": "^1.2.5",
|
||||
"selderee": "^0.6.0"
|
||||
},
|
||||
"bin": {
|
||||
"html-to-text": "bin/cli.js"
|
||||
"@selderee/plugin-htmlparser2": "^0.11.0",
|
||||
"deepmerge": "^4.3.1",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"htmlparser2": "^8.0.2",
|
||||
"selderee": "^0.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.23.2"
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
|
||||
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
@@ -11054,16 +10989,19 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.0.0",
|
||||
"domutils": "^2.5.2",
|
||||
"entities": "^2.0.0"
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
@@ -14377,6 +14315,14 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/leac": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
|
||||
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@@ -16188,11 +16134,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/moo": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
|
||||
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -16298,27 +16239,6 @@
|
||||
"ncp": "bin/ncp"
|
||||
}
|
||||
},
|
||||
"node_modules/nearley": {
|
||||
"version": "2.20.1",
|
||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
|
||||
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
|
||||
"dependencies": {
|
||||
"commander": "^2.19.0",
|
||||
"moo": "^0.5.0",
|
||||
"railroad-diagrams": "^1.0.0",
|
||||
"randexp": "0.4.6"
|
||||
},
|
||||
"bin": {
|
||||
"nearley-railroad": "bin/nearley-railroad.js",
|
||||
"nearley-test": "bin/nearley-test.js",
|
||||
"nearley-unparse": "bin/nearley-unparse.js",
|
||||
"nearleyc": "bin/nearleyc.js"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://nearley.js.org/#give-to-nearley"
|
||||
}
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@@ -16977,12 +16897,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/parseley": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz",
|
||||
"integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
|
||||
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
|
||||
"dependencies": {
|
||||
"moo": "^0.5.1",
|
||||
"nearley": "^2.20.1"
|
||||
"leac": "^0.6.0",
|
||||
"peberminta": "^0.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -17069,6 +16989,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/peberminta": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
|
||||
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
},
|
||||
"node_modules/phin": {
|
||||
"version": "2.9.3",
|
||||
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
|
||||
@@ -17807,31 +17735,6 @@
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
|
||||
},
|
||||
"node_modules/railroad-diagrams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
||||
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A=="
|
||||
},
|
||||
"node_modules/randexp": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||
"dependencies": {
|
||||
"discontinuous-range": "1.0.0",
|
||||
"ret": "~0.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/randexp/node_modules/ret": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@@ -19159,11 +19062,11 @@
|
||||
"integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w=="
|
||||
},
|
||||
"node_modules/selderee": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz",
|
||||
"integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
|
||||
"dependencies": {
|
||||
"parseley": "^0.7.0"
|
||||
"parseley": "^0.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -24311,7 +24214,7 @@
|
||||
"dayjs": "^1.10.4",
|
||||
"entities": "^3.0.1",
|
||||
"fflate": "^0.7.3",
|
||||
"html-to-text": "8.1.0",
|
||||
"html-to-text": "9.0.5",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
@@ -25573,12 +25476,12 @@
|
||||
"integrity": "sha512-1k57PXJIfhZrjA4ZQr+goTEYer8MBhe1At6XjVYJq1oBuhG/CIu0gX1EH9fU9exC65e5uDs/zAwjl6Ps+3lCgg=="
|
||||
},
|
||||
"@selderee/plugin-htmlparser2": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz",
|
||||
"integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
|
||||
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
|
||||
"requires": {
|
||||
"domhandler": "^4.2.0",
|
||||
"selderee": "^0.6.0"
|
||||
"domhandler": "^5.0.3",
|
||||
"selderee": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"@shopify/flash-list": {
|
||||
@@ -27520,41 +27423,6 @@
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||
"requires": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"entities": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
|
||||
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
@@ -27627,9 +27495,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
|
||||
},
|
||||
"defaults": {
|
||||
"version": "1.0.4",
|
||||
@@ -27823,11 +27691,6 @@
|
||||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"discontinuous-range": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
||||
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ=="
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -27838,19 +27701,19 @@
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.2.0",
|
||||
"entities": "^2.0.0"
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -27865,21 +27728,21 @@
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.2.0"
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
|
||||
"requires": {
|
||||
"dom-serializer": "^1.0.1",
|
||||
"domelementtype": "^2.2.0",
|
||||
"domhandler": "^4.2.0"
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"dooboolab-welcome": {
|
||||
@@ -29594,11 +29457,6 @@
|
||||
"type-fest": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"hermes-engine": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
|
||||
@@ -29645,33 +29503,32 @@
|
||||
"dev": true
|
||||
},
|
||||
"html-to-text": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.1.0.tgz",
|
||||
"integrity": "sha512-Z9iYAqYK2c18GswSbnxJSeMs7lyJgwR2oIkDOyOHGBbYsPsG4HvT379jj3Lcbfko8A5ceyyMHAfkmp/BiXA9/Q==",
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
|
||||
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
|
||||
"requires": {
|
||||
"@selderee/plugin-htmlparser2": "^0.6.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"he": "^1.2.0",
|
||||
"htmlparser2": "^6.1.0",
|
||||
"minimist": "^1.2.5",
|
||||
"selderee": "^0.6.0"
|
||||
"@selderee/plugin-htmlparser2": "^0.11.0",
|
||||
"deepmerge": "^4.3.1",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"htmlparser2": "^8.0.2",
|
||||
"selderee": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
|
||||
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.0.0",
|
||||
"domutils": "^2.5.2",
|
||||
"entities": "^2.0.0"
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -32133,6 +31990,11 @@
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
|
||||
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
|
||||
},
|
||||
"leac": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
|
||||
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="
|
||||
},
|
||||
"leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@@ -33541,11 +33403,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"moo": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
|
||||
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -33632,17 +33489,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nearley": {
|
||||
"version": "2.20.1",
|
||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
|
||||
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
|
||||
"requires": {
|
||||
"commander": "^2.19.0",
|
||||
"moo": "^0.5.0",
|
||||
"railroad-diagrams": "^1.0.0",
|
||||
"randexp": "0.4.6"
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@@ -34119,12 +33965,12 @@
|
||||
}
|
||||
},
|
||||
"parseley": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz",
|
||||
"integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
|
||||
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
|
||||
"requires": {
|
||||
"moo": "^0.5.1",
|
||||
"nearley": "^2.20.1"
|
||||
"leac": "^0.6.0",
|
||||
"peberminta": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
@@ -34184,6 +34030,11 @@
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
},
|
||||
"peberminta": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
|
||||
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="
|
||||
},
|
||||
"phin": {
|
||||
"version": "2.9.3",
|
||||
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
|
||||
@@ -34734,27 +34585,6 @@
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
|
||||
},
|
||||
"railroad-diagrams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
||||
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A=="
|
||||
},
|
||||
"randexp": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
|
||||
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
|
||||
"requires": {
|
||||
"discontinuous-range": "1.0.0",
|
||||
"ret": "~0.1.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"ret": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@@ -35710,11 +35540,11 @@
|
||||
"integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w=="
|
||||
},
|
||||
"selderee": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz",
|
||||
"integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
|
||||
"requires": {
|
||||
"parseley": "^0.7.0"
|
||||
"parseley": "^0.12.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.12",
|
||||
"version": "2.4.13",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
diff --git a/node_modules/html-to-text/lib/formatter.js b/node_modules/html-to-text/lib/formatter.js
|
||||
index d6cb1e6..68d0337 100644
|
||||
--- a/node_modules/html-to-text/lib/formatter.js
|
||||
+++ b/node_modules/html-to-text/lib/formatter.js
|
||||
@@ -1,4 +1,4 @@
|
||||
-const he = require('he');
|
||||
+const {decode} = require('entities');
|
||||
|
||||
const { get, numberToLetterSequence, numberToRoman, splitClassesAndIds, trimCharacter } = require('./helper');
|
||||
|
||||
@@ -146,7 +146,7 @@ function withBrackets (str, brackets) {
|
||||
function formatImage (elem, walk, builder, formatOptions) {
|
||||
const attribs = elem.attribs || {};
|
||||
const alt = (attribs.alt)
|
||||
- ? he.decode(attribs.alt, builder.options.decodeOptions)
|
||||
+ ? decode(attribs.alt, builder.options.decodeOptions)
|
||||
: '';
|
||||
const src = (!attribs.src)
|
||||
? ''
|
||||
@@ -176,7 +176,7 @@ function formatAnchor (elem, walk, builder, formatOptions) {
|
||||
href = (formatOptions.baseUrl && href[0] === '/')
|
||||
? formatOptions.baseUrl + href
|
||||
: href;
|
||||
- return he.decode(href, builder.options.decodeOptions);
|
||||
+ return decode(href, builder.options.decodeOptions);
|
||||
}
|
||||
const href = getHref();
|
||||
if (!href) {
|
||||
diff --git a/node_modules/html-to-text/lib/html-to-text.js b/node_modules/html-to-text/lib/html-to-text.js
|
||||
index 9ebda73..8c5345f 100644
|
||||
--- a/node_modules/html-to-text/lib/html-to-text.js
|
||||
+++ b/node_modules/html-to-text/lib/html-to-text.js
|
||||
@@ -1,6 +1,6 @@
|
||||
const { hp2Builder } = require('@selderee/plugin-htmlparser2');
|
||||
const merge = require('deepmerge');
|
||||
-const he = require('he');
|
||||
+const {decode, EntityLevel} = require("entities");
|
||||
const htmlparser = require('htmlparser2');
|
||||
const selderee = require('selderee');
|
||||
|
||||
@@ -27,8 +27,7 @@ const DEFAULT_OPTIONS = {
|
||||
returnDomByDefault: true
|
||||
},
|
||||
decodeOptions: {
|
||||
- isAttributeValue: false,
|
||||
- strict: false
|
||||
+ level:EntityLevel.HTML
|
||||
},
|
||||
formatters: {},
|
||||
limits: {
|
||||
@@ -377,7 +376,7 @@ function recursiveWalk (walk, dom, builder) {
|
||||
for (const elem of dom) {
|
||||
switch (elem.type) {
|
||||
case 'text': {
|
||||
- builder.addInline(he.decode(elem.data, options.decodeOptions));
|
||||
+ builder.addInline(decode(elem.data, options.decodeOptions));
|
||||
break;
|
||||
}
|
||||
case 'tag': {
|
||||
89
apps/mobile/patches/leac+0.6.0.patch
Normal file
89
apps/mobile/patches/leac+0.6.0.patch
Normal file
@@ -0,0 +1,89 @@
|
||||
diff --git a/node_modules/leac/lib/leac.cjs b/node_modules/leac/lib/leac.cjs
|
||||
index 13123da..9d490e2 100644
|
||||
--- a/node_modules/leac/lib/leac.cjs
|
||||
+++ b/node_modules/leac/lib/leac.cjs
|
||||
@@ -1 +1,83 @@
|
||||
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=/\n/g;function t(t){const o=[...t.matchAll(e)].map((e=>e.index||0));o.unshift(-1);const s=n(o,0,o.length);return e=>r(s,e)}function n(e,t,r){if(r-t==1)return{offset:e[t],index:t+1};const o=Math.ceil((t+r)/2),s=n(e,t,o),l=n(e,o,r);return{offset:s.offset,low:s,high:l}}function r(e,t){return function(e){return Object.prototype.hasOwnProperty.call(e,"index")}(e)?{line:e.index,column:t-e.offset}:r(e.high.offset<t?e.high:e.low,t)}function o(e,t){return{...e,regex:s(e,t)}}function s(e,t){if(0===e.name.length)throw new Error(`Rule #${t} has empty name, which is not allowed.`);if(function(e){return Object.prototype.hasOwnProperty.call(e,"regex")}(e))return function(e){if(e.global)throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);return e.sticky?e:new RegExp(e.source,e.flags+"y")}(e.regex);if(function(e){return Object.prototype.hasOwnProperty.call(e,"str")}(e)){if(0===e.str.length)throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);return new RegExp(l(e.str),"y")}return new RegExp(l(e.name),"y")}function l(e){return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g,"\\$&")}exports.createLexer=function(e,n="",r={}){const s="string"!=typeof n?n:r,l="string"==typeof n?n:"",c=e.map(o),i=!!s.lineNumbers;return function(e,n=0){const r=i?t(e):()=>({line:0,column:0});let o=n;const s=[];e:for(;o<e.length;){let t=!1;for(const n of c){n.regex.lastIndex=o;const c=n.regex.exec(e);if(c&&c[0].length>0){if(!n.discard){const e=r(o),t="string"==typeof n.replace?c[0].replace(new RegExp(n.regex.source,n.regex.flags),n.replace):c[0];s.push({state:l,name:n.name,text:t,offset:o,len:c[0].length,line:e.line,column:e.column})}if(o=n.regex.lastIndex,t=!0,n.push){const t=n.push(e,o);s.push(...t.tokens),o=t.offset}if(n.pop)break e;break}}if(!t)break}return{tokens:s,offset:o,complete:e.length<=o}}};
|
||||
+"use strict";
|
||||
+Object.defineProperty(exports, "__esModule", { value: !0 });
|
||||
+const e = /\n/g;
|
||||
+function t(t) {
|
||||
+ const o = [...t.matchAll(e)].map((e) => e.index || 0);
|
||||
+ o.unshift(-1);
|
||||
+ const s = n(o, 0, o.length);
|
||||
+ return (e) => r(s, e);
|
||||
+}
|
||||
+function n(e, t, r) {
|
||||
+ if (r - t == 1) return { offset: e[t], index: t + 1 };
|
||||
+ const o = Math.ceil((t + r) / 2),
|
||||
+ s = n(e, t, o),
|
||||
+ l = n(e, o, r);
|
||||
+ return { offset: s.offset, low: s, high: l };
|
||||
+}
|
||||
+function r(e, t) {
|
||||
+ return (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "index");
|
||||
+ })(e)
|
||||
+ ? { line: e.index, column: t - e.offset }
|
||||
+ : r(e.high.offset < t ? e.high : e.low, t);
|
||||
+}
|
||||
+function _o(e, t) {
|
||||
+ return { ...e, regex: s(e, t) };
|
||||
+}
|
||||
+function s(e, t) {
|
||||
+ if (0 === e.name.length) throw new Error(`Rule #${t} has empty name, which is not allowed.`);
|
||||
+ if (
|
||||
+ (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "regex");
|
||||
+ })(e)
|
||||
+ )
|
||||
+ return (function (e) {
|
||||
+ if (e.global) throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);
|
||||
+ return e.sticky ? e : new RegExp(e.source, e.flags + "y");
|
||||
+ })(e.regex);
|
||||
+ if (
|
||||
+ (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "str");
|
||||
+ })(e)
|
||||
+ ) {
|
||||
+ if (0 === e.str.length) throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);
|
||||
+ return new RegExp(l(e.str), "y");
|
||||
+ }
|
||||
+ return new RegExp(l(e.name), "y");
|
||||
+}
|
||||
+function l(e) {
|
||||
+ return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, "\\$&");
|
||||
+}
|
||||
+exports.createLexer = function (_e, n = "", r = {}) {
|
||||
+ const s = "string" != typeof n ? n : r,
|
||||
+ l = "string" == typeof n ? n : "",
|
||||
+ i = !!s.lineNumbers;
|
||||
+ return function (e, n = 0) {
|
||||
+ const r = i ? t(e) : () => ({ line: 0, column: 0 });
|
||||
+ let o = n;
|
||||
+ const s = [];
|
||||
+ e: for (; o < e.length; ) {
|
||||
+ let t = !1;
|
||||
+ let z = _e.map(_o);
|
||||
+ for (const n of z) {
|
||||
+ n.regex.lastIndex = o;
|
||||
+ const c = n.regex.exec(e);
|
||||
+ if (c && c[0].length > 0) {
|
||||
+ if (!n.discard) {
|
||||
+ const e = r(o),
|
||||
+ t = "string" == typeof n.replace ? c[0].replace(new RegExp(n.regex.source, n.regex.flags), n.replace) : c[0];
|
||||
+ s.push({ state: l, name: n.name, text: t, offset: o, len: c[0].length, line: e.line, column: e.column });
|
||||
+ }
|
||||
+ if (((o = n.regex.lastIndex), (t = !0), n.push)) {
|
||||
+ const t = n.push(e, o);
|
||||
+ s.push(...t.tokens), (o = t.offset);
|
||||
+ }
|
||||
+ if (n.pop) break e;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (!t) break;
|
||||
+ }
|
||||
+ return { tokens: s, offset: o, complete: true };
|
||||
+ };
|
||||
+};
|
||||
@@ -32,7 +32,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../app/common/database";
|
||||
import { getElevation } from "../app/utils";
|
||||
import { useShareStore } from "./store";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
|
||||
const ListItem = ({ item, mode, close }) => {
|
||||
const colors = useShareStore((state) => state.colors);
|
||||
@@ -226,8 +226,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
|
||||
|
||||
const onSearch = async () => {
|
||||
if (!searchableItems.current) {
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
}
|
||||
if (timer.current) {
|
||||
@@ -248,8 +247,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
setSearchResults(searchableItems.current);
|
||||
})();
|
||||
|
||||
@@ -26,12 +26,12 @@ import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
ScrollView
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import {
|
||||
SafeAreaProvider,
|
||||
@@ -45,10 +45,9 @@ import Storage from "../app/common/database/storage";
|
||||
import { eSendEvent } from "../app/services/event-manager";
|
||||
import { getElevation } from "../app/utils";
|
||||
import { eOnLoadNote } from "../app/utils/events";
|
||||
import { sleep } from "../app/utils/time";
|
||||
import { Search } from "./search";
|
||||
import { useShareStore } from "./store";
|
||||
import { Editor } from "./editor";
|
||||
import { Search } from "./search";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
const getLinkPreview = (url) => {
|
||||
return getPreviewData(url, 5000);
|
||||
};
|
||||
@@ -314,9 +313,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
|
||||
const onPress = async () => {
|
||||
setLoading(true);
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
await sleep(1500);
|
||||
await initDatabase();
|
||||
if (!noteContent.current) return;
|
||||
if (appendNote && !db.notes.note(appendNote.id)) {
|
||||
useShareStore.getState().setAppendNote(null);
|
||||
@@ -359,6 +356,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
await db.sync(false, false);
|
||||
await Storage.write("notesAddedFromIntent", "added");
|
||||
close();
|
||||
setLoading(false);
|
||||
|
||||
@@ -25,6 +25,14 @@ import {
|
||||
COLOR_SCHEME_LIGHT
|
||||
} from "../app/utils/color-scheme";
|
||||
import { MMKV } from "../app/common/database/mmkv";
|
||||
import { db } from "../app/common/database";
|
||||
|
||||
export async function initDatabase() {
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
}
|
||||
await db.notes.init();
|
||||
}
|
||||
|
||||
const StorageKeys = {
|
||||
appendNote: "shareMenuAppendNote",
|
||||
|
||||
4
apps/web/desktop/package-lock.json
generated
4
apps/web/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"dependencies": {
|
||||
"diary": "^0.3.1",
|
||||
"electron-updater": "^5.3.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./build/electron.js",
|
||||
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.8",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
@@ -138,11 +138,6 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
@keyframes fadeUp {
|
||||
0% {
|
||||
transform: translateY(500px);
|
||||
|
||||
@@ -90,7 +90,13 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
}
|
||||
]
|
||||
: isDesktop()
|
||||
? []
|
||||
? [
|
||||
{
|
||||
title: "Default font size & font family",
|
||||
subtitle:
|
||||
"You can now change the default font family and size used by the editor across all your older & newer notes. Go into Settings > Editor settings to change these settings."
|
||||
}
|
||||
]
|
||||
: [],
|
||||
cta: {
|
||||
title: "Got it",
|
||||
|
||||
@@ -21,14 +21,19 @@ import { Button, Flex } from "@theme-ui/components";
|
||||
import { useMenuTrigger } from "../../hooks/use-menu";
|
||||
import { ChevronDown } from "../icons";
|
||||
|
||||
export default function DropdownButton({ title, options }) {
|
||||
export default function DropdownButton(props) {
|
||||
const { openMenu } = useMenuTrigger();
|
||||
const { options, title, sx, buttonStyle, chevronStyle } = props;
|
||||
|
||||
if (!options || !options.length) return null;
|
||||
return (
|
||||
<Flex>
|
||||
<Flex sx={sx}>
|
||||
<Button
|
||||
sx={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
||||
sx={{
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
...buttonStyle
|
||||
}}
|
||||
onClick={options[0].onClick}
|
||||
>
|
||||
{options[0].title()}
|
||||
@@ -38,7 +43,8 @@ export default function DropdownButton({ title, options }) {
|
||||
px={1}
|
||||
sx={{
|
||||
borderBottomLeftRadius: 0,
|
||||
borderTopLeftRadius: 0
|
||||
borderTopLeftRadius: 0,
|
||||
...chevronStyle
|
||||
}}
|
||||
onClick={() => openMenu(options.slice(1), { title })}
|
||||
>
|
||||
|
||||
@@ -24,17 +24,26 @@ import BaseStore from "../../stores";
|
||||
import { UseBoundStore } from "zustand";
|
||||
import shallow from "zustand/shallow";
|
||||
import type { ToolbarDefinition } from "@notesnook/editor";
|
||||
import Config from "../../utils/config";
|
||||
|
||||
type EditorConfig = { fontFamily: string; fontSize: number };
|
||||
type EditorSubState = {
|
||||
editor?: IEditor;
|
||||
canUndo?: boolean;
|
||||
canRedo?: boolean;
|
||||
searching?: boolean;
|
||||
toolbarConfig?: ToolbarDefinition;
|
||||
editorConfig: EditorConfig;
|
||||
statistics?: NoteStatistics;
|
||||
};
|
||||
|
||||
class EditorContext extends BaseStore {
|
||||
subState: EditorSubState = {};
|
||||
subState: EditorSubState = {
|
||||
editorConfig: Config.get("editorConfig", {
|
||||
fontFamily: "sans-serif",
|
||||
fontSize: 16
|
||||
})
|
||||
};
|
||||
|
||||
configure = (
|
||||
partial:
|
||||
@@ -116,3 +125,27 @@ export function useNoteStatistics(): NoteStatistics {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function useEditorConfig() {
|
||||
const editorConfig = useEditorContext((store) => store.subState.editorConfig);
|
||||
const configure = useEditorContext((store) => store.configure);
|
||||
const setEditorConfig = useCallback(
|
||||
(config: Partial<EditorConfig>) => {
|
||||
if (editorConfig)
|
||||
Config.set("editorConfig", {
|
||||
...editorConfig,
|
||||
...config
|
||||
});
|
||||
|
||||
configure({
|
||||
editorConfig: {
|
||||
...editorConfig,
|
||||
...config
|
||||
}
|
||||
});
|
||||
},
|
||||
[editorConfig, configure]
|
||||
);
|
||||
|
||||
return { editorConfig, setEditorConfig };
|
||||
}
|
||||
|
||||
@@ -33,9 +33,10 @@ import {
|
||||
usePermissionHandler,
|
||||
getHTMLFromFragment,
|
||||
Fragment,
|
||||
type DownloadOptions,
|
||||
getTotalWords,
|
||||
countWords,
|
||||
type DownloadOptions
|
||||
getFontById
|
||||
} from "@notesnook/editor";
|
||||
import { Box, Flex } from "@theme-ui/components";
|
||||
import { PropsWithChildren, useEffect, useRef, useState } from "react";
|
||||
@@ -45,7 +46,8 @@ import {
|
||||
useConfigureEditor,
|
||||
useSearch,
|
||||
useToolbarConfig,
|
||||
configureEditor
|
||||
configureEditor,
|
||||
useEditorConfig
|
||||
} from "./context";
|
||||
import { createPortal } from "react-dom";
|
||||
import { getCurrentPreset } from "../../common/toolbar-config";
|
||||
@@ -71,6 +73,8 @@ type TipTapProps = {
|
||||
theme: Theme;
|
||||
isMobile?: boolean;
|
||||
downloadOptions?: DownloadOptions;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
};
|
||||
|
||||
const SAVE_INTERVAL = process.env.REACT_APP_TEST ? 100 : 300;
|
||||
@@ -115,7 +119,9 @@ function TipTap(props: TipTapProps) {
|
||||
nonce,
|
||||
theme,
|
||||
isMobile,
|
||||
downloadOptions
|
||||
downloadOptions,
|
||||
fontSize,
|
||||
fontFamily
|
||||
} = props;
|
||||
|
||||
const isUserPremium = useIsUserPremium();
|
||||
@@ -158,7 +164,6 @@ function TipTap(props: TipTapProps) {
|
||||
},
|
||||
downloadOptions,
|
||||
doubleSpacedLines,
|
||||
isKeyboardOpen: true,
|
||||
isMobile: isMobile || false,
|
||||
element: editorContainer,
|
||||
editable: !readonly,
|
||||
@@ -302,7 +307,6 @@ function TipTap(props: TipTapProps) {
|
||||
}, [editor, editorContainer]);
|
||||
|
||||
if (!toolbarContainerId) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Portal containerId={toolbarContainerId}>
|
||||
@@ -311,16 +315,24 @@ function TipTap(props: TipTapProps) {
|
||||
theme={theme}
|
||||
location={isMobile ? "bottom" : "top"}
|
||||
tools={toolbarConfig}
|
||||
defaultFontFamily={fontFamily}
|
||||
defaultFontSize={fontSize}
|
||||
/>
|
||||
</Portal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TiptapWrapper(props: Omit<TipTapProps, "editorContainer" | "theme">) {
|
||||
function TiptapWrapper(
|
||||
props: Omit<
|
||||
TipTapProps,
|
||||
"editorContainer" | "theme" | "fontSize" | "fontFamily"
|
||||
>
|
||||
) {
|
||||
const theme = useTheme() as Theme;
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const editorContainerRef = useRef<HTMLDivElement>(null);
|
||||
const { editorConfig } = useEditorConfig();
|
||||
useEffect(() => {
|
||||
setIsReady(true);
|
||||
}, []);
|
||||
@@ -333,6 +345,8 @@ function TiptapWrapper(props: Omit<TipTapProps, "editorContainer" | "theme">) {
|
||||
{...props}
|
||||
editorContainer={editorContainerRef.current}
|
||||
theme={theme}
|
||||
fontFamily={editorConfig.fontFamily}
|
||||
fontSize={editorConfig.fontSize}
|
||||
/>
|
||||
) : null}
|
||||
<Box
|
||||
@@ -342,7 +356,9 @@ function TiptapWrapper(props: Omit<TipTapProps, "editorContainer" | "theme">) {
|
||||
flex: 1,
|
||||
cursor: "text",
|
||||
color: theme.colors.text, // TODO!
|
||||
paddingBottom: 150
|
||||
paddingBottom: 150,
|
||||
fontSize: editorConfig.fontSize,
|
||||
fontFamily: getFontById(editorConfig.fontFamily)?.font
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -23,6 +23,8 @@ import { useStore, store } from "../../stores/editor-store";
|
||||
import { debounceWithId } from "../../utils/debounce";
|
||||
import useMobile from "../../hooks/use-mobile";
|
||||
import useTablet from "../../hooks/use-tablet";
|
||||
import { useEditorConfig } from "./context";
|
||||
import { getFontById } from "@notesnook/editor";
|
||||
|
||||
type TitleBoxProps = {
|
||||
readonly: boolean;
|
||||
@@ -35,6 +37,7 @@ function TitleBox(props: TitleBoxProps) {
|
||||
const id = useStore((store) => store.session.id);
|
||||
const isMobile = useMobile();
|
||||
const isTablet = useTablet();
|
||||
const { editorConfig } = useEditorConfig();
|
||||
|
||||
const MAX_FONT_SIZE = useMemo(() => {
|
||||
return isMobile || isTablet ? 1.625 : 2.625;
|
||||
@@ -66,7 +69,7 @@ function TitleBox(props: TitleBoxProps) {
|
||||
readOnly={readonly}
|
||||
sx={{
|
||||
p: 0,
|
||||
fontFamily: "heading",
|
||||
fontFamily: getFontById(editorConfig.fontFamily)?.font || "heading",
|
||||
fontSize: ["1.625em", "1.625em", "2.625em"],
|
||||
fontWeight: "heading",
|
||||
width: "100%"
|
||||
|
||||
@@ -92,6 +92,7 @@ function Field(props) {
|
||||
sx={{
|
||||
fontSize: "subtitle",
|
||||
fontWeight: "bold",
|
||||
fontFamily: "body",
|
||||
color: "icon",
|
||||
flexDirection: "column",
|
||||
...styles.label
|
||||
|
||||
@@ -1019,13 +1019,14 @@ function openURL(url: string, force?: boolean) {
|
||||
|
||||
function maskEmail(email: string) {
|
||||
if (!email) return "";
|
||||
const [username, domain] = email.split("@");
|
||||
const maskChars = "*".repeat(
|
||||
username.substring(2, username.length - 2).length
|
||||
);
|
||||
return `${username.substring(0, 2)}${maskChars}${username.substring(
|
||||
username.length - 2
|
||||
)}@${domain}`;
|
||||
const [username, provider] = email.split("@");
|
||||
if (username.length === 1) return `****@${provider}`;
|
||||
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
|
||||
for (let i = 0; i < gp3.length; i++) {
|
||||
gp2 += "*";
|
||||
}
|
||||
return gp2;
|
||||
});
|
||||
}
|
||||
|
||||
function isSessionExpired() {
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { Button, Flex, Input, Text } from "@theme-ui/components";
|
||||
import * as Icon from "../components/icons";
|
||||
import { useStore as useUserStore } from "../stores/user-store";
|
||||
import { useStore as useNoteStore } from "../stores/note-store";
|
||||
@@ -81,6 +81,8 @@ import { useTelemetry } from "../hooks/use-telemetry";
|
||||
import useSpellChecker from "../hooks/use-spell-checker";
|
||||
import useDesktopIntegration from "../hooks/use-desktop-integration";
|
||||
import { writeText } from "clipboard-polyfill";
|
||||
import { useEditorConfig } from "../components/editor/context";
|
||||
import { getFonts } from "@notesnook/editor";
|
||||
|
||||
function subscriptionStatusToString(user) {
|
||||
const status = user?.subscription?.type;
|
||||
@@ -219,6 +221,7 @@ function Settings() {
|
||||
"corsProxy",
|
||||
"https://cors.notesnook.com"
|
||||
);
|
||||
const { editorConfig, setEditorConfig } = useEditorConfig();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -622,6 +625,51 @@ function Settings() {
|
||||
/>
|
||||
{groups.editor && (
|
||||
<>
|
||||
<OptionsItem
|
||||
title={"Default font family"}
|
||||
tip={"Set default font family"}
|
||||
selectedOption={editorConfig.fontFamily}
|
||||
options={getFonts().map((font) => ({
|
||||
value: font.id,
|
||||
title: font.title
|
||||
}))}
|
||||
onSelectionChanged={(option) => {
|
||||
setEditorConfig({ fontFamily: option.value });
|
||||
}}
|
||||
/>
|
||||
<Flex
|
||||
py={2}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid",
|
||||
borderBottomColor: "border",
|
||||
":hover": { borderBottomColor: "primary" },
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Tip
|
||||
text="Default font size"
|
||||
tip="Set default font size"
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
min={8}
|
||||
sx={{ width: 70 }}
|
||||
defaultValue={editorConfig.fontSize}
|
||||
onChange={(e) => {
|
||||
setEditorConfig({ fontSize: e.currentTarget.valueAsNumber });
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{/* <Counter
|
||||
value={editorConfig.fontSize}
|
||||
onDecrease={() => {}}
|
||||
onIncrease={() => {}}
|
||||
title="Set default font size"
|
||||
onReset={() => setEditorConfig({ fontFamily: "16px" })}
|
||||
/> */}
|
||||
{/* <DefaultFont /> */}
|
||||
<Toggle
|
||||
title="Use double spaced lines"
|
||||
onTip="New lines will be double spaced (old ones won't be affected)."
|
||||
|
||||
@@ -95,7 +95,7 @@ describe.each([
|
||||
let tag = db[collection].tag(value);
|
||||
await db[collection].rename(tag.id, value + " new");
|
||||
tag = db[collection].tag(tag.id);
|
||||
expect(db[collection].alias(tag.id)).toBe(value + "new");
|
||||
expect(db[collection].alias(tag.id)).toBe(value + " new");
|
||||
}));
|
||||
|
||||
test(`remove a ${action}`, () =>
|
||||
|
||||
@@ -58,6 +58,7 @@ var NNEventSource;
|
||||
// const DIFFERENCE_THRESHOLD = 20 * 1000;
|
||||
// const MAX_TIME_ERROR_FAILURES = 5;
|
||||
class Database {
|
||||
isInitialized = false;
|
||||
/**
|
||||
*
|
||||
* @param {any} storage
|
||||
@@ -131,6 +132,19 @@ class Database {
|
||||
this.pricing = new Pricing();
|
||||
this.subscriptions = new Subscriptions(this.user.tokenManager);
|
||||
|
||||
await this.initCollections();
|
||||
|
||||
await this.settings.init();
|
||||
await this.outbox.init();
|
||||
await this.user.init();
|
||||
await this.migrations.init();
|
||||
this.isInitialized = true;
|
||||
if (this.migrations.required()) {
|
||||
logger.warn("Database migration is required.");
|
||||
}
|
||||
}
|
||||
|
||||
async initCollections() {
|
||||
// collections
|
||||
/** @type {Notes} */
|
||||
this.notes = await Notes.new(this, "notes", true, true);
|
||||
@@ -154,15 +168,6 @@ class Database {
|
||||
this.relations = await Relations.new(this, "relations");
|
||||
|
||||
this.trash = new Trash(this);
|
||||
|
||||
await this.settings.init();
|
||||
await this.outbox.init();
|
||||
await this.user.init();
|
||||
await this.migrations.init();
|
||||
|
||||
if (this.migrations.required()) {
|
||||
logger.warn("Database migration is required.");
|
||||
}
|
||||
}
|
||||
|
||||
disconnectSSE() {
|
||||
|
||||
@@ -29,13 +29,13 @@ export default class Subscriptions {
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
const token = this._tokenManager.getAccessToken();
|
||||
const token = await this._tokenManager.getAccessToken();
|
||||
if (!token) return;
|
||||
await http.delete(`${hosts.SUBSCRIPTIONS_HOST}/subscriptions`, token);
|
||||
}
|
||||
|
||||
async updateUrl() {
|
||||
const token = this._tokenManager.getAccessToken();
|
||||
const token = await this._tokenManager.getAccessToken();
|
||||
if (!token) return;
|
||||
return await http.get(
|
||||
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/update_url`,
|
||||
|
||||
@@ -117,7 +117,6 @@ export default class Reminders extends Collection {
|
||||
async remove(...reminderIds) {
|
||||
for (const id of reminderIds) {
|
||||
await this._collection.removeItem(id);
|
||||
await this._db.relations.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import dataurl from "../utils/dataurl";
|
||||
import { extractFirstParagraph, getDummyDocument } from "../utils/html-parser";
|
||||
import { HTMLParser, HTMLRewriter } from "../utils/html-rewriter";
|
||||
import { convert } from "html-to-text";
|
||||
import { list } from "html-to-text/lib/formatter";
|
||||
|
||||
const ATTRIBUTES = {
|
||||
hash: "data-hash",
|
||||
@@ -51,11 +50,12 @@ export class Tiptap {
|
||||
preserveNewlines: true,
|
||||
selectors: [
|
||||
{ selector: "table", format: "dataTable" },
|
||||
{ selector: "ul.checklist", format: "taskList" }
|
||||
{ selector: "ul.checklist", format: "taskList" },
|
||||
{ selector: "p", format: "paragraph" }
|
||||
],
|
||||
formatters: {
|
||||
taskList: (elem, walk, builder, formatOptions) => {
|
||||
return list(elem, walk, builder, formatOptions, (elem) => {
|
||||
return formatList(elem, walk, builder, formatOptions, (elem) => {
|
||||
return elem.attribs.class.includes("checked") ? " ✅ " : " ☐ ";
|
||||
});
|
||||
},
|
||||
@@ -225,3 +225,53 @@ export class Tiptap {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { import("html-to-text").DomNode } elem List items with their prefixes.
|
||||
* @param { import("html-to-text").RecursiveCallback } walk Recursive callback to process child nodes.
|
||||
* @param { import("html-to-text/lib/block-text-builder").BlockTextBuilder } builder Passed around to accumulate output text.
|
||||
* @param { import("html-to-text").FormatOptions } formatOptions Options specific to a formatter.
|
||||
* @param { (elem: import("html-to-text").DomNode) => string } nextPrefixCallback Function that returns increasing index each time it is called.
|
||||
*/
|
||||
function formatList(elem, walk, builder, formatOptions, nextPrefixCallback) {
|
||||
const isNestedList = elem?.parent?.name === "li";
|
||||
|
||||
// With Roman numbers, index length is not as straightforward as with Arabic numbers or letters,
|
||||
// so the dumb length comparison is the most robust way to get the correct value.
|
||||
let maxPrefixLength = 0;
|
||||
const listItems = (elem.children || [])
|
||||
// it might be more accurate to check only for html spaces here, but no significant benefit
|
||||
.filter((child) => child.type !== "text" || !/^\s*$/.test(child.data))
|
||||
.map(function (child) {
|
||||
if (child.name !== "li") {
|
||||
return { node: child, prefix: "" };
|
||||
}
|
||||
const prefix = isNestedList
|
||||
? nextPrefixCallback(child).trimStart()
|
||||
: nextPrefixCallback(child);
|
||||
if (prefix.length > maxPrefixLength) {
|
||||
maxPrefixLength = prefix.length;
|
||||
}
|
||||
return { node: child, prefix: prefix };
|
||||
});
|
||||
if (!listItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
builder.openList({
|
||||
interRowLineBreaks: 1,
|
||||
leadingLineBreaks: isNestedList ? 1 : formatOptions.leadingLineBreaks || 2,
|
||||
maxPrefixLength: maxPrefixLength,
|
||||
prefixAlign: "left"
|
||||
});
|
||||
|
||||
for (const { node, prefix } of listItems) {
|
||||
builder.openListItem({ prefix: prefix });
|
||||
walk([node], builder);
|
||||
builder.closeListItem();
|
||||
}
|
||||
|
||||
builder.closeList({
|
||||
trailingLineBreaks: isNestedList ? 1 : formatOptions.trailingLineBreaks || 2
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { getContentFromData } from "../content-types";
|
||||
import { CHECK_IDS, checkIsUserPremium } from "../common";
|
||||
import { addItem, deleteItem } from "../utils/array";
|
||||
import { formatDate } from "../utils/date";
|
||||
import qclone from "qclone";
|
||||
|
||||
export default class Note {
|
||||
/**
|
||||
@@ -135,12 +136,14 @@ export default class Note {
|
||||
async duplicate() {
|
||||
const content = await this._db.content.raw(this._note.contentId);
|
||||
return await this._db.notes.add({
|
||||
...this._note,
|
||||
...qclone(this._note),
|
||||
id: undefined,
|
||||
content: {
|
||||
type: content.type,
|
||||
data: content.data
|
||||
},
|
||||
content: content
|
||||
? {
|
||||
type: content.type,
|
||||
data: content.data
|
||||
}
|
||||
: undefined,
|
||||
readonly: false,
|
||||
favorite: false,
|
||||
pinned: false,
|
||||
|
||||
259
packages/core/package-lock.json
generated
259
packages/core/package-lock.json
generated
@@ -15,7 +15,7 @@
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "^1.11.3",
|
||||
"entities": "^4.3.1",
|
||||
"html-to-text": "github:thecodrr/node-html-to-text",
|
||||
"html-to-text": "^9.0.5",
|
||||
"htmlparser2": "^8.0.1",
|
||||
"linkedom": "^0.14.17",
|
||||
"liqe": "^1.13.0",
|
||||
@@ -28,6 +28,7 @@
|
||||
"@babel/plugin-transform-runtime": "^7.18.9",
|
||||
"@babel/preset-env": "^7.18.9",
|
||||
"@babel/runtime": "^7.18.9",
|
||||
"@types/html-to-text": "^9.0.0",
|
||||
"@types/jest": "^28.1.6",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.3",
|
||||
@@ -2570,12 +2571,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@selderee/plugin-htmlparser2": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.9.0.tgz",
|
||||
"integrity": "sha512-d4zFHnTLNEjLRJxSxjsRSmLUIT5+EEG42rEs/F/LF6EoIE+KLTMqeyfJa+N3ngmehCyZj3cUA53kZda9c8dILw==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
|
||||
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
|
||||
"dependencies": {
|
||||
"domhandler": "^4.2.2",
|
||||
"selderee": "^0.9.0"
|
||||
"domhandler": "^5.0.3",
|
||||
"selderee": "^0.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -2676,6 +2677,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/html-to-text": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.0.tgz",
|
||||
"integrity": "sha512-FnF3p2FJZ1kJT/0C/lmBzw7HSlH3RhtACVYyrwUsJoCmFNuiLpusWT2FWWB7P9A48CaYpvD6Q2fprn7sZeffpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
||||
@@ -5120,20 +5127,6 @@
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||
@@ -5223,9 +5216,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -5287,20 +5280,6 @@
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
@@ -5325,11 +5304,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@@ -5351,20 +5330,6 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||
@@ -5830,22 +5795,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/html-to-text": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "git+ssh://git@github.com/thecodrr/node-html-to-text.git#8acb9b2fccceacbf5c01664fc15f9bc5f0e82762",
|
||||
"license": "MIT",
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
|
||||
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
|
||||
"dependencies": {
|
||||
"@selderee/plugin-htmlparser2": "^0.9.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"entities": "^4.4.0",
|
||||
"htmlparser2": "github:thecodrr/htmlparser2",
|
||||
"minimist": "^1.2.7",
|
||||
"selderee": "^0.9.0"
|
||||
},
|
||||
"bin": {
|
||||
"html-to-text": "bin/cli.js"
|
||||
"@selderee/plugin-htmlparser2": "^0.11.0",
|
||||
"deepmerge": "^4.3.1",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"htmlparser2": "^8.0.2",
|
||||
"selderee": "^0.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.23.2"
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
@@ -5866,20 +5827,6 @@
|
||||
"entities": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
@@ -7980,9 +7927,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/leac": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.5.1.tgz",
|
||||
"integrity": "sha512-ItrUZwFdQSJT5sHceYI8qQif98Sc1XV0kLhn0aWpE4f82B+FICsF4egayLMS1iCHxNKWLbvK6DQNaRSTAbjnYA==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
|
||||
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
@@ -8181,6 +8128,7 @@
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -8486,12 +8434,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/parseley": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.10.0.tgz",
|
||||
"integrity": "sha512-8r4sZD2bNjTxACVk9Sqkzc0pXO9G0KznjPnd+UbHs+ZkvFr5irw4P5HwMVoixanPFP5ZlZKsuf1QP+e47SYZAQ==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
|
||||
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
|
||||
"dependencies": {
|
||||
"leac": "^0.5.1",
|
||||
"peberminta": "^0.6.0"
|
||||
"leac": "^0.6.0",
|
||||
"peberminta": "^0.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -8531,9 +8479,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/peberminta": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.6.0.tgz",
|
||||
"integrity": "sha512-qeJGsC0f6dIWtpIN6t9MhpbIviMJ7wNosu+RtzXCyoxlEUlFJWVaMd3KvYO6ciNPi5XF6TvsVpSVq+a8iBtUBw==",
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
|
||||
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
@@ -8911,11 +8859,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/selderee": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.9.0.tgz",
|
||||
"integrity": "sha512-Zsg9YrMit8Z2u4L/f4oGuVjreT3KRml2Ak2aUKr4S+an3vy6ntGv7MLUs4i2hN6bz9b9DcDMl2fS4r1obhDMJA==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
|
||||
"dependencies": {
|
||||
"parseley": "^0.10.0"
|
||||
"parseley": "^0.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
@@ -11514,12 +11462,12 @@
|
||||
}
|
||||
},
|
||||
"@selderee/plugin-htmlparser2": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.9.0.tgz",
|
||||
"integrity": "sha512-d4zFHnTLNEjLRJxSxjsRSmLUIT5+EEG42rEs/F/LF6EoIE+KLTMqeyfJa+N3ngmehCyZj3cUA53kZda9c8dILw==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
|
||||
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
|
||||
"requires": {
|
||||
"domhandler": "^4.2.2",
|
||||
"selderee": "^0.9.0"
|
||||
"domhandler": "^5.0.3",
|
||||
"selderee": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"@sinclair/typebox": {
|
||||
@@ -11607,6 +11555,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/html-to-text": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.0.tgz",
|
||||
"integrity": "sha512-FnF3p2FJZ1kJT/0C/lmBzw7HSlH3RhtACVYyrwUsJoCmFNuiLpusWT2FWWB7P9A48CaYpvD6Q2fprn7sZeffpw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
||||
@@ -13950,16 +13904,6 @@
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
@@ -14033,9 +13977,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
@@ -14077,16 +14021,6 @@
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domelementtype": {
|
||||
@@ -14104,11 +14038,11 @@
|
||||
}
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.2.0"
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
@@ -14119,16 +14053,6 @@
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
@@ -14471,15 +14395,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"html-to-text": {
|
||||
"version": "git+ssh://git@github.com/thecodrr/node-html-to-text.git#8acb9b2fccceacbf5c01664fc15f9bc5f0e82762",
|
||||
"from": "html-to-text@github:thecodrr/node-html-to-text",
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
|
||||
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
|
||||
"requires": {
|
||||
"@selderee/plugin-htmlparser2": "^0.9.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"entities": "^4.4.0",
|
||||
"@selderee/plugin-htmlparser2": "^0.11.0",
|
||||
"deepmerge": "^4.3.1",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"htmlparser2": "^8.0.1",
|
||||
"minimist": "^1.2.7",
|
||||
"selderee": "^0.9.0"
|
||||
"selderee": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"htmlparser2": {
|
||||
@@ -14491,16 +14415,6 @@
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-proxy-agent": {
|
||||
@@ -16050,9 +15964,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"leac": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.5.1.tgz",
|
||||
"integrity": "sha512-ItrUZwFdQSJT5sHceYI8qQif98Sc1XV0kLhn0aWpE4f82B+FICsF4egayLMS1iCHxNKWLbvK6DQNaRSTAbjnYA=="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
|
||||
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="
|
||||
},
|
||||
"leven": {
|
||||
"version": "3.1.0",
|
||||
@@ -16209,7 +16123,8 @@
|
||||
"minimist": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
@@ -16443,12 +16358,12 @@
|
||||
}
|
||||
},
|
||||
"parseley": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.10.0.tgz",
|
||||
"integrity": "sha512-8r4sZD2bNjTxACVk9Sqkzc0pXO9G0KznjPnd+UbHs+ZkvFr5irw4P5HwMVoixanPFP5ZlZKsuf1QP+e47SYZAQ==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
|
||||
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
|
||||
"requires": {
|
||||
"leac": "^0.5.1",
|
||||
"peberminta": "^0.6.0"
|
||||
"leac": "^0.6.0",
|
||||
"peberminta": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
@@ -16476,9 +16391,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"peberminta": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.6.0.tgz",
|
||||
"integrity": "sha512-qeJGsC0f6dIWtpIN6t9MhpbIviMJ7wNosu+RtzXCyoxlEUlFJWVaMd3KvYO6ciNPi5XF6TvsVpSVq+a8iBtUBw=="
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
|
||||
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
@@ -16769,11 +16684,11 @@
|
||||
}
|
||||
},
|
||||
"selderee": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.9.0.tgz",
|
||||
"integrity": "sha512-Zsg9YrMit8Z2u4L/f4oGuVjreT3KRml2Ak2aUKr4S+an3vy6ntGv7MLUs4i2hN6bz9b9DcDMl2fS4r1obhDMJA==",
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
|
||||
"requires": {
|
||||
"parseley": "^0.10.0"
|
||||
"parseley": "^0.12.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@babel/preset-env": "^7.18.9",
|
||||
"@babel/runtime": "^7.18.9",
|
||||
"@notesnook/crypto": "^1.0.1",
|
||||
"@types/html-to-text": "^9.0.0",
|
||||
"@types/jest": "^28.1.6",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.3",
|
||||
@@ -42,13 +43,13 @@
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "^1.11.3",
|
||||
"entities": "^4.3.1",
|
||||
"html-to-text": "github:thecodrr/node-html-to-text",
|
||||
"html-to-text": "^9.0.5",
|
||||
"htmlparser2": "^8.0.1",
|
||||
"linkedom": "^0.14.17",
|
||||
"liqe": "^1.13.0",
|
||||
"mime-db": "1.52.0",
|
||||
"qclone": "^1.2.0",
|
||||
"spark-md5": "^3.0.2",
|
||||
"mime-db": "1.52.0"
|
||||
"spark-md5": "^3.0.2"
|
||||
},
|
||||
"overrides": {
|
||||
"htmlparser2": "^8.0.1"
|
||||
|
||||
@@ -33,16 +33,6 @@
|
||||
color: var(--nn_placeholder) !important;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Open Sans";
|
||||
color: var(--nn_heading);
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -19,11 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {
|
||||
Editor,
|
||||
getFontById,
|
||||
PortalProvider,
|
||||
Toolbar,
|
||||
usePermissionHandler,
|
||||
useTiptap
|
||||
} from "@notesnook/editor";
|
||||
import { keepLastLineInView } from "@notesnook/editor/dist/extensions/keep-in-view/keep-in-view";
|
||||
import { Theme, useTheme } from "@notesnook/theme";
|
||||
import {
|
||||
forwardRef,
|
||||
@@ -41,12 +43,10 @@ import Header from "./header";
|
||||
import StatusBar from "./statusbar";
|
||||
import Tags from "./tags";
|
||||
import Title from "./title";
|
||||
import { keepLastLineInView } from "@notesnook/editor/dist/extensions/keep-in-view/keep-in-view";
|
||||
|
||||
function isIOSBrowser() {
|
||||
return __PLATFORM__ !== "android";
|
||||
}
|
||||
|
||||
const Tiptap = ({
|
||||
editorTheme,
|
||||
toolbarTheme,
|
||||
@@ -104,7 +104,6 @@ const Tiptap = ({
|
||||
},
|
||||
content: global.editorController?.content?.current,
|
||||
isMobile: true,
|
||||
isKeyboardOpen: settings.keyboardShown,
|
||||
doubleSpacedLines: settings.doubleSpacedLines,
|
||||
onOpenLink: (url) => {
|
||||
return global.editorController.openLink(url);
|
||||
@@ -192,7 +191,6 @@ const Tiptap = ({
|
||||
.focus("end")
|
||||
.run();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -230,6 +228,7 @@ const Tiptap = ({
|
||||
readonly={settings.readonly}
|
||||
controller={controllerRef}
|
||||
title={controller.title}
|
||||
fontFamily={settings.fontFamily}
|
||||
/>
|
||||
<StatusBar container={containerRef} />
|
||||
</>
|
||||
@@ -237,6 +236,8 @@ const Tiptap = ({
|
||||
|
||||
<ContentDiv
|
||||
padding={settings.doubleSpacedLines ? 0 : 6}
|
||||
fontSize={settings.fontSize}
|
||||
fontFamily={settings.fontFamily}
|
||||
ref={contentRef}
|
||||
/>
|
||||
|
||||
@@ -257,6 +258,8 @@ const Tiptap = ({
|
||||
editor={_editor}
|
||||
location="bottom"
|
||||
tools={[...settings.tools]}
|
||||
defaultFontFamily={settings.fontFamily}
|
||||
defaultFontSize={settings.fontSize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -265,7 +268,10 @@ const Tiptap = ({
|
||||
};
|
||||
|
||||
const ContentDiv = memo(
|
||||
forwardRef<HTMLDivElement, { padding: number }>((props, ref) => {
|
||||
forwardRef<
|
||||
HTMLDivElement,
|
||||
{ padding: number; fontSize: number; fontFamily: string }
|
||||
>((props, ref) => {
|
||||
const theme = useEditorThemeStore((state) => state.colors);
|
||||
return (
|
||||
<div
|
||||
@@ -275,12 +281,18 @@ const ContentDiv = memo(
|
||||
paddingTop: props.padding,
|
||||
color: theme.pri,
|
||||
marginTop: -12,
|
||||
caretColor: theme.accent
|
||||
caretColor: theme.accent,
|
||||
fontSize: props.fontSize,
|
||||
fontFamily: getFontById(props.fontFamily)?.font
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
() => true
|
||||
(prev, next) => {
|
||||
if (prev.fontSize !== next.fontSize || prev.fontFamily !== next.fontFamily)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
const modifyToolbarTheme = (toolbarTheme: Theme) => {
|
||||
|
||||
@@ -54,7 +54,7 @@ const Button = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default function Header({
|
||||
function Header({
|
||||
noHeader,
|
||||
settings,
|
||||
hasUndo,
|
||||
@@ -296,3 +296,17 @@ export default function Header({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Header, (prev, next) => {
|
||||
if (
|
||||
prev.settings.deviceMode !== next.settings.deviceMode ||
|
||||
prev.settings.fullscreen !== next.settings.fullscreen ||
|
||||
prev.settings.premium !== next.settings.premium ||
|
||||
prev.noHeader !== next.noHeader ||
|
||||
prev.hasRedo !== next.hasRedo ||
|
||||
prev.hasUndo !== next.hasUndo
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -31,7 +31,6 @@ function StatusBar({ container }: { container: RefObject<HTMLDivElement> }) {
|
||||
const lastStickyChangeTime = useRef(0);
|
||||
const [words, setWords] = useState("0 words");
|
||||
const currentWords = useRef(words);
|
||||
const interval = useRef(0);
|
||||
const statusBar = useRef({
|
||||
set: setStatus,
|
||||
updateWords: () => {
|
||||
@@ -69,17 +68,6 @@ function StatusBar({ container }: { container: RefObject<HTMLDivElement> }) {
|
||||
currentWords.current = words;
|
||||
}, [words]);
|
||||
|
||||
useEffect(() => {
|
||||
clearInterval(interval.current);
|
||||
interval.current = setInterval(
|
||||
statusBar.current.updateWords,
|
||||
3000
|
||||
) as unknown as number;
|
||||
return () => {
|
||||
clearInterval(interval.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const node = container.current;
|
||||
node?.addEventListener("scroll", onScroll);
|
||||
|
||||
@@ -17,11 +17,11 @@ 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 { useRef, useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { EventTypes } from "../utils";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export default function Tags(): JSX.Element {
|
||||
function Tags(): JSX.Element {
|
||||
const [tags, setTags] = useState<{ title: string; alias: string }[]>([]);
|
||||
const editorTags = useRef({
|
||||
setTags: setTags
|
||||
@@ -122,3 +122,5 @@ export default function Tags(): JSX.Element {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Tags, () => true);
|
||||
|
||||
@@ -17,20 +17,22 @@ 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 from "react";
|
||||
import { RefObject, useEffect, useRef } from "react";
|
||||
import { getFontById } from "@notesnook/editor";
|
||||
import React, { RefObject, useEffect, useRef } from "react";
|
||||
import { EditorController } from "../hooks/useEditorController";
|
||||
import styles from "./styles.module.css";
|
||||
function Title({
|
||||
controller,
|
||||
title,
|
||||
titlePlaceholder,
|
||||
readonly
|
||||
readonly,
|
||||
fontFamily
|
||||
}: {
|
||||
controller: RefObject<EditorController>;
|
||||
title: string;
|
||||
titlePlaceholder: string;
|
||||
readonly: boolean;
|
||||
fontFamily: string;
|
||||
}) {
|
||||
const titleRef = useRef<HTMLInputElement>(null);
|
||||
const emitUpdate = useRef(true);
|
||||
@@ -59,7 +61,7 @@ function Title({
|
||||
paddingRight: 12,
|
||||
paddingLeft: 12,
|
||||
fontWeight: 600,
|
||||
fontFamily: "Open Sans",
|
||||
fontFamily: getFontById(fontFamily)?.font || "Open Sans",
|
||||
backgroundColor: "transparent",
|
||||
color: "var(--nn_heading)",
|
||||
caretColor: "var(--nn_accent)",
|
||||
@@ -76,8 +78,13 @@ function Title({
|
||||
}
|
||||
|
||||
export default React.memo(Title, (prev, next) => {
|
||||
if (prev.title !== next.title) return false;
|
||||
if (prev.titlePlaceholder !== next.titlePlaceholder) return false;
|
||||
if (prev.readonly !== next.readonly) return false;
|
||||
if (
|
||||
prev.title !== next.title ||
|
||||
prev.titlePlaceholder !== next.titlePlaceholder ||
|
||||
prev.readonly !== next.readonly ||
|
||||
prev.fontFamily !== next.fontFamily
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -47,6 +47,7 @@ export type Selection = {
|
||||
type Timers = {
|
||||
selectionChange: NodeJS.Timeout | null;
|
||||
change: NodeJS.Timeout | null;
|
||||
wordCounter: NodeJS.Timeout | null;
|
||||
};
|
||||
|
||||
export type EditorController = {
|
||||
@@ -64,6 +65,7 @@ export type EditorController = {
|
||||
titlePlaceholder: string;
|
||||
openLink: (url: string) => boolean;
|
||||
setTitlePlaceholder: React.Dispatch<React.SetStateAction<string>>;
|
||||
countWords: (ms: number) => void;
|
||||
};
|
||||
|
||||
export function useEditorController(update: () => void): EditorController {
|
||||
@@ -72,7 +74,8 @@ export function useEditorController(update: () => void): EditorController {
|
||||
const htmlContentRef = useRef<string | null>(null);
|
||||
const timers = useRef<Timers>({
|
||||
selectionChange: null,
|
||||
change: null
|
||||
change: null,
|
||||
wordCounter: null
|
||||
});
|
||||
|
||||
const selectionChange = useCallback((_editor: Editor) => {}, []);
|
||||
@@ -82,19 +85,34 @@ export function useEditorController(update: () => void): EditorController {
|
||||
post(EventTypes.title, title);
|
||||
}, []);
|
||||
|
||||
const contentChange = useCallback((editor: Editor) => {
|
||||
const currentSessionId = globalThis.sessionId;
|
||||
post(EventTypes.contentchange);
|
||||
if (!editor) return;
|
||||
if (typeof timers.current.change === "number") {
|
||||
clearTimeout(timers.current?.change);
|
||||
}
|
||||
timers.current.change = setTimeout(() => {
|
||||
htmlContentRef.current = editor.getHTML();
|
||||
post(EventTypes.content, htmlContentRef.current, currentSessionId);
|
||||
}, 300);
|
||||
const countWords = useCallback((ms = 300) => {
|
||||
if (typeof timers.current.wordCounter === "number")
|
||||
clearTimeout(timers.current.wordCounter);
|
||||
timers.current.wordCounter = setTimeout(() => {
|
||||
console.time("wordCounter");
|
||||
statusBar?.current?.updateWords();
|
||||
console.timeEnd("wordCounter");
|
||||
}, ms);
|
||||
}, []);
|
||||
|
||||
const contentChange = useCallback(
|
||||
(editor: Editor) => {
|
||||
const currentSessionId = globalThis.sessionId;
|
||||
post(EventTypes.contentchange);
|
||||
if (!editor) return;
|
||||
if (typeof timers.current.change === "number") {
|
||||
clearTimeout(timers.current?.change);
|
||||
}
|
||||
timers.current.change = setTimeout(() => {
|
||||
htmlContentRef.current = editor.getHTML();
|
||||
post(EventTypes.content, htmlContentRef.current, currentSessionId);
|
||||
}, 300);
|
||||
|
||||
countWords(5000);
|
||||
},
|
||||
[countWords]
|
||||
);
|
||||
|
||||
const scroll = useCallback(
|
||||
(_event: React.UIEvent<HTMLDivElement, UIEvent>) => {},
|
||||
[]
|
||||
@@ -124,13 +142,13 @@ export function useEditorController(update: () => void): EditorController {
|
||||
from,
|
||||
to
|
||||
});
|
||||
statusBar.current?.updateWords();
|
||||
countWords();
|
||||
break;
|
||||
}
|
||||
case "native:html":
|
||||
htmlContentRef.current = value;
|
||||
update();
|
||||
statusBar.current?.updateWords();
|
||||
countWords();
|
||||
break;
|
||||
case "native:theme":
|
||||
useEditorThemeStore.getState().setColors(message.value);
|
||||
@@ -148,7 +166,7 @@ export function useEditorController(update: () => void): EditorController {
|
||||
}
|
||||
post(type); // Notify that message was delivered successfully.
|
||||
},
|
||||
[update]
|
||||
[update, countWords]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -194,6 +212,7 @@ export function useEditorController(update: () => void): EditorController {
|
||||
previewAttachment,
|
||||
content: htmlContentRef,
|
||||
openLink,
|
||||
onUpdate: onUpdate
|
||||
onUpdate: onUpdate,
|
||||
countWords
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ const initialState = {
|
||||
noToolbar: globalThis.noToolbar,
|
||||
noHeader: globalThis.noHeader,
|
||||
readonly: globalThis.readonly,
|
||||
doubleSpacedLines: true
|
||||
doubleSpacedLines: true,
|
||||
fontFamily: "sans-serif",
|
||||
fontSize: "16px"
|
||||
};
|
||||
|
||||
global.settingsController = {
|
||||
|
||||
@@ -37,9 +37,10 @@ export type Settings = {
|
||||
tools: ToolbarGroupDefinition[];
|
||||
noToolbar?: boolean;
|
||||
noHeader?: boolean;
|
||||
keyboardShown?: boolean;
|
||||
doubleSpacedLines?: boolean;
|
||||
corsProxy: string;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
};
|
||||
|
||||
/* eslint-disable no-var */
|
||||
|
||||
@@ -17,8 +17,8 @@ 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 "@tiptap/extension-text-style";
|
||||
import { getFontConfig } from "@notesnook/theme/dist/theme/font";
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { getFontById } from "../../utils/font";
|
||||
|
||||
export type FontFamilyOptions = {
|
||||
types: string[];
|
||||
@@ -39,12 +39,6 @@ declare module "@tiptap/core" {
|
||||
}
|
||||
}
|
||||
|
||||
const FONTS: Record<string, string> = {
|
||||
monospace: getFontConfig().fonts.monospace,
|
||||
"sans-serif": getFontConfig().fonts.body,
|
||||
serif: `Noto Serif, Times New Roman, serif`
|
||||
};
|
||||
|
||||
export const FontFamily = Extension.create<FontFamilyOptions>({
|
||||
name: "fontFamily",
|
||||
|
||||
@@ -70,7 +64,8 @@ export const FontFamily = Extension.create<FontFamilyOptions>({
|
||||
|
||||
const realFontFamily =
|
||||
attributes["data-font-family"] || attributes.fontFamily;
|
||||
const font = FONTS[realFontFamily] || attributes.fontFamily;
|
||||
const font =
|
||||
getFontById(realFontFamily)?.font || attributes.fontFamily;
|
||||
return {
|
||||
"data-font-family": realFontFamily,
|
||||
style: `font-family: ${font}`
|
||||
|
||||
@@ -22,7 +22,6 @@ import "@tiptap/extension-text-style";
|
||||
|
||||
type FontSizeOptions = {
|
||||
types: string[];
|
||||
defaultFontSize: number;
|
||||
};
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
@@ -44,8 +43,7 @@ export const FontSize = Extension.create<FontSizeOptions>({
|
||||
name: "fontSize",
|
||||
|
||||
defaultOptions: {
|
||||
types: ["textStyle"],
|
||||
defaultFontSize: 16
|
||||
types: ["textStyle"]
|
||||
},
|
||||
|
||||
addGlobalAttributes() {
|
||||
@@ -54,7 +52,6 @@ export const FontSize = Extension.create<FontSizeOptions>({
|
||||
types: this.options.types,
|
||||
attributes: {
|
||||
fontSize: {
|
||||
default: `${this.options.defaultFontSize}px`,
|
||||
parseHTML: (element) => element.style.fontSize,
|
||||
renderHTML: (attributes) => {
|
||||
if (!attributes.fontSize) {
|
||||
|
||||
@@ -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 { Flex, Text } from "@theme-ui/components";
|
||||
import { Box, Flex } from "@theme-ui/components";
|
||||
import { ReactNodeViewProps } from "../react";
|
||||
import { Icon } from "../../toolbar/components/icon";
|
||||
import { Icons } from "../../toolbar/icons";
|
||||
@@ -26,10 +26,7 @@ import { Transaction } from "prosemirror-state";
|
||||
import { findChildren, findChildrenInRange } from "@tiptap/core";
|
||||
import { useCallback } from "react";
|
||||
import { TaskItemNode, TaskItemAttributes } from "./task-item";
|
||||
import {
|
||||
useIsKeyboardOpen,
|
||||
useIsMobile
|
||||
} from "../../toolbar/stores/toolbar-store";
|
||||
import { useIsMobile } from "../../toolbar/stores/toolbar-store";
|
||||
import { isiOS } from "../../utils/platform";
|
||||
import { DesktopOnly } from "../../components/responsive";
|
||||
|
||||
@@ -92,10 +89,10 @@ export function TaskItemComponent(
|
||||
".icon:hover path": {
|
||||
fill: "var(--checked) !important"
|
||||
},
|
||||
mt: "1px",
|
||||
mx: 1
|
||||
mx: 1,
|
||||
fontFamily: "inherit"
|
||||
}}
|
||||
size={isMobile ? 24 : 20}
|
||||
size={isMobile ? "2.46ch" : "2.22ch"}
|
||||
/>
|
||||
)}
|
||||
<Icon
|
||||
@@ -109,7 +106,6 @@ export function TaskItemComponent(
|
||||
borderRadius: "default",
|
||||
alignSelf: "start",
|
||||
p: "1px",
|
||||
mt: "2px",
|
||||
mr: 1,
|
||||
cursor: editor.isEditable ? "pointer" : "unset",
|
||||
":hover": {
|
||||
@@ -117,26 +113,26 @@ export function TaskItemComponent(
|
||||
},
|
||||
":hover .icon path": {
|
||||
fill: "var(--checked) !important"
|
||||
}
|
||||
},
|
||||
fontFamily: "inherit"
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
if (useIsKeyboardOpen.current) {
|
||||
if (globalThis["keyboardShown"]) {
|
||||
e.preventDefault();
|
||||
}
|
||||
toggle();
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
if (useIsKeyboardOpen.current || isiOS()) {
|
||||
if (globalThis["keyboardShown"] || isiOS()) {
|
||||
e.preventDefault();
|
||||
toggle();
|
||||
}
|
||||
}}
|
||||
color={checked ? "checked" : "icon"}
|
||||
size={isMobile ? 16 : 14}
|
||||
size={isMobile ? "1.66ch" : "1.46ch"}
|
||||
/>
|
||||
|
||||
<Text
|
||||
as="div"
|
||||
<Box
|
||||
ref={forwardRef}
|
||||
sx={{
|
||||
"> .taskitem-content-wrapper > p": {
|
||||
|
||||
@@ -76,7 +76,7 @@ export function TaskListComponent(
|
||||
sx={{
|
||||
position: "relative",
|
||||
bg: "bgSecondary",
|
||||
py: 1,
|
||||
py: "5px",
|
||||
borderRadius: "default",
|
||||
mb: 2,
|
||||
alignItems: "center",
|
||||
@@ -108,7 +108,8 @@ export function TaskListComponent(
|
||||
px: 2,
|
||||
zIndex: 1,
|
||||
color: "fontTertiary",
|
||||
fontSize: "title"
|
||||
fontSize: "inherit",
|
||||
fontFamily: "inherit"
|
||||
}}
|
||||
placeholder="Untitled"
|
||||
onChange={(e) => {
|
||||
@@ -168,15 +169,15 @@ export function TaskListComponent(
|
||||
mr: 2,
|
||||
color: "fontTertiary",
|
||||
flexShrink: 0,
|
||||
zIndex: 1
|
||||
zIndex: 1,
|
||||
fontFamily: "inherit"
|
||||
}}
|
||||
>
|
||||
{stats.checked}/{stats.total}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text
|
||||
as={"div"}
|
||||
<Box
|
||||
ref={forwardRef}
|
||||
dir={textDirection}
|
||||
sx={{
|
||||
|
||||
@@ -77,6 +77,12 @@ import Toolbar from "./toolbar";
|
||||
import { useToolbarStore } from "./toolbar/stores/toolbar-store";
|
||||
import { DownloadOptions } from "./utils/downloader";
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var keyboardShown: boolean;
|
||||
}
|
||||
|
||||
globalThis["keyboardShown"] = true;
|
||||
const CoreExtensions = Object.entries(TiptapCoreExtensions)
|
||||
// we will implement our own customized clipboard serializer
|
||||
.filter(([name]) => name !== "ClipboardTextSerializer")
|
||||
@@ -90,7 +96,6 @@ type TiptapOptions = EditorOptions &
|
||||
downloadOptions?: DownloadOptions;
|
||||
theme: Theme;
|
||||
isMobile?: boolean;
|
||||
isKeyboardOpen?: boolean;
|
||||
doubleSpacedLines?: boolean;
|
||||
};
|
||||
|
||||
@@ -102,7 +107,6 @@ const useTiptap = (
|
||||
theme,
|
||||
doubleSpacedLines = true,
|
||||
isMobile,
|
||||
isKeyboardOpen,
|
||||
onDownloadAttachment,
|
||||
onOpenAttachmentPicker,
|
||||
onPreviewAttachment,
|
||||
@@ -115,7 +119,6 @@ const useTiptap = (
|
||||
const setIsMobile = useToolbarStore((store) => store.setIsMobile);
|
||||
const setTheme = useToolbarStore((store) => store.setTheme);
|
||||
const closeAllPopups = useToolbarStore((store) => store.closeAllPopups);
|
||||
const setIsKeyboardOpen = useToolbarStore((store) => store.setIsKeyboardOpen);
|
||||
const setDownloadOptions = useToolbarStore(
|
||||
(store) => store.setDownloadOptions
|
||||
);
|
||||
@@ -123,9 +126,8 @@ const useTiptap = (
|
||||
useEffect(() => {
|
||||
setIsMobile(isMobile || false);
|
||||
setTheme(theme);
|
||||
setIsKeyboardOpen(isKeyboardOpen || false);
|
||||
setDownloadOptions(downloadOptions);
|
||||
}, [isMobile, theme, isKeyboardOpen, downloadOptions]);
|
||||
}, [isMobile, theme, downloadOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
closeAllPopups();
|
||||
@@ -271,6 +273,7 @@ export * from "./extensions/react";
|
||||
export * from "./toolbar";
|
||||
export * from "./types";
|
||||
export * from "./utils/word-counter";
|
||||
export * from "./utils/font";
|
||||
export {
|
||||
useTiptap,
|
||||
Toolbar,
|
||||
|
||||
@@ -64,6 +64,7 @@ export function Dropdown(props: DropdownProps) {
|
||||
m: 0,
|
||||
bg: isPopupOpen ? "hover" : "transparent",
|
||||
mr: 1,
|
||||
flexShrink: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
":hover": { bg: "hover" },
|
||||
@@ -75,7 +76,9 @@ export function Dropdown(props: DropdownProps) {
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
{typeof selectedItem === "string" ? (
|
||||
<Text sx={{ fontSize: "subBody", mr: 1, color: "text" }}>
|
||||
<Text
|
||||
sx={{ fontSize: "subBody", mr: 1, color: "text", flexShrink: 0 }}
|
||||
>
|
||||
{selectedItem}
|
||||
</Text>
|
||||
) : (
|
||||
|
||||
@@ -34,7 +34,7 @@ function isSchemeColor(
|
||||
type IconProps = {
|
||||
title?: string;
|
||||
path: string;
|
||||
size?: keyof Theme["iconSizes"] | number;
|
||||
size?: keyof Theme["iconSizes"] | number | string;
|
||||
color?: keyof SchemeColors | string;
|
||||
stroke?: string;
|
||||
rotate?: boolean;
|
||||
@@ -60,8 +60,10 @@ function MDIIconWrapper({
|
||||
title={title}
|
||||
path={path}
|
||||
size={
|
||||
typeof size === "string"
|
||||
? `${theme?.iconSizes[size] || 24}px`
|
||||
theme && typeof size === "string" && size in theme.iconSizes
|
||||
? `${(theme?.iconSizes as any)[size] || 24}px`
|
||||
: typeof size === "string"
|
||||
? size
|
||||
: `${size}px`
|
||||
}
|
||||
style={{
|
||||
|
||||
@@ -39,7 +39,8 @@ function _SplitButton(props: PropsWithChildren<SplitButtonProps>) {
|
||||
<Flex
|
||||
ref={ref}
|
||||
sx={{
|
||||
borderRadius: "default"
|
||||
borderRadius: "default",
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<ToolButton
|
||||
|
||||
@@ -29,8 +29,6 @@ interface ToolbarState {
|
||||
setTheme: (theme?: Theme) => void;
|
||||
downloadOptions?: DownloadOptions;
|
||||
setDownloadOptions: (options?: DownloadOptions) => void;
|
||||
isKeyboardOpen: boolean;
|
||||
setIsKeyboardOpen: (isKeyboardOpen: boolean) => void;
|
||||
isMobile: boolean;
|
||||
openedPopups: Record<string, PopupRef | false | undefined>;
|
||||
setIsMobile: (isMobile: boolean) => void;
|
||||
@@ -41,22 +39,21 @@ interface ToolbarState {
|
||||
closePopup: (popupId: string) => void;
|
||||
closePopupGroup: (groupId: string, excluded: string[]) => void;
|
||||
closeAllPopups: () => void;
|
||||
fontFamily: string;
|
||||
setFontFamily: (fontFamily: string) => void;
|
||||
fontSize: number;
|
||||
setFontSize: (fontSize: number) => void;
|
||||
}
|
||||
|
||||
export const useToolbarStore = create<ToolbarState>((set, get) => ({
|
||||
theme: undefined,
|
||||
downloadOptions: undefined,
|
||||
isMobile: false,
|
||||
isKeyboardOpen: true,
|
||||
openedPopups: {},
|
||||
setDownloadOptions: (options) =>
|
||||
set((state) => {
|
||||
state.downloadOptions = options;
|
||||
}),
|
||||
setIsKeyboardOpen: (isKeyboardOpen) =>
|
||||
set((state) => {
|
||||
state.isKeyboardOpen = isKeyboardOpen;
|
||||
}),
|
||||
setIsMobile: (isMobile) =>
|
||||
set((state) => {
|
||||
state.isMobile = isMobile;
|
||||
@@ -99,6 +96,16 @@ export const useToolbarStore = create<ToolbarState>((set, get) => ({
|
||||
for (const key in state.openedPopups) {
|
||||
state.openedPopups[key] = undefined;
|
||||
}
|
||||
}),
|
||||
fontFamily: "sans-serif",
|
||||
setFontFamily: (fontFamily) =>
|
||||
set((state) => {
|
||||
state.fontFamily = fontFamily;
|
||||
}),
|
||||
fontSize: 16,
|
||||
setFontSize: (fontSize) =>
|
||||
set((state) => {
|
||||
state.fontSize = fontSize;
|
||||
})
|
||||
}));
|
||||
|
||||
@@ -119,13 +126,3 @@ export const useTheme = Object.defineProperty(
|
||||
get: () => useToolbarStore.getState().theme
|
||||
}
|
||||
) as (() => Theme | undefined) & { current: Theme | undefined };
|
||||
|
||||
export const useIsKeyboardOpen = Object.defineProperty(
|
||||
() => {
|
||||
return useToolbarStore((store) => store.isKeyboardOpen);
|
||||
},
|
||||
"current",
|
||||
{
|
||||
get: () => useToolbarStore.getState().isKeyboardOpen
|
||||
}
|
||||
) as (() => boolean) & { current: boolean };
|
||||
|
||||
@@ -41,6 +41,8 @@ type ToolbarProps = FlexProps & {
|
||||
editor: Editor | null;
|
||||
location: ToolbarLocation;
|
||||
tools?: ToolbarDefinition;
|
||||
defaultFontFamily: string;
|
||||
defaultFontSize: number;
|
||||
};
|
||||
|
||||
export function Toolbar(props: ToolbarProps) {
|
||||
@@ -49,10 +51,11 @@ export function Toolbar(props: ToolbarProps) {
|
||||
theme,
|
||||
location,
|
||||
tools = getDefaultPresets().default,
|
||||
defaultFontFamily,
|
||||
defaultFontSize,
|
||||
sx,
|
||||
...flexProps
|
||||
} = props;
|
||||
|
||||
const toolbarTools = useMemo(
|
||||
() => [...STATIC_TOOLBAR_GROUPS, ...tools],
|
||||
[tools]
|
||||
@@ -63,11 +66,23 @@ export function Toolbar(props: ToolbarProps) {
|
||||
const setToolbarLocation = useToolbarStore(
|
||||
(store) => store.setToolbarLocation
|
||||
);
|
||||
const setDefaultFontFamily = useToolbarStore((store) => store.setFontFamily);
|
||||
const setDefaultFontSize = useToolbarStore((store) => store.setFontSize);
|
||||
|
||||
useEffect(() => {
|
||||
setToolbarLocation(location);
|
||||
}, [location, setToolbarLocation]);
|
||||
|
||||
useEffect(() => {
|
||||
setDefaultFontFamily(defaultFontFamily);
|
||||
setDefaultFontSize(defaultFontSize);
|
||||
}, [
|
||||
defaultFontFamily,
|
||||
defaultFontSize,
|
||||
setDefaultFontFamily,
|
||||
setDefaultFontSize
|
||||
]);
|
||||
|
||||
if (!editor) return null;
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
|
||||
@@ -24,12 +24,16 @@ import { MenuItem } from "../../components/menu/types";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Counter } from "../components/counter";
|
||||
import { useRefValue } from "../../hooks/use-ref-value";
|
||||
import { useToolbarStore } from "../stores/toolbar-store";
|
||||
import { getFontById, getFontIds, getFonts } from "../../utils/font";
|
||||
|
||||
export function FontSize(props: ToolProps) {
|
||||
const { editor } = props;
|
||||
const { fontSize: _fontSize } = editor.getAttributes("textStyle");
|
||||
const fontSize = _fontSize || "16px";
|
||||
const fontSizeAsNumber = useRefValue(parseInt(fontSize.replace("px", "")));
|
||||
const defaultFontSize = useToolbarStore((store) => store.fontSize);
|
||||
const { fontSize } = editor.getAttributes("textStyle");
|
||||
const fontSizeAsNumber = useRefValue(
|
||||
fontSize ? parseInt(fontSize.replace("px", "")) : defaultFontSize
|
||||
);
|
||||
|
||||
const decreaseFontSize = useCallback(() => {
|
||||
return Math.max(8, fontSizeAsNumber.current - 1);
|
||||
@@ -56,27 +60,26 @@ export function FontSize(props: ToolProps) {
|
||||
.setFontSize(`${increaseFontSize()}px`)
|
||||
.run();
|
||||
}}
|
||||
onReset={() => editor.current?.chain().focus().setFontSize("16px").run()}
|
||||
value={fontSize}
|
||||
onReset={() =>
|
||||
editor.current
|
||||
?.chain()
|
||||
.focus()
|
||||
.setFontSize(`${defaultFontSize}px`)
|
||||
.run()
|
||||
}
|
||||
value={fontSize || `${defaultFontSize}px`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const fontFamilies = {
|
||||
"Sans-serif": "Open Sans",
|
||||
Serif: "serif",
|
||||
Monospace: "monospace"
|
||||
};
|
||||
export function FontFamily(props: ToolProps) {
|
||||
const { editor } = props;
|
||||
|
||||
const defaultFontFamily = useToolbarStore((store) => store.fontFamily);
|
||||
const currentFontFamily =
|
||||
Object.entries(fontFamilies)
|
||||
.find(([_key, value]) =>
|
||||
editor.isActive("textStyle", { fontFamily: value })
|
||||
)
|
||||
?.map((a) => a)
|
||||
?.at(0) || "Sans-serif";
|
||||
getFontIds().find((id) =>
|
||||
editor.isActive("textStyle", { fontFamily: id })
|
||||
) || defaultFontFamily;
|
||||
|
||||
const items = useMemo(
|
||||
() => toMenuItems(editor, currentFontFamily),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -87,7 +90,7 @@ export function FontFamily(props: ToolProps) {
|
||||
<Dropdown
|
||||
id="fontFamily"
|
||||
group="font"
|
||||
selectedItem={currentFontFamily}
|
||||
selectedItem={getFontById(currentFontFamily)?.title || defaultFontFamily}
|
||||
items={items}
|
||||
menuWidth={130}
|
||||
/>
|
||||
@@ -96,16 +99,16 @@ export function FontFamily(props: ToolProps) {
|
||||
|
||||
function toMenuItems(editor: Editor, currentFontFamily: string): MenuItem[] {
|
||||
const menuItems: MenuItem[] = [];
|
||||
for (const key in fontFamilies) {
|
||||
const value = fontFamilies[key as keyof typeof fontFamilies];
|
||||
for (const font of getFonts()) {
|
||||
menuItems.push({
|
||||
key,
|
||||
key: font.id,
|
||||
type: "button",
|
||||
title: key,
|
||||
isChecked: key === currentFontFamily,
|
||||
onClick: () => editor.current?.chain().focus().setFontFamily(value).run(),
|
||||
title: font.title,
|
||||
isChecked: font.id === currentFontFamily,
|
||||
onClick: () =>
|
||||
editor.current?.chain().focus().setFontFamily(font.id).run(),
|
||||
styles: {
|
||||
fontFamily: value
|
||||
fontFamily: font.font
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ export function EditLink(props: ToolProps) {
|
||||
|
||||
const onDone = useCallback(
|
||||
(link: LinkDefinition) => {
|
||||
if (!selectedNode.current) return;
|
||||
|
||||
const { href, text, isImage } = link;
|
||||
const { from, node, to } = selectedNode.current;
|
||||
if (!href || !editor.current || !node) return;
|
||||
@@ -117,6 +119,7 @@ export function EditLink(props: ToolProps) {
|
||||
let commandChain = editor.current.chain();
|
||||
|
||||
if (!isImage) {
|
||||
console.log(from, to);
|
||||
commandChain = commandChain.command(({ tr }) => {
|
||||
tr.removeMark(from, to, mark.type);
|
||||
tr.insertText(
|
||||
@@ -161,6 +164,8 @@ export function EditLink(props: ToolProps) {
|
||||
isEditing
|
||||
onDone={onDone}
|
||||
onClick={() => {
|
||||
if (!selectedNode.current) return;
|
||||
|
||||
const { node } = selectedNode.current;
|
||||
if (!node) return;
|
||||
|
||||
@@ -202,7 +207,7 @@ export function OpenLink(props: ToolProps) {
|
||||
const selectedNode = useRefValue(
|
||||
_selectedNode || selectionToOffset(editor.state)
|
||||
);
|
||||
const { node } = selectedNode.current;
|
||||
const { node } = selectedNode.current || {};
|
||||
const link = node ? findMark(node, "link") : null;
|
||||
if (!link) return null;
|
||||
const href = link?.attrs.href;
|
||||
|
||||
@@ -46,9 +46,10 @@ export function WebClipFullScreen(props: ToolProps) {
|
||||
{...props}
|
||||
toggled={false}
|
||||
onClick={() => {
|
||||
const dom = editor.current?.view.nodeDOM(
|
||||
selectionToOffset(editor.state).from
|
||||
);
|
||||
const offset = selectionToOffset(editor.state);
|
||||
if (!offset) return;
|
||||
|
||||
const dom = editor.current?.view.nodeDOM(offset.from);
|
||||
if (!dom || !(dom instanceof HTMLElement)) return;
|
||||
|
||||
const iframe = dom.querySelector("iframe");
|
||||
@@ -70,9 +71,10 @@ export function WebClipOpenExternal(props: ToolProps) {
|
||||
{...props}
|
||||
toggled={false}
|
||||
onClick={async () => {
|
||||
const dom = editor.current?.view.nodeDOM(
|
||||
selectionToOffset(editor.state).from
|
||||
);
|
||||
const offset = selectionToOffset(editor.state);
|
||||
if (!offset) return;
|
||||
|
||||
const dom = editor.current?.view.nodeDOM(offset.from);
|
||||
if (!dom || !(dom instanceof HTMLElement)) return;
|
||||
|
||||
const iframe = dom.querySelector("iframe");
|
||||
|
||||
@@ -17,15 +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/>.
|
||||
*/
|
||||
|
||||
function withAnimation(_duration = 300) {
|
||||
return;
|
||||
import { getFontConfig } from "@notesnook/theme/dist/theme/font";
|
||||
|
||||
const FONTS = [
|
||||
{
|
||||
title: "Monospace",
|
||||
id: "monospace",
|
||||
font: getFontConfig().fonts.monospace
|
||||
},
|
||||
{
|
||||
title: "Sans-serif",
|
||||
id: "sans-serif",
|
||||
font: getFontConfig().fonts.body
|
||||
},
|
||||
{
|
||||
title: "Serif",
|
||||
id: "serif",
|
||||
font: `Noto Serif, Times New Roman, serif`
|
||||
}
|
||||
];
|
||||
|
||||
export function getFonts() {
|
||||
return FONTS;
|
||||
}
|
||||
|
||||
function withSpringAnimation(_duration = 300) {
|
||||
return;
|
||||
export function getFontById(id: string) {
|
||||
return FONTS.find((a) => a.id === id);
|
||||
}
|
||||
|
||||
export default {
|
||||
withAnimation,
|
||||
withSpringAnimation
|
||||
};
|
||||
export function getFontIds() {
|
||||
return FONTS.map((a) => a.id);
|
||||
}
|
||||
@@ -120,12 +120,17 @@ export function findMark(
|
||||
return mark;
|
||||
}
|
||||
|
||||
export function selectionToOffset(state: EditorState): NodeWithOffset {
|
||||
const { $from, from } = state.selection;
|
||||
export function selectionToOffset(
|
||||
state: EditorState
|
||||
): NodeWithOffset | undefined {
|
||||
const { from, $from } = state.selection;
|
||||
const node = state.doc.nodeAt(from);
|
||||
if (!node) return;
|
||||
|
||||
return {
|
||||
node: state.doc.nodeAt(from) || undefined,
|
||||
from,
|
||||
to: from + $from.node().nodeSize
|
||||
node,
|
||||
from: from - $from.textOffset,
|
||||
to: from - $from.textOffset + node.nodeSize
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
|
||||
|
||||
.ProseMirror span * {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.ProseMirror {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.ProseMirror p.is-editor-empty:first-child::before {
|
||||
color: var(--placeholder);
|
||||
content: attr(data-placeholder);
|
||||
@@ -87,6 +93,7 @@
|
||||
|
||||
.ProseMirror p {
|
||||
margin-bottom: 0px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.ProseMirror > p[data-spacing="double"] {
|
||||
@@ -502,7 +509,7 @@ p > *::selection {
|
||||
|
||||
.outline-list li > p::before {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
top: 15%;
|
||||
cursor: pointer;
|
||||
content: "";
|
||||
background-size: 18px;
|
||||
|
||||
Reference in New Issue
Block a user