mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 11:39:21 +02:00
Compare commits
77 Commits
mobile-rel
...
3.3.10-and
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f42489ecb5 | ||
|
|
abd5fa00ed | ||
|
|
d66bda7adc | ||
|
|
a45b54dd85 | ||
|
|
a4d29f3e7a | ||
|
|
2a8ec8245f | ||
|
|
d85f32e1f2 | ||
|
|
adc7bb1d9e | ||
|
|
2dafde0117 | ||
|
|
7582a01b3f | ||
|
|
9081012673 | ||
|
|
bf83dfbd28 | ||
|
|
903b96e629 | ||
|
|
3e9007806e | ||
|
|
b6ec0091f0 | ||
|
|
726b4f5c0d | ||
|
|
2fdd3a2a85 | ||
|
|
88ecb58153 | ||
|
|
d084da2914 | ||
|
|
716de6f9eb | ||
|
|
dfc4cbe01f | ||
|
|
3a5b4c48f5 | ||
|
|
08b0fd498e | ||
|
|
3f365e7ee1 | ||
|
|
ebb5c078c9 | ||
|
|
f536def5fa | ||
|
|
2918b8f04b | ||
|
|
bb3b05aab0 | ||
|
|
2cdfe2c158 | ||
|
|
d1dfafe1da | ||
|
|
144febb3cc | ||
|
|
22b2d50139 | ||
|
|
121e521eb7 | ||
|
|
7ada9cacda | ||
|
|
5585d69dcf | ||
|
|
f5d73a0c8f | ||
|
|
4563739f6e | ||
|
|
79ed081c99 | ||
|
|
dc1c9a9d37 | ||
|
|
25c90537c8 | ||
|
|
b7aadf4b17 | ||
|
|
b9587e0fd5 | ||
|
|
38e529f5bf | ||
|
|
81e2d7738f | ||
|
|
380a681cc1 | ||
|
|
8c2a557618 | ||
|
|
7b98d2ede2 | ||
|
|
64c544a86e | ||
|
|
a048125a14 | ||
|
|
4c37e16e92 | ||
|
|
c448bc151b | ||
|
|
efcbefe1c8 | ||
|
|
339a15803c | ||
|
|
582a06c05a | ||
|
|
852f1ec31a | ||
|
|
2ca142566c | ||
|
|
9a13d112cb | ||
|
|
bc9cbbc1f6 | ||
|
|
129bf49204 | ||
|
|
a29acb6f30 | ||
|
|
c957094b1e | ||
|
|
8e5743f417 | ||
|
|
4ff42f1ae4 | ||
|
|
06c8f5367e | ||
|
|
f74d7cf37e | ||
|
|
b971b4d07a | ||
|
|
09fd5d8642 | ||
|
|
97447e8d6c | ||
|
|
c388045c68 | ||
|
|
db4d998705 | ||
|
|
011cc992e8 | ||
|
|
2c1d8b0eea | ||
|
|
65ae22e70e | ||
|
|
81da60333d | ||
|
|
aa572febd4 | ||
|
|
f7914c57c7 | ||
|
|
6dd16f6791 |
20
.github/workflows/android.publish.yml
vendored
20
.github/workflows/android.publish.yml
vendored
@@ -24,6 +24,22 @@ jobs:
|
||||
java-version: "17"
|
||||
architecture: "x64"
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: false
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
@@ -143,8 +159,8 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ steps.package-version.outputs.current-version}}-android
|
||||
name: Notesnook Android v${{ steps.package-version.outputs.current-version}}
|
||||
tag_name: ${{ steps.package-version.outputs.version}}-android
|
||||
name: Notesnook Android v${{ steps.package-version.outputs.version}}
|
||||
repository: streetwriters/notesnook
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
|
||||
23
.github/workflows/ios.publish.yml
vendored
23
.github/workflows/ios.publish.yml
vendored
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: "16.1"
|
||||
xcode-version: "26.1.1"
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.11
|
||||
with:
|
||||
key: ${{ runner.os }}-ccache-${{ hashFiles(format('apps/mobile/native/ios/Podfile.lock')) }}
|
||||
key: ${{ runner.os }}-ccache-${{ hashFiles(format('apps/mobile/ios/Podfile.lock')) }}
|
||||
max-size: 1500M
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-
|
||||
@@ -55,14 +55,13 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
id: pods-cache
|
||||
with:
|
||||
path: apps/mobile/native/ios/Pods
|
||||
key: ${{ runner.os }}-pods-${{ hashFiles('apps/mobile/native/ios/Podfile.lock') }}
|
||||
path: apps/mobile/ios/Pods
|
||||
key: ${{ runner.os }}-pods-${{ hashFiles('apps/mobile/ios/Podfile.lock') }}
|
||||
|
||||
- name: Install Pods
|
||||
run: |
|
||||
cd apps/mobile/native/ios
|
||||
bundle install
|
||||
RCT_NEW_ARCH_ENABLED=0 bundle exec pod install
|
||||
cd apps/mobile/ios
|
||||
pod install
|
||||
|
||||
- name: Check for typescript errors
|
||||
run: |
|
||||
@@ -79,9 +78,9 @@ jobs:
|
||||
bundle-identifier: org.streetwriters.notesnook
|
||||
scheme: Notesnook
|
||||
configuration: "Release"
|
||||
export-options: apps/mobile/native/ios/ExportOptions.plist
|
||||
project-path: apps/mobile/native/ios/Notesnook.xcodeproj
|
||||
workspace-path: apps/mobile/native/ios/Notesnook.xcworkspace
|
||||
export-options: apps/mobile/ios/ExportOptions.plist
|
||||
project-path: apps/mobile/ios/Notesnook.xcodeproj
|
||||
workspace-path: apps/mobile/ios/Notesnook.xcworkspace
|
||||
update-targets: |
|
||||
Notesnook
|
||||
Make Note
|
||||
@@ -117,5 +116,5 @@ jobs:
|
||||
name: Notesnook.zip
|
||||
path: |
|
||||
Notesnook.ipa
|
||||
apps/mobile/native/ios/**/*.map
|
||||
apps/mobile/ios/**/*.map
|
||||
packages/editor-mobile/sourcemaps/*.map
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -11,5 +11,5 @@
|
||||
"cache": true,
|
||||
"cacheStrategy": "content"
|
||||
},
|
||||
"git.rebaseWhenSync": true
|
||||
"git.rebaseWhenSync": false
|
||||
}
|
||||
|
||||
60
apps/desktop/package-lock.json
generated
60
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -19,7 +19,7 @@
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite-better-trigram": "0.0.5",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
@@ -6316,23 +6316,23 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/sqlite-better-trigram": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram/-/sqlite-better-trigram-0.0.3.tgz",
|
||||
"integrity": "sha512-r+SDeNk+K1VotrUx/w0TMu4FdjoDDPCqxhtfU50dSeTzfCFtkMBq1/E3gvsaiHi3UYtOa6siA8CK/gYlj75eCA==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram/-/sqlite-better-trigram-0.0.5.tgz",
|
||||
"integrity": "sha512-LDAuMzU2u9rhJyNmSxZyJiL3lWb8FjUqX/JZ3dwmo16Cl0zmkzFvGS94yfjLhwEce1yg1UE851Mz5UplWNhhpg==",
|
||||
"license": "Public Domain",
|
||||
"optionalDependencies": {
|
||||
"sqlite-better-trigram-darwin-arm64": "0.0.3",
|
||||
"sqlite-better-trigram-darwin-x64": "0.0.3",
|
||||
"sqlite-better-trigram-linux-arm64": "0.0.3",
|
||||
"sqlite-better-trigram-linux-x64": "0.0.3",
|
||||
"sqlite-better-trigram-windows-arm64": "0.0.3",
|
||||
"sqlite-better-trigram-windows-x64": "0.0.3"
|
||||
"sqlite-better-trigram-darwin-arm64": "0.0.5",
|
||||
"sqlite-better-trigram-darwin-x64": "0.0.5",
|
||||
"sqlite-better-trigram-linux-arm64": "0.0.5",
|
||||
"sqlite-better-trigram-linux-x64": "0.0.5",
|
||||
"sqlite-better-trigram-windows-arm64": "0.0.5",
|
||||
"sqlite-better-trigram-windows-x64": "0.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-darwin-arm64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-darwin-arm64/-/sqlite-better-trigram-darwin-arm64-0.0.3.tgz",
|
||||
"integrity": "sha512-ddL0K3v10bvqjuUgPSRfP1qFZAhO/43ruaWtWS5wu4DgikldGz1tBY+nQtdc+Xt0arrEMUyiEvOz7MuKHQNA0Q==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-darwin-arm64/-/sqlite-better-trigram-darwin-arm64-0.0.5.tgz",
|
||||
"integrity": "sha512-AVSfFHLDAuttkKtlXCMHbhtV6Dv8M48e4ucTfjMOpGenJTEWmXx3Goj/ZCHe3uLEW57qaely15AAl/xFOsGSLA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6343,9 +6343,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-darwin-x64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-darwin-x64/-/sqlite-better-trigram-darwin-x64-0.0.3.tgz",
|
||||
"integrity": "sha512-IQzFzXt0ib1MZ/UQm71X7RKv8G0vwepNqcmncWbK1R7LAWwpS/qaJ1NyrMOhL2AnfeQ33x1IkQE0GlamxFVC9A==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-darwin-x64/-/sqlite-better-trigram-darwin-x64-0.0.5.tgz",
|
||||
"integrity": "sha512-SxrQxI8r42+WN8DZxKuTQtSPmY7TLwtk5VoKbRN6HuNkb4rx8ZmmKeVmVA8u+hLkO4bzh2GtydOrlTdIgg5g5Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6356,9 +6356,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-linux-arm64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-linux-arm64/-/sqlite-better-trigram-linux-arm64-0.0.3.tgz",
|
||||
"integrity": "sha512-NTykERCSxAutAtJ9ITwOQBitC9zDg61tTDoLOGdMoDO2iAQNLuGApNS8qFvnCNl1wZy8yowI24rHYMCWLDIEIA==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-linux-arm64/-/sqlite-better-trigram-linux-arm64-0.0.5.tgz",
|
||||
"integrity": "sha512-jrm5jDUfozFEU3ErN2NosQHTi53XouXBrrhzoigAYWYhfk+oeMbN7MWdzv5y5eZNH5uUSRc8/Kj6XO8rx147mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6369,9 +6369,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-linux-x64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-linux-x64/-/sqlite-better-trigram-linux-x64-0.0.3.tgz",
|
||||
"integrity": "sha512-WoB2qMeFX6XGOWdTYCrF4imEBL5/a/UKqZpzWvPhtV92Ys47z4AIuwiWvekO8/5FRLYCyT9rJLXLSY2eUwOqLg==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-linux-x64/-/sqlite-better-trigram-linux-x64-0.0.5.tgz",
|
||||
"integrity": "sha512-AqiEOF7fK8MPcJKxWkkrF9DWSs5Ma/AlJfuWDrdxKzlLPaV4OWucL2zm/ShdEMGZ+Uz5EgTShGxnvLRRMmZYsQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6382,9 +6382,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-windows-arm64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-windows-arm64/-/sqlite-better-trigram-windows-arm64-0.0.3.tgz",
|
||||
"integrity": "sha512-hXIT+PkkJQ3vJ0+E8Zh2/BoOCQ2dRj6LmchjP7T5afpbKXI/lcI77Cgk3tPwe8s/Ywci6OyeJwcaFuBtf1CLhw==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-windows-arm64/-/sqlite-better-trigram-windows-arm64-0.0.5.tgz",
|
||||
"integrity": "sha512-HEY3GbhtINKyM2ztcBJ7GqpgHNDA0aBUFNnKsvAkorF08+0v0sRs3oZsoDBqDQY0tm90iUZVLoTqvtN3sLhaGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6395,9 +6395,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/sqlite-better-trigram-windows-x64": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-windows-x64/-/sqlite-better-trigram-windows-x64-0.0.3.tgz",
|
||||
"integrity": "sha512-dli7MaDld9k02Vx74Mrovxxy+SQ72GwaObvIOcbI7ATiKUvhRvjHZgmuIvgxF2DM8aRejFHgPIC4/GUJcjauEg==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sqlite-better-trigram-windows-x64/-/sqlite-better-trigram-windows-x64-0.0.5.tgz",
|
||||
"integrity": "sha512-JeLPlGc5yL7KL/uqAgcRb9wwS51xN1aezP18k2dvuxpXWmfGDc8e8/bpKYVVzUbnMF63MdpSa9PBch2NhT6CPA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
@@ -36,7 +36,7 @@
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite-better-trigram": "0.0.5",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
|
||||
@@ -47,7 +47,7 @@ module.exports = {
|
||||
simulator: {
|
||||
type: "ios.simulator",
|
||||
device: {
|
||||
type: "iPhone 12"
|
||||
type: "iPhone 17 Pro Max"
|
||||
}
|
||||
},
|
||||
attached: {
|
||||
@@ -59,7 +59,7 @@ module.exports = {
|
||||
emulator: {
|
||||
type: "android.emulator",
|
||||
device: {
|
||||
avdName: "Pixel_5_API_34"
|
||||
avdName: "Pixel_5_API_36"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -125,7 +125,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3081
|
||||
versionCode 3086
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.zoontek.rnbootsplash.RNBootSplash;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
RNBootSplash.init(this, R.style.BootTheme);
|
||||
|
||||
super.onCreate(null);
|
||||
@@ -32,19 +32,6 @@ public class MainActivity extends ReactActivity {
|
||||
startService(new Intent(getBaseContext(), OnClearFromRecentService.class));
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
final View rootView = findViewById(android.R.id.content);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
|
||||
Insets innerPadding = insets.getInsets(WindowInsetsCompat.Type.ime());
|
||||
rootView.setPadding(
|
||||
innerPadding.left,
|
||||
innerPadding.top,
|
||||
innerPadding.right,
|
||||
innerPadding.bottom
|
||||
);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,20 +47,20 @@ public class MainActivity extends ReactActivity {
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getReactInstanceManager().onConfigurationChanged(this, newConfig);
|
||||
Intent intent = new Intent("onConfigurationChanged");
|
||||
intent.putExtra("newConfig", newConfig);
|
||||
this.sendBroadcast(intent);
|
||||
}
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getReactHost().onConfigurationChanged(this);
|
||||
Intent intent = new Intent("onConfigurationChanged");
|
||||
intent.putExtra("newConfig", newConfig);
|
||||
this.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
||||
|
||||
import org.wonday.orientation.OrientationActivityLifecycle;
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
override val reactHost: ReactHost by lazy {
|
||||
getDefaultReactHost(
|
||||
@@ -22,6 +22,7 @@ class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
|
||||
loadReactNative(this)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
</style>
|
||||
|
||||
<!-- BootTheme should inherit from Theme.SplashScreen -->
|
||||
<style name="BootTheme" parent="Theme.SplashScreen">
|
||||
<style name="BootTheme" parent="Theme.BootSplash.EdgeToEdge">
|
||||
<item name="bootSplashBackground">@color/bootsplash_background</item>
|
||||
<item name="bootSplashLogo">@mipmap/bootsplash_logo_dark</item>
|
||||
<item name="android:navigationBarColor">@color/bootsplash_background</item>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
- Get an overview of your year with Notesnook with Wrapped 2025
|
||||
- Cell selection and column resizing in tables
|
||||
- Added support for uploading large files in background
|
||||
- Bug fixes and minor improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
Thank you for using Notesnook!
|
||||
|
||||
@@ -51,8 +51,9 @@ const { appLockEnabled, appLockMode } = SettingsService.get();
|
||||
if (appLockEnabled || appLockMode !== "none") {
|
||||
useUserStore.getState().lockApp(true);
|
||||
}
|
||||
|
||||
RNBootSplash.hide();
|
||||
RNBootSplash.hide({
|
||||
fade: true
|
||||
});
|
||||
const App = (props: { configureMode: "note-preview" }) => {
|
||||
useAppEvents();
|
||||
//@ts-ignore
|
||||
|
||||
@@ -70,7 +70,7 @@ export async function downloadFile(
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const totalChunks = Math.ceil(size / chunkSize);
|
||||
const totalChunks = Math.ceil(size / (chunkSize + ABYTES));
|
||||
const decryptedLength = size - totalChunks * ABYTES;
|
||||
|
||||
if (attachment && attachment.size !== decryptedLength) {
|
||||
|
||||
@@ -155,7 +155,7 @@ export async function checkUpload(
|
||||
expectedSize: number
|
||||
) {
|
||||
const size = await getUploadedFileSize(filename);
|
||||
const totalChunks = Math.ceil(size / chunkSize);
|
||||
const totalChunks = Math.ceil(size / (chunkSize + ABYTES));
|
||||
const decryptedLength = size - totalChunks * ABYTES;
|
||||
const error =
|
||||
size === 0
|
||||
|
||||
@@ -324,7 +324,9 @@ export const Signup = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
width: DDS.isTab ? "50%" : "100%",
|
||||
alignSelf: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
|
||||
@@ -42,9 +42,7 @@ export default function DelayLayout({
|
||||
...props
|
||||
}: IDelayLayoutProps) {
|
||||
const { colors } = useThemeColors();
|
||||
const loading = useDelayLayout(
|
||||
!props.delay || props.delay < 300 ? 0 : props.delay
|
||||
);
|
||||
const loading = useDelayLayout(props.delay === undefined ? 200 : props.delay);
|
||||
const Placeholder = placeholder[props.type || "default"];
|
||||
|
||||
return loading || props.wait ? (
|
||||
|
||||
@@ -136,12 +136,12 @@ const BaseDialog = ({
|
||||
backgroundColor: background
|
||||
? background
|
||||
: transparent
|
||||
? "transparent"
|
||||
: "rgba(0,0,0,0.3)"
|
||||
? "transparent"
|
||||
: "rgba(0,0,0,0.3)"
|
||||
}}
|
||||
>
|
||||
<KeyboardAvoidingView
|
||||
enabled={!floating && Platform.OS === "ios" && !avoidKeyboardResize}
|
||||
enabled={!floating && !avoidKeyboardResize}
|
||||
behavior="padding"
|
||||
>
|
||||
<BouncingView
|
||||
@@ -155,8 +155,8 @@ const BaseDialog = ({
|
||||
justifyContent: centered
|
||||
? "center"
|
||||
: bottom
|
||||
? "flex-end"
|
||||
: "flex-start"
|
||||
? "flex-end"
|
||||
: "flex-start"
|
||||
}
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -96,22 +96,22 @@ export class VaultDialog extends Component {
|
||||
this.title = !this.state.novault
|
||||
? strings.createVault()
|
||||
: this.state.fingerprintAccess
|
||||
? strings.vaultFingerprintUnlock()
|
||||
: this.state.revokeFingerprintAccess
|
||||
? strings.revokeVaultFingerprintUnlock()
|
||||
: this.state.changePassword
|
||||
? strings.changeVaultPassword()
|
||||
: this.state.noteLocked
|
||||
? this.state.deleteNote
|
||||
? strings.deleteNote()
|
||||
: this.state.share
|
||||
? strings.shareNote()
|
||||
: this.state.copyNote
|
||||
? strings.copyNote()
|
||||
: this.state.goToEditor
|
||||
? strings.goToEditor()
|
||||
: strings.goToEditor()
|
||||
: strings.lockNote();
|
||||
? strings.vaultFingerprintUnlock()
|
||||
: this.state.revokeFingerprintAccess
|
||||
? strings.revokeVaultFingerprintUnlock()
|
||||
: this.state.changePassword
|
||||
? strings.changeVaultPassword()
|
||||
: this.state.noteLocked
|
||||
? this.state.deleteNote
|
||||
? strings.deleteNote()
|
||||
: this.state.share
|
||||
? strings.shareNote()
|
||||
: this.state.copyNote
|
||||
? strings.copyNote()
|
||||
: this.state.goToEditor
|
||||
? strings.goToEditor()
|
||||
: strings.goToEditor()
|
||||
: strings.lockNote();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -337,12 +337,13 @@ export class VaultDialog extends Component {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
eSendEvent("vaultUpdated");
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
this.close();
|
||||
setTimeout(() => {
|
||||
this.close();
|
||||
}, 100);
|
||||
} else {
|
||||
ToastManager.show({
|
||||
heading: strings.passwordIncorrect(),
|
||||
@@ -353,9 +354,6 @@ export class VaultDialog extends Component {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
};
|
||||
|
||||
clearVault = async () => {
|
||||
@@ -874,24 +872,24 @@ export class VaultDialog extends Component {
|
||||
deleteVault
|
||||
? strings.delete()
|
||||
: clearVault
|
||||
? strings.clear()
|
||||
: fingerprintAccess
|
||||
? strings.enable()
|
||||
: this.state.revokeFingerprintAccess
|
||||
? strings.revoke()
|
||||
: changePassword
|
||||
? strings.change()
|
||||
: this.state.noteLocked
|
||||
? deleteNote
|
||||
? strings.delete()
|
||||
: share
|
||||
? strings.share()
|
||||
: goToEditor
|
||||
? strings.open()
|
||||
: strings.unlock()
|
||||
: !note.id
|
||||
? strings.create()
|
||||
: strings.lock()
|
||||
? strings.clear()
|
||||
: fingerprintAccess
|
||||
? strings.enable()
|
||||
: this.state.revokeFingerprintAccess
|
||||
? strings.revoke()
|
||||
: changePassword
|
||||
? strings.change()
|
||||
: this.state.noteLocked
|
||||
? deleteNote
|
||||
? strings.delete()
|
||||
: share
|
||||
? strings.share()
|
||||
: goToEditor
|
||||
? strings.open()
|
||||
: strings.unlock()
|
||||
: !note.id
|
||||
? strings.create()
|
||||
: strings.lock()
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -111,7 +111,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const containerWidth = widths
|
||||
? widths.sidebar + widths.list + widths.editor
|
||||
: dimensions.width;
|
||||
|
||||
const drawerPosition = 0;
|
||||
const homePosition = widths.sidebar;
|
||||
const editorPosition = widths.sidebar + widths.list;
|
||||
@@ -355,8 +354,9 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const finalValue = isSwipeLeft
|
||||
? translateX.value - velocityX / 40.0
|
||||
: translateX.value + velocityX / 40.0;
|
||||
const velocity = velocityX / 5000;
|
||||
const animationConfig: WithSpringConfig = {
|
||||
velocity: velocityX / 10,
|
||||
velocity: velocity,
|
||||
...SnappySpringConfig
|
||||
};
|
||||
|
||||
@@ -440,15 +440,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
width: containerWidth,
|
||||
flexDirection: "row"
|
||||
},
|
||||
deviceMode === "tablet"
|
||||
? {
|
||||
transform: [
|
||||
{
|
||||
translateX: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
: animatedStyles
|
||||
animatedStyles
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -994,7 +994,7 @@ const PricingPlanCard = ({
|
||||
PremiumService.get() &&
|
||||
(pricingPlans?.user?.subscription?.productId ===
|
||||
(product as RNIap.Subscription)?.productId ||
|
||||
pricingPlans?.user?.subscription?.productId.startsWith(
|
||||
pricingPlans?.user?.subscription?.productId?.startsWith(
|
||||
(product as RNIap.Subscription)?.productId
|
||||
));
|
||||
pricingPlans?.selectPlan(
|
||||
|
||||
@@ -352,7 +352,7 @@ const ProductItem = (props: {
|
||||
props.pricingPlans.isSubscribed() &&
|
||||
(props.pricingPlans.user?.subscription?.productId ===
|
||||
(product as RNIap.Subscription)?.productId ||
|
||||
props.pricingPlans.user?.subscription?.productId.startsWith(
|
||||
props.pricingPlans.user?.subscription?.productId?.startsWith(
|
||||
(product as RNIap.Subscription)?.productId
|
||||
) ||
|
||||
props.pricingPlans.user?.subscription?.productId ===
|
||||
|
||||
@@ -103,7 +103,7 @@ export function PlanLimits() {
|
||||
|
||||
if (
|
||||
user?.subscription.plan !== SubscriptionPlan.FREE &&
|
||||
user?.subscription.productId.includes("5year")
|
||||
user?.subscription.productId?.includes("5year")
|
||||
) {
|
||||
ToastManager.show({
|
||||
message:
|
||||
|
||||
@@ -58,6 +58,7 @@ import { Button } from "../ui/button";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { isFeatureAvailable } from "@notesnook/common";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
const renderScene = SceneMap({
|
||||
home: SideMenuHome,
|
||||
notebooks: SideMenuNotebooks,
|
||||
@@ -68,6 +69,7 @@ const renderScene = SceneMap({
|
||||
export const SideMenu = React.memo(
|
||||
function SideMenu() {
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [index, setIndex] = React.useState(
|
||||
SettingsService.getProperty("defaultSidebarTab")
|
||||
);
|
||||
@@ -87,10 +89,13 @@ export const SideMenu = React.memo(
|
||||
]);
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: colors.primary.background
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingLeft: insets.left
|
||||
}}
|
||||
>
|
||||
<TabView
|
||||
@@ -103,7 +108,7 @@ export const SideMenu = React.memo(
|
||||
animationEnabled={false}
|
||||
lazy
|
||||
/>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
@@ -351,9 +356,8 @@ const TabBar = (
|
||||
color={colors.primary.icon}
|
||||
onPress={async () => {
|
||||
if (props.navigationState.index === 1) {
|
||||
const notebooksFeature = await isFeatureAvailable(
|
||||
"notebooks"
|
||||
);
|
||||
const notebooksFeature =
|
||||
await isFeatureAvailable("notebooks");
|
||||
if (!notebooksFeature.isAllowed) {
|
||||
PaywallSheet.present(notebooksFeature);
|
||||
return;
|
||||
|
||||
@@ -104,15 +104,12 @@ function _MenuItem({
|
||||
const _onPress = () => {
|
||||
if (useSideBarDraggingStore.getState().dragging) return;
|
||||
if (item.onPress) return item.onPress(item);
|
||||
|
||||
Navigation.closeDrawer();
|
||||
if (useNavigationStore.getState().currentRoute !== item.id) {
|
||||
Navigation.navigate(item.id as keyof RouteParams, {
|
||||
canGoBack: false
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
Navigation.closeDrawer();
|
||||
}, 32);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { SubscriptionPlan } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import dayjs from "dayjs";
|
||||
import React from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { DraxProvider, DraxScrollView } from "react-native-drax";
|
||||
@@ -140,19 +141,35 @@ export function SideMenuHome() {
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
{(subscriptionType === SubscriptionPlan.FREE ||
|
||||
!subscriptionType ||
|
||||
!user) &&
|
||||
!SettingsService.getProperty("serverUrls") ? (
|
||||
{dayjs().month() !== 11 ? (
|
||||
<>
|
||||
{(subscriptionType === SubscriptionPlan.FREE ||
|
||||
!subscriptionType ||
|
||||
!user) &&
|
||||
!SettingsService.getProperty("serverUrls") ? (
|
||||
<Button
|
||||
title={pro.title}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
type="accent"
|
||||
onPress={pro.onPress}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
title={pro.title}
|
||||
title={`Wrapped ${dayjs().year()} 🎉`}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
type="accent"
|
||||
onPress={pro.onPress}
|
||||
bold
|
||||
type="secondaryAccented"
|
||||
onPress={() => {
|
||||
Navigation.navigate("Wrapped");
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -660,9 +660,25 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
}
|
||||
}
|
||||
|
||||
function isSubscribedToPlan(planId: string) {
|
||||
if (!PremiumService.get()) return false;
|
||||
return user?.subscription?.productId?.includes(planId);
|
||||
}
|
||||
|
||||
function isSubscribedToProduct(productId: string) {
|
||||
if (!PremiumService.get()) return false;
|
||||
return (
|
||||
user?.subscription?.productId &&
|
||||
(user?.subscription?.productId === productId ||
|
||||
user?.subscription?.productId?.startsWith(productId))
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
currentPlan: pricingPlans.find((p) => p.id === currentPlan),
|
||||
pricingPlans: plans,
|
||||
isSubscribedToPlan,
|
||||
isSubscribedToProduct,
|
||||
getStandardPrice: getLocalizedPrice,
|
||||
loadingPlans,
|
||||
loading,
|
||||
|
||||
@@ -29,7 +29,7 @@ import React, {
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Dimensions, LayoutChangeEvent, Platform, View } from "react-native";
|
||||
import { LayoutChangeEvent, View } from "react-native";
|
||||
import Orientation, {
|
||||
OrientationType,
|
||||
useDeviceOrientationChange
|
||||
@@ -39,7 +39,6 @@ import Animated, {
|
||||
useSharedValue,
|
||||
withTiming
|
||||
} from "react-native-reanimated";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { notesnook } from "../../e2e/test.ids";
|
||||
import { db } from "../common/database";
|
||||
import { FluidPanels } from "../components/fluid-panels";
|
||||
@@ -69,15 +68,13 @@ import {
|
||||
eOpenFullscreenEditor,
|
||||
eUnlockNote
|
||||
} from "../utils/events";
|
||||
import { editorRef, fluidTabsRef } from "../utils/global-refs";
|
||||
import { valueLimiter } from "../utils/functions";
|
||||
import { fluidTabsRef } from "../utils/global-refs";
|
||||
import { AppNavigationStack } from "./navigation-stack";
|
||||
import type { PaneWidths } from "../screens/editor/wrapper";
|
||||
|
||||
const MOBILE_SIDEBAR_SIZE = 0.85;
|
||||
|
||||
const valueLimiter = (value: number, min: number, max: number) => {
|
||||
return value < min ? min : value > max ? max : value;
|
||||
};
|
||||
|
||||
let SideMenu: any = null;
|
||||
let EditorWrapper: any = null;
|
||||
|
||||
@@ -93,7 +90,6 @@ export const FluidPanelsView = React.memo(
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const animatedOpacity = useSharedValue(0);
|
||||
const animatedTranslateY = useSharedValue(-9999);
|
||||
|
||||
const overlayRef = useRef<Animated.View>(null);
|
||||
const [orientation, setOrientation] = useState<OrientationType>(
|
||||
Orientation.getInitialOrientation()
|
||||
@@ -102,16 +98,21 @@ export const FluidPanelsView = React.memo(
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useDeviceOrientationChange((o) => {
|
||||
setOrientation(o);
|
||||
if (
|
||||
o !== OrientationType.UNKNOWN &&
|
||||
o !== OrientationType["FACE-UP"] &&
|
||||
o !== OrientationType["FACE-DOWN"] &&
|
||||
o !== OrientationType["PORTRAIT-UPSIDEDOWN"]
|
||||
) {
|
||||
setOrientation(o);
|
||||
}
|
||||
});
|
||||
|
||||
const isLandscape = orientation.includes("LANDSCAPE");
|
||||
|
||||
useEffect(() => {
|
||||
if (!appLoading) {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 500);
|
||||
}, 200);
|
||||
}
|
||||
}, [appLoading]);
|
||||
|
||||
@@ -147,16 +148,6 @@ export const FluidPanelsView = React.memo(
|
||||
if (deviceMode === "smallTablet") {
|
||||
fluidTabsRef.current?.openDrawer(false);
|
||||
}
|
||||
editorRef.current?.setNativeProps({
|
||||
style: {
|
||||
width: dimensions.width,
|
||||
zIndex: 999,
|
||||
paddingHorizontal:
|
||||
deviceMode === "smallTablet"
|
||||
? dimensions.width * 0
|
||||
: dimensions.width * 0.15
|
||||
}
|
||||
});
|
||||
}, [deviceMode, dimensions.width, setFullscreen]);
|
||||
|
||||
const closeFullScreenEditor = useCallback(
|
||||
@@ -169,17 +160,6 @@ export const FluidPanelsView = React.memo(
|
||||
editorController.current?.commands.updateSettings({
|
||||
fullscreen: false
|
||||
});
|
||||
editorRef.current?.setNativeProps({
|
||||
style: {
|
||||
width:
|
||||
_deviceMode === "smallTablet"
|
||||
? dimensions.width -
|
||||
valueLimiter(dimensions.width * 0.4, 300, 450)
|
||||
: dimensions.width * 0.48,
|
||||
zIndex: null,
|
||||
paddingHorizontal: 0
|
||||
}
|
||||
});
|
||||
if (_deviceMode === "smallTablet") {
|
||||
fluidTabsRef.current?.goToIndex(1, false);
|
||||
}
|
||||
@@ -221,38 +201,8 @@ export const FluidPanelsView = React.memo(
|
||||
(current: string | null, size: { width: number; height: number }) => {
|
||||
setDeviceModeState(current);
|
||||
|
||||
const needsLayout = current !== deviceMode;
|
||||
|
||||
if (fullscreen && current !== "mobile") {
|
||||
// Runs after size is set via state.
|
||||
setTimeout(() => {
|
||||
editorRef.current?.setNativeProps({
|
||||
style: {
|
||||
width: size.width,
|
||||
zIndex: 999,
|
||||
paddingHorizontal:
|
||||
current === "smallTablet" ? size.width * 0 : size.width * 0.15
|
||||
}
|
||||
});
|
||||
}, 1);
|
||||
} else {
|
||||
if (fullscreen) eSendEvent(eCloseFullscreenEditor, current);
|
||||
editorRef.current?.setNativeProps({
|
||||
style: {
|
||||
position: "relative",
|
||||
width:
|
||||
current === "tablet"
|
||||
? size.width * 0.48
|
||||
: current === "smallTablet"
|
||||
? size.width - valueLimiter(size.width * 0.4, 300, 450)
|
||||
: size.width,
|
||||
zIndex: null,
|
||||
paddingHorizontal: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!needsLayout) {
|
||||
return;
|
||||
if (fullscreen && current === "mobile") {
|
||||
eSendEvent(eCloseFullscreenEditor, current);
|
||||
}
|
||||
|
||||
const state = getAppState();
|
||||
@@ -264,8 +214,6 @@ export const FluidPanelsView = React.memo(
|
||||
case "smallTablet":
|
||||
if (!fullscreen) {
|
||||
fluidTabsRef.current?.closeDrawer(false);
|
||||
} else {
|
||||
fluidTabsRef.current?.openDrawer(false);
|
||||
}
|
||||
break;
|
||||
case "mobile":
|
||||
@@ -274,46 +222,52 @@ export const FluidPanelsView = React.memo(
|
||||
editorState().movedAway === false &&
|
||||
useTabStore.getState().getCurrentNoteId()
|
||||
) {
|
||||
fluidTabsRef.current?.goToIndex(2, false);
|
||||
fluidTabsRef.current?.goToPage("editor", false);
|
||||
} else {
|
||||
fluidTabsRef.current?.goToIndex(1, false);
|
||||
fluidTabsRef.current?.goToPage(
|
||||
fluidTabsRef.current?.page(),
|
||||
false
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}, 32);
|
||||
}, 0);
|
||||
},
|
||||
[deviceMode, fullscreen, setDeviceModeState]
|
||||
);
|
||||
|
||||
const checkDeviceType = React.useCallback(
|
||||
(size: { width: number; height: number }) => {
|
||||
setDimensions({
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
if (DDS.width === size.width && orientation === DDS.orientation) return;
|
||||
DDS.setSize(size, orientation);
|
||||
const nextDeviceMode = DDS.isLargeTablet()
|
||||
? "tablet"
|
||||
: DDS.isSmallTab
|
||||
? "smallTablet"
|
||||
: "mobile";
|
||||
|
||||
setDeviceMode(nextDeviceMode, size);
|
||||
},
|
||||
[orientation, setDeviceMode, setDimensions]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (orientation !== "UNKNOWN") {
|
||||
checkDeviceType(dimensions);
|
||||
}
|
||||
}, [orientation, dimensions]);
|
||||
|
||||
const _onLayout = React.useCallback(
|
||||
(event: LayoutChangeEvent) => {
|
||||
const size = event?.nativeEvent?.layout;
|
||||
if (!size || (size.width === dimensions.width && deviceMode !== null)) {
|
||||
DDS.setSize(size, orientation);
|
||||
setDeviceMode(deviceMode, size);
|
||||
checkDeviceType(size);
|
||||
return;
|
||||
setDimensions({
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
if (size.width > size.height) {
|
||||
setOrientation(OrientationType["LANDSCAPE-RIGHT"]);
|
||||
} else {
|
||||
setOrientation(OrientationType["PORTRAIT"]);
|
||||
}
|
||||
|
||||
checkDeviceType(size);
|
||||
},
|
||||
[
|
||||
checkDeviceType,
|
||||
@@ -324,11 +278,6 @@ export const FluidPanelsView = React.memo(
|
||||
]
|
||||
);
|
||||
|
||||
if (!deviceMode) {
|
||||
const size = Dimensions.get("window");
|
||||
checkDeviceType(size);
|
||||
}
|
||||
|
||||
const PANE_OFFSET = useMemo(
|
||||
() => ({
|
||||
mobile: {
|
||||
@@ -356,7 +305,7 @@ export const FluidPanelsView = React.memo(
|
||||
[dimensions.width, fullscreen]
|
||||
);
|
||||
|
||||
const PANE_WIDTHS = useMemo(
|
||||
const PANE_WIDTHS: PaneWidths = useMemo(
|
||||
() => ({
|
||||
mobile: {
|
||||
sidebar: dimensions.width * MOBILE_SIDEBAR_SIZE,
|
||||
@@ -382,6 +331,7 @@ export const FluidPanelsView = React.memo(
|
||||
(scrollOffset: number) => {
|
||||
if (!deviceMode) return;
|
||||
hideAllTooltips();
|
||||
|
||||
if (
|
||||
scrollOffset >
|
||||
PANE_OFFSET[deviceMode as keyof typeof PANE_OFFSET].sidebar - 10
|
||||
@@ -409,7 +359,7 @@ export const FluidPanelsView = React.memo(
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!isLoading) {
|
||||
if (!isLoading && !SideMenu && !EditorWrapper) {
|
||||
SideMenu = require("../components/side-menu").SideMenu;
|
||||
EditorWrapper = require("../screens/editor/wrapper").EditorWrapper;
|
||||
}
|
||||
@@ -421,15 +371,7 @@ export const FluidPanelsView = React.memo(
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
marginRight:
|
||||
orientation === "LANDSCAPE-RIGHT" && Platform.OS === "ios"
|
||||
? insets.right
|
||||
: 0,
|
||||
marginLeft:
|
||||
orientation === "LANDSCAPE-LEFT" && Platform.OS === "ios"
|
||||
? insets.left
|
||||
: 0
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
{deviceMode && PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS] ? (
|
||||
@@ -503,9 +445,7 @@ export const FluidPanelsView = React.memo(
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingLeft: insets.left,
|
||||
paddingRight: insets.right
|
||||
paddingBottom: insets.bottom
|
||||
}}
|
||||
>
|
||||
<AppNavigationStack />
|
||||
|
||||
@@ -273,6 +273,7 @@ let Settings: any = null;
|
||||
let ManageTags: any = null;
|
||||
let AddReminder: any = null;
|
||||
let PayWall: any = null;
|
||||
let Wrapped: any = null;
|
||||
export const RootNavigation = () => {
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
@@ -384,6 +385,14 @@ export const RootNavigation = () => {
|
||||
return PayWall;
|
||||
}}
|
||||
/>
|
||||
|
||||
<RootStack.Screen
|
||||
name="Wrapped"
|
||||
getComponent={() => {
|
||||
Wrapped = Wrapped || require("../screens/wrapped").default;
|
||||
return Wrapped;
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
@@ -62,6 +62,7 @@ import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { i18n } from "@lingui/core";
|
||||
import { useVaultStatus } from "../../hooks/use-vault-status";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
|
||||
const style: ViewStyle = {
|
||||
height: "100%",
|
||||
@@ -231,7 +232,6 @@ const useLockedNoteHandler = () => {
|
||||
const unlockWithBiometrics = async () => {
|
||||
try {
|
||||
if (!tabRef.current?.session?.noteLocked || !tabRef.current) return;
|
||||
console.log("Trying to unlock with biometrics...");
|
||||
const credentials = await BiometricService.getCredentials(
|
||||
"Unlock note",
|
||||
"Unlock note to open it in editor."
|
||||
@@ -320,12 +320,16 @@ const useLockedNoteHandler = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const unlock = () => {
|
||||
const unlock = (forced?: boolean) => {
|
||||
const isMovedAway =
|
||||
useSettingStore.getState().deviceMode !== "mobile"
|
||||
? false
|
||||
: editorState().movedAway;
|
||||
if (
|
||||
(tabRef.current?.session?.locked,
|
||||
tabRef.current?.session?.locked &&
|
||||
useTabStore.getState().biometryAvailable &&
|
||||
useTabStore.getState().biometryEnrolled &&
|
||||
!editorState().movedAway)
|
||||
useTabStore.getState().biometryEnrolled &&
|
||||
(!isMovedAway || forced)
|
||||
) {
|
||||
setTimeout(() => {
|
||||
unlockWithBiometrics();
|
||||
@@ -344,13 +348,15 @@ const useLockedNoteHandler = () => {
|
||||
const subs = [
|
||||
eSubscribeEvent(eUnlockNote, unlock),
|
||||
eSubscribeEvent(eUnlockWithBiometrics, () => {
|
||||
unlock();
|
||||
unlock(true);
|
||||
}),
|
||||
eSubscribeEvent(eUnlockWithPassword, onSubmit)
|
||||
];
|
||||
|
||||
if (
|
||||
tabRef.current?.session?.locked &&
|
||||
fluidTabsRef.current?.page() === "editor"
|
||||
(fluidTabsRef.current?.page() === "editor" ||
|
||||
useSettingStore.getState().deviceMode !== "mobile")
|
||||
) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
@@ -23,21 +23,36 @@ import {
|
||||
AppState,
|
||||
AppStateStatus,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TextInput,
|
||||
View
|
||||
} from "react-native";
|
||||
import Editor from ".";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
|
||||
import useKeyboard from "../../hooks/use-keyboard";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { editorRef } from "../../utils/global-refs";
|
||||
import { editorController, textInput } from "./tiptap/utils";
|
||||
import deviceInfo from "react-native-device-info";
|
||||
|
||||
export const EditorWrapper = ({ widths }: { widths: any }) => {
|
||||
export type PaneWidths = {
|
||||
mobile: {
|
||||
sidebar: number;
|
||||
list: number;
|
||||
editor: number;
|
||||
};
|
||||
smallTablet: {
|
||||
sidebar: number;
|
||||
list: number;
|
||||
editor: number;
|
||||
};
|
||||
tablet: {
|
||||
sidebar: number;
|
||||
list: number;
|
||||
editor: number;
|
||||
};
|
||||
};
|
||||
|
||||
export const EditorWrapper = ({ widths }: { widths: PaneWidths }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const { colors: toolBarColors } = useThemeColors("editorToolbar");
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
@@ -47,8 +62,9 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const keyboard = useKeyboard();
|
||||
const prevState = useRef<AppStateStatus>(undefined);
|
||||
const isFullscreen = useSettingStore((state) => state.fullscreen);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
|
||||
const onAppStateChanged = async (state: AppStateStatus) => {
|
||||
if (!prevState.current) {
|
||||
@@ -72,32 +88,37 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
|
||||
};
|
||||
}, [loading]);
|
||||
|
||||
const getMarginBottom = () => {
|
||||
const bottomInsets =
|
||||
Platform.OS === "android" ? 12 : insets.bottom + 16 || 14;
|
||||
if (!keyboard.keyboardShown) return bottomInsets / 1.5;
|
||||
if (deviceInfo.isTablet() && Platform.OS === "ios" && !floating)
|
||||
return bottomInsets;
|
||||
if (Platform.OS === "ios") return bottomInsets / 1.5;
|
||||
return 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
testID="editor-wrapper"
|
||||
ref={editorRef}
|
||||
style={{
|
||||
width: widths[!introCompleted ? "mobile" : (deviceMode as any)]?.editor,
|
||||
height: "100%",
|
||||
minHeight: "100%",
|
||||
backgroundColor: toolBarColors.primary.background,
|
||||
borderLeftWidth: DDS.isTab ? 1 : 0,
|
||||
borderLeftColor: DDS.isTab
|
||||
? colors.secondary.background
|
||||
: "transparent",
|
||||
paddingBottom:
|
||||
Platform.OS === "android" ? insets.bottom + 10 : insets.bottom
|
||||
}}
|
||||
style={[
|
||||
{
|
||||
width: isFullscreen
|
||||
? dimensions.width
|
||||
: widths[
|
||||
!introCompleted ? "mobile" : (deviceMode as keyof PaneWidths)
|
||||
]?.editor,
|
||||
height: "100%",
|
||||
minHeight: "100%",
|
||||
backgroundColor: toolBarColors.primary.background,
|
||||
paddingLeft: isFullscreen
|
||||
? deviceMode === "smallTablet"
|
||||
? 0
|
||||
: dimensions.width * 0.15
|
||||
: null,
|
||||
paddingRight: isFullscreen
|
||||
? deviceMode === "smallTablet"
|
||||
? 0
|
||||
: dimensions.width * 0.15
|
||||
: insets.right,
|
||||
borderLeftWidth: DDS.isTab ? 1 : 0,
|
||||
borderLeftColor: DDS.isTab
|
||||
? colors.secondary.background
|
||||
: "transparent",
|
||||
paddingBottom: insets.bottom
|
||||
}
|
||||
]}
|
||||
>
|
||||
{loading || !introCompleted ? null : (
|
||||
<KeyboardAvoidingView
|
||||
|
||||
@@ -217,12 +217,12 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
position: "absolute",
|
||||
bottom: 20,
|
||||
right: 20,
|
||||
gap: DefaultAppStyles.GAP_VERTICAL,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<FloatingButton
|
||||
icon="file-tree"
|
||||
testID="notebookTreeSheet"
|
||||
size="small"
|
||||
onPress={() => {
|
||||
Notebooks.present(params.current.item);
|
||||
@@ -230,7 +230,8 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
style={{
|
||||
position: "relative",
|
||||
right: 0,
|
||||
bottom: 5
|
||||
bottom: 0,
|
||||
paddingBottom: 10
|
||||
}}
|
||||
/>
|
||||
<FloatingButton
|
||||
@@ -238,6 +239,7 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
alwaysVisible
|
||||
style={{
|
||||
position: "relative",
|
||||
paddingTop: 10,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}}
|
||||
|
||||
@@ -23,7 +23,6 @@ import React, { useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { TextInput } from "react-native-gesture-handler";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import Navigation from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
@@ -41,7 +40,6 @@ export const SearchBar = ({
|
||||
const isFocused = useNavigationStore(
|
||||
(state) => state.focusedRouteId === "Search"
|
||||
);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const { colors } = useThemeColors();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const _onChangeText = (value: string) => {
|
||||
@@ -58,7 +56,6 @@ export const SearchBar = ({
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
marginTop: DefaultAppStyles.GAP_SMALL,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
|
||||
@@ -59,7 +59,7 @@ const Group = ({
|
||||
id="Settings"
|
||||
/>
|
||||
)}
|
||||
<DelayLayout type="settings" delay={0}>
|
||||
<DelayLayout type="settings">
|
||||
<View
|
||||
style={{
|
||||
flex: 1
|
||||
|
||||
@@ -59,7 +59,7 @@ const Home = ({
|
||||
hasSearch={false}
|
||||
id="Settings"
|
||||
/>
|
||||
<DelayLayout delay={300} type="settings">
|
||||
<DelayLayout type="settings">
|
||||
<LegendList
|
||||
data={settingsGroups}
|
||||
keyExtractor={keyExtractor}
|
||||
|
||||
@@ -17,23 +17,29 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import Group from "./group";
|
||||
import Home from "./home";
|
||||
import { RouteParams } from "./types";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
const SettingsStack = createNativeStackNavigator<RouteParams>();
|
||||
|
||||
export const Settings = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useSafeAreaInsets();
|
||||
return (
|
||||
<SafeAreaView
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: colors.primary.background
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: insets.top,
|
||||
paddingBottom: insets.bottom,
|
||||
paddingLeft: insets.left,
|
||||
paddingRight: insets.right
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider value="list">
|
||||
@@ -58,7 +64,7 @@ export const Settings = () => {
|
||||
<SettingsStack.Screen name="SettingsGroup" component={Group} />
|
||||
</SettingsStack.Navigator>
|
||||
</ScopedThemeProvider>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -118,7 +118,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
if (
|
||||
(user.subscription?.provider === SubscriptionProvider.GOOGLE ||
|
||||
user.subscription?.provider === SubscriptionProvider.APPLE) &&
|
||||
isCurrentPlatform
|
||||
isCurrentPlatform &&
|
||||
user?.subscription?.productId
|
||||
) {
|
||||
RNIap.deepLinkToSubscriptions({
|
||||
sku: user?.subscription.productId
|
||||
@@ -142,13 +143,18 @@ export const settingsGroups: SettingSection[] = [
|
||||
"dddd, MMMM D, YYYY h:mm A"
|
||||
);
|
||||
|
||||
if (user.subscription?.plan !== SubscriptionPlan.FREE) {
|
||||
if (
|
||||
user.subscription?.plan !== SubscriptionPlan.FREE &&
|
||||
user.subscription?.productId
|
||||
) {
|
||||
const status = user.subscription?.status;
|
||||
return status === SubscriptionStatus.TRIAL
|
||||
? strings.trialEndsOn(
|
||||
dayjs(user?.subscription?.start)
|
||||
.add(
|
||||
user?.subscription?.productId.includes("monthly") ? 7 : 14
|
||||
user?.subscription?.productId?.includes("monthly")
|
||||
? 7
|
||||
: 14
|
||||
)
|
||||
.format("dddd, MMMM D, YYYY h:mm A")
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ import type {
|
||||
ThemeMetadata,
|
||||
ThemesRouter
|
||||
} from "@notesnook/themes-server";
|
||||
import { pick } from "@react-native-documents/picker";
|
||||
import { keepLocalCopy, pick } from "@react-native-documents/picker";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
|
||||
@@ -57,6 +57,7 @@ import { getColorLinearShade } from "../../utils/colors";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { LegendList } from "@legendapp/list";
|
||||
import ReactNativeBlobUtil from "react-native-blob-util";
|
||||
|
||||
const THEME_SERVER_URL = "https://themes-api.notesnook.com";
|
||||
//@ts-ignore
|
||||
@@ -412,20 +413,39 @@ function ThemeSelector() {
|
||||
type={"secondaryAccented"}
|
||||
icon="folder"
|
||||
fontSize={AppFontSize.xs}
|
||||
onPress={() => {
|
||||
pick({
|
||||
allowMultiSelection: false
|
||||
}).then((r) => {
|
||||
fetch(r[0].uri).then(async (response) => {
|
||||
const json = await response.json();
|
||||
const result = validateTheme(json);
|
||||
if (result.error) {
|
||||
ToastManager.error(new Error(result.error));
|
||||
return;
|
||||
}
|
||||
select(json, true);
|
||||
onPress={async () => {
|
||||
try {
|
||||
const pickResponse = await pick({
|
||||
allowMultiSelection: false
|
||||
});
|
||||
});
|
||||
const copiedFile = await keepLocalCopy({
|
||||
destination: "cachesDirectory",
|
||||
files: [
|
||||
{
|
||||
uri: pickResponse[0].uri,
|
||||
fileName: pickResponse[0].name || "theme.json"
|
||||
}
|
||||
]
|
||||
});
|
||||
if (copiedFile[0].status !== "success") return;
|
||||
|
||||
const themeJson = await ReactNativeBlobUtil.fs.readFile(
|
||||
copiedFile[0].localUri,
|
||||
"utf8"
|
||||
);
|
||||
ReactNativeBlobUtil.fs
|
||||
.unlink(copiedFile[0].localUri)
|
||||
.catch(() => {});
|
||||
const json = JSON.parse(themeJson);
|
||||
const result = validateTheme(json);
|
||||
if (result.error) {
|
||||
ToastManager.error(new Error(result.error));
|
||||
return;
|
||||
}
|
||||
select(json, true);
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
1142
apps/mobile/app/screens/wrapped/index.tsx
Normal file
1142
apps/mobile/app/screens/wrapped/index.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,7 @@ export class DeviceDetectionService {
|
||||
windowSize = size || windowSize;
|
||||
this.width = windowSize.width;
|
||||
this.height = windowSize.height;
|
||||
this.orientation = orientation;
|
||||
this.adjustedWidth = this.width * this.pixelDensity;
|
||||
this.adjustedHeight = this.height * this.pixelDensity;
|
||||
screenSize = Dimensions.get("screen");
|
||||
|
||||
@@ -71,7 +71,8 @@ const routeNames = {
|
||||
Archive: "Archive",
|
||||
ManageTags: "ManageTags",
|
||||
AddReminder: "AddReminder",
|
||||
PayWall: "PayWall"
|
||||
PayWall: "PayWall",
|
||||
Wrapped: "Wrapped"
|
||||
};
|
||||
|
||||
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
|
||||
|
||||
@@ -112,6 +112,7 @@ export interface RouteParams extends ParamListBase {
|
||||
context: "signup" | "logged-in" | "logged-out" | "subscribed";
|
||||
state?: BillingState;
|
||||
};
|
||||
Wrapped: GenericRouteParam;
|
||||
}
|
||||
|
||||
export type RouteName = keyof RouteParams;
|
||||
|
||||
@@ -35,6 +35,10 @@ import { useTagStore } from "../stores/use-tag-store";
|
||||
import { eUpdateNoteInEditor } from "./events";
|
||||
import { unlockVault } from "./unlock-vault";
|
||||
|
||||
export const valueLimiter = (value: number, min: number, max: number) => {
|
||||
return value < min ? min : value > max ? max : value;
|
||||
};
|
||||
|
||||
export function getObfuscatedEmail(email: string) {
|
||||
if (!email) return "";
|
||||
const [username, provider] = email.split("@");
|
||||
|
||||
@@ -72,10 +72,8 @@ export const MenuItemsList: SideMenuItem[] = [
|
||||
title: "Monographs",
|
||||
icon: "text-box-multiple-outline",
|
||||
onPress: () => {
|
||||
Navigation.closeDrawer();
|
||||
Monographs.navigate();
|
||||
setTimeout(() => {
|
||||
Navigation.closeDrawer();
|
||||
}, 32);
|
||||
},
|
||||
type: "side-menu-item"
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ module.exports = function (api, opts) {
|
||||
});
|
||||
}
|
||||
|
||||
env.plugins.push("react-native-reanimated/plugin");
|
||||
env.plugins.push("react-native-worklets/plugin");
|
||||
|
||||
api.cache(true);
|
||||
return env;
|
||||
|
||||
@@ -134,7 +134,7 @@ describe("NOTE TESTS", () => {
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Assign colors to a note", async () => {
|
||||
it.only("Assign colors to a note", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
|
||||
@@ -121,7 +121,7 @@ describe("NOTEBOOKS", () => {
|
||||
.run();
|
||||
});
|
||||
|
||||
it.only("Remove note from notebook", async () => {
|
||||
it("Remove note from notebook", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
@@ -138,7 +138,8 @@ describe("NOTEBOOKS", () => {
|
||||
.longPressByText(note.body)
|
||||
.wait(500)
|
||||
.waitAndTapById("select-minus")
|
||||
.isNotVisibleById(note.title)
|
||||
.wait(500)
|
||||
.isNotVisibleByText(note.body)
|
||||
.run();
|
||||
})
|
||||
.run();
|
||||
@@ -199,6 +200,7 @@ describe("NOTEBOOKS", () => {
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Move to trash")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Delete")
|
||||
.waitAndTapById("tab-home")
|
||||
.waitAndTapByText("Trash")
|
||||
|
||||
@@ -34,7 +34,7 @@ describe("Sort & filter", () => {
|
||||
.element(by.web.className("ProseMirror"))
|
||||
.typeText("Edited ", true);
|
||||
})
|
||||
.pressBack()
|
||||
.pressBack(2)
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Date created")
|
||||
|
||||
@@ -32,7 +32,10 @@ const testvars = {
|
||||
|
||||
class Element {
|
||||
element: Detox.NativeElement;
|
||||
constructor(public type: "id" | "text", public value: string) {
|
||||
constructor(
|
||||
public type: "id" | "text",
|
||||
public value: string
|
||||
) {
|
||||
if (type == "id") {
|
||||
this.element = element(by.id(value)).atIndex(0);
|
||||
} else {
|
||||
@@ -135,6 +138,7 @@ const Tests = {
|
||||
);
|
||||
await titleInput.isVisible();
|
||||
await titleInput.element.typeText(title);
|
||||
await Tests.sleep(1000);
|
||||
if (description) {
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
|
||||
@@ -84,7 +84,7 @@ describe("VAULT", () => {
|
||||
.run();
|
||||
});
|
||||
|
||||
it.only("Change vault password", async () => {
|
||||
it("Change vault password", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
@@ -94,7 +94,7 @@ describe("VAULT", () => {
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.changePwd, "2362")
|
||||
.waitAndTapByText("Change")
|
||||
.pressBack(3)
|
||||
.pressBack(4)
|
||||
.addStep(async () => await openLockedNote("2362"))
|
||||
.run();
|
||||
});
|
||||
|
||||
@@ -1029,7 +1029,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1104,7 +1104,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1135,7 +1135,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1210,7 +1210,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1367,7 +1367,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1379,7 +1379,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1410,7 +1410,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1423,7 +1423,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1453,7 +1453,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1534,7 +1534,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1565,7 +1565,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2160;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1647,7 +1647,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = "3.3.10-beta.2";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
<string>3B52.1</string>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
|
||||
@@ -1969,7 +1969,7 @@ PODS:
|
||||
- React-Core
|
||||
- React-RCTFabric
|
||||
- ReactCommon/turbomodule/core
|
||||
- react-native-mmkv-storage (12.0.0):
|
||||
- react-native-mmkv-storage (12.0.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -2244,8 +2244,36 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-upload (6.27.0):
|
||||
- react-native-upload (6.29.0):
|
||||
- React
|
||||
- react-native-view-shot (4.0.3):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
- fmt
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-jsi
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-renderercss
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-webview (13.16.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -3033,14 +3061,14 @@ PODS:
|
||||
- Yoga
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
- RNNotifee (7.4.9):
|
||||
- RNNotifee (7.4.10):
|
||||
- React-Core
|
||||
- RNNotifee/NotifeeCore (= 7.4.9)
|
||||
- RNNotifee/NotifeeCore (7.4.9):
|
||||
- RNNotifee/NotifeeCore (= 7.4.10)
|
||||
- RNNotifee/NotifeeCore (7.4.10):
|
||||
- React-Core
|
||||
- RNPrivacySnapshot (1.0.0):
|
||||
- React-Core
|
||||
- RNReanimated (4.1.3):
|
||||
- RNReanimated (4.2.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3067,11 +3095,11 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNReanimated/reanimated (= 4.1.3)
|
||||
- RNReanimated/reanimated (= 4.2.0)
|
||||
- RNWorklets
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNReanimated/reanimated (4.1.3):
|
||||
- RNReanimated/reanimated (4.2.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3098,11 +3126,11 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNReanimated/reanimated/apple (= 4.1.3)
|
||||
- RNReanimated/reanimated/apple (= 4.2.0)
|
||||
- RNWorklets
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNReanimated/reanimated/apple (4.1.3):
|
||||
- RNReanimated/reanimated/apple (4.2.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3282,7 +3310,7 @@ PODS:
|
||||
- pop (~> 1.0)
|
||||
- React
|
||||
- SexyTooltip
|
||||
- RNWorklets (0.6.1):
|
||||
- RNWorklets (0.7.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3309,10 +3337,10 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNWorklets/worklets (= 0.6.1)
|
||||
- RNWorklets/worklets (= 0.7.1)
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNWorklets/worklets (0.6.1):
|
||||
- RNWorklets/worklets (0.7.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3339,10 +3367,10 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNWorklets/worklets/apple (= 0.6.1)
|
||||
- RNWorklets/worklets/apple (= 0.7.1)
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNWorklets/worklets/apple (0.6.1):
|
||||
- RNWorklets/worklets/apple (0.7.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3465,6 +3493,7 @@ DEPENDENCIES:
|
||||
- "react-native-sodium (from `../node_modules/@ammarahmed/react-native-sodium`)"
|
||||
- react-native-theme-switch-animation (from `../node_modules/react-native-theme-switch-animation`)
|
||||
- "react-native-upload (from `../node_modules/@ammarahmed/react-native-upload`)"
|
||||
- react-native-view-shot (from `../node_modules/react-native-view-shot`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
|
||||
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
|
||||
@@ -3680,6 +3709,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-theme-switch-animation"
|
||||
react-native-upload:
|
||||
:path: "../node_modules/@ammarahmed/react-native-upload"
|
||||
react-native-view-shot:
|
||||
:path: "../node_modules/react-native-view-shot"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
React-NativeModulesApple:
|
||||
@@ -3868,7 +3899,7 @@ SPEC CHECKSUMS:
|
||||
react-native-image-resizer: 290b045c34c69db7574e4d08aadfc4abe1ff5a99
|
||||
react-native-in-app-review: 1516ba69d60d58053b7eb3aaaf8d2a5a74af8b57
|
||||
react-native-keep-awake: a351e6f67006b47f316ae2b17ee8ee69386167f4
|
||||
react-native-mmkv-storage: 31b7b155e690339f5a25cfe56d5e2f68dee4f066
|
||||
react-native-mmkv-storage: e84980084e371a9c2230bbde071536a7e1539406
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
|
||||
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
|
||||
@@ -3880,7 +3911,8 @@ SPEC CHECKSUMS:
|
||||
react-native-share-extension: fdc6aaab51591a2d445df239c446aaa3a99658ec
|
||||
react-native-sodium: 066f76e46c9be13e9260521e3fa994937c4cdab4
|
||||
react-native-theme-switch-animation: 449d6db7a760f55740505e7403ae8061debc9a7e
|
||||
react-native-upload: f0198bb4db00ff7dae14904ad59da470550be075
|
||||
react-native-upload: 1459a4be625dda39e4364d9e7d859477a3000f2a
|
||||
react-native-view-shot: 6c008e58f4720de58370848201c5d4a082c6d4ca
|
||||
react-native-webview: 654f794a7686b47491cf43aa67f7f428bea00eed
|
||||
React-NativeModulesApple: 46690a0fe94ec28fc6fc686ec797b911d251ded0
|
||||
React-oscompat: 95875e81f5d4b3c7b2c888d5bd2c9d83450d8bdb
|
||||
@@ -3927,15 +3959,15 @@ SPEC CHECKSUMS:
|
||||
RNIap: 61f183ac917792fae42b0326b1bef33598c1adf6
|
||||
RNImageCropPicker: 5fd4ceaead64d8c53c787e4e559004f97bc76df7
|
||||
RNKeychain: ffd0513e676445c637410b47249460cbf56bc9cb
|
||||
RNNotifee: dabf3cdd7bfd9340bb84358cc78f635af4bc80e2
|
||||
RNNotifee: 3840cc81add9954a5dec040f96442498f3f95d7b
|
||||
RNPrivacySnapshot: ccad3a548338c2f526bb7b1789af3fb0618b7d1d
|
||||
RNReanimated: 732e7d1662f8cc0e533fa32791800de5b5934726
|
||||
RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee
|
||||
RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479
|
||||
RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc
|
||||
RNShare: 5d39a36f2d3d9a6c9344bfb3232c8ed607924a35
|
||||
RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6
|
||||
RNTooltips: 50ff94bd2bff84ef74279c9532d008f5fdcddb95
|
||||
RNWorklets: ab618bf7d1c7fd2cb793b9f0f39c3e29274b3ebf
|
||||
RNWorklets: d9c050940f140af5d8b611d937eab1cbfce5e9a5
|
||||
RNZipArchive: 71befbdc20e7fbd4b64552a0b324ad0e8d0efefb
|
||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
|
||||
|
||||
File diff suppressed because one or more lines are too long
187
apps/mobile/package-lock.json
generated
187
apps/mobile/package-lock.json
generated
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10-beta.2",
|
||||
"version": "3.3.10-beta.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10-beta.2",
|
||||
"version": "3.3.10-beta.6",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.9",
|
||||
"@ammarahmed/notifee-react-native": "^7.4.10",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.1",
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.5",
|
||||
"@ammarahmed/react-native-sodium": "^1.6.8",
|
||||
"@ammarahmed/react-native-upload": "^6.28.0",
|
||||
"@ammarahmed/react-native-upload": "^6.29.0",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.11",
|
||||
"@callstack/repack": "~5.2.1",
|
||||
@@ -72,7 +72,7 @@
|
||||
"react": "19.1.1",
|
||||
"react-async-hook": "^4.0.0",
|
||||
"react-native": "0.82.1",
|
||||
"react-native-actions-sheet": "^10.0.0-alpha.13",
|
||||
"react-native-actions-sheet": "^10.1.0",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-background-actions": "^2.6.6",
|
||||
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",
|
||||
@@ -97,7 +97,7 @@
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-material-menu": "^2.0.0",
|
||||
"react-native-mmkv-storage": "^12.0.0",
|
||||
"react-native-mmkv-storage": "^12.0.1",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
@@ -108,7 +108,7 @@
|
||||
"react-native-progress": "5.0.0",
|
||||
"react-native-qrcode-svg": "^6.0.6",
|
||||
"react-native-quick-sqlite": "^8.2.7",
|
||||
"react-native-reanimated": "^4.1.3",
|
||||
"react-native-reanimated": "^4.2.0",
|
||||
"react-native-reanimated-progress-bar": "1.0.1",
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-scoped-storage": "^1.9.5",
|
||||
@@ -123,9 +123,10 @@
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-vector-icons": "10.3.0",
|
||||
"react-native-view-shot": "^4.0.3",
|
||||
"react-native-webview": "^13.13.5",
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
"react-native-worklets": "^0.6.1",
|
||||
"react-native-worklets": "^0.7.1",
|
||||
"react-native-zip-archive": "6.0.9",
|
||||
"timeago.js": "4.0.2",
|
||||
"toggle-switch-react-native": "3.2.0",
|
||||
@@ -228,6 +229,7 @@
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"dayjs": "1.11.13",
|
||||
"dom-serializer": "^2.0.0",
|
||||
@@ -483,9 +485,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ammarahmed/notifee-react-native": {
|
||||
"version": "7.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/notifee-react-native/-/notifee-react-native-7.4.9.tgz",
|
||||
"integrity": "sha512-DK4ogXPBnoM8UsDQQLK6AQZUbP9TqOcL3YDEKtXmsWug1hzvcP/AfJ42KUCni/XR5NMI1VWW7a7tEwdbdzmS8g==",
|
||||
"version": "7.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/notifee-react-native/-/notifee-react-native-7.4.10.tgz",
|
||||
"integrity": "sha512-19t1vhIddFneOI7JVmXghunEt5mEmDBY/G1YVMuRNNT/HFQ1EcArJWuAaFAJrhrl/2CBS7xoPGLusKbkbT/JjA==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react-native": "*"
|
||||
@@ -531,9 +533,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@ammarahmed/react-native-upload": {
|
||||
"version": "6.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-upload/-/react-native-upload-6.28.0.tgz",
|
||||
"integrity": "sha512-0cU6ulbJB2s9KgV2QlQ91//YZ+K5USlKjM6CW9fDHFcGn+Td3tFWB0atnplP35Ih+HeOnYL9t1MuNG4vNzjoFw==",
|
||||
"version": "6.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-upload/-/react-native-upload-6.29.0.tgz",
|
||||
"integrity": "sha512-k2FJAVR4Nohx4VhfgHuXzLXQpgEnco/PAv9R080X7oY3O12DTgY0ruDvOFwvQGFW0PDIHlXfLfUiMq2kZc/k1A==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@@ -2001,6 +2003,7 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz",
|
||||
"integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
@@ -2483,16 +2486,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-typescript": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz",
|
||||
"integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
|
||||
"integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-validator-option": "^7.27.1",
|
||||
"@babel/plugin-syntax-jsx": "^7.27.1",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
|
||||
"@babel/plugin-transform-typescript": "^7.28.5"
|
||||
"@babel/plugin-transform-typescript": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -7497,6 +7500,15 @@
|
||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
|
||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -8553,6 +8565,15 @@
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
|
||||
@@ -11633,6 +11654,19 @@
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
|
||||
@@ -17130,9 +17164,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-actions-sheet": {
|
||||
"version": "10.0.0-alpha.13",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-10.0.0-alpha.13.tgz",
|
||||
"integrity": "sha512-jy2PruTrz5Hp4NGFk6ykOLoTYcUaSYQ9ylTBi+JM32Lh8UQl1SkKm9kp7VcEMpwXw/gf+/eYpDtPffi3CAzfIA==",
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-10.1.0.tgz",
|
||||
"integrity": "sha512-pfbfWs2YqdF3Vo7Cx6LUsDQ/uS3iw8H64iFxZvtLXhZ3cVHWundJ903sZtadzJC+TpQIRV7NQsdnhErhf+wBlg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react-native": "*",
|
||||
@@ -17655,13 +17689,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-mmkv-storage": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-mmkv-storage/-/react-native-mmkv-storage-12.0.0.tgz",
|
||||
"integrity": "sha512-sssZInILQBquytDDfjosjEdvevwMPk3fqQQjdjfnH362IcsBsApvtT8yJS406Mz9aJ6VhqVsZw/awltsUuPSYg==",
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-mmkv-storage/-/react-native-mmkv-storage-12.0.1.tgz",
|
||||
"integrity": "sha512-4I4NzjStuCqkeXGfJ7pcACNQ2FlZKlAQqeRvI80iZL7mQcaO5NMO26ILUk2f4LPEvxaT2ns+NcqPMZSj7L83ng==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mmkv-link": "autolink/postlink/run.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": "*"
|
||||
}
|
||||
@@ -17799,19 +17830,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.3.tgz",
|
||||
"integrity": "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.2.0.tgz",
|
||||
"integrity": "sha512-frhu5b8/m/VvaMWz48V8RxcsXnE3hrlErQ5chr21MzAeDCpY4X14sQjvm+jvu3aOI+7Cz2atdRpyhhIuqxVaXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-native-is-edge-to-edge": "^1.2.1",
|
||||
"semver": "7.7.2"
|
||||
"react-native-is-edge-to-edge": "1.2.1",
|
||||
"semver": "7.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-worklets": ">=0.5.0"
|
||||
"react-native-worklets": ">=0.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated-progress-bar": {
|
||||
@@ -17826,9 +17856,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -18069,6 +18099,19 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-view-shot": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-4.0.3.tgz",
|
||||
"integrity": "sha512-USNjYmED7C0me02c1DxKA0074Hw+y/nxo+xJKlffMvfUWWzL5ELh/TJA/pTnVqFurIrzthZDPtDM7aBFJuhrHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"html2canvas": "^1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-webview": {
|
||||
"version": "13.16.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.0.tgz",
|
||||
@@ -18093,33 +18136,49 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-worklets": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.6.1.tgz",
|
||||
"integrity": "sha512-URca8l7c7Uog7gv4mcg9KILdJlnbvwdS5yfXQYf5TDkD2W1VY1sduEKrD+sA3lUPXH/TG1vmXAvNxCNwPMYgGg==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.1.tgz",
|
||||
"integrity": "sha512-KNsvR48ULg73QhTlmwPbdJLPsWcyBotrGPsrDRDswb5FYpQaJEThUKc2ncXE4UM5dn/ewLoQHjSjLaKUVPxPhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
||||
"@babel/plugin-transform-class-properties": "^7.0.0-0",
|
||||
"@babel/plugin-transform-classes": "^7.0.0-0",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
|
||||
"@babel/plugin-transform-optional-chaining": "^7.0.0-0",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
|
||||
"@babel/plugin-transform-template-literals": "^7.0.0-0",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.0.0-0",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"semver": "7.7.2"
|
||||
"@babel/plugin-transform-arrow-functions": "7.27.1",
|
||||
"@babel/plugin-transform-class-properties": "7.27.1",
|
||||
"@babel/plugin-transform-classes": "7.28.4",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "7.27.1",
|
||||
"@babel/plugin-transform-optional-chaining": "7.27.1",
|
||||
"@babel/plugin-transform-shorthand-properties": "7.27.1",
|
||||
"@babel/plugin-transform-template-literals": "7.27.1",
|
||||
"@babel/plugin-transform-unicode-regex": "7.27.1",
|
||||
"@babel/preset-typescript": "7.27.1",
|
||||
"convert-source-map": "2.0.0",
|
||||
"semver": "7.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0",
|
||||
"@babel/core": "*",
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-worklets/node_modules/@babel/plugin-transform-optional-chaining": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
|
||||
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-worklets/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -20266,6 +20325,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/thingies": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz",
|
||||
@@ -21077,6 +21145,15 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10-beta.2",
|
||||
"version": "3.3.10",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -26,13 +26,13 @@
|
||||
"react": "19.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.9",
|
||||
"@ammarahmed/notifee-react-native": "^7.4.10",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.1",
|
||||
"@ammarahmed/react-native-share-extension": "^2.9.5",
|
||||
"@ammarahmed/react-native-sodium": "^1.6.8",
|
||||
"@ammarahmed/react-native-upload": "^6.28.0",
|
||||
"@ammarahmed/react-native-upload": "^6.29.0",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.11",
|
||||
"@callstack/repack": "~5.2.1",
|
||||
@@ -88,7 +88,7 @@
|
||||
"react": "19.1.1",
|
||||
"react-async-hook": "^4.0.0",
|
||||
"react-native": "0.82.1",
|
||||
"react-native-actions-sheet": "^10.0.0-alpha.13",
|
||||
"react-native-actions-sheet": "^10.1.0",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-background-actions": "^2.6.6",
|
||||
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",
|
||||
@@ -113,7 +113,7 @@
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-material-menu": "^2.0.0",
|
||||
"react-native-mmkv-storage": "^12.0.0",
|
||||
"react-native-mmkv-storage": "^12.0.1",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
@@ -124,7 +124,7 @@
|
||||
"react-native-progress": "5.0.0",
|
||||
"react-native-qrcode-svg": "^6.0.6",
|
||||
"react-native-quick-sqlite": "^8.2.7",
|
||||
"react-native-reanimated": "^4.1.3",
|
||||
"react-native-reanimated": "^4.2.0",
|
||||
"react-native-reanimated-progress-bar": "1.0.1",
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-scoped-storage": "^1.9.5",
|
||||
@@ -139,9 +139,10 @@
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-vector-icons": "10.3.0",
|
||||
"react-native-view-shot": "^4.0.3",
|
||||
"react-native-webview": "^13.13.5",
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
"react-native-worklets": "^0.6.1",
|
||||
"react-native-worklets": "^0.7.1",
|
||||
"react-native-zip-archive": "6.0.9",
|
||||
"timeago.js": "4.0.2",
|
||||
"toggle-switch-react-native": "3.2.0",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/node_modules/react-native-gesture-handler/android/build.gradle b/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
index ac149a0..37f620c 100644
|
||||
--- a/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
+++ b/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
@@ -187,6 +187,7 @@ android {
|
||||
exclude "**/libreactnative.so"
|
||||
exclude "**/libjsi.so"
|
||||
exclude "**/libc++_shared.so"
|
||||
+ exclude "**/libfbjni.so"
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
File diff suppressed because one or more lines are too long
92
apps/web/package-lock.json
generated
92
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -59,6 +59,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"hash-wasm": "4.12.0",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"htmlparser2": "^10.0.0",
|
||||
"katex": "0.16.11",
|
||||
"mac-scrollbar": "0.13.6",
|
||||
"mutative": "^1.1.0",
|
||||
@@ -234,6 +235,7 @@
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"dayjs": "1.11.13",
|
||||
"dom-serializer": "^2.0.0",
|
||||
@@ -321,9 +323,6 @@
|
||||
"@tiptap/extension-placeholder": "2.6.6",
|
||||
"@tiptap/extension-subscript": "2.6.6",
|
||||
"@tiptap/extension-superscript": "2.6.6",
|
||||
"@tiptap/extension-table": "2.6.6",
|
||||
"@tiptap/extension-table-cell": "2.6.6",
|
||||
"@tiptap/extension-table-header": "2.6.6",
|
||||
"@tiptap/extension-table-row": "2.6.6",
|
||||
"@tiptap/extension-task-item": "2.6.6",
|
||||
"@tiptap/extension-task-list": "2.6.6",
|
||||
@@ -504,7 +503,7 @@
|
||||
},
|
||||
"../desktop": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.4",
|
||||
"version": "3.3.6-beta.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -517,7 +516,7 @@
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite-better-trigram": "0.0.5",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
@@ -2896,6 +2895,25 @@
|
||||
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@notesnook-importer/core/node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook-importer/enex": {
|
||||
"version": "2.2.3",
|
||||
"license": "GPL-3.0-or-later",
|
||||
@@ -2909,6 +2927,25 @@
|
||||
"spark-md5": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook-importer/enex/node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook-importer/storage": {
|
||||
"version": "2.2.3",
|
||||
"license": "GPL-3.0-or-later"
|
||||
@@ -2926,6 +2963,25 @@
|
||||
"spark-md5": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook-importer/znel/node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook/common": {
|
||||
"resolved": "../../packages/common",
|
||||
"link": true
|
||||
@@ -7438,9 +7494,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
|
||||
"integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
@@ -7452,8 +7508,20 @@
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
"domutils": "^3.2.1",
|
||||
"entities": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/entities": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.6-beta.1",
|
||||
"version": "3.3.6-beta.3",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -57,6 +57,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"hash-wasm": "4.12.0",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"htmlparser2": "^10.0.0",
|
||||
"katex": "0.16.11",
|
||||
"mac-scrollbar": "0.13.6",
|
||||
"mutative": "^1.1.0",
|
||||
|
||||
@@ -48,6 +48,7 @@ import { getFontSizes } from "@notesnook/theme/theme/font/fontsize.js";
|
||||
import { useWindowControls } from "./hooks/use-window-controls";
|
||||
import { STATUS_BAR_HEIGHT } from "./common/constants";
|
||||
import { NavigationEvents } from "./navigation";
|
||||
import { db } from "./common/db";
|
||||
|
||||
new WebExtensionRelay();
|
||||
|
||||
@@ -143,6 +144,10 @@ function DesktopAppContents() {
|
||||
const isTablet = useTablet();
|
||||
const navPane = useRef<SplitPaneImperativeHandle>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTablet) navPane.current?.collapse(0);
|
||||
else if (navPane.current?.isCollapsed(0)) navPane.current?.expand(0);
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
isFeatureSupported
|
||||
} from "./utils/feature-check";
|
||||
import { initializeLogger } from "./utils/logger";
|
||||
import { shouldShowWrapped } from "./utils/should-show-wrapped";
|
||||
|
||||
type Route<TProps = null> = {
|
||||
component: () => Promise<{
|
||||
@@ -55,6 +56,9 @@ const routes = {
|
||||
"/plans": {
|
||||
component: () => import("./views/plans")
|
||||
},
|
||||
"/wrapped": {
|
||||
component: () => import("./views/wrapped")
|
||||
},
|
||||
"/checkout": {
|
||||
component: () => import("./views/checkout")
|
||||
},
|
||||
@@ -122,6 +126,12 @@ function getRoute(): RouteWithPath<AuthProps> | RouteWithPath {
|
||||
routes[path] ? { route: routes[path], path } : null
|
||||
) as RouteWithPath<AuthProps> | null;
|
||||
|
||||
if (route?.path === "/wrapped" && !shouldShowWrapped())
|
||||
return {
|
||||
route: routes.default,
|
||||
path: "default"
|
||||
};
|
||||
|
||||
return signup || sessionExpired || route || fallback;
|
||||
}
|
||||
|
||||
|
||||
@@ -226,11 +226,13 @@ export default function TabsView() {
|
||||
<TableOfContents sessionId={activeSession.id} />
|
||||
</Pane>
|
||||
) : null}
|
||||
{arePropertiesVisible && activeSession && (
|
||||
<Pane id="properties-pane" initialSize={250} minSize={250}>
|
||||
<Properties sessionId={activeSession.id} />
|
||||
</Pane>
|
||||
)}
|
||||
{arePropertiesVisible &&
|
||||
activeSession &&
|
||||
activeSession.type !== "new" && (
|
||||
<Pane id="properties-pane" initialSize={250} minSize={250}>
|
||||
<Properties sessionId={activeSession.id} />
|
||||
</Pane>
|
||||
)}
|
||||
</SplitPane>
|
||||
<DropZone overlayRef={overlayRef} />
|
||||
</ScopedThemeProvider>
|
||||
@@ -848,17 +850,27 @@ function useDragOverlay() {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
hideOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
dropElement.addEventListener("dragenter", showOverlay);
|
||||
overlay.addEventListener("drop", hideOverlay);
|
||||
overlay.addEventListener("dragenter", allowDrag);
|
||||
overlay.addEventListener("dragover", allowDrag);
|
||||
overlay.addEventListener("dragleave", hideOverlay);
|
||||
overlay.addEventListener("click", hideOverlay);
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
dropElement.removeEventListener("dragenter", showOverlay);
|
||||
overlay.removeEventListener("drop", hideOverlay);
|
||||
overlay.removeEventListener("dragenter", allowDrag);
|
||||
overlay.removeEventListener("dragover", allowDrag);
|
||||
overlay.removeEventListener("dragleave", hideOverlay);
|
||||
overlay.removeEventListener("click", hideOverlay);
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ function ListItem<TItem extends Item, TContext>(
|
||||
data-test-id={`title`}
|
||||
variant={"body"}
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
whiteSpace: "pre",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontWeight: isCompact ? "body" : "medium",
|
||||
|
||||
143
apps/web/src/components/monthly-activity-heatmap/index.tsx
Normal file
143
apps/web/src/components/monthly-activity-heatmap/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Box, Flex, Text } from "@theme-ui/components";
|
||||
import { SxProp } from "@theme-ui/core";
|
||||
|
||||
type MonthlyActivityHeatmapProps = {
|
||||
monthlyStats: Record<string, number>;
|
||||
year?: number;
|
||||
} & SxProp;
|
||||
|
||||
const MONTHS = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
];
|
||||
|
||||
const MONTHS_SHORT = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec"
|
||||
];
|
||||
|
||||
export function MonthlyActivityHeatmap({
|
||||
monthlyStats
|
||||
}: MonthlyActivityHeatmapProps) {
|
||||
// Find max count for bar height calculation
|
||||
const maxCount = Math.max(...Object.values(monthlyStats), 1);
|
||||
|
||||
// Create month data with counts
|
||||
const monthData = MONTHS.map((month) => ({
|
||||
month,
|
||||
count: monthlyStats[month] || 0
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Bar chart */}
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
gap: 2,
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
pb: 2
|
||||
}}
|
||||
>
|
||||
{monthData.map((data, index) => {
|
||||
const barHeight = maxCount > 0 ? (data.count / maxCount) * 100 : 0;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={data.month}
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
flex: 1,
|
||||
gap: 2,
|
||||
cursor: "pointer",
|
||||
"&:hover .tooltip": { opacity: 1 },
|
||||
"&:hover .bar": { bg: "accent" },
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
className="tooltip"
|
||||
sx={{ opacity: 0, bg: "background", zIndex: 1000 }}
|
||||
>
|
||||
<Text variant="body" sx={{ fontWeight: "bold" }}>
|
||||
{data.count}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Box
|
||||
className="bar"
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: `${barHeight}%`,
|
||||
minHeight: data.count > 0 ? "10px" : "5px",
|
||||
bg: maxCount === data.count ? "accent" : "paragraph",
|
||||
borderRadius: "4px 4px 0 0",
|
||||
transition: "all 0.3s ease",
|
||||
position: "relative",
|
||||
"&:hover": {
|
||||
transform: "scaleY(1.05)",
|
||||
transformOrigin: "bottom"
|
||||
}
|
||||
}}
|
||||
></Box>
|
||||
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "11px",
|
||||
color: "paragraph-secondary",
|
||||
fontWeight: "normal",
|
||||
transition: "all 0.3s ease"
|
||||
}}
|
||||
>
|
||||
{MONTHS_SHORT[index]}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -97,7 +97,6 @@ import { strings } from "@notesnook/intl";
|
||||
import Tags from "../../views/tags";
|
||||
import { Notebooks } from "../../views/notebooks";
|
||||
import { UserProfile } from "../../dialogs/settings/components/user-profile";
|
||||
import { SUBSCRIPTION_STATUS } from "../../common/constants";
|
||||
import {
|
||||
checkFeature,
|
||||
createSetDefaultHomepageMenuItem,
|
||||
@@ -105,7 +104,6 @@ import {
|
||||
withFeatureCheck
|
||||
} from "../../common";
|
||||
import { TabItem } from "./tab-item";
|
||||
import Notice from "../notice";
|
||||
import { Freeze } from "react-freeze";
|
||||
import { CREATE_BUTTON_MAP } from "../../common";
|
||||
import { useStore as useNotebookStore } from "../../stores/notebook-store";
|
||||
@@ -118,6 +116,7 @@ import {
|
||||
useIsFeatureAvailable
|
||||
} from "@notesnook/common";
|
||||
import { isUserSubscribed } from "../../hooks/use-is-user-premium";
|
||||
import { shouldShowWrapped } from "../../utils/should-show-wrapped";
|
||||
|
||||
type Route = {
|
||||
id: "notes" | "favorites" | "reminders" | "monographs" | "trash" | "archive";
|
||||
@@ -449,7 +448,17 @@ function NavigationMenu({ onExpand }: { onExpand?: () => void }) {
|
||||
</FlexScrollContainer>
|
||||
</Freeze>
|
||||
</Flex>
|
||||
{currentTab.id === "home" && !isCollapsed ? <Notice /> : null}
|
||||
{currentTab.id === "home" && !isCollapsed && shouldShowWrapped() ? (
|
||||
<Button
|
||||
variant="accent"
|
||||
sx={{ m: 2 }}
|
||||
onClick={() => {
|
||||
hardNavigate("/wrapped");
|
||||
}}
|
||||
>
|
||||
🎉 Wrapped {new Date().getFullYear()}
|
||||
</Button>
|
||||
) : null}
|
||||
</ScopedThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export function Notebook(props: NotebookProps) {
|
||||
variant={"body"}
|
||||
color={isOpened ? "paragraph-selected" : "paragraph"}
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
whiteSpace: "pre",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontWeight: "body",
|
||||
|
||||
@@ -67,7 +67,7 @@ function Tag(props: TagProps) {
|
||||
variant={"body"}
|
||||
color={isSelected ? "paragraph-selected" : "paragraph"}
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
whiteSpace: "pre",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontWeight: "body",
|
||||
|
||||
@@ -147,7 +147,7 @@ export const BuyDialog = DialogManager.register(function BuyDialog(
|
||||
</Text>
|
||||
</Flex>
|
||||
<PlansList
|
||||
selectedPlan={user?.subscription.productId}
|
||||
selectedPlan={user?.subscription?.productId}
|
||||
recommendedPlan={SubscriptionPlan.PRO}
|
||||
onPlanSelected={(plan) => selectPlan(plan)}
|
||||
/>
|
||||
|
||||
@@ -42,7 +42,7 @@ import { useStore as useUserStore } from "../../stores/user-store";
|
||||
import { getCurrencySymbol } from "../../common/currencies";
|
||||
|
||||
type PlansListProps = {
|
||||
selectedPlan?: string;
|
||||
selectedPlan?: string | null;
|
||||
loadAllPlans?: boolean;
|
||||
ignoreTrial?: boolean;
|
||||
recommendedPlan?: SubscriptionPlan;
|
||||
|
||||
@@ -454,7 +454,7 @@ export async function checkUpload(
|
||||
expectedSize: number
|
||||
) {
|
||||
const size = await getUploadedFileSize(filename);
|
||||
const totalChunks = Math.ceil(size / chunkSize);
|
||||
const totalChunks = Math.ceil(size / (chunkSize + ABYTES));
|
||||
const decryptedLength = size - totalChunks * ABYTES;
|
||||
const error =
|
||||
size === 0
|
||||
@@ -520,7 +520,7 @@ async function downloadFile(
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const totalChunks = Math.ceil(size / chunkSize);
|
||||
const totalChunks = Math.ceil(size / (chunkSize + ABYTES));
|
||||
const decryptedLength = size - totalChunks * ABYTES;
|
||||
if (attachment && attachment.size !== decryptedLength) {
|
||||
const error = `File length mismatch. Expected ${attachment.size} but got ${decryptedLength} bytes. Please upload this file again from the attachment manager. (File hash: ${filename})`;
|
||||
|
||||
@@ -20,8 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { INNCrypto } from "@notesnook/crypto";
|
||||
import CryptoWorker from "./nncrypto.worker?worker";
|
||||
import { wrap } from "comlink";
|
||||
import { NNCrypto as NNCryptoSync } from "@notesnook/crypto";
|
||||
import { isTransferableStreamsSupported } from "../utils/feature-check";
|
||||
|
||||
export const NNCrypto = wrap<INNCrypto>(new CryptoWorker()) as INNCrypto;
|
||||
export const NNCrypto = isTransferableStreamsSupported()
|
||||
? (wrap<INNCrypto>(new CryptoWorker()) as INNCrypto)
|
||||
: new NNCryptoSync();
|
||||
// TODO: disable until we fix the `pull failed` errors for good.
|
||||
// IS_DESKTOP_APP && window.NativeNNCrypto
|
||||
// ? new window.NativeNNCrypto()
|
||||
|
||||
@@ -22,9 +22,28 @@ const FEATURE_CHECKS = {
|
||||
cache: false,
|
||||
indexedDB: false,
|
||||
clonableCryptoKey: false,
|
||||
applePaySupported: false
|
||||
applePaySupported: false,
|
||||
transferableStreams: false
|
||||
};
|
||||
|
||||
export function isTransferableStreamsSupported() {
|
||||
try {
|
||||
const readable = new ReadableStream({
|
||||
pull(controller) {
|
||||
controller.enqueue(new Uint8Array([1, 2, 3]));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
window.postMessage(readable, [readable]);
|
||||
FEATURE_CHECKS.transferableStreams = true;
|
||||
return true;
|
||||
} catch {
|
||||
console.log("Transferable streams not supported");
|
||||
FEATURE_CHECKS.transferableStreams = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function isApplePaySupported() {
|
||||
try {
|
||||
FEATURE_CHECKS.applePaySupported =
|
||||
@@ -99,6 +118,7 @@ async function isCryptoKeyClonable() {
|
||||
|
||||
export async function initializeFeatureChecks() {
|
||||
await Promise.allSettled([
|
||||
isTransferableStreamsSupported(),
|
||||
isOPFSSupported(),
|
||||
isCacheSupported(),
|
||||
isIndexedDBSupported(),
|
||||
|
||||
23
apps/web/src/utils/should-show-wrapped.ts
Normal file
23
apps/web/src/utils/should-show-wrapped.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export const shouldShowWrapped = () => {
|
||||
const now = new Date();
|
||||
return now.getMonth() === 11;
|
||||
};
|
||||
776
apps/web/src/views/wrapped.tsx
Normal file
776
apps/web/src/views/wrapped.tsx
Normal file
@@ -0,0 +1,776 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Button, Flex, Text, Box, FlexProps } from "@theme-ui/components";
|
||||
import { db } from "../common/db";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { NoteStats, WrappedStats } from "@notesnook/core";
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
import { ArrowDown, ArrowLeft, Loading } from "../components/icons";
|
||||
import { hardNavigate } from "../navigation";
|
||||
import { MonthlyActivityHeatmap } from "../components/monthly-activity-heatmap";
|
||||
|
||||
function formatNumber(num: number) {
|
||||
return num.toLocaleString();
|
||||
}
|
||||
function formatCount(num: number) {
|
||||
return num > 1000 ? `${(num / 1000).toFixed(0)}k` : num.toString();
|
||||
}
|
||||
|
||||
interface SlideProps {
|
||||
children: React.ReactNode;
|
||||
pattern?: "dots" | "grid" | "diagonal" | "none";
|
||||
}
|
||||
|
||||
function Slide({
|
||||
children,
|
||||
pattern = "none",
|
||||
sx,
|
||||
...flexProps
|
||||
}: SlideProps & FlexProps) {
|
||||
const slideRef = useRef<HTMLDivElement>(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
|
||||
if (slideRef.current) {
|
||||
observer.observe(slideRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (slideRef.current) {
|
||||
observer.unobserve(slideRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getBackgroundPattern = () => {
|
||||
switch (pattern) {
|
||||
case "dots":
|
||||
return "radial-gradient(circle, var(--border) 1px, transparent 1px)";
|
||||
case "grid":
|
||||
return "linear-gradient(var(--border) 1px, transparent 1px), linear-gradient(90deg, var(--border) 1px, transparent 1px)";
|
||||
case "diagonal":
|
||||
return "repeating-linear-gradient(-45deg, transparent, transparent 20px, var(--border) 20px, var(--border) 21px)";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
};
|
||||
|
||||
const getBackgroundSize = () => {
|
||||
switch (pattern) {
|
||||
case "dots":
|
||||
return "20px 20px";
|
||||
case "grid":
|
||||
return "30px 30px";
|
||||
case "diagonal":
|
||||
default:
|
||||
return "auto";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={slideRef}
|
||||
sx={{
|
||||
minHeight: "100vh",
|
||||
minWidth: "100%",
|
||||
scrollSnapAlign: "start",
|
||||
scrollSnapStop: "always",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
p: 4,
|
||||
position: "relative",
|
||||
backgroundImage: getBackgroundPattern(),
|
||||
backgroundSize: getBackgroundSize(),
|
||||
backgroundPosition: "center"
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
{...flexProps}
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
opacity: isVisible ? 1 : 0,
|
||||
transform: isVisible ? "translateY(0)" : "translateY(50px)",
|
||||
transition: "opacity 0.8s ease-out, transform 0.8s ease-out",
|
||||
...sx
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
function WelcomeSlide({ loading }: { loading: boolean }) {
|
||||
return (
|
||||
<Slide pattern="dots">
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "6rem",
|
||||
fontWeight: "bold",
|
||||
mb: 3,
|
||||
textAlign: "center",
|
||||
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
animation: "slideUp 1s ease-out"
|
||||
}}
|
||||
>
|
||||
🎉
|
||||
</Text>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: ["2rem", "3rem", "4rem"],
|
||||
fontWeight: "bold",
|
||||
mb: 2,
|
||||
textAlign: "center",
|
||||
animation: "slideUp 1s ease-out 0.2s both"
|
||||
}}
|
||||
>
|
||||
Your {new Date().getFullYear()} Wrapped
|
||||
</Text>
|
||||
|
||||
{loading ? (
|
||||
<Loading
|
||||
sx={{
|
||||
animation: "fadeIn 1s ease-out 0.4s both"
|
||||
}}
|
||||
size={30}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: ["1rem", "1.2rem"],
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 1s ease-out 0.4s both"
|
||||
}}
|
||||
>
|
||||
Let's look back at your year in Notesnook
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
mt: 4,
|
||||
fontSize: "title",
|
||||
color: "paragraph-secondary",
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 1s ease-out 0.4s both"
|
||||
}}
|
||||
>
|
||||
Scroll down to explore
|
||||
</Text>
|
||||
<ArrowDown sx={{ mt: 2 }} color="paragraph-secondary" />
|
||||
</>
|
||||
)}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
function TotalNotesSlide({ count }: { count: number }) {
|
||||
return (
|
||||
<Slide pattern="dots">
|
||||
<Flex>
|
||||
<Flex sx={{ flexDirection: "column" }}>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"],
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 0.8s ease-out"
|
||||
}}
|
||||
>
|
||||
You created
|
||||
</Text>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: ["5rem", "7rem", "8rem"],
|
||||
textAlign: "center",
|
||||
color: "accent",
|
||||
animation: "scaleIn 0.8s ease-out 0.2s both"
|
||||
}}
|
||||
>
|
||||
{formatNumber(count)}
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"],
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 0.8s ease-out 0.4s both"
|
||||
}}
|
||||
>
|
||||
notes this year
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
ml: [0, 5],
|
||||
mt: [4, 0],
|
||||
borderLeft: ["none", "1px solid var(--border)"],
|
||||
pl: [0, 5],
|
||||
borderTop: ["1px solid var(--border)", "none"]
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
variant="body"
|
||||
color="paragraph-secondary"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.2rem"],
|
||||
animation: "fadeIn 0.8s ease-out",
|
||||
lineHeight: 1.8
|
||||
}}
|
||||
>
|
||||
That's <strong>{formatNumber(count)}</strong>
|
||||
<br />
|
||||
ideas
|
||||
<br />
|
||||
thoughts
|
||||
<br />
|
||||
memories.
|
||||
<br />
|
||||
100% encrypted.
|
||||
<br />
|
||||
100% yours.
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
const TOTAL_WORDS_TAGLINES = {
|
||||
1000: "That's longer than the average blog post on Medium!",
|
||||
10000: "That's real commitment to your mindspace.",
|
||||
25000: "That's almost the length of a short novel!",
|
||||
50000:
|
||||
"That's the length of The Great Gatsby if you were F. Scott Fitzgerald.",
|
||||
100000: "You wrote more than many published authors this year.",
|
||||
250000: "Woah! Your notes could fill a small library.",
|
||||
500000: "Your vault is becoming a chronicle.",
|
||||
1000000: "That's longer than the entire Harry Potter series!"
|
||||
};
|
||||
function TotalWordsSlide({ count }: { count: number }) {
|
||||
const tagline = Object.entries(TOTAL_WORDS_TAGLINES)
|
||||
.sort((a, b) => Number(b[0]) - Number(a[0]))
|
||||
.find(([threshold]) => count >= Number(threshold))?.[1];
|
||||
return (
|
||||
<Slide pattern="dots">
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"],
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 0.8s ease-out"
|
||||
}}
|
||||
>
|
||||
You wrote a total of
|
||||
</Text>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: ["5rem", "7rem"],
|
||||
textAlign: "center",
|
||||
color: "accent",
|
||||
animation: "scaleIn 0.8s ease-out 0.2s both"
|
||||
}}
|
||||
>
|
||||
{formatNumber(count)}
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"],
|
||||
textAlign: "center",
|
||||
animation: "fadeIn 0.8s ease-out 0.4s both"
|
||||
}}
|
||||
>
|
||||
words this year
|
||||
</Text>
|
||||
{tagline ? (
|
||||
<Text
|
||||
variant="body"
|
||||
color="paragraph-secondary"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.2rem"],
|
||||
animation: "fadeIn 0.8s ease-out",
|
||||
borderTop: "1px solid var(--border)",
|
||||
mt: 3,
|
||||
pt: 3
|
||||
}}
|
||||
>
|
||||
{tagline}
|
||||
</Text>
|
||||
) : null}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
type ActivityStatsSlideProps = {
|
||||
mostNotesCreatedInMonth: NoteStats["mostNotesCreatedInMonth"];
|
||||
mostNotesCreatedInDay: NoteStats["mostNotesCreatedInDay"];
|
||||
};
|
||||
|
||||
function ActivityStatsSlide({
|
||||
mostNotesCreatedInMonth,
|
||||
mostNotesCreatedInDay
|
||||
}: ActivityStatsSlideProps) {
|
||||
if (!mostNotesCreatedInMonth && !mostNotesCreatedInDay) return null;
|
||||
|
||||
return (
|
||||
<Slide pattern="diagonal" sx={{ alignItems: "start" }}>
|
||||
{mostNotesCreatedInMonth && (
|
||||
<>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"]
|
||||
}}
|
||||
>
|
||||
Your most productive month was
|
||||
</Text>
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: ["5rem", "7rem", "3rem"]
|
||||
}}
|
||||
>
|
||||
{mostNotesCreatedInMonth.month}
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{ fontSize: "1rem" }}
|
||||
color="paragraph-secondary"
|
||||
>
|
||||
{formatNumber(mostNotesCreatedInMonth.count)} notes
|
||||
</Text>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{mostNotesCreatedInDay && (
|
||||
<>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
fontSize: ["1.2rem", "1.5rem"],
|
||||
mt: 5
|
||||
}}
|
||||
>
|
||||
Your favorite day to write was
|
||||
</Text>
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: ["5rem", "7rem", "3rem"]
|
||||
}}
|
||||
>
|
||||
{mostNotesCreatedInDay.day}
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{ fontSize: "1rem" }}
|
||||
color="paragraph-secondary"
|
||||
>
|
||||
{formatNumber(mostNotesCreatedInDay.count)} notes
|
||||
</Text>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
function SummarySlide({ stats }: { stats: WrappedStats }) {
|
||||
return (
|
||||
<Slide
|
||||
pattern="dots"
|
||||
sx={{
|
||||
bg: "background-secondary",
|
||||
border: "1px solid var(--accent)",
|
||||
borderRadius: "dialog",
|
||||
boxShadow: "-15px 15px 0px 0px var(--accent)",
|
||||
p: 5
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
height: 20,
|
||||
width: 20,
|
||||
position: "absolute",
|
||||
bottom: "20px",
|
||||
right: "20px"
|
||||
}}
|
||||
>
|
||||
<use href="#themed-logo" />
|
||||
</svg>
|
||||
<Text
|
||||
variant="heading"
|
||||
sx={{
|
||||
fontSize: "1.2rem",
|
||||
fontWeight: 800,
|
||||
textAlign: "center",
|
||||
transform: "skew(-10deg) scaleX(1.5)",
|
||||
letterSpacing: "-1px",
|
||||
mb: "25px",
|
||||
textDecorationLine: "underline",
|
||||
textDecorationColor: "border"
|
||||
}}
|
||||
>
|
||||
NOTESNOOK WRAPPED {new Date().getFullYear()}
|
||||
</Text>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: ["column", "row"],
|
||||
gap: "30px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr 1fr",
|
||||
gridTemplateRows: "1fr 1fr",
|
||||
gap: 4,
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{
|
||||
icon: "📝",
|
||||
count: stats.totalNotes,
|
||||
label: "Notes"
|
||||
},
|
||||
{
|
||||
icon: "🎨",
|
||||
count: stats.totalColors,
|
||||
label: "Colors"
|
||||
},
|
||||
{
|
||||
icon: "📚",
|
||||
count: stats.totalNotebooks,
|
||||
label: "Notebooks"
|
||||
},
|
||||
{
|
||||
icon: "🏷️",
|
||||
count: stats.totalTags,
|
||||
label: "Tags"
|
||||
},
|
||||
{
|
||||
icon: "📂",
|
||||
count: stats.totalAttachments,
|
||||
label: "Files"
|
||||
},
|
||||
{
|
||||
icon: "☁️",
|
||||
count: stats.totalMonographs,
|
||||
label: "Monographs"
|
||||
}
|
||||
].map(({ icon, count, label }) => (
|
||||
<Flex
|
||||
key={label}
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: "1.5rem" }}>{icon}</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "2rem",
|
||||
fontWeight: "bold"
|
||||
}}
|
||||
>
|
||||
{formatCount(count)}
|
||||
</Text>
|
||||
<Text sx={{ fontSize: "0.85rem", color: "fontTertiary" }}>
|
||||
{label}
|
||||
</Text>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
borderTop: "1px solid var(--border)",
|
||||
mt: 6,
|
||||
pt: 3,
|
||||
"& strong": {
|
||||
color: "accent"
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: "1rem", fontWeight: "bold" }}>
|
||||
Fun facts of the year
|
||||
</Text>
|
||||
{stats.mostNotesCreatedInMonth && (
|
||||
<Text sx={{ fontSize: "0.9rem", color: "fontTertiary" }}>
|
||||
📅 Your most productive month was{" "}
|
||||
<Text as="strong">{stats.mostNotesCreatedInMonth.month}</Text>
|
||||
</Text>
|
||||
)}
|
||||
{stats.mostNotesCreatedInDay && (
|
||||
<Text sx={{ fontSize: "0.9rem", color: "fontTertiary" }}>
|
||||
🗓️ Your favorite day to write was{" "}
|
||||
<Text as="strong">{stats.mostNotesCreatedInDay.day}</Text>
|
||||
</Text>
|
||||
)}
|
||||
{stats.largestNote && (
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "0.9rem",
|
||||
color: "fontTertiary",
|
||||
maxWidth: 340
|
||||
}}
|
||||
>
|
||||
📝 Your longest note was{" "}
|
||||
<Text as="strong">
|
||||
{formatNumber(stats.largestNote.length)}
|
||||
{" words"}
|
||||
</Text>
|
||||
</Text>
|
||||
)}
|
||||
{stats.largestAttachment && (
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "0.9rem",
|
||||
color: "fontTertiary",
|
||||
maxWidth: 340
|
||||
}}
|
||||
>
|
||||
🔗 Your largest attachment was{" "}
|
||||
<strong>{formatBytes(stats.largestAttachment.size)}</strong>
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
justifyContent: "stretch",
|
||||
height: "100%",
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: "2.5rem" }}>✍️</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "3rem",
|
||||
fontWeight: "bold",
|
||||
color: "accent"
|
||||
}}
|
||||
>
|
||||
{formatNumber(stats.totalWords)}
|
||||
</Text>
|
||||
<Text sx={{ fontSize: "1rem", color: "fontTertiary" }}>
|
||||
Words Written
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
borderTop: "1px solid var(--border)",
|
||||
mt: 6,
|
||||
pt: 2,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<MonthlyActivityHeatmap monthlyStats={stats.monthlyStats} />
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
color: "fontTertiary",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
Notes per month
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Text
|
||||
variant="body"
|
||||
color="paragraph-secondary"
|
||||
sx={{ mt: 2, borderTop: "1px solid var(--border)", pt: 2 }}
|
||||
>
|
||||
Generated 100% locally on your device.
|
||||
</Text>
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Wrapped() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [stats, setStats] = useState<WrappedStats>();
|
||||
|
||||
useEffect(() => {
|
||||
async function loadWrapped() {
|
||||
setLoading(true);
|
||||
try {
|
||||
console.time("wrapped - getting from core");
|
||||
const wrapped = await db.wrapped.get();
|
||||
console.timeEnd("wrapped - getting from core");
|
||||
setStats(wrapped);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
loadWrapped();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>
|
||||
{`
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
`}
|
||||
</style>
|
||||
<Flex
|
||||
sx={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
flexDirection: "column",
|
||||
overflowY: "scroll",
|
||||
overflowX: "hidden",
|
||||
scrollSnapType: "y mandatory",
|
||||
scrollBehavior: "smooth",
|
||||
"&::-webkit-scrollbar": {
|
||||
display: "none"
|
||||
},
|
||||
msOverflowStyle: "none",
|
||||
scrollbarWidth: "none"
|
||||
}}
|
||||
>
|
||||
<WelcomeSlide loading={loading} />
|
||||
{stats ? (
|
||||
<>
|
||||
<TotalNotesSlide count={stats.totalNotes} />
|
||||
{stats.totalWords > 0 && (
|
||||
<TotalWordsSlide count={stats.totalWords} />
|
||||
)}
|
||||
{(stats.mostNotesCreatedInMonth || stats.mostNotesCreatedInDay) && (
|
||||
<ActivityStatsSlide
|
||||
mostNotesCreatedInMonth={stats.mostNotesCreatedInMonth}
|
||||
mostNotesCreatedInDay={stats.mostNotesCreatedInDay}
|
||||
/>
|
||||
)}
|
||||
<SummarySlide stats={stats} />
|
||||
</>
|
||||
) : null}
|
||||
</Flex>
|
||||
|
||||
<Button
|
||||
onClick={() => hardNavigate("/")}
|
||||
variant="secondary"
|
||||
sx={{
|
||||
position: "fixed",
|
||||
top: 3,
|
||||
left: 3,
|
||||
zIndex: 1000
|
||||
}}
|
||||
>
|
||||
<Flex sx={{ alignItems: "center", gap: 1, justifyContent: "center" }}>
|
||||
<ArrowLeft size={16} />
|
||||
<Text variant="body">Go back to app</Text>
|
||||
</Flex>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -9,71 +9,73 @@ The following keyboard shortcuts will help you navigate Notesnook faster.
|
||||
|
||||
### General
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| -------------------------------------------------- | ------ | ------------- | --- |
|
||||
| Search in notes list view if editor is not focused | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Settings | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Keyboard shortcuts | Ctrl / | Ctrl / | ⌘ / |
|
||||
| New note | - | Ctrl N | ⌘ N |
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Search in notes list view if editor is not focused | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Settings | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Keyboard shortcuts | Ctrl / | Ctrl / | ⌘ / |
|
||||
| New note | - | Ctrl N | ⌘ N |
|
||||
|
||||
### Navigation
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| ---------------- | ------------------------- | ------------- | ------- |
|
||||
| Next tab | Ctrl Alt → / Ctrl Alt ⇧ → | Ctrl tab | ⌘ tab |
|
||||
| Previous tab | Ctrl Alt ← / Ctrl Alt ⇧ ← | Ctrl ⇧ tab | ⌘ ⇧ tab |
|
||||
| Command palette | Ctrl ⇧ P | Ctrl ⇧ P | ⌘ ⇧ P |
|
||||
| Quick open | Ctrl P | Ctrl P | ⌘ P |
|
||||
| New tab | - | Ctrl T | ⌘ T |
|
||||
| Close active tab | - | Ctrl W | ⌘ W |
|
||||
| Close all tabs | - | Ctrl ⇧ W | ⌘ ⇧ W |
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Next tab | Ctrl Alt → / Ctrl Alt ⇧ → | Ctrl tab | ⌘ tab |
|
||||
| Previous tab | Ctrl Alt ← / Ctrl Alt ⇧ ← | Ctrl ⇧ tab | ⌘ ⇧ tab |
|
||||
| Command palette | Ctrl ⇧ P | Ctrl ⇧ P | ⌘ ⇧ P |
|
||||
| Quick open | Ctrl P | Ctrl P | ⌘ P |
|
||||
| New tab | - | Ctrl T | ⌘ T |
|
||||
| Close active tab | - | Ctrl W | ⌘ W |
|
||||
| Close all tabs | - | Ctrl ⇧ W | ⌘ ⇧ W |
|
||||
|
||||
### Editor
|
||||
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| ---------------------------------- | ----------------- | ----------------- | ----------- |
|
||||
| Add attachment | Ctrl ⇧ A | Ctrl ⇧ A | ⌘ ⇧ A |
|
||||
| Insert blockquote | Ctrl ⇧ B | Ctrl ⇧ B | ⌘ ⇧ B |
|
||||
| Toggle bold | Ctrl B | Ctrl B | ⌘ B |
|
||||
| Toggle bullet list | Ctrl ⇧ 8 | Ctrl ⇧ 8 | ⌘ ⇧ 8 |
|
||||
| Toggle check list | Ctrl ⇧ 9 | Ctrl ⇧ 9 | ⌘ ⇧ 9 |
|
||||
| Split list item | ↵ | ↵ | ↵ |
|
||||
| Lift list item | ⇧ Tab | ⇧ Tab | ⇧ Tab |
|
||||
| Sink list item | Ctrl ⇧ Down | Ctrl ⇧ Down | ⌘ ⇧ Down |
|
||||
| Toggle code | Ctrl E | Ctrl E | ⌘ E |
|
||||
| Toggle code block | Ctrl ⇧ C | Ctrl ⇧ C | ⌘ ⇧ C |
|
||||
| Insert date | Alt D | Alt D | ⌥ D |
|
||||
| Insert time | Alt T | Alt T | ⌥ T |
|
||||
| Insert date and time | Ctrl Alt D | Ctrl Alt D | ⌘ ⌥ D |
|
||||
| Insert date and time with timezone | Ctrl Alt Z | Ctrl Alt Z | ⌘ ⌥ Z |
|
||||
| Increase font size | Ctrl [ | Ctrl [ | ⌘ [ |
|
||||
| Decrease font size | Ctrl ] | Ctrl ] | ⌘ ] |
|
||||
| Insert paragraph | Ctrl ⇧ 0 | Ctrl ⇧ 0 | ⌘ ⇧ 0 |
|
||||
| Insert heading 1 | Ctrl Alt 1 | Ctrl Alt 1 | ⌘ ⌥ 1 |
|
||||
| Insert heading 2 | Ctrl Alt 2 | Ctrl Alt 2 | ⌘ ⌥ 2 |
|
||||
| Insert heading 3 | Ctrl Alt 3 | Ctrl Alt 3 | ⌘ ⌥ 3 |
|
||||
| Insert heading 4 | Ctrl Alt 4 | Ctrl Alt 4 | ⌘ ⌥ 4 |
|
||||
| Insert heading 5 | Ctrl Alt 5 | Ctrl Alt 5 | ⌘ ⌥ 5 |
|
||||
| Insert heading 6 | Ctrl Alt 6 | Ctrl Alt 6 | ⌘ ⌥ 6 |
|
||||
| Undo | Ctrl Z | Ctrl Z | ⌘ Z |
|
||||
| Redo | Ctrl ⇧ Z / Ctrl Y | Ctrl ⇧ Z / Ctrl Y | ⌘ ⇧ Z / ⌘ Y |
|
||||
| Add image | Ctrl ⇧ I | Ctrl ⇧ I | ⌘ ⇧ I |
|
||||
| Toggle italic | Ctrl I | Ctrl I | ⌘ I |
|
||||
| Remove formatting in selection | Ctrl \ | Ctrl \ | ⌘ \ |
|
||||
| Insert internal link | Ctrl ⇧ K | Ctrl ⇧ K | ⌘ ⇧ K |
|
||||
| Insert link | Ctrl K | Ctrl K | ⌘ K |
|
||||
| Insert math block | Ctrl ⇧ M | Ctrl ⇧ M | ⌘ ⇧ M |
|
||||
| Toggle ordered list | Ctrl ⇧ 7 | Ctrl ⇧ 7 | ⌘ ⇧ 7 |
|
||||
| Toggle outline list | Ctrl ⇧ O | Ctrl ⇧ O | ⌘ ⇧ O |
|
||||
| Toggle outline list expand | Ctrl Space | Ctrl Space | ⌘ Space |
|
||||
| Open search | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Toggle strike | Ctrl ⇧ S | Ctrl ⇧ S | ⌘ ⇧ S |
|
||||
| Toggle subscript | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Toggle superscript | Ctrl . | Ctrl . | ⌘ . |
|
||||
| Toggle task list | Ctrl ⇧ T | Ctrl ⇧ T | ⌘ ⇧ T |
|
||||
| Text align center | Ctrl ⇧ E | Ctrl ⇧ E | ⌘ ⇧ E |
|
||||
| Text align justify | Ctrl ⇧ J | Ctrl ⇧ J | ⌘ ⇧ J |
|
||||
| Text align left | Ctrl ⇧ L | Ctrl ⇧ L | ⌘ ⇧ L |
|
||||
| Text align right | Ctrl ⇧ R | Ctrl ⇧ R | ⌘ ⇧ R |
|
||||
| Underline | Ctrl U | Ctrl U | ⌘ U |
|
||||
| Toggle highlight | Ctrl Alt H | Ctrl Alt H | ⌘ ⌥ H |
|
||||
| Description | Web | Windows/Linux | Mac |
|
||||
| --- | --- | --- | --- |
|
||||
| Add attachment | Ctrl ⇧ A | Ctrl ⇧ A | ⌘ ⇧ A |
|
||||
| Insert blockquote | Ctrl ⇧ B | Ctrl ⇧ B | ⌘ ⇧ B |
|
||||
| Toggle bold | Ctrl B | Ctrl B | ⌘ B |
|
||||
| Toggle bullet list | Ctrl ⇧ 8 | Ctrl ⇧ 8 | ⌘ ⇧ 8 |
|
||||
| Toggle check list | Ctrl ⇧ 9 | Ctrl ⇧ 9 | ⌘ ⇧ 9 |
|
||||
| Split list item | ↵ | ↵ | ↵ |
|
||||
| Lift list item | ⇧ Tab | ⇧ Tab | ⇧ Tab |
|
||||
| Sink list item | Ctrl ⇧ Down | Ctrl ⇧ Down | ⌘ ⇧ Down |
|
||||
| Toggle code | Ctrl E | Ctrl E | ⌘ E |
|
||||
| Toggle code block | Ctrl ⇧ C | Ctrl ⇧ C | ⌘ ⇧ C |
|
||||
| Insert date | Alt D | Alt D | ⌥ D |
|
||||
| Insert time | Alt T | Alt T | ⌥ T |
|
||||
| Insert date and time | Ctrl Alt D | Ctrl Alt D | ⌘ ⌥ D |
|
||||
| Insert date and time with timezone | Ctrl Alt Z | Ctrl Alt Z | ⌘ ⌥ Z |
|
||||
| Increase font size | Ctrl [ | Ctrl [ | ⌘ [ |
|
||||
| Decrease font size | Ctrl ] | Ctrl ] | ⌘ ] |
|
||||
| Insert paragraph | Ctrl Alt 0 | Ctrl Alt 0 | ⌘ ⌥ 0 |
|
||||
| Insert heading 1 | Ctrl Alt 1 | Ctrl Alt 1 | ⌘ ⌥ 1 |
|
||||
| Insert heading 2 | Ctrl Alt 2 | Ctrl Alt 2 | ⌘ ⌥ 2 |
|
||||
| Insert heading 3 | Ctrl Alt 3 | Ctrl Alt 3 | ⌘ ⌥ 3 |
|
||||
| Insert heading 4 | Ctrl Alt 4 | Ctrl Alt 4 | ⌘ ⌥ 4 |
|
||||
| Insert heading 5 | Ctrl Alt 5 | Ctrl Alt 5 | ⌘ ⌥ 5 |
|
||||
| Insert heading 6 | Ctrl Alt 6 | Ctrl Alt 6 | ⌘ ⌥ 6 |
|
||||
| Undo | Ctrl Z | Ctrl Z | ⌘ Z |
|
||||
| Redo | Ctrl ⇧ Z / Ctrl Y | Ctrl ⇧ Z / Ctrl Y | ⌘ ⇧ Z / ⌘ Y |
|
||||
| Add image | Ctrl ⇧ I | Ctrl ⇧ I | ⌘ ⇧ I |
|
||||
| Toggle italic | Ctrl I | Ctrl I | ⌘ I |
|
||||
| Remove formatting in selection | Ctrl \ | Ctrl \ | ⌘ \ |
|
||||
| Insert internal link | Ctrl ⇧ K | Ctrl ⇧ K | ⌘ ⇧ K |
|
||||
| Insert link | Ctrl K | Ctrl K | ⌘ K |
|
||||
| Insert math block | Ctrl ⇧ M | Ctrl ⇧ M | ⌘ ⇧ M |
|
||||
| Toggle ordered list | Ctrl ⇧ 7 | Ctrl ⇧ 7 | ⌘ ⇧ 7 |
|
||||
| Toggle outline list | Ctrl ⇧ O | Ctrl ⇧ O | ⌘ ⇧ O |
|
||||
| Toggle outline list expand | Ctrl Space | Ctrl Space | ⌘ Space |
|
||||
| Open search | Ctrl F | Ctrl F | ⌘ F |
|
||||
| Open search and replace | Ctrl Alt F | Ctrl Alt F | ⌘ ⌥ F |
|
||||
| Toggle strike | Ctrl ⇧ S | Ctrl ⇧ S | ⌘ ⇧ S |
|
||||
| Toggle subscript | Ctrl , | Ctrl , | ⌘ , |
|
||||
| Toggle superscript | Ctrl . | Ctrl . | ⌘ . |
|
||||
| Toggle task list | Ctrl ⇧ T | Ctrl ⇧ T | ⌘ ⇧ T |
|
||||
| Text align center | Ctrl ⇧ E | Ctrl ⇧ E | ⌘ ⇧ E |
|
||||
| Text align justify | Ctrl ⇧ J | Ctrl ⇧ J | ⌘ ⇧ J |
|
||||
| Text align left | Ctrl ⇧ L | Ctrl ⇧ L | ⌘ ⇧ L |
|
||||
| Text align right | Ctrl ⇧ R | Ctrl ⇧ R | ⌘ ⇧ R |
|
||||
| Underline | Ctrl U | Ctrl U | ⌘ U |
|
||||
| Toggle highlight | Ctrl Alt H | Ctrl Alt H | ⌘ ⌥ H |
|
||||
| Toggle text color | Ctrl Alt C | Ctrl Alt C | ⌘ ⌥ C |
|
||||
5
fastlane/metadata/android/en-US/changelogs/15434.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15434.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
- Get an overview of your year with Notesnook with Wrapped 2025
|
||||
- Cell selection and column resizing in tables
|
||||
- Bug fixes and minor improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -34,10 +34,7 @@ export default defineConfig({
|
||||
js: "clipper.bundle.js"
|
||||
},
|
||||
distPath: {
|
||||
root: path.resolve(
|
||||
__dirname,
|
||||
"../../apps/mobile/native/ios/extension.bundle"
|
||||
)
|
||||
root: path.resolve(__dirname, "../../apps/mobile/ios/extension.bundle")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -26,9 +26,13 @@ type CloneNodeOptions = {
|
||||
|
||||
export function cloneNode(node: HTMLElement, options: CloneNodeOptions) {
|
||||
node = node.cloneNode(true) as HTMLElement;
|
||||
const images = node.querySelectorAll("img");
|
||||
if (!options.images) {
|
||||
const images = node.getElementsByTagName("img");
|
||||
for (const image of images) image.remove();
|
||||
} else {
|
||||
for (const image of images) {
|
||||
image.src = image.currentSrc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.styles) {
|
||||
|
||||
1
packages/common/package-lock.json
generated
1
packages/common/package-lock.json
generated
@@ -41,6 +41,7 @@
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"dayjs": "1.11.13",
|
||||
"dom-serializer": "^2.0.0",
|
||||
|
||||
@@ -335,6 +335,12 @@ export const tiptapKeys = {
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
openSearchAndReplace: {
|
||||
keys: "Mod-Alt-f",
|
||||
description: "Open search and replace",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleStrike: {
|
||||
keys: "Mod-Shift-S",
|
||||
description: "Toggle strike",
|
||||
@@ -394,6 +400,12 @@ export const tiptapKeys = {
|
||||
description: "Toggle highlight",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
},
|
||||
toggleTextColor: {
|
||||
keys: "Mod-Alt-c",
|
||||
description: "Toggle text color",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
}
|
||||
} satisfies Record<string, TipTapKey>;
|
||||
|
||||
|
||||
7
packages/core/package-lock.json
generated
7
packages/core/package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"dayjs": "1.11.13",
|
||||
"dom-serializer": "^2.0.0",
|
||||
@@ -1391,6 +1392,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/alfaaz": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/alfaaz/-/alfaaz-1.1.0.tgz",
|
||||
"integrity": "sha512-J/P07R41APslK7NmD5303bwStN8jpRA4DdvtLeAr1Jhfj6XWGrASUWI0G6jbWjJAZyw3Lu1Pb4J8rsM/cb+xDQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"dayjs": "1.11.13",
|
||||
"dom-serializer": "^2.0.0",
|
||||
|
||||
@@ -83,6 +83,7 @@ import { ConfigStorage } from "../database/config.js";
|
||||
import { LazyPromise } from "../utils/lazy-promise.js";
|
||||
import { InboxApiKeys } from "./inbox-api-keys.js";
|
||||
import { Circle } from "./circle.js";
|
||||
import { Wrapped } from "./wrapped.js";
|
||||
|
||||
type EventSourceConstructor = new (
|
||||
uri: string,
|
||||
@@ -224,6 +225,8 @@ class Database {
|
||||
|
||||
inboxApiKeys = new InboxApiKeys(this, this.tokenManager);
|
||||
|
||||
wrapped = new Wrapped(this);
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
|
||||
342
packages/core/src/api/wrapped.ts
Normal file
342
packages/core/src/api/wrapped.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Parser } from "htmlparser2";
|
||||
import Database from "./index.js";
|
||||
import { countWords } from "alfaaz";
|
||||
import { DatabaseSchema, isFalse } from "../database/index.js";
|
||||
import { SelectQueryBuilder } from "@streetwriters/kysely";
|
||||
|
||||
const dayNames = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday"
|
||||
];
|
||||
|
||||
const monthNames = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
];
|
||||
|
||||
export type NoteStats = {
|
||||
totalNotes: number;
|
||||
totalWords: number;
|
||||
totalMonographs: number;
|
||||
mostNotesCreatedInMonth: { month: string; count: number } | null;
|
||||
mostNotesCreatedInDay: { day: string; count: number } | null;
|
||||
monthlyStats: Record<string, number>;
|
||||
dayOfWeekStats: Record<string, number>;
|
||||
largestNote: { title: string; length: number } | null;
|
||||
};
|
||||
|
||||
export type OrganizationStats = {
|
||||
totalNotebooks: number;
|
||||
totalTags: number;
|
||||
mostUsedTags: { id: string; title: string; noteCount: number }[];
|
||||
mostActiveNotebooks: { id: string; title: string; noteCount: number }[];
|
||||
totalColors: number;
|
||||
};
|
||||
|
||||
export type AttachmentStats = {
|
||||
totalAttachments: number;
|
||||
totalStorageUsed: number;
|
||||
largestAttachment: { id: string; filename: string; size: number } | null;
|
||||
mostCommonFileType: string | null;
|
||||
};
|
||||
|
||||
export type WrappedStats = NoteStats & OrganizationStats & AttachmentStats;
|
||||
|
||||
export class Wrapped {
|
||||
constructor(private readonly db: Database) {}
|
||||
|
||||
async get(): Promise<WrappedStats> {
|
||||
const { startDate, endDate } = this.getYearRange();
|
||||
|
||||
const [noteStats, organizationStats, attachmentStats] = await Promise.all([
|
||||
this.getNoteStats(startDate, endDate),
|
||||
this.getOrganizationStats(startDate, endDate),
|
||||
this.getAttachmentStats(startDate, endDate)
|
||||
]);
|
||||
|
||||
return {
|
||||
...noteStats,
|
||||
...organizationStats,
|
||||
...attachmentStats
|
||||
};
|
||||
}
|
||||
|
||||
private getYearRange(): { startDate: number; endDate: number } {
|
||||
const year = new Date().getFullYear();
|
||||
const startDate = new Date(year, 0, 1, 0, 0, 0, 0).getTime();
|
||||
const endDate = new Date(year + 1, 0, 1, 0, 0, 0, 0).getTime();
|
||||
return { startDate, endDate };
|
||||
}
|
||||
|
||||
private async getNoteStats(
|
||||
startDate: number,
|
||||
endDate: number
|
||||
): Promise<NoteStats> {
|
||||
const notesSelector = this.db
|
||||
.sql()
|
||||
.selectFrom("notes")
|
||||
.where((eb) => eb("dateCreated", ">=", startDate))
|
||||
.where((eb) => eb("dateCreated", "<", endDate))
|
||||
.where(isFalse("deleted"))
|
||||
.where(isFalse("dateDeleted"));
|
||||
const notes = await notesSelector.select(["notes.dateCreated"]).execute();
|
||||
|
||||
const monthlyStats: Map<string, number> = new Map();
|
||||
const dayOfWeekStats: Map<string, number> = new Map();
|
||||
let totalNotes = 0;
|
||||
|
||||
for (const note of notes) {
|
||||
if (!note.dateCreated) continue;
|
||||
|
||||
totalNotes++;
|
||||
const month = new Date(note.dateCreated).getMonth();
|
||||
const monthName = monthNames[month];
|
||||
monthlyStats.set(monthName, (monthlyStats.get(monthName) || 0) + 1);
|
||||
const dayOfWeek = new Date(note.dateCreated).getDay();
|
||||
const dayName = dayNames[dayOfWeek];
|
||||
dayOfWeekStats.set(dayName, (dayOfWeekStats.get(dayName) || 0) + 1);
|
||||
}
|
||||
|
||||
let mostNotesCreatedInMonth: NoteStats["mostNotesCreatedInMonth"] = null;
|
||||
for (const [month, count] of monthlyStats.entries()) {
|
||||
if (!mostNotesCreatedInMonth || count > mostNotesCreatedInMonth.count) {
|
||||
mostNotesCreatedInMonth = { month, count };
|
||||
}
|
||||
}
|
||||
|
||||
let mostNotesCreatedInDay: NoteStats["mostNotesCreatedInDay"] = null;
|
||||
let maxDayCount = 0;
|
||||
for (const [day, count] of dayOfWeekStats.entries()) {
|
||||
if (count > maxDayCount) {
|
||||
maxDayCount = count;
|
||||
mostNotesCreatedInDay = { day, count };
|
||||
}
|
||||
}
|
||||
|
||||
const totalMonographs = await this.db.monographs.all
|
||||
.where((eb) =>
|
||||
eb.and([
|
||||
eb("dateCreated", ">=", startDate),
|
||||
eb("dateCreated", "<", endDate)
|
||||
])
|
||||
)
|
||||
.count();
|
||||
|
||||
const { largestNote, totalWords } = await this.countTotalWords(
|
||||
notesSelector
|
||||
);
|
||||
|
||||
return {
|
||||
totalNotes,
|
||||
totalWords,
|
||||
totalMonographs,
|
||||
largestNote: largestNote
|
||||
? {
|
||||
title: (await this.db.notes.note(largestNote.id))?.title || "",
|
||||
length: largestNote.wordCount
|
||||
}
|
||||
: null,
|
||||
monthlyStats: Object.fromEntries(monthlyStats),
|
||||
dayOfWeekStats: Object.fromEntries(dayOfWeekStats),
|
||||
mostNotesCreatedInMonth,
|
||||
mostNotesCreatedInDay
|
||||
};
|
||||
}
|
||||
|
||||
private async countItemNotes<T extends { id: string; title: string }>(
|
||||
items: T[],
|
||||
itemType: "tag" | "notebook"
|
||||
): Promise<Array<T & { noteCount: number }>> {
|
||||
const allRelations = await this.db.relations
|
||||
.from({ ids: items.map((item) => item.id), type: itemType }, "note")
|
||||
.get();
|
||||
|
||||
const noteCounts: Map<string, number> = new Map();
|
||||
for (const relation of allRelations) {
|
||||
const itemId = relation.fromId;
|
||||
noteCounts.set(itemId, (noteCounts.get(itemId) || 0) + 1);
|
||||
}
|
||||
|
||||
return items
|
||||
.map((item) => ({
|
||||
...item,
|
||||
noteCount: noteCounts.get(item.id) || 0
|
||||
}))
|
||||
.filter((item) => item.noteCount > 0)
|
||||
.sort((a, b) => b.noteCount - a.noteCount);
|
||||
}
|
||||
|
||||
private async getOrganizationStats(
|
||||
startDate: number,
|
||||
endDate: number
|
||||
): Promise<OrganizationStats> {
|
||||
const notebookSelector = this.db.notebooks.all
|
||||
.where((eb) => eb("dateCreated", ">=", startDate))
|
||||
.where((eb) => eb("dateCreated", "<", endDate));
|
||||
const tagSelector = this.db.tags.all
|
||||
.where((eb) => eb("dateCreated", ">=", startDate))
|
||||
.where((eb) => eb("dateCreated", "<", endDate));
|
||||
|
||||
const [totalNotebooks, totalTags, tags, notebooks, totalColors] =
|
||||
await Promise.all([
|
||||
notebookSelector.count(),
|
||||
tagSelector.count(),
|
||||
tagSelector.fields(["tags.id", "tags.title"]).items(),
|
||||
notebookSelector.fields(["notebooks.id", "notebooks.title"]).items(),
|
||||
this.db.colors.all
|
||||
.where((eb) => eb("dateCreated", ">=", startDate))
|
||||
.where((eb) => eb("dateCreated", "<", endDate))
|
||||
.count()
|
||||
]);
|
||||
|
||||
const tagNotes = await this.countItemNotes(tags, "tag");
|
||||
const mostUsedTags = tagNotes.slice(0, 3);
|
||||
|
||||
const notebookNotes = await this.countItemNotes(notebooks, "notebook");
|
||||
const mostActiveNotebooks = notebookNotes.slice(0, 3);
|
||||
|
||||
return {
|
||||
totalNotebooks,
|
||||
totalTags,
|
||||
mostUsedTags:
|
||||
mostUsedTags.length > 0
|
||||
? mostUsedTags
|
||||
: tags.slice(0, 3).map((tag) => ({ ...tag, noteCount: 0 })),
|
||||
mostActiveNotebooks:
|
||||
mostActiveNotebooks.length > 0
|
||||
? mostActiveNotebooks
|
||||
: notebooks.slice(0, 3).map((n) => ({ ...n, noteCount: 0 })),
|
||||
totalColors
|
||||
};
|
||||
}
|
||||
|
||||
private async getAttachmentStats(
|
||||
startDate: number,
|
||||
endDate: number
|
||||
): Promise<AttachmentStats> {
|
||||
const attachmentsSelector = this.db.attachments.all
|
||||
.where((eb) => eb("dateCreated", ">=", startDate))
|
||||
.where((eb) => eb("dateCreated", "<", endDate));
|
||||
const totalAttachments = await attachmentsSelector.count();
|
||||
|
||||
if (totalAttachments === 0) {
|
||||
return {
|
||||
totalAttachments: 0,
|
||||
totalStorageUsed: 0,
|
||||
largestAttachment: null,
|
||||
mostCommonFileType: null
|
||||
};
|
||||
}
|
||||
|
||||
const totalStorageUsed =
|
||||
(await this.db.attachments.totalSize(attachmentsSelector)) || 0;
|
||||
const attachments = await attachmentsSelector.items();
|
||||
let largestAttachment: AttachmentStats["largestAttachment"] = null;
|
||||
const mimeTypeCounts: Map<string, number> = new Map();
|
||||
|
||||
for (const attachment of attachments) {
|
||||
if (!largestAttachment || attachment.size > largestAttachment.size) {
|
||||
largestAttachment = {
|
||||
id: attachment.id,
|
||||
filename: attachment.filename,
|
||||
size: attachment.size
|
||||
};
|
||||
}
|
||||
|
||||
const mimeType = attachment.mimeType.split("/")[0] || attachment.mimeType;
|
||||
mimeTypeCounts.set(mimeType, (mimeTypeCounts.get(mimeType) || 0) + 1);
|
||||
}
|
||||
|
||||
let mostCommonFileType: string | null = null;
|
||||
let maxCount = 0;
|
||||
for (const [mimeType, count] of mimeTypeCounts.entries()) {
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
mostCommonFileType = mimeType;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalAttachments,
|
||||
totalStorageUsed,
|
||||
largestAttachment,
|
||||
mostCommonFileType
|
||||
};
|
||||
}
|
||||
|
||||
private async countTotalWords(
|
||||
selector: SelectQueryBuilder<DatabaseSchema, "notes", unknown>
|
||||
) {
|
||||
let words = 0;
|
||||
let largestNote = { id: "", wordCount: 0 };
|
||||
const contents = await this.db
|
||||
.sql()
|
||||
.selectFrom("content")
|
||||
.where("noteId", "in", selector.select("id"))
|
||||
.where(isFalse("locked"))
|
||||
.where(isFalse("deleted"))
|
||||
.select(["content.data", "content.noteId"])
|
||||
.execute();
|
||||
|
||||
for (const content of contents) {
|
||||
if (typeof content?.data !== "string") continue;
|
||||
const counted = countWords(toTextContent(content.data));
|
||||
words += counted;
|
||||
if (content.noteId && counted > largestNote.wordCount) {
|
||||
largestNote = { id: content.noteId, wordCount: counted };
|
||||
}
|
||||
}
|
||||
|
||||
return { totalWords: words, largestNote };
|
||||
}
|
||||
}
|
||||
|
||||
function toTextContent(html: string) {
|
||||
let text = "";
|
||||
|
||||
const parser = new Parser({
|
||||
ontext: (data) => {
|
||||
text += data;
|
||||
},
|
||||
onclosetag() {
|
||||
text += " ";
|
||||
}
|
||||
});
|
||||
parser.write(html);
|
||||
parser.end();
|
||||
return text;
|
||||
}
|
||||
@@ -45,3 +45,4 @@ export type { SyncOptions } from "./api/sync/index.js";
|
||||
export { sanitizeTag } from "./collections/tags.js";
|
||||
export { default as DataURL } from "./utils/dataurl.js";
|
||||
export { type ResolveInternalLink } from "./content-types/tiptap.js";
|
||||
export type * from "./api/wrapped.js";
|
||||
|
||||
@@ -604,7 +604,7 @@ export type User = {
|
||||
cancelURL: string | null;
|
||||
expiry: number;
|
||||
trialExpiry?: number;
|
||||
productId: string;
|
||||
productId: string | null;
|
||||
provider: SubscriptionProvider;
|
||||
start: number;
|
||||
plan: SubscriptionPlan;
|
||||
|
||||
@@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { base64_variants, ISodium } from "@notesnook/sodium";
|
||||
import KeyUtils from "./keyutils.js";
|
||||
import { ISodium } from "@notesnook/sodium";
|
||||
import KeyUtils, { base64_variants } from "./keyutils.js";
|
||||
import {
|
||||
Cipher,
|
||||
Output,
|
||||
|
||||
@@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ISodium, base64_variants } from "@notesnook/sodium";
|
||||
import KeyUtils from "./keyutils.js";
|
||||
import { ISodium } from "@notesnook/sodium";
|
||||
import KeyUtils, { base64_variants } from "./keyutils.js";
|
||||
import { Chunk, Cipher, Input, DataFormat, SerializedKey } from "./types.js";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ISodium, Sodium } from "@notesnook/sodium";
|
||||
import { ISodium } from "@notesnook/sodium";
|
||||
import Decryption from "./decryption.js";
|
||||
import Encryption from "./encryption.js";
|
||||
import { INNCrypto } from "./interfaces.js";
|
||||
@@ -37,10 +37,11 @@ import {
|
||||
|
||||
export class NNCrypto implements INNCrypto {
|
||||
private isReady = false;
|
||||
private sodium: ISodium = new Sodium();
|
||||
private sodium!: ISodium;
|
||||
|
||||
private async init() {
|
||||
if (this.isReady) return;
|
||||
this.sodium = new (await import("@notesnook/sodium")).Sodium();
|
||||
await this.sodium.initialize();
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { base64_variants, ISodium } from "@notesnook/sodium";
|
||||
import { ISodium } from "@notesnook/sodium";
|
||||
import {
|
||||
EncryptionKey,
|
||||
EncryptionKeyPair,
|
||||
@@ -25,6 +25,13 @@ import {
|
||||
SerializedKeyPair
|
||||
} from "./types.js";
|
||||
|
||||
export enum base64_variants {
|
||||
ORIGINAL = 1,
|
||||
ORIGINAL_NO_PADDING = 3,
|
||||
URLSAFE = 5,
|
||||
URLSAFE_NO_PADDING = 7
|
||||
}
|
||||
|
||||
export default class KeyUtils {
|
||||
static deriveKey(
|
||||
sodium: ISodium,
|
||||
|
||||
@@ -109,3 +109,19 @@ for (const { name, setNode } of nodes) {
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
|
||||
test("empty heading should have empty class", () => {
|
||||
const { editor } = createEditor({
|
||||
extensions: {
|
||||
heading: Heading.configure({ levels: [1, 2, 3, 4, 5, 6] })
|
||||
},
|
||||
initialContent: "<h1></h1>"
|
||||
});
|
||||
|
||||
const headingElement = editor.view.dom.querySelector("h1");
|
||||
expect(headingElement?.classList.contains("empty")).toBe(true);
|
||||
|
||||
editor.commands.setTextSelection(0);
|
||||
editor.commands.insertContent("Some content");
|
||||
expect(headingElement?.classList.contains("empty")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -23,11 +23,9 @@ import {
|
||||
textblockTypeInputRule
|
||||
} from "@tiptap/core";
|
||||
import { Heading as TiptapHeading } from "@tiptap/extension-heading";
|
||||
import { isClickWithinBounds } from "../../utils/prosemirror.js";
|
||||
import { Plugin, PluginKey, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Node } from "@tiptap/pm/model";
|
||||
import { useToolbarStore } from "../../toolbar/stores/toolbar-store.js";
|
||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||
import { Plugin, PluginKey, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Callout } from "../callout/callout.js";
|
||||
|
||||
const COLLAPSIBLE_BLOCK_TYPES = [
|
||||
"paragraph",
|
||||
@@ -44,7 +42,8 @@ const COLLAPSIBLE_BLOCK_TYPES = [
|
||||
"outlineList",
|
||||
"mathBlock",
|
||||
"webclip",
|
||||
"embed"
|
||||
"embed",
|
||||
"horizontalRule"
|
||||
];
|
||||
|
||||
const HEADING_REGEX = /^(#{1,6})\s$/;
|
||||
@@ -174,6 +173,10 @@ export const Heading = TiptapHeading.extend({
|
||||
heading.setAttribute(attr, HTMLAttributes[attr]);
|
||||
}
|
||||
|
||||
if (node.textContent === "") {
|
||||
heading.classList.add("empty");
|
||||
}
|
||||
|
||||
if (node.attrs.collapsed) heading.dataset.collapsed = "true";
|
||||
else delete heading.dataset.collapsed;
|
||||
|
||||
@@ -187,6 +190,17 @@ export const Heading = TiptapHeading.extend({
|
||||
if (typeof getPos === "boolean") return;
|
||||
|
||||
const pos = getPos();
|
||||
const resolvedPos = editor.state.doc.resolve(pos);
|
||||
|
||||
const callout = findParentNodeClosestToPos(
|
||||
resolvedPos,
|
||||
(node) => node.type.name === Callout.name
|
||||
);
|
||||
// the first callout heading's collapsibility is handled by callout itself
|
||||
if (callout?.node.firstChild === node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientX =
|
||||
e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
||||
const clientY =
|
||||
@@ -194,7 +208,7 @@ export const Heading = TiptapHeading.extend({
|
||||
const isRtl =
|
||||
e.target.dir === "rtl" ||
|
||||
findParentNodeClosestToPos(
|
||||
editor.state.doc.resolve(pos),
|
||||
resolvedPos,
|
||||
(node) => !!node.attrs.textDirection
|
||||
)?.node.attrs.textDirection === "rtl";
|
||||
|
||||
@@ -254,6 +268,12 @@ export const Heading = TiptapHeading.extend({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (updatedNode.textContent === "") {
|
||||
heading.classList.add("empty");
|
||||
} else {
|
||||
heading.classList.remove("empty");
|
||||
}
|
||||
|
||||
if (updatedNode.attrs.collapsed) heading.dataset.collapsed = "true";
|
||||
else delete heading.dataset.collapsed;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import { showLinkPopup } from "../../toolbar/popups/link-popup.js";
|
||||
import { isListActive } from "../../utils/list.js";
|
||||
import { tiptapKeys } from "@notesnook/common";
|
||||
import { isInTable } from "../table/prosemirror-tables/util.js";
|
||||
import { config } from "../../utils/config.js";
|
||||
import { DEFAULT_COLORS } from "../../toolbar/tools/colors.js";
|
||||
|
||||
export const KeyMap = Extension.create({
|
||||
name: "key-map",
|
||||
@@ -71,6 +73,11 @@ export const KeyMap = Extension.create({
|
||||
[tiptapKeys.insertLink.keys]: ({ editor }) => {
|
||||
showLinkPopup(editor);
|
||||
return true;
|
||||
},
|
||||
[tiptapKeys.toggleTextColor.keys]: ({ editor }) => {
|
||||
const color =
|
||||
config.get<"string">("textColor") || DEFAULT_COLORS.text[0];
|
||||
return editor.commands.toggleMark("textStyle", { color });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type DispatchFn = (tr: Transaction) => void;
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
searchreplace: {
|
||||
startSearch: () => ReturnType;
|
||||
startSearch: (isReplacing?: boolean) => ReturnType;
|
||||
endSearch: () => ReturnType;
|
||||
search: (term: string, options?: SearchSettings) => ReturnType;
|
||||
moveToNextResult: () => ReturnType;
|
||||
@@ -51,7 +51,7 @@ interface Result {
|
||||
|
||||
interface SearchOptions {
|
||||
searchResultClass: string;
|
||||
onStartSearch: (term?: string) => boolean;
|
||||
onStartSearch: (term?: string, isReplacing?: boolean) => boolean;
|
||||
onEndSearch: () => boolean;
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ export const SearchReplace = Extension.create<SearchOptions, SearchStorage>({
|
||||
addCommands() {
|
||||
return {
|
||||
startSearch:
|
||||
() =>
|
||||
(isReplacing) =>
|
||||
({ state, commands }) => {
|
||||
const term = !state.selection.empty
|
||||
? state.doc.textBetween(
|
||||
@@ -252,7 +252,7 @@ export const SearchReplace = Extension.create<SearchOptions, SearchStorage>({
|
||||
: undefined;
|
||||
if (term) commands.search(term);
|
||||
|
||||
return this.options.onStartSearch(term);
|
||||
return this.options.onStartSearch(term, isReplacing);
|
||||
},
|
||||
endSearch:
|
||||
() =>
|
||||
@@ -357,6 +357,8 @@ export const SearchReplace = Extension.create<SearchOptions, SearchStorage>({
|
||||
return {
|
||||
[tiptapKeys.openSearch.keys]: ({ editor }) =>
|
||||
editor.commands.startSearch(),
|
||||
[tiptapKeys.openSearchAndReplace.keys]: ({ editor }) =>
|
||||
editor.commands.startSearch(true),
|
||||
Escape: ({ editor }) => editor.commands.endSearch()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -198,7 +198,7 @@ export function handleMouseDown(
|
||||
// cell selection to be created).
|
||||
setCellSelection($anchor, startEvent);
|
||||
startEvent.preventDefault();
|
||||
} else if (startEvent instanceof TouchEvent) {
|
||||
} else if (isTouchEvent(startEvent)) {
|
||||
const selectedCell = view.domAtPos(view.state.selection.from).node;
|
||||
if (startDOMCell?.contains(selectedCell)) {
|
||||
$anchor = cellUnderMouse(view, startEvent);
|
||||
@@ -313,3 +313,7 @@ function cellUnderMouse(
|
||||
if (!mousePos) return null;
|
||||
return mousePos ? cellAround(view.state.doc.resolve(mousePos.pos)) : null;
|
||||
}
|
||||
|
||||
function isTouchEvent(event: Event): event is TouchEvent {
|
||||
return "TouchEvent" in window && event instanceof TouchEvent;
|
||||
}
|
||||
|
||||
@@ -191,11 +191,12 @@ const useTiptap = (
|
||||
extensions: [
|
||||
...CoreExtensions,
|
||||
SearchReplace.configure({
|
||||
onStartSearch: (term) => {
|
||||
onStartSearch: (term, isReplacing) => {
|
||||
useEditorSearchStore.setState({
|
||||
isSearching: true,
|
||||
searchTerm: term,
|
||||
focusNonce: Math.random()
|
||||
focusNonce: Math.random(),
|
||||
isReplacing: isReplacing
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ type ColorToolProps = ToolProps & {
|
||||
type: ColorType;
|
||||
};
|
||||
|
||||
const DEFAULT_COLORS: Record<ColorType, string[]> = {
|
||||
export const DEFAULT_COLORS: Record<ColorType, string[]> = {
|
||||
background: [
|
||||
"#b0bec5", // Neutralized pink
|
||||
"#a1887f", // Neutralized purple
|
||||
|
||||
@@ -761,10 +761,11 @@ p > *::selection {
|
||||
mask-size: cover;
|
||||
}
|
||||
|
||||
.simple-checklist > li.checked p {
|
||||
.simple-checklist > li.checked > :not(.simple-checklist) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
|
||||
/* Callout */
|
||||
.ProseMirror div.callout {
|
||||
padding: 15px;
|
||||
@@ -955,12 +956,12 @@ del.diffdel {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ProseMirror h1.is-empty::after,
|
||||
.ProseMirror h2.is-empty::after,
|
||||
.ProseMirror h3.is-empty::after,
|
||||
.ProseMirror h4.is-empty::after,
|
||||
.ProseMirror h5.is-empty::after,
|
||||
.ProseMirror h6.is-empty::after {
|
||||
.ProseMirror h1.empty::after,
|
||||
.ProseMirror h2.empty::after,
|
||||
.ProseMirror h3.empty::after,
|
||||
.ProseMirror h4.empty::after,
|
||||
.ProseMirror h5.empty::after,
|
||||
.ProseMirror h6.empty::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user