upgrade to react-native 0.69

This commit is contained in:
Ammar Ahmed
2022-06-30 09:45:28 +05:00
parent 473343f5e1
commit c9d193568e
41 changed files with 1612 additions and 28177 deletions

View File

@@ -72,4 +72,4 @@ untyped-import
untyped-type-import
[version]
^0.170.0
^0.176.3

View File

@@ -23,8 +23,10 @@ DerivedData
*.ipa
*.xcuserstate
*.hprof
ios/.xcode.env.local
# Android/IntelliJ
#
rn-build-deps/
build/
.idea
.gradle

View File

@@ -1 +1 @@
2.7.4
2.7.5

View File

@@ -1,4 +1,4 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.4'
ruby '2.7.5'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'

View File

@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
@@ -47,7 +48,7 @@
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

View File

@@ -53,6 +53,13 @@ public class MainActivity extends ReactActivity {
return reactRootView;
}
@Override
protected boolean isConcurrentRootEnabled() {
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected void loadApp(String appKey) {
RNBootSplash.init(getPlainActivity());

View File

@@ -2,8 +2,6 @@ package com.streetwriters.notesnook;
import android.app.Application;
import android.content.Context;
import com.RNFetchBlob.RNFetchBlob;
import com.dooboolab.RNIap.RNIapModule;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
@@ -11,21 +9,18 @@ import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.multidex.MultiDexApplication;
//import com.facebook.react.bridge.JavaScriptExecutorFactory;
//import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.learnium.RNDeviceInfo.RNDeviceModule;
import com.oblador.keychain.KeychainPackage;
import com.onibenjo.htmltopdf.RNHTMLtoPDFModule;
import com.reactnativedocumentpicker.DocumentPickerModule;
import com.vinzscam.reactnativefileviewer.RNFileViewerModule;
@@ -33,8 +28,7 @@ import com.facebook.react.config.ReactFeatureFlags;
import com.streetwriters.notesnook.newarchitecture.MainApplicationReactNativeHost;
import cl.json.RNShareModule;
import px.tooltips.RNTooltipsModule;
import com.facebook.react.bridge.JSIModulePackage; // <- add
import com.swmansion.reanimated.ReanimatedJSIModulePackage; // <- add
//import io.csie.kudo.reactnative.v8.executor.V8ExecutorFactory;
public class MainApplication extends MultiDexApplication implements ReactApplication {
@@ -48,6 +42,15 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
return BuildConfig.DEBUG;
}
// @Override
// protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
// return new V8ExecutorFactory(
// getApplicationContext(),
// getPackageName(),
// AndroidInfoHelpers.getFriendlyDeviceName(),
// getUseDeveloperSupport());
// }
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
@@ -104,10 +107,6 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
protected String getJSMainModuleName() {
return "index";
}
@Override protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage(); // <- add
}
};
public ReactModuleInfo getModuleInfo(String reactClass, String className) {

View File

@@ -16,7 +16,6 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.EmptyReactNativeConfig;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.streetwriters.notesnook.BuildConfig;
@@ -24,6 +23,7 @@ import com.streetwriters.notesnook.newarchitecture.components.MainComponentsRegi
import com.streetwriters.notesnook.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
import com.facebook.react.fabric.ReactNativeConfig;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
@@ -105,7 +105,7 @@ public class MainApplicationReactNativeHost extends ReactNativeHost {
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
new EmptyReactNativeConfig(),
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry);
}
});

View File

@@ -10,7 +10,7 @@ include $(CLEAR_VARS)
LOCAL_PATH := $(THIS_DIR)
# You can customize the name of your application .so file here.
LOCAL_MODULE := rndiffapp_appmodules
LOCAL_MODULE := notesnook_appmodules
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
@@ -28,8 +28,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SHARED_LIBRARIES := \
libfabricjni \
libfbjni \
libfolly_futures \
libfolly_json \
libfolly_runtime \
libglog \
libjsi \
libreact_codegen_rncore \

View File

@@ -1,47 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:windowDisablePreview">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
<!-- Allow drawing under the system bars background -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:fitsSystemWindows">false</item>
<!-- Set system bars background transparent -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<!-- Disable auto contrasted system bars background (on Android 10+) -->
<item name="android:enforceStatusBarContrast" tools:targetApi="q">false</item>
<item name="android:enforceNavigationBarContrast" tools:targetApi="q">false</item>
</style>
<style name="Share.Window" parent="Theme.AppCompat">
<item name="android:windowEnterAnimation">@null</item>
<item name="android:windowExitAnimation">@null</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
</style>
<!-- BootTheme should inherit from Theme.SplashScreen -->
<style name="BootTheme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/bootsplash_background</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/bootsplash_logo</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
<!-- Base application theme. -->
<style name="AppThemeB" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
</style>
</resources>

View File

@@ -2,15 +2,9 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Allow drawing under the system bars background -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:fitsSystemWindows">false</item>
<!-- Set status bar background transparent -->
<item name="android:statusBarColor">@android:color/transparent</item>
<!-- Navigation bar will stay translucent on Android < 8.1 -->
<item name="android:windowTranslucentNavigation">true</item>
<!-- Customize your theme here. -->
<item name="android:windowDisablePreview">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
</style>
<style name="Share.Window" parent="Theme.AppCompat">

View File

@@ -6,5 +6,7 @@ include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid');
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
}

View File

@@ -0,0 +1,10 @@
# This `.xcode.env` file is versioned and is used to source the environment
# used when running script phases inside Xcode.
# To customize your local environment, you can create an `.xcode.env.local`
# file that is not versioned.
# NODE_BINARY variable contains the PATH to the node executable.
#
# Customize the NODE_BINARY variable here.
# For example, to use nvm with brew, add the following line
# . "$(brew --prefix nvm)/nvm.sh" --no-use
export NODE_BINARY=$(command -v node)

View File

@@ -1077,7 +1077,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1150,7 +1150,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1179,7 +1179,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
HEADER_SEARCH_PATHS = (
@@ -1251,7 +1251,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1409,7 +1409,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1420,7 +1420,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1450,7 +1450,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1461,7 +1461,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1490,7 +1490,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1563,7 +1563,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1593,7 +1593,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1809;
CURRENT_PROJECT_VERSION = 1810;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1666,7 +1666,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.9;
MARKETING_VERSION = 1.8.10;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -16,6 +16,7 @@
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
@@ -70,8 +71,9 @@ RCTBridge *bridge;
shareViewController.view = shareView;
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:shareView];
} else {
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Notesnook" initialProperties:nil];
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"Notesnook" , initProps);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
@@ -87,6 +89,25 @@ RCTBridge *bridge;
return YES;
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return false;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG

View File

@@ -1,7 +1,7 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '11.0'
platform :ios, '12.4'
install! 'cocoapods', :deterministic_uuids => false
pod 'Base64'

0
apps/mobile/new.js Normal file
View File

View File

