1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-10 22:11:50 +02:00

Mobile: Upgrade to React Native 0.77 (#12179)

This commit is contained in:
Henry Heino
2025-05-19 15:02:18 -07:00
committed by GitHub
parent cbf6d5506f
commit a4dacd65e6
38 changed files with 4191 additions and 3262 deletions

View File

@@ -893,6 +893,7 @@ packages/app-mobile/utils/makeShowMessageBox.test.js
packages/app-mobile/utils/makeShowMessageBox.js
packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/crypto-polyfill/index.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareFile.js

1
.gitignore vendored
View File

@@ -867,6 +867,7 @@ packages/app-mobile/utils/makeShowMessageBox.test.js
packages/app-mobile/utils/makeShowMessageBox.js
packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/crypto-polyfill/index.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareFile.js

View File

@@ -1,21 +1,21 @@
# This patch improves the note actions menu (the kebab menu)'s accessibility
# by labelling its dismiss button.
diff --git a/build/rnpm.js b/build/rnpm.js
index 1111c2de99b3d4c5651ca4eee3ba59c0ce8e13e1..d410ee12b38d02c399b0a40973217da0082d73c0 100644
index 47bc91a88b9e2246a0ce4295f9f932da6a572461..75b5a22bdcbc2594238bcf953df6d54e18cc7793 100644
--- a/build/rnpm.js
+++ b/build/rnpm.js
@@ -1573,7 +1573,9 @@
@@ -1267,7 +1267,9 @@
onPress = _this$props.onPress,
style = _this$props.style;
return /*#__PURE__*/React__default.createElement(reactNative.TouchableWithoutFeedback, {
return React__default.createElement(reactNative.TouchableWithoutFeedback, {
- onPress: onPress
+ onPress: onPress,
+ accessibilityLabel: _this$props.accessibilityLabel,
+ accessibilityRole: 'button',
}, /*#__PURE__*/React__default.createElement(reactNative.Animated.View, {
}, React__default.createElement(reactNative.Animated.View, {
style: [styles.fullscreen, {
opacity: this.fadeAnim
@@ -1588,7 +1590,8 @@
@@ -1282,7 +1284,8 @@
}(React.Component);
Backdrop.propTypes = {
@@ -25,24 +25,33 @@ index 1111c2de99b3d4c5651ca4eee3ba59c0ce8e13e1..d410ee12b38d02c399b0a40973217da0
};
var styles = reactNative.StyleSheet.create({
fullscreen: {
@@ -1658,6 +1661,7 @@
@@ -1352,6 +1355,7 @@
style: styles$1.placeholder
}, /*#__PURE__*/React__default.createElement(Backdrop, {
}, React__default.createElement(Backdrop, {
onPress: ctx._onBackdropPress,
+ accessibilityLabel: this.props.closeButtonLabel,
style: backdropStyles,
ref: ctx.onBackdropRef
}), ctx._makeOptions());
@@ -2090,6 +2094,7 @@
}), /*#__PURE__*/React__default.createElement(MenuPlaceholder, {
@@ -1784,6 +1788,7 @@
}), React__default.createElement(MenuPlaceholder, {
ctx: this,
backdropStyles: customStyles.backdrop,
+ closeButtonLabel: this.props.closeButtonLabel,
ref: this._onPlaceholderRef
}))));
}
@@ -1854,7 +1859,7 @@
var _options$props = options.props,
optionsContainerStyle = _options$props.optionsContainerStyle,
renderOptionsContainer = _options$props.renderOptionsContainer,
- customStyles = _options$props.customStyles;
+ customStyles = _options$props.customStyles || {};
var optionsRenderer = renderOptionsContainer || defaultOptionsContainerRenderer;
var isOutside = !triggerLayout || !optionsLayout;
diff --git a/src/index.d.ts b/src/index.d.ts
index 1db1e643a915e4bfb715e33354678ec1be219f50..007157e366d1935368bdd8eff5e7a0773e183d0f 100644
index 7e1ef2e441a665e97c304984080399f9646395df..673c4f713757abfb1851cba0d4560020c83e5f50 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -18,6 +18,7 @@ declare module "react-native-popup-menu" {

View File

@@ -110,6 +110,6 @@
"app-builder-lib@24.13.3": "patch:app-builder-lib@npm%3A24.13.3#./.yarn/patches/app-builder-lib-npm-24.13.3-86a66c0bf3.patch",
"react-native-sqlite-storage@6.0.1": "patch:react-native-sqlite-storage@npm%3A6.0.1#./.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch",
"react-native-paper@5.13.1": "patch:react-native-paper@npm%3A5.13.1#./.yarn/patches/react-native-paper-npm-5.13.1-f153e542e2.patch",
"react-native-popup-menu@0.16.1": "patch:react-native-popup-menu@npm%3A0.16.1#./.yarn/patches/react-native-popup-menu-npm-0.16.1-28fd66ecb5.patch"
"react-native-popup-menu@0.17.0": "patch:react-native-popup-menu@npm%3A0.17.0#./.yarn/patches/react-native-popup-menu-npm-0.17.0-8b745d88dd.patch"
}
}

View File

@@ -33,6 +33,7 @@ local.properties
.cxx/
*.keystore
!debug.keystore
.kotlin/
# node.js
#

View File

@@ -3,7 +3,14 @@ source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby ">= 2.6.10"
# Cocoapods 1.15 introduced a bug which breaks the build. We will remove the upper
# bound in the template on Cocoapods with next React Native release.
gem 'cocoapods', '>= 1.13', '< 1.15'
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'
# Ruby 3.4.0 has removed some libraries from the standard library.
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'

View File

@@ -8,14 +8,14 @@ apply plugin: "com.facebook.react"
*/
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
// The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../..")
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
// reactNativeDir = file("../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
// codegenDir = file("../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
// cliFile = file("../../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
@@ -49,6 +49,9 @@ react {
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
/* Autolinking */
autolinkLibrariesWithApp()
}
/**
@@ -60,14 +63,14 @@ def enableProguardInReleaseBuilds = false
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
android {
@@ -141,5 +144,4 @@ dependencies {
}
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

View File

@@ -43,7 +43,8 @@
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:resizeableActivity="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:supportsRtl="true">
<!--
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification

View File

@@ -1,6 +1,8 @@
#pragma once
#include <string>
#include <vector>
#include "whisper.h"
class WhisperSession {

View File

@@ -12,7 +12,7 @@ class MainActivity : ReactActivity() {
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
override fun getMainComponentName(): String = "Joplin"
override fun getMainComponentName(): String = "main"
/**
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]

View File

@@ -12,6 +12,7 @@ import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import net.cozic.joplin.audio.SpeechToTextPackage
import net.cozic.joplin.versioninfo.SystemVersionInformationPackage
@@ -31,7 +32,7 @@ class MainApplication : Application(), ReactApplication {
add(SpeechToTextPackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
@@ -45,7 +46,7 @@ class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
SoLoader.init(this, /* native exopackage */false)
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()

View File

@@ -2,14 +2,14 @@
buildscript {
ext {
buildToolsVersion = "34.0.0"
minSdkVersion = 23
buildToolsVersion = "35.0.0"
minSdkVersion = 24
compileSdkVersion = 34
compileSdkVersion = 35
targetSdkVersion = 34
ndkVersion = "26.1.10909125"
kotlinVersion = "1.9.22"
ndkVersion = "27.1.12297006"
kotlinVersion = "2.0.21"
}
repositories {
google()

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -203,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.

View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################

View File

@@ -1,8 +1,10 @@
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
rootProject.name = 'Joplin'
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/@react-native/gradle-plugin')
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle")
useExpoModules()
useExpoModules()

View File

@@ -1,3 +1,5 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
presets: [
'module:@react-native/babel-preset',
],
};

View File

@@ -1,9 +1,14 @@
import * as React from 'react';
import { CameraDirection } from '@joplin/lib/models/settings/builtInMetadata';
import { BarcodeSettings, CameraRatio, CameraView, useCameraPermissions } from 'expo-camera';
import { ForwardedRef, forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import { BarcodeSettings, CameraMountError, CameraRatio, CameraView, useCameraPermissions } from 'expo-camera';
import { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
import { CameraRef, Props } from './types';
import { _ } from '@joplin/lib/locale';
import { Platform } from 'react-native';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('Camera/expo');
const barcodeScannerSettings: BarcodeSettings = {
// Rocketbook pages use both QR and datamatrix
@@ -11,7 +16,9 @@ const barcodeScannerSettings: BarcodeSettings = {
};
const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
const cameraRef = useRef<CameraView>(null);
const [camera, setCamera] = useState<CameraView|null>(null);
const cameraRef = useRef<CameraView>(camera);
cameraRef.current = camera;
useImperativeHandle(ref, () => ({
takePictureAsync: async () => {
@@ -42,12 +49,31 @@ const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
}
}, [hasPermission, props.onHasPermission]);
const onMountError = useCallback((event: CameraMountError) => {
const message = _('Error starting camera: %s', event.message);
logger.error(message);
}, []);
useAsyncEffect(async (event) => {
// iOS issue workaround: Since upgrading to Expo SDK 52, closing and reopening the camera on iOS
// never emits onCameraReady. As a workaround, call .resumePreview and wait for it to resolve,
// rather than relying on the CameraView's onCameraReady prop.
if (Platform.OS === 'ios') {
// Work around an issue on iOS where the onCameraReady callback is never called.
// Instead, wait for the preview to start using resumePreview:
await camera.resumePreview();
if (event.cancelled) return;
props.onCameraReady();
}
}, [camera, props.onCameraReady]);
return <CameraView
ref={cameraRef}
ref={setCamera}
style={props.style}
facing={props.cameraType === CameraDirection.Front ? 'front' : 'back'}
ratio={props.ratio as CameraRatio}
onCameraReady={props.onCameraReady}
onCameraReady={Platform.OS === 'android' ? props.onCameraReady : undefined}
onMountError={onMountError}
animateShutter={false}
barcodeScannerSettings={barcodeScannerSettings}
onBarcodeScanned={props.codeScanner.onBarcodeScanned}

View File

@@ -18,6 +18,9 @@ import ScannedBarcodes from './ScannedBarcodes';
import { CameraRef } from './Camera/types';
import Camera from './Camera';
import { CameraResult } from './types';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('CameraView');
interface Props {
themeId: number;
@@ -130,6 +133,7 @@ const CameraViewComponent: React.FC<Props> = props => {
const codeScanner = useBarcodeScanner();
const onCameraReady = useCallback(() => {
logger.debug('Camera ready');
setCameraReady(true);
}, []);

View File

@@ -101,7 +101,7 @@ const MenuComponent: React.FC<Props> = props => {
<AccessibleView refocusCounter={canAutoFocus ? refocusCounter : undefined} testID={key}>
<Text
style={option.disabled ? styles.contextMenuItemTextDisabled : styles.contextMenuItemText}
disabled={!!option.disabled}
disabled={option.disabled}
>{option.title}</Text>
</AccessibleView>
</MenuOptionComponent>,
@@ -149,7 +149,7 @@ const MenuComponent: React.FC<Props> = props => {
<FocusControl.ModalWrapper state={open ? ModalState.Open : ModalState.Closed}>
<ScrollView
style={styles.menuContentScroller}
testID={`menu-content-${refocusCounter ? 'refocusing' : ''}`}
testID={`menu-content-${open ? 'open' : 'closed'}`}
>{menuOptionComponents}</ScrollView>
</FocusControl.ModalWrapper>
</MenuOptions>

View File

@@ -118,10 +118,10 @@ const openNoteActionsMenu = async () => {
await userEvent.press(actionMenuButton);
});
// State can update until the menu content is marked as in the process of refocusing (part of the
// State can update until the menu content is marked as open (part of the
// menu transition).
await waitFor(async () => {
expect(await screen.findByTestId('menu-content-refocusing')).toBeVisible();
expect(await screen.findByTestId('menu-content-open')).toBeVisible();
});
});
};

View File

@@ -14,7 +14,7 @@ import { Text } from 'react-native-paper';
import { AndroidAudioEncoder, AndroidOutputFormat, IOSAudioQuality, IOSOutputFormat, RecordingOptions } from 'expo-av/build/Audio';
import time from '@joplin/lib/time';
import { toFileExtension } from '@joplin/lib/mime-utils';
import { formatMsToDurationCompat } from '@joplin/utils/time';
import { formatMsToDurationCompat, msleep } from '@joplin/utils/time';
const logger = Logger.create('AudioRecording');
@@ -119,6 +119,11 @@ const useAudioRecorder = (onFileSaved: OnFileSavedCallback, onDismiss: ()=> void
if (!response.granted) {
throw new Error(_('Missing permission to record audio.'));
}
// Work around "This experience is currently in the background, so the audio session could not be activated"
// See https://github.com/expo/expo/issues/21782
// May be resolved by migrating to expo-audio.
await msleep(500);
}
await Audio.setAudioModeAsync({

View File

@@ -8,7 +8,13 @@
import './utils/polyfills';
import { LogBox, AppRegistry } from 'react-native';
import { LogBox } from 'react-native';
import { registerRootComponent } from 'expo';
// Allows loading image assets. See https://github.com/expo/expo/issues/31240
import 'expo-asset';
import shim from '@joplin/lib/shim';
shim.setReact(require('react'));
const Root = require('./root').default;
// Seems JavaScript developers love adding warnings everywhere, even when these warnings can't be fixed
@@ -45,7 +51,7 @@ LogBox.ignoreLogs([
'Did not receive response to shouldStartLoad in time, defaulting to YES',
]);
AppRegistry.registerComponent('Joplin', () => Root);
registerRootComponent(Root);
// Using streams on react-native requires to polyfill process.nextTick()
global.process.nextTick = setImmediate;

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -335,7 +335,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n# To fix a \"could not find asset\" error\nexport ENTRY_FILE=\"$REACT_NATIVE_PATH/../../index.js\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
};
63951A9079A5D0302FB331B7 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
@@ -347,6 +347,7 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
@@ -367,13 +368,17 @@
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
@@ -394,7 +399,10 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -539,8 +547,11 @@
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 13.3.7;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -567,11 +578,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 140;
CURRENT_PROJECT_VERSION = 136;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 13.3.7;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -641,10 +655,13 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n";
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD = "";
LDPLUSPLUS = "";
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
);
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -667,6 +684,7 @@
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
};
name = Debug;
@@ -715,10 +733,13 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS\n\n";
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD = "";
LDPLUSPLUS = "";
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
);
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -764,7 +785,11 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 13.3.7;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -803,7 +828,11 @@
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 13.3.7;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (

View File

@@ -67,7 +67,7 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"Joplin";
self.moduleName = @"main";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
@@ -84,11 +84,11 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
{
return [self bundleURL];
}
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

View File

@@ -1,3 +1,8 @@
# Use the Legacy Architecture:
# See https://blog.logrocket.com/react-native-new-architecture-sync-async-rendering
# https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here
ENV['RCT_NEW_ARCH_ENABLED'] = '0'
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',

File diff suppressed because it is too large Load Diff

View File

@@ -74,7 +74,7 @@ const emptyMockPackages = [
'react-native-share',
'react-native-file-viewer',
'react-native-image-picker',
'react-native-document-picker',
'@react-native-documents/picker',
'@joplin/react-native-saf-x',
'expo-av',
'expo-av/build/Audio',
@@ -97,7 +97,7 @@ jest.mock('react-native-zip-archive', () => {
return { default: { } };
});
jest.mock('react-native-document-picker', () => ({ default: { } }));
jest.mock('@react-native-documents/picker', () => ({ default: { } }));
// Used by the renderer
jest.doMock('react-native-vector-icons/Ionicons', () => {

View File

@@ -11,7 +11,8 @@
// https://github.com/facebook/metro/issues/1#issuecomment-511228599
const path = require('path');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { mergeConfig, getDefaultConfig } = require('@react-native/metro-config');
const { getDefaultConfig: getExpoDefaultConfig } = require('expo/metro-config');
const localPackages = {
'@joplin/lib': path.resolve(__dirname, '../lib/'),
@@ -28,20 +29,22 @@ const localPackages = {
'@joplin/fork-sax': path.resolve(__dirname, '../fork-sax/'),
};
const remappedPackages = {
...localPackages,
};
// cSpell:disable
// Some packages aren't available in react-native and thus must be polyfilled
// For example, this allows us to `import {resolve} from 'path'` rather than
// `const { resolve } = require('path-browserify')` ('path-browerify' doesn't have its own type
// definitions).
// cSpell:enable
const polyfilledPackages = ['path'];
for (const package of polyfilledPackages) {
remappedPackages[package] = path.resolve(__dirname, `./node_modules/${package}-browserify/`);
}
const polyfilledPackages = {
path: path.resolve(__dirname, './node_modules/path-browserify/'),
crypto: path.resolve(__dirname, './utils/polyfills/crypto-polyfill/'),
};
const remappedPackages = {
...localPackages,
...polyfilledPackages,
};
const watchedFolders = [];
for (const [, v] of Object.entries(localPackages)) {
@@ -98,4 +101,4 @@ const config = {
watchFolders: watchedFolders,
};
module.exports = mergeConfig(defaultConfig, config);
module.exports = mergeConfig(defaultConfig, getExpoDefaultConfig(__dirname), config);

View File

@@ -31,8 +31,9 @@
"@react-native-clipboard/clipboard": "1.14.3",
"@react-native-community/datetimepicker": "8.2.0",
"@react-native-community/geolocation": "3.3.0",
"@react-native-community/netinfo": "11.3.3",
"@react-native-community/netinfo": "11.4.1",
"@react-native-community/push-notification-ios": "1.11.0",
"@react-native-documents/picker": "10.1.2",
"assert-browserify": "2.0.0",
"buffer": "6.0.3",
"color": "3.2.1",
@@ -40,18 +41,17 @@
"crypto-browserify": "3.12.0",
"deprecated-react-native-prop-types": "5.0.0",
"events": "3.3.0",
"expo": "51.0.26",
"expo-av": "14.0.7",
"expo-camera": "15.0.16",
"expo": "52.0.46",
"expo-av": "15.0.2",
"expo-camera": "16.0.18",
"lodash": "4.17.21",
"md5": "2.3.0",
"path-browserify": "1.0.1",
"prop-types": "15.8.1",
"punycode": "2.3.1",
"react": "18.3.1",
"react-native": "0.74.1",
"react-native": "0.77.2",
"react-native-device-info": "10.14.0",
"react-native-document-picker": "9.3.0",
"react-native-dropdownalert": "5.1.0",
"react-native-exit-app": "2.0.0",
"react-native-file-viewer": "2.1.5",
@@ -61,12 +61,12 @@
"react-native-image-picker": "7.1.1",
"react-native-localize": "3.2.1",
"react-native-modal-datetime-picker": "17.1.0",
"react-native-paper": "5.13.1",
"react-native-popup-menu": "0.16.1",
"react-native-paper": "5.13.4",
"react-native-popup-menu": "0.17.0",
"react-native-quick-actions": "0.3.13",
"react-native-quick-crypto": "0.7.12",
"react-native-rsa-native": "2.0.5",
"react-native-safe-area-context": "4.10.8",
"react-native-safe-area-context": "5.4.0",
"react-native-securerandom": "1.0.1",
"react-native-share": "10.2.1",
"react-native-sqlite-storage": "6.0.1",
@@ -74,7 +74,7 @@
"react-native-vector-icons": "10.1.0",
"react-native-version-info": "1.1.1",
"react-native-vosk": "0.1.12",
"react-native-webview": "13.10.5",
"react-native-webview": "13.13.5",
"react-native-zip-archive": "6.1.2",
"react-redux": "8.1.3",
"redux": "4.2.1",
@@ -87,24 +87,26 @@
"url": "0.11.4"
},
"devDependencies": {
"@babel/core": "7.24.7",
"@babel/core": "7.26.0",
"@babel/plugin-transform-export-namespace-from": "7.24.7",
"@babel/preset-env": "7.24.7",
"@babel/runtime": "7.24.7",
"@babel/preset-env": "7.25.3",
"@babel/runtime": "7.25.0",
"@joplin/tools": "~3.3",
"@js-draw/material-icons": "1.30.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@react-native/babel-preset": "0.74.86",
"@react-native/metro-config": "0.74.87",
"@react-native-community/cli": "15.0.1",
"@react-native-community/cli-platform-android": "15.0.1",
"@react-native-community/cli-platform-ios": "15.0.1",
"@react-native/babel-preset": "0.77.2",
"@react-native/metro-config": "0.77.2",
"@react-native/typescript-config": "0.77.2",
"@sqlite.org/sqlite-wasm": "3.46.0-build2",
"@testing-library/jest-native": "5.4.3",
"@testing-library/react-native": "12.3.3",
"@tsconfig/react-native": "2.0.2",
"@types/fs-extra": "11.0.4",
"@types/jest": "29.5.12",
"@types/node": "18.19.67",
"@types/react": "18.3.3",
"@types/react-native": "0.70.6",
"@types/react": "18.3.12",
"@types/react-redux": "7.1.33",
"@types/serviceworker": "0.0.95",
"@types/tar-stream": "3.1.3",
@@ -132,7 +134,7 @@
"ts-jest": "29.1.5",
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"typescript": "5.4.5",
"typescript": "5.8.3",
"url-loader": "4.1.1",
"webpack": "5.97.1",
"webpack-cli": "5.1.4",
@@ -147,6 +149,16 @@
"expo-application",
"expo-keep-awake"
]
},
"install": {
"exclude": [
"react-native@~0.76.6",
"react-native-reanimated@~3.16.1",
"react-native-gesture-handler@~2.20.0",
"react-native-screens@~4.4.0",
"react-native-safe-area-context@~4.12.0",
"react-native-webview@~13.12.5"
]
}
}
}

View File

@@ -1,5 +1,5 @@
import shim from '@joplin/lib/shim';
import DocumentPicker, { DocumentPickerResponse } from 'react-native-document-picker';
import { DocumentPickerResponse, pick, isErrorWithCode } from '@react-native-documents/picker';
import { openDocument } from '@joplin/react-native-saf-x';
import Logger from '@joplin/utils/Logger';
import type FsDriverWeb from './fs-driver/fs-driver-rn.web';
@@ -83,9 +83,9 @@ const pickDocument = async ({ multiple = false, preferCamera = false }: Options
} else {
let docPickerResult: DocumentPickerResponse[] = [];
if (multiple) {
docPickerResult = await DocumentPicker.pick({ allowMultiSelection: true });
docPickerResult = await pick({ allowMultiSelection: true });
} else {
docPickerResult = [await DocumentPicker.pickSingle()];
docPickerResult = await pick();
}
result = docPickerResult.map(r => {
@@ -98,7 +98,7 @@ const pickDocument = async ({ multiple = false, preferCamera = false }: Options
});
}
} catch (error) {
if (DocumentPicker?.isCancel?.(error) || error?.message?.includes('cancel')) {
if ((isErrorWithCode(error) && error.code === 'OPERATION_CANCELED') || error?.message?.includes('cancel')) {
logger.info('user has cancelled');
return [];
} else {

View File

@@ -0,0 +1,9 @@
import 'react-native-get-random-values';
// nanoid uses require('crypto').getRandomValues, which doesn't work in React Native.
// This file is a partial polyfill for the NodeJS crypto module.
// eslint-disable-next-line import/prefer-default-export -- This needs to match the exports from NodeJS crypto
export const getRandomValues = (array: ArrayBufferView<ArrayBufferLike>) => {
return crypto.getRandomValues(array);
};

View File

@@ -0,0 +1,5 @@
{
"name": "crypto-polyfill",
"private": true,
"main": "./index.js"
}

View File

@@ -4,6 +4,13 @@ window.__DEV__ = window.location.origin.includes('localhost');
// Silences errors related to generated code.
window.exports = {};
// Expo libraries expect window.process variable to be defined.
window.process = {
env: {
EXPO_OS: 'web',
},
};
if (__DEV__) {
document.title = 'Joplin DEV';
}

View File

@@ -75,7 +75,7 @@ const buildSharedConfig = (hotReload: boolean): webpack.Configuration => {
'react-native-share': emptyLibraryMock,
'react-native-camera': emptyLibraryMock,
'react-native-zip-archive': emptyLibraryMock,
'react-native-document-picker': emptyLibraryMock,
'@react-native-documents/picker': emptyLibraryMock,
'react-native-exit-app': emptyLibraryMock,
'expo-camera': emptyLibraryMock,

View File

@@ -237,7 +237,7 @@ export const settingsSections = createSelector(
} else {
output.push(...([
'tools', 'importOrExport', 'moreInfo',
].map(name => {
].map((name): SettingMetadataSection => {
return {
name,
metadatas: [],

View File

@@ -45,7 +45,8 @@ export default async (command: string | string[], options: ExecCommandOptions |
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
const executableName = args[0];
args.splice(0, 1);
const promise = execa(executableName, args, { env: options.env, detached: options.detached });
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Workaround for type definition conflicts. Expo currently overrides NodeJs.ProcessEnv, making NODE_ENV required. This changes the type of the "env" argument to execa.
const promise = execa(executableName, args, { env: options.env as any });
if (options.showStdout && promise.stdout) promise.stdout.pipe(process.stdout);
if (options.showStderr && promise.stderr) promise.stderr.pipe(process.stderr);
const result = await promise;

4031
yarn.lock

File diff suppressed because it is too large Load Diff