mirror of
https://github.com/standardnotes/app
synced 2026-09-19 09:13:53 -04:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b9341257c | ||
|
|
ef541cf70a | ||
|
|
4fccc55f25 | ||
|
|
6fea8a3095 | ||
|
|
6fcde388d6 | ||
|
|
616c700b7f | ||
|
|
41a5b1415a | ||
|
|
b3556ef8d2 | ||
|
|
de8d44f507 | ||
|
|
daf5071706 | ||
|
|
67a2627a0d | ||
|
|
ba7d0cf4c1 | ||
|
|
ea5bec4a23 | ||
|
|
031b0a496f | ||
|
|
36d136a4d1 | ||
|
|
f884e2cd66 | ||
|
|
fbb0dc0cea | ||
|
|
c50e29db31 | ||
|
|
541adcf8f8 | ||
|
|
c832371e3a | ||
|
|
c3d3633e6b | ||
|
|
7773becdec | ||
|
|
860746d4e2 | ||
|
|
09fd65903b | ||
|
|
b57d16dd68 | ||
|
|
08a20e25b4 | ||
|
|
c3a5b249b2 | ||
|
|
3c209329a3 |
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.105.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.105.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.105.32",
|
||||
"version": "3.105.44",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/icons
|
||||
|
||||
## [1.10.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
|
||||
|
||||
## [1.10.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-02-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/icons
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/icons",
|
||||
"version": "1.10.5",
|
||||
"version": "1.10.7",
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="currentColor"
|
||||
d="m11.4 16l2.6-2.6l2.6 2.6l1.4-1.4l-2.6-2.6L18 9.4L16.6 8L14 10.6L11.4 8L10 9.4l2.6 2.6l-2.6 2.6l1.4 1.4ZM3 12l4.35-6.15q.275-.4.713-.625T9 5h10q.825 0 1.413.588T21 7v10q0 .825-.588 1.413T19 19H9q-.5 0-.938-.225t-.712-.625L3 12Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 330 B |
@@ -6,6 +6,7 @@ import AccountVariantIcon from './ic-account-variant.svg'
|
||||
import AddBoldIcon from './ic-add-bold.svg'
|
||||
import AddIcon from './ic-add.svg'
|
||||
import AddTextIcon from './ic-add-text.svg'
|
||||
import AegisIcon from './ic-aegis.svg'
|
||||
import ArchiveIcon from './ic-archive.svg'
|
||||
import ArrowDownCheckmarkIcon from './arrow-down-checkmark.svg'
|
||||
import ArrowDownIcon from './ic-arrow-down.svg'
|
||||
@@ -21,6 +22,7 @@ import AttachmentFileIcon from './ic-attachment-file.svg'
|
||||
import AuthenticatorIcon from './ic-authenticator.svg'
|
||||
import AuthenticatorVariantIcon from './ic-authenticator-variant.svg'
|
||||
import BackIosIcon from './ic-back-ios.svg'
|
||||
import BackspaceIcon from './ic-backspace.svg'
|
||||
import BlockIcon from './ic-block.svg'
|
||||
import BlueDotIcon from './blue-dot.svg'
|
||||
import BoldIcon from './ic-bold.svg'
|
||||
@@ -57,6 +59,7 @@ import EditorIcon from './ic-editor.svg'
|
||||
import EmailFilledIcon from './ic-email-filled.svg'
|
||||
import EmailIcon from './ic-email.svg'
|
||||
import EnterIcon from './ic-enter.svg'
|
||||
import EvernoteIcon from './ic-evernote.svg'
|
||||
import EyeFilledIcon from './ic-eye-filled.svg'
|
||||
import EyeIcon from './ic-eye.svg'
|
||||
import EyeOffFilledIcon from './ic-eye-off-filled.svg'
|
||||
@@ -87,6 +90,7 @@ import ForwardIosIcon from './ic-forward-ios.svg'
|
||||
import FullscreenExitIcon from './ic-fullscreen-exit.svg'
|
||||
import FullscreenIcon from './ic-fullscreen.svg'
|
||||
import GiftOutlineIcon from './ic-gift-outline.svg'
|
||||
import GoogleKeepIcon from './ic-gkeep.svg'
|
||||
import HashtagFilledIcon from './ic-hashtag-filled.svg'
|
||||
import HashtagIcon from './ic-hashtag.svg'
|
||||
import HashtagOffIcon from './ic-hashtag-off.svg'
|
||||
@@ -112,9 +116,9 @@ import LineWidthIcon from './ic-line-width.svg'
|
||||
import LinkIcon from './ic-link.svg'
|
||||
import LinkOffIcon from './ic-link-off.svg'
|
||||
import ListBulleted from './ic-list-bulleted.svg'
|
||||
import ListNumbered from './ic-list-numbered.svg'
|
||||
import ListedFilledIcon from './ic-listed-filled.svg'
|
||||
import ListedIcon from './ic-listed.svg'
|
||||
import ListNumbered from './ic-list-numbered.svg'
|
||||
import LockFilledIcon from './ic-lock-filled.svg'
|
||||
import LockIcon from './ic-lock.svg'
|
||||
import MarkdownIcon from './ic-markdown.svg'
|
||||
@@ -146,6 +150,8 @@ import PrintIcon from './ic-print.svg'
|
||||
import ProtectedIllustration from './il-protected.svg'
|
||||
import RedoIcon from './ic-redo.svg'
|
||||
import ReorderIcon from './ic-reorder.svg'
|
||||
import ReplaceAllIcon from './ic-replace-all.svg'
|
||||
import ReplaceIcon from './ic-replace.svg'
|
||||
import RestoreIcon from './ic-restore.svg'
|
||||
import RichTextIcon from './ic-text-rich.svg'
|
||||
import SafeIcon from './ic-safe.svg'
|
||||
@@ -164,6 +170,7 @@ import ShareIcon from './ic-share.svg'
|
||||
import ShortcutButtonIcon from './ic-shortcut-button.svg'
|
||||
import SignInIcon from './ic-signin.svg'
|
||||
import SignOutIcon from './ic-signout.svg'
|
||||
import SimplenoteIcon from './ic-simplenote.svg'
|
||||
import SNLogoAltIcon from './ic-standard-notes-2.svg'
|
||||
import SNLogoFull from './ic-sn-logo-full.svg'
|
||||
import SNLogoIcon from './ic-standard-notes.svg'
|
||||
@@ -201,12 +208,6 @@ import UserSwitch from './ic-user-switch.svg'
|
||||
import ViewIcon from './ic-view.svg'
|
||||
import WarningIcon from './ic-warning.svg'
|
||||
import WindowIcon from './ic-window.svg'
|
||||
import EvernoteIcon from './ic-evernote.svg'
|
||||
import GoogleKeepIcon from './ic-gkeep.svg'
|
||||
import SimplenoteIcon from './ic-simplenote.svg'
|
||||
import AegisIcon from './ic-aegis.svg'
|
||||
import ReplaceIcon from './ic-replace.svg'
|
||||
import ReplaceAllIcon from './ic-replace-all.svg'
|
||||
|
||||
export {
|
||||
AccessibilityIcon,
|
||||
@@ -232,6 +233,7 @@ export {
|
||||
AuthenticatorIcon,
|
||||
AuthenticatorVariantIcon,
|
||||
BackIosIcon,
|
||||
BackspaceIcon,
|
||||
BlockIcon,
|
||||
BlueDotIcon,
|
||||
BoldIcon,
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.51.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.75](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.74](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.73](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.72](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.71](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.70](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.51.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -30,18 +30,20 @@
|
||||
android:theme="@style/AppTheme"
|
||||
tools:targetApi="n">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name">Standard Notes</string>
|
||||
|
||||
<string name="asset_statements" translatable="false">
|
||||
[{
|
||||
\"include\": \"https://app.standardnotes.com/.well-known/assetlinks.json\"
|
||||
}]
|
||||
</string>
|
||||
</resources>
|
||||
|
||||
@@ -637,7 +637,7 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
|
||||
@@ -658,7 +658,7 @@ SPEC CHECKSUMS:
|
||||
MMKV: 9c4663aa7ca255d478ff10f2f5cb7d17c1651ccd
|
||||
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
|
||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
|
||||
RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a
|
||||
RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a
|
||||
React: d7433ccb6a8c36e4cbed59a73c0700fc83c3e98a
|
||||
|
||||
@@ -674,7 +674,11 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = HKF9BXSN95;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = StandardNotes/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -689,6 +693,7 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.standardnotes.standardnotes;
|
||||
PRODUCT_NAME = StandardNotes;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.51.65",
|
||||
"version": "3.51.77",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.220](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.219](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.218](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.217](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.216](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.215](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.214](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.213](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.212](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.211](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.210](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.208",
|
||||
"version": "1.4.220",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.166.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.166.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
@@ -170,7 +170,9 @@ describe('featuresService', () => {
|
||||
featuresService.getExperimentalFeatures = jest.fn().mockReturnValue([FeatureIdentifier.PlusEditor])
|
||||
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -191,7 +193,9 @@ describe('featuresService', () => {
|
||||
featuresService.getEnabledExperimentalFeatures = jest.fn().mockReturnValue([FeatureIdentifier.PlusEditor])
|
||||
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -226,9 +230,10 @@ describe('featuresService', () => {
|
||||
const spy = jest.spyOn(featuresService, 'notifyEvent' as never)
|
||||
|
||||
const newRoles = [...roles, RoleName.NAMES.ProUser]
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(spy.mock.calls[2][0]).toEqual(FeaturesEvent.DidPurchaseSubscription)
|
||||
expect(spy.mock.calls[0][0]).toEqual(FeaturesEvent.DidPurchaseSubscription)
|
||||
})
|
||||
|
||||
it('should not notify of subscription purchase on initial roles load after sign in', async () => {
|
||||
@@ -240,7 +245,8 @@ describe('featuresService', () => {
|
||||
const spy = jest.spyOn(featuresService, 'notifyEvent' as never)
|
||||
|
||||
const newRoles = [...roles, RoleName.NAMES.ProUser]
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
const triggeredEvents = spy.mock.calls.map((call) => call[0])
|
||||
expect(triggeredEvents).not.toContain(FeaturesEvent.DidPurchaseSubscription)
|
||||
@@ -252,8 +258,11 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserRoles, newRoles)
|
||||
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
expect(apiService.getUserFeatures).toHaveBeenCalledWith('123')
|
||||
})
|
||||
|
||||
@@ -263,7 +272,9 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserRoles, newRoles)
|
||||
expect(apiService.getUserFeatures).toHaveBeenCalledWith('123')
|
||||
})
|
||||
@@ -274,7 +285,9 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserFeatures, features)
|
||||
})
|
||||
|
||||
@@ -284,7 +297,9 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).toHaveBeenCalledTimes(2)
|
||||
expect(itemManager.createItem).toHaveBeenCalledWith(
|
||||
ContentType.Theme,
|
||||
@@ -328,7 +343,8 @@ describe('featuresService', () => {
|
||||
itemManager.getItems = jest.fn().mockReturnValue([existingItem])
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.changeComponent).toHaveBeenCalledWith(existingItem, expect.any(Function))
|
||||
})
|
||||
@@ -354,7 +370,9 @@ describe('featuresService', () => {
|
||||
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).toHaveBeenCalledWith(
|
||||
ContentType.Component,
|
||||
expect.objectContaining({
|
||||
@@ -401,7 +419,9 @@ describe('featuresService', () => {
|
||||
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.setItemsToBeDeleted).toHaveBeenCalledWith([existingItem])
|
||||
})
|
||||
|
||||
@@ -424,7 +444,9 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -447,7 +469,9 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(itemManager.createItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -455,10 +479,14 @@ describe('featuresService', () => {
|
||||
storageService.getValue = jest.fn().mockReturnValue(roles)
|
||||
const featuresService = createService()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
|
||||
const { didChangeRoles: didChangeRoles1 } = await featuresService.updateOnlineRoles(roles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles1)
|
||||
const { didChangeRoles: didChangeRoles2 } = await featuresService.updateOnlineRoles(roles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles2)
|
||||
const { didChangeRoles: didChangeRoles3 } = await featuresService.updateOnlineRoles(roles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles3)
|
||||
const { didChangeRoles: didChangeRoles4 } = await featuresService.updateOnlineRoles(roles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles4)
|
||||
expect(storageService.setValue).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
@@ -482,7 +510,9 @@ describe('featuresService', () => {
|
||||
const nativeFeature = featuresService['mapRemoteNativeFeatureToStaticFeature'](remoteFeature)
|
||||
featuresService['mapRemoteNativeFeatureToItem'] = jest.fn()
|
||||
featuresService.initializeFromDisk()
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService['mapRemoteNativeFeatureToItem']).toHaveBeenCalledWith(
|
||||
nativeFeature,
|
||||
expect.anything(),
|
||||
@@ -522,7 +552,11 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.CoreUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.MidnightTheme)).toBe(FeatureStatus.Entitled)
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.SuperEditor)).toBe(FeatureStatus.Entitled)
|
||||
@@ -554,7 +588,8 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.MidnightTheme)).toBe(FeatureStatus.NoUserSubscription)
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.PlusEditor)).toBe(FeatureStatus.NoUserSubscription)
|
||||
@@ -566,7 +601,8 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.ProUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.SuperEditor)).toBe(FeatureStatus.NotInCurrentPlan)
|
||||
})
|
||||
@@ -615,7 +651,8 @@ describe('featuresService', () => {
|
||||
} as never),
|
||||
])
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService.getFeatureStatus(themeFeature.identifier)).toBe(FeatureStatus.Entitled)
|
||||
expect(featuresService.getFeatureStatus(editorFeature.identifier)).toBe(FeatureStatus.InCurrentPlanButExpired)
|
||||
@@ -627,7 +664,8 @@ describe('featuresService', () => {
|
||||
it('feature status should be not entitled if no account or offline repo', async () => {
|
||||
const featuresService = createService()
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
|
||||
|
||||
@@ -652,7 +690,11 @@ describe('featuresService', () => {
|
||||
it('feature status for offline subscription', async () => {
|
||||
const featuresService = createService()
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.CoreUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
|
||||
featuresService.onlineRolesIncludePaidSubscription = jest.fn().mockReturnValue(false)
|
||||
@@ -680,7 +722,11 @@ describe('featuresService', () => {
|
||||
FeatureStatus.NoUserSubscription,
|
||||
)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.CoreUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
expect(featuresService.getFeatureStatus(FeatureIdentifier.DeprecatedFileSafe as FeatureIdentifier)).toBe(
|
||||
FeatureStatus.Entitled,
|
||||
@@ -690,12 +736,17 @@ describe('featuresService', () => {
|
||||
it('has paid subscription', async () => {
|
||||
const featuresService = createService()
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles: didChangeRoles1 } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles1)
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
expect(featuresService.hasPaidAnyPartyOnlineOrOfflineSubscription()).toBeFalsy
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles: didChangeRoles2 } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.CoreUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles2)
|
||||
|
||||
expect(featuresService.hasPaidAnyPartyOnlineOrOfflineSubscription()).toEqual(true)
|
||||
})
|
||||
@@ -703,7 +754,8 @@ describe('featuresService', () => {
|
||||
it('has paid subscription should be true if offline repo and signed into third party server', async () => {
|
||||
const featuresService = createService()
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
featuresService.hasOfflineRepo = jest.fn().mockReturnValue(true)
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
|
||||
@@ -725,7 +777,11 @@ describe('featuresService', () => {
|
||||
|
||||
const featuresService = createService()
|
||||
await featuresService.migrateFeatureRepoToUserSetting([extensionRepoItem])
|
||||
expect(settingsService.updateSetting).toHaveBeenCalledWith(SettingName.create(SettingName.NAMES.ExtensionKey).getValue(), extensionKey, true)
|
||||
expect(settingsService.updateSetting).toHaveBeenCalledWith(
|
||||
SettingName.create(SettingName.NAMES.ExtensionKey).getValue(),
|
||||
extensionKey,
|
||||
true,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -789,7 +845,8 @@ describe('featuresService', () => {
|
||||
it('should be false if core user checks for plus role', async () => {
|
||||
const featuresService = createService()
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
const hasPlusUserRole = featuresService.hasMinimumRole(RoleName.NAMES.PlusUser)
|
||||
|
||||
@@ -801,7 +858,11 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.PlusUser, RoleName.NAMES.CoreUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.PlusUser,
|
||||
RoleName.NAMES.CoreUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
const hasProUserRole = featuresService.hasMinimumRole(RoleName.NAMES.ProUser)
|
||||
|
||||
@@ -813,7 +874,11 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.ProUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
const hasCoreUserRole = featuresService.hasMinimumRole(RoleName.NAMES.CoreUser)
|
||||
|
||||
@@ -825,7 +890,11 @@ describe('featuresService', () => {
|
||||
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
|
||||
|
||||
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser])
|
||||
const { didChangeRoles } = await featuresService.updateOnlineRoles([
|
||||
RoleName.NAMES.ProUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
])
|
||||
await featuresService.fetchFeatures('123', didChangeRoles)
|
||||
|
||||
const hasProUserRole = featuresService.hasMinimumRole(RoleName.NAMES.ProUser)
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ export class SNFeaturesService
|
||||
const {
|
||||
payload: { userUuid, currentRoles },
|
||||
} = data as UserRolesChangedEvent
|
||||
await this.updateOnlineRolesAndFetchFeatures(userUuid, currentRoles)
|
||||
const { didChangeRoles } = await this.updateOnlineRoles(currentRoles)
|
||||
await this.fetchFeatures(userUuid, didChangeRoles)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -143,6 +144,9 @@ export class SNFeaturesService
|
||||
return
|
||||
}
|
||||
|
||||
const { userUuid, userRoles } = event.payload as MetaReceivedData
|
||||
const { didChangeRoles } = await this.updateOnlineRoles(userRoles.map((role) => role.name))
|
||||
|
||||
/**
|
||||
* All user data must be downloaded before we map features. Otherwise, feature mapping
|
||||
* may think a component doesn't exist and create a new one, when in reality the component
|
||||
@@ -152,11 +156,7 @@ export class SNFeaturesService
|
||||
return
|
||||
}
|
||||
|
||||
const { userUuid, userRoles } = event.payload as MetaReceivedData
|
||||
await this.updateOnlineRolesAndFetchFeatures(
|
||||
userUuid,
|
||||
userRoles.map((role) => role.name),
|
||||
)
|
||||
await this.fetchFeatures(userUuid, didChangeRoles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +400,9 @@ export class SNFeaturesService
|
||||
return hasFirstPartyOfflineSubscription || new URL(offlineRepo.content.offlineFeaturesUrl).hostname === 'localhost'
|
||||
}
|
||||
|
||||
async updateOnlineRolesAndFetchFeatures(userUuid: UuidString, roles: string[]): Promise<void> {
|
||||
async updateOnlineRoles(roles: string[]): Promise<{
|
||||
didChangeRoles: boolean
|
||||
}> {
|
||||
const previousRoles = this.onlineRoles
|
||||
|
||||
const userRolesChanged =
|
||||
@@ -409,13 +411,31 @@ export class SNFeaturesService
|
||||
const isInitialLoadRolesChange = previousRoles.length === 0 && userRolesChanged
|
||||
|
||||
if (!userRolesChanged && !this.needsInitialFeaturesUpdate) {
|
||||
return {
|
||||
didChangeRoles: false,
|
||||
}
|
||||
}
|
||||
|
||||
if (userRolesChanged && !isInitialLoadRolesChange) {
|
||||
if (this.onlineRolesIncludePaidSubscription()) {
|
||||
await this.notifyEvent(FeaturesEvent.DidPurchaseSubscription)
|
||||
}
|
||||
}
|
||||
|
||||
await this.setOnlineRoles(roles)
|
||||
|
||||
return {
|
||||
didChangeRoles: true,
|
||||
}
|
||||
}
|
||||
|
||||
async fetchFeatures(userUuid: UuidString, didChangeRoles: boolean): Promise<void> {
|
||||
if (!didChangeRoles && !this.needsInitialFeaturesUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
this.needsInitialFeaturesUpdate = false
|
||||
|
||||
await this.setOnlineRoles(roles)
|
||||
|
||||
const shouldDownloadRoleBasedFeatures = !this.hasOfflineRepo()
|
||||
|
||||
if (shouldDownloadRoleBasedFeatures) {
|
||||
@@ -426,12 +446,6 @@ export class SNFeaturesService
|
||||
await this.didDownloadFeatures(features)
|
||||
}
|
||||
}
|
||||
|
||||
if (userRolesChanged && !isInitialLoadRolesChange) {
|
||||
if (this.onlineRolesIncludePaidSubscription()) {
|
||||
await this.notifyEvent(FeaturesEvent.DidPurchaseSubscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setOnlineRoles(roles: string[]): Promise<void> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.166.17",
|
||||
"version": "2.166.18",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/styles
|
||||
|
||||
## [1.7.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
|
||||
|
||||
## [1.7.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/styles
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/styles",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.6",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -41,7 +41,7 @@ const getColorsForPrimaryVariant = (style: AlertButtonStyle) => {
|
||||
type AlertButton = {
|
||||
text: string
|
||||
style: AlertButtonStyle
|
||||
action: () => void
|
||||
action?: () => void
|
||||
primary?: boolean
|
||||
}
|
||||
|
||||
@@ -168,7 +168,9 @@ export class SKAlert {
|
||||
this.buttons.forEach((buttonDesc, index) => {
|
||||
const buttonElem = this.element.querySelector(`#button-${index}`) as HTMLButtonElement
|
||||
buttonElem.onclick = () => {
|
||||
buttonDesc.action && buttonDesc.action()
|
||||
if (buttonDesc.action) {
|
||||
buttonDesc.action()
|
||||
}
|
||||
this.dismiss()
|
||||
}
|
||||
if (index === 0) {
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/toast
|
||||
|
||||
## [1.3.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/toast
|
||||
|
||||
## [1.3.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-02-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/toast
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/toast",
|
||||
"version": "1.3.24",
|
||||
"version": "1.3.26",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.24.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.24.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.24.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.24.39",
|
||||
"version": "1.24.41",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,59 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.150.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Files uploaded to a protected Super note will also be marked as protected ([09fd659](https://github.com/standardnotes/app/commit/09fd65903b5e34c7f2b9f805e372efda383c7e4c))
|
||||
* Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal ([7773bec](https://github.com/standardnotes/app/commit/7773becdec0f5a623337b25a7fd218e5bcfebf13))
|
||||
|
||||
## [3.150.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
|
||||
|
||||
## [3.150.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.150.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,145 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "3.150.44",
|
||||
"title": "[3.150.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.43",
|
||||
"title": "[3.150.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.42",
|
||||
"title": "[3.150.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.41",
|
||||
"title": "[3.150.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.40",
|
||||
"title": "[3.150.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.39",
|
||||
"title": "[3.150.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.38",
|
||||
"title": "[3.150.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.37",
|
||||
"title": "[3.150.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.36",
|
||||
"title": "[3.150.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.35",
|
||||
"title": "[3.150.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Files uploaded to a protected Super note will also be marked as protected ([09fd659](https://github.com/standardnotes/app/commit/09fd65903b5e34c7f2b9f805e372efda383c7e4c))\n* Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal ([7773bec](https://github.com/standardnotes/app/commit/7773becdec0f5a623337b25a7fd218e5bcfebf13))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Files uploaded to a protected Super note will also be marked as protected (09fd659)",
|
||||
"Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal (7773bec)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Files uploaded to a protected Super note will also be marked as protected (09fd659)",
|
||||
"Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal (7773bec)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.34",
|
||||
"title": "[3.150.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Added backspace button on Super toolbar on Android as a workaround for backspace issue (08a20e2)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Added backspace button on Super toolbar on Android as a workaround for backspace issue (08a20e2)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.33",
|
||||
"title": "[3.150.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.150.32",
|
||||
"title": "[3.150.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.150.32",
|
||||
"version": "3.150.44",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@babel/plugin-transform-react-jsx": "^7.19.0",
|
||||
"@babel/preset-env": "*",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@lexical/react": "0.9.0",
|
||||
"@lexical/react": "0.9.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@reach/alert": "^0.18.0",
|
||||
"@reach/alert-dialog": "^0.18.0",
|
||||
@@ -84,7 +84,7 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"lexical": "0.9.0",
|
||||
"lexical": "0.9.1",
|
||||
"lint-staged": ">=13",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"minimatch": "^5.1.1",
|
||||
@@ -116,7 +116,7 @@
|
||||
"app/**/*.{js,ts,jsx,tsx,css,md}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/headless": "0.9.0",
|
||||
"@lexical/headless": "0.9.1",
|
||||
"contactjs": "2.1.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"webcredentials": {
|
||||
"apps": [
|
||||
"HKF9BXSN95.com.standardnotes.standardnotes"
|
||||
"HKF9BXSN95.com.standardnotes.standardnotes",
|
||||
"MVYA8NJCQ5.com.standardnotes.standardnotes"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
|
||||
"target": {
|
||||
"namespace": "web",
|
||||
"site": "https://app.standardnotes.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.standardnotes",
|
||||
"sha256_cert_fingerprints": [
|
||||
"58:3F:C4:1B:49:0C:38:0B:56:FB:E9:EE:47:38:1E:B4:EF:53:E0:37:19:A5:50:3F:C1:F2:9D:CD:8E:28:90:2A"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -131,6 +131,7 @@ const CreateAccount: FunctionComponent<Props> = ({
|
||||
ref={emailInputRef}
|
||||
type="email"
|
||||
value={email}
|
||||
spellcheck={false}
|
||||
/>
|
||||
<DecoratedPasswordInput
|
||||
className={{ container: 'mb-2' }}
|
||||
|
||||
@@ -102,9 +102,9 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
<div className="wrap my-0.5 font-bold">{user.email}</div>
|
||||
<span className="text-neutral">{application.getHost()}</span>
|
||||
</div>
|
||||
<div className="mb-2 flex items-start justify-between px-3">
|
||||
<div className="mb-2 flex items-start justify-between px-3 text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item">
|
||||
{isSyncingInProgress ? (
|
||||
<div className="flex items-center text-base font-semibold text-info lg:text-sm">
|
||||
<div className="flex items-center font-semibold text-info">
|
||||
<Spinner className="mr-2 h-5 w-5" />
|
||||
Syncing...
|
||||
</div>
|
||||
@@ -112,8 +112,8 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
<div className="flex items-start">
|
||||
<Icon type="check-circle" className={`mr-2 text-success ${MenuItemIconSize}`} />
|
||||
<div>
|
||||
<div className="text-base font-semibold text-success lg:text-sm">Last synced:</div>
|
||||
<div className="text-base text-text lg:text-sm">{lastSyncDate}</div>
|
||||
<div className="font-semibold text-success">Last synced:</div>
|
||||
<div className="text-text">{lastSyncDate}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -210,6 +210,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
onKeyDown={handleKeyDown}
|
||||
disabled={isSigningIn || isPrivateUsername}
|
||||
ref={emailInputRef}
|
||||
spellcheck={false}
|
||||
/>
|
||||
<DecoratedPasswordInput
|
||||
className={{ container: `mb-2 ${error ? 'border-danger' : null}` }}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
|
||||
return (
|
||||
<div className="relative">
|
||||
<MenuRadioButtonItem
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item"
|
||||
onClick={onClick}
|
||||
checked={descriptor.primary}
|
||||
>
|
||||
|
||||
@@ -51,7 +51,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
const currentFileIndex = otherFiles.findIndex((fileFromArray) => fileFromArray.uuid === currentFile.uuid)
|
||||
const currentFileIndex = otherFiles.findIndex((file) => file.uuid === currentFile.uuid)
|
||||
|
||||
switch (event.key) {
|
||||
case KeyboardKey.Left: {
|
||||
|
||||
@@ -73,6 +73,7 @@ export const IconNameToSvgMapping = {
|
||||
archive: icons.ArchiveIcon,
|
||||
asterisk: icons.AsteriskIcon,
|
||||
authenticator: icons.AuthenticatorIcon,
|
||||
backspace: icons.BackspaceIcon,
|
||||
bold: icons.BoldIcon,
|
||||
camera: icons.CameraIcon,
|
||||
check: icons.CheckIcon,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { SNNote } from '@standardnotes/snjs'
|
||||
import { useEffect } from 'react'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
||||
|
||||
type Props = {
|
||||
@@ -12,6 +13,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, filesController }: Props) => {
|
||||
const application = useApplication()
|
||||
const { isDraggingFiles, addDragTarget, removeDragTarget } = useFileDragNDrop()
|
||||
|
||||
useEffect(() => {
|
||||
@@ -22,6 +24,9 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
|
||||
tooltipText: 'Drop your files to upload and link them to the current note',
|
||||
callback: async (uploadedFile) => {
|
||||
await linkingController.linkItems(note, uploadedFile)
|
||||
void application.mutator.changeAndSaveItem(uploadedFile, (mutator) => {
|
||||
mutator.protected = note.protected
|
||||
})
|
||||
filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid)
|
||||
},
|
||||
})
|
||||
@@ -32,7 +37,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
|
||||
removeDragTarget(target)
|
||||
}
|
||||
}
|
||||
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController])
|
||||
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController, application.mutator])
|
||||
|
||||
return isDraggingFiles ? (
|
||||
// Required to block drag events to editor iframe
|
||||
|
||||
@@ -112,7 +112,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>Sign-in notification emails</Subtitle>
|
||||
<Subtitle>Mute sign-in notification emails</Subtitle>
|
||||
{isMuteSignInEmailsFeatureAvailable ? (
|
||||
<Text>
|
||||
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
|
||||
@@ -145,7 +145,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>Marketing notification emails</Subtitle>
|
||||
<Subtitle>Mute marketing notification emails</Subtitle>
|
||||
<Text>Disables email notifications with special deals and promotions.</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
|
||||
-1546
File diff suppressed because it is too large
Load Diff
@@ -1,390 +0,0 @@
|
||||
import {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
EditorConfig,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
NodeKey,
|
||||
SerializedLexicalNode,
|
||||
Spread,
|
||||
DecoratorNode,
|
||||
} from 'lexical'
|
||||
|
||||
import * as React from 'react'
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export type Cell = {
|
||||
colSpan: number
|
||||
json: string
|
||||
type: 'normal' | 'header'
|
||||
id: string
|
||||
width: number | null
|
||||
}
|
||||
|
||||
export type Row = {
|
||||
cells: Array<Cell>
|
||||
height: null | number
|
||||
id: string
|
||||
}
|
||||
|
||||
export type Rows = Array<Row>
|
||||
|
||||
export const cellHTMLCache: Map<string, string> = new Map()
|
||||
export const cellTextContentCache: Map<string, string> = new Map()
|
||||
|
||||
const emptyEditorJSON =
|
||||
'{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'
|
||||
|
||||
const plainTextEditorJSON = (text: string) => {
|
||||
return text === ''
|
||||
? emptyEditorJSON
|
||||
: `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":${text},"type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`
|
||||
}
|
||||
|
||||
const TableComponent = React.lazy(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
() => import('./TableComponent'),
|
||||
)
|
||||
|
||||
export function createUID(): string {
|
||||
return Math.random()
|
||||
.toString(36)
|
||||
.replace(/[^a-z]+/g, '')
|
||||
.substr(0, 5)
|
||||
}
|
||||
|
||||
function createCell(type: 'normal' | 'header'): Cell {
|
||||
return {
|
||||
colSpan: 1,
|
||||
id: createUID(),
|
||||
json: emptyEditorJSON,
|
||||
type,
|
||||
width: null,
|
||||
}
|
||||
}
|
||||
|
||||
export function createRow(): Row {
|
||||
return {
|
||||
cells: [],
|
||||
height: null,
|
||||
id: createUID(),
|
||||
}
|
||||
}
|
||||
|
||||
export type SerializedTableNode = Spread<
|
||||
{
|
||||
rows: Rows
|
||||
type: 'tablesheet'
|
||||
version: 1
|
||||
},
|
||||
SerializedLexicalNode
|
||||
>
|
||||
|
||||
export function extractRowsFromHTML(tableElem: HTMLTableElement): Rows {
|
||||
const rowElems = tableElem.querySelectorAll('tr')
|
||||
const rows: Rows = []
|
||||
for (let y = 0; y < rowElems.length; y++) {
|
||||
const rowElem = rowElems[y]
|
||||
const cellElems = rowElem.querySelectorAll('td,th')
|
||||
if (!cellElems || cellElems.length === 0) {
|
||||
continue
|
||||
}
|
||||
const cells: Array<Cell> = []
|
||||
for (let x = 0; x < cellElems.length; x++) {
|
||||
const cellElem = cellElems[x] as HTMLElement
|
||||
const isHeader = cellElem.nodeName === 'TH'
|
||||
const cell = createCell(isHeader ? 'header' : 'normal')
|
||||
cell.json = plainTextEditorJSON(JSON.stringify(cellElem.innerText.replace(/\n/g, ' ')))
|
||||
cells.push(cell)
|
||||
}
|
||||
const row = createRow()
|
||||
row.cells = cells
|
||||
rows.push(row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
function convertTableElement(domNode: HTMLElement): null | DOMConversionOutput {
|
||||
const rowElems = domNode.querySelectorAll('tr')
|
||||
if (!rowElems || rowElems.length === 0) {
|
||||
return null
|
||||
}
|
||||
const rows: Rows = []
|
||||
for (let y = 0; y < rowElems.length; y++) {
|
||||
const rowElem = rowElems[y]
|
||||
const cellElems = rowElem.querySelectorAll('td,th')
|
||||
if (!cellElems || cellElems.length === 0) {
|
||||
continue
|
||||
}
|
||||
const cells: Array<Cell> = []
|
||||
for (let x = 0; x < cellElems.length; x++) {
|
||||
const cellElem = cellElems[x] as HTMLElement
|
||||
const isHeader = cellElem.nodeName === 'TH'
|
||||
const cell = createCell(isHeader ? 'header' : 'normal')
|
||||
cell.json = plainTextEditorJSON(JSON.stringify(cellElem.innerText.replace(/\n/g, ' ')))
|
||||
cells.push(cell)
|
||||
}
|
||||
const row = createRow()
|
||||
row.cells = cells
|
||||
rows.push(row)
|
||||
}
|
||||
return { node: $createTableNode(rows) }
|
||||
}
|
||||
|
||||
export function exportTableCellsToHTML(
|
||||
rows: Rows,
|
||||
rect?: { startX: number; endX: number; startY: number; endY: number },
|
||||
): HTMLElement {
|
||||
const table = document.createElement('table')
|
||||
const colGroup = document.createElement('colgroup')
|
||||
const tBody = document.createElement('tbody')
|
||||
const firstRow = rows[0]
|
||||
|
||||
for (let x = rect != null ? rect.startX : 0; x < (rect != null ? rect.endX + 1 : firstRow.cells.length); x++) {
|
||||
const col = document.createElement('col')
|
||||
colGroup.append(col)
|
||||
}
|
||||
|
||||
for (let y = rect != null ? rect.startY : 0; y < (rect != null ? rect.endY + 1 : rows.length); y++) {
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const rowElem = document.createElement('tr')
|
||||
|
||||
for (let x = rect != null ? rect.startX : 0; x < (rect != null ? rect.endX + 1 : cells.length); x++) {
|
||||
const cell = cells[x]
|
||||
const cellElem = document.createElement(cell.type === 'header' ? 'th' : 'td')
|
||||
cellElem.innerHTML = cellHTMLCache.get(cell.json) || ''
|
||||
rowElem.appendChild(cellElem)
|
||||
}
|
||||
tBody.appendChild(rowElem)
|
||||
}
|
||||
|
||||
table.appendChild(colGroup)
|
||||
table.appendChild(tBody)
|
||||
return table
|
||||
}
|
||||
|
||||
export class TableNode extends DecoratorNode<JSX.Element> {
|
||||
__rows: Rows
|
||||
|
||||
static override getType(): string {
|
||||
return 'tablesheet'
|
||||
}
|
||||
|
||||
static override clone(node: TableNode): TableNode {
|
||||
return new TableNode(Array.from(node.__rows), node.__key)
|
||||
}
|
||||
|
||||
static override importJSON(serializedNode: SerializedTableNode): TableNode {
|
||||
return $createTableNode(serializedNode.rows)
|
||||
}
|
||||
|
||||
override exportJSON(): SerializedTableNode {
|
||||
return {
|
||||
rows: this.__rows,
|
||||
type: 'tablesheet',
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap | null {
|
||||
return {
|
||||
table: (_node: Node) => ({
|
||||
conversion: convertTableElement,
|
||||
priority: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
override exportDOM(): DOMExportOutput {
|
||||
return { element: exportTableCellsToHTML(this.__rows) }
|
||||
}
|
||||
|
||||
constructor(rows?: Rows, key?: NodeKey) {
|
||||
super(key)
|
||||
this.__rows = rows || []
|
||||
}
|
||||
|
||||
override createDOM(): HTMLElement {
|
||||
const div = document.createElement('div')
|
||||
div.style.display = 'contents'
|
||||
return div
|
||||
}
|
||||
|
||||
override updateDOM(): false {
|
||||
return false
|
||||
}
|
||||
|
||||
mergeRows(startX: number, startY: number, mergeRows: Rows): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
const endY = Math.min(rows.length, startY + mergeRows.length)
|
||||
for (let y = startY; y < endY; y++) {
|
||||
const row = rows[y]
|
||||
const mergeRow = mergeRows[y - startY]
|
||||
const cells = row.cells
|
||||
const cellsClone = Array.from(cells)
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
const mergeCells = mergeRow.cells
|
||||
const endX = Math.min(cells.length, startX + mergeCells.length)
|
||||
for (let x = startX; x < endX; x++) {
|
||||
const cell = cells[x]
|
||||
const mergeCell = mergeCells[x - startX]
|
||||
const cellClone = { ...cell, json: mergeCell.json, type: mergeCell.type }
|
||||
cellsClone[x] = cellClone
|
||||
}
|
||||
rows[y] = rowClone
|
||||
}
|
||||
}
|
||||
|
||||
updateCellJSON(x: number, y: number, json: string): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cell = cells[x]
|
||||
const cellsClone = Array.from(cells)
|
||||
const cellClone = { ...cell, json }
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
cellsClone[x] = cellClone
|
||||
rows[y] = rowClone
|
||||
}
|
||||
|
||||
updateCellType(x: number, y: number, type: 'header' | 'normal'): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cell = cells[x]
|
||||
const cellsClone = Array.from(cells)
|
||||
const cellClone = { ...cell, type }
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
cellsClone[x] = cellClone
|
||||
rows[y] = rowClone
|
||||
}
|
||||
|
||||
insertColumnAt(x: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
for (let y = 0; y < rows.length; y++) {
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cellsClone = Array.from(cells)
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
const type = (cells[x] || cells[x - 1]).type
|
||||
cellsClone.splice(x, 0, createCell(type))
|
||||
rows[y] = rowClone
|
||||
}
|
||||
}
|
||||
|
||||
deleteColumnAt(x: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
for (let y = 0; y < rows.length; y++) {
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cellsClone = Array.from(cells)
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
cellsClone.splice(x, 1)
|
||||
rows[y] = rowClone
|
||||
}
|
||||
}
|
||||
|
||||
addColumns(count: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
for (let y = 0; y < rows.length; y++) {
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cellsClone = Array.from(cells)
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
const type = cells[cells.length - 1].type
|
||||
for (let x = 0; x < count; x++) {
|
||||
cellsClone.push(createCell(type))
|
||||
}
|
||||
rows[y] = rowClone
|
||||
}
|
||||
}
|
||||
|
||||
insertRowAt(y: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
const prevRow = rows[y] || rows[y - 1]
|
||||
const cellCount = prevRow.cells.length
|
||||
const row = createRow()
|
||||
for (let x = 0; x < cellCount; x++) {
|
||||
const cell = createCell(prevRow.cells[x].type)
|
||||
row.cells.push(cell)
|
||||
}
|
||||
rows.splice(y, 0, row)
|
||||
}
|
||||
|
||||
deleteRowAt(y: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
rows.splice(y, 1)
|
||||
}
|
||||
|
||||
addRows(count: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
const prevRow = rows[rows.length - 1]
|
||||
const cellCount = prevRow.cells.length
|
||||
|
||||
for (let y = 0; y < count; y++) {
|
||||
const row = createRow()
|
||||
for (let x = 0; x < cellCount; x++) {
|
||||
const cell = createCell(prevRow.cells[x].type)
|
||||
row.cells.push(cell)
|
||||
}
|
||||
rows.push(row)
|
||||
}
|
||||
}
|
||||
|
||||
updateColumnWidth(x: number, width: number): void {
|
||||
const self = this.getWritable()
|
||||
const rows = self.__rows
|
||||
for (let y = 0; y < rows.length; y++) {
|
||||
const row = rows[y]
|
||||
const cells = row.cells
|
||||
const cellsClone = Array.from(cells)
|
||||
const rowClone = { ...row, cells: cellsClone }
|
||||
cellsClone[x].width = width
|
||||
rows[y] = rowClone
|
||||
}
|
||||
}
|
||||
|
||||
override decorate(_: LexicalEditor, config: EditorConfig): JSX.Element {
|
||||
return (
|
||||
<Suspense>
|
||||
<TableComponent nodeKey={this.__key} theme={config.theme} rows={this.__rows} />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function $isTableNode(node: LexicalNode | null | undefined): node is TableNode {
|
||||
return node instanceof TableNode
|
||||
}
|
||||
|
||||
export function $createTableNode(rows: Rows): TableNode {
|
||||
return new TableNode(rows)
|
||||
}
|
||||
|
||||
export function $createTableNodeWithDimensions(
|
||||
rowCount: number,
|
||||
columnCount: number,
|
||||
includeHeaders = true,
|
||||
): TableNode {
|
||||
const rows: Rows = []
|
||||
for (let y = 0; y < columnCount; y++) {
|
||||
const row: Row = createRow()
|
||||
rows.push(row)
|
||||
for (let x = 0; x < rowCount; x++) {
|
||||
row.cells.push(createCell(includeHeaders === true && (y === 0 || x === 0) ? 'header' : 'normal'))
|
||||
}
|
||||
}
|
||||
return new TableNode(rows)
|
||||
}
|
||||
@@ -6,7 +6,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import type { EditorConfig, ElementFormatType, LexicalEditor, LexicalNode, NodeKey, Spread } from 'lexical'
|
||||
import type {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
EditorConfig,
|
||||
ElementFormatType,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
NodeKey,
|
||||
Spread,
|
||||
} from 'lexical'
|
||||
|
||||
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
||||
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
|
||||
@@ -46,6 +56,15 @@ export type SerializedYouTubeNode = Spread<
|
||||
SerializedDecoratorBlockNode
|
||||
>
|
||||
|
||||
function convertYoutubeElement(domNode: HTMLElement): null | DOMConversionOutput {
|
||||
const videoID = domNode.getAttribute('data-lexical-youtube')
|
||||
if (videoID) {
|
||||
const node = $createYouTubeNode(videoID)
|
||||
return { node }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export class YouTubeNode extends DecoratorBlockNode {
|
||||
__id: string
|
||||
|
||||
@@ -72,6 +91,36 @@ export class YouTubeNode extends DecoratorBlockNode {
|
||||
}
|
||||
}
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
const element = document.createElement('iframe')
|
||||
element.setAttribute('data-lexical-youtube', this.__id)
|
||||
element.setAttribute('width', '560')
|
||||
element.setAttribute('height', '315')
|
||||
element.setAttribute('src', `https://www.youtube.com/embed/${this.__id}`)
|
||||
element.setAttribute('frameborder', '0')
|
||||
element.setAttribute(
|
||||
'allow',
|
||||
'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
||||
)
|
||||
element.setAttribute('allowfullscreen', 'true')
|
||||
element.setAttribute('title', 'YouTube video')
|
||||
return { element }
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap | null {
|
||||
return {
|
||||
iframe: (domNode: HTMLElement) => {
|
||||
if (!domNode.hasAttribute('data-lexical-youtube')) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
conversion: convertYoutubeElement,
|
||||
priority: 1,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
constructor(id: string, format?: ElementFormatType, key?: NodeKey) {
|
||||
super(format, key)
|
||||
this.__id = id
|
||||
|
||||
@@ -33,5 +33,7 @@ export const IS_IOS: boolean = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.
|
||||
|
||||
// Keep these in case we need to use them in the future.
|
||||
// export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
|
||||
// export const IS_CHROME: boolean = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
|
||||
export const IS_CHROME: boolean = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent)
|
||||
// export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
|
||||
|
||||
export const IS_APPLE_WEBKIT = CAN_USE_DOM && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME
|
||||
|
||||
+34
-8
@@ -16,7 +16,7 @@ import {
|
||||
URL_MATCHER,
|
||||
} from '@lexical/react/LexicalAutoEmbedPlugin'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
|
||||
import useModal from '../../Lexical/Hooks/useModal'
|
||||
@@ -174,6 +174,18 @@ function AutoEmbedMenu({
|
||||
)
|
||||
}
|
||||
|
||||
const debounce = (callback: (text: string) => void, delay: number) => {
|
||||
let timeoutId: number
|
||||
|
||||
return (text: string) => {
|
||||
window.clearTimeout(timeoutId)
|
||||
|
||||
timeoutId = window.setTimeout(() => {
|
||||
callback(text)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
export function AutoEmbedDialog({
|
||||
embedConfig,
|
||||
onClose,
|
||||
@@ -183,14 +195,26 @@ export function AutoEmbedDialog({
|
||||
}): JSX.Element {
|
||||
const [text, setText] = useState('')
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const [embedResult, setEmbedResult] = useState<EmbedMatchResult | null>(null)
|
||||
|
||||
const urlMatch = URL_MATCHER.exec(text)
|
||||
const embedResult = text != null && urlMatch != null ? embedConfig.parseUrl(text) : null
|
||||
const validateText = useMemo(
|
||||
() =>
|
||||
debounce((inputText: string) => {
|
||||
const urlMatch = URL_MATCHER.exec(inputText)
|
||||
if (embedConfig != null && inputText != null && urlMatch != null) {
|
||||
void Promise.resolve(embedConfig.parseUrl(inputText)).then((parseResult) => {
|
||||
setEmbedResult(parseResult)
|
||||
})
|
||||
} else if (embedResult != null) {
|
||||
setEmbedResult(null)
|
||||
}
|
||||
}, 200),
|
||||
[embedConfig, embedResult],
|
||||
)
|
||||
|
||||
const onClick = async () => {
|
||||
const result = await embedResult
|
||||
if (result != null) {
|
||||
embedConfig.insertNode(editor, result)
|
||||
const onClick = () => {
|
||||
if (embedResult != null) {
|
||||
embedConfig.insertNode(editor, embedResult)
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
@@ -205,7 +229,9 @@ export function AutoEmbedDialog({
|
||||
value={text}
|
||||
data-test-id={`${embedConfig.type}-embed-modal-url`}
|
||||
onChange={(e) => {
|
||||
setText(e.target.value)
|
||||
const { value } = e.target
|
||||
setText(value)
|
||||
validateText(value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+21
-48
@@ -1,55 +1,28 @@
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { AutoLinkPlugin as LexicalAutoLinkPlugin } from '@lexical/react/LexicalAutoLinkPlugin'
|
||||
import { COMMAND_PRIORITY_EDITOR, KEY_MODIFIER_COMMAND, $getSelection } from 'lexical'
|
||||
import { useEffect } from 'react'
|
||||
import { TOGGLE_LINK_COMMAND } from '@lexical/link'
|
||||
import { mergeRegister } from '@lexical/utils'
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
const URL_MATCHER =
|
||||
import { AutoLinkPlugin, createLinkMatcherWithRegExp } from '@lexical/react/LexicalAutoLinkPlugin'
|
||||
|
||||
const URL_REGEX =
|
||||
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
|
||||
|
||||
const EMAIL_REGEX =
|
||||
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
|
||||
|
||||
const MATCHERS = [
|
||||
(text: string) => {
|
||||
const match = URL_MATCHER.exec(text)
|
||||
if (match === null) {
|
||||
return null
|
||||
}
|
||||
const fullMatch = match[0]
|
||||
return {
|
||||
index: match.index,
|
||||
length: fullMatch.length,
|
||||
text: fullMatch,
|
||||
url: fullMatch.startsWith('http') ? fullMatch : `https://${fullMatch}`,
|
||||
}
|
||||
},
|
||||
createLinkMatcherWithRegExp(URL_REGEX, (text) => {
|
||||
return text.startsWith('http') ? text : `https://${text}`
|
||||
}),
|
||||
createLinkMatcherWithRegExp(EMAIL_REGEX, (text) => {
|
||||
return `mailto:${text}`
|
||||
}),
|
||||
]
|
||||
|
||||
export default function AutoLinkPlugin(): JSX.Element | null {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
useEffect(() => {
|
||||
return mergeRegister(
|
||||
editor.registerCommand(
|
||||
KEY_MODIFIER_COMMAND,
|
||||
(event: KeyboardEvent) => {
|
||||
const isCmdK = event.key === 'k' && !event.altKey && (event.metaKey || event.ctrlKey)
|
||||
if (isCmdK) {
|
||||
const selection = $getSelection()
|
||||
if (selection) {
|
||||
editor.dispatchCommand(TOGGLE_LINK_COMMAND, selection.getTextContent())
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<>
|
||||
<LexicalAutoLinkPlugin matchers={MATCHERS} />
|
||||
</>
|
||||
)
|
||||
export default function LexicalAutoLinkPlugin(): JSX.Element {
|
||||
return <AutoLinkPlugin matchers={MATCHERS} />
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-width: 4px 6px 4px 6px;
|
||||
border-left-color: #000;
|
||||
border-left-color: var(--sn-stylekit-contrast-color);
|
||||
display: block;
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
+36
-4
@@ -8,8 +8,11 @@
|
||||
|
||||
import {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
EditorConfig,
|
||||
ElementNode,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
NodeKey,
|
||||
SerializedElementNode,
|
||||
@@ -25,6 +28,14 @@ type SerializedCollapsibleContainerNode = Spread<
|
||||
SerializedElementNode
|
||||
>
|
||||
|
||||
export function convertDetailsElement(domNode: HTMLDetailsElement): DOMConversionOutput | null {
|
||||
const isOpen = domNode.open !== undefined ? domNode.open : true
|
||||
const node = $createCollapsibleContainerNode(isOpen)
|
||||
return {
|
||||
node,
|
||||
}
|
||||
}
|
||||
|
||||
export class CollapsibleContainerNode extends ElementNode {
|
||||
__open: boolean
|
||||
|
||||
@@ -41,10 +52,16 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
return new CollapsibleContainerNode(node.__open, node.__key)
|
||||
}
|
||||
|
||||
override createDOM(_: EditorConfig): HTMLElement {
|
||||
override createDOM(_: EditorConfig, editor: LexicalEditor): HTMLElement {
|
||||
const dom = document.createElement('details')
|
||||
dom.classList.add('Collapsible__container')
|
||||
dom.open = this.__open
|
||||
dom.addEventListener('toggle', () => {
|
||||
const open = editor.getEditorState().read(() => this.getOpen())
|
||||
if (open !== dom.open) {
|
||||
editor.update(() => this.toggleOpen())
|
||||
}
|
||||
})
|
||||
return dom
|
||||
}
|
||||
|
||||
@@ -56,8 +73,15 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
return false
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap | null {
|
||||
return {}
|
||||
static importDOM(): DOMConversionMap<HTMLDetailsElement> | null {
|
||||
return {
|
||||
details: () => {
|
||||
return {
|
||||
conversion: convertDetailsElement,
|
||||
priority: 1,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
static override importJSON(serializedNode: SerializedCollapsibleContainerNode): CollapsibleContainerNode {
|
||||
@@ -65,6 +89,14 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
return node
|
||||
}
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
const element = document.createElement('details')
|
||||
if (this.getLatest().__open) {
|
||||
element.setAttribute('open', '')
|
||||
}
|
||||
return { element }
|
||||
}
|
||||
|
||||
override exportJSON(): SerializedCollapsibleContainerNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
@@ -80,7 +112,7 @@ export class CollapsibleContainerNode extends ElementNode {
|
||||
}
|
||||
|
||||
getOpen(): boolean {
|
||||
return this.__open
|
||||
return this.getLatest().__open
|
||||
}
|
||||
|
||||
toggleOpen(): void {
|
||||
|
||||
+34
-2
@@ -6,7 +6,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { DOMConversionMap, EditorConfig, ElementNode, LexicalNode, SerializedElementNode, Spread } from 'lexical'
|
||||
import {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
EditorConfig,
|
||||
ElementNode,
|
||||
LexicalNode,
|
||||
SerializedElementNode,
|
||||
Spread,
|
||||
} from 'lexical'
|
||||
|
||||
type SerializedCollapsibleContentNode = Spread<
|
||||
{
|
||||
@@ -16,6 +25,13 @@ type SerializedCollapsibleContentNode = Spread<
|
||||
SerializedElementNode
|
||||
>
|
||||
|
||||
export function convertCollapsibleContentElement(): DOMConversionOutput | null {
|
||||
const node = $createCollapsibleContentNode()
|
||||
return {
|
||||
node,
|
||||
}
|
||||
}
|
||||
|
||||
export class CollapsibleContentNode extends ElementNode {
|
||||
static override getType(): string {
|
||||
return 'collapsible-content'
|
||||
@@ -36,7 +52,17 @@ export class CollapsibleContentNode extends ElementNode {
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap | null {
|
||||
return {}
|
||||
return {
|
||||
div: (domNode: HTMLElement) => {
|
||||
if (!domNode.hasAttribute('data-lexical-collapsible-content')) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
conversion: convertCollapsibleContentElement,
|
||||
priority: 2,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
static override importJSON(_serializedNode: SerializedCollapsibleContentNode): CollapsibleContentNode {
|
||||
@@ -47,6 +73,12 @@ export class CollapsibleContentNode extends ElementNode {
|
||||
return true
|
||||
}
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
const element = document.createElement('div')
|
||||
element.setAttribute('data-lexical-collapsible-content', 'true')
|
||||
return { element }
|
||||
}
|
||||
|
||||
override exportJSON(): SerializedCollapsibleContentNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
|
||||
+25
-16
@@ -10,10 +10,10 @@ import {
|
||||
$createParagraphNode,
|
||||
$isElementNode,
|
||||
DOMConversionMap,
|
||||
EditorConfig,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
ElementFormatType,
|
||||
ElementNode,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
NodeKey,
|
||||
RangeSelection,
|
||||
@@ -32,6 +32,13 @@ type SerializedCollapsibleTitleNode = Spread<
|
||||
SerializedElementNode
|
||||
>
|
||||
|
||||
export function convertSummaryElement(): DOMConversionOutput | null {
|
||||
const node = $createCollapsibleTitleNode()
|
||||
return {
|
||||
node,
|
||||
}
|
||||
}
|
||||
|
||||
export class CollapsibleTitleNode extends ElementNode {
|
||||
static override getType(): string {
|
||||
return 'collapsible-title'
|
||||
@@ -48,21 +55,11 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
return new CollapsibleTitleNode({ key: node.__key })
|
||||
}
|
||||
|
||||
override createDOM(_config: EditorConfig, editor: LexicalEditor): HTMLElement {
|
||||
override createDOM(): HTMLElement {
|
||||
const dom = document.createElement('summary')
|
||||
dom.classList.add('Collapsible__title')
|
||||
const format = this.getFormatType()
|
||||
dom.style.textAlign = format
|
||||
dom.onclick = (event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
editor.update(() => {
|
||||
const containerNode = this.getParentOrThrow()
|
||||
if ($isCollapsibleContainerNode(containerNode)) {
|
||||
containerNode.toggleOpen()
|
||||
}
|
||||
})
|
||||
}
|
||||
return dom
|
||||
}
|
||||
|
||||
@@ -71,13 +68,25 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
}
|
||||
|
||||
static importDOM(): DOMConversionMap | null {
|
||||
return {}
|
||||
return {
|
||||
summary: () => {
|
||||
return {
|
||||
conversion: convertSummaryElement,
|
||||
priority: 1,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
static override importJSON(serializedNode: SerializedCollapsibleTitleNode): CollapsibleTitleNode {
|
||||
return $createCollapsibleTitleNode(serializedNode.format)
|
||||
}
|
||||
|
||||
exportDOM(): DOMExportOutput {
|
||||
const element = document.createElement('summary')
|
||||
return { element }
|
||||
}
|
||||
|
||||
override exportJSON(): SerializedCollapsibleTitleNode {
|
||||
return {
|
||||
...super.exportJSON(),
|
||||
@@ -91,7 +100,7 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
return true
|
||||
}
|
||||
|
||||
override insertNewAfter(): ElementNode {
|
||||
override insertNewAfter(_: RangeSelection, restoreSelection = true): ElementNode {
|
||||
const containerNode = this.getParentOrThrow()
|
||||
|
||||
if (!$isCollapsibleContainerNode(containerNode)) {
|
||||
@@ -114,7 +123,7 @@ export class CollapsibleTitleNode extends ElementNode {
|
||||
}
|
||||
} else {
|
||||
const paragraph = $createParagraphNode()
|
||||
containerNode.insertAfter(paragraph)
|
||||
containerNode.insertAfter(paragraph, restoreSelection)
|
||||
return paragraph
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -16,8 +16,11 @@ import { $wrapNodeInElement, mergeRegister } from '@lexical/utils'
|
||||
import { useFilesController } from '@/Controllers/FilesControllerProvider'
|
||||
import { FilesControllerEvent } from '@/Controllers/FilesController'
|
||||
import { useLinkingController } from '@/Controllers/LinkingControllerProvider'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { SNNote } from '@standardnotes/snjs'
|
||||
|
||||
export default function FilePlugin(): JSX.Element | null {
|
||||
export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JSX.Element | null {
|
||||
const application = useApplication()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const filesController = useFilesController()
|
||||
const linkingController = useLinkingController()
|
||||
@@ -34,6 +37,9 @@ export default function FilePlugin(): JSX.Element | null {
|
||||
if (uploadedFile) {
|
||||
editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid)
|
||||
void linkingController.linkItemToSelectedItem(uploadedFile)
|
||||
void application.mutator.changeAndSaveItem(uploadedFile, (mutator) => {
|
||||
mutator.protected = currentNote.protected
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -70,7 +76,7 @@ export default function FilePlugin(): JSX.Element | null {
|
||||
COMMAND_PRIORITY_NORMAL,
|
||||
),
|
||||
)
|
||||
}, [editor, filesController, linkingController])
|
||||
}, [application.mutator, currentNote.protected, editor, filesController, linkingController])
|
||||
|
||||
useEffect(() => {
|
||||
const disposer = filesController.addEventObserver((event, data) => {
|
||||
|
||||
+1
-1
@@ -200,7 +200,7 @@ function useFloatingLinkEditorToolbar(editor: LexicalEditor, anchorElem: HTMLEle
|
||||
const linkParent = $findMatchingParent(node, $isLinkNode)
|
||||
const autoLinkParent = $findMatchingParent(node, $isAutoLinkNode)
|
||||
|
||||
if (linkParent != null || autoLinkParent != null) {
|
||||
if (linkParent != null && autoLinkParent == null) {
|
||||
setIsLink(true)
|
||||
} else {
|
||||
setIsLink(false)
|
||||
|
||||
+62
-4
@@ -4,7 +4,7 @@ import useModal from '../../Lexical/Hooks/useModal'
|
||||
import { InsertTableDialog } from '../../Plugins/TablePlugin'
|
||||
import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode'
|
||||
import { sanitizeUrl } from '../../Lexical/Utils/sanitizeUrl'
|
||||
import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
|
||||
import { $getSelection, $isRangeSelection, DELETE_CHARACTER_COMMAND, FORMAT_TEXT_COMMAND } from 'lexical'
|
||||
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { GetAlignmentBlocks } from '../Blocks/Alignment'
|
||||
@@ -23,11 +23,14 @@ import { GetPasswordBlock } from '../Blocks/Password'
|
||||
import { GetQuoteBlock } from '../Blocks/Quote'
|
||||
import { GetTableBlock } from '../Blocks/Table'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { classNames } from '@standardnotes/snjs'
|
||||
import { classNames, Platform } from '@standardnotes/snjs'
|
||||
import { SUPER_TOGGLE_SEARCH } from '@standardnotes/ui-services'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { GetRemoteImageBlock } from '../Blocks/RemoteImage'
|
||||
import { InsertRemoteImageDialog } from '../RemoteImagePlugin/RemoteImagePlugin'
|
||||
import { SKAlert } from '@standardnotes/styles'
|
||||
|
||||
const DontShowSuperAndroidBackspaceAlertKey = 'dontShowSuperAndroidBackspaceAlert'
|
||||
|
||||
const MobileToolbarPlugin = () => {
|
||||
const application = useApplication()
|
||||
@@ -37,8 +40,10 @@ const MobileToolbarPlugin = () => {
|
||||
const [isInEditor, setIsInEditor] = useState(false)
|
||||
const [isInToolbar, setIsInToolbar] = useState(false)
|
||||
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
const isAndroid = application.platform === Platform.Android
|
||||
|
||||
const toolbarRef = useRef<HTMLDivElement>(null)
|
||||
const backspaceButtonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const insertLink = useCallback(() => {
|
||||
const selection = $getSelection()
|
||||
@@ -156,7 +161,8 @@ const MobileToolbarPlugin = () => {
|
||||
|
||||
const handleFocus = () => setIsInEditor(true)
|
||||
const handleBlur = (event: FocusEvent) => {
|
||||
if (toolbarRef.current?.contains(event.relatedTarget as Node)) {
|
||||
const elementToBeFocused = event.relatedTarget as Node
|
||||
if (toolbarRef.current?.contains(elementToBeFocused) || elementToBeFocused === backspaceButtonRef.current) {
|
||||
return
|
||||
}
|
||||
setIsInEditor(false)
|
||||
@@ -179,7 +185,13 @@ const MobileToolbarPlugin = () => {
|
||||
const toolbar = toolbarRef.current
|
||||
|
||||
const handleFocus = () => setIsInToolbar(true)
|
||||
const handleBlur = () => setIsInToolbar(false)
|
||||
const handleBlur = (event: FocusEvent) => {
|
||||
const elementToBeFocused = event.relatedTarget as Node
|
||||
if (elementToBeFocused === backspaceButtonRef.current) {
|
||||
return
|
||||
}
|
||||
setIsInToolbar(false)
|
||||
}
|
||||
|
||||
toolbar.addEventListener('focus', handleFocus)
|
||||
toolbar.addEventListener('blur', handleBlur)
|
||||
@@ -190,6 +202,38 @@ const MobileToolbarPlugin = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAndroid) {
|
||||
return
|
||||
}
|
||||
|
||||
const dontShowAgain = application.getValue(DontShowSuperAndroidBackspaceAlertKey)
|
||||
|
||||
if (dontShowAgain) {
|
||||
return
|
||||
}
|
||||
|
||||
const alert = new SKAlert({
|
||||
title: 'Android backspace issue',
|
||||
text: 'There is a known issue with Super on Android where pressing the backspace will also delete the character after the cursor. We are working on a fix for this. Please use the backspace button in the toolbar as a workaround.',
|
||||
buttons: [
|
||||
{
|
||||
text: 'OK',
|
||||
style: 'default',
|
||||
},
|
||||
{
|
||||
text: "Don't show again",
|
||||
style: 'default',
|
||||
action: () => {
|
||||
application.setValue(DontShowSuperAndroidBackspaceAlertKey, true)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
alert.present()
|
||||
}, [application, isAndroid])
|
||||
|
||||
const isFocusInEditorOrToolbar = isInEditor || isInToolbar
|
||||
if (!isMobile || !isFocusInEditorOrToolbar) {
|
||||
return null
|
||||
@@ -223,6 +267,20 @@ const MobileToolbarPlugin = () => {
|
||||
>
|
||||
<Icon type="keyboard-close" size="medium" />
|
||||
</button>
|
||||
{isAndroid && (
|
||||
<button
|
||||
className="flex flex-shrink-0 items-center justify-center rounded border-l border-border py-3 px-3"
|
||||
aria-label="Backspace"
|
||||
ref={backspaceButtonRef}
|
||||
onClick={() => {
|
||||
editor.update(() => {
|
||||
editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Icon type="backspace" size="medium" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
+48
-32
@@ -2,13 +2,27 @@ import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
import { isDesktopApplication } from '@/Utils'
|
||||
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { classNames } from '@standardnotes/snjs'
|
||||
import { ElementFormatType, NodeKey } from 'lexical'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { $createFileNode } from '../EncryptedFilePlugin/Nodes/FileUtils'
|
||||
import { RemoteImageNode } from './RemoteImageNode'
|
||||
|
||||
const RemoteImageComponent = ({ src, alt, node }: { src: string; alt?: string; node: RemoteImageNode }) => {
|
||||
type Props = {
|
||||
src: string
|
||||
alt?: string
|
||||
node: RemoteImageNode
|
||||
className: Readonly<{
|
||||
base: string
|
||||
focus: string
|
||||
}>
|
||||
format: ElementFormatType | null
|
||||
nodeKey: NodeKey
|
||||
}
|
||||
|
||||
const RemoteImageComponent = ({ className, src, alt, node, format, nodeKey }: Props) => {
|
||||
const application = useApplication()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
@@ -51,37 +65,39 @@ const RemoteImageComponent = ({ src, alt, node }: { src: string; alt?: string; n
|
||||
const canShowSaveButton = application.isNativeMobileWeb() || isDesktopApplication()
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
|
||||
<img
|
||||
alt={alt}
|
||||
src={src}
|
||||
onLoad={() => {
|
||||
setDidImageLoad(true)
|
||||
}}
|
||||
/>
|
||||
{didImageLoad && canShowSaveButton && (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex items-center gap-2.5 rounded border border-border bg-default px-2.5 py-1.5',
|
||||
!isSaving && 'hover:bg-info hover:text-info-contrast',
|
||||
)}
|
||||
onClick={fetchAndUploadImage}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isSaving ? (
|
||||
<>
|
||||
<Spinner className="h-4 w-4" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon type="download" />
|
||||
Save image to Files
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
|
||||
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
|
||||
<img
|
||||
alt={alt}
|
||||
src={src}
|
||||
onLoad={() => {
|
||||
setDidImageLoad(true)
|
||||
}}
|
||||
/>
|
||||
{didImageLoad && canShowSaveButton && (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex items-center gap-2.5 rounded border border-border bg-default px-2.5 py-1.5',
|
||||
!isSaving && 'hover:bg-info hover:text-info-contrast',
|
||||
)}
|
||||
onClick={fetchAndUploadImage}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isSaving ? (
|
||||
<>
|
||||
<Spinner className="h-4 w-4" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon type="download" />
|
||||
Save image to Files
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</BlockWithAlignableContents>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+18
-3
@@ -1,5 +1,5 @@
|
||||
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
|
||||
import { DOMConversionMap, DOMExportOutput, LexicalNode, Spread } from 'lexical'
|
||||
import { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalEditor, LexicalNode, Spread } from 'lexical'
|
||||
import RemoteImageComponent from './RemoteImageComponent'
|
||||
|
||||
type SerializedRemoteImageNode = Spread<
|
||||
@@ -75,8 +75,23 @@ export class RemoteImageNode extends DecoratorBlockNode {
|
||||
return { element }
|
||||
}
|
||||
|
||||
decorate(): JSX.Element {
|
||||
return <RemoteImageComponent node={this} src={this.__src} alt={this.__alt} />
|
||||
decorate(_editor: LexicalEditor, config: EditorConfig): JSX.Element {
|
||||
const embedBlockTheme = config.theme.embedBlock || {}
|
||||
const className = {
|
||||
base: embedBlockTheme.base || '',
|
||||
focus: embedBlockTheme.focus || '',
|
||||
}
|
||||
|
||||
return (
|
||||
<RemoteImageComponent
|
||||
className={className}
|
||||
format={this.__format}
|
||||
nodeKey={this.getKey()}
|
||||
node={this}
|
||||
src={this.__src}
|
||||
alt={this.__alt}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,90 +6,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { INSERT_TABLE_COMMAND } from '@lexical/table'
|
||||
import {
|
||||
$createNodeSelection,
|
||||
$createParagraphNode,
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
$isRootOrShadowRoot,
|
||||
$setSelection,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
createCommand,
|
||||
EditorThemeClasses,
|
||||
Klass,
|
||||
LexicalCommand,
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
} from 'lexical'
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import invariant from '../Lexical/Shared/invariant'
|
||||
import { $createTableNodeWithDimensions, TableNode } from '../Lexical/Nodes/TableNode'
|
||||
import { LexicalEditor } from 'lexical'
|
||||
import { useState } from 'react'
|
||||
import Button from '../Lexical/UI/Button'
|
||||
import { DialogActions } from '../Lexical/UI/Dialog'
|
||||
import TextInput from '../Lexical/UI/TextInput'
|
||||
|
||||
export type InsertTableCommandPayload = Readonly<{
|
||||
columns: string
|
||||
rows: string
|
||||
includeHeaders?: boolean
|
||||
}>
|
||||
|
||||
export type CellContextShape = {
|
||||
cellEditorConfig: null | CellEditorConfig
|
||||
cellEditorPlugins: null | JSX.Element | Array<JSX.Element>
|
||||
set: (cellEditorConfig: null | CellEditorConfig, cellEditorPlugins: null | JSX.Element | Array<JSX.Element>) => void
|
||||
}
|
||||
|
||||
export type CellEditorConfig = Readonly<{
|
||||
namespace: string
|
||||
nodes?: ReadonlyArray<Klass<LexicalNode>>
|
||||
onError: (error: Error, editor: LexicalEditor) => void
|
||||
readOnly?: boolean
|
||||
theme?: EditorThemeClasses
|
||||
}>
|
||||
|
||||
export const INSERT_NEW_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload> =
|
||||
createCommand('INSERT_NEW_TABLE_COMMAND')
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: not sure why TS doesn't like using null as the value?
|
||||
export const CellContext: React.Context<CellContextShape> = createContext({
|
||||
cellEditorConfig: null,
|
||||
cellEditorPlugins: null,
|
||||
set: () => {
|
||||
// Empty
|
||||
},
|
||||
})
|
||||
|
||||
export function TableContext({ children }: { children: JSX.Element }) {
|
||||
const [contextValue, setContextValue] = useState<{
|
||||
cellEditorConfig: null | CellEditorConfig
|
||||
cellEditorPlugins: null | JSX.Element | Array<JSX.Element>
|
||||
}>({
|
||||
cellEditorConfig: null,
|
||||
cellEditorPlugins: null,
|
||||
})
|
||||
return (
|
||||
<CellContext.Provider
|
||||
value={useMemo(
|
||||
() => ({
|
||||
cellEditorConfig: contextValue.cellEditorConfig,
|
||||
cellEditorPlugins: contextValue.cellEditorPlugins,
|
||||
set: (cellEditorConfig, cellEditorPlugins) => {
|
||||
setContextValue({ cellEditorConfig, cellEditorPlugins })
|
||||
},
|
||||
}),
|
||||
[contextValue.cellEditorConfig, contextValue.cellEditorPlugins],
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</CellContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function InsertTableDialog({
|
||||
activeEditor,
|
||||
onClose,
|
||||
@@ -115,91 +38,3 @@ export function InsertTableDialog({
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function InsertNewTableDialog({
|
||||
activeEditor,
|
||||
onClose,
|
||||
}: {
|
||||
activeEditor: LexicalEditor
|
||||
onClose: () => void
|
||||
}): JSX.Element {
|
||||
const [rows, setRows] = useState('5')
|
||||
const [columns, setColumns] = useState('5')
|
||||
|
||||
const onClick = () => {
|
||||
activeEditor.dispatchCommand(INSERT_NEW_TABLE_COMMAND, { columns, rows })
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextInput label="No of rows" onChange={setRows} value={rows} />
|
||||
<TextInput label="No of columns" onChange={setColumns} value={columns} />
|
||||
<DialogActions data-test-id="table-model-confirm-insert">
|
||||
<Button onClick={onClick}>Confirm</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function TablePlugin({
|
||||
cellEditorConfig,
|
||||
children,
|
||||
}: {
|
||||
cellEditorConfig: CellEditorConfig
|
||||
children: JSX.Element | Array<JSX.Element>
|
||||
}): JSX.Element | null {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const cellContext = useContext(CellContext)
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([TableNode])) {
|
||||
invariant(false, 'TablePlugin: TableNode is not registered on editor')
|
||||
}
|
||||
|
||||
cellContext.set(cellEditorConfig, children)
|
||||
|
||||
return editor.registerCommand<InsertTableCommandPayload>(
|
||||
INSERT_TABLE_COMMAND,
|
||||
({ columns, rows, includeHeaders }) => {
|
||||
const selection = $getSelection()
|
||||
|
||||
if (!$isRangeSelection(selection)) {
|
||||
return true
|
||||
}
|
||||
|
||||
const focus = selection.focus
|
||||
const focusNode = focus.getNode()
|
||||
|
||||
if (focusNode !== null) {
|
||||
const tableNode = $createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders)
|
||||
|
||||
if ($isRootOrShadowRoot(focusNode)) {
|
||||
const target = focusNode.getChildAtIndex(focus.offset)
|
||||
|
||||
if (target !== null) {
|
||||
target.insertBefore(tableNode)
|
||||
} else {
|
||||
focusNode.append(tableNode)
|
||||
}
|
||||
|
||||
tableNode.insertBefore($createParagraphNode())
|
||||
} else {
|
||||
const topLevelNode = focusNode.getTopLevelElementOrThrow()
|
||||
topLevelNode.insertAfter(tableNode)
|
||||
}
|
||||
|
||||
tableNode.insertAfter($createParagraphNode())
|
||||
const nodeSelection = $createNodeSelection()
|
||||
nodeSelection.add(tableNode.getKey())
|
||||
$setSelection(nodeSelection)
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
}, [cellContext, cellEditorConfig, children, editor])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
spellcheck={spellcheck}
|
||||
>
|
||||
<ItemSelectionPlugin currentNote={note.current} />
|
||||
<FilePlugin />
|
||||
<FilePlugin currentNote={note.current} />
|
||||
<ItemBubblePlugin />
|
||||
<BlockPickerMenuPlugin />
|
||||
<GetMarkdownPlugin ref={getMarkdownPlugin} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ContentType, FileItem } from '@standardnotes/snjs'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
|
||||
export class FilePreviewModalController {
|
||||
@@ -6,7 +7,9 @@ export class FilePreviewModalController {
|
||||
currentFile: FileItem | undefined = undefined
|
||||
otherFiles: FileItem[] = []
|
||||
|
||||
constructor() {
|
||||
eventObservers: (() => void)[] = []
|
||||
|
||||
constructor(application: WebApplication) {
|
||||
makeObservable(this, {
|
||||
isOpen: observable,
|
||||
currentFile: observable,
|
||||
@@ -16,6 +19,35 @@ export class FilePreviewModalController {
|
||||
dismiss: action,
|
||||
setCurrentFile: action,
|
||||
})
|
||||
|
||||
this.eventObservers.push(
|
||||
application.streamItems(ContentType.File, ({ changed, removed }) => {
|
||||
if (!this.currentFile) {
|
||||
return
|
||||
}
|
||||
if (changed.includes(this.currentFile)) {
|
||||
this.setCurrentFile(this.currentFile)
|
||||
}
|
||||
if (removed.find((f) => f.uuid === this.currentFile?.uuid)) {
|
||||
if (!this.otherFiles.length) {
|
||||
this.dismiss()
|
||||
this.currentFile = undefined
|
||||
return
|
||||
}
|
||||
|
||||
const currentFileIndex = this.otherFiles.findIndex((file) => file.uuid === this.currentFile?.uuid)
|
||||
const nextFileIndex = currentFileIndex + 1 < this.otherFiles.length ? currentFileIndex + 1 : 0
|
||||
this.setCurrentFile(this.otherFiles[nextFileIndex])
|
||||
this.otherFiles = this.otherFiles.filter((file) => file.uuid !== this.currentFile?.uuid)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
deinit = () => {
|
||||
this.eventObservers.forEach((observer) => observer())
|
||||
;(this.currentFile as any) = undefined
|
||||
;(this.otherFiles as any) = undefined
|
||||
}
|
||||
|
||||
setCurrentFile = (currentFile: FileItem) => {
|
||||
|
||||
@@ -51,7 +51,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
readonly accountMenuController: AccountMenuController
|
||||
readonly actionsMenuController = new ActionsMenuController()
|
||||
readonly featuresController: FeaturesController
|
||||
readonly filePreviewModalController = new FilePreviewModalController()
|
||||
readonly filePreviewModalController: FilePreviewModalController
|
||||
readonly filesController: FilesController
|
||||
readonly noAccountWarningController: NoAccountWarningController
|
||||
readonly notesController: NotesController
|
||||
@@ -88,6 +88,8 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
|
||||
this.subscriptionManager = application.subscriptions
|
||||
|
||||
this.filePreviewModalController = new FilePreviewModalController(application)
|
||||
|
||||
this.quickSettingsMenuController = new QuickSettingsController(application, this.eventBus)
|
||||
|
||||
this.paneController = new PaneController(application, this.eventBus)
|
||||
@@ -201,6 +203,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
this.appEventObserverRemovers.forEach((remover) => remover())
|
||||
this.appEventObserverRemovers.length = 0
|
||||
;(this.device as unknown) = undefined
|
||||
this.filePreviewModalController.deinit()
|
||||
;(this.filePreviewModalController as unknown) = undefined
|
||||
;(this.preferencesController as unknown) = undefined
|
||||
;(this.quickSettingsMenuController as unknown) = undefined
|
||||
|
||||
@@ -46,6 +46,7 @@ module.exports = (env) => {
|
||||
{ from: 'src/index.html' },
|
||||
{ from: 'src/manifest.webmanifest' },
|
||||
{ from: 'src/robots.txt' },
|
||||
{ from: 'src/.well-known', to: '.well-known' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -3219,255 +3219,255 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/clipboard@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/clipboard@npm:0.9.0"
|
||||
"@lexical/clipboard@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/clipboard@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/html": 0.9.0
|
||||
"@lexical/list": 0.9.0
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/html": 0.9.1
|
||||
"@lexical/list": 0.9.1
|
||||
"@lexical/selection": 0.9.1
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: e420ad35ebccf8cf3adf389ca75af02ca1698ae91f839f2fad5e22438f7825dd67691f7c8ceb203b3db3eed40efc642b8e72311665d792a0fc836b894029db09
|
||||
lexical: 0.9.1
|
||||
checksum: 9bce904afd459b21b3f85f0a7969e2bc18b0ada6ec661fcf7d9f6ae734d10dd6d5bc95310f04153d81d809d856310cd703d9af3072fa929c5a4f84eb1e224357
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/code@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/code@npm:0.9.0"
|
||||
"@lexical/code@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/code@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
prismjs: ^1.27.0
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 84addf448018b2baecec52c069dc4f19df7f36b097ac837df4eb3ab35bcc0d17083d6290a6bbdd1bdf085c4ee9c5aa5e1909f47732af1c92d47fc1e108105091
|
||||
lexical: 0.9.1
|
||||
checksum: ca34880b763d87c7bf5570e83b3e5342fbdafe29cc815e7f39783e6f39ec781f44af33e0a08f286b222b06d1deaa610ad4f5974c650e56460b6d8b80b4825d23
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/dragon@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/dragon@npm:0.9.0"
|
||||
"@lexical/dragon@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/dragon@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 15c6e94c5a1ecf4661dd9453dfa2af1118040901873a5c185704c914fbd32ce0a45d1fabbbe5210e9b4cacdfc793e1fac57935d6976d60a3409268343086b3fe
|
||||
lexical: 0.9.1
|
||||
checksum: e899847eddfa2f7908536062478619266d5506193ba9ba4dd5c14418a4ec9b5cf42e2f44f965d25ff38a8d0581b331dd5841ec46cc36ff37703f4ec2d3710f8c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/hashtag@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/hashtag@npm:0.9.0"
|
||||
"@lexical/hashtag@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/hashtag@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: ad9159a9c06b577d979bd31691013d064407fb85b78067b9723ccd166c8e96a78570b7dc41f711133116de51c36ab99bb058aa568e28cb3dee6955638c981789
|
||||
lexical: 0.9.1
|
||||
checksum: 011c04773ee30c13708aee7aad8fabd12f2cc1b10a53f3876a56a683284ba98442560f8f367dbfaa5b03a9e779bcafd1eace4be6e8cee29ee46cf140b64bcc45
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/headless@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/headless@npm:0.9.0"
|
||||
"@lexical/headless@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/headless@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: d754eada955b8c77dba3303f408ee8aff8480225342cba512a85c50ad252fd7de36aca49347d62c1e0895685b3aa9b06dd3bbb1905baa74f54cce9ba300ebbf2
|
||||
lexical: 0.9.1
|
||||
checksum: b5e868cb6d2898d281ae0e8380d64553c6f22a18299a5ae699ea15264f38d7a875a0fca901ef985563b95e37490f9e1df737bb2608e5c6f490165fcbbf6409fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/history@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/history@npm:0.9.0"
|
||||
"@lexical/history@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/history@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 496e9028a532d15b6a65e66dee4e2dfebc91e973b3837ecb5bcd79a11734df8f301cc6c13bcea498c55936f6cb83c881416f3d16405e403a8e2937046ca18ac9
|
||||
lexical: 0.9.1
|
||||
checksum: 58d17e3e5e24a36ae8164c254a429f8d137251a143173145d4b9ca99e46812ae3243485908b8f3fc0572d0b811fece6b4294d70e95060a62cc83072bbb2caead
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/html@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/html@npm:0.9.0"
|
||||
"@lexical/html@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/html@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/selection": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 18f47f61bae7bdfed78f2f82cf14bc80655dcdb18359efb3378ae0987660d44570ce55c615b4ba05e2909a26f98be613586a4be892ad3c249ea86042a1d942e8
|
||||
lexical: 0.9.1
|
||||
checksum: ee16ac244554c8793e81e75253ca214d7c9af29384216fd0084b159fc151e743b132520b5d16c6d796d890d54126fabeb54d0508eb4423db72ecde5c0a4ecf74
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/link@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/link@npm:0.9.0"
|
||||
"@lexical/link@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/link@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 47cbf2c05e07662544d47075fea586b155b8909a54e55897de2d53a19562c5d8481efd996e16837ae56b404f432f3d1295f064789102191437d3b83e2608592e
|
||||
lexical: 0.9.1
|
||||
checksum: cfdf16e2a88c67fb95c4db2a26d769fa8792731c647300a14acfab6c0758982e8b55126bc41d567bda53afb804738ec281f398061f5ae314f6cb5126801d99e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/list@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/list@npm:0.9.0"
|
||||
"@lexical/list@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/list@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 890d4733b4e8a6460ab69c49968f46732e0fe6b3a8c04b84438fbfb8d92f31d736f3c97522d47830204a02204f47613dfd3a201b1f3c4cb7b554aa958fe3a0be
|
||||
lexical: 0.9.1
|
||||
checksum: 6c12d096fbdbf70af122d61068ca355d8cbd9d6beea5212bf6b36abbc36a700e54c8460569a34cd53eaae3d7695943dc45288eb609fd618fe1dfc2a014980421
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/mark@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/mark@npm:0.9.0"
|
||||
"@lexical/mark@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/mark@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 87c648b157571ee320988350410569a98bf55385e2c38d05062cb86a4e0b691a5fe358f426f886ccf853b48a2edc61b74001320170b9412313169e108cd29c55
|
||||
lexical: 0.9.1
|
||||
checksum: 3a164d042c855a38e862b22c8f42f1847eda97f36d06b10430ad2e8f4546ce30d884a9b508c242577c100c71a43fbcda9f891b0a54b4b8e1460f2776bef43545
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/markdown@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/markdown@npm:0.9.0"
|
||||
"@lexical/markdown@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/markdown@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/code": 0.9.0
|
||||
"@lexical/link": 0.9.0
|
||||
"@lexical/list": 0.9.0
|
||||
"@lexical/rich-text": 0.9.0
|
||||
"@lexical/text": 0.9.0
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/code": 0.9.1
|
||||
"@lexical/link": 0.9.1
|
||||
"@lexical/list": 0.9.1
|
||||
"@lexical/rich-text": 0.9.1
|
||||
"@lexical/text": 0.9.1
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 00171e7950661ce92e614e12b8285a518558c423c810479fd9d3283d038fcf4898115315aee93c9d40c998377be07764d66c967c730154937a9e35f55d12cf25
|
||||
lexical: 0.9.1
|
||||
checksum: f322f9609d774fc35e644e06b3ed4412a11475926d0e15f35f393bbf9f1a2fd2dce549f8fc3f1c9cf31a83fff40de86038c6f84a28a09ba09df643a98da09a71
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/offset@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/offset@npm:0.9.0"
|
||||
"@lexical/offset@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/offset@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: cba756182f35b961bbfc604238466cb4a3e80d45ae275da7ae852403b9a398ee04e1602cd2360ec6f762e8fff0df6b68be0dffbe965c37a0ab68b44565e3ea7a
|
||||
lexical: 0.9.1
|
||||
checksum: 3583a5acdc8ce8e16508546e72729eec4bdef509b5d0403a4434b2c10a449fea44c421d7026a54084b0a61687116a196dc999dba329e0555ed96f54d6f21c892
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/overflow@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/overflow@npm:0.9.0"
|
||||
"@lexical/overflow@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/overflow@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 0076ed1a2fa639318532b28e1739da7a922f09d88546b60adb5c6fb21183883dbaa4c0ca72af0295d0049304a19e9224fb5145eaff438f615d524bb51935b8da
|
||||
lexical: 0.9.1
|
||||
checksum: 25f15bff65b6d16ce2ba003cfd858e7625643d06aa3da205ab36ca2380b10a564c0b7727dfc01728fe97bade5075f96b232c8f56ef88c4080b0b256efc6f9aaf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/plain-text@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/plain-text@npm:0.9.0"
|
||||
"@lexical/plain-text@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/plain-text@npm:0.9.1"
|
||||
peerDependencies:
|
||||
"@lexical/clipboard": 0.9.0
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/utils": 0.9.0
|
||||
lexical: 0.9.0
|
||||
checksum: ab909fd7e58a2440a1c82c8d69abb44afa9580360c09c49e4712445137d8be44f3ebdda92fd1af9e547fdda619c3855cf31dd1b61a93f6a2bea7bd7d85e0c795
|
||||
"@lexical/clipboard": 0.9.1
|
||||
"@lexical/selection": 0.9.1
|
||||
"@lexical/utils": 0.9.1
|
||||
lexical: 0.9.1
|
||||
checksum: 8a5c22af4f5309aedce15eddfca43628cdd5deb21081d11aa2e5912d7e055c4292681819fcc257cb694a26a47e5b813aacfa3878199651bc258653f84a9eb729
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/react@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/react@npm:0.9.0"
|
||||
"@lexical/react@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/react@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/clipboard": 0.9.0
|
||||
"@lexical/code": 0.9.0
|
||||
"@lexical/dragon": 0.9.0
|
||||
"@lexical/hashtag": 0.9.0
|
||||
"@lexical/history": 0.9.0
|
||||
"@lexical/link": 0.9.0
|
||||
"@lexical/list": 0.9.0
|
||||
"@lexical/mark": 0.9.0
|
||||
"@lexical/markdown": 0.9.0
|
||||
"@lexical/overflow": 0.9.0
|
||||
"@lexical/plain-text": 0.9.0
|
||||
"@lexical/rich-text": 0.9.0
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/table": 0.9.0
|
||||
"@lexical/text": 0.9.0
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/yjs": 0.9.0
|
||||
"@lexical/clipboard": 0.9.1
|
||||
"@lexical/code": 0.9.1
|
||||
"@lexical/dragon": 0.9.1
|
||||
"@lexical/hashtag": 0.9.1
|
||||
"@lexical/history": 0.9.1
|
||||
"@lexical/link": 0.9.1
|
||||
"@lexical/list": 0.9.1
|
||||
"@lexical/mark": 0.9.1
|
||||
"@lexical/markdown": 0.9.1
|
||||
"@lexical/overflow": 0.9.1
|
||||
"@lexical/plain-text": 0.9.1
|
||||
"@lexical/rich-text": 0.9.1
|
||||
"@lexical/selection": 0.9.1
|
||||
"@lexical/table": 0.9.1
|
||||
"@lexical/text": 0.9.1
|
||||
"@lexical/utils": 0.9.1
|
||||
"@lexical/yjs": 0.9.1
|
||||
react-error-boundary: ^3.1.4
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
lexical: 0.9.1
|
||||
react: ">=17.x"
|
||||
react-dom: ">=17.x"
|
||||
checksum: f8df9f93785656d0326b7650d9ffab618f5e2cbf0aff00f419159d079b201f133ac6cbc4b92fc377f31edddf5463f9f127bc5e50ba370cfea0462794bee18003
|
||||
checksum: e4644c3902dc2f8b77bd2d38543380ea3425d2826e55ff7e6807934630547e9c76e9e3bdf44412d6e49437c900f5fce53ff179a1bdb26ddc988f15e23f54ac44
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/rich-text@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/rich-text@npm:0.9.0"
|
||||
"@lexical/rich-text@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/rich-text@npm:0.9.1"
|
||||
peerDependencies:
|
||||
"@lexical/clipboard": 0.9.0
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/utils": 0.9.0
|
||||
lexical: 0.9.0
|
||||
checksum: a546fb0d990e0db23d4337a2e9f279c05ba42f7364e460b621afc23cd176ec227c85bdf39ad731fd299b8cee81cfcfda17295495b9ad0ab195a4753bef7b6908
|
||||
"@lexical/clipboard": 0.9.1
|
||||
"@lexical/selection": 0.9.1
|
||||
"@lexical/utils": 0.9.1
|
||||
lexical: 0.9.1
|
||||
checksum: 45ff431534a5aeac66b8337d78e15448b64e7e55b1bfb472c927ca70a26b61c2b069a19b23c2e8a6da9ce6d64495fcf87f676b33c291abb031428d2a860b7da9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/selection@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/selection@npm:0.9.0"
|
||||
"@lexical/selection@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/selection@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 2801bfbeb284138a2803fdd3ebdbd4faca0b4f41b4772afbe600399be7287eae9f06e6e713cd4e32040ad51a0b3dc716a9b4cedba8898827ca0e02213a6ba5e6
|
||||
lexical: 0.9.1
|
||||
checksum: e5cff47f5aadbe69a67faa1ab7895a57a688e732e5e734e58104caccf90a17978de18f63c6baa04823ab21f4ce4ce5f72238ec482728f2d1ed65974a6b7c7d28
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/table@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/table@npm:0.9.0"
|
||||
"@lexical/table@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/table@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/utils": 0.9.0
|
||||
"@lexical/utils": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: 269f51a9fe73105d29c2b8440ccbb555f48591be3b7fc778638f55b278754c71272724e8868c63eddfd9e551b6dd4575afaf545a7475ffa48953fc50433a4e24
|
||||
lexical: 0.9.1
|
||||
checksum: 76a1b8f0bfcf78a332da201314fa3bc93628b44e39ec867a66566c2194ac05182fda2b1c212389be922241c2731cbdfffe830abfb223fd2ae7fd399e337d532b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/text@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/text@npm:0.9.0"
|
||||
"@lexical/text@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/text@npm:0.9.1"
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: dd6b28f8627f0896e78a3f0567dc0b8da96c8fcc267f14b8e1eeff00cdf50b48da7a2d4b0bea6227cbd1f626b852f03e2c57e97d616eba16e656974fd478f902
|
||||
lexical: 0.9.1
|
||||
checksum: 9f393e0c87c969a2fc5d6092b5818f6f9919e3d00ceea842f1c943b07d34bf4ef58bc48657b10f5fa109ff7e09429bde7dc2081eb8dfdf70ab5d6a1ff9d22328
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/utils@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/utils@npm:0.9.0"
|
||||
"@lexical/utils@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/utils@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/list": 0.9.0
|
||||
"@lexical/selection": 0.9.0
|
||||
"@lexical/table": 0.9.0
|
||||
"@lexical/list": 0.9.1
|
||||
"@lexical/selection": 0.9.1
|
||||
"@lexical/table": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
checksum: eca60b5f31aa6d7823abdd9414e26e851c677637b9639bff4debb4f252a609422b95b5aeec7784e3c155f66a72e7d9709a1c319fe644bb773c9a555d05f2e206
|
||||
lexical: 0.9.1
|
||||
checksum: 4c3f7dba5c508162b655741e2e965e61f535a470c5c770d4165267d02b44bbfc76831595d555dd77ca431c2d51f707166276759dc706bcf0d5ca701a8356f939
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lexical/yjs@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "@lexical/yjs@npm:0.9.0"
|
||||
"@lexical/yjs@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "@lexical/yjs@npm:0.9.1"
|
||||
dependencies:
|
||||
"@lexical/offset": 0.9.0
|
||||
"@lexical/offset": 0.9.1
|
||||
peerDependencies:
|
||||
lexical: 0.9.0
|
||||
lexical: 0.9.1
|
||||
yjs: ">=13.5.22"
|
||||
checksum: a7e076dc267adb4d8e64d597570f35b94c7037f577be2bbca0e5182e4658f0a61581aabe7ad40c953db9be09dcda3186cbd91c2404a249a768641d4e4dbdcdcc
|
||||
checksum: b2022bb4020d8602420a36a4fc2c13e5893195a75bfb9545a277390b5ac9326832bdb849d41f59a469b93dbf47459545a79e29e242c8f8365f2245a4f2f889cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5572,8 +5572,8 @@ __metadata:
|
||||
"@babel/plugin-transform-react-jsx": ^7.19.0
|
||||
"@babel/preset-env": "*"
|
||||
"@babel/preset-typescript": ^7.18.6
|
||||
"@lexical/headless": 0.9.0
|
||||
"@lexical/react": 0.9.0
|
||||
"@lexical/headless": 0.9.1
|
||||
"@lexical/react": 0.9.1
|
||||
"@pmmmwh/react-refresh-webpack-plugin": ^0.5.10
|
||||
"@reach/alert": ^0.18.0
|
||||
"@reach/alert-dialog": ^0.18.0
|
||||
@@ -5630,7 +5630,7 @@ __metadata:
|
||||
identity-obj-proxy: ^3.0.0
|
||||
jest: ^29.3.1
|
||||
jest-environment-jsdom: ^29.3.1
|
||||
lexical: 0.9.0
|
||||
lexical: 0.9.1
|
||||
lint-staged: ">=13"
|
||||
mini-css-extract-plugin: ^2.7.2
|
||||
minimatch: ^5.1.1
|
||||
@@ -15561,10 +15561,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lexical@npm:0.9.0":
|
||||
version: 0.9.0
|
||||
resolution: "lexical@npm:0.9.0"
|
||||
checksum: b4f33b77477f93f482bd491579a1fdc510add8b1aaf99071cb1c4015b281efee227e3de68e5dd3e3ba9713cdfc8ac0ea34d962883f02abdc21db10cc28552c9b
|
||||
"lexical@npm:0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "lexical@npm:0.9.1"
|
||||
checksum: 062b2c3c176072f1a4ba9cf930f1241986c739caa13831b209baa6db32289df70265bce796167864a39de8f75d250ff036b71846814d20c973c3f9610b123f21
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user