mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 15:09:33 +01:00
upgrade to react-native 0.69
This commit is contained in:
@@ -72,4 +72,4 @@ untyped-import
|
|||||||
untyped-type-import
|
untyped-type-import
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
^0.170.0
|
^0.176.3
|
||||||
|
|||||||
2
apps/mobile/.gitignore
vendored
2
apps/mobile/.gitignore
vendored
@@ -23,8 +23,10 @@ DerivedData
|
|||||||
*.ipa
|
*.ipa
|
||||||
*.xcuserstate
|
*.xcuserstate
|
||||||
*.hprof
|
*.hprof
|
||||||
|
ios/.xcode.env.local
|
||||||
# Android/IntelliJ
|
# Android/IntelliJ
|
||||||
#
|
#
|
||||||
|
rn-build-deps/
|
||||||
build/
|
build/
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.7.4
|
2.7.5
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
# 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'
|
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<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.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ public class MainActivity extends ReactActivity {
|
|||||||
return reactRootView;
|
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
|
@Override
|
||||||
protected void loadApp(String appKey) {
|
protected void loadApp(String appKey) {
|
||||||
RNBootSplash.init(getPlainActivity());
|
RNBootSplash.init(getPlainActivity());
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package com.streetwriters.notesnook;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.RNFetchBlob.RNFetchBlob;
|
|
||||||
import com.dooboolab.RNIap.RNIapModule;
|
import com.dooboolab.RNIap.RNIapModule;
|
||||||
import com.facebook.react.PackageList;
|
import com.facebook.react.PackageList;
|
||||||
import com.facebook.react.ReactApplication;
|
import com.facebook.react.ReactApplication;
|
||||||
@@ -11,21 +9,18 @@ import com.facebook.react.ReactInstanceManager;
|
|||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.soloader.SoLoader;
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.multidex.MultiDexApplication;
|
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.TurboReactPackage;
|
||||||
import com.facebook.react.module.model.ReactModuleInfo;
|
import com.facebook.react.module.model.ReactModuleInfo;
|
||||||
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
||||||
import com.facebook.react.bridge.NativeModule;
|
import com.facebook.react.bridge.NativeModule;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.learnium.RNDeviceInfo.RNDeviceModule;
|
|
||||||
import com.oblador.keychain.KeychainPackage;
|
|
||||||
import com.onibenjo.htmltopdf.RNHTMLtoPDFModule;
|
import com.onibenjo.htmltopdf.RNHTMLtoPDFModule;
|
||||||
import com.reactnativedocumentpicker.DocumentPickerModule;
|
import com.reactnativedocumentpicker.DocumentPickerModule;
|
||||||
import com.vinzscam.reactnativefileviewer.RNFileViewerModule;
|
import com.vinzscam.reactnativefileviewer.RNFileViewerModule;
|
||||||
@@ -33,8 +28,7 @@ import com.facebook.react.config.ReactFeatureFlags;
|
|||||||
import com.streetwriters.notesnook.newarchitecture.MainApplicationReactNativeHost;
|
import com.streetwriters.notesnook.newarchitecture.MainApplicationReactNativeHost;
|
||||||
import cl.json.RNShareModule;
|
import cl.json.RNShareModule;
|
||||||
import px.tooltips.RNTooltipsModule;
|
import px.tooltips.RNTooltipsModule;
|
||||||
import com.facebook.react.bridge.JSIModulePackage; // <- add
|
//import io.csie.kudo.reactnative.v8.executor.V8ExecutorFactory;
|
||||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage; // <- add
|
|
||||||
|
|
||||||
public class MainApplication extends MultiDexApplication implements ReactApplication {
|
public class MainApplication extends MultiDexApplication implements ReactApplication {
|
||||||
|
|
||||||
@@ -48,6 +42,15 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
|
|||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
|
||||||
|
// return new V8ExecutorFactory(
|
||||||
|
// getApplicationContext(),
|
||||||
|
// getPackageName(),
|
||||||
|
// AndroidInfoHelpers.getFriendlyDeviceName(),
|
||||||
|
// getUseDeveloperSupport());
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||||
@@ -104,10 +107,6 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
|
|||||||
protected String getJSMainModuleName() {
|
protected String getJSMainModuleName() {
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected JSIModulePackage getJSIModulePackage() {
|
|
||||||
return new ReanimatedJSIModulePackage(); // <- add
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public ReactModuleInfo getModuleInfo(String reactClass, String className) {
|
public ReactModuleInfo getModuleInfo(String reactClass, String className) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|||||||
import com.facebook.react.bridge.UIManager;
|
import com.facebook.react.bridge.UIManager;
|
||||||
import com.facebook.react.fabric.ComponentFactory;
|
import com.facebook.react.fabric.ComponentFactory;
|
||||||
import com.facebook.react.fabric.CoreComponentsRegistry;
|
import com.facebook.react.fabric.CoreComponentsRegistry;
|
||||||
import com.facebook.react.fabric.EmptyReactNativeConfig;
|
|
||||||
import com.facebook.react.fabric.FabricJSIModuleProvider;
|
import com.facebook.react.fabric.FabricJSIModuleProvider;
|
||||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||||
import com.streetwriters.notesnook.BuildConfig;
|
import com.streetwriters.notesnook.BuildConfig;
|
||||||
@@ -24,6 +23,7 @@ import com.streetwriters.notesnook.newarchitecture.components.MainComponentsRegi
|
|||||||
import com.streetwriters.notesnook.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
|
import com.streetwriters.notesnook.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import com.facebook.react.fabric.ReactNativeConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
|
* 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(
|
return new FabricJSIModuleProvider(
|
||||||
reactApplicationContext,
|
reactApplicationContext,
|
||||||
componentFactory,
|
componentFactory,
|
||||||
new EmptyReactNativeConfig(),
|
ReactNativeConfig.DEFAULT_CONFIG,
|
||||||
viewManagerRegistry);
|
viewManagerRegistry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_PATH := $(THIS_DIR)
|
LOCAL_PATH := $(THIS_DIR)
|
||||||
|
|
||||||
# You can customize the name of your application .so file here.
|
# 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_C_INCLUDES := $(LOCAL_PATH)
|
||||||
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
|
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
|
||||||
@@ -28,8 +28,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
|||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
libfabricjni \
|
libfabricjni \
|
||||||
libfbjni \
|
libfbjni \
|
||||||
libfolly_futures \
|
libfolly_runtime \
|
||||||
libfolly_json \
|
|
||||||
libglog \
|
libglog \
|
||||||
libjsi \
|
libjsi \
|
||||||
libreact_codegen_rncore \
|
libreact_codegen_rncore \
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -2,15 +2,9 @@
|
|||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||||
<!-- Allow drawing under the system bars background -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
<item name="android:windowDisablePreview">true</item>
|
||||||
<item name="android:fitsSystemWindows">false</item>
|
<item name="android:editTextBackground">@drawable/edit_text</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>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Share.Window" parent="Theme.AppCompat">
|
<style name="Share.Window" parent="Theme.AppCompat">
|
||||||
|
|||||||
@@ -6,5 +6,7 @@ include ':app'
|
|||||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||||
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
||||||
include(":ReactAndroid")
|
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')
|
||||||
}
|
}
|
||||||
|
|||||||
10
apps/mobile/ios/.xcode.env
Normal file
10
apps/mobile/ios/.xcode.env
Normal 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)
|
||||||
@@ -1077,7 +1077,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
@@ -1150,7 +1150,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
@@ -1179,7 +1179,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
@@ -1251,7 +1251,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -1409,7 +1409,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
@@ -1420,7 +1420,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||||
@@ -1450,7 +1450,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
@@ -1461,7 +1461,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -1490,7 +1490,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
@@ -1563,7 +1563,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||||
@@ -1593,7 +1593,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 1809;
|
CURRENT_PROJECT_VERSION = 1810;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
@@ -1666,7 +1666,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.9;
|
MARKETING_VERSION = 1.8.10;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||||
|
|
||||||
#import <react/config/ReactNativeConfig.h>
|
#import <react/config/ReactNativeConfig.h>
|
||||||
|
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||||
|
|
||||||
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
||||||
RCTTurboModuleManager *_turboModuleManager;
|
RCTTurboModuleManager *_turboModuleManager;
|
||||||
@@ -70,8 +71,9 @@ RCTBridge *bridge;
|
|||||||
shareViewController.view = shareView;
|
shareViewController.view = shareView;
|
||||||
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:shareView];
|
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:shareView];
|
||||||
} else {
|
} else {
|
||||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
|
NSDictionary *initProps = [self prepareInitialProps];
|
||||||
moduleName:@"Notesnook" initialProperties:nil];
|
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"Notesnook" , initProps);
|
||||||
|
|
||||||
if (@available(iOS 13.0, *)) {
|
if (@available(iOS 13.0, *)) {
|
||||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||||
} else {
|
} else {
|
||||||
@@ -87,6 +89,25 @@ RCTBridge *bridge;
|
|||||||
return YES;
|
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
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
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
|
install! 'cocoapods', :deterministic_uuids => false
|
||||||
|
|
||||||
pod 'Base64'
|
pod 'Base64'
|
||||||
|
|||||||
0
apps/mobile/new.js
Normal file
0
apps/mobile/new.js
Normal 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
|
|
||||||
@@ -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,
|
||||||
421
apps/mobile/patches/react-native-reanimated+2.8.0.patch
Normal file
421
apps/mobile/patches/react-native-reanimated+2.8.0.patch
Normal 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
103
apps/mobile/patches/rn-fetch-blob+0.12.0.patch
Normal file
103
apps/mobile/patches/rn-fetch-blob+0.12.0.patch
Normal 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')
|
||||||
|
-}
|
||||||
@@ -87,21 +87,6 @@ const Launcher = React.memo(
|
|||||||
const hideSplashScreen = async () => {
|
const hideSplashScreen = async () => {
|
||||||
if (requireIntro.value) await sleep(500);
|
if (requireIntro.value) await sleep(500);
|
||||||
await RNBootSplash.hide({ fade: true });
|
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(() => {
|
useEffect(() => {
|
||||||
@@ -161,6 +146,7 @@ const Launcher = React.memo(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const checkAppUpdateAvailable = async () => {
|
const checkAppUpdateAvailable = async () => {
|
||||||
|
return;
|
||||||
try {
|
try {
|
||||||
const version = await checkVersion();
|
const version = await checkVersion();
|
||||||
if (!version.needsUpdate) return false;
|
if (!version.needsUpdate) return false;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const openNote = async (item, isTrash, setSelectedItem) => {
|
|||||||
history.selectedItemsList = [];
|
history.selectedItemsList = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_note.conflicted) {
|
if (!_note.conflicted) {
|
||||||
eSendEvent(eShowMergeDialog, _note);
|
eSendEvent(eShowMergeDialog, _note);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
import KeepAwake from '@sayem314/react-native-keep-awake';
|
import KeepAwake from '@sayem314/react-native-keep-awake';
|
||||||
import { EV, EVENTS } from 'notes-core/common';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import React, { createRef, useEffect, useState } from 'react';
|
import { Modal, SafeAreaView, Text, View } from 'react-native';
|
||||||
import { Modal, Platform, SafeAreaView, Text, View } from 'react-native';
|
|
||||||
import Animated from 'react-native-reanimated';
|
import Animated from 'react-native-reanimated';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
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 { editorController } from '../../screens/editor/tiptap/utils';
|
||||||
import { DDS } from '../../services/device-detection';
|
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 Navigation from '../../services/navigation';
|
||||||
import Sync from '../../services/sync';
|
import Sync from '../../services/sync';
|
||||||
import { useThemeStore } from '../../stores/use-theme-store';
|
import { useThemeStore } from '../../stores/use-theme-store';
|
||||||
import { dHeight } from '../../utils';
|
import { dHeight } from '../../utils';
|
||||||
import { db } from '../../utils/database';
|
import { db } from '../../utils/database';
|
||||||
import { eApplyChanges, eShowMergeDialog } from '../../utils/events';
|
import { eOnLoadNote, eShowMergeDialog } from '../../utils/events';
|
||||||
import { openLinkInBrowser } from '../../utils/functions';
|
import { SIZE } from '../../utils/size';
|
||||||
import { normalize, SIZE } from '../../utils/size';
|
|
||||||
import { timeConverter } from '../../utils/time';
|
import { timeConverter } from '../../utils/time';
|
||||||
import BaseDialog from '../dialog/base-dialog';
|
import BaseDialog from '../dialog/base-dialog';
|
||||||
import DialogButtons from '../dialog/dialog-buttons';
|
import DialogButtons from '../dialog/dialog-buttons';
|
||||||
@@ -28,128 +26,42 @@ import Paragraph from '../ui/typography/paragraph';
|
|||||||
|
|
||||||
const sourceUri = '';
|
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 MergeConflicts = () => {
|
||||||
const colors = useThemeStore(state => state.colors);
|
const colors = useThemeStore(state => state.colors);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [primary, setPrimary] = useState(true);
|
const [keep, setKeep] = useState(null);
|
||||||
const [secondary, setSecondary] = useState(true);
|
const [copy, setCopy] = useState(null);
|
||||||
const [keepContentFrom, setKeepContentFrom] = useState(null);
|
|
||||||
const [copyToSave, setCopyToSave] = useState(null);
|
|
||||||
const [disardedContent, setDiscardedContent] = useState(null);
|
|
||||||
const [dialogVisible, setDialogVisible] = useState(false);
|
const [dialogVisible, setDialogVisible] = useState(false);
|
||||||
const [loadingAttachments, setLoadingAttachments] = useState(false);
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
const content = useRef({});
|
||||||
const onPrimaryWebViewLoad = async () => {
|
const isKeepingConflicted = !keep?.conflicted;
|
||||||
let content = await db.content.insertPlaceholders(primaryData, 'placeholder.svg');
|
const isKeeping = !!keep;
|
||||||
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 applyChanges = async () => {
|
const applyChanges = async () => {
|
||||||
let content = keepContentFrom === 'primary' ? primaryData : secondaryData;
|
let _content = keep;
|
||||||
let keepCopy =
|
let note = db.notes.note(_content.noteId).data;
|
||||||
copyToSave === 'primary' ? primaryData : copyToSave === 'secondary' ? secondaryData : null;
|
|
||||||
|
|
||||||
await db.notes.add({
|
await db.notes.add({
|
||||||
id: note.id,
|
id: note.id,
|
||||||
conflicted: false,
|
conflicted: false,
|
||||||
dateEdited: content.dateEdited
|
dateEdited: _content.dateEdited
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.content.add({
|
await db.content.add({
|
||||||
id: note.contentId,
|
id: note.contentId,
|
||||||
data: content.data,
|
data: _content.data,
|
||||||
type: content.type,
|
type: _content.type,
|
||||||
dateResolved: secondaryData.dateModified,
|
dateResolved: content.current.conflicted.dateModified,
|
||||||
sessionId: Date.now(),
|
sessionId: Date.now(),
|
||||||
conflicted: false
|
conflicted: false
|
||||||
});
|
});
|
||||||
|
|
||||||
if (keepCopy) {
|
if (copy) {
|
||||||
await db.notes.add({
|
await db.notes.add({
|
||||||
|
title: note.title + ' (Copy)',
|
||||||
content: {
|
content: {
|
||||||
data: keepCopy.data,
|
data: copy.data,
|
||||||
type: keepCopy.type
|
type: copy.type
|
||||||
},
|
}
|
||||||
id: null
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Navigation.queueRoutesForUpdate(
|
Navigation.queueRoutesForUpdate(
|
||||||
@@ -167,89 +79,126 @@ const MergeConflicts = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const show = async item => {
|
const show = async item => {
|
||||||
note = item;
|
let noteContent = await db.content.raw(item.contentId);
|
||||||
let content = await db.content.raw(note.contentId);
|
switch (noteContent.type) {
|
||||||
switch (content.type) {
|
|
||||||
case 'tiny':
|
case 'tiny':
|
||||||
primaryData = content;
|
content.current = { ...noteContent };
|
||||||
secondaryData = content.conflicted;
|
if (!noteContent.conflicted) {
|
||||||
|
content.current.conflicted = { ...noteContent };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent(eApplyChanges, applyChanges);
|
|
||||||
eSubscribeEvent(eShowMergeDialog, show);
|
eSubscribeEvent(eShowMergeDialog, show);
|
||||||
return () => {
|
return () => {
|
||||||
eUnSubscribeEvent(eApplyChanges, applyChanges);
|
|
||||||
eUnSubscribeEvent(eShowMergeDialog, show);
|
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 = () => {
|
const close = () => {
|
||||||
db.fs.cancel(primaryData?.noteId);
|
|
||||||
|
|
||||||
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
setPrimary(true);
|
setCopy(null);
|
||||||
setSecondary(true);
|
setKeep(null);
|
||||||
setCopyToSave(null);
|
|
||||||
setDiscardedContent(null);
|
|
||||||
setKeepContentFrom(null);
|
|
||||||
setDialogVisible(false);
|
setDialogVisible(false);
|
||||||
primaryData = null;
|
|
||||||
secondaryData = null;
|
|
||||||
note = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLoadImages = async () => {
|
const ConfigBar = ({ isDiscarded, keeping, back, isCurrent, contentToKeep }) => {
|
||||||
try {
|
return (
|
||||||
setLoadingAttachments(true);
|
<View
|
||||||
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
|
style={{
|
||||||
await db.content.downloadMedia(primaryData.data.noteId, primaryData);
|
width: '100%',
|
||||||
await db.content.downloadMedia(primaryData.data.noteId, secondaryData);
|
height: 50,
|
||||||
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
|
flexDirection: 'row',
|
||||||
setLoadingAttachments(false);
|
justifyContent: 'space-between',
|
||||||
} catch (e) {
|
alignItems: 'center',
|
||||||
setLoadingAttachments(false);
|
paddingHorizontal: 12,
|
||||||
eUnSubscribeEvent(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
|
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 : (
|
return !visible ? null : (
|
||||||
@@ -302,103 +251,13 @@ const MergeConflicts = () => {
|
|||||||
backgroundColor: DDS.isLargeTablet() ? 'rgba(0,0,0,0.3)' : null
|
backgroundColor: DDS.isLargeTablet() ? 'rgba(0,0,0,0.3)' : null
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<ConfigBar
|
||||||
style={{
|
back={true}
|
||||||
width: '100%',
|
isCurrent={true}
|
||||||
height: 50,
|
isDiscarded={isKeeping && isKeepingConflicted}
|
||||||
flexDirection: 'row',
|
keeping={isKeeping}
|
||||||
justifyContent: 'space-between',
|
contentToKeep={content.current}
|
||||||
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}
|
|
||||||
/>
|
/>
|
||||||
) : 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
|
<Animated.View
|
||||||
style={{
|
style={{
|
||||||
@@ -408,129 +267,26 @@ const MergeConflicts = () => {
|
|||||||
borderBottomColor: colors.nav
|
borderBottomColor: colors.nav
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WebView
|
<Editor
|
||||||
onLoad={onPrimaryWebViewLoad}
|
noHeader
|
||||||
ref={primaryWebView}
|
noToolbar
|
||||||
style={{
|
readonly
|
||||||
width: '100%',
|
editorId=":conflictPrimary"
|
||||||
height: '100%',
|
onLoad={() => {
|
||||||
backgroundColor: 'transparent'
|
const note = db.notes.note(content.current?.noteId)?.data;
|
||||||
}}
|
if (!note) return;
|
||||||
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
|
eSendEvent(eOnLoadNote + ':conflictPrimary', { ...note, content: content.current });
|
||||||
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'
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|
||||||
<View
|
<ConfigBar
|
||||||
style={{
|
back={false}
|
||||||
width: '100%',
|
isCurrent={false}
|
||||||
height: 50,
|
isDiscarded={isKeeping && !isKeepingConflicted}
|
||||||
flexDirection: 'row',
|
keeping={isKeeping}
|
||||||
justifyContent: 'space-between',
|
contentToKeep={content.current.conflicted}
|
||||||
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"
|
|
||||||
/>
|
/>
|
||||||
) : 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
|
<Animated.View
|
||||||
style={{
|
style={{
|
||||||
@@ -539,29 +295,18 @@ const MergeConflicts = () => {
|
|||||||
borderRadius: 10
|
borderRadius: 10
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WebView
|
<Editor
|
||||||
onLoad={onSecondaryWebViewLoad}
|
noHeader
|
||||||
ref={secondaryWebView}
|
noToolbar
|
||||||
style={{
|
readonly
|
||||||
width: '100%',
|
editorId=":conflictSecondary"
|
||||||
height: '100%',
|
onLoad={() => {
|
||||||
backgroundColor: 'transparent'
|
const note = db.notes.note(content.current?.noteId)?.data;
|
||||||
}}
|
if (!note) return;
|
||||||
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
|
eSendEvent(eOnLoadNote + ':conflictSecondary', {
|
||||||
cacheMode="LOAD_DEFAULT"
|
...note,
|
||||||
domStorageEnabled={true}
|
content: content.current.conflicted
|
||||||
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'
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Seperator from '../ui/seperator';
|
|||||||
import Paragraph from '../ui/typography/paragraph';
|
import Paragraph from '../ui/typography/paragraph';
|
||||||
import NotePreview from './preview';
|
import NotePreview from './preview';
|
||||||
|
|
||||||
export default function NoteHistory({ note, ref }) {
|
export default function NoteHistory({ note, fwdRef }) {
|
||||||
const [history, setHistory] = useState([]);
|
const [history, setHistory] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const colors = useThemeStore(state => state.colors);
|
const colors = useThemeStore(state => state.colors);
|
||||||
@@ -92,7 +92,7 @@ export default function NoteHistory({ note, ref }) {
|
|||||||
|
|
||||||
<FlatList
|
<FlatList
|
||||||
onMomentumScrollEnd={() => {
|
onMomentumScrollEnd={() => {
|
||||||
ref?.current?.handleChildScrollEnd();
|
fwdRef?.current?.handleChildScrollEnd();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12
|
paddingHorizontal: 12
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useRef } from 'react';
|
import React from 'react';
|
||||||
import { Platform, View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import WebView from 'react-native-webview';
|
import Editor from '../../screens/editor';
|
||||||
|
import EditorOverlay from '../../screens/editor/loading';
|
||||||
import { editorController } from '../../screens/editor/tiptap/utils';
|
import { editorController } from '../../screens/editor/tiptap/utils';
|
||||||
import { eSendEvent, ToastEvent } from '../../services/event-manager';
|
import { eSendEvent, ToastEvent } from '../../services/event-manager';
|
||||||
import Navigation from '../../services/navigation';
|
import Navigation from '../../services/navigation';
|
||||||
@@ -8,66 +9,13 @@ import { useEditorStore } from '../../stores/use-editor-store';
|
|||||||
import { useThemeStore } from '../../stores/use-theme-store';
|
import { useThemeStore } from '../../stores/use-theme-store';
|
||||||
import { db } from '../../utils/database';
|
import { db } from '../../utils/database';
|
||||||
import { eCloseProgressDialog, eOnLoadNote } from '../../utils/events';
|
import { eCloseProgressDialog, eOnLoadNote } from '../../utils/events';
|
||||||
import { openLinkInBrowser } from '../../utils/functions';
|
|
||||||
import { normalize } from '../../utils/size';
|
|
||||||
import DialogHeader from '../dialog/dialog-header';
|
import DialogHeader from '../dialog/dialog-header';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import Paragraph from '../ui/typography/paragraph';
|
import Paragraph from '../ui/typography/paragraph';
|
||||||
|
|
||||||
const sourceUri = '';
|
|
||||||
|
|
||||||
export default function NotePreview({ session, content }) {
|
export default function NotePreview({ session, content }) {
|
||||||
const colors = useThemeStore(state => state.colors);
|
const colors = useThemeStore(state => state.colors);
|
||||||
const webviewRef = useRef();
|
const editorId = ':noteHistory';
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function restore() {
|
async function restore() {
|
||||||
await db.noteHistory.restore(session.id);
|
await db.noteHistory.restore(session.id);
|
||||||
@@ -101,35 +49,19 @@ export default function NotePreview({ session, content }) {
|
|||||||
>
|
>
|
||||||
<DialogHeader padding={12} title={session.session} />
|
<DialogHeader padding={12} title={session.session} />
|
||||||
{!session.locked ? (
|
{!session.locked ? (
|
||||||
<WebView
|
<>
|
||||||
ref={webviewRef}
|
<EditorOverlay editorId={editorId} />
|
||||||
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
|
<Editor
|
||||||
onLoad={onLoad}
|
noHeader
|
||||||
style={{
|
noToolbar
|
||||||
width: '100%',
|
readonly
|
||||||
height: '100%',
|
editorId={editorId}
|
||||||
backgroundColor: 'transparent'
|
onLoad={() => {
|
||||||
}}
|
const note = db.notes.note(session.noteId)?.data;
|
||||||
onError={e => {
|
eSendEvent(eOnLoadNote + editorId, { ...note, content });
|
||||||
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'
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export interface TabsRef {
|
|||||||
setScrollEnabled: () => true;
|
setScrollEnabled: () => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NewTabs = forwardRef<TabsRef, TabProps>(
|
export const FluidTabs = forwardRef<TabsRef, TabProps>(
|
||||||
(
|
(
|
||||||
{ children, dimensions, widths, onChangeTab, onScroll, enabled, onDrawerStateChange }: TabProps,
|
{ children, dimensions, widths, onChangeTab, onScroll, enabled, onDrawerStateChange }: TabProps,
|
||||||
ref
|
ref
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { activateKeepAwake, deactivateKeepAwake } from '@sayem314/react-native-keep-awake';
|
import { activateKeepAwake, deactivateKeepAwake } from '@sayem314/react-native-keep-awake';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { Platform, View } from 'react-native';
|
import { Platform, View, StatusBar } from 'react-native';
|
||||||
import { NavigationBar, StatusBar } from 'react-native-bars';
|
|
||||||
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { notesnook } from '../../e2e/test.ids';
|
import { notesnook } from '../../e2e/test.ids';
|
||||||
import { SideMenu } from '../components/side-menu';
|
import { SideMenu } from '../components/side-menu';
|
||||||
import { NewTabs } from '../components/tabs';
|
import { FluidTabs } from '../components/tabs';
|
||||||
import { editorState } from '../screens/editor/tiptap/utils';
|
import { editorState } from '../screens/editor/tiptap/utils';
|
||||||
import { EditorWrapper } from '../screens/editor/wrapper';
|
import { EditorWrapper } from '../screens/editor/wrapper';
|
||||||
import { DDS } from '../services/device-detection';
|
import { DDS } from '../services/device-detection';
|
||||||
@@ -255,18 +254,22 @@ export const TabsHolder = React.memo(
|
|||||||
paddingBottom: Platform.OS === 'android' ? insets?.bottom : 0
|
paddingBottom: Platform.OS === 'android' ? insets?.bottom : 0
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StatusBar animated={true} barStyle={colors.night ? 'light-content' : 'dark-content'} />
|
<StatusBar
|
||||||
<NavigationBar barStyle={colors.night ? 'light-content' : 'dark-content'} />
|
animated={true}
|
||||||
|
barStyle={colors.night ? 'light-content' : 'dark-content'}
|
||||||
|
translucent={true}
|
||||||
|
backgroundColor="transparent"
|
||||||
|
/>
|
||||||
|
|
||||||
{deviceMode && widths[deviceMode] ? (
|
{deviceMode && widths[deviceMode] ? (
|
||||||
<NewTabs
|
<FluidTabs
|
||||||
ref={tabBarRef}
|
ref={tabBarRef}
|
||||||
dimensions={dimensions}
|
dimensions={dimensions}
|
||||||
widths={widths[deviceMode]}
|
widths={widths[deviceMode]}
|
||||||
enabled={deviceMode !== 'tablet'}
|
enabled={deviceMode !== 'tablet'}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
onChangeTab={onChangeTab}
|
onChangeTab={onChangeTab}
|
||||||
onDrawerStateChange={state => {}}
|
onDrawerStateChange={state => true}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
key="1"
|
key="1"
|
||||||
@@ -309,7 +312,7 @@ export const TabsHolder = React.memo(
|
|||||||
<NavigationStack />
|
<NavigationStack />
|
||||||
</View>
|
</View>
|
||||||
<EditorWrapper key="3" width={widths} dimensions={dimensions} />
|
<EditorWrapper key="3" width={widths} dimensions={dimensions} />
|
||||||
</NewTabs>
|
</FluidTabs>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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"
|
|
||||||
118
apps/mobile/src/screens/editor/index.tsx
Executable file
118
apps/mobile/src/screens/editor/index.tsx
Executable 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"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { View } from 'react-native';
|
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 { Button } from '../../components/ui/button';
|
||||||
import Heading from '../../components/ui/typography/heading';
|
import Heading from '../../components/ui/typography/heading';
|
||||||
import Paragraph from '../../components/ui/typography/paragraph';
|
import Paragraph from '../../components/ui/typography/paragraph';
|
||||||
@@ -11,47 +11,43 @@ import { SIZE } from '../../utils/size';
|
|||||||
import { timeConverter } from '../../utils/time';
|
import { timeConverter } from '../../utils/time';
|
||||||
import { editorState } from './tiptap/utils';
|
import { editorState } from './tiptap/utils';
|
||||||
|
|
||||||
let timer = null;
|
const EditorOverlay = ({ editorId = '', editor }) => {
|
||||||
let timerError = null;
|
|
||||||
let timerClosing = null;
|
|
||||||
const EditorOverlay = () => {
|
|
||||||
const colors = useThemeStore(state => state.colors);
|
const colors = useThemeStore(state => state.colors);
|
||||||
const [loading, setLoading] = useState(null);
|
const [loading, setLoading] = useState(null);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const opacity = useSharedValue(1);
|
const opacity = useSharedValue(1);
|
||||||
|
const isDefaultEditor = editorId === '';
|
||||||
const animatedStyle = useAnimatedStyle(() => {
|
const timers = useRef({
|
||||||
return {
|
loading: 0,
|
||||||
opacity: opacity.value
|
error: 0,
|
||||||
};
|
closing: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const clearTimers = () => {
|
||||||
|
clearTimeout(timers.current.loading);
|
||||||
|
clearTimeout(timers.current.error);
|
||||||
|
clearTimeout(timers.current.closing);
|
||||||
|
};
|
||||||
|
|
||||||
const load = async _loading => {
|
const load = async _loading => {
|
||||||
editorState().overlay = true;
|
editorState().overlay = true;
|
||||||
clearTimeout(timer);
|
clearTimers();
|
||||||
clearTimeout(timerError);
|
|
||||||
clearTimeout(timerClosing);
|
|
||||||
if (_loading) {
|
if (_loading) {
|
||||||
opacity.value = 1;
|
opacity.value = 1;
|
||||||
setLoading(_loading);
|
setLoading(_loading);
|
||||||
timerError = setTimeout(() => {
|
timers.current.error = setTimeout(() => {
|
||||||
if (_loading) {
|
if (_loading) {
|
||||||
let _n = _loading;
|
let note = _loading;
|
||||||
_n.forced = true;
|
note.forced = true;
|
||||||
eSendEvent(eOnLoadNote, _n);
|
eSendEvent(eOnLoadNote + editorId, note);
|
||||||
}
|
}
|
||||||
setError(true);
|
setError(true);
|
||||||
}, 4000);
|
}, 4000);
|
||||||
} else {
|
} else {
|
||||||
clearTimeout(timer);
|
clearTimers();
|
||||||
clearTimeout(timerError);
|
|
||||||
clearTimeout(timerClosing);
|
|
||||||
setError(false);
|
setError(false);
|
||||||
editorState().overlay = false;
|
editorState().overlay = false;
|
||||||
opacity.value = withTiming(0, { duration: 150 });
|
|
||||||
timerClosing = setTimeout(() => {
|
|
||||||
setLoading(null);
|
setLoading(null);
|
||||||
}, 150);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,11 +58,18 @@ const EditorOverlay = () => {
|
|||||||
}, [loading]);
|
}, [loading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent('loadingNote', load);
|
eSubscribeEvent('loadingNote' + editorId, load);
|
||||||
return () => {
|
return () => {
|
||||||
eUnSubscribeEvent('loadingNote', load);
|
clearTimers();
|
||||||
|
eUnSubscribeEvent('loadingNote' + editorId, load);
|
||||||
};
|
};
|
||||||
}, [loading]);
|
}, [loading, editorId]);
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
opacity: opacity.value
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
@@ -98,6 +101,7 @@ const EditorOverlay = () => {
|
|||||||
paddingVertical: 20
|
paddingVertical: 20
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{isDefaultEditor ? (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -118,6 +122,7 @@ const EditorOverlay = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{loading?.title ? (
|
{loading?.title ? (
|
||||||
<Heading
|
<Heading
|
||||||
@@ -129,7 +134,7 @@ const EditorOverlay = () => {
|
|||||||
</Heading>
|
</Heading>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{loading?.dateEdited ? (
|
{loading?.dateEdited && isDefaultEditor ? (
|
||||||
<Paragraph
|
<Paragraph
|
||||||
textBreakStrategy="balanced"
|
textBreakStrategy="balanced"
|
||||||
style={{ textAlign: 'center' }}
|
style={{ textAlign: 'center' }}
|
||||||
@@ -155,7 +160,8 @@ const EditorOverlay = () => {
|
|||||||
}}
|
}}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setError(false);
|
setError(false);
|
||||||
eSendEvent('webviewreset');
|
editor.setLoading(true);
|
||||||
|
setTimeout(() => editor.setLoading(false), 10);
|
||||||
}}
|
}}
|
||||||
title="Taking too long? Reload editor"
|
title="Taking too long? Reload editor"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,29 +4,22 @@ import { EdgeInsets } from 'react-native-safe-area-context';
|
|||||||
import WebView from 'react-native-webview';
|
import WebView from 'react-native-webview';
|
||||||
import { db } from '../../../utils/database';
|
import { db } from '../../../utils/database';
|
||||||
import { sleep } from '../../../utils/time';
|
import { sleep } from '../../../utils/time';
|
||||||
import { Note } from './types';
|
import { Note, Settings } from './types';
|
||||||
import { getResponse, randId, textInput } from './utils';
|
import { getResponse, randId, textInput } from './utils';
|
||||||
import { Attachment, AttachmentProgress } from 'notesnook-editor/dist/extensions/attachment/index';
|
import { Attachment, AttachmentProgress } from 'notesnook-editor/dist/extensions/attachment/index';
|
||||||
import { ImageAttributes } from 'notesnook-editor/dist/extensions/image/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 };
|
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) {
|
async function call(webview: RefObject<WebView | undefined>, action?: Action) {
|
||||||
if (!webview.current || !action) return;
|
if (!webview.current || !action) return;
|
||||||
setImmediate(() => webview.current?.injectJavaScript(action.job));
|
setImmediate(() => webview.current?.injectJavaScript(action.job));
|
||||||
let response = await getResponse(action.id);
|
let response = await getResponse(action.id);
|
||||||
console.log('webview job: ', action.id, response ? response.value : response);
|
console.log('webview job: ', action.id, response ? response.value : response);
|
||||||
if (!response) {
|
if (!response) {
|
||||||
console.warn('webview job failed', action.id, action.job);
|
console.warn('webview job failed', action.id);
|
||||||
}
|
}
|
||||||
return response ? response.value : response;
|
return response ? response.value : response;
|
||||||
}
|
}
|
||||||
@@ -49,17 +42,17 @@ const fn = (fn: string) => {
|
|||||||
|
|
||||||
class Commands {
|
class Commands {
|
||||||
ref = createRef<WebView | undefined>();
|
ref = createRef<WebView | undefined>();
|
||||||
constructor(ref: MutableRefObject<WebView | undefined>) {
|
constructor(ref: RefObject<WebView>) {
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
async doAsync<T>(job: string) {
|
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>;
|
return call(this.ref, fn(job)) as Promise<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
focus = async () => {
|
focus = async () => {
|
||||||
if (!this.ref) return;
|
if (!this.ref.current) return;
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
this.ref.current?.requestFocus();
|
this.ref.current?.requestFocus();
|
||||||
setTimeout(async () => {
|
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 () => {
|
clearContent = async () => {
|
||||||
await this.doAsync(
|
await this.doAsync(
|
||||||
`
|
`
|
||||||
editor.commands.blur();
|
editor.commands.blur();
|
||||||
editorTitle.current?.blur();
|
typeof globalThis.editorTitle !== "undefined" && editorTitle.current && editorTitle.current?.blur();
|
||||||
editor?.commands.clearContent(false);
|
editor?.commands.clearContent(false);
|
||||||
editorController.setTitle(null);
|
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}"`);
|
setSessionId = async (id: string | null) => await this.doAsync(`globalThis.sessionId = "${id}"`);
|
||||||
|
|
||||||
setStatus = async (date: string | undefined, saved: string) =>
|
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) => {
|
setPlaceholder = async (placeholder: string) => {
|
||||||
await this.doAsync(`
|
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;
|
if (!note) return;
|
||||||
let tags = note.tags
|
let tags = note.tags
|
||||||
.map((t: any) =>
|
.map((t: any) =>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEditor } from './use-editor';
|
import { useEditor } from './use-editor';
|
||||||
|
import { ToolbarGroupDefinition } from 'notesnook-editor/dist/toolbar/types';
|
||||||
export type useEditorType = ReturnType<typeof useEditor>;
|
export type useEditorType = ReturnType<typeof useEditor>;
|
||||||
|
|
||||||
export type EditorState = {
|
export type EditorState = {
|
||||||
@@ -16,6 +16,25 @@ export type EditorState = {
|
|||||||
saveCount: 0;
|
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 = {
|
export type EditorMessage = {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
value: any;
|
value: any;
|
||||||
|
|||||||
@@ -13,15 +13,17 @@ import { MMKV } from '../../../utils/database/mmkv';
|
|||||||
import { eOnLoadNote } from '../../../utils/events';
|
import { eOnLoadNote } from '../../../utils/events';
|
||||||
import { tabBarRef } from '../../../utils/global-refs';
|
import { tabBarRef } from '../../../utils/global-refs';
|
||||||
import { timeConverter } from '../../../utils/time';
|
import { timeConverter } from '../../../utils/time';
|
||||||
|
import { NoteType } from '../../../utils/types';
|
||||||
import Commands from './commands';
|
import Commands from './commands';
|
||||||
import { AppState, Content, EditorState, Note, SavePayload } from './types';
|
import { AppState, Content, EditorState, Note, SavePayload } from './types';
|
||||||
import { defaultState, EditorEvents, isEditorLoaded, makeSessionId, post } from './utils';
|
import { defaultState, EditorEvents, isEditorLoaded, makeSessionId, post } from './utils';
|
||||||
|
|
||||||
export const useEditor = () => {
|
export const useEditor = (editorId = '', readonly?: boolean) => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [sessionId, setSessionId] = useState<string>(makeSessionId());
|
const [sessionId, setSessionId] = useState<string>(makeSessionId());
|
||||||
const editorRef = useRef<WebView>();
|
const sessionIdRef = useRef(sessionId);
|
||||||
const currentNote = useRef<Note | null>();
|
const editorRef = useRef<WebView>(null);
|
||||||
|
const currentNote = useRef<NoteType | null>();
|
||||||
const currentContent = useRef<Content | null>();
|
const currentContent = useRef<Content | null>();
|
||||||
const timers = useRef<{ [name: string]: NodeJS.Timeout }>({});
|
const timers = useRef<{ [name: string]: NodeJS.Timeout }>({});
|
||||||
const commands = useMemo(() => new Commands(editorRef), []);
|
const commands = useMemo(() => new Commands(editorRef), []);
|
||||||
@@ -30,16 +32,21 @@ export const useEditor = () => {
|
|||||||
const placeholderTip = useRef(TipManager.placeholderTip());
|
const placeholderTip = useRef(TipManager.placeholderTip());
|
||||||
const tags = useTagStore(state => state.tags);
|
const tags = useTagStore(state => state.tags);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
const isDefaultEditor = editorId === '';
|
||||||
|
|
||||||
const postMessage = useCallback(
|
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(() => {
|
useEffect(() => {
|
||||||
commands.setInsets(insets);
|
commands.setInsets(isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 });
|
||||||
}, [insets]);
|
}, [insets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sessionIdRef.current = sessionId;
|
||||||
|
}, [sessionId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
commands.setTags(currentNote.current);
|
commands.setTags(currentNote.current);
|
||||||
}, [tags]);
|
}, [tags]);
|
||||||
@@ -69,11 +76,11 @@ export const useEditor = () => {
|
|||||||
}, [sessionId, loading]);
|
}, [sessionId, loading]);
|
||||||
|
|
||||||
const overlay = (show: boolean, data = { type: 'new' }) => {
|
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 () => {
|
const onReady = useCallback(async () => {
|
||||||
if (!(await isEditorLoaded(editorRef))) {
|
if (!(await isEditorLoaded(editorRef, sessionId))) {
|
||||||
console.log('reload editor');
|
console.log('reload editor');
|
||||||
overlay(true);
|
overlay(true);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -99,7 +106,7 @@ export const useEditor = () => {
|
|||||||
await commands.clearContent();
|
await commands.clearContent();
|
||||||
console.log('reset state: ', resetState);
|
console.log('reset state: ', resetState);
|
||||||
if (resetState) {
|
if (resetState) {
|
||||||
useEditorStore.getState().setCurrentlyEditingNote(null);
|
isDefaultEditor && useEditorStore.getState().setCurrentlyEditingNote(null);
|
||||||
placeholderTip.current = TipManager.placeholderTip();
|
placeholderTip.current = TipManager.placeholderTip();
|
||||||
await commands.setPlaceholder(placeholderTip.current);
|
await commands.setPlaceholder(placeholderTip.current);
|
||||||
}
|
}
|
||||||
@@ -115,9 +122,10 @@ export const useEditor = () => {
|
|||||||
sessionHistoryId: currentSessionHistoryId
|
sessionHistoryId: currentSessionHistoryId
|
||||||
}: SavePayload) => {
|
}: SavePayload) => {
|
||||||
console.log('saving note', id);
|
console.log('saving note', id);
|
||||||
|
if (readonly) return;
|
||||||
try {
|
try {
|
||||||
if (id && !db.notes?.note(id)) {
|
if (id && !db.notes?.note(id)) {
|
||||||
useEditorStore.getState().setCurrentlyEditingNote(null);
|
isDefaultEditor && useEditorStore.getState().setCurrentlyEditingNote(null);
|
||||||
await reset();
|
await reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -144,11 +152,11 @@ export const useEditor = () => {
|
|||||||
if (!locked) {
|
if (!locked) {
|
||||||
id = await db.notes?.add(noteData);
|
id = await db.notes?.add(noteData);
|
||||||
if (!note && id) {
|
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);
|
state.current?.onNoteCreated && state.current.onNoteCreated(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useEditorStore.getState().currentEditingNote !== id) {
|
if (useEditorStore.getState().currentEditingNote !== id && isDefaultEditor) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
id && useEditorStore.getState().setCurrentlyEditingNote(id);
|
id && useEditorStore.getState().setCurrentlyEditingNote(id);
|
||||||
});
|
});
|
||||||
@@ -179,31 +187,25 @@ export const useEditor = () => {
|
|||||||
[commands, reset]
|
[commands, reset]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadContent = useCallback(async (note: Note) => {
|
const loadContent = useCallback(async (note: NoteType) => {
|
||||||
currentNote.current = note;
|
currentNote.current = note;
|
||||||
if (note.locked) {
|
if (note.locked || note.content) {
|
||||||
currentContent.current = {
|
currentContent.current = {
|
||||||
data: note.content.data,
|
data: note.content?.data,
|
||||||
type: note.content.type
|
type: note.content?.type || 'tiny'
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let data = await db.content?.raw(note.contentId);
|
currentContent.current = await db.content?.raw(note.contentId);
|
||||||
if (data) {
|
|
||||||
data = await db.content?.insertPlaceholders(data, 'placeholder.svg');
|
|
||||||
currentContent.current = {
|
|
||||||
data: data.data,
|
|
||||||
type: data.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadNote = useCallback(
|
const loadNote = useCallback(
|
||||||
async (item: Note) => {
|
async (item: NoteType) => {
|
||||||
console.log('loading note', item.type);
|
console.log('loading note', item.type, eOnLoadNote + editorId);
|
||||||
state.current.currentlyEditing = true;
|
state.current.currentlyEditing = true;
|
||||||
const editorState = useEditorStore.getState();
|
const editorState = useEditorStore.getState();
|
||||||
|
|
||||||
|
//@ts-ignore todo
|
||||||
if (item && item.type === 'new') {
|
if (item && item.type === 'new') {
|
||||||
currentNote.current && (await reset());
|
currentNote.current && (await reset());
|
||||||
let nextSessionId = makeSessionId(item);
|
let nextSessionId = makeSessionId(item);
|
||||||
@@ -212,8 +214,9 @@ export const useEditor = () => {
|
|||||||
await commands.setSessionId(nextSessionId);
|
await commands.setSessionId(nextSessionId);
|
||||||
await commands.focus();
|
await commands.focus();
|
||||||
} else {
|
} else {
|
||||||
|
//@ts-ignore todo
|
||||||
if (!item.forced && currentNote.current?.id === item.id) return;
|
if (!item.forced && currentNote.current?.id === item.id) return;
|
||||||
editorState.setCurrentlyEditingNote(item.id);
|
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
|
||||||
overlay(true, item);
|
overlay(true, item);
|
||||||
currentNote.current && (await reset(false));
|
currentNote.current && (await reset(false));
|
||||||
await loadContent(item);
|
await loadContent(item);
|
||||||
@@ -242,11 +245,11 @@ export const useEditor = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent(eOnLoadNote, loadNote);
|
eSubscribeEvent(eOnLoadNote + editorId, loadNote);
|
||||||
return () => {
|
return () => {
|
||||||
eUnSubscribeEvent(eOnLoadNote, loadNote);
|
eUnSubscribeEvent(eOnLoadNote + editorId, loadNote);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [editorId]);
|
||||||
|
|
||||||
const saveContent = useCallback(
|
const saveContent = useCallback(
|
||||||
({ title, content, type }: { title?: string; content?: string; type: string }) => {
|
({ title, content, type }: { title?: string; content?: string; type: string }) => {
|
||||||
@@ -284,13 +287,14 @@ export const useEditor = () => {
|
|||||||
state.current.ready = true;
|
state.current.ready = true;
|
||||||
onReady();
|
onReady();
|
||||||
postMessage(EditorEvents.theme, useThemeStore.getState().colors);
|
postMessage(EditorEvents.theme, useThemeStore.getState().colors);
|
||||||
|
commands.setInsets(isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 });
|
||||||
if (currentNote.current) {
|
if (currentNote.current) {
|
||||||
console.log('force reload note');
|
console.log('force reload note');
|
||||||
|
//@ts-ignore
|
||||||
loadNote({ ...currentNote.current, forced: true });
|
loadNote({ ...currentNote.current, forced: true });
|
||||||
} else {
|
} else {
|
||||||
await commands.setPlaceholder(placeholderTip.current);
|
await commands.setPlaceholder(placeholderTip.current);
|
||||||
commands.setInsets(insets);
|
isDefaultEditor && restoreEditorState();
|
||||||
restoreEditorState();
|
|
||||||
}
|
}
|
||||||
}, [state, currentNote, loadNote]);
|
}, [state, currentNote, loadNote]);
|
||||||
|
|
||||||
@@ -311,7 +315,9 @@ export const useEditor = () => {
|
|||||||
tabBarRef.current?.goToPage(1);
|
tabBarRef.current?.goToPage(1);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
console.log('restoring app state here');
|
||||||
if (appState.note) {
|
if (appState.note) {
|
||||||
|
//@ts-ignore
|
||||||
loadNote(appState.note);
|
loadNote(appState.note);
|
||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
@@ -339,6 +345,7 @@ export const useEditor = () => {
|
|||||||
setSessionId,
|
setSessionId,
|
||||||
note: currentNote,
|
note: currentNote,
|
||||||
onReady,
|
onReady,
|
||||||
saveContent
|
saveContent,
|
||||||
|
editorId: editorId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { BackHandler, InteractionManager, NativeEventSubscription } from 'react-native';
|
import { BackHandler, InteractionManager, NativeEventSubscription } from 'react-native';
|
||||||
|
import { WebViewMessageEvent } from 'react-native-webview';
|
||||||
import { Properties } from '../../../components/properties';
|
import { Properties } from '../../../components/properties';
|
||||||
import { DDS } from '../../../services/device-detection';
|
import { DDS } from '../../../services/device-detection';
|
||||||
import {
|
import {
|
||||||
@@ -29,8 +30,8 @@ import { tabBarRef } from '../../../utils/global-refs';
|
|||||||
import { NoteType } from '../../../utils/types';
|
import { NoteType } from '../../../utils/types';
|
||||||
import { useDragState } from '../../settings/editor/state';
|
import { useDragState } from '../../settings/editor/state';
|
||||||
import picker from './picker';
|
import picker from './picker';
|
||||||
import { EditorMessage, useEditorType } from './types';
|
import { EditorMessage, EditorProps, useEditorType } from './types';
|
||||||
import { editorController, EditorEvents, editorState } from './utils';
|
import { EditorEvents, editorState } from './utils';
|
||||||
|
|
||||||
export const EventTypes = {
|
export const EventTypes = {
|
||||||
selection: 'editor-event:selection',
|
selection: 'editor-event:selection',
|
||||||
@@ -49,7 +50,7 @@ export const EventTypes = {
|
|||||||
properties: 'editor-event:properties'
|
properties: 'editor-event:properties'
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishNote = async () => {
|
const publishNote = async (editor: useEditorType) => {
|
||||||
const user = useUserStore.getState().user;
|
const user = useUserStore.getState().user;
|
||||||
if (!user) {
|
if (!user) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
@@ -72,7 +73,7 @@ const publishNote = async () => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentNote = editorController.current?.note?.current;
|
const currentNote = editor?.note?.current;
|
||||||
if (currentNote?.id) {
|
if (currentNote?.id) {
|
||||||
let note = db.notes?.note(currentNote.id)?.data as NoteType;
|
let note = db.notes?.note(currentNote.id)?.data as NoteType;
|
||||||
if (note?.locked) {
|
if (note?.locked) {
|
||||||
@@ -90,8 +91,8 @@ const publishNote = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showActionsheet = async () => {
|
const showActionsheet = async (editor: useEditorType) => {
|
||||||
const currentNote = editorController.current?.note?.current;
|
const currentNote = editor?.note?.current;
|
||||||
if (currentNote?.id) {
|
if (currentNote?.id) {
|
||||||
let note = db.notes?.note(currentNote.id)?.data as NoteType;
|
let note = db.notes?.note(currentNote.id)?.data as NoteType;
|
||||||
if (!note) {
|
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 deviceMode = useSettingStore(state => state.deviceMode);
|
||||||
const fullscreen = useSettingStore(state => state.fullscreen);
|
const fullscreen = useSettingStore(state => state.fullscreen);
|
||||||
const handleBack = useRef<NativeEventSubscription>();
|
const handleBack = useRef<NativeEventSubscription>();
|
||||||
const currentEditingNote = useEditorStore(state => state.currentEditingNote);
|
|
||||||
const readonly = useEditorStore(state => state.readonly);
|
const readonly = useEditorStore(state => state.readonly);
|
||||||
const isPremium = useUserStore(state => state.premium);
|
const isPremium = useUserStore(state => state.premium);
|
||||||
const tools = useDragState(state => state.data);
|
const tools = useDragState(state => state.data);
|
||||||
@@ -129,17 +129,10 @@ export const useEditorEvents = (editor: useEditorType) => {
|
|||||||
fullscreen: fullscreen,
|
fullscreen: fullscreen,
|
||||||
premium: isPremium,
|
premium: isPremium,
|
||||||
readonly: readonly,
|
readonly: readonly,
|
||||||
tools: tools
|
tools: tools,
|
||||||
|
...editorProps
|
||||||
});
|
});
|
||||||
}, [
|
}, [fullscreen, isPremium, readonly, editor.sessionId, editor.loading, tools]);
|
||||||
currentEditingNote,
|
|
||||||
fullscreen,
|
|
||||||
isPremium,
|
|
||||||
readonly,
|
|
||||||
editor.sessionId,
|
|
||||||
editor.loading,
|
|
||||||
tools
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onBackPress = useCallback(async () => {
|
const onBackPress = useCallback(async () => {
|
||||||
const editorHandledBack = await editor.commands.handleBack();
|
const editorHandledBack = await editor.commands.handleBack();
|
||||||
@@ -165,7 +158,7 @@ export const useEditorEvents = (editor: useEditorType) => {
|
|||||||
});
|
});
|
||||||
setImmediate(() => useEditorStore.getState().setCurrentlyEditingNote(null));
|
setImmediate(() => useEditorStore.getState().setCurrentlyEditingNote(null));
|
||||||
editorState().currentlyEditing = false;
|
editorState().currentlyEditing = false;
|
||||||
editorController.current?.reset();
|
editor.reset();
|
||||||
}, 1);
|
}, 1);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -217,8 +210,8 @@ export const useEditorEvents = (editor: useEditorType) => {
|
|||||||
}, [fullscreen]);
|
}, [fullscreen]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent(eOnLoadNote, onLoadNote);
|
eSubscribeEvent(eOnLoadNote + editor.editorId, onLoadNote);
|
||||||
eSubscribeEvent(eClearEditor, onCallClear);
|
eSubscribeEvent(eClearEditor + editor.editorId, onCallClear);
|
||||||
return () => {
|
return () => {
|
||||||
eUnSubscribeEvent(eClearEditor, onCallClear);
|
eUnSubscribeEvent(eClearEditor, onCallClear);
|
||||||
eUnSubscribeEvent(eOnLoadNote, onLoadNote);
|
eUnSubscribeEvent(eOnLoadNote, onLoadNote);
|
||||||
@@ -226,8 +219,7 @@ export const useEditorEvents = (editor: useEditorType) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onMessage = useCallback(
|
const onMessage = useCallback(
|
||||||
event => {
|
(event: WebViewMessageEvent) => {
|
||||||
event;
|
|
||||||
const data = event.nativeEvent.data;
|
const data = event.nativeEvent.data;
|
||||||
let editorMessage = JSON.parse(data) as EditorMessage;
|
let editorMessage = JSON.parse(data) as EditorMessage;
|
||||||
|
|
||||||
@@ -303,10 +295,10 @@ export const useEditorEvents = (editor: useEditorType) => {
|
|||||||
eSendEvent(eOpenPremiumDialog);
|
eSendEvent(eOpenPremiumDialog);
|
||||||
break;
|
break;
|
||||||
case EventTypes.monograph:
|
case EventTypes.monograph:
|
||||||
publishNote();
|
publishNote(editor);
|
||||||
break;
|
break;
|
||||||
case EventTypes.properties:
|
case EventTypes.properties:
|
||||||
showActionsheet();
|
showActionsheet(editor);
|
||||||
break;
|
break;
|
||||||
case EventTypes.back:
|
case EventTypes.back:
|
||||||
onBackPress();
|
onBackPress();
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { createRef, MutableRefObject } from 'react';
|
import { createRef, RefObject } from 'react';
|
||||||
import { TextInput } from 'react-native';
|
import { TextInput } from 'react-native';
|
||||||
import WebView from 'react-native-webview';
|
import WebView from 'react-native-webview';
|
||||||
import { eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager';
|
import { eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager';
|
||||||
|
import { NoteType } from '../../../utils/types';
|
||||||
import { EditorState, useEditorType } from './types';
|
import { EditorState, useEditorType } from './types';
|
||||||
export const textInput = createRef<TextInput>();
|
export const textInput = createRef<TextInput>();
|
||||||
export const editorController = createRef<useEditorType>();
|
export const editorController = createRef<useEditorType>();
|
||||||
@@ -32,16 +33,15 @@ export function randId(prefix: string) {
|
|||||||
.replace('0.', prefix || '');
|
.replace('0.', prefix || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeSessionId(item?: any) {
|
export function makeSessionId(item?: NoteType) {
|
||||||
return item?.id ? item.id + randId('_session_') : randId('session_');
|
return item?.id ? item.id + randId('_session_') : randId('session_');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isEditorLoaded(ref: MutableRefObject<WebView | undefined>) {
|
export async function isEditorLoaded(ref: RefObject<WebView>, sessionId: string) {
|
||||||
return await post(ref, EditorEvents.status);
|
return await post(ref, sessionId, EditorEvents.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function post(ref: MutableRefObject<WebView | undefined>, type: string, value = null) {
|
export async function post(ref: RefObject<WebView>, sessionId: string, type: string, value = null) {
|
||||||
let sessionId = editorController.current?.sessionId;
|
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
console.warn('post called without sessionId of type:', type);
|
console.warn('post called without sessionId of type:', type);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ export const EditorWrapper = ({ width }) => {
|
|||||||
blurOnSubmit={false}
|
blurOnSubmit={false}
|
||||||
/>
|
/>
|
||||||
<ProgressBar />
|
<ProgressBar />
|
||||||
<Editor key="editor" />
|
<Editor key="editor" withController={true} />
|
||||||
<EditorOverlay key="overlay" />
|
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ export const useActions = ({ close = () => {}, item }) => {
|
|||||||
close();
|
close();
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
presentSheet({
|
presentSheet({
|
||||||
component: ref => <NoteHistory ref={ref} note={item} />
|
component: ref => <NoteHistory fwdRef={ref} note={item} />
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,14 +58,6 @@ export const useAppEvents = () => {
|
|||||||
isReconnecting: false
|
isReconnecting: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onMediaDownloaded = ({ hash, groupId, src }) => {
|
|
||||||
if (groupId?.startsWith('monograph')) return;
|
|
||||||
editorController.current?.commands.updateImage({
|
|
||||||
hash: hash,
|
|
||||||
src: src
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoadingAttachmentProgress = data => {
|
const onLoadingAttachmentProgress = data => {
|
||||||
useAttachmentStore.getState().setLoading(data.total === data.current ? null : 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.userSessionExpired, onSessionExpired);
|
||||||
EV.subscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
|
EV.subscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
|
||||||
EV.subscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
|
EV.subscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
|
||||||
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
|
|
||||||
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
|
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
|
||||||
eSubscribeEvent('userLoggedIn', onUserUpdated);
|
eSubscribeEvent('userLoggedIn', onUserUpdated);
|
||||||
|
|
||||||
@@ -123,7 +114,6 @@ export const useAppEvents = () => {
|
|||||||
EV.unsubscribe(EVENTS.userSessionExpired, onSessionExpired);
|
EV.unsubscribe(EVENTS.userSessionExpired, onSessionExpired);
|
||||||
EV.unsubscribe(EVENTS.userLoggedOut, onLogout);
|
EV.unsubscribe(EVENTS.userLoggedOut, onLogout);
|
||||||
EV.unsubscribe(EVENTS.userEmailConfirmed, onEmailVerified);
|
EV.unsubscribe(EVENTS.userEmailConfirmed, onEmailVerified);
|
||||||
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
|
|
||||||
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
|
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress);
|
||||||
EV.unsubscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
|
EV.unsubscribe(EVENTS.userCheckStatus, PremiumService.onUserStatusCheck);
|
||||||
EV.unsubscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
|
EV.unsubscribe(EVENTS.userSubscriptionUpdated, onAccountStatusChange);
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ export interface NoteType extends Entity<'note'> {
|
|||||||
contentId?: string;
|
contentId?: string;
|
||||||
headline?: string;
|
headline?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
content?: {
|
||||||
|
data: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookType extends Entity<'notebook'> {
|
export interface NotebookType extends Entity<'notebook'> {
|
||||||
|
|||||||
Reference in New Issue
Block a user