@@ -1,56 +0,0 @@
diff --git a/node_modules/react-native-cli-bump-version/lib/index.js b/node_modules/react-native-cli-bump-version/lib/index.js
index a319832..a6710aa 100644
--- a/node_modules/react-native-cli-bump-version/lib/index.js
+++ b/node_modules/react-native-cli-bump-version/lib/index.js
@@ -94,6 +94,14 @@ class BuildGradleManager extends BaseFileManager {
return { current, next };
}
}
+
+class VersionManager extends BaseFileManager {
+
+ setVersionCode(code) {
+ this.content = `export const APP_VERSION=${code};`;
+ }
+}
+
class PackageJSONManager {
constructor(basePath) {
this.content = null;
@@ -122,6 +130,8 @@ class PackageJSONManager {
};
}
}
+
+
class ProjectFilesManager {
constructor(configs) {
const { root, pbxprojPath, buildGradlePath } = configs;
@@ -129,7 +139,9 @@ class ProjectFilesManager {
this.buildGradle = new BuildGradleManager(buildGradlePath);
this.pbx = new PBXManager(pbxprojPath);
this.packageJSON = new PackageJSONManager(path_1.default.join(root, 'package.json'));
+ this.versionManager = new VersionManager(path_1.default.join(root, 'version.js'));
}
+
syncSemver(semverString) {
const { skipSemVerFor } = this.configs;
if (!skipSemVerFor.includes('ios')) {
@@ -151,14 +163,17 @@ class ProjectFilesManager {
}
if (!skipCodeFor.includes('android')) {
const { next: gradleNext, current: gradleCurrent } = this.buildGradle.bumpCode();
+ this.versionManager.setVersionCode(gradleNext);
console.log(success(`Android build.gradle code: ${gradleCurrent} -> ${gradleNext}`));
}
+
}
run() {
this.dryRun();
this.pbx.write();
this.buildGradle.write();
this.packageJSON.write();
+ this.versionManager.write();
}
/**
* Separated for testing

View File

@@ -0,0 +1,22 @@
diff --git a/node_modules/react-native-cli-bump-version/react-native.config.js b/node_modules/react-native-cli-bump-version/react-native.config.js
index fe5274e..6197368 100644
--- a/node_modules/react-native-cli-bump-version/react-native.config.js
+++ b/node_modules/react-native-cli-bump-version/react-native.config.js
@@ -10,16 +10,14 @@ module.exports = {
console.log("My work here is done.");
return;
}
-
const appGradlePath = path.join(
config.project.android.sourceDir,
config.project.android.appName,
"build.gradle",
);
-
versioner({
root: config.root,
- pbxprojPath: config.project.ios.pbxprojPath,
+ pbxprojPath: `${config.project.ios.sourceDir}/${config.project.ios.xcodeProject.name.replace(".xcworkspace",".xcodeproj")}/project.pbxproj`,
buildGradlePath: appGradlePath,
type: args.type,
semver: args.semver,

View File

@@ -0,0 +1,421 @@
diff --git a/node_modules/react-native-reanimated/android/CMakeLists.txt b/node_modules/react-native-reanimated/android/CMakeLists.txt
index e6ff5f2..945b1fb 100644
--- a/node_modules/react-native-reanimated/android/CMakeLists.txt
+++ b/node_modules/react-native-reanimated/android/CMakeLists.txt
@@ -1,3 +1,4 @@
+project(Reanimated)
cmake_minimum_required(VERSION 3.5.1)
set (CMAKE_VERBOSE_MAKEFILE ON)
@@ -13,6 +14,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
set (PACKAGE_NAME "reanimated")
+set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
set (SRC_DIR ${CMAKE_SOURCE_DIR}/src)
if(${CLIENT_SIDE_BUILD})
@@ -119,26 +121,18 @@ find_library(
PATHS ${LIBRN_DIR}
NO_CMAKE_FIND_ROOT_PATH
)
-find_library(
- FOLLY_JSON_LIB
- folly_json
- PATHS ${LIBRN_DIR}
- NO_CMAKE_FIND_ROOT_PATH
-)
find_library(
REACT_NATIVE_JNI_LIB
reactnativejni
PATHS ${LIBRN_DIR}
NO_CMAKE_FIND_ROOT_PATH
)
-
find_library(
GLOG_LIB
glog
PATHS ${LIBRN_DIR}
NO_CMAKE_FIND_ROOT_PATH
)
-
find_library(
FBJNI_LIB
fbjni
@@ -146,6 +140,22 @@ find_library(
NO_CMAKE_FIND_ROOT_PATH
)
+if(${REACT_NATIVE_TARGET_VERSION} LESS 69)
+ find_library(
+ FOLLY_LIB
+ folly_json
+ PATHS ${LIBRN_DIR}
+ NO_CMAKE_FIND_ROOT_PATH
+ )
+else()
+ find_library(
+ FOLLY_LIB
+ folly_runtime
+ PATHS ${LIBRN_DIR}
+ NO_CMAKE_FIND_ROOT_PATH
+ )
+endif()
+
if(${REACT_NATIVE_TARGET_VERSION} LESS 66)
set (JSI_LIB "")
else()
@@ -169,7 +179,7 @@ if(${FOR_HERMES})
${HERMES_LIB}
${GLOG_LIB}
${FBJNI_LIB}
- ${FOLLY_JSON_LIB}
+ ${FOLLY_LIB}
${REACT_NATIVE_JNI_LIB}
android
)
@@ -181,7 +191,7 @@ else()
${JSEXECUTOR_LIB}
${GLOG_LIB}
${FBJNI_LIB}
- ${FOLLY_JSON_LIB}
+ ${FOLLY_LIB}
${REACT_NATIVE_JNI_LIB}
android
)
diff --git a/node_modules/react-native-reanimated/android/build.gradle b/node_modules/react-native-reanimated/android/build.gradle
index cc460da..23f491e 100644
--- a/node_modules/react-native-reanimated/android/build.gradle
+++ b/node_modules/react-native-reanimated/android/build.gradle
@@ -1,10 +1,8 @@
+import com.android.Version
+
import java.nio.file.Paths
import org.apache.tools.ant.filters.ReplaceTokens
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
import groovy.json.JsonSlurper
-import java.util.zip.ZipFile
/**
* Finds the path of the installed npm package with the given name using Node's
@@ -45,18 +43,6 @@ def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
-def getCurrentFlavor() {
- String taskRequestName = getGradle().getStartParameter().getTaskRequests().toString()
- Pattern pattern = Pattern.compile("(assemble|bundle|install|generate)(\\w*)(Release|Debug)")
- Matcher matcher = pattern.matcher(taskRequestName)
-
- if (matcher.find()) {
- return matcher.group(2)
- }
-
- return "NOT-FOUND"
-}
-
def resolveBuildType() {
def buildType = System.getenv("CLIENT_SIDE_BUILD")
if (buildType != null) {
@@ -86,127 +72,6 @@ def resolveReactNativeDirectory() {
return file("$projectDir/../../react-native")
}
-abstract class replaceSoTask extends DefaultTask {
- public static String appName = ":app"
- public static String buildDir = "../../../android/app/build"
- public static String fbjniVersion = "0.3.0"
-
- @TaskAction
- def run() {
- def libSoDir = new File("${buildDir}/tmp/libSo")
- if (!libSoDir.exists()) {
- libSoDir.mkdirs()
- def fbjniUrl = "https://repo1.maven.org/maven2/com/facebook/fbjni/fbjni/${fbjniVersion}/fbjni-${fbjniVersion}.aar"
- def aarFile = new File("${libSoDir.path}/fbjni-${fbjniVersion}.aar")
- aarFile.createNewFile()
- aarFile.withOutputStream { out ->
- def url = new URL(fbjniUrl).openConnection()
- out << url.inputStream
- }
- if (!aarFile.exists()) {
- println("Unable to find ${libSoDir.path}/fbjni-${fbjniVersion}.aar")
- return
- }
- def zipFile = new ZipFile(new File("${libSoDir.path}/fbjni-${fbjniVersion}.aar"))
- zipFile.entries().each {
- def zipIt = it
- if (zipIt.name.contains("libfbjni.so")) {
- new File("${libSoDir.path}/" + zipIt.name.replace("/libfbjni.so", "")).mkdirs()
- new File("${libSoDir.path}/" + zipIt.name).withOutputStream{
- it << zipFile.getInputStream(zipIt)
- }
- }
- }
- }
- for(def abiVersion in ["x86", "x86_64", "armeabi-v7a", "arm64-v8a"]) {
- ant.sequential {
- copy(
- tofile: "${buildDir}/intermediates/merged_native_libs/debug/out/lib/${abiVersion}/libfbjni.so",
- file: "${buildDir}/tmp/libSo/jni/${abiVersion}/libfbjni.so",
- overwrite: true
- )
- }
- }
- }
-}
-
-def detectAAR(minor, engine) {
- def rnMinorVersionCopy = Integer.parseInt(minor)
- def aar = file("react-native-reanimated-${rnMinorVersionCopy}-${engine}.aar")
-
- if (aar.exists()) {
- println "AAR for react-native-reanimated has been found\n$aar"
- return aar
- } else {
- while (!aar.exists() && rnMinorVersionCopy >= 63) {
- rnMinorVersionCopy -= 1
- aar = file("react-native-reanimated-${rnMinorVersionCopy}-${engine}.aar")
- }
-
- if (rnMinorVersionCopy < 63) {
- println "No AAR for react-native-reanimated found. Attempting to build from source."
- } else { // aar exists, but was build for lower react-native version
- println "\n\n\n"
- println "****************************************************************************************"
- println "\n\n\n"
- println "WARNING reanimated - no version-specific reanimated AAR for react-native version $rnMinorVersion found."
- println "Falling back to AAR for react-native version $rnMinorVersionCopy."
- println "The react-native JSI interface is not ABI-safe yet, this may result in crashes."
- println "Please post a pull request to implement support for react-native version $rnMinorVersion to the reanimated repo."
- println "Thanks!"
- println "\n\n\n"
- println "****************************************************************************************"
-
- return aar
- }
- }
- return null
-}
-
-def detectJsRuntime() {
- def runtimeType = "jsc"
- rootProject.getSubprojects().forEach({project ->
- if (project.plugins.hasPlugin("com.android.application")) {
- if (project.ext.react.enableHermes) {
- runtimeType = "hermes"
- }
- }
- })
- return runtimeType
-}
-
-def detectReanimatedConfig() {
- def buildFromSourceConf = false
- rootProject.getSubprojects().forEach({project ->
- if (project.plugins.hasPlugin("com.android.application")) {
- if (
- project.ext.has("reanimated")
- && project.ext.reanimated.buildFromSource
- ) {
- buildFromSourceConf = true
- }
- }
- })
- return buildFromSourceConf
-}
-
-def shouldBuildFromSource(aar) {
- if (isDeveloperMode()) {
- // Example app
- return true
- }
- else if (detectReanimatedConfig()) {
- // on user demand
- return true
- }
- else if (aar != null) {
- // when binary exist
- return false
- }
- // when binary is not found
- return true
-}
-
boolean CLIENT_SIDE_BUILD = resolveBuildType()
if (CLIENT_SIDE_BUILD) {
configurations.maybeCreate("default")
@@ -217,70 +82,6 @@ def reactNativeManifestAsJson = new JsonSlurper().parseText(reactNativeManifest.
def reactNativeVersion = reactNativeManifestAsJson.version as String
def (major, minor, patch) = reactNativeVersion.tokenize('.')
def rnMinorVersion = Integer.parseInt(minor)
-def engine = detectJsRuntime()
-def aar = detectAAR(minor, engine)
-boolean BUILD_FROM_SOURCE = shouldBuildFromSource(aar)
-
-def getTaskByPath(project, String appName, String secondPart, String flavorString, String lastPart) {
- String pathName = "${appName}:${secondPart}${flavorString}${lastPart}"
- Task task = project.getTasks().findByPath(pathName)
- if (task != null) {
- return task
- }
- pathName = "${appName}:${secondPart}${flavorString.capitalize()}${lastPart}"
- return project.getTasks().findByPath(pathName)
-}
-
-if (!BUILD_FROM_SOURCE) {
- if (rnMinorVersion < 65) {
- tasks.register("replaceSoTaskDebug", replaceSoTask)
- tasks.register("replaceSoTaskRelease", replaceSoTask)
- Task replaceSoTaskDebug = project.getTasks().findByPath(":react-native-reanimated:replaceSoTaskDebug")
- Task replaceSoTaskRelease = project.getTasks().findByPath(":react-native-reanimated:replaceSoTaskRelease")
-
- if (replaceSoTaskDebug != null && replaceSoTaskRelease != null) {
- rootProject.getSubprojects().forEach({project ->
- if (project.plugins.hasPlugin("com.android.application") && project.getProperties().get("android")) {
- def projectProperties = project.getProperties()
- def flavorString = getCurrentFlavor()
- def reanimatedConf = projectProperties.get("reanimated")
-
- if (
- flavorString != "NOT-FOUND"
- && (!reanimatedConf || (reanimatedConf && !reanimatedConf.get("enablePackagingOptions")))
- ) {
- replaceSoTask.appName = projectProperties.path
- replaceSoTask.buildDir = projectProperties.buildDir
- def appName = projectProperties.path
-
- Task debugNativeLibsTask = getTaskByPath(project, appName, "merge", flavorString, "DebugNativeLibs")
- Task debugDebugSymbolsTask = getTaskByPath(project, appName, "strip", flavorString, "DebugDebugSymbols")
- Task releaseNativeLibsTask = getTaskByPath(project, appName, "merge", flavorString, "ReleaseNativeLibs")
- Task releaseDebugSymbolsTask = getTaskByPath(project, appName, "strip", flavorString, "ReleaseDebugSymbols")
- Task debugTask = getTaskByPath(project, appName, "package", flavorString, "Debug")
- Task releaseTask = getTaskByPath(project, appName, "package", flavorString, "Release")
-
- if (
- debugNativeLibsTask != null && debugDebugSymbolsTask != null
- && releaseNativeLibsTask != null && releaseDebugSymbolsTask != null
- && debugTask != null && releaseTask != null
- ) {
- replaceSoTaskDebug.dependsOn(debugNativeLibsTask, debugDebugSymbolsTask)
- debugTask.dependsOn(replaceSoTaskDebug)
- replaceSoTaskRelease.dependsOn(releaseNativeLibsTask, releaseDebugSymbolsTask)
- releaseTask.dependsOn(replaceSoTaskRelease)
- }
- }
- }
- })
- }
- }
-
- artifacts.add("default", aar)
-}
-
-// end if already loaded aar
-if (!BUILD_FROM_SOURCE) return
def localProps = new Properties()
def localPropertiesFile = file("local.properties")
@@ -381,8 +182,7 @@ android {
"-DBOOST_VERSION=${BOOST_VERSION}",
"-DBUILD_DIR=${buildDir}",
"-DFOR_HERMES=${FOR_HERMES}",
- "-DCLIENT_SIDE_BUILD=${CLIENT_SIDE_BUILD}",
- "--clean-first"
+ "-DCLIENT_SIDE_BUILD=${CLIENT_SIDE_BUILD}"
abiFilters (*reactNativeArchitectures())
_stackProtectorFlag ? (cppFlags("-fstack-protector-all")) : null
}
@@ -407,6 +207,7 @@ android {
"**/libfbjni.so",
"**/libjsi.so",
"**/libfolly_json.so",
+ "**/libfolly_runtime.so",
"**/libglog.so",
"**/libhermes.so",
"**/libreactnativejni.so",
@@ -435,7 +236,7 @@ task cleanCmakeCache() {
}
task printVersions {
- println "Android gradle plugin: ${com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION}"
+ println "Android gradle plugin: ${Version.ANDROID_GRADLE_PLUGIN_VERSION}"
println "Gradle: ${project.gradle.gradleVersion}"
}
@@ -459,7 +260,12 @@ task createNativeDepsDirectories(dependsOn: applyJavaPatches) {
}
task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
- src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz")
+ def transformedVersion = BOOST_VERSION.replace("_", ".")
+ def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/boost_${BOOST_VERSION}.tar.gz"
+ if (rnMinorVersion < 69) {
+ srcUrl = "https://github.com/react-native-community/boost-for-react-native/releases/download/v${transformedVersion}-0/boost_${BOOST_VERSION}.tar.gz"
+ }
+ src(srcUrl)
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
@@ -672,9 +478,29 @@ dependencies {
extractHeaders("com.facebook.fbjni:fbjni:" + FBJNI_VERSION + ":headers")
extractSO("com.facebook.fbjni:fbjni:" + FBJNI_VERSION)
- def rnAAR = fileTree("$reactNative/android").matching({ it.include "**/**/*.aar" }).singleFile
def jscAAR = fileTree("$reactNative/../jsc-android/dist/org/webkit/android-jsc").matching({ it.include "**/**/*.aar" }).singleFile
- extractSO(files(rnAAR, jscAAR))
+ extractSO(files(jscAAR))
+}
+
+task unpackReactNativeAAR {
+ def buildType = "debug"
+ tasks.all({ task ->
+ if (task.name == "buildCMakeRelease") {
+ buildType = "release"
+ }
+ })
+ def rnAarMatcher = "**/react-native/**/*${buildType}.aar"
+ if (rnMinorVersion < 69) {
+ rnAarMatcher = "**/**/*.aar"
+ }
+ def rnAAR = fileTree("$reactNative/android").matching({ it.include rnAarMatcher }).singleFile
+ def file = rnAAR.absoluteFile
+ def packageName = file.name.tokenize('-')[0]
+ copy {
+ from zipTree(file)
+ into "$reactNative/ReactAndroid/src/main/jni/first-party/$packageName/"
+ include "jni/**/*.so"
+ }
}
task downloadNdkBuildDependencies {
@@ -686,7 +512,7 @@ task downloadNdkBuildDependencies {
dependsOn(downloadGlog)
}
-task prepareThirdPartyNdkHeaders(dependsOn:[downloadNdkBuildDependencies, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog]) {
+task prepareThirdPartyNdkHeaders(dependsOn:[downloadNdkBuildDependencies, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, unpackReactNativeAAR]) {
}
def nativeBuildDependsOn(dependsOnTask) {
@@ -702,11 +528,17 @@ afterEvaluate {
nativeBuildDependsOn(prepareThirdPartyNdkHeaders)
nativeBuildDependsOn(extractAARHeaders)
nativeBuildDependsOn(extractSOFiles)
+
+ tasks.forEach({ task ->
+ if (task.name.contains("JniLibFolders")) {
+ task.dependsOn(packageNdkLibs)
+ }
+ })
}
if (CLIENT_SIDE_BUILD) {
def aarDir = "${buildDir}/outputs"
- aar = file("${aarDir}/android-debug.aar")
+ def aar = file("${aarDir}/android-debug.aar")
if (aar == null) {
throw GradleScriptException("AAR build failed. No AAR found in ${aarDir}.")
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,103 @@
diff --git a/node_modules/rn-fetch-blob/android/build.gradle b/node_modules/rn-fetch-blob/android/build.gradle
index a4ca7a4..4fd3cfa 100644
--- a/node_modules/rn-fetch-blob/android/build.gradle
+++ b/node_modules/rn-fetch-blob/android/build.gradle
@@ -41,6 +41,7 @@ android {
dependencies {
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
- //compile 'com.squareup.okhttp3:okhttp:+'
+ implementation 'com.squareup.okhttp3:okhttp:3.4.1'
+
//{RNFetchBlob_PRE_0.28_DEPDENDENCY}
}
diff --git a/node_modules/rn-fetch-blob/react-native.config.js b/node_modules/rn-fetch-blob/react-native.config.js
deleted file mode 100644
index 03c61b6..0000000
--- a/node_modules/rn-fetch-blob/react-native.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = {
- dependency: {
- hooks: {
- prelink: 'node ./node_modules/rn-fetch-blob/scripts/prelink.js',
- },
- },
-};
diff --git a/node_modules/rn-fetch-blob/scripts/prelink.js b/node_modules/rn-fetch-blob/scripts/prelink.js
deleted file mode 100644
index e2c3ac4..0000000
--- a/node_modules/rn-fetch-blob/scripts/prelink.js
+++ /dev/null
@@ -1,71 +0,0 @@
-try {
- var fs = require('fs');
- var glob = require('glob');
- var addAndroidPermissions = process.env.RNFB_ANDROID_PERMISSIONS == 'true';
- var MANIFEST_PATH = glob.sync(process.cwd() + '/android/app/src/main/**/AndroidManifest.xml')[0];
- var PACKAGE_JSON = process.cwd() + '/package.json';
- var package = JSON.parse(fs.readFileSync(PACKAGE_JSON));
- var APP_NAME = package.name;
- var PACKAGE_GRADLE = process.cwd() + '/node_modules/rn-fetch-blob/android/build.gradle'
- var VERSION = checkVersion();
-
- console.log('RNFetchBlob detected app version => ' + VERSION);
-
- if(VERSION < 0.28) {
- console.log('You project version is '+ VERSION + ' which may not compatible to rn-fetch-blob 7.0+, please consider upgrade your application template to react-native 0.27+.')
- // add OkHttp3 dependency fo pre 0.28 project
- var main = fs.readFileSync(PACKAGE_GRADLE);
- console.log('adding OkHttp3 dependency to pre 0.28 project .. ')
- main = String(main).replace('//{RNFetchBlob_PRE_0.28_DEPDENDENCY}', "compile 'com.squareup.okhttp3:okhttp:3.4.1'");
- fs.writeFileSync(PACKAGE_GRADLE, main);
- console.log('adding OkHttp3 dependency to pre 0.28 project .. ok')
- }
-
- console.log('Add Android permissions => ' + (addAndroidPermissions == "true"))
-
- if(addAndroidPermissions) {
-
- // set file access permission for Android < 6.0
- fs.readFile(MANIFEST_PATH, function(err, data) {
-
- if(err)
- console.log('failed to locate AndroidManifest.xml file, you may have to add file access permission manually.');
- else {
-
- console.log('RNFetchBlob patching AndroidManifest.xml .. ');
- // append fs permission
- data = String(data).replace(
- '<uses-permission android:name="android.permission.INTERNET" />',
- '<uses-permission android:name="android.permission.INTERNET" />\n <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> '
- )
- // append DOWNLOAD_COMPLETE intent permission
- data = String(data).replace(
- '<category android:name="android.intent.category.LAUNCHER" />',
- '<category android:name="android.intent.category.LAUNCHER" />\n <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>'
- )
- fs.writeFileSync(MANIFEST_PATH, data);
- console.log('RNFetchBlob patching AndroidManifest.xml .. ok');
-
- }
-
- })
- }
- else {
- console.log(
- '\033[95mrn-fetch-blob \033[97mwill not automatically add Android permissions after \033[92m0.9.4 '+
- '\033[97mplease run the following command if you want to add default permissions :\n\n' +
- '\033[96m\tRNFB_ANDROID_PERMISSIONS=true react-native link \n')
- }
-
- function checkVersion() {
- console.log('RNFetchBlob checking app version ..');
- return parseFloat(/\d\.\d+(?=\.)/.exec(package.dependencies['react-native']));
- }
-
-} catch(err) {
- console.log(
- '\033[95mrn-fetch-blob\033[97m link \033[91mFAILED \033[97m\nCould not automatically link package :'+
- err.stack +
- 'please follow the instructions to manually link the library : ' +
- '\033[4mhttps://github.com/joltup/rn-fetch-blob/wiki/Manually-Link-Package\n')
-}

View File

@@ -87,21 +87,6 @@ const Launcher = React.memo(
const hideSplashScreen = async () => {
if (requireIntro.value) await sleep(500);
await RNBootSplash.hide({ fade: true });
setTimeout(async () => {
if (Platform.OS === 'android') {
NativeModules.RNBars.setStatusBarStyle(!colors.night ? 'light-content' : 'dark-content');
NativeModules.RNBars.setNavigationBarStyle(
!colors.night ? 'light-content' : 'dark-content'
);
await sleep(5);
NativeModules.RNBars.setStatusBarStyle(colors.night ? 'light-content' : 'dark-content');
NativeModules.RNBars.setNavigationBarStyle(
colors.night ? 'light-content' : 'dark-content'
);
} else {
StatusBar.setBarStyle(colors.night ? 'light-content' : 'dark-content');
}
}, 1000);
};
useEffect(() => {
@@ -161,6 +146,7 @@ const Launcher = React.memo(
};
const checkAppUpdateAvailable = async () => {
return;
try {
const version = await checkVersion();
if (!version.needsUpdate) return false;

View File

@@ -44,7 +44,7 @@ export const openNote = async (item, isTrash, setSelectedItem) => {
history.selectedItemsList = [];
}
if (_note.conflicted) {
if (!_note.conflicted) {
eSendEvent(eShowMergeDialog, _note);
return;
}

View File

@@ -1,21 +1,19 @@
import KeepAwake from '@sayem314/react-native-keep-awake';
import { EV, EVENTS } from 'notes-core/common';
import React, { createRef, useEffect, useState } from 'react';
import { Modal, Platform, SafeAreaView, Text, View } from 'react-native';
import React, { useEffect, useRef, useState } from 'react';
import { Modal, SafeAreaView, Text, View } from 'react-native';
import Animated from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import WebView from 'react-native-webview';
import Editor from '../../screens/editor';
import { editorController } from '../../screens/editor/tiptap/utils';
import { DDS } from '../../services/device-detection';
import { eSubscribeEvent, eUnSubscribeEvent, ToastEvent } from '../../services/event-manager';
import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../../services/event-manager';
import Navigation from '../../services/navigation';
import Sync from '../../services/sync';
import { useThemeStore } from '../../stores/use-theme-store';
import { dHeight } from '../../utils';
import { db } from '../../utils/database';
import { eApplyChanges, eShowMergeDialog } from '../../utils/events';
import { openLinkInBrowser } from '../../utils/functions';
import { normalize, SIZE } from '../../utils/size';
import { eOnLoadNote, eShowMergeDialog } from '../../utils/events';
import { SIZE } from '../../utils/size';
import { timeConverter } from '../../utils/time';
import BaseDialog from '../dialog/base-dialog';
import DialogButtons from '../dialog/dialog-buttons';
@@ -28,128 +26,42 @@ import Paragraph from '../ui/typography/paragraph';
const sourceUri = '';
const primaryWebView = createRef();
const secondaryWebView = createRef();
let note = null;
let primaryData = null;
let secondaryData = null;
function onMediaLoaded({ hash, src }) {
console.log('on media download complete');
let inject = `
(function(){
const elements = document.querySelectorAll("img[data-hash=${hash}]");
if (!elements || !elements.length) return;
for (let element of elements) element.setAttribute("src", "${src}");
})();`;
primaryWebView.current?.injectJavaScript(inject);
secondaryWebView.current?.injectJavaScript(inject);
}
const MergeConflicts = () => {
const colors = useThemeStore(state => state.colors);
const [visible, setVisible] = useState(false);
const [primary, setPrimary] = useState(true);
const [secondary, setSecondary] = useState(true);
const [keepContentFrom, setKeepContentFrom] = useState(null);
const [copyToSave, setCopyToSave] = useState(null);
const [disardedContent, setDiscardedContent] = useState(null);
const [keep, setKeep] = useState(null);
const [copy, setCopy] = useState(null);
const [dialogVisible, setDialogVisible] = useState(false);
const [loadingAttachments, setLoadingAttachments] = useState(false);
const insets = useSafeAreaInsets();
const onPrimaryWebViewLoad = async () => {
let content = await db.content.insertPlaceholders(primaryData, 'placeholder.svg');
postMessage(primaryWebView, 'htmldiff', content.data);
let theme = { ...colors };
theme.factor = normalize(1);
primaryWebView.current?.injectJavaScript(`
(function() {
let v = ${JSON.stringify(theme)}
if (pageTheme) {
pageTheme.colors = v;
}
setTheme();
})();
`);
};
const onSecondaryWebViewLoad = async () => {
if (!secondaryData) return;
let content = await db.content.insertPlaceholders(secondaryData, 'placeholder.svg');
postMessage(secondaryWebView, 'htmldiff', content?.data);
let theme = { ...colors };
theme.factor = normalize(1);
secondaryWebView.current?.injectJavaScript(`
(function() {
let v = ${JSON.stringify(theme)}
if (pageTheme) {
pageTheme.colors = v;
}
setTheme();
})();
`);
};
function postMessage(webview, type, value = null) {
let message = {
type: type,
value
};
webview.current?.postMessage(JSON.stringify(message));
}
const _onShouldStartLoadWithRequest = request => {
if (request.url.includes('http')) {
openLinkInBrowser(request.url, colors)
.catch(e =>
ToastEvent.show({
title: 'Failed to open link',
message: e.message,
type: 'success',
context: 'local'
})
)
.then(r => {
console.log('closed');
});
return false;
} else {
return true;
}
};
const content = useRef({});
const isKeepingConflicted = !keep?.conflicted;
const isKeeping = !!keep;
const applyChanges = async () => {
let content = keepContentFrom === 'primary' ? primaryData : secondaryData;
let keepCopy =
copyToSave === 'primary' ? primaryData : copyToSave === 'secondary' ? secondaryData : null;
let _content = keep;
let note = db.notes.note(_content.noteId).data;
await db.notes.add({
id: note.id,
conflicted: false,
dateEdited: content.dateEdited
dateEdited: _content.dateEdited
});
await db.content.add({
id: note.contentId,
data: content.data,
type: content.type,
dateResolved: secondaryData.dateModified,
data: _content.data,
type: _content.type,
dateResolved: content.current.conflicted.dateModified,
sessionId: Date.now(),
conflicted: false
});
if (keepCopy) {
if (copy) {
await db.notes.add({
title: note.title + ' (Copy)',
content: {
data: keepCopy.data,
type: keepCopy.type
},
id: null
data: copy.data,
type: copy.type
}
});
}
Navigation.queueRoutesForUpdate(
@@ -167,89 +79,126 @@ const MergeConflicts = () => {
};
const show = async item => {
note = item;
let content = await db.content.raw(note.contentId);
switch (content.type) {
let noteContent = await db.content.raw(item.contentId);
switch (noteContent.type) {
case 'tiny':
primaryData = content;
secondaryData = content.conflicted;
content.current = { ...noteContent };
if (!noteContent.conflicted) {
content.current.conflicted = { ...noteContent };
}
}
setVisible(true);
};
useEffect(() => {
eSubscribeEvent(eApplyChanges, applyChanges);
eSubscribeEvent(eShowMergeDialog, show);
return () => {
eUnSubscribeEvent(eApplyChanges, applyChanges);
eUnSubscribeEvent(eShowMergeDialog, show);
};
}, []);
const onPressKeepFromPrimaryWebView = () => {
if (keepContentFrom == 'primary') {
setKeepContentFrom(null);
} else {
setKeepContentFrom('primary');
}
};
const onPressSaveCopyFromPrimaryWebView = () => {
setCopyToSave('primary');
setDialogVisible(true);
};
const onPressKeepFromSecondaryWebView = () => {
if (keepContentFrom == 'secondary') {
setKeepContentFrom(null);
} else {
setKeepContentFrom('secondary');
}
};
const onPressSaveCopyFromSecondaryWebView = () => {
setCopyToSave('secondary');
setDialogVisible(true);
};
const onPressDiscardFromPrimaryWebView = () => {
setDiscardedContent('primary');
setDialogVisible(true);
};
const onPressDiscardFromSecondaryWebView = () => {
setDiscardedContent('secondary');
setDialogVisible(true);
};
const close = () => {
db.fs.cancel(primaryData?.noteId);
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
setVisible(false);
setPrimary(true);
setSecondary(true);
setCopyToSave(null);
setDiscardedContent(null);
setKeepContentFrom(null);
setCopy(null);
setKeep(null);
setDialogVisible(false);
primaryData = null;
secondaryData = null;
note = null;
};
const onLoadImages = async () => {
try {
setLoadingAttachments(true);
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
await db.content.downloadMedia(primaryData.data.noteId, primaryData);
await db.content.downloadMedia(primaryData.data.noteId, secondaryData);
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
setLoadingAttachments(false);
} catch (e) {
setLoadingAttachments(false);
eUnSubscribeEvent(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
}
const ConfigBar = ({ isDiscarded, keeping, back, isCurrent, contentToKeep }) => {
return (
<View
style={{
width: '100%',
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 12,
paddingLeft: 6
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
}}
>
{back && <IconButton onPress={close} color={colors.pri} name="arrow-left" />}
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: isCurrent ? colors.accent : colors.red, fontWeight: 'bold' }}>
{isCurrent ? '(This Device)' : '(Incoming)'}
</Text>
{'\n'}
{timeConverter(contentToKeep.dateEdited)}
</Paragraph>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
}}
>
{isDiscarded ? (
<Button
onPress={() => {
setCopy(contentToKeep);
setDialogVisible(true);
}}
title="Save a copy"
type="grayBg"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
/>
) : null}
<View style={{ width: 10 }} />
{isDiscarded ? (
<Button
title="Discard"
type="accent"
accentColor="red"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
accentText="light"
color={colors.errorText}
onPress={() => {
setDialogVisible(true);
}}
/>
) : null}
{isDiscarded ? null : (
<>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
fontSize={SIZE.xs}
title={keeping && !isDiscarded ? 'Undo' : 'Keep'}
onPress={() => {
setKeep(keeping && !isDiscarded ? null : contentToKeep);
}}
/>
</>
)}
</View>
</View>
);
};
return !visible ? null : (
@@ -302,103 +251,13 @@ const MergeConflicts = () => {
backgroundColor: DDS.isLargeTablet() ? 'rgba(0,0,0,0.3)' : null
}}
>
<View
style={{
width: '100%',
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 12,
paddingLeft: 6
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
}}
>
<IconButton onPress={close} color={colors.pri} name="arrow-left" />
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: colors.accent, fontWeight: 'bold' }}>(This Device)</Text>
{'\n'}
{timeConverter(primaryData?.dateEdited)}
</Paragraph>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
}}
>
{keepContentFrom === 'secondary' ? (
<Button
onPress={onPressSaveCopyFromPrimaryWebView}
title="Save a copy"
type="grayBg"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
<ConfigBar
back={true}
isCurrent={true}
isDiscarded={isKeeping && isKeepingConflicted}
keeping={isKeeping}
contentToKeep={content.current}
/>
) : null}
<View style={{ width: 10 }} />
{keepContentFrom === 'secondary' ? (
<Button
title="Discard"
type="accent"
accentColor="red"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
accentText="light"
color={colors.errorText}
onPress={onPressDiscardFromPrimaryWebView}
/>
) : null}
{keepContentFrom === 'secondary' ? null : (
<>
<Button
type="grayBg"
title="Load images"
onPress={onLoadImages}
height={30}
loading={loadingAttachments}
fontSize={SIZE.xs}
icon="download"
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
/>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
fontSize={SIZE.xs}
title={keepContentFrom === 'primary' ? 'Undo' : 'Keep'}
onPress={onPressKeepFromPrimaryWebView}
/>
</>
)}
</View>
</View>
<Animated.View
style={{
@@ -408,129 +267,26 @@ const MergeConflicts = () => {
borderBottomColor: colors.nav
}}
>
<WebView
onLoad={onPrimaryWebViewLoad}
ref={primaryWebView}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
}}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
domStorageEnabled={true}
scrollEnabled={true}
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheEnabled={true}
source={{
uri: sourceUri + 'plaineditor.html'
<Editor
noHeader
noToolbar
readonly
editorId=":conflictPrimary"
onLoad={() => {
const note = db.notes.note(content.current?.noteId)?.data;
if (!note) return;
eSendEvent(eOnLoadNote + ':conflictPrimary', { ...note, content: content.current });
}}
/>
</Animated.View>
<View
style={{
width: '100%',
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 12
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
}}
>
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: 'red', fontWeight: 'bold' }}>(Incoming)</Text>
{'\n'}
{timeConverter(secondaryData?.dateEdited)}
</Paragraph>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
}}
>
{keepContentFrom === 'primary' ? (
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
type="accent"
fontSize={SIZE.xs}
onPress={onPressSaveCopyFromSecondaryWebView}
title="Save a copy"
<ConfigBar
back={false}
isCurrent={false}
isDiscarded={isKeeping && !isKeepingConflicted}
keeping={isKeeping}
contentToKeep={content.current.conflicted}
/>
) : null}
<View style={{ width: 10 }} />
{keepContentFrom === 'primary' ? (
<Button
title="Discard"
type="accent"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
fontSize={SIZE.xs}
accentColor="red"
accentText="light"
onPress={onPressDiscardFromSecondaryWebView}
/>
) : null}
{keepContentFrom === 'primary' ? null : (
<>
<Button
type="grayBg"
title="Load images"
height={30}
loading={loadingAttachments}
fontSize={SIZE.xs}
icon="download"
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
/>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
fontSize={SIZE.xs}
title={keepContentFrom === 'secondary' ? 'Undo' : 'Keep'}
onPress={onPressKeepFromSecondaryWebView}
/>
</>
)}
</View>
</View>
<Animated.View
style={{
@@ -539,29 +295,18 @@ const MergeConflicts = () => {
borderRadius: 10
}}
>
<WebView
onLoad={onSecondaryWebViewLoad}
ref={secondaryWebView}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
}}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
domStorageEnabled={true}
scrollEnabled={true}
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheEnabled={true}
source={{
uri: sourceUri + 'plaineditor.html'
<Editor
noHeader
noToolbar
readonly
editorId=":conflictSecondary"
onLoad={() => {
const note = db.notes.note(content.current?.noteId)?.data;
if (!note) return;
eSendEvent(eOnLoadNote + ':conflictSecondary', {
...note,
content: content.current.conflicted
});
}}
/>
</Animated.View>

View File

@@ -15,7 +15,7 @@ import Seperator from '../ui/seperator';
import Paragraph from '../ui/typography/paragraph';
import NotePreview from './preview';
export default function NoteHistory({ note, ref }) {
export default function NoteHistory({ note, fwdRef }) {
const [history, setHistory] = useState([]);
const [loading, setLoading] = useState(true);
const colors = useThemeStore(state => state.colors);
@@ -92,7 +92,7 @@ export default function NoteHistory({ note, ref }) {
<FlatList
onMomentumScrollEnd={() => {
ref?.current?.handleChildScrollEnd();
fwdRef?.current?.handleChildScrollEnd();
}}
style={{
paddingHorizontal: 12

View File

@@ -1,6 +1,7 @@
import React, { useRef } from 'react';
import { Platform, View } from 'react-native';
import WebView from 'react-native-webview';
import React from 'react';
import { View } from 'react-native';
import Editor from '../../screens/editor';
import EditorOverlay from '../../screens/editor/loading';
import { editorController } from '../../screens/editor/tiptap/utils';
import { eSendEvent, ToastEvent } from '../../services/event-manager';
import Navigation from '../../services/navigation';
@@ -8,66 +9,13 @@ import { useEditorStore } from '../../stores/use-editor-store';
import { useThemeStore } from '../../stores/use-theme-store';
import { db } from '../../utils/database';
import { eCloseProgressDialog, eOnLoadNote } from '../../utils/events';
import { openLinkInBrowser } from '../../utils/functions';
import { normalize } from '../../utils/size';
import DialogHeader from '../dialog/dialog-header';
import { Button } from '../ui/button';
import Paragraph from '../ui/typography/paragraph';
const sourceUri = '';
export default function NotePreview({ session, content }) {
const colors = useThemeStore(state => state.colors);
const webviewRef = useRef();
const onLoad = async () => {
let preview = await db.content.insertPlaceholders(content, 'placeholder.svg');
let theme = { ...colors };
theme.factor = normalize(1);
webviewRef.current?.injectJavaScript(`
(function() {
let v = ${JSON.stringify(theme)}
if (pageTheme) {
pageTheme.colors = v;
}
setTheme()
})();
`);
postMessage('htmldiff', preview?.data);
};
function postMessage(type, value = null) {
let message = {
type: type,
value
};
webviewRef.current?.postMessage(JSON.stringify(message));
}
const _onShouldStartLoadWithRequest = request => {
if (request.url.includes('https')) {
if (Platform.OS === 'ios' && !request.isTopFrame) return;
openLinkInBrowser(request.url, colors)
.catch(e =>
ToastEvent.show({
title: 'Failed to open link',
message: e.message,
type: 'success',
context: 'local'
})
)
.then(r => {
console.log('closed');
});
return false;
} else {
return true;
}
};
const editorId = ':noteHistory';
async function restore() {
await db.noteHistory.restore(session.id);
@@ -101,35 +49,19 @@ export default function NotePreview({ session, content }) {
>
<DialogHeader padding={12} title={session.session} />
{!session.locked ? (
<WebView
ref={webviewRef}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
onLoad={onLoad}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
}}
onError={e => {
console.log(e);
}}
nestedScrollEnabled
domStorageEnabled={true}
scrollEnabled={true}
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
source={{
uri: sourceUri + 'plaineditor.html'
<>
<EditorOverlay editorId={editorId} />
<Editor
noHeader
noToolbar
readonly
editorId={editorId}
onLoad={() => {
const note = db.notes.note(session.noteId)?.data;
eSendEvent(eOnLoadNote + editorId, { ...note, content });
}}
/>
</>
) : (
<View
style={{

View File

@@ -30,7 +30,7 @@ export interface TabsRef {
setScrollEnabled: () => true;
}
export const NewTabs = forwardRef<TabsRef, TabProps>(
export const FluidTabs = forwardRef<TabsRef, TabProps>(
(
{ children, dimensions, widths, onChangeTab, onScroll, enabled, onDrawerStateChange }: TabProps,
ref

View File

@@ -1,12 +1,11 @@
import { activateKeepAwake, deactivateKeepAwake } from '@sayem314/react-native-keep-awake';
import React, { useEffect, useRef } from 'react';
import { Platform, View } from 'react-native';
import { NavigationBar, StatusBar } from 'react-native-bars';
import { Platform, View, StatusBar } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { notesnook } from '../../e2e/test.ids';
import { SideMenu } from '../components/side-menu';
import { NewTabs } from '../components/tabs';
import { FluidTabs } from '../components/tabs';
import { editorState } from '../screens/editor/tiptap/utils';
import { EditorWrapper } from '../screens/editor/wrapper';
import { DDS } from '../services/device-detection';
@@ -255,18 +254,22 @@ export const TabsHolder = React.memo(
paddingBottom: Platform.OS === 'android' ? insets?.bottom : 0
}}
>
<StatusBar animated={true} barStyle={colors.night ? 'light-content' : 'dark-content'} />
<NavigationBar barStyle={colors.night ? 'light-content' : 'dark-content'} />
<StatusBar
animated={true}
barStyle={colors.night ? 'light-content' : 'dark-content'}
translucent={true}
backgroundColor="transparent"
/>
{deviceMode && widths[deviceMode] ? (
<NewTabs
<FluidTabs
ref={tabBarRef}
dimensions={dimensions}
widths={widths[deviceMode]}
enabled={deviceMode !== 'tablet'}
onScroll={onScroll}
onChangeTab={onChangeTab}
onDrawerStateChange={state => {}}
onDrawerStateChange={state => true}
>
<View
key="1"
@@ -309,7 +312,7 @@ export const TabsHolder = React.memo(
<NavigationStack />
</View>
<EditorWrapper key="3" width={widths} dimensions={dimensions} />
</NewTabs>
</FluidTabs>
) : null}
</View>
);

View File

@@ -1,76 +0,0 @@
import React from 'react';
import { Linking, Platform } from 'react-native';
import WebView from 'react-native-webview';
import { notesnook } from '../../../e2e/test.ids';
import { useEditor } from './tiptap/use-editor';
import { useEditorEvents } from './tiptap/use-editor-events';
import { editorController } from './tiptap/utils';
const sourceUri = '';
const source = { uri: sourceUri + 'index.html' };
const style = {
height: '100%',
maxHeight: '100%',
width: '100%',
alignSelf: 'center',
backgroundColor: 'transparent'
};
const onShouldStartLoadWithRequest = request => {
Linking.openURL(request.url);
return false;
};
const Editor = React.memo(
() => {
const editor = useEditor();
const onMessage = useEditorEvents(editor);
editorController.current = editor;
const onError = () => {
editorController.current?.setLoading(true);
setTimeout(() => editorController.current?.setLoading(false), 10);
};
return editor.loading ? null : (
<WebView
testID={notesnook.editor.id}
ref={editor.ref}
onLoad={editor.onLoad}
onRenderProcessGone={onError}
onError={onError}
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"
keyboardDisplayRequiresUserAction={false}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
domStorageEnabled={true}
bounces={false}
setBuiltInZoomControls={false}
setDisplayZoomControls={false}
allowFileAccess={true}
scalesPageToFit={true}
hideKeyboardAccessoryView={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
source={{
uri: 'http://localhost:3000'
}}
style={style}
autoManageStatusBarEnabled={false}
onMessage={onMessage}
/>
);
},
() => true
);
export default Editor;
// test uri "http://192.168.10.8:3000/index.html"

View File

@@ -0,0 +1,118 @@
import { EV, EVENTS } from 'notes-core/common';
import React, { useEffect } from 'react';
import { Linking, ViewStyle } from 'react-native';
import WebView from 'react-native-webview';
import { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes';
import { notesnook } from '../../../e2e/test.ids';
import EditorOverlay from './loading';
import { EditorProps } from './tiptap/types';
import { useEditor } from './tiptap/use-editor';
import { useEditorEvents } from './tiptap/use-editor-events';
import { editorController } from './tiptap/utils';
const sourceUri = '';
const source = { uri: sourceUri + 'index.html' };
const style: ViewStyle = {
height: '100%',
maxHeight: '100%',
width: '100%',
alignSelf: 'center',
backgroundColor: 'transparent'
};
const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
Linking.openURL(request.url);
return false;
};
const Editor = React.memo(
({ readonly, noToolbar, noHeader, withController, editorId, onLoad }: EditorProps) => {
const editor = useEditor(editorId || '', readonly);
const onMessage = useEditorEvents(editor, { readonly, noToolbar, noHeader });
const onMediaDownloaded = ({
hash,
groupId,
src
}: {
hash: string;
groupId: string;
src: string;
}) => {
console.log('onMediaDownoaded', groupId);
if (groupId !== editor.note.current?.id) return;
editor.commands.updateImage({
hash: hash,
src: src
});
};
useEffect(() => {
onLoad && onLoad();
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
return () => {
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
};
}, []);
if (withController) {
//@ts-ignore
editorController.current = editor;
}
const onError = () => {
editor.setLoading(true);
setTimeout(() => editor.setLoading(false), 10);
};
return editor.loading ? null : (
<>
<WebView
testID={notesnook.editor.id}
ref={editor.ref}
onLoad={editor.onLoad}
onRenderProcessGone={onError}
nestedScrollEnabled
onError={onError}
injectedJavaScriptBeforeContentLoaded={`
globalThis.readonly=${readonly};
globalThis.noToolbar=${noToolbar};
globalThis.noHeader=${noHeader};
`}
injectedJavaScript={`
globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"
keyboardDisplayRequiresUserAction={false}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
domStorageEnabled={true}
bounces={false}
setBuiltInZoomControls={false}
setDisplayZoomControls={false}
allowFileAccess={true}
scalesPageToFit={true}
hideKeyboardAccessoryView={true}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
source={{
uri: 'http://localhost:3000'
}}
style={style}
autoManageStatusBarEnabled={false}
onMessage={onMessage || undefined}
/>
<EditorOverlay editorId={editorId || ''} editor={editor} />
</>
);
},
() => true
);
export default Editor;
// test uri "http://192.168.10.8:3000/index.html"

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { View } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
import { Button } from '../../components/ui/button';
import Heading from '../../components/ui/typography/heading';
import Paragraph from '../../components/ui/typography/paragraph';
@@ -11,47 +11,43 @@ import { SIZE } from '../../utils/size';
import { timeConverter } from '../../utils/time';
import { editorState } from './tiptap/utils';
let timer = null;
let timerError = null;
let timerClosing = null;
const EditorOverlay = () => {
const EditorOverlay = ({ editorId = '', editor }) => {
const colors = useThemeStore(state => state.colors);
const [loading, setLoading] = useState(null);
const [error, setError] = useState(false);
const opacity = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: opacity.value
};
const isDefaultEditor = editorId === '';
const timers = useRef({
loading: 0,
error: 0,
closing: 0
});
const clearTimers = () => {
clearTimeout(timers.current.loading);
clearTimeout(timers.current.error);
clearTimeout(timers.current.closing);
};
const load = async _loading => {
editorState().overlay = true;
clearTimeout(timer);
clearTimeout(timerError);
clearTimeout(timerClosing);
clearTimers();
if (_loading) {
opacity.value = 1;
setLoading(_loading);
timerError = setTimeout(() => {
timers.current.error = setTimeout(() => {
if (_loading) {
let _n = _loading;
_n.forced = true;
eSendEvent(eOnLoadNote, _n);
let note = _loading;
note.forced = true;
eSendEvent(eOnLoadNote + editorId, note);
}
setError(true);
}, 4000);
} else {
clearTimeout(timer);
clearTimeout(timerError);
clearTimeout(timerClosing);
clearTimers();
setError(false);
editorState().overlay = false;
opacity.value = withTiming(0, { duration: 150 });
timerClosing = setTimeout(() => {
setLoading(null);
}, 150);
}
};
@@ -62,11 +58,18 @@ const EditorOverlay = () => {
}, [loading]);
useEffect(() => {
eSubscribeEvent('loadingNote', load);
eSubscribeEvent('loadingNote' + editorId, load);
return () => {
eUnSubscribeEvent('loadingNote', load);
clearTimers();
eUnSubscribeEvent('loadingNote' + editorId, load);
};
}, [loading]);
}, [loading, editorId]);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: opacity.value
};
});
return (
<Animated.View
@@ -98,6 +101,7 @@ const EditorOverlay = () => {
paddingVertical: 20
}}
>
{isDefaultEditor ? (
<View
style={{
flexDirection: 'row',
@@ -118,6 +122,7 @@ const EditorOverlay = () => {
}}
/>
</View>
) : null}
{loading?.title ? (
<Heading
@@ -129,7 +134,7 @@ const EditorOverlay = () => {
</Heading>
) : null}
{loading?.dateEdited ? (
{loading?.dateEdited && isDefaultEditor ? (
<Paragraph
textBreakStrategy="balanced"
style={{ textAlign: 'center' }}
@@ -155,7 +160,8 @@ const EditorOverlay = () => {
}}
onPress={() => {
setError(false);
eSendEvent('webviewreset');
editor.setLoading(true);
setTimeout(() => editor.setLoading(false), 10);
}}
title="Taking too long? Reload editor"
/>

View File

@@ -4,29 +4,22 @@ import { EdgeInsets } from 'react-native-safe-area-context';
import WebView from 'react-native-webview';
import { db } from '../../../utils/database';
import { sleep } from '../../../utils/time';
import { Note } from './types';
import { Note, Settings } from './types';
import { getResponse, randId, textInput } from './utils';
import { Attachment, AttachmentProgress } from 'notesnook-editor/dist/extensions/attachment/index';
import { ImageAttributes } from 'notesnook-editor/dist/extensions/image/index';
import { ToolbarGroupDefinition } from 'notesnook-editor/dist/toolbar/types';
import { NoteType } from '../../../utils/types';
import { sanitizeFilename } from '../../../utils/sanitizer';
type Action = { job: string; id: string };
export type Settings = {
readonly: boolean;
fullscreen: boolean;
deviceMode: string;
premium: boolean;
tools: ToolbarGroupDefinition[];
};
async function call(webview: RefObject<WebView | undefined>, action?: Action) {
if (!webview.current || !action) return;
setImmediate(() => webview.current?.injectJavaScript(action.job));
let response = await getResponse(action.id);
console.log('webview job: ', action.id, response ? response.value : response);
if (!response) {
console.warn('webview job failed', action.id, action.job);
console.warn('webview job failed', action.id);
}
return response ? response.value : response;
}
@@ -49,17 +42,17 @@ const fn = (fn: string) => {
class Commands {
ref = createRef<WebView | undefined>();
constructor(ref: MutableRefObject<WebView | undefined>) {
constructor(ref: RefObject<WebView>) {
this.ref = ref;
}
async doAsync<T>(job: string) {
if (!this.ref) return false;
if (!this.ref.current) return false;
return call(this.ref, fn(job)) as Promise<T>;
}
focus = async () => {
if (!this.ref) return;
if (!this.ref.current) return;
if (Platform.OS === 'android') {
this.ref.current?.requestFocus();
setTimeout(async () => {
@@ -74,16 +67,20 @@ class Commands {
}
};
blur = async () => await this.doAsync(`editor.commands.blur();editorTitle.current?.blur()`);
blur = async () =>
await this.doAsync(`
editor && editor.commands.blur();
typeof globalThis.editorTitle !== "undefined" && editorTitle.current && editorTitle.current.blur();
`);
clearContent = async () => {
await this.doAsync(
`
editor.commands.blur();
editorTitle.current?.blur();
typeof globalThis.editorTitle !== "undefined" && editorTitle.current && editorTitle.current?.blur();
editor?.commands.clearContent(false);
editorController.setTitle(null);
statusBar.current.set({date:"",saved:""});
typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",saved:""});
`
);
};
@@ -91,7 +88,9 @@ statusBar.current.set({date:"",saved:""});
setSessionId = async (id: string | null) => await this.doAsync(`globalThis.sessionId = "${id}"`);
setStatus = async (date: string | undefined, saved: string) =>
await this.doAsync(`statusBar.current.set({date:"${date}",saved:"${saved}"})`);
await this.doAsync(
`typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"${date}",saved:"${saved}"})`
);
setPlaceholder = async (placeholder: string) => {
await this.doAsync(`
@@ -119,7 +118,7 @@ statusBar.current.set({date:"",saved:""});
`);
};
setTags = async (note: Note | null | undefined) => {
setTags = async (note: NoteType | null | undefined) => {
if (!note) return;
let tags = note.tags
.map((t: any) =>

View File

@@ -1,5 +1,5 @@
import { useEditor } from './use-editor';
import { ToolbarGroupDefinition } from 'notesnook-editor/dist/toolbar/types';
export type useEditorType = ReturnType<typeof useEditor>;
export type EditorState = {
@@ -16,6 +16,25 @@ export type EditorState = {
saveCount: 0;
};
export type Settings = {
readonly: boolean;
fullscreen: boolean;
deviceMode: string;
premium: boolean;
tools: ToolbarGroupDefinition[];
noToolbar?: boolean;
noHeader?: boolean;
};
export type EditorProps = {
readonly: boolean;
noToolbar: boolean;
noHeader: boolean;
withController: boolean;
editorId?: string;
onLoad?: () => void;
};
export type EditorMessage = {
sessionId: string;
value: any;

View File

@@ -13,15 +13,17 @@ import { MMKV } from '../../../utils/database/mmkv';
import { eOnLoadNote } from '../../../utils/events';
import { tabBarRef } from '../../../utils/global-refs';
import { timeConverter } from '../../../utils/time';
import { NoteType } from '../../../utils/types';
import Commands from './commands';
import { AppState, Content, EditorState, Note, SavePayload } from './types';
import { defaultState, EditorEvents, isEditorLoaded, makeSessionId, post } from './utils';
export const useEditor = () => {
export const useEditor = (editorId = '', readonly?: boolean) => {
const [loading, setLoading] = useState(false);
const [sessionId, setSessionId] = useState<string>(makeSessionId());
const editorRef = useRef<WebView>();
const currentNote = useRef<Note | null>();
const sessionIdRef = useRef(sessionId);
const editorRef = useRef<WebView>(null);
const currentNote = useRef<NoteType | null>();
const currentContent = useRef<Content | null>();
const timers = useRef<{ [name: string]: NodeJS.Timeout }>({});
const commands = useMemo(() => new Commands(editorRef), []);
@@ -30,16 +32,21 @@ export const useEditor = () => {
const placeholderTip = useRef(TipManager.placeholderTip());
const tags = useTagStore(state => state.tags);
const insets = useSafeAreaInsets();
const isDefaultEditor = editorId === '';
const postMessage = useCallback(
async (type: string, data: any) => await post(editorRef, type, data),
[]
async (type: string, data: any) => await post(editorRef, sessionIdRef.current, type, data),
[sessionIdRef]
);
useEffect(() => {
commands.setInsets(insets);
commands.setInsets(isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 });
}, [insets]);
useEffect(() => {
sessionIdRef.current = sessionId;
}, [sessionId]);
useEffect(() => {
commands.setTags(currentNote.current);
}, [tags]);
@@ -69,11 +76,11 @@ export const useEditor = () => {
}, [sessionId, loading]);
const overlay = (show: boolean, data = { type: 'new' }) => {
eSendEvent('loadingNote', show ? currentNote.current || data : false);
eSendEvent('loadingNote' + editorId, show ? currentNote.current || data : false);
};
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef))) {
if (!(await isEditorLoaded(editorRef, sessionId))) {
console.log('reload editor');
overlay(true);
setLoading(true);
@@ -99,7 +106,7 @@ export const useEditor = () => {
await commands.clearContent();
console.log('reset state: ', resetState);
if (resetState) {
useEditorStore.getState().setCurrentlyEditingNote(null);
isDefaultEditor && useEditorStore.getState().setCurrentlyEditingNote(null);
placeholderTip.current = TipManager.placeholderTip();
await commands.setPlaceholder(placeholderTip.current);
}
@@ -115,9 +122,10 @@ export const useEditor = () => {
sessionHistoryId: currentSessionHistoryId
}: SavePayload) => {
console.log('saving note', id);
if (readonly) return;
try {
if (id && !db.notes?.note(id)) {
useEditorStore.getState().setCurrentlyEditingNote(null);
isDefaultEditor && useEditorStore.getState().setCurrentlyEditingNote(null);
await reset();
return;
}
@@ -144,11 +152,11 @@ export const useEditor = () => {
if (!locked) {
id = await db.notes?.add(noteData);
if (!note && id) {
currentNote.current = db.notes?.note(id).data as Note;
currentNote.current = db.notes?.note(id).data as NoteType;
state.current?.onNoteCreated && state.current.onNoteCreated(id);
}
if (useEditorStore.getState().currentEditingNote !== id) {
if (useEditorStore.getState().currentEditingNote !== id && isDefaultEditor) {
setTimeout(() => {
id && useEditorStore.getState().setCurrentlyEditingNote(id);
});
@@ -179,31 +187,25 @@ export const useEditor = () => {
[commands, reset]
);
const loadContent = useCallback(async (note: Note) => {
const loadContent = useCallback(async (note: NoteType) => {
currentNote.current = note;
if (note.locked) {
if (note.locked || note.content) {
currentContent.current = {
data: note.content.data,
type: note.content.type
data: note.content?.data,
type: note.content?.type || 'tiny'
};
} else {
let data = await db.content?.raw(note.contentId);
if (data) {
data = await db.content?.insertPlaceholders(data, 'placeholder.svg');
currentContent.current = {
data: data.data,
type: data.type
};
}
currentContent.current = await db.content?.raw(note.contentId);
}
}, []);
const loadNote = useCallback(
async (item: Note) => {
console.log('loading note', item.type);
async (item: NoteType) => {
console.log('loading note', item.type, eOnLoadNote + editorId);
state.current.currentlyEditing = true;
const editorState = useEditorStore.getState();
//@ts-ignore todo
if (item && item.type === 'new') {
currentNote.current && (await reset());
let nextSessionId = makeSessionId(item);
@@ -212,8 +214,9 @@ export const useEditor = () => {
await commands.setSessionId(nextSessionId);
await commands.focus();
} else {
//@ts-ignore todo
if (!item.forced && currentNote.current?.id === item.id) return;
editorState.setCurrentlyEditingNote(item.id);
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
overlay(true, item);
currentNote.current && (await reset(false));
await loadContent(item);
@@ -242,11 +245,11 @@ export const useEditor = () => {
};
useEffect(() => {
eSubscribeEvent(eOnLoadNote, loadNote);
eSubscribeEvent(eOnLoadNote + editorId, loadNote);
return () => {
eUnSubscribeEvent(eOnLoadNote, loadNote);
eUnSubscribeEvent(eOnLoadNote + editorId, loadNote);
};
}, []);
}, [editorId]);
const saveContent = useCallback(
({ title, content, type }: { title?: string; content?: string; type: string }) => {
@@ -284,13 +287,14 @@ export const useEditor = () => {
state.current.ready = true;
onReady();
postMessage(EditorEvents.theme, useThemeStore.getState().colors);
commands.setInsets(isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 });
if (currentNote.current) {
console.log('force reload note');
//@ts-ignore
loadNote({ ...currentNote.current, forced: true });
} else {
await commands.setPlaceholder(placeholderTip.current);
commands.setInsets(insets);
restoreEditorState();
isDefaultEditor && restoreEditorState();
}
}, [state, currentNote, loadNote]);
@@ -311,7 +315,9 @@ export const useEditor = () => {
tabBarRef.current?.goToPage(1);
}
setTimeout(() => {
console.log('restoring app state here');
if (appState.note) {
//@ts-ignore
loadNote(appState.note);
}
}, 1);
@@ -339,6 +345,7 @@ export const useEditor = () => {
setSessionId,
note: currentNote,
onReady,
saveContent
saveContent,
editorId: editorId
};
};

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef } from 'react';
import { BackHandler, InteractionManager, NativeEventSubscription } from 'react-native';
import { WebViewMessageEvent } from 'react-native-webview';
import { Properties } from '../../../components/properties';
import { DDS } from '../../../services/device-detection';
import {
@@ -29,8 +30,8 @@ import { tabBarRef } from '../../../utils/global-refs';
import { NoteType } from '../../../utils/types';
import { useDragState } from '../../settings/editor/state';
import picker from './picker';
import { EditorMessage, useEditorType } from './types';
import { editorController, EditorEvents, editorState } from './utils';
import { EditorMessage, EditorProps, useEditorType } from './types';
import { EditorEvents, editorState } from './utils';
export const EventTypes = {
selection: 'editor-event:selection',
@@ -49,7 +50,7 @@ export const EventTypes = {
properties: 'editor-event:properties'
};
const publishNote = async () => {
const publishNote = async (editor: useEditorType) => {
const user = useUserStore.getState().user;
if (!user) {
ToastEvent.show({
@@ -72,7 +73,7 @@ const publishNote = async () => {
});
return;
}
const currentNote = editorController.current?.note?.current;
const currentNote = editor?.note?.current;
if (currentNote?.id) {
let note = db.notes?.note(currentNote.id)?.data as NoteType;
if (note?.locked) {
@@ -90,8 +91,8 @@ const publishNote = async () => {
}
};
const showActionsheet = async () => {
const currentNote = editorController.current?.note?.current;
const showActionsheet = async (editor: useEditorType) => {
const currentNote = editor?.note?.current;
if (currentNote?.id) {
let note = db.notes?.note(currentNote.id)?.data as NoteType;
if (!note) {
@@ -112,11 +113,10 @@ const showActionsheet = async () => {
}
};
export const useEditorEvents = (editor: useEditorType) => {
export const useEditorEvents = (editor: useEditorType, editorProps: Partial<EditorProps>) => {
const deviceMode = useSettingStore(state => state.deviceMode);
const fullscreen = useSettingStore(state => state.fullscreen);
const handleBack = useRef<NativeEventSubscription>();
const currentEditingNote = useEditorStore(state => state.currentEditingNote);
const readonly = useEditorStore(state => state.readonly);
const isPremium = useUserStore(state => state.premium);
const tools = useDragState(state => state.data);
@@ -129,17 +129,10 @@ export const useEditorEvents = (editor: useEditorType) => {
fullscreen: fullscreen,
premium: isPremium,
readonly: readonly,
tools: tools
tools: tools,
...editorProps
});
}, [
currentEditingNote,
fullscreen,
isPremium,
readonly,
editor.sessionId,
editor.loading,
tools
]);
}, [fullscreen, isPremium, readonly, editor.sessionId, editor.loading, tools]);
const onBackPress = useCallback(async () => {
const editorHandledBack = await editor.commands.handleBack();
@@ -165,7 +158,7 @@ export const useEditorEvents = (editor: useEditorType) => {
});
setImmediate(() => useEditorStore.getState().setCurrentlyEditingNote(null));
editorState().currentlyEditing = false;
editorController.current?.reset();
editor.reset();
}, 1);
}, []);
@@ -217,8 +210,8 @@ export const useEditorEvents = (editor: useEditorType) => {
}, [fullscreen]);
useEffect(() => {
eSubscribeEvent(eOnLoadNote, onLoadNote);
eSubscribeEvent(eClearEditor, onCallClear);
eSubscribeEvent(eOnLoadNote + editor.editorId, onLoadNote);
eSubscribeEvent(eClearEditor + editor.editorId, onCallClear);
return () => {
eUnSubscribeEvent(eClearEditor, onCallClear);
eUnSubscribeEvent(eOnLoadNote, onLoadNote);
@@ -226,8 +219,7 @@ export const useEditorEvents = (editor: useEditorType) => {
}, []);
const onMessage = useCallback(
event => {
event;
(event: WebViewMessageEvent) => {
const data = event.nativeEvent.data;
let editorMessage = JSON.parse(data) as EditorMessage;
@@ -303,10 +295,10 @@ export const useEditorEvents = (editor: useEditorType) => {
eSendEvent(eOpenPremiumDialog);
break;
case EventTypes.monograph:
publishNote();
publishNote(editor);
break;
case EventTypes.properties:
showActionsheet();
showActionsheet(editor);
break;
case EventTypes.back:
onBackPress();

View File

@@ -1,7 +1,8 @@
import { createRef, MutableRefObject } from 'react';
import { createRef, RefObject } from 'react';
import { TextInput } from 'react-native';
import WebView from 'react-native-webview';
import { eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager';
import { NoteType } from '../../../utils/types';
import { EditorState, useEditorType } from './types';
export const textInput = createRef<TextInput>();
export const editorController = createRef<useEditorType>();
@@ -32,16 +33,15 @@ export function randId(prefix: string) {
.replace('0.', prefix || '');
}
export function makeSessionId(item?: any) {
export function makeSessionId(item?: NoteType) {
return item?.id ? item.id + randId('_session_') : randId('session_');
}
export async function isEditorLoaded(ref: MutableRefObject<WebView | undefined>) {
return await post(ref, EditorEvents.status);
export async function isEditorLoaded(ref: RefObject<WebView>, sessionId: string) {
return await post(ref, sessionId, EditorEvents.status);
}
export async function post(ref: MutableRefObject<WebView | undefined>, type: string, value = null) {
let sessionId = editorController.current?.sessionId;
export async function post(ref: RefObject<WebView>, sessionId: string, type: string, value = null) {
if (!sessionId) {
console.warn('post called without sessionId of type:', type);
return;

View File

@@ -76,8 +76,7 @@ export const EditorWrapper = ({ width }) => {
blurOnSubmit={false}
/>
<ProgressBar />
<Editor key="editor" />
<EditorOverlay key="overlay" />
<Editor key="editor" withController={true} />
</KeyboardAvoidingView>
</SafeAreaView>
)}

View File

@@ -496,7 +496,7 @@ export const useActions = ({ close = () => {}, item }) => {
close();
await sleep(300);
presentSheet({
component: ref => <NoteHistory ref={ref} note={item} />
component: ref => <NoteHistory fwdRef={ref} note={item} />
});
}

View File

@@ -58,14 +58,6 @@ export const useAppEvents = () => {
isReconnecting: false
});
const onMediaDownloaded = ({ hash, groupId, src }) => {
if (groupId?.startsWith('monograph')) return;
editorController.current?.commands.updateImage({
hash: hash,
src: src
});
};
const onLoadingAttachmentProgress = data => {
useAttachmentStore.getState().setLoading(data.total === data.current ? null : data);
};
@@ -112,7 +104,6 @@ export const useAppEvents = () => {
EV.subscribe(EVENTS.userSessionExpired, onSessionExpired);
EV.subscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
EV.subscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
eSubscribeEvent('userLoggedIn', onUserUpdated);
@@ -123,7 +114,6 @@ export const useAppEvents = () => {
EV.unsubscribe(EVENTS.userSessionExpired, onSessionExpired);
EV.unsubscribe(EVENTS.userLoggedOut, onLogout);
EV.unsubscribe(EVENTS.userEmailConfirmed, onEmailVerified);
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
EV.unsubscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
EV.unsubscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);

View File

@@ -64,6 +64,10 @@ export interface NoteType extends Entity<'note'> {
contentId?: string;
headline?: string;
color?: string;
content?: {
data: string;
type: string;
};
}
export interface NotebookType extends Entity<'notebook'> {