mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-30 18:48:27 +02:00
Compare commits
54 Commits
fix-clippe
...
editor/inl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6ff0a2430 | ||
|
|
eab0bdee8b | ||
|
|
0d94797bc1 | ||
|
|
372c49d81e | ||
|
|
97d21c818e | ||
|
|
3beb60d8b6 | ||
|
|
6343212d7e | ||
|
|
f728ad8826 | ||
|
|
b96af6c4cf | ||
|
|
6069a41fa4 | ||
|
|
7190207e5c | ||
|
|
015453a40b | ||
|
|
dad92a186c | ||
|
|
7e5e19e72c | ||
|
|
8e1484a029 | ||
|
|
4a3b3a14ec | ||
|
|
758157cf78 | ||
|
|
a1a171d648 | ||
|
|
92ba4d7624 | ||
|
|
d44ee5ca05 | ||
|
|
d7dde74675 | ||
|
|
5fde1c0f07 | ||
|
|
ffddbe2d7a | ||
|
|
eb3998d72d | ||
|
|
fca61afd7c | ||
|
|
33559250d8 | ||
|
|
38daca0099 | ||
|
|
36c8b8cc25 | ||
|
|
dd4ca817c7 | ||
|
|
594ceb1f52 | ||
|
|
fbc74ec359 | ||
|
|
0dfc1dfa52 | ||
|
|
b9c14ef9aa | ||
|
|
3fec2b397f | ||
|
|
ec841caef7 | ||
|
|
c163fc3be4 | ||
|
|
1e04315596 | ||
|
|
9aef28b03d | ||
|
|
88628c650f | ||
|
|
9523fd52d8 | ||
|
|
8cc6df92f4 | ||
|
|
042656856a | ||
|
|
c5cd214d97 | ||
|
|
b30c35e59d | ||
|
|
ce4fa13758 | ||
|
|
1f47df0f45 | ||
|
|
91109278b6 | ||
|
|
78e4672871 | ||
|
|
38600bf2a5 | ||
|
|
4720456d63 | ||
|
|
5a2221c5d3 | ||
|
|
14c1567c07 | ||
|
|
fb778f56ed | ||
|
|
50176d2b3a |
4
apps/desktop/package-lock.json
generated
4
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.2",
|
||||
"version": "3.3.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.2",
|
||||
"version": "3.3.5",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.2",
|
||||
"version": "3.3.5",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
|
||||
@@ -39,9 +39,7 @@ import {
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import Exporter from "../../../services/exporter";
|
||||
import PremiumService from "../../../services/premium";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
@@ -83,7 +81,6 @@ const ExportNotesSheet = ({
|
||||
type: "pdf" | "txt" | "md" | "html" | "md-frontmatter"
|
||||
) => {
|
||||
if (exporting) return;
|
||||
if (!PremiumService.get() && type !== "txt") return;
|
||||
setExporting(true);
|
||||
update?.({ disableClosing: true } as PresentSheetOptions);
|
||||
setComplete(false);
|
||||
@@ -145,8 +142,7 @@ const ExportNotesSheet = ({
|
||||
await exportNoteAs("txt");
|
||||
},
|
||||
icon: "card-text",
|
||||
id: notesnook.ids.dialogs.export.text,
|
||||
pro: true
|
||||
id: notesnook.ids.dialogs.export.text
|
||||
},
|
||||
{
|
||||
title: "HTML",
|
||||
@@ -194,8 +190,7 @@ const ExportNotesSheet = ({
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
opacity: item.pro ? 1 : 0.5
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { TextInput } from "react-native-gesture-handler";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
@@ -36,6 +36,7 @@ export const SearchBar = ({
|
||||
onChangeText: (value: string) => void;
|
||||
loading?: boolean;
|
||||
}) => {
|
||||
const [clearButton, setClearButton] = useState(false);
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === "Search"
|
||||
@@ -45,6 +46,7 @@ export const SearchBar = ({
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const _onChangeText = (value: string) => {
|
||||
onChangeText(value);
|
||||
setClearButton(!!value);
|
||||
};
|
||||
|
||||
return selectionMode && isFocused ? null : (
|
||||
@@ -100,6 +102,23 @@ export const SearchBar = ({
|
||||
autoCorrect={false}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
|
||||
{clearButton ? (
|
||||
<IconButton
|
||||
name="close"
|
||||
size={AppFontSize.xxl}
|
||||
top={10}
|
||||
testID="clear-search"
|
||||
bottom={10}
|
||||
onPress={() => {
|
||||
inputRef.current?.clear();
|
||||
onChangeText("");
|
||||
setClearButton(false);
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
type="plain"
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -322,6 +322,7 @@ export const RestoreBackup = () => {
|
||||
});
|
||||
|
||||
setFiles(files);
|
||||
setLoading(false);
|
||||
BACKUP_FILES_CACHE.splice(0, BACKUP_FILES_CACHE.length, ...files);
|
||||
} catch (e) {
|
||||
e;
|
||||
@@ -519,7 +520,11 @@ const BackupItem = ({
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Paragraph size={AppFontSize.sm}>{itemName}</Paragraph>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
|
||||
@@ -82,15 +82,22 @@ export const settingsGroups: SettingSection[] = [
|
||||
{
|
||||
id: "subscription-status",
|
||||
useHook: () => useUserStore((state) => state.user),
|
||||
hidden: (current) =>
|
||||
!current ||
|
||||
(current as User).subscription?.plan === SubscriptionPlan.FREE,
|
||||
hidden: (current) => {
|
||||
const user = current as User;
|
||||
return (
|
||||
!user ||
|
||||
!user.subscription ||
|
||||
user.subscription.provider === undefined ||
|
||||
!strings.subscriptionProviderInfo[user?.subscription?.provider] ||
|
||||
user.subscription?.plan === SubscriptionPlan.FREE
|
||||
);
|
||||
},
|
||||
name: (current) => {
|
||||
const user = (current as User) || useUserStore.getState().user;
|
||||
return (
|
||||
strings.subscriptionProviderInfo[
|
||||
user?.subscription?.provider
|
||||
].title() || "Unknown provider"
|
||||
]?.title() || `Unknown provider id: ${user?.subscription?.provider}`
|
||||
);
|
||||
},
|
||||
icon: "credit-card",
|
||||
@@ -100,6 +107,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
const subscriptionProviderInfo =
|
||||
strings.subscriptionProviderInfo[user?.subscription?.provider];
|
||||
|
||||
if (!subscriptionProviderInfo) return;
|
||||
|
||||
const isCurrentPlatform =
|
||||
(user.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
Platform.OS === "ios") ||
|
||||
@@ -1427,7 +1436,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
id: "docs-link",
|
||||
name: strings.documentation(),
|
||||
modifer: async () => {
|
||||
Linking.openURL("https://docs.notesnook.com");
|
||||
Linking.openURL("https://help.notesnook.com/");
|
||||
},
|
||||
description: strings.documentationDesc(),
|
||||
icon: "file-document"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { execSync } from "child_process";
|
||||
|
||||
//@ts-ignore
|
||||
import { pathExists, ensureDir } from "fs-extra";
|
||||
|
||||
import { resolveConfig } from "detox/internals";
|
||||
|
||||
@@ -28,6 +28,9 @@ describe("Search", () => {
|
||||
.typeTextById("search-input", "Test")
|
||||
.wait(1000)
|
||||
.isVisibleByText("1")
|
||||
.waitAndTapById("clear-search")
|
||||
.wait(2000)
|
||||
.isNotVisibleByText("1")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { expect as jestExpect } from "@jest/globals";
|
||||
import { device as _device, expect } from "detox";
|
||||
import { readFileSync } from "fs";
|
||||
//@ts-ignore
|
||||
import { toMatchImageSnapshot } from "jest-image-snapshot";
|
||||
import type { RouteName } from "../../app/stores/use-navigation-store";
|
||||
import { notesnook } from "../test.ids";
|
||||
|
||||
@@ -124,7 +124,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3075
|
||||
versionCode 3078
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
- Minor bug fixes and improvements
|
||||
- Bug fixes and minor improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -654,6 +654,7 @@
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook/Pods-Notesnook-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
@@ -665,6 +666,7 @@
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
@@ -821,6 +823,7 @@
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-NotesnookTests/Pods-Notesnook-NotesnookTests-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
@@ -832,6 +835,7 @@
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
@@ -1091,7 +1095,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1165,7 +1169,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1196,7 +1200,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1270,7 +1274,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1429,7 +1433,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1441,7 +1445,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1472,7 +1476,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1485,7 +1489,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1515,7 +1519,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1596,7 +1600,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1627,7 +1631,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2154;
|
||||
CURRENT_PROJECT_VERSION = 2157;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1709,7 +1713,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.6;
|
||||
MARKETING_VERSION = 3.3.9;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1786,7 +1790,10 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
@@ -1851,7 +1858,10 @@
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>3B52.1</string>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
@@ -1273,11 +1273,11 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-keep-awake (1.3.1):
|
||||
- React-Core
|
||||
- react-native-mmkv-storage (0.11.2):
|
||||
- react-native-mmkv-storage (12.0.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- MMKV (~> 1.3.9)
|
||||
- MMKV (~> 1.3.14)
|
||||
- RCT-Folly (= 2024.11.18.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
@@ -1372,7 +1372,7 @@ PODS:
|
||||
- react-native-screenguard (1.0.0):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.11.1)
|
||||
- react-native-share-extension (2.9.0):
|
||||
- react-native-share-extension (2.9.5):
|
||||
- React
|
||||
- react-native-sodium (1.6.5):
|
||||
- React
|
||||
@@ -1770,15 +1770,53 @@ PODS:
|
||||
- Yoga
|
||||
- RNIap (12.16.2):
|
||||
- React-Core
|
||||
- RNImageCropPicker (0.40.2):
|
||||
- RNImageCropPicker (0.51.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.11.18.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-RCTImage
|
||||
- RNImageCropPicker/QBImagePickerController (= 0.40.2)
|
||||
- TOCropViewController
|
||||
- RNImageCropPicker/QBImagePickerController (0.40.2):
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNImageCropPicker/QBImagePickerController (= 0.51.1)
|
||||
- TOCropViewController (~> 2.8.0)
|
||||
- Yoga
|
||||
- RNImageCropPicker/QBImagePickerController (0.51.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.11.18.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-RCTImage
|
||||
- TOCropViewController
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- TOCropViewController (~> 2.8.0)
|
||||
- Yoga
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
- RNNotifee (7.4.9):
|
||||
@@ -1975,7 +2013,7 @@ PODS:
|
||||
- SwiftyRSA (1.7.0):
|
||||
- SwiftyRSA/ObjC (= 1.7.0)
|
||||
- SwiftyRSA/ObjC (1.7.0)
|
||||
- TOCropViewController (2.7.4)
|
||||
- TOCropViewController (2.8.0)
|
||||
- toolbar-android (0.2.1):
|
||||
- React
|
||||
- Yoga (0.0.0)
|
||||
@@ -2425,7 +2463,7 @@ SPEC CHECKSUMS:
|
||||
react-native-image-resizer: 24c5d06fae2176dc0caed4b6396e02befb44064a
|
||||
react-native-in-app-review: b3d1eed3d1596ebf6539804778272c4c65e4a400
|
||||
react-native-keep-awake: 03b74eebe4f2bb5e8478fc8f420651a92463b6f8
|
||||
react-native-mmkv-storage: 51c33c6f6678d67061059cce8189a57c3e47192e
|
||||
react-native-mmkv-storage: 935d661dc3913a41be70d041aa3ff860d6aa4873
|
||||
react-native-netinfo: 66c9ac0d0ae92a57a9ed99ab8a75429740700c73
|
||||
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
|
||||
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
|
||||
@@ -2434,7 +2472,7 @@ SPEC CHECKSUMS:
|
||||
react-native-quick-sqlite: 1bfc7f1e9acbe9a5aa5c4cc81712e9bde3ab7672
|
||||
react-native-safe-area-context: 9d72abf6d8473da73033b597090a80b709c0b2f1
|
||||
react-native-screenguard: 82437eeb0086a90b5e5d7e54130bb04fb406373e
|
||||
react-native-share-extension: bcb7e466390a9e50c742f4b1019d6f181aedd7ad
|
||||
react-native-share-extension: fdc6aaab51591a2d445df239c446aaa3a99658ec
|
||||
react-native-sodium: 285eec063e4232cb67347ef6a434b85e588d38cb
|
||||
react-native-theme-switch-animation: d90fe2de0d9e87a63cd6235d98cba6e7054e9a10
|
||||
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
|
||||
@@ -2479,7 +2517,7 @@ SPEC CHECKSUMS:
|
||||
RNFlashList: ff5a0b3113c4cda0eaf4b94df8572ccad3c40fd5
|
||||
RNGestureHandler: 92e89a04cd0d1c77f383a55d14c15e7f423f4c00
|
||||
RNIap: f94647b3a3dbd5fa08ad7ed847eb588aa5fe95fe
|
||||
RNImageCropPicker: 30d770b383d84e1067d82ea7b5ed4fff851bbbb2
|
||||
RNImageCropPicker: aa81bda9f887e20542b569938cde5ce50a4ec404
|
||||
RNKeychain: ffd0513e676445c637410b47249460cbf56bc9cb
|
||||
RNNotifee: dabf3cdd7bfd9340bb84358cc78f635af4bc80e2
|
||||
RNPrivacySnapshot: ccad3a548338c2f526bb7b1789af3fb0618b7d1d
|
||||
@@ -2495,7 +2533,7 @@ SPEC CHECKSUMS:
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6
|
||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||
TOCropViewController: 797deaf39c90e6e9ddd848d88817f6b9a8a09888
|
||||
toolbar-android: c426ed5bd3dcccfed20fd79533efc0d1ae0ef018
|
||||
Yoga: 31a098f74c16780569aebd614a0f37a907de0189
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@react-navigation/native": "^6.0.10",
|
||||
"@react-navigation/native-stack": "6.6.2",
|
||||
"@sayem314/react-native-keep-awake": "^1.3.1",
|
||||
"react-native-image-crop-picker": "^0.51.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.77.2",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
@@ -48,7 +49,7 @@
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"@shopify/flash-list": "^1.8.0",
|
||||
"react-native-mmkv-storage": "^0.11.2",
|
||||
"react-native-mmkv-storage": "^12.0.0",
|
||||
"react-native-quick-sqlite": "^8.2.7",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-webview": "^13.13.5",
|
||||
@@ -61,7 +62,6 @@
|
||||
"react-native-zip-archive": "6.0.9",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"react-native-image-crop-picker": "^0.40.2",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-screenguard": "^1.0.0",
|
||||
"@formatjs/intl-locale": "4.0.0",
|
||||
|
||||
344
apps/mobile/package-lock.json
generated
344
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.6",
|
||||
"version": "3.3.9",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||
index 5de0845..1b158d8 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||
+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java
|
||||
@@ -692,6 +692,7 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi
|
||||
image.putString("mime", options.outMimeType);
|
||||
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/ImageCropPicker.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/ImageCropPicker.java
|
||||
index b9c494e..34ef800 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/ImageCropPicker.java
|
||||
+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/ImageCropPicker.java
|
||||
@@ -679,6 +679,7 @@ class ImageCropPicker implements ActivityEventListener {
|
||||
image.putInt("size", (int) new File(compressedImagePath).length());
|
||||
image.putString("modificationDate", String.valueOf(modificationDate));
|
||||
image.putString("filename", new File(path).getName());
|
||||
+ image.putString("sourceURL", path);
|
||||
|
||||
if (includeBase64) {
|
||||
image.putString("data", getBase64StringFromFile(compressedImagePath));
|
||||
diff --git a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
index 9f20973..5e14da8 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
+++ b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
@@ -595,8 +595,10 @@ - (void)qb_imagePickerController:
|
||||
NSString *mimeType = [self determineMimeTypeFromImageData:imageData];
|
||||
diff --git a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.mm b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.mm
|
||||
index 354ad09..622f779 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.mm
|
||||
+++ b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.mm
|
||||
@@ -603,7 +603,10 @@ - (void)qb_imagePickerController:(QBImagePickerController *)imagePickerControlle
|
||||
Boolean isKnownMimeType = [mimeType length] > 0;
|
||||
|
||||
+ Boolean isHeicOrHeif = [mimeType isEqualToString:@"image/heic"] || [mimeType isEqualToString:@"image/heif"];
|
||||
+
|
||||
ImageResult *imageResult = [[ImageResult alloc] init];
|
||||
- if (isLossless && useOriginalWidth && useOriginalHeight && isKnownMimeType && !forceJpg) {
|
||||
+
|
||||
+ Boolean isHeicOrHeif = [mimeType isEqualToString:@"image/heic"] || [mimeType isEqualToString:@"image/heif"];
|
||||
+
|
||||
+ if (isLossless && useOriginalWidth && useOriginalHeight && isKnownMimeType && !forceJpg && !isHeicOrHeif) {
|
||||
// Use original, unmodified image
|
||||
imageResult.data = imageData;
|
||||
169
apps/mobile/patches/react-native-keychain+4.0.5.patch
Normal file
169
apps/mobile/patches/react-native-keychain+4.0.5.patch
Normal file
@@ -0,0 +1,169 @@
|
||||
diff --git a/node_modules/react-native-keychain/android/build.gradle b/node_modules/react-native-keychain/android/build.gradle
|
||||
index bd2fe04..53ce4b1 100755
|
||||
--- a/node_modules/react-native-keychain/android/build.gradle
|
||||
+++ b/node_modules/react-native-keychain/android/build.gradle
|
||||
@@ -51,5 +51,4 @@ dependencies {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'com.facebook.react:react-native:+' // From node_modules
|
||||
implementation 'androidx.annotation:annotation:1.1.0'
|
||||
- implementation 'com.facebook.conceal:conceal:1.1.3@aar'
|
||||
}
|
||||
diff --git a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java
|
||||
index 61d061e..be49f8a 100644
|
||||
--- a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java
|
||||
+++ b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/KeychainModule.java
|
||||
@@ -15,7 +15,6 @@ import com.oblador.keychain.PrefsStorage.ResultSet;
|
||||
import com.oblador.keychain.cipherStorage.CipherStorage;
|
||||
import com.oblador.keychain.cipherStorage.CipherStorage.DecryptionResult;
|
||||
import com.oblador.keychain.cipherStorage.CipherStorage.EncryptionResult;
|
||||
-import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal;
|
||||
import com.oblador.keychain.cipherStorage.CipherStorageKeystoreAESCBC;
|
||||
import com.oblador.keychain.exceptions.CryptoFailedException;
|
||||
import com.oblador.keychain.exceptions.EmptyParameterException;
|
||||
@@ -48,7 +47,6 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||
super(reactContext);
|
||||
prefsStorage = new PrefsStorage(reactContext);
|
||||
|
||||
- addCipherStorageToMap(new CipherStorageFacebookConceal(reactContext));
|
||||
addCipherStorageToMap(new CipherStorageKeystoreAESCBC());
|
||||
}
|
||||
|
||||
diff --git a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/PrefsStorage.java b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/PrefsStorage.java
|
||||
index 99448b1..792c24c 100644
|
||||
--- a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/PrefsStorage.java
|
||||
+++ b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/PrefsStorage.java
|
||||
@@ -7,7 +7,6 @@ import android.util.Base64;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.oblador.keychain.cipherStorage.CipherStorage.EncryptionResult;
|
||||
-import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal;
|
||||
|
||||
public class PrefsStorage {
|
||||
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
||||
@@ -35,10 +34,6 @@ public class PrefsStorage {
|
||||
byte[] bytesForPassword = getBytesForPassword(service);
|
||||
String cipherStorageName = getCipherStorageName(service);
|
||||
if (bytesForUsername != null && bytesForPassword != null) {
|
||||
- if (cipherStorageName == null) {
|
||||
- // If the CipherStorage name is not found, we assume it is because the entry was written by an older version of this library. The older version used Facebook Conceal, so we default to that.
|
||||
- cipherStorageName = CipherStorageFacebookConceal.CIPHER_STORAGE_NAME;
|
||||
- }
|
||||
return new ResultSet(cipherStorageName, bytesForUsername, bytesForPassword);
|
||||
}
|
||||
return null;
|
||||
diff --git a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java b/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java
|
||||
deleted file mode 100644
|
||||
index 3162f99..0000000
|
||||
--- a/node_modules/react-native-keychain/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.java
|
||||
+++ /dev/null
|
||||
@@ -1,110 +0,0 @@
|
||||
-package com.oblador.keychain.cipherStorage;
|
||||
-
|
||||
-import android.os.Build;
|
||||
-import androidx.annotation.NonNull;
|
||||
-
|
||||
-import com.facebook.android.crypto.keychain.AndroidConceal;
|
||||
-import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain;
|
||||
-import com.facebook.crypto.Crypto;
|
||||
-import com.facebook.crypto.CryptoConfig;
|
||||
-import com.facebook.crypto.Entity;
|
||||
-import com.facebook.crypto.keychain.KeyChain;
|
||||
-import com.facebook.react.bridge.ReactApplicationContext;
|
||||
-import com.oblador.keychain.SecurityLevel;
|
||||
-import com.oblador.keychain.exceptions.CryptoFailedException;
|
||||
-
|
||||
-import java.nio.charset.Charset;
|
||||
-
|
||||
-public class CipherStorageFacebookConceal implements CipherStorage {
|
||||
- public static final String CIPHER_STORAGE_NAME = "FacebookConceal";
|
||||
- public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
||||
- private final Crypto crypto;
|
||||
-
|
||||
- public CipherStorageFacebookConceal(ReactApplicationContext reactContext) {
|
||||
- KeyChain keyChain = new SharedPrefsBackedKeyChain(reactContext, CryptoConfig.KEY_256);
|
||||
- this.crypto = AndroidConceal.get().createDefaultCrypto(keyChain);
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public String getCipherStorageName() {
|
||||
- return CIPHER_STORAGE_NAME;
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public int getMinSupportedApiLevel() {
|
||||
- return Build.VERSION_CODES.JELLY_BEAN;
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public SecurityLevel securityLevel() {
|
||||
- return SecurityLevel.ANY;
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public boolean supportsSecureHardware() {
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password, SecurityLevel level) throws CryptoFailedException {
|
||||
-
|
||||
- if (!this.securityLevel().satisfiesSafetyThreshold(level)) {
|
||||
- throw new CryptoFailedException(String.format("Insufficient security level (wants %s; got %s)", level, this.securityLevel()));
|
||||
- }
|
||||
-
|
||||
- if (!crypto.isAvailable()) {
|
||||
- throw new CryptoFailedException("Crypto is missing");
|
||||
- }
|
||||
- Entity usernameEntity = createUsernameEntity(service);
|
||||
- Entity passwordEntity = createPasswordEntity(service);
|
||||
-
|
||||
- try {
|
||||
- byte[] encryptedUsername = crypto.encrypt(username.getBytes(Charset.forName("UTF-8")), usernameEntity);
|
||||
- byte[] encryptedPassword = crypto.encrypt(password.getBytes(Charset.forName("UTF-8")), passwordEntity);
|
||||
-
|
||||
- return new EncryptionResult(encryptedUsername, encryptedPassword, this);
|
||||
- } catch (Exception e) {
|
||||
- throw new CryptoFailedException("Encryption failed for service " + service, e);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException {
|
||||
- if (!crypto.isAvailable()) {
|
||||
- throw new CryptoFailedException("Crypto is missing");
|
||||
- }
|
||||
- Entity usernameEntity = createUsernameEntity(service);
|
||||
- Entity passwordEntity = createPasswordEntity(service);
|
||||
-
|
||||
- try {
|
||||
- byte[] decryptedUsername = crypto.decrypt(username, usernameEntity);
|
||||
- byte[] decryptedPassword = crypto.decrypt(password, passwordEntity);
|
||||
-
|
||||
- return new DecryptionResult(
|
||||
- new String(decryptedUsername, Charset.forName("UTF-8")),
|
||||
- new String(decryptedPassword, Charset.forName("UTF-8")),
|
||||
- SecurityLevel.ANY);
|
||||
- } catch (Exception e) {
|
||||
- throw new CryptoFailedException("Decryption failed for service " + service, e);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public void removeKey(@NonNull String service) {
|
||||
- // Facebook Conceal stores only one key across all services, so we cannot delete the key (otherwise decryption will fail for encrypted data of other services).
|
||||
- }
|
||||
-
|
||||
- private Entity createUsernameEntity(String service) {
|
||||
- String prefix = getEntityPrefix(service);
|
||||
- return Entity.create(prefix + "user");
|
||||
- }
|
||||
-
|
||||
- private Entity createPasswordEntity(String service) {
|
||||
- String prefix = getEntityPrefix(service);
|
||||
- return Entity.create(prefix + "pass");
|
||||
- }
|
||||
-
|
||||
- private String getEntityPrefix(String service) {
|
||||
- return KEYCHAIN_DATA + ":" + service;
|
||||
- }
|
||||
-}
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/node_modules/react-native-mmkv-storage/android/build.gradle b/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
index abb696e..ba35baa 100644
|
||||
--- a/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
+++ b/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
@@ -98,7 +98,8 @@ android {
|
||||
cmake {
|
||||
cppFlags "-O3 -frtti -fexceptions -Wall -fstack-protector-all"
|
||||
arguments "-DANDROID_STL=c++_shared",
|
||||
- "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}"
|
||||
+ "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}",
|
||||
+ "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
||||
abiFilters (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
2
apps/monograph/package-lock.json
generated
2
apps/monograph/package-lock.json
generated
@@ -141,7 +141,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook-importer/core": "^2.2.2",
|
||||
"@notesnook-importer/core": "^2.2.5",
|
||||
"@notesnook/common": "file:../common",
|
||||
"@notesnook/intl": "file:../intl",
|
||||
"@notesnook/theme": "file:../theme",
|
||||
|
||||
4
apps/theme-builder/package-lock.json
generated
4
apps/theme-builder/package-lock.json
generated
@@ -987,7 +987,7 @@
|
||||
},
|
||||
"../web": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.3.1",
|
||||
"version": "3.3.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -1001,7 +1001,7 @@
|
||||
"@lingui/react": "5.1.2",
|
||||
"@mdi/js": "7.4.47",
|
||||
"@mdi/react": "1.6.1",
|
||||
"@notesnook-importer/core": "^2.2.2",
|
||||
"@notesnook-importer/core": "^2.2.5",
|
||||
"@notesnook/common": "file:../../packages/common",
|
||||
"@notesnook/core": "file:../../packages/core",
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
|
||||
1410
apps/web/package-lock.json
generated
1410
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.2",
|
||||
"version": "3.3.5",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
656
extensions/web-clipper/package-lock.json
generated
656
extensions/web-clipper/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/web-clipper",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.0",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
@@ -105,7 +105,7 @@
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.3.1",
|
||||
"ts-loader": "^9.2.6",
|
||||
"web-ext": "^7.6.2",
|
||||
"web-ext": "^7.12.0",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
|
||||
@@ -138,7 +138,8 @@ var options = {
|
||||
plugins: [
|
||||
new CleanWebpackPlugin({
|
||||
verbose: false,
|
||||
dangerouslyAllowCleanPatternsOutsideProject: true
|
||||
dangerouslyAllowCleanPatternsOutsideProject: true,
|
||||
dry: false
|
||||
}),
|
||||
new webpack.ProgressPlugin(),
|
||||
// expose and write the allowed env vars on the compiled bundle
|
||||
|
||||
3
fastlane/metadata/android/en-US/changelogs/15384.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15384.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Minor bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
3
fastlane/metadata/android/en-US/changelogs/15389.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15389.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Minor bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
3
fastlane/metadata/android/en-US/changelogs/15394.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15394.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Minor bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
@@ -104,6 +104,10 @@
|
||||
"run-ios",
|
||||
"build-ios",
|
||||
"build-android",
|
||||
"build-android-debug",
|
||||
"e2e-android-debug",
|
||||
"e2e-android",
|
||||
"e2e-ios",
|
||||
"release-android",
|
||||
"release-android-bundle",
|
||||
"release-ios",
|
||||
|
||||
1
packages/clipper/package-lock.json
generated
1
packages/clipper/package-lock.json
generated
@@ -368,7 +368,6 @@
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
|
||||
@@ -139,3 +139,18 @@ test("search reminders", () =>
|
||||
const descriptionSearch = await db.lookup.reminders("please do").ids();
|
||||
expect(descriptionSearch).toHaveLength(1);
|
||||
}));
|
||||
|
||||
describe("notesWithHighlighting", () => {
|
||||
test("search notes with parentheses in query should load the item", () =>
|
||||
noteTest({
|
||||
title: "(with parantheses)"
|
||||
}).then(async ({ db }) => {
|
||||
await db.notes.add(TEST_NOTE);
|
||||
const filtered = await db.lookup.notesWithHighlighting(
|
||||
"(with parantheses)",
|
||||
db.notes.all
|
||||
);
|
||||
const item = await filtered.item(0);
|
||||
expect(item.item).toBeDefined();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1154,7 +1154,8 @@ function textContainsTokens(text: string, tokens: QueryTokens) {
|
||||
const lowerCasedText = text.toLowerCase();
|
||||
|
||||
const createTagPattern = (token: string) => {
|
||||
return `<${MATCH_TAG_NAME}\\s+id="(.+?)">${token}<\\/${MATCH_TAG_NAME}>`;
|
||||
const escapedToken = token.replace(/[()]/g, "\\$&");
|
||||
return `<${MATCH_TAG_NAME}\\s+id="(.+?)">${escapedToken}<\\/${MATCH_TAG_NAME}>`;
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
@@ -666,6 +666,15 @@ class UserManager {
|
||||
usesFallback: await this.usesFallbackPWHash(old_password)
|
||||
});
|
||||
|
||||
// retrieve user keys before deriving a new encryption key
|
||||
const oldUserKeys = {
|
||||
attachmentsKey: await this.getAttachmentsKey(),
|
||||
monographPasswordsKey: await this.getMonographPasswordsKey(),
|
||||
inboxKeys: (await this.hasInboxKeys())
|
||||
? await this.getInboxKeys()
|
||||
: undefined
|
||||
} as const;
|
||||
|
||||
await this.db.storage().deriveCryptoKey({
|
||||
password: new_password,
|
||||
salt
|
||||
@@ -678,27 +687,33 @@ class UserManager {
|
||||
const userEncryptionKey = await this.getEncryptionKey();
|
||||
if (userEncryptionKey) {
|
||||
const updateUserPayload: Partial<User> = {};
|
||||
const attachmentsKey = await this.getAttachmentsKey();
|
||||
if (attachmentsKey) {
|
||||
if (oldUserKeys.attachmentsKey) {
|
||||
user.attachmentsKey = await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(attachmentsKey));
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.attachmentsKey)
|
||||
);
|
||||
updateUserPayload.attachmentsKey = user.attachmentsKey;
|
||||
}
|
||||
const monographPasswordsKey = await this.getMonographPasswordsKey();
|
||||
if (monographPasswordsKey) {
|
||||
if (oldUserKeys.monographPasswordsKey) {
|
||||
user.monographPasswordsKey = await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(monographPasswordsKey));
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.monographPasswordsKey)
|
||||
);
|
||||
updateUserPayload.monographPasswordsKey = user.monographPasswordsKey;
|
||||
}
|
||||
const inboxKeys = await this.getInboxKeys();
|
||||
if (inboxKeys) {
|
||||
if (oldUserKeys.inboxKeys) {
|
||||
user.inboxKeys = {
|
||||
public: inboxKeys.publicKey,
|
||||
public: oldUserKeys.inboxKeys.publicKey,
|
||||
private: await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(inboxKeys.privateKey))
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.inboxKeys.privateKey)
|
||||
)
|
||||
};
|
||||
updateUserPayload.inboxKeys = user.inboxKeys;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,8 @@ const Tiptap = ({
|
||||
alignItems: "center",
|
||||
padding: "0px 16px",
|
||||
paddingBottom: "3px",
|
||||
boxSizing: "border-box"
|
||||
boxSizing: "border-box",
|
||||
minHeight: "28px"
|
||||
}}
|
||||
>
|
||||
<StatusBar
|
||||
|
||||
@@ -258,10 +258,10 @@ export const Callout = Node.create({
|
||||
container.onmousedown = onClick;
|
||||
container.ontouchstart = onClick;
|
||||
|
||||
if (node.attrs.hiddenUnder) {
|
||||
container.dataset.hiddenUnder = node.attrs.hiddenUnder;
|
||||
if (node.attrs.hidden) {
|
||||
container.dataset.hidden = node.attrs.hidden;
|
||||
} else {
|
||||
delete container.dataset.hiddenUnder;
|
||||
delete container.dataset.hidden;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -275,9 +275,9 @@ export const Callout = Node.create({
|
||||
if (updatedNode.attrs.collapsed) container.classList.add("collapsed");
|
||||
else container.classList.remove("collapsed");
|
||||
|
||||
if (updatedNode.attrs.hiddenUnder)
|
||||
container.dataset.hiddenUnder = updatedNode.attrs.hiddenUnder;
|
||||
else delete container.dataset.hiddenUnder;
|
||||
if (updatedNode.attrs.hidden)
|
||||
container.dataset.hidden = updatedNode.attrs.hidden;
|
||||
else delete container.dataset.hidden;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`check list item > inline image as first child in check list item 1`] = `"<ul class="simple-checklist"><li class="simple-checklist--item"><p data-spacing="double">item 1</p></li><li class="simple-checklist--item"><p data-spacing="double"></p><img src="image.png" data-aspect-ratio="1"></li></ul>"`;
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
createEditor,
|
||||
h,
|
||||
p,
|
||||
checkList,
|
||||
checkListItem
|
||||
} from "../../../../test-utils/index.js";
|
||||
import { CheckList } from "../../check-list/check-list.js";
|
||||
import { CheckListItem } from "../check-list-item.js";
|
||||
import { Paragraph } from "../../paragraph/paragraph.js";
|
||||
import { ImageNode } from "../../image/image.js";
|
||||
|
||||
describe("check list item", () => {
|
||||
/**
|
||||
* see https://github.com/streetwriters/notesnook/pull/8877 for more context
|
||||
*/
|
||||
test("inline image as first child in check list item", async () => {
|
||||
const el = checkList(
|
||||
checkListItem([p(["item 1"])]),
|
||||
checkListItem([h("img", [], { src: "image.png" })])
|
||||
);
|
||||
|
||||
const { editor } = createEditor({
|
||||
initialContent: el.outerHTML,
|
||||
extensions: {
|
||||
checkList: CheckList,
|
||||
checkListItem: CheckListItem.configure({ nested: true }),
|
||||
paragraph: Paragraph,
|
||||
image: ImageNode
|
||||
}
|
||||
});
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from "@tiptap/core";
|
||||
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||
import { CheckList } from "../check-list/check-list.js";
|
||||
import { ensureLeadingParagraph } from "../../utils/prosemirror.js";
|
||||
|
||||
export interface CheckListItemOptions {
|
||||
onReadOnlyChecked?: (node: ProseMirrorNode, checked: boolean) => boolean;
|
||||
@@ -67,7 +68,8 @@ export const CheckListItem = Node.create<CheckListItemOptions>({
|
||||
return [
|
||||
{
|
||||
tag: `li.simple-checklist--item`,
|
||||
priority: 51
|
||||
priority: 51,
|
||||
getContent: ensureLeadingParagraph
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
@@ -555,7 +555,7 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
|
||||
compareCaretPosition(prev.caretPosition, next.caretPosition) ||
|
||||
prev.language !== next.language ||
|
||||
prev.indentType !== next.indentType ||
|
||||
prev.hiddenUnder !== next.hiddenUnder
|
||||
prev.hidden !== next.hidden
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from "@tiptap/core";
|
||||
import { Heading as TiptapHeading } from "@tiptap/extension-heading";
|
||||
import { isClickWithinBounds } from "../../utils/prosemirror.js";
|
||||
import { Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Plugin, PluginKey, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Node } from "@tiptap/pm/model";
|
||||
import { useToolbarStore } from "../../toolbar/stores/toolbar-store.js";
|
||||
|
||||
@@ -72,13 +72,14 @@ export const Heading = TiptapHeading.extend({
|
||||
return false;
|
||||
}
|
||||
|
||||
const { textAlign, textDirection } =
|
||||
const { textAlign, textDirection, collapsed } =
|
||||
state.selection.$from.parent.attrs;
|
||||
|
||||
return commands.setNode(this.name, {
|
||||
...attributes,
|
||||
textAlign,
|
||||
textDirection
|
||||
textDirection,
|
||||
collapsed
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -89,14 +90,14 @@ export const Heading = TiptapHeading.extend({
|
||||
{
|
||||
types: COLLAPSIBLE_BLOCK_TYPES,
|
||||
attributes: {
|
||||
hiddenUnder: {
|
||||
default: null,
|
||||
hidden: {
|
||||
default: false,
|
||||
keepOnSplit: false,
|
||||
parseHTML: (element) => element.dataset.hiddenUnder || null,
|
||||
parseHTML: (element) => element.dataset.hidden === "true",
|
||||
renderHTML: (attributes) => {
|
||||
if (!attributes.hiddenUnder) return {};
|
||||
if (!attributes.hidden) return {};
|
||||
return {
|
||||
"data-hidden-under": attributes.hiddenUnder
|
||||
"data-hidden": attributes.hidden === true
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -151,15 +152,19 @@ export const Heading = TiptapHeading.extend({
|
||||
find: HEADING_REGEX,
|
||||
type: this.type,
|
||||
getAttributes: (match) => {
|
||||
const { textAlign, textDirection } =
|
||||
const { textAlign, textDirection, collapsed } =
|
||||
this.editor.state.selection.$from.parent?.attrs || {};
|
||||
const level = match[1].length;
|
||||
return { level, textAlign, textDirection };
|
||||
return { level, textAlign, textDirection, collapsed };
|
||||
}
|
||||
})
|
||||
];
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [headingUpdatePlugin];
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ({ node, getPos, editor, HTMLAttributes }) => {
|
||||
const heading = document.createElement(`h${node.attrs.level}`);
|
||||
@@ -179,11 +184,14 @@ export const Heading = TiptapHeading.extend({
|
||||
if (typeof pos !== "number") return;
|
||||
|
||||
const resolvedPos = editor.state.doc.resolve(pos);
|
||||
const calloutAncestor = findParentNodeClosestToPos(
|
||||
resolvedPos,
|
||||
(node) => node.type.name === "callout"
|
||||
);
|
||||
if (calloutAncestor) return;
|
||||
const forbiddenParents = ["callout", "table"];
|
||||
if (
|
||||
findParentNodeClosestToPos(resolvedPos, (node) =>
|
||||
forbiddenParents.includes(node.type.name)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isClickWithinBounds(
|
||||
@@ -200,16 +208,9 @@ export const Heading = TiptapHeading.extend({
|
||||
if (currentNode && currentNode.type.name === "heading") {
|
||||
const shouldCollapse = !currentNode.attrs.collapsed;
|
||||
const headingLevel = currentNode.attrs.level;
|
||||
const headingId = currentNode.attrs.blockId;
|
||||
|
||||
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
|
||||
toggleNodesUnderHeading(
|
||||
tr,
|
||||
pos,
|
||||
headingLevel,
|
||||
shouldCollapse,
|
||||
headingId
|
||||
);
|
||||
toggleNodesUnderHeading(tr, pos, headingLevel, shouldCollapse);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -234,9 +235,9 @@ export const Heading = TiptapHeading.extend({
|
||||
if (updatedNode.attrs.collapsed) heading.dataset.collapsed = "true";
|
||||
else delete heading.dataset.collapsed;
|
||||
|
||||
if (updatedNode.attrs.hiddenUnder)
|
||||
heading.dataset.hiddenUnder = updatedNode.attrs.hiddenUnder;
|
||||
else delete heading.dataset.hiddenUnder;
|
||||
if (updatedNode.attrs.hidden)
|
||||
heading.dataset.hidden = updatedNode.attrs.hidden;
|
||||
else delete heading.dataset.hidden;
|
||||
|
||||
if (updatedNode.attrs.textAlign)
|
||||
heading.style.textAlign =
|
||||
@@ -259,8 +260,7 @@ function toggleNodesUnderHeading(
|
||||
tr: Transaction,
|
||||
headingPos: number,
|
||||
headingLevel: number,
|
||||
isCollapsing: boolean,
|
||||
headingId: string
|
||||
isCollapsing: boolean
|
||||
) {
|
||||
const { doc } = tr;
|
||||
const headingNode = doc.nodeAt(headingPos);
|
||||
@@ -269,6 +269,8 @@ function toggleNodesUnderHeading(
|
||||
let nextPos = headingPos + headingNode.nodeSize;
|
||||
const cursorPos = tr.selection.from;
|
||||
let shouldMoveCursor = false;
|
||||
let insideCollapsedHeading = false;
|
||||
let nestedHeadingLevel: number | null = null;
|
||||
|
||||
while (nextPos < doc.content.size) {
|
||||
const nextNode = doc.nodeAt(nextPos);
|
||||
@@ -289,15 +291,33 @@ function toggleNodesUnderHeading(
|
||||
shouldMoveCursor = true;
|
||||
}
|
||||
|
||||
const currentPos = nextPos;
|
||||
nextPos += nextNode.nodeSize;
|
||||
|
||||
if (COLLAPSIBLE_BLOCK_TYPES.includes(nextNode.type.name)) {
|
||||
if (isCollapsing && typeof nextNode.attrs.hiddenUnder !== "string") {
|
||||
tr.setNodeAttribute(nextPos, "hiddenUnder", headingId);
|
||||
} else if (!isCollapsing && nextNode.attrs.hiddenUnder === headingId) {
|
||||
tr.setNodeAttribute(nextPos, "hiddenUnder", null);
|
||||
if (isCollapsing) {
|
||||
tr.setNodeAttribute(currentPos, "hidden", true);
|
||||
} else {
|
||||
if (insideCollapsedHeading) {
|
||||
if (
|
||||
nextNode.type.name === "heading" &&
|
||||
nestedHeadingLevel !== null &&
|
||||
nextNode.attrs.level <= nestedHeadingLevel
|
||||
) {
|
||||
insideCollapsedHeading = false;
|
||||
nestedHeadingLevel = null;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
tr.setNodeAttribute(currentPos, "hidden", false);
|
||||
if (nextNode.type.name === "heading" && nextNode.attrs.collapsed) {
|
||||
insideCollapsedHeading = true;
|
||||
nestedHeadingLevel = nextNode.attrs.level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextPos += nextNode.nodeSize;
|
||||
}
|
||||
|
||||
if (shouldMoveCursor) {
|
||||
@@ -332,3 +352,45 @@ function findEndOfCollapsedSection(
|
||||
|
||||
return nextPos;
|
||||
}
|
||||
|
||||
const headingUpdatePlugin = new Plugin({
|
||||
key: new PluginKey("headingUpdate"),
|
||||
appendTransaction(transactions, oldState, newState) {
|
||||
const hasDocChanges = transactions.some(
|
||||
(transaction) => transaction.docChanged
|
||||
);
|
||||
if (!hasDocChanges) return null;
|
||||
|
||||
const tr = newState.tr;
|
||||
const oldDoc = oldState.doc;
|
||||
const newDoc = newState.doc;
|
||||
let modified = false;
|
||||
|
||||
newDoc.descendants((newNode, pos) => {
|
||||
if (newNode.type.name === "heading") {
|
||||
if (pos >= oldDoc.content.size) return;
|
||||
|
||||
const oldNode = oldDoc.nodeAt(pos);
|
||||
if (
|
||||
oldNode &&
|
||||
oldNode.type.name === "heading" &&
|
||||
oldNode.attrs.level !== newNode.attrs.level
|
||||
) {
|
||||
/**
|
||||
* if the level of a collapsed heading is changed,
|
||||
* we need to reset visibility of all the nodes under it as there
|
||||
* might be a heading of same or higher level previously
|
||||
* hidden under this heading
|
||||
*/
|
||||
if (newNode.attrs.collapsed) {
|
||||
toggleNodesUnderHeading(tr, pos, oldNode.attrs.level, false);
|
||||
toggleNodesUnderHeading(tr, pos, newNode.attrs.level, true);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return modified ? tr : null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,8 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ListItem as TiptapListItem } from "@tiptap/extension-list-item";
|
||||
import { ensureLeadingParagraph } from "../../utils/prosemirror.js";
|
||||
|
||||
export const ListItem = TiptapListItem.extend({
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
priority: 50,
|
||||
tag: `li`,
|
||||
getContent: ensureLeadingParagraph
|
||||
}
|
||||
];
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
|
||||
@@ -5,3 +5,5 @@ exports[`hitting backspace at the start of first list item 1`] = `"<div><div con
|
||||
exports[`hitting backspace at the start of the second (or next) list item 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item1item2</p></li></ul></div></div>"`;
|
||||
|
||||
exports[`hitting backspace at the start of the second (or next) paragraph inside the list item 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item 1item 2</p></li></ul></div></div>"`;
|
||||
|
||||
exports[`inline image as first child in list item 1`] = `"<ul><li><p data-spacing="double">item 1</p></li><li><p data-spacing="double"></p><img src="image.png" data-aspect-ratio="1"></li></ul>"`;
|
||||
|
||||
@@ -23,6 +23,8 @@ import { createEditor, h, li, p, ul } from "../../../../test-utils/index.js";
|
||||
import BulletList from "../../bullet-list/index.js";
|
||||
import OrderedList from "../../ordered-list/index.js";
|
||||
import { ListItem } from "../index.js";
|
||||
import { Paragraph } from "../../paragraph/paragraph.js";
|
||||
import { ImageNode } from "../../image/image.js";
|
||||
|
||||
test("hitting backspace at the start of first list item", async () => {
|
||||
const el = ul([li([p(["item1"])]), li([p(["item2"])])]);
|
||||
@@ -97,3 +99,24 @@ test("hitting backspace at the start of the second (or next) paragraph inside th
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
expect(editorElement.outerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
/**
|
||||
* see https://github.com/streetwriters/notesnook/pull/8877 for more context
|
||||
*/
|
||||
test("inline image as first child in list item", async () => {
|
||||
const el = ul([
|
||||
li([p(["item 1"])]),
|
||||
li([h("img", [], { src: "image.png" })])
|
||||
]);
|
||||
|
||||
const { editor } = createEditor({
|
||||
initialContent: el.outerHTML,
|
||||
extensions: {
|
||||
listItem: ListItem,
|
||||
paragraph: Paragraph,
|
||||
image: ImageNode
|
||||
}
|
||||
});
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -125,10 +125,10 @@ export class MathView implements NodeView, ICursorPosObserver {
|
||||
if (options.className) this.dom.classList.add(options.className);
|
||||
this.dom.classList.add("math-node");
|
||||
|
||||
if (node.attrs.hiddenUnder) {
|
||||
this.dom.dataset.hiddenUnder = node.attrs.hiddenUnder;
|
||||
if (node.attrs.hidden) {
|
||||
this.dom.dataset.hidden = node.attrs.hidden;
|
||||
} else {
|
||||
delete this.dom.dataset.hiddenUnder;
|
||||
delete this.dom.dataset.hidden;
|
||||
}
|
||||
|
||||
this._mathRenderElt = document.createElement("span");
|
||||
|
||||
@@ -128,3 +128,5 @@ exports[`outline list item > code block in outline list item 1`] = `
|
||||
"type": "doc",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`outline list item > inline image as first child in the old outline list item 1`] = `"<ul data-type="outlineList"><li data-type="outlineListItem"><p data-spacing="double">item 1</p></li><li data-type="outlineListItem"><p data-spacing="double"></p><img src="image.png" data-aspect-ratio="1"></li></ul>"`;
|
||||
|
||||
@@ -28,6 +28,8 @@ import { test, expect, describe, beforeAll, vi } from "vitest";
|
||||
import { OutlineList } from "../../outline-list/outline-list.js";
|
||||
import { OutlineListItem } from "../outline-list-item.js";
|
||||
import { CodeBlock } from "../../code-block/code-block.js";
|
||||
import { Paragraph } from "../../paragraph/paragraph.js";
|
||||
import { ImageNode } from "../../image/image.js";
|
||||
|
||||
describe("outline list item", () => {
|
||||
beforeAll(() => {
|
||||
@@ -70,4 +72,30 @@ describe("outline list item", () => {
|
||||
|
||||
expect(editor.getJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
/**
|
||||
* Two changes happened:
|
||||
* 1. Images were converted from inline nodes to block nodes (https://github.com/streetwriters/notesnook/pull/8563)
|
||||
* 2. Outline list item's `content` schema was changed from `paragraph + list?` to `block+` to `paragraph block*` (https://github.com/streetwriters/notesnook/pull/8772 and https://github.com/streetwriters/notesnook/commit/0b943d8ecdf04fd7d996fd0a4b1d62ec9569f071)
|
||||
*
|
||||
* In the old editor, it was possible to have an inline image as the first item in the outline list item, but based on the new schema it is not possible anymore. So the editor should insert an empty paragraph before the image.
|
||||
*/
|
||||
test("inline image as first child in the old outline list item", async () => {
|
||||
const el = outlineList(
|
||||
outlineListItem(["item 1"]),
|
||||
outlineListItem([h("img", [], { src: "image.png" })])
|
||||
);
|
||||
|
||||
const { editor } = createEditor({
|
||||
initialContent: el.outerHTML,
|
||||
extensions: {
|
||||
outlineList: OutlineList,
|
||||
outlineListItem: OutlineListItem,
|
||||
paragraph: Paragraph,
|
||||
image: ImageNode
|
||||
}
|
||||
});
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,8 @@ import {
|
||||
} from "@tiptap/core";
|
||||
import {
|
||||
findParentNodeOfTypeClosestToPos,
|
||||
isClickWithinBounds
|
||||
isClickWithinBounds,
|
||||
ensureLeadingParagraph
|
||||
} from "../../utils/prosemirror.js";
|
||||
import { OutlineList } from "../outline-list/outline-list.js";
|
||||
import { keybindings, tiptapKeys } from "@notesnook/common";
|
||||
@@ -64,7 +65,8 @@ export const OutlineListItem = Node.create<ListItemOptions>({
|
||||
return [
|
||||
{
|
||||
priority: 100,
|
||||
tag: `li[data-type="${this.name}"]`
|
||||
tag: `li[data-type="${this.name}"]`,
|
||||
getContent: ensureLeadingParagraph
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
@@ -112,10 +112,10 @@ export class ReactNodeView<P extends ReactNodeViewProps> implements NodeView {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.node.attrs.hiddenUnder) {
|
||||
this.domRef.dataset.hiddenUnder = this.node.attrs.hiddenUnder;
|
||||
if (this.node.attrs.hidden) {
|
||||
this.domRef.dataset.hidden = this.node.attrs.hidden;
|
||||
} else {
|
||||
delete this.domRef.dataset.hiddenUnder;
|
||||
delete this.domRef.dataset.hidden;
|
||||
}
|
||||
|
||||
portalProviderAPI.render(this.Component, this.domRef);
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ReactNodeView, ReactNodeViewProps } from "../react/index.js";
|
||||
import { Node as ProsemirrorNode } from "prosemirror-model";
|
||||
import { Editor } from "../../types.js";
|
||||
import { Editor as TiptapEditor } from "@tiptap/core";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { updateColumnsOnResize } from "@tiptap/pm/tables";
|
||||
import { EditorView, NodeView } from "prosemirror-view";
|
||||
import {
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
TableProperties
|
||||
} from "../../toolbar/tools/table.js";
|
||||
import { getToolDefinition } from "../../toolbar/tool-definitions.js";
|
||||
import { getPosition } from "@notesnook/ui";
|
||||
import { getPosition, ScrollContainer } from "@notesnook/ui";
|
||||
import {
|
||||
findSelectedDOMNode,
|
||||
hasSameAttributes
|
||||
@@ -41,12 +41,14 @@ import { DesktopOnly } from "../../components/responsive/index.js";
|
||||
import { TextDirections } from "../text-direction/index.js";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import SimpleBar from "simplebar-react";
|
||||
import { useIsMobile } from "../../toolbar/stores/toolbar-store.js";
|
||||
|
||||
export function TableComponent(props: ReactNodeViewProps) {
|
||||
const { editor, node, forwardRef } = props;
|
||||
const colgroupRef = useRef<HTMLTableColElement>(null);
|
||||
const tableRef = useRef<HTMLTableElement>();
|
||||
const { textDirection } = node.attrs;
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
useEffect(() => {
|
||||
if (!colgroupRef.current || !tableRef.current) return;
|
||||
@@ -54,6 +56,22 @@ export function TableComponent(props: ReactNodeViewProps) {
|
||||
updateColumnsOnResize(node, colgroupRef.current, tableRef.current, 50);
|
||||
}, [node]);
|
||||
|
||||
const renderScrollContent = useCallback(() => {
|
||||
return (
|
||||
<div dir={textDirection}>
|
||||
<table
|
||||
ref={(ref) => {
|
||||
forwardRef?.(ref);
|
||||
tableRef.current = ref || undefined;
|
||||
}}
|
||||
>
|
||||
<colgroup ref={colgroupRef} />
|
||||
{/* <tbody /> */}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}, [forwardRef, textDirection]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DesktopOnly>
|
||||
@@ -68,19 +86,12 @@ export function TableComponent(props: ReactNodeViewProps) {
|
||||
textDirection={textDirection}
|
||||
/>
|
||||
</DesktopOnly>
|
||||
<SimpleBar autoHide>
|
||||
<Box dir={textDirection}>
|
||||
<table
|
||||
ref={(ref) => {
|
||||
forwardRef?.(ref);
|
||||
tableRef.current = ref || undefined;
|
||||
}}
|
||||
>
|
||||
<colgroup ref={colgroupRef} />
|
||||
{/* <tbody /> */}
|
||||
</table>
|
||||
</Box>
|
||||
</SimpleBar>
|
||||
|
||||
{isMobile ? (
|
||||
<ScrollContainer>{renderScrollContent()}</ScrollContainer>
|
||||
) : (
|
||||
<SimpleBar>{renderScrollContent()}</SimpleBar>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -208,6 +219,7 @@ function TableRowToolbar(props: TableToolbarProps) {
|
||||
function TableColumnToolbar(props: TableToolbarProps) {
|
||||
const { editor, table } = props;
|
||||
const columnToolsRef = useRef<HTMLDivElement>(null);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
useEffect(() => {
|
||||
function onSelectionUpdate() {
|
||||
@@ -234,8 +246,9 @@ function TableColumnToolbar(props: TableToolbarProps) {
|
||||
yOffset: 2
|
||||
});
|
||||
|
||||
const scrollLeft =
|
||||
table.current?.closest(".simplebar-content-wrapper")?.scrollLeft || 0;
|
||||
const scrollLeft = isMobile
|
||||
? table.current.parentElement?.parentElement?.scrollLeft || 0
|
||||
: table.current?.closest(".simplebar-content-wrapper")?.scrollLeft || 0;
|
||||
|
||||
columnToolsRef.current.style.left = `${pos.left - scrollLeft}px`;
|
||||
columnToolsRef.current.style.top = `${pos.top}px`;
|
||||
@@ -245,7 +258,7 @@ function TableColumnToolbar(props: TableToolbarProps) {
|
||||
return () => {
|
||||
editor.off("selectionUpdate", onSelectionUpdate);
|
||||
};
|
||||
}, []);
|
||||
}, [isMobile]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`task list item > inline image as first child in task list item 1`] = `"<ul class="checklist"><li class="checklist--item"><p data-spacing="double">item 1</p></li><li class="checklist--item"><p data-spacing="double"></p><img src="image.png" data-aspect-ratio="1"></li></ul>"`;
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
createEditor,
|
||||
h,
|
||||
p,
|
||||
taskList,
|
||||
taskItem
|
||||
} from "../../../../test-utils/index.js";
|
||||
import { TaskListNode } from "../../task-list/task-list.js";
|
||||
import { TaskItemNode } from "../task-item.js";
|
||||
import { Paragraph } from "../../paragraph/paragraph.js";
|
||||
import { ImageNode } from "../../image/image.js";
|
||||
|
||||
describe("task list item", () => {
|
||||
/**
|
||||
* see https://github.com/streetwriters/notesnook/pull/8877 for more context
|
||||
*/
|
||||
test("inline image as first child in task list item", async () => {
|
||||
const el = taskList(
|
||||
taskItem([p(["item 1"])]),
|
||||
taskItem([h("img", [], { src: "image.png" })])
|
||||
);
|
||||
|
||||
const { editor } = createEditor({
|
||||
initialContent: el.outerHTML,
|
||||
extensions: {
|
||||
taskList: TaskListNode,
|
||||
taskListItem: TaskItemNode.configure({ nested: true }),
|
||||
paragraph: Paragraph,
|
||||
image: ImageNode
|
||||
}
|
||||
});
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,7 @@ import { mergeAttributes } from "@tiptap/core";
|
||||
import { TaskItem } from "@tiptap/extension-task-item";
|
||||
import { TaskItemComponent } from "./component.js";
|
||||
import { createNodeView } from "../react/index.js";
|
||||
import { ensureLeadingParagraph } from "../../utils/prosemirror.js";
|
||||
|
||||
export type TaskItemAttributes = {
|
||||
checked: boolean;
|
||||
@@ -56,7 +57,8 @@ export const TaskItemNode = TaskItem.extend({
|
||||
return [
|
||||
{
|
||||
tag: ".checklist > li",
|
||||
priority: 51
|
||||
priority: 100,
|
||||
getContent: ensureLeadingParagraph
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
@@ -17,52 +17,39 @@ 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 { createEditor, h, ul, li } from "../../../../test-utils/index.js";
|
||||
import {
|
||||
createEditor,
|
||||
taskItem,
|
||||
taskList
|
||||
} from "../../../../test-utils/index.js";
|
||||
import { test, expect } from "vitest";
|
||||
import { TaskListNode } from "../index.js";
|
||||
import { TaskItemNode } from "../../task-item/index.js";
|
||||
import { p, eq } from "prosemirror-test-builder";
|
||||
import { countCheckedItems, deleteCheckedItems, sortList } from "../utils.js";
|
||||
|
||||
function taskList(...children: HTMLLIElement[]) {
|
||||
return ul(children, { class: "checklist" });
|
||||
}
|
||||
|
||||
function taskItem(
|
||||
text: string,
|
||||
attr: { checked?: boolean } = {},
|
||||
subList?: HTMLUListElement
|
||||
) {
|
||||
const children: HTMLElement[] = [h("p", [text])];
|
||||
if (subList) children.push(subList);
|
||||
|
||||
return li(children, {
|
||||
class: "checklist--item " + (attr.checked ? "checked" : "")
|
||||
});
|
||||
}
|
||||
|
||||
const NESTED_TASK_LIST = taskList(
|
||||
taskItem("Task item 1", { checked: true }),
|
||||
taskItem("Task item 2"),
|
||||
taskItem(["Task item 1"], { checked: true }),
|
||||
taskItem(["Task item 2"]),
|
||||
taskItem(
|
||||
"Task item 3",
|
||||
["Task item 3"],
|
||||
{ checked: false },
|
||||
taskList(
|
||||
taskItem("Task item 4", { checked: true }),
|
||||
taskItem("Task item 5"),
|
||||
taskItem(["Task item 4"], { checked: true }),
|
||||
taskItem(["Task item 5"]),
|
||||
taskItem(
|
||||
"Task item 6",
|
||||
["Task item 6"],
|
||||
{ checked: false },
|
||||
taskList(
|
||||
taskItem("Task item 7", { checked: true }),
|
||||
taskItem("Task item 8", { checked: true }),
|
||||
taskItem(["Task item 7"], { checked: true }),
|
||||
taskItem(["Task item 8"], { checked: true }),
|
||||
taskItem(
|
||||
"Task item 9",
|
||||
["Task item 9"],
|
||||
{ checked: false },
|
||||
taskList(
|
||||
taskItem("Task item 10", { checked: true }),
|
||||
taskItem("Task item 11", { checked: true }),
|
||||
taskItem("Task item 12")
|
||||
taskItem(["Task item 10"], { checked: true }),
|
||||
taskItem(["Task item 11"], { checked: true }),
|
||||
taskItem(["Task item 12"])
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -99,8 +86,8 @@ test(`count items in a task list`, async () => {
|
||||
test(`delete checked items in a task list`, async () => {
|
||||
const { editor } = createEditor({
|
||||
initialContent: taskList(
|
||||
taskItem("Task item 1", { checked: true }),
|
||||
taskItem("Task item 2")
|
||||
taskItem(["Task item 1"], { checked: true }),
|
||||
taskItem(["Task item 2"])
|
||||
).outerHTML,
|
||||
extensions: {
|
||||
taskItem: TaskItemNode.configure({ nested: true }),
|
||||
@@ -132,15 +119,15 @@ test(`delete checked items in a nested task list`, async () => {
|
||||
test(`delete checked items in a task list with no checked items should do nothing`, async () => {
|
||||
const { editor } = createEditor({
|
||||
initialContent: taskList(
|
||||
taskItem("Task item 1", { checked: false }),
|
||||
taskItem("Task item 2"),
|
||||
taskItem(["Task item 1"], { checked: false }),
|
||||
taskItem(["Task item 2"]),
|
||||
taskItem(
|
||||
"Task item 3",
|
||||
["Task item 3"],
|
||||
{ checked: false },
|
||||
taskList(
|
||||
taskItem("Task item 4", { checked: false }),
|
||||
taskItem("Task item 5"),
|
||||
taskItem("Task item 6", { checked: false })
|
||||
taskItem(["Task item 4"], { checked: false }),
|
||||
taskItem(["Task item 5"]),
|
||||
taskItem(["Task item 6"], { checked: false })
|
||||
)
|
||||
)
|
||||
).outerHTML,
|
||||
@@ -172,8 +159,10 @@ test(`sort checked items to the bottom of the task list`, async () => {
|
||||
|
||||
test(`sorting a task list with no checked items should do nothing`, async () => {
|
||||
const { editor } = createEditor({
|
||||
initialContent: taskList(taskItem("Task item 1"), taskItem("Task item 2"))
|
||||
.outerHTML,
|
||||
initialContent: taskList(
|
||||
taskItem(["Task item 1"]),
|
||||
taskItem(["Task item 2"])
|
||||
).outerHTML,
|
||||
extensions: {
|
||||
taskItem: TaskItemNode.configure({ nested: true }),
|
||||
taskList: TaskListNode
|
||||
|
||||
@@ -31,7 +31,10 @@ import {
|
||||
NodeType,
|
||||
ResolvedPos,
|
||||
Attrs,
|
||||
Slice
|
||||
Slice,
|
||||
DOMParser,
|
||||
Schema,
|
||||
Fragment
|
||||
} from "prosemirror-model";
|
||||
import { EditorState, Selection, Transaction } from "prosemirror-state";
|
||||
import TextStyle from "@tiptap/extension-text-style";
|
||||
@@ -394,3 +397,16 @@ export function isClickWithinBounds(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureLeadingParagraph(node: Node, schema: Schema): Fragment {
|
||||
const parser = DOMParser.fromSchema(schema);
|
||||
const fragment = parser.parse(node).content;
|
||||
const firstNode = fragment.firstChild;
|
||||
|
||||
if (firstNode && firstNode.type.name !== "paragraph") {
|
||||
const emptyParagraph = schema.nodes.paragraph.create();
|
||||
return fragment.addToStart(emptyParagraph);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ProseMirror p code {
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.ProseMirror:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -54,13 +50,14 @@
|
||||
}
|
||||
|
||||
.ProseMirror p code {
|
||||
background-color: var(--background);
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--paragraph-secondary);
|
||||
border-radius: 5px;
|
||||
padding: 3px 5px 0px 5px;
|
||||
padding: 2px 5px 2px 5px;
|
||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono,
|
||||
Menlo, monospace !important;
|
||||
font-size: 10pt !important;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
.ProseMirror code > span {
|
||||
@@ -978,7 +975,22 @@ del.diffdel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-hidden-under] {
|
||||
.ProseMirror table h1::before,
|
||||
.ProseMirror table h2::before,
|
||||
.ProseMirror table h3::before,
|
||||
.ProseMirror table h4::before,
|
||||
.ProseMirror table h5::before,
|
||||
.ProseMirror table h6::before,
|
||||
.ProseMirror table h1::after,
|
||||
.ProseMirror table h2::after,
|
||||
.ProseMirror table h3::after,
|
||||
.ProseMirror table h4::after,
|
||||
.ProseMirror table h5::after,
|
||||
.ProseMirror table h6::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-hidden="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,3 +108,36 @@ export function outlineListItem(
|
||||
"data-type": "outlineListItem"
|
||||
});
|
||||
}
|
||||
|
||||
export function taskList(...children: HTMLLIElement[]) {
|
||||
return ul(children, { class: "checklist" });
|
||||
}
|
||||
|
||||
export function taskItem(
|
||||
paragraphChildren: (string | HTMLElement)[],
|
||||
attr: { checked?: boolean } = {},
|
||||
subList?: HTMLUListElement
|
||||
) {
|
||||
const children: HTMLElement[] = [h("p", paragraphChildren)];
|
||||
if (subList) children.push(subList);
|
||||
|
||||
return li(children, {
|
||||
class: "checklist--item " + (attr.checked ? "checked" : "")
|
||||
});
|
||||
}
|
||||
|
||||
export function checkList(...children: HTMLLIElement[]) {
|
||||
return ul(children, { class: "simple-checklist" });
|
||||
}
|
||||
|
||||
export function checkListItem(
|
||||
paragraphChildren: (string | HTMLElement)[],
|
||||
subList?: HTMLUListElement
|
||||
) {
|
||||
const children: HTMLElement[] = [h("p", paragraphChildren)];
|
||||
if (subList) children.push(subList);
|
||||
|
||||
return li(children, {
|
||||
class: "simple-checklist--item "
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user