mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 18:19:38 +02:00
Compare commits
120 Commits
rn-82
...
fix-notifi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f66ee04cc | ||
|
|
ecf75a07e4 | ||
|
|
50d0827972 | ||
|
|
a0d4be2379 | ||
|
|
e4eb5d1f0f | ||
|
|
9ae4d3df55 | ||
|
|
95b6b45cc4 | ||
|
|
9280d0ef7e | ||
|
|
5171b2e5e2 | ||
|
|
8455ff876a | ||
|
|
0503af6819 | ||
|
|
fbaba8ebe8 | ||
|
|
64c70f51c9 | ||
|
|
946da6e274 | ||
|
|
f42489ecb5 | ||
|
|
abd5fa00ed | ||
|
|
9450849b9d | ||
|
|
d66bda7adc | ||
|
|
461c4acfb7 | ||
|
|
ccb0840771 | ||
|
|
b5a706a6a9 | ||
|
|
a45b54dd85 | ||
|
|
a4d29f3e7a | ||
|
|
2a8ec8245f | ||
|
|
d85f32e1f2 | ||
|
|
adc7bb1d9e | ||
|
|
2dafde0117 | ||
|
|
7582a01b3f | ||
|
|
9081012673 | ||
|
|
bf83dfbd28 | ||
|
|
903b96e629 | ||
|
|
3e9007806e | ||
|
|
b6ec0091f0 | ||
|
|
726b4f5c0d | ||
|
|
2fdd3a2a85 | ||
|
|
4fbb39efd0 | ||
|
|
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 | ||
|
|
6060cb457d | ||
|
|
6ac0107274 | ||
|
|
95de4855c0 | ||
|
|
bcfff70e78 | ||
|
|
11e7d34961 | ||
|
|
e48e523b90 | ||
|
|
4689b4290e | ||
|
|
f9af8d116c | ||
|
|
1f9339e5ab | ||
|
|
4bc9b718cc | ||
|
|
eae6b058fc | ||
|
|
a29bcf713f | ||
|
|
6b171f578d | ||
|
|
3f45c2ebd4 | ||
|
|
883b1a63b2 | ||
|
|
4c0efa8b08 | ||
|
|
ed2a04d590 | ||
|
|
52a3d1c373 | ||
|
|
573b24f28f | ||
|
|
e68e5bc6fb | ||
|
|
6473ce4244 | ||
|
|
82685314f7 | ||
|
|
c1370af395 | ||
|
|
7779a8b752 | ||
|
|
f7914c57c7 | ||
|
|
6dd16f6791 |
6
.github/workflows/android.e2e.yml
vendored
6
.github/workflows/android.e2e.yml
vendored
@@ -55,13 +55,13 @@ jobs:
|
||||
- name: Detox build
|
||||
run: |
|
||||
yarn build:android
|
||||
ls apps/mobile/native/android/app/build/outputs/apk
|
||||
ls apps/mobile/native/android/app/build/outputs/apk/release
|
||||
ls apps/mobile/android/app/build/outputs/apk
|
||||
ls apps/mobile/android/app/build/outputs/apk/release
|
||||
|
||||
- name: Get device name
|
||||
id: device
|
||||
run: |
|
||||
AVD_NAME=$(node -p "require('./apps/mobile/native/.detoxrc.js').devices.emulator.device.avdName")
|
||||
AVD_NAME=$(node -p "require('./apps/mobile/.detoxrc.js').devices.emulator.device.avdName")
|
||||
echo "AVD_NAME=$AVD_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: AVD cache
|
||||
|
||||
22
.github/workflows/android.publish.yml
vendored
22
.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
|
||||
@@ -124,7 +140,7 @@ jobs:
|
||||
|
||||
- name: Get app version
|
||||
id: package-version
|
||||
uses: martinbeentjes/npm-get-version-action@master
|
||||
uses: saionaro/extract-package-version@master
|
||||
with:
|
||||
path: apps/mobile
|
||||
|
||||
@@ -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: |
|
||||
|
||||
2
.github/workflows/desktop.tests.yml
vendored
2
.github/workflows/desktop.tests.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
test-macos-x64:
|
||||
name: Test macOS x64
|
||||
needs: build
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
689
apps/desktop/package-lock.json
generated
689
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
@@ -36,8 +36,8 @@
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"sqlite-better-trigram": "0.0.5",
|
||||
"sqlite3-fts5-html": "^0.0.6",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "3.24.3"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "com.facebook.react"
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
|
||||
|
||||
@@ -124,7 +125,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3079
|
||||
versionCode 3087
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
5
apps/mobile/android/app/proguard-rules.pro
vendored
5
apps/mobile/android/app/proguard-rules.pro
vendored
@@ -63,4 +63,7 @@
|
||||
-keepattributes Signature
|
||||
-keep class com.google.gson.reflect.TypeToken { *; }
|
||||
-keep class * extends com.google.gson.reflect.TypeToken
|
||||
-keep class com.streetwriters.notesnook.datatypes.* { *; }
|
||||
-keep class com.streetwriters.notesnook.datatypes.* { *; }
|
||||
|
||||
-keep class net.gotev.uploadservice.* { *; }
|
||||
-keep class kotlinx.parcelize.* { *; }
|
||||
@@ -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>
|
||||
|
||||
0
apps/mobile/android/gradlew
vendored
Normal file → Executable file
0
apps/mobile/android/gradlew
vendored
Normal file → Executable file
@@ -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
|
||||
@@ -63,7 +64,7 @@ const App = (props: { configureMode: "note-preview" }) => {
|
||||
setTimeout(async () => {
|
||||
await Notifications.get();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(true);
|
||||
Notifications.pinQuickNote();
|
||||
}
|
||||
TipManager.init();
|
||||
}, 100);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 { RequestOptions } from "@notesnook/core";
|
||||
import { Platform } from "react-native";
|
||||
import { isImage, RequestOptions } from "@notesnook/core";
|
||||
import { PermissionsAndroid, Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
FileSizeResult,
|
||||
getUploadedFileSize
|
||||
} from "./utils";
|
||||
import Upload from "@ammarahmed/react-native-upload";
|
||||
|
||||
export async function uploadFile(
|
||||
filename: string,
|
||||
@@ -64,47 +65,87 @@ export async function uploadFile(
|
||||
);
|
||||
}
|
||||
|
||||
const fileSize = (await RNFetchBlob.fs.stat(filePath)).size;
|
||||
const fileInfo = await RNFetchBlob.fs.stat(filePath);
|
||||
|
||||
const remoteFileSize = await getUploadedFileSize(filename);
|
||||
if (remoteFileSize === FileSizeResult.Error) return false;
|
||||
|
||||
if (remoteFileSize > FileSizeResult.Empty && remoteFileSize === fileSize) {
|
||||
if (
|
||||
remoteFileSize > FileSizeResult.Empty &&
|
||||
remoteFileSize === fileInfo.size
|
||||
) {
|
||||
DatabaseLogger.log(`File ${filename} is already uploaded.`);
|
||||
return true;
|
||||
}
|
||||
|
||||
DatabaseLogger.info(`Starting upload: ${filename}`);
|
||||
let attachmentInfo = await db.attachments.attachment(filename);
|
||||
|
||||
const uploadRequest = RNFetchBlob.config({
|
||||
//@ts-ignore
|
||||
IOSBackgroundTask: !globalThis["IS_SHARE_EXTENSION"]
|
||||
})
|
||||
.fetch(
|
||||
"PUT",
|
||||
url,
|
||||
{
|
||||
...headers,
|
||||
"content-type": ""
|
||||
},
|
||||
RNFetchBlob.wrap(filePath)
|
||||
)
|
||||
.uploadProgress((sent, total) => {
|
||||
useAttachmentStore
|
||||
.getState()
|
||||
.setProgress(sent, total, filename, 0, "upload");
|
||||
DatabaseLogger.info(
|
||||
`File upload progress: ${filename}, ${sent}/${total}`
|
||||
);
|
||||
});
|
||||
DatabaseLogger.info(
|
||||
`Starting upload of ${filename} at path: ${fileInfo.path} ${fileInfo.size}`
|
||||
);
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
const status = await PermissionsAndroid.request(
|
||||
"android.permission.POST_NOTIFICATIONS"
|
||||
);
|
||||
if (status !== "granted") {
|
||||
ToastManager.show({
|
||||
message: `The permission to show file upload notification was disallowed by the user.`,
|
||||
type: "info"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const upload = Upload.create({
|
||||
customUploadId: filename,
|
||||
path: Platform.OS === "ios" ? "file://" + fileInfo.path : fileInfo.path,
|
||||
url: url,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
...headers,
|
||||
"content-type": "application/octet-stream"
|
||||
},
|
||||
appGroup: IOS_APPGROUPID,
|
||||
notification: {
|
||||
filename:
|
||||
attachmentInfo && isImage(attachmentInfo?.mimeType)
|
||||
? "image"
|
||||
: attachmentInfo?.filename || "file",
|
||||
enabled: true,
|
||||
enableRingTone: true,
|
||||
autoClear: true
|
||||
}
|
||||
}).onChange((event) => {
|
||||
switch (event.status) {
|
||||
case "running":
|
||||
case "pending":
|
||||
useAttachmentStore
|
||||
.getState()
|
||||
.setProgress(
|
||||
event.uploadedBytes || 0,
|
||||
event.totalBytes || fileInfo.size,
|
||||
filename,
|
||||
0,
|
||||
"upload"
|
||||
);
|
||||
DatabaseLogger.info(
|
||||
`File upload progress: ${filename}, ${event.uploadedBytes}/${
|
||||
event.totalBytes || fileInfo.size
|
||||
}`
|
||||
);
|
||||
break;
|
||||
case "completed":
|
||||
console.log("Upload completed");
|
||||
break;
|
||||
}
|
||||
});
|
||||
const result = await upload.start();
|
||||
cancelToken.cancel = async () => {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
uploadRequest.cancel();
|
||||
upload.cancel();
|
||||
};
|
||||
|
||||
const uploadResponse = await uploadRequest;
|
||||
const status = uploadResponse.info().status;
|
||||
const status = result.responseCode || 0;
|
||||
const uploaded = status >= 200 && status < 300;
|
||||
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
@@ -114,12 +155,12 @@ export async function uploadFile(
|
||||
throw new Error(
|
||||
`${status}, name: ${fileInfo.filename}, length: ${
|
||||
fileInfo.size
|
||||
}, info: ${JSON.stringify(uploadResponse.info())}`
|
||||
}, info: ${JSON.stringify(result.error)}`
|
||||
);
|
||||
}
|
||||
const attachment = await db.attachments.attachment(filename);
|
||||
if (!attachment) return false;
|
||||
await checkUpload(filename, requestOptions.chunkSize, attachment.size);
|
||||
attachmentInfo = await db.attachments.attachment(filename);
|
||||
if (!attachmentInfo) return false;
|
||||
await checkUpload(filename, requestOptions.chunkSize, attachmentInfo.size);
|
||||
DatabaseLogger.info(`File upload status: ${filename}, ${status}`);
|
||||
return uploaded;
|
||||
} catch (e) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -31,6 +31,7 @@ import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
SharedValue,
|
||||
SnappySpringConfig,
|
||||
useAnimatedReaction,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
@@ -110,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;
|
||||
@@ -354,13 +354,10 @@ 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,
|
||||
mass: 0.5,
|
||||
overshootClamping: true,
|
||||
damping: 800,
|
||||
stiffness: 800
|
||||
velocity: velocity,
|
||||
...SnappySpringConfig
|
||||
};
|
||||
|
||||
if (finalValue < homePosition) {
|
||||
@@ -443,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
|
||||
});
|
||||
}
|
||||
setImmediate(() => {
|
||||
Navigation.closeDrawer();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -154,8 +151,8 @@ function _MenuItem({
|
||||
item.icon === "crown"
|
||||
? colors.static.yellow
|
||||
: isFocused
|
||||
? colors.selected.icon
|
||||
: colors.secondary.icon
|
||||
? colors.selected.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
size={AppFontSize.md}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ const SheetWrapper = ({
|
||||
closeOnTouchBackdrop = true,
|
||||
onHasReachedTop,
|
||||
overlay,
|
||||
overlayOpacity = 0.3,
|
||||
overlayOpacity = 0.7,
|
||||
enableGesturesInScrollView = false,
|
||||
bottomPadding = true,
|
||||
keyboardHandlerDisabled
|
||||
|
||||
@@ -176,6 +176,7 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
if (id) {
|
||||
const note = await db.notes.note(id);
|
||||
if (note) {
|
||||
editorState().initialLoadCalled = true;
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
@@ -454,9 +455,6 @@ const initializeDatabase = async (password?: string) => {
|
||||
await setAppMessage();
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
Notifications.setupReminders(true);
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
}
|
||||
DatabaseLogger.info("Database initialized");
|
||||
Notifications.restorePinnedNotes();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -205,7 +205,9 @@ const camera = async (options: PickerOptions) => {
|
||||
options
|
||||
);
|
||||
})
|
||||
.catch((e) => {});
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
} catch (e) {
|
||||
ToastManager.show({
|
||||
heading: (e as Error).message,
|
||||
|
||||
@@ -664,7 +664,8 @@ export const useEditorEvents = (
|
||||
if (note) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note,
|
||||
tabId: editorMessage.tabId
|
||||
tabId: editorMessage.tabId,
|
||||
loadedFromEditor: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -675,7 +676,8 @@ export const useEditorEvents = (
|
||||
if (note) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note,
|
||||
tabId: editorMessage.tabId
|
||||
tabId: editorMessage.tabId,
|
||||
loadedFromEditor: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,9 +508,17 @@ export const useEditor = (
|
||||
newTab?: boolean;
|
||||
refresh?: boolean;
|
||||
searchResultIndex?: number;
|
||||
loadedFromEditor?: boolean;
|
||||
}) => {
|
||||
loadNoteMutex.runExclusive(async () => {
|
||||
if (!event) return;
|
||||
if (
|
||||
!event ||
|
||||
(event.loadedFromEditor &&
|
||||
event.item &&
|
||||
event.item?.id !== useTabStore.getState().getCurrentNoteId())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (event.blockId) {
|
||||
blockIdRef.current = event.blockId;
|
||||
}
|
||||
@@ -719,6 +727,7 @@ export const useEditor = (
|
||||
}, 300);
|
||||
}
|
||||
postMessage(NativeEvents.theme, theme);
|
||||
console.log("load finished", event.item?.id);
|
||||
});
|
||||
},
|
||||
[
|
||||
|
||||
@@ -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,37 +88,40 @@ 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;
|
||||
};
|
||||
|
||||
const KeyboardAvoidingViewIOS =
|
||||
Platform.OS === "ios" ? KeyboardAvoidingView : View;
|
||||
|
||||
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: 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 : (
|
||||
<KeyboardAvoidingViewIOS
|
||||
<KeyboardAvoidingView
|
||||
behavior="padding"
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
@@ -118,7 +137,7 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
|
||||
blurOnSubmit={false}
|
||||
/>
|
||||
<Editor key="editor" withController={true} />
|
||||
</KeyboardAvoidingViewIOS>
|
||||
</KeyboardAvoidingView>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -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={0} 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")
|
||||
)
|
||||
@@ -1323,7 +1329,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
if (settings.notifNotes) {
|
||||
Notifications.unpinQuickNote();
|
||||
} else {
|
||||
Notifications.pinQuickNote(false);
|
||||
Notifications.pinQuickNote();
|
||||
}
|
||||
SettingsService.set({
|
||||
notifNotes: !settings.notifNotes
|
||||
|
||||
@@ -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>
|
||||
|
||||
1139
apps/mobile/app/screens/wrapped/index.tsx
Normal file
1139
apps/mobile/app/screens/wrapped/index.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -114,7 +114,7 @@ async function onBackgroundSyncStarted() {
|
||||
}
|
||||
await Notifications.setupReminders();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
Notifications.pinQuickNote();
|
||||
}
|
||||
Notifications.restorePinnedNotes();
|
||||
NotePreviewWidget.updateNotes();
|
||||
@@ -142,7 +142,7 @@ const onBoot = async () => {
|
||||
|
||||
await Notifications.setupReminders();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
Notifications.pinQuickNote();
|
||||
}
|
||||
Notifications.restorePinnedNotes();
|
||||
NotePreviewWidget.updateNotes();
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -109,6 +109,15 @@ async function initDatabase() {
|
||||
const onEvent = async ({ type, detail }: Event) => {
|
||||
await initDatabase();
|
||||
const { notification, pressAction, input } = detail;
|
||||
if (
|
||||
type === EventType.DISMISSED &&
|
||||
Platform.OS === "android" &&
|
||||
notification?.id === "notesnook_note_input" &&
|
||||
SettingsService.getProperty("notifNotes")
|
||||
) {
|
||||
pinQuickNote();
|
||||
}
|
||||
|
||||
if (type === EventType.DELIVERED && Platform.OS === "android") {
|
||||
if (notification?.id) {
|
||||
const reminder = await db.reminders?.reminder(
|
||||
@@ -438,6 +447,7 @@ async function scheduleNotification(
|
||||
|
||||
async function loadNote(id: string, jump: boolean) {
|
||||
if (!id || id === "notesnook_note_input") return;
|
||||
editorState().initialLoadCalled = true;
|
||||
const note = await db.notes.note(id);
|
||||
if (!note) return;
|
||||
if (!DDS.isTab && jump) {
|
||||
@@ -502,7 +512,8 @@ async function displayNotification({
|
||||
ongoing,
|
||||
reply_placeholder_text,
|
||||
reply_button_text,
|
||||
id
|
||||
id,
|
||||
channelId = "default"
|
||||
}: {
|
||||
title?: string;
|
||||
message: string;
|
||||
@@ -513,6 +524,7 @@ async function displayNotification({
|
||||
reply_placeholder_text?: string;
|
||||
reply_button_text?: string;
|
||||
id?: string;
|
||||
channelId?: "silent" | "vibrate" | "urgent" | "default";
|
||||
}) {
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: true
|
||||
@@ -537,7 +549,7 @@ async function displayNotification({
|
||||
ongoing: ongoing,
|
||||
smallIcon: "ic_stat_name",
|
||||
localOnly: true,
|
||||
channelId: await getChannelId("default"),
|
||||
channelId: await getChannelId(channelId),
|
||||
autoCancel: false,
|
||||
pressAction: {
|
||||
id: "default",
|
||||
@@ -884,7 +896,7 @@ function init() {
|
||||
notifee.onForegroundEvent(onEvent);
|
||||
}
|
||||
|
||||
async function pinQuickNote(launch: boolean) {
|
||||
async function pinQuickNote() {
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: true
|
||||
});
|
||||
@@ -896,14 +908,13 @@ async function pinQuickNote(launch: boolean) {
|
||||
return;
|
||||
}
|
||||
get().then((items) => {
|
||||
const notification = items.filter((n) => n.id === "notesnook_note_input");
|
||||
if (notification && launch) {
|
||||
return;
|
||||
}
|
||||
const notification = items.find((n) => n.id === "notesnook_note_input");
|
||||
if (notification) return;
|
||||
displayNotification({
|
||||
title: strings.quickNoteTitle(),
|
||||
message: strings.quickNoteContent(),
|
||||
ongoing: true,
|
||||
channelId: "silent",
|
||||
actions: ["ReplyInput", strings.hide()],
|
||||
reply_button_text: strings.takeNote(),
|
||||
reply_placeholder_text: strings.quickNotePlaceholder(),
|
||||
@@ -1030,6 +1041,7 @@ async function pinNote(id: string) {
|
||||
subtitle: "",
|
||||
bigText: html,
|
||||
ongoing: true,
|
||||
channelId: "silent",
|
||||
actions: [strings.unpin()],
|
||||
id: note.id
|
||||
});
|
||||
@@ -1040,9 +1052,13 @@ async function pinNote(id: string) {
|
||||
|
||||
async function restorePinnedNotes() {
|
||||
const pinnedNotes = PinnedNotesStorage.get();
|
||||
for (const id of pinnedNotes) {
|
||||
pinNote(id);
|
||||
}
|
||||
Notifications.get().then(() => {
|
||||
for (const id of pinnedNotes) {
|
||||
if (!isNotePinned(id)) {
|
||||
pinNote(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function clearPinnedNotes() {
|
||||
|
||||
@@ -251,6 +251,7 @@ const ShareView = () => {
|
||||
setRawData(item);
|
||||
if (isURL(item.value)) {
|
||||
note = await showLinkPreview(note, item.value);
|
||||
noteTitle.current = note.title;
|
||||
} else {
|
||||
note.content.data = makeHtmlFromPlainText(item.value);
|
||||
}
|
||||
@@ -261,9 +262,6 @@ const ShareView = () => {
|
||||
if (key.includes("TITLE") || key.includes("SUBJECT")) {
|
||||
note.title = item[key];
|
||||
noteTitle.current = note.title;
|
||||
inputRef.current?.setNativeProps?.({
|
||||
text: noteTitle.current
|
||||
});
|
||||
}
|
||||
if (key.includes("TEXT") && !note.content.data) {
|
||||
note.content.data = item[key];
|
||||
@@ -294,6 +292,10 @@ const ShareView = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputRef.current?.setNativeProps?.({
|
||||
text: noteTitle.current
|
||||
});
|
||||
onLoad();
|
||||
setNote({ ...note });
|
||||
} catch (e) {
|
||||
@@ -308,7 +310,6 @@ const ShareView = () => {
|
||||
try {
|
||||
await initDatabase();
|
||||
setLoadingExtension(false);
|
||||
loadData(false);
|
||||
useShareStore.getState().restore();
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
|
||||
@@ -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("@");
|
||||
|
||||
@@ -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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1104,7 +1104,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
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;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
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;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
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;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
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;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
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 = 2158;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
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;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#import "RNShortcuts.h"
|
||||
#import "RNBootSplash.h"
|
||||
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
|
||||
#import "RNFileUploader.h"
|
||||
|
||||
@interface ReactNativeDelegate : RCTDefaultReactNativeFactoryDelegate
|
||||
@end
|
||||
@@ -79,4 +80,8 @@
|
||||
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // ⬅️ initialize the splash screen
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
|
||||
[RNFileUploader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
<string>3B52.1</string>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
|
||||
@@ -1887,7 +1887,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-document-picker (10.1.7):
|
||||
- react-native-document-picker (11.0.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -1969,14 +1969,14 @@ PODS:
|
||||
- React-Core
|
||||
- React-RCTFabric
|
||||
- ReactCommon/turbomodule/core
|
||||
- react-native-mmkv-storage (0.11.2):
|
||||
- react-native-mmkv-storage (12.0.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
- fmt
|
||||
- glog
|
||||
- hermes-engine
|
||||
- MMKV (~> 1.3.9)
|
||||
- MMKV (~> 1.3.14)
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- RCTRequired
|
||||
@@ -2244,6 +2244,36 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- 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
|
||||
@@ -3031,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
|
||||
@@ -3065,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
|
||||
@@ -3096,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
|
||||
@@ -3280,7 +3310,7 @@ PODS:
|
||||
- pop (~> 1.0)
|
||||
- React
|
||||
- SexyTooltip
|
||||
- RNWorklets (0.6.1):
|
||||
- RNWorklets (0.7.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -3307,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
|
||||
@@ -3337,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
|
||||
@@ -3462,6 +3492,8 @@ DEPENDENCIES:
|
||||
- "react-native-share-extension (from `../node_modules/@ammarahmed/react-native-share-extension`)"
|
||||
- "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`)
|
||||
@@ -3675,6 +3707,10 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@ammarahmed/react-native-sodium"
|
||||
react-native-theme-switch-animation:
|
||||
: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:
|
||||
@@ -3855,7 +3891,7 @@ SPEC CHECKSUMS:
|
||||
react-native-blob-util: 7946b7e13acf0da5e849dc2f73fcfebe1d981699
|
||||
react-native-config: 963b5efabc864cf69412e54b5de49b6a23e4af03
|
||||
react-native-date-picker: 4f4f40f6e65798038bb4b1bff47890c2be69c2e6
|
||||
react-native-document-picker: 1734eb0aa3dbd1cd7bf1b105936f9b55031ae616
|
||||
react-native-document-picker: 254467fec90f263dfc4828210daf3e8baa4fcb81
|
||||
react-native-fingerprint-scanner: d5e143a361f3f01858e9c45141ddcabc4fd57055
|
||||
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
|
||||
react-native-gzip: 794e0e964a0d9e1dfd1773fee938adb4d4310e26
|
||||
@@ -3863,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: b7ce5a5eb3d3bfddcf2df127671abba6ef3d76ae
|
||||
react-native-mmkv-storage: e84980084e371a9c2230bbde071536a7e1539406
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
|
||||
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
|
||||
@@ -3875,6 +3911,8 @@ SPEC CHECKSUMS:
|
||||
react-native-share-extension: fdc6aaab51591a2d445df239c446aaa3a99658ec
|
||||
react-native-sodium: 066f76e46c9be13e9260521e3fa994937c4cdab4
|
||||
react-native-theme-switch-animation: 449d6db7a760f55740505e7403ae8061debc9a7e
|
||||
react-native-upload: 1459a4be625dda39e4364d9e7d859477a3000f2a
|
||||
react-native-view-shot: 6c008e58f4720de58370848201c5d4a082c6d4ca
|
||||
react-native-webview: 654f794a7686b47491cf43aa67f7f428bea00eed
|
||||
React-NativeModulesApple: 46690a0fe94ec28fc6fc686ec797b911d251ded0
|
||||
React-oscompat: 95875e81f5d4b3c7b2c888d5bd2c9d83450d8bdb
|
||||
@@ -3921,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
200
apps/mobile/package-lock.json
generated
200
apps/mobile/package-lock.json
generated
@@ -1,21 +1,22 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10-beta.0",
|
||||
"version": "3.3.10-beta.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10-beta.0",
|
||||
"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.29.0",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.11",
|
||||
"@callstack/repack": "~5.2.1",
|
||||
@@ -41,7 +42,7 @@
|
||||
"@react-native-community/datetimepicker": "^8.4.5",
|
||||
"@react-native-community/netinfo": "^11.4.1",
|
||||
"@react-native-community/toolbar-android": "^0.2.1",
|
||||
"@react-native-documents/picker": "^10.1.7",
|
||||
"@react-native-documents/picker": "^11.0.1",
|
||||
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||
"@react-navigation/elements": "^1.3.3",
|
||||
"@react-navigation/native": "^6.0.10",
|
||||
@@ -71,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.8",
|
||||
"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",
|
||||
@@ -96,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",
|
||||
@@ -107,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",
|
||||
@@ -122,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",
|
||||
@@ -227,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",
|
||||
@@ -482,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": "*"
|
||||
@@ -529,6 +532,16 @@
|
||||
"integrity": "sha512-FbX9fMfqJ3ysd+zkSS5di459tPv5iB0fhmK3dx/xo+l07sf7f1gzJu17mghSGkOKkbjXaoLPcq3XLMwWGpvapQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@ammarahmed/react-native-upload": {
|
||||
"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": "*",
|
||||
"react-native": ">=0.47.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-asynciterator-polyfill": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz",
|
||||
@@ -1990,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",
|
||||
@@ -2472,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"
|
||||
@@ -4839,13 +4853,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-documents/picker": {
|
||||
"version": "10.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-documents/picker/-/picker-10.1.7.tgz",
|
||||
"integrity": "sha512-Tb8SPU+pHxrSJmDHBozSUStIPeyFHTHLrU3MW0N3sUAioLd5z+nmUdypfg5fs+Yzp7KTxVW06APe2HLB1ysLww==",
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-documents/picker/-/picker-11.0.1.tgz",
|
||||
"integrity": "sha512-aUq4MHGO/f8BslCFFx9OXz9NLLmcLkYAXp5PAEVau31v7obItPpb71Fe84bxpGV6gALIvGlGgSm6W9kEyU4toA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
"react-native": ">=0.79.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-masked-view/masked-view": {
|
||||
@@ -7486,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",
|
||||
@@ -8542,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",
|
||||
@@ -11622,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",
|
||||
@@ -17119,9 +17164,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-actions-sheet": {
|
||||
"version": "10.0.0-alpha.8",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-10.0.0-alpha.8.tgz",
|
||||
"integrity": "sha512-olKqucpGaxP/kiO2/nw0uPXn69ZdWWhcHFzu2tcv5RBMl+PhRv870HuODbE21X4jXY/fWTG6FwPJqRqWpKkY8A==",
|
||||
"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": "*",
|
||||
@@ -17644,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": "*"
|
||||
}
|
||||
@@ -17788,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": {
|
||||
@@ -17815,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"
|
||||
@@ -18058,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",
|
||||
@@ -18082,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"
|
||||
@@ -20255,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",
|
||||
@@ -21066,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.0",
|
||||
"version": "3.3.11",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -26,12 +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.29.0",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.11",
|
||||
"@callstack/repack": "~5.2.1",
|
||||
@@ -57,7 +58,7 @@
|
||||
"@react-native-community/datetimepicker": "^8.4.5",
|
||||
"@react-native-community/netinfo": "^11.4.1",
|
||||
"@react-native-community/toolbar-android": "^0.2.1",
|
||||
"@react-native-documents/picker": "^10.1.7",
|
||||
"@react-native-documents/picker": "^11.0.1",
|
||||
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||
"@react-navigation/elements": "^1.3.3",
|
||||
"@react-navigation/native": "^6.0.10",
|
||||
@@ -87,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.8",
|
||||
"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",
|
||||
@@ -112,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",
|
||||
@@ -123,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",
|
||||
@@ -138,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",
|
||||
@@ -206,4 +208,4 @@
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
}
|
||||
}
|
||||
13
apps/mobile/patches/react-native+0.82.1.patch
Normal file
13
apps/mobile/patches/react-native+0.82.1.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
|
||||
index 6eca156..5a42107 100644
|
||||
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
|
||||
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
|
||||
@@ -208,7 +208,7 @@ class KeyboardAvoidingView extends React.Component<
|
||||
];
|
||||
} else {
|
||||
this._subscriptions = [
|
||||
- Keyboard.addListener('keyboardDidHide', this._onKeyboardChange),
|
||||
+ Keyboard.addListener('keyboardDidHide', this._onKeyboardHide),
|
||||
Keyboard.addListener('keyboardDidShow', this._onKeyboardChange),
|
||||
];
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
diff --git a/node_modules/react-native-actions-shortcuts/android/.settings/org.eclipse.buildship.core.prefs b/node_modules/react-native-actions-shortcuts/android/.settings/org.eclipse.buildship.core.prefs
|
||||
deleted file mode 100644
|
||||
index 8c253d6..0000000
|
||||
--- a/node_modules/react-native-actions-shortcuts/android/.settings/org.eclipse.buildship.core.prefs
|
||||
+++ /dev/null
|
||||
@@ -1,13 +0,0 @@
|
||||
-arguments=
|
||||
-auto.sync=false
|
||||
-build.scans.enabled=false
|
||||
-connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.0))
|
||||
-connection.project.dir=
|
||||
-eclipse.preferences.version=1
|
||||
-gradle.user.home=
|
||||
-java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
|
||||
-jvm.arguments=
|
||||
-offline.mode=false
|
||||
-override.workspace.settings=true
|
||||
-show.console.view=true
|
||||
-show.executions.view=true
|
||||
diff --git a/node_modules/react-native-actions-shortcuts/android/build.gradle b/node_modules/react-native-actions-shortcuts/android/build.gradle
|
||||
index 19bd311..642ddbc 100644
|
||||
--- a/node_modules/react-native-actions-shortcuts/android/build.gradle
|
||||
@@ -3788,7 +3807,7 @@ index b438823..ba03915 100644
|
||||
}
|
||||
|
||||
diff --git a/node_modules/react-native-actions-shortcuts/android/src/main/java/com/reactnativeshortcuts/ShortcutsModule.kt b/node_modules/react-native-actions-shortcuts/android/src/main/java/com/reactnativeshortcuts/ShortcutsModule.kt
|
||||
index ec847cc..a4686a0 100644
|
||||
index ec847cc..f101793 100644
|
||||
--- a/node_modules/react-native-actions-shortcuts/android/src/main/java/com/reactnativeshortcuts/ShortcutsModule.kt
|
||||
+++ b/node_modules/react-native-actions-shortcuts/android/src/main/java/com/reactnativeshortcuts/ShortcutsModule.kt
|
||||
@@ -28,18 +28,18 @@ class ShortcutsModule(reactContext: ReactApplicationContext) :
|
||||
@@ -3853,7 +3872,7 @@ index ec847cc..a4686a0 100644
|
||||
shortcutManager?.removeAllDynamicShortcuts()
|
||||
}
|
||||
|
||||
@@ -145,6 +145,19 @@ class ShortcutsModule(reactContext: ReactApplicationContext) :
|
||||
@@ -145,6 +145,17 @@ class ShortcutsModule(reactContext: ReactApplicationContext) :
|
||||
fun isSupported(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 25
|
||||
}
|
||||
@@ -3863,9 +3882,7 @@ index ec847cc..a4686a0 100644
|
||||
+ requestCode: Int,
|
||||
+ resultCode: Int,
|
||||
+ data: Intent?
|
||||
+ ) {
|
||||
+ TODO("Not yet implemented")
|
||||
+ }
|
||||
+ ) {}
|
||||
+
|
||||
+ override fun onNewIntent(intent: Intent) {
|
||||
+ emitEvent(intent)
|
||||
|
||||
@@ -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
1488
apps/web/package-lock.json
generated
1488
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -86,7 +86,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.22.5",
|
||||
"@cloudflare/workers-types": "^4.20250224.0",
|
||||
"@playwright/test": "1.48.2",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@swc/plugin-react-remove-properties": "^6.0.2",
|
||||
"@trpc/server": "10.45.2",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -93,14 +93,23 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: "Notesnook Circle",
|
||||
title: "Wrapped 2025",
|
||||
subtitle:
|
||||
"Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom. As a member you get exclusive discounts and offers from our partners. Check it out in Settings > Notesnook Circle."
|
||||
"Notesnook Wrapped 2025 is your personal recap of the year you spent with Notesnook. Available throughout December and accessible from the sidebar, it highlights your activity, stats and how you used Notesnook this year."
|
||||
},
|
||||
{
|
||||
title: "Collapsible headings",
|
||||
title: "Monograph analytics",
|
||||
subtitle:
|
||||
"You can now collapse and expand headings in your notes. This makes it easier to manage large notes and focus on specific sections."
|
||||
"Pro users can now see view count analytics for monographs."
|
||||
},
|
||||
{
|
||||
title: "Twitter/X embed support",
|
||||
subtitle: "Simply paste the post URL in the Insert Embed dialog."
|
||||
},
|
||||
{
|
||||
title: "New keyboard shortcuts",
|
||||
subtitle:
|
||||
"Use Ctrl/Cmd + Alt/Option + F to open search/replace in the editor. We have also added Ctrl/Cmd + Alt/Option + C for toggling text color."
|
||||
}
|
||||
],
|
||||
cta: {
|
||||
|
||||
@@ -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 ⇧ : | 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!
|
||||
5
fastlane/metadata/android/en-US/changelogs/15439.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15439.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",
|
||||
|
||||
@@ -96,7 +96,10 @@ export const hotkeys = {
|
||||
type: "hotkeys"
|
||||
},
|
||||
openCommandPalette: {
|
||||
keys: normalizeKeys(["ctrl+shift+p"]),
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+shift+p"],
|
||||
web: ["ctrl+shift+p", "ctrl+shift+:"]
|
||||
}),
|
||||
description: "Command palette",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
@@ -335,6 +338,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 +403,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
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user