Mobile: Upgrade to React Native 0.79 (#12337)
This commit is contained in:
parent
caba91fdf6
commit
42a156c2bb
@ -1,25 +0,0 @@
|
||||
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
index 8a719ca35af1cc3a4192c5c5f8258fd4f7fea990..5f8831f81cd164a4f627423427ead92fa286b115 100644
|
||||
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
@@ -37,7 +37,7 @@ import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
-import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
+import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,10 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
||||
}
|
||||
|
||||
private class ConcurrentOperationQueue {
|
||||
- private final Queue<UIThreadOperation> mQueue = new ConcurrentLinkedQueue<>();
|
||||
+ // Patch: Use LinkedBlockingQueue instead of ConcurrentLinkedQueue.
|
||||
+ // In some versions of Android, ConcurrentLinkedQueue is known to drop
|
||||
+ // items, causing crashing. See https://github.com/laurent22/joplin/issues/8425
|
||||
+ private final Queue<UIThreadOperation> mQueue = new LinkedBlockingQueue<>();
|
||||
@Nullable private UIThreadOperation mPeekedOperation = null;
|
||||
|
||||
@AnyThread
|
205
.yarn/patches/react-native-npm-0.79.2-9db13eddfe.patch
Normal file
205
.yarn/patches/react-native-npm-0.79.2-9db13eddfe.patch
Normal file
@ -0,0 +1,205 @@
|
||||
# This patch fixes two issues:
|
||||
# - Updates RCTDeviceInfo.m to match https://github.com/facebook/react-native/commit/0b8db7e5e814cfbf9974cc5b6ceb64e8006d8a3c.
|
||||
# This fixes an issue in which useWindowDimensions returns incorrect
|
||||
# values in landscape mode in iOS.
|
||||
# This should be fixed in React Native 0.80. See https://github.com/facebook/react-native/issues/51086.
|
||||
# - Updates NativeAnimatedModule.java to work around an Android 12-specific crash.
|
||||
diff --git a/React/CoreModules/RCTDeviceInfo.mm b/React/CoreModules/RCTDeviceInfo.mm
|
||||
index 6b4fcef852252e8d4ac2aceb12175fdfafb4def7..8ceab21e8653d429876d10e2d12ed1342780ad7d 100644
|
||||
--- a/React/CoreModules/RCTDeviceInfo.mm
|
||||
+++ b/React/CoreModules/RCTDeviceInfo.mm
|
||||
@@ -14,9 +14,7 @@
|
||||
#import <React/RCTEventDispatcherProtocol.h>
|
||||
#import <React/RCTInitializing.h>
|
||||
#import <React/RCTInvalidating.h>
|
||||
-#import <React/RCTKeyWindowValuesProxy.h>
|
||||
#import <React/RCTUtils.h>
|
||||
-#import <React/RCTWindowSafeAreaProxy.h>
|
||||
#import <atomic>
|
||||
|
||||
#import "CoreModulesPlugins.h"
|
||||
@@ -31,8 +29,13 @@ using namespace facebook::react;
|
||||
NSDictionary *_currentInterfaceDimensions;
|
||||
BOOL _isFullscreen;
|
||||
std::atomic<BOOL> _invalidated;
|
||||
+ NSDictionary *_constants;
|
||||
+
|
||||
+ __weak UIWindow *_applicationWindow;
|
||||
}
|
||||
|
||||
+static NSString *const kFrameKeyPath = @"frame";
|
||||
+
|
||||
@synthesize moduleRegistry = _moduleRegistry;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
@@ -40,14 +43,26 @@ RCT_EXPORT_MODULE()
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
- [[RCTKeyWindowValuesProxy sharedInstance] startObservingWindowSizeIfNecessary];
|
||||
+ _applicationWindow = RCTKeyWindow();
|
||||
+ [_applicationWindow addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
+ ofObject:(id)object
|
||||
+ change:(NSDictionary *)change
|
||||
+ context:(void *)context
|
||||
+{
|
||||
+ if ([keyPath isEqualToString:kFrameKeyPath]) {
|
||||
+ [self interfaceFrameDidChange];
|
||||
+ [[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
- return NO;
|
||||
+ return YES;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
@@ -81,7 +96,7 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
- _currentInterfaceOrientation = [RCTKeyWindowValuesProxy sharedInstance].currentInterfaceOrientation;
|
||||
+ _currentInterfaceOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(interfaceFrameDidChange)
|
||||
@@ -98,6 +113,15 @@ RCT_EXPORT_MODULE()
|
||||
selector:@selector(invalidate)
|
||||
name:RCTBridgeWillInvalidateModulesNotification
|
||||
object:nil];
|
||||
+
|
||||
+ _constants = @{
|
||||
+ @"Dimensions" : [self _exportedDimensions],
|
||||
+ // Note:
|
||||
+ // This prop is deprecated and will be removed in a future release.
|
||||
+ // Please use this only for a quick and temporary solution.
|
||||
+ // Use <SafeAreaView> instead.
|
||||
+ @"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()),
|
||||
+ };
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
@@ -120,6 +144,8 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RCTBridgeWillInvalidateModulesNotification object:nil];
|
||||
|
||||
+ [_applicationWindow removeObserver:self forKeyPath:kFrameKeyPath];
|
||||
+
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
|
||||
#endif
|
||||
@@ -132,8 +158,13 @@ static BOOL RCTIsIPhoneNotched()
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
dispatch_once(&onceToken, ^{
|
||||
+ RCTAssertMainQueue();
|
||||
+
|
||||
// 20pt is the top safeArea value in non-notched devices
|
||||
- isIPhoneNotched = [RCTWindowSafeAreaProxy sharedInstance].currentSafeAreaInsets.top > 20;
|
||||
+ UIWindow *keyWindow = RCTKeyWindow();
|
||||
+ if (keyWindow) {
|
||||
+ isIPhoneNotched = keyWindow.safeAreaInsets.top > 20;
|
||||
+ }
|
||||
});
|
||||
#endif
|
||||
|
||||
@@ -142,11 +173,13 @@ static BOOL RCTIsIPhoneNotched()
|
||||
|
||||
static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
|
||||
{
|
||||
+ RCTAssertMainQueue();
|
||||
UIScreen *mainScreen = UIScreen.mainScreen;
|
||||
CGSize screenSize = mainScreen.bounds.size;
|
||||
+ UIView *mainWindow = RCTKeyWindow();
|
||||
|
||||
// We fallback to screen size if a key window is not found.
|
||||
- CGSize windowSize = [RCTKeyWindowValuesProxy sharedInstance].windowSize;
|
||||
+ CGSize windowSize = mainWindow ? mainWindow.bounds.size : screenSize;
|
||||
|
||||
NSDictionary<NSString *, NSNumber *> *dimsWindow = @{
|
||||
@"width" : @(windowSize.width),
|
||||
@@ -170,7 +203,10 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
|
||||
RCTAssert(_moduleRegistry, @"Failed to get exported dimensions: RCTModuleRegistry is nil");
|
||||
RCTAccessibilityManager *accessibilityManager =
|
||||
(RCTAccessibilityManager *)[_moduleRegistry moduleForName:"AccessibilityManager"];
|
||||
- RCTAssert(accessibilityManager, @"Failed to get exported dimensions: AccessibilityManager is nil");
|
||||
+ // TOOD(T225745315): For some reason, accessibilityManager is nil in some cases.
|
||||
+ // We default the fontScale to 1.0 in this case. This should be okay: if we assume
|
||||
+ // that accessibilityManager will eventually become available, js will eventually
|
||||
+ // be updated with the correct fontScale.
|
||||
CGFloat fontScale = accessibilityManager ? accessibilityManager.multiplier : 1.0;
|
||||
return RCTExportedDimensions(fontScale);
|
||||
}
|
||||
@@ -182,14 +218,7 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
|
||||
|
||||
- (NSDictionary<NSString *, id> *)getConstants
|
||||
{
|
||||
- return @{
|
||||
- @"Dimensions" : [self _exportedDimensions],
|
||||
- // Note:
|
||||
- // This prop is deprecated and will be removed in a future release.
|
||||
- // Please use this only for a quick and temporary solution.
|
||||
- // Use <SafeAreaView> instead.
|
||||
- @"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()),
|
||||
- };
|
||||
+ return _constants;
|
||||
}
|
||||
|
||||
- (void)didReceiveNewContentSizeMultiplier
|
||||
@@ -209,10 +238,11 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
|
||||
- (void)interfaceOrientationDidChange
|
||||
{
|
||||
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
||||
- UIWindow *keyWindow = RCTKeyWindow();
|
||||
- UIInterfaceOrientation nextOrientation = keyWindow.windowScene.interfaceOrientation;
|
||||
+ UIApplication *application = RCTSharedApplication();
|
||||
+ UIInterfaceOrientation nextOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
|
||||
|
||||
- BOOL isRunningInFullScreen = CGRectEqualToRect(keyWindow.frame, keyWindow.screen.bounds);
|
||||
+ BOOL isRunningInFullScreen =
|
||||
+ CGRectEqualToRect(application.delegate.window.frame, application.delegate.window.screen.bounds);
|
||||
// We are catching here two situations for multitasking view:
|
||||
// a) The app is in Split View and the container gets resized -> !isRunningInFullScreen
|
||||
// b) The app changes to/from fullscreen example: App runs in slide over mode and goes into fullscreen->
|
||||
@@ -276,3 +306,4 @@ Class RCTDeviceInfoCls(void)
|
||||
{
|
||||
return RCTDeviceInfo.class;
|
||||
}
|
||||
+
|
||||
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
index cf14e51cf5f561b84f1b6ace8410fc77d626758e..abc8c64adf26fbf73429aee7fd4f76877e98849a 100644
|
||||
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
@@ -42,6 +42,7 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
+import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
@@ -155,8 +156,15 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
||||
}
|
||||
|
||||
private class ConcurrentOperationQueue {
|
||||
- private final Queue<UIThreadOperation> mQueue = new ConcurrentLinkedQueue<>();
|
||||
- @Nullable private UIThreadOperation mPeekedOperation = null;
|
||||
+ // Patch: Use LinkedBlockingQueue instead of ConcurrentLinkedQueue.
|
||||
+ // In some versions of Android, ConcurrentLinkedQueue is known to drop
|
||||
+ // items, causing crashing. See https://github.com/laurent22/joplin/issues/8425
|
||||
+ private final Queue<UIThreadOperation> mQueue = (
|
||||
+ // The issue exists for Android 12, which corresponds to API levels 31 and 32.
|
||||
+ Build.VERSION.SDK_INT == 31 || Build.VERSION.SDK_INT == 32
|
||||
+ ) ? new LinkedBlockingQueue<>() : new ConcurrentLinkedQueue<>();
|
||||
+
|
||||
+ @Nullable private UIThreadOperation mPeekedOperation = null;
|
||||
|
||||
@AnyThread
|
||||
boolean isEmpty() {
|
@ -104,13 +104,13 @@
|
||||
"nanoid": "patch:nanoid@npm%3A3.3.7#./.yarn/patches/nanoid-npm-3.3.7-98824ba130.patch",
|
||||
"pdfjs-dist": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
|
||||
"chokidar@^2.0.0": "3.5.3",
|
||||
"react-native@0.74.1": "patch:react-native@npm%3A0.74.1#./.yarn/patches/react-native-npm-0.74.1-754c02ae9e.patch",
|
||||
"rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch",
|
||||
"app-builder-lib@26.0.0-alpha.7": "patch:app-builder-lib@npm%3A26.0.0-alpha.7#./.yarn/patches/app-builder-lib-npm-26.0.0-alpha.7-e1b3dca119.patch",
|
||||
"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.17.0": "patch:react-native-popup-menu@npm%3A0.17.0#./.yarn/patches/react-native-popup-menu-npm-0.17.0-8b745d88dd.patch",
|
||||
"react-native@0.79.2": "patch:react-native@npm%3A0.79.2#./.yarn/patches/react-native-npm-0.79.2-9db13eddfe.patch",
|
||||
"pdfjs-dist@2.16.105": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
|
||||
"pdfjs-dist@*": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
|
||||
"pdfjs-dist@3.11.174": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
|
||||
|
@ -6,7 +6,7 @@ buildscript {
|
||||
minSdkVersion = 24
|
||||
|
||||
compileSdkVersion = 35
|
||||
targetSdkVersion = 34
|
||||
targetSdkVersion = 35
|
||||
|
||||
ndkVersion = "27.1.12297006"
|
||||
kotlinVersion = "2.0.21"
|
||||
|
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
@ -1,10 +1,42 @@
|
||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||
pluginManagement {
|
||||
def reactNativeGradlePlugin = new File(
|
||||
providers.exec {
|
||||
workingDir(rootDir)
|
||||
commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
|
||||
}.standardOutput.asText.get().trim()
|
||||
).getParentFile().absolutePath
|
||||
includeBuild(reactNativeGradlePlugin)
|
||||
|
||||
def expoPluginsPath = new File(
|
||||
providers.exec {
|
||||
workingDir(rootDir)
|
||||
commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
|
||||
}.standardOutput.asText.get().trim(),
|
||||
"../android/expo-gradle-plugin"
|
||||
).absolutePath
|
||||
includeBuild(expoPluginsPath)
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.facebook.react.settings")
|
||||
id("expo-autolinking-settings")
|
||||
}
|
||||
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
|
||||
if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
|
||||
ex.autolinkLibrariesFromCommand()
|
||||
} else {
|
||||
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
|
||||
}
|
||||
}
|
||||
expoAutolinking.useExpoModules()
|
||||
|
||||
rootProject.name = 'Joplin'
|
||||
|
||||
expoAutolinking.useExpoVersionCatalog()
|
||||
|
||||
include ':react-native-vector-icons'
|
||||
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
|
||||
|
||||
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()
|
||||
includeBuild(expoAutolinking.reactNativeGradlePlugin)
|
@ -58,7 +58,7 @@ const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
|
||||
// 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') {
|
||||
if (Platform.OS === 'ios' && camera) {
|
||||
// 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();
|
||||
@ -67,7 +67,7 @@ const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
|
||||
}
|
||||
}, [camera, props.onCameraReady]);
|
||||
|
||||
return <CameraView
|
||||
return hasPermission?.granted ? <CameraView
|
||||
ref={setCamera}
|
||||
style={props.style}
|
||||
facing={props.cameraType === CameraDirection.Front ? 'front' : 'back'}
|
||||
@ -77,7 +77,7 @@ const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
|
||||
animateShutter={false}
|
||||
barcodeScannerSettings={barcodeScannerSettings}
|
||||
onBarcodeScanned={props.codeScanner.onBarcodeScanned}
|
||||
/>;
|
||||
/> : null;
|
||||
};
|
||||
|
||||
export default forwardRef(Camera);
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import CameraView from './CameraView';
|
||||
import { CameraResult } from './types';
|
||||
import { fireEvent, render, screen } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import createMockReduxStore from '../../utils/testing/createMockReduxStore';
|
||||
import TestProviderStack from '../testing/TestProviderStack';
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
||||
|
||||
import { describe, it, expect, jest } from '@jest/globals';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native';
|
||||
|
||||
import Dropdown, { DropdownListItem } from './Dropdown';
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
|
||||
import { Store } from 'redux';
|
||||
import { AppState } from '../../utils/types';
|
||||
@ -46,11 +45,19 @@ const toggleSettingsItem = async (props: ToggleSettingItemProps) => {
|
||||
|
||||
const itemCheckbox = await screen.findByRole('checkbox', { name: props.name });
|
||||
expect(itemCheckbox).toBeVisible();
|
||||
expect(itemCheckbox).toHaveAccessibilityState({ checked: initialChecked });
|
||||
if (initialChecked) {
|
||||
expect(itemCheckbox).toBeChecked();
|
||||
} else {
|
||||
expect(itemCheckbox).not.toBeChecked();
|
||||
}
|
||||
fireEvent.press(itemCheckbox);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(itemCheckbox).toHaveAccessibilityState({ checked: finalChecked });
|
||||
if (finalChecked) {
|
||||
expect(itemCheckbox).toBeChecked();
|
||||
} else {
|
||||
expect(itemCheckbox).not.toBeChecked();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
|
||||
import { describe, it, beforeEach } from '@jest/globals';
|
||||
import { render, screen, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
|
||||
|
||||
import NoteBodyViewer from './NoteBodyViewer';
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
|
||||
import { describe, it, expect, beforeEach } from '@jest/globals';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native';
|
||||
|
||||
import NoteEditor from './NoteEditor';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
@ -3,7 +3,6 @@ import { WarningBannerComponent } from './WarningBanner';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import NavService from '@joplin/lib/services/NavService';
|
||||
import { render, screen, userEvent } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import { ShareInvitation, ShareUserStatus } from '@joplin/lib/services/share/reducer';
|
||||
import makeShareInvitation from '@joplin/lib/testing/share/makeMockShareInvitation';
|
||||
|
||||
|
@ -3,7 +3,6 @@ import * as React from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { act, fireEvent, render, waitFor } from '@testing-library/react-native';
|
||||
import { expect, describe, beforeEach, test, jest } from '@jest/globals';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import { createNTestNotes, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import configScreenStyles from '../configScreenStyles';
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import { createTempDir, mockMobilePlatform, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
import { act, fireEvent, render, screen, userEvent, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/react-native/extend-expect';
|
||||
|
||||
import PluginService, { PluginSettings, defaultPluginSetting } from '@joplin/lib/services/plugins/PluginService';
|
||||
import pluginServiceSetup from './testUtils/pluginServiceSetup';
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import { mockMobilePlatform, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
import { render, screen, userEvent, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/react-native/extend-expect';
|
||||
|
||||
import pluginServiceSetup from './testUtils/pluginServiceSetup';
|
||||
import createMockReduxStore from '../../../../utils/testing/createMockReduxStore';
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
|
||||
import { describe, it, beforeEach } from '@jest/globals';
|
||||
import { act, fireEvent, render, screen, userEvent, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
|
||||
import NoteScreen from './Note';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, simulateReadOnlyShareEnv, supportDir, synchronizerStart, resourceFetcher, runWithFakeTimers } from '@joplin/lib/testing/test-utils';
|
||||
@ -113,7 +112,7 @@ const openNoteActionsMenu = async () => {
|
||||
|
||||
// Wrap in act(...) -- this tells the test library that component state is intended to update (prevents
|
||||
// warnings).
|
||||
await act(async () => {
|
||||
await waitFor(async () => {
|
||||
await runWithFakeTimers(async () => {
|
||||
await userEvent.press(actionMenuButton);
|
||||
});
|
||||
@ -156,10 +155,7 @@ describe('screens/Note', () => {
|
||||
// In order for note changes to be saved, note-screen-shared requires
|
||||
// that at least one folder exist.
|
||||
await Folder.save({ title: 'test', parent_id: '' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
screen.unmount();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should show the currently selected note', async () => {
|
||||
@ -210,27 +206,27 @@ describe('screens/Note', () => {
|
||||
const noteId = await openNewNote({ title: 'Unchanged title', body: defaultBody });
|
||||
|
||||
const noteScreen = render(<WrappedNoteScreen />);
|
||||
await act(async () => await runWithFakeTimers(async () => {
|
||||
await openEditor();
|
||||
const editor = await getNoteEditorControl();
|
||||
await openEditor();
|
||||
const editor = await getNoteEditorControl();
|
||||
await act(async () => {
|
||||
editor.select(defaultBody.length, defaultBody.length);
|
||||
|
||||
editor.insertText(' Testing!!!');
|
||||
await waitForNoteToMatch(noteId, { body: 'Change me! Testing!!!' });
|
||||
|
||||
expect(editor.editor.state.doc.toString()).toBe('Change me! Testing!!!');
|
||||
});
|
||||
|
||||
await waitForNoteToMatch(noteId, { body: 'Change me! Testing!!!' });
|
||||
|
||||
await act(async () => {
|
||||
editor.insertText(' This is a test.');
|
||||
await waitForNoteToMatch(noteId, { body: 'Change me! Testing!!! This is a test.' });
|
||||
|
||||
// should also save changes made shortly before unmounting
|
||||
editor.insertText(' Test!');
|
||||
|
||||
// TODO: Decreasing this below 100 causes the test to fail.
|
||||
// See issue #11125.
|
||||
await jest.advanceTimersByTimeAsync(450);
|
||||
|
||||
noteScreen.unmount();
|
||||
await waitForNoteToMatch(noteId, { body: 'Change me! Testing!!! This is a test. Test!' });
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('pressing "delete" should move the note to the trash', async () => {
|
||||
@ -290,7 +286,7 @@ describe('screens/Note', () => {
|
||||
|
||||
await openNoteActionsMenu();
|
||||
const deleteButton = await screen.findByText('Delete');
|
||||
expect(deleteButton).toBeDisabled();
|
||||
expect(deleteButton).toHaveProp('disabled', true);
|
||||
|
||||
act(() => cleanup());
|
||||
});
|
||||
|
@ -148,7 +148,7 @@ interface State {
|
||||
showSpeechToTextDialog: boolean;
|
||||
}
|
||||
|
||||
class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> implements BaseNoteScreenComponent {
|
||||
class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> implements BaseNoteScreenComponent<State> {
|
||||
// This isn't in this.state because we don't want changing scroll to trigger
|
||||
// a re-render.
|
||||
private lastBodyScroll: number|undefined = undefined;
|
||||
@ -725,9 +725,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
this.selection = { start: event.from, end: event.to };
|
||||
};
|
||||
|
||||
public makeSaveAction() {
|
||||
public makeSaveAction(state: State) {
|
||||
return async () => {
|
||||
return shared.saveNoteButton_press(this, null, null);
|
||||
return shared.saveNoteButton_press(this, state, null, null);
|
||||
};
|
||||
}
|
||||
|
||||
@ -738,12 +738,12 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
return this.saveActionQueues_[noteId];
|
||||
}
|
||||
|
||||
public scheduleSave() {
|
||||
this.saveActionQueue(this.state.note.id).push(this.makeSaveAction());
|
||||
public scheduleSave(state: State) {
|
||||
this.saveActionQueue(state.note.id).push(this.makeSaveAction(state));
|
||||
}
|
||||
|
||||
private async saveNoteButton_press(folderId: string = null) {
|
||||
await shared.saveNoteButton_press(this, folderId, null);
|
||||
await shared.saveNoteButton_press(this, this.state, folderId, null);
|
||||
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
@ -913,7 +913,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
|
||||
void this.refreshResource(resource, newNote.body);
|
||||
|
||||
this.scheduleSave();
|
||||
this.scheduleSave({ ...this.state, note: newNote });
|
||||
|
||||
return resource;
|
||||
}
|
||||
@ -1024,7 +1024,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
private toggleIsTodo_onPress() {
|
||||
shared.toggleIsTodo_onPress(this);
|
||||
|
||||
this.scheduleSave();
|
||||
this.scheduleSave(this.state);
|
||||
}
|
||||
|
||||
private async share_onPress() {
|
||||
@ -1473,7 +1473,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
const newNote: NoteEntity = { ...this.state.note };
|
||||
newNote.body = `${newNote.body} ${text}`;
|
||||
this.setState({ note: newNote });
|
||||
this.scheduleSave();
|
||||
this.scheduleSave(this.state);
|
||||
} else {
|
||||
if (this.useEditorBeta()) {
|
||||
// We add a space so that if the feature is used twice in a row,
|
||||
|
@ -3,11 +3,10 @@ import { Store } from 'redux';
|
||||
import { AppState } from '../../utils/types';
|
||||
import TestProviderStack from '../testing/TestProviderStack';
|
||||
import NoteRevisionViewer from './NoteRevisionViewer';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, revisionService, waitFor } from '@joplin/lib/testing/test-utils';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, revisionService } from '@joplin/lib/testing/test-utils';
|
||||
import createMockReduxStore from '../../utils/testing/createMockReduxStore';
|
||||
import setupGlobalStore from '../../utils/testing/setupGlobalStore';
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react-native';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import { useMemo } from 'react';
|
||||
import Revision from '@joplin/lib/models/Revision';
|
||||
@ -86,20 +85,19 @@ describe('screens/NoteRevisionViewer', () => {
|
||||
|
||||
test('selecting a revision should render its content', async () => {
|
||||
const note = await createNoteWithTestRevisions(3);
|
||||
const { unmount } = render(<WrappedRevisionViewerScreen noteId={note.id}/>);
|
||||
render(<WrappedRevisionViewerScreen noteId={note.id}/>);
|
||||
|
||||
const dropdown = screen.getByRole('button', { name: 'Select a revision...' });
|
||||
fireEvent.press(dropdown);
|
||||
|
||||
// Select the second revision
|
||||
await act(() => waitFor(async () => {
|
||||
await waitFor(() => {
|
||||
const firstRevision = screen.getAllByRole('menuitem')[1];
|
||||
fireEvent.press(firstRevision);
|
||||
}));
|
||||
});
|
||||
|
||||
await act(() => waitFor(async () => {
|
||||
await waitFor(async () => {
|
||||
expect(await getRevisionViewerText()).toBe('Update 2');
|
||||
}));
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,6 @@ import { Store } from 'redux';
|
||||
import createMockReduxStore from '../../../utils/testing/createMockReduxStore';
|
||||
import setupGlobalStore from '../../../utils/testing/setupGlobalStore';
|
||||
import { act, render, screen, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import { AccessibilityActionInfo } from 'react-native';
|
||||
import { setupDatabaseAndSynchronizer } from '@joplin/lib/testing/test-utils';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
|
@ -2,8 +2,7 @@ import * as React from 'react';
|
||||
import { ShareManagerComponent } from './index';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import mockShareService from '@joplin/lib/testing/share/mockShareService';
|
||||
import { fireEvent, render, screen, userEvent, waitFor } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import { act, render, screen, userEvent, waitFor } from '@testing-library/react-native';
|
||||
import { ShareInvitation, ShareUserStatus } from '@joplin/lib/services/share/reducer';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
@ -64,7 +63,9 @@ describe('ShareManager', () => {
|
||||
|
||||
// See https://github.com/callstack/react-native-testing-library/issues/809#issuecomment-984823700
|
||||
const { refreshControl } = screen.getByTestId('refreshControl').props;
|
||||
fireEvent(refreshControl, 'refresh');
|
||||
await act(async () => {
|
||||
await refreshControl.props.onRefresh();
|
||||
});
|
||||
|
||||
// Should try to refresh shares
|
||||
expect(getShareInvitationsMock).toHaveBeenCalled();
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import { AppState } from '../../utils/types';
|
||||
import { Store } from 'redux';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
import createMockReduxStore from '../../utils/testing/createMockReduxStore';
|
||||
import setupGlobalStore from '../../utils/testing/setupGlobalStore';
|
||||
import TestProviderStack from '../testing/TestProviderStack';
|
||||
|
@ -8,7 +8,6 @@ import createMockReduxStore from '../../utils/testing/createMockReduxStore';
|
||||
import setupGlobalStore from '../../utils/testing/setupGlobalStore';
|
||||
import { getActiveMasterKeyId, setEncryptionEnabled, setMasterKeyEnabled } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
import { act, render, screen } from '@testing-library/react-native';
|
||||
import '@testing-library/jest-native/extend-expect';
|
||||
|
||||
interface WrapperProps { }
|
||||
|
||||
|
97
packages/app-mobile/ios/AppDelegate.swift
Normal file
97
packages/app-mobile/ios/AppDelegate.swift
Normal file
@ -0,0 +1,97 @@
|
||||
import Expo
|
||||
import React
|
||||
import ReactAppDependencyProvider
|
||||
|
||||
// Notes:
|
||||
// - UNUserNotificationCenterDelegate is required by @react-native-community/push-notification-ios
|
||||
// - This file is derived from the default React Native and Expo `AppDelegate.swift`.
|
||||
@UIApplicationMain
|
||||
public class AppDelegate: ExpoAppDelegate, UNUserNotificationCenterDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
||||
var reactNativeFactory: RCTReactNativeFactory?
|
||||
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
let delegate = ReactNativeDelegate()
|
||||
let factory = ExpoReactNativeFactory(delegate: delegate)
|
||||
delegate.dependencyProvider = RCTAppDependencyProvider()
|
||||
|
||||
reactNativeDelegate = delegate
|
||||
reactNativeFactory = factory
|
||||
bindReactNativeFactory(factory)
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
factory.startReactNative(
|
||||
withModuleName: "main",
|
||||
in: window,
|
||||
launchOptions: launchOptions)
|
||||
#endif
|
||||
|
||||
// Define UNUserNotificationCenter -- required by @react-native-community/push-notification-ios
|
||||
let center = UNUserNotificationCenter.current();
|
||||
center.delegate = self;
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
// Linking API
|
||||
public override func application(
|
||||
_ app: UIApplication,
|
||||
open url: URL,
|
||||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||||
) -> Bool {
|
||||
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
||||
}
|
||||
|
||||
// Universal Links
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
continue userActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
||||
) -> Bool {
|
||||
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
||||
}
|
||||
|
||||
// Quick actions
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
performActionFor shortcutItem: UIApplicationShortcutItem,
|
||||
completionHandler: @escaping (Bool) -> Void
|
||||
) {
|
||||
RNQuickActionManager.onQuickActionPress(shortcutItem, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
// Notifications with @react-native-community/push-notification-ios
|
||||
// IOS 10+ Required for localNotification event
|
||||
public func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
RNCPushNotificationIOS.didReceive(response);
|
||||
completionHandler();
|
||||
}
|
||||
}
|
||||
|
||||
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
||||
// Extension point for config-plugins
|
||||
|
||||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
||||
// needed to return the correct URL for expo-dev-client.
|
||||
bridge.bundleURL ?? bundleURL()
|
||||
}
|
||||
|
||||
override func bundleURL() -> URL? {
|
||||
#if DEBUG
|
||||
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
||||
#else
|
||||
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#import <Expo/Expo.h>
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import <RNCPushNotificationIOS.h>
|
||||
#import "RNQuickActionManager.h"
|
||||
|
@ -7,11 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
2F4891ED2DDDE04E0089027D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F4891EC2DDDE04E0089027D /* AppDelegate.swift */; };
|
||||
4C036D13E81D8DB9640B0DC1 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF14612B39CE1556A9A31631 /* ExpoModulesProvider.swift */; };
|
||||
4D122473270878D700DE23E8 /* wtf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D122472270878D700DE23E8 /* wtf.swift */; };
|
||||
57317DBFCCF429AEF0A019CB /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C9F257EEF9EAC998DCD8BDEC /* libPods-ShareExtension.a */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
AE152142260F770400217DCB /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AE152141260F770400217DCB /* ShareViewController.m */; };
|
||||
@ -51,14 +49,11 @@
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* JoplinTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JoplinTests.m; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* Joplin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Joplin.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Joplin/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.mm; path = Joplin/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Joplin/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Joplin/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Joplin/main.m; sourceTree = "<group>"; };
|
||||
2F4891EC2DDDE04E0089027D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
4281BC1941ED8712A952DC60 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
4D122471270878D600DE23E8 /* Joplin-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Joplin-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
4D122472270878D700DE23E8 /* wtf.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = wtf.swift; sourceTree = "<group>"; };
|
||||
78131A70125DE0AE4D6BF72E /* Pods-Joplin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.debug.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
800581C1ADC9CA9A7AC1BB75 /* libPods-Joplin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Joplin/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
@ -118,15 +113,12 @@
|
||||
13B07FAE1A68108700A75B9A /* Joplin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2F4891EC2DDDE04E0089027D /* AppDelegate.swift */,
|
||||
AE7945E6259C9AEE00051BE2 /* Joplin.entitlements */,
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
4D122472270878D700DE23E8 /* wtf.swift */,
|
||||
4D122471270878D600DE23E8 /* Joplin-Bridging-Header.h */,
|
||||
);
|
||||
name = Joplin;
|
||||
@ -499,9 +491,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
4D122473270878D700DE23E8 /* wtf.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
2F4891ED2DDDE04E0089027D /* AppDelegate.swift in Sources */,
|
||||
4C036D13E81D8DB9640B0DC1 /* ExpoModulesProvider.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -654,7 +644,9 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
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";
|
||||
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 = 15.6;
|
||||
LD = "";
|
||||
LDPLUSPLUS = "";
|
||||
@ -678,10 +670,7 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
@ -732,7 +721,9 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
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";
|
||||
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 = 15.6;
|
||||
LD = "";
|
||||
LDPLUSPLUS = "";
|
||||
@ -755,10 +746,7 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
@ -1,8 +0,0 @@
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <Expo/Expo.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||
|
||||
@interface AppDelegate : EXAppDelegateWrapper
|
||||
|
||||
@end
|
@ -1,97 +0,0 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
#import <RNCPushNotificationIOS.h>
|
||||
#import "RNQuickActionManager.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
// ===================================================
|
||||
// BEGIN Linking support
|
||||
// ===================================================
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
return [RCTLinkingManager application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// END Linking support
|
||||
// ===================================================
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// BEGIN react-native-quick-actions
|
||||
// ===================================================
|
||||
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded)) completionHandler {
|
||||
[RNQuickActionManager onQuickActionPress:shortcutItem completionHandler:completionHandler];
|
||||
}
|
||||
// ===================================================
|
||||
// END react-native-quick-actions
|
||||
// ===================================================
|
||||
|
||||
|
||||
|
||||
|
||||
// ===================================================
|
||||
// BEGIN react-native-push-notification-ios
|
||||
// ===================================================
|
||||
|
||||
// IOS 10+ Required for localNotification event
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
|
||||
completionHandler();
|
||||
}
|
||||
// IOS 4-10 Required for the localNotification event.
|
||||
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// END react-native-push-notification-ios
|
||||
// ===================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
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 = @{};
|
||||
|
||||
// BEGIN react-native-push-notification-ios
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
center.delegate = self;
|
||||
// END react-native-push-notification-ios
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self bundleURL];
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
@ -1,10 +0,0 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
@ -1,43 +1,40 @@
|
||||
# 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',
|
||||
'require.resolve(
|
||||
"react-native/scripts/react_native_pods.rb",
|
||||
{paths: [process.argv[1]]},
|
||||
)', __dir__]).strip
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
|
||||
# From Expo
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] = '0' if podfile_properties['newArchEnabled'] == 'false'
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
|
||||
install! 'cocoapods',
|
||||
:deterministic_uuids => false
|
||||
|
||||
# Note: it was 13.4 to get @react-native-community/datetimepicker to work but
|
||||
# it's probably not necessary actually. Just needed to upgrade XCode.
|
||||
#
|
||||
# 2021-11-04: Set to 13.0 because it crashes with 12.x
|
||||
# https://github.com/laurent22/joplin/issues/5671
|
||||
#
|
||||
# 2021-12-17: Changed back to 11.0 because after the fix it works with at least
|
||||
# 12.x, and probably 11.0 too, which is the version supported by React Native.
|
||||
platform :ios, min_ios_version_supported
|
||||
prepare_react_native_project!
|
||||
|
||||
linkage = ENV['USE_FRAMEWORKS']
|
||||
if linkage != nil
|
||||
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||
use_frameworks! :linkage => linkage.to_sym
|
||||
end
|
||||
|
||||
target 'Joplin' do
|
||||
use_expo_modules!
|
||||
post_integrate do |installer|
|
||||
begin
|
||||
expo_patch_react_imports!(installer)
|
||||
rescue => e
|
||||
Pod::UI.warn e
|
||||
end
|
||||
|
||||
if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
|
||||
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
|
||||
else
|
||||
config_command = [
|
||||
'npx',
|
||||
'expo-modules-autolinking',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'ios'
|
||||
]
|
||||
end
|
||||
config = use_native_modules!
|
||||
|
||||
config = use_native_modules!(config_command)
|
||||
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
|
File diff suppressed because it is too large
Load Diff
3
packages/app-mobile/ios/Podfile.properties.json
Normal file
3
packages/app-mobile/ios/Podfile.properties.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"newArchEnabled": "false"
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
//
|
||||
// wtf.swift
|
||||
// Joplin
|
||||
//
|
||||
// Created by Laurent on 02/10/2021.
|
||||
// Copyright © 2021 joplinapp.org. All rights reserved.
|
||||
//
|
||||
|
||||
// For whatever reason, this empty file is needed to fix this bug:
|
||||
// https://github.com/facebook/react-native/issues/32242
|
||||
//
|
||||
// Solution:
|
||||
// https://github.com/facebook/react-native/issues/32242#issuecomment-924770488
|
||||
|
||||
import Foundation
|
@ -29,7 +29,7 @@
|
||||
"@joplin/renderer": "~3.4",
|
||||
"@joplin/utils": "~3.4",
|
||||
"@react-native-clipboard/clipboard": "1.14.3",
|
||||
"@react-native-community/datetimepicker": "8.2.0",
|
||||
"@react-native-community/datetimepicker": "8.3.0",
|
||||
"@react-native-community/geolocation": "3.3.0",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
@ -41,16 +41,16 @@
|
||||
"crypto-browserify": "3.12.1",
|
||||
"deprecated-react-native-prop-types": "5.0.0",
|
||||
"events": "3.3.0",
|
||||
"expo": "52.0.46",
|
||||
"expo-av": "15.0.2",
|
||||
"expo-camera": "16.0.18",
|
||||
"expo": "53.0.9",
|
||||
"expo-av": "15.1.4",
|
||||
"expo-camera": "16.1.6",
|
||||
"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.77.2",
|
||||
"react": "19.0.0",
|
||||
"react-native": "0.79.2",
|
||||
"react-native-device-info": "10.14.0",
|
||||
"react-native-dropdownalert": "5.1.0",
|
||||
"react-native-exit-app": "2.0.0",
|
||||
@ -97,16 +97,15 @@
|
||||
"@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",
|
||||
"@react-native/babel-preset": "0.79.2",
|
||||
"@react-native/metro-config": "0.79.2",
|
||||
"@react-native/typescript-config": "0.79.2",
|
||||
"@sqlite.org/sqlite-wasm": "3.46.0-build2",
|
||||
"@testing-library/jest-native": "5.4.3",
|
||||
"@testing-library/react-native": "12.3.3",
|
||||
"@testing-library/react-native": "13.2.0",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/node": "18.19.67",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-redux": "7.1.33",
|
||||
"@types/serviceworker": "0.0.123",
|
||||
"@types/tar-stream": "3.1.3",
|
||||
@ -124,10 +123,10 @@
|
||||
"jsdom": "24.1.3",
|
||||
"nodemon": "3.1.7",
|
||||
"punycode": "2.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native-web": "0.19.13",
|
||||
"react-dom": "19.0.0",
|
||||
"react-native-web": "0.20.0",
|
||||
"react-refresh": "0.16.0",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"react-test-renderer": "19.0.0",
|
||||
"sharp": "0.33.5",
|
||||
"sqlite3": "5.1.6",
|
||||
"timers-browserify": "2.0.12",
|
||||
|
@ -31,14 +31,31 @@ export interface Props {
|
||||
sharedData: SharedData|undefined;
|
||||
}
|
||||
|
||||
export interface BaseNoteScreenComponent {
|
||||
props: Props;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
state: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
setState: (newState: any)=> void;
|
||||
export interface BaseState {
|
||||
note: NoteEntity;
|
||||
lastSavedNote: NoteEntity;
|
||||
newAndNoTitleChangeNoteId: boolean;
|
||||
mode: string;
|
||||
folder: FolderEntity;
|
||||
isLoading: boolean;
|
||||
fromShare: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of old code before rule was applied
|
||||
noteResources: any;
|
||||
readOnly: boolean;
|
||||
noteLastLoadTime: number;
|
||||
}
|
||||
|
||||
scheduleSave(): void;
|
||||
export interface BaseNoteScreenComponent<State extends BaseState = BaseState> {
|
||||
props: Props;
|
||||
state: State;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
setState(state: any): void;
|
||||
|
||||
// To prevent race conditions, scheduleSave takes a snapshot of the
|
||||
// current state. Previously, the delay between calling setState(state) and
|
||||
// this.state getting the new state value could cause the wrong state
|
||||
// to be saved.
|
||||
scheduleSave(currentState: State): void;
|
||||
scheduleFocusUpdate(): void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
attachFile(asset: any, fileType: any): void;
|
||||
@ -49,7 +66,7 @@ interface Shared {
|
||||
noteExists?: (noteId: string)=> Promise<boolean>;
|
||||
handleNoteDeletedWhileEditing_?: (note: NoteEntity)=> Promise<NoteEntity>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
saveNoteButton_press?: (comp: BaseNoteScreenComponent, folderId: string, options: any)=> Promise<void>;
|
||||
saveNoteButton_press?: (comp: BaseNoteScreenComponent, state: BaseState, folderId: string, options: any)=> Promise<void>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
saveOneProperty?: (comp: BaseNoteScreenComponent, name: string, value: any)=> void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@ -97,12 +114,13 @@ shared.handleNoteDeletedWhileEditing_ = async (note: NoteEntity) => {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, folderId: string = null, options: any = null) {
|
||||
shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, state: BaseState, folderId: string = null, options: any = null) {
|
||||
options = { autoTitle: true, ...options };
|
||||
state = { ...comp.state, ...state };
|
||||
|
||||
const releaseMutex = await saveNoteMutex_.acquire();
|
||||
|
||||
let note = { ...comp.state.note };
|
||||
let note = { ...state.note };
|
||||
|
||||
const recreatedNote = await shared.handleNoteDeletedWhileEditing_(note);
|
||||
if (recreatedNote) note = recreatedNote;
|
||||
@ -121,11 +139,11 @@ shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, fold
|
||||
|
||||
const saveOptions = {
|
||||
userSideValidation: true,
|
||||
fields: BaseModel.diffObjectsFields(comp.state.lastSavedNote, note),
|
||||
fields: BaseModel.diffObjectsFields(state.lastSavedNote, note),
|
||||
dispatchOptions: { preserveSelection: true },
|
||||
};
|
||||
|
||||
const hasAutoTitle = comp.state.newAndNoTitleChangeNoteId || (isProvisionalNote && !note.title);
|
||||
const hasAutoTitle = state.newAndNoTitleChangeNoteId || (isProvisionalNote && !note.title);
|
||||
if (hasAutoTitle && options.autoTitle) {
|
||||
note.title = Note.defaultTitle(note.body);
|
||||
if (saveOptions.fields && saveOptions.fields.indexOf('title') < 0) saveOptions.fields.push('title');
|
||||
@ -133,7 +151,7 @@ shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, fold
|
||||
|
||||
const savedNote = 'fields' in saveOptions && !saveOptions.fields.length ? { ...note } : await Note.save(note, saveOptions);
|
||||
|
||||
const stateNote = comp.state.note;
|
||||
const stateNote = state.note;
|
||||
|
||||
// Note was reloaded while being saved.
|
||||
if (!recreatedNote && (!stateNote || stateNote.id !== savedNote.id)) return releaseMutex();
|
||||
@ -169,7 +187,7 @@ shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, fold
|
||||
const updateGeoloc = async () => {
|
||||
const geoNote: NoteEntity = await Note.updateGeolocation(note.id);
|
||||
|
||||
const stateNote = comp.state.note;
|
||||
const stateNote = state.note;
|
||||
if (!stateNote || !geoNote) return;
|
||||
if (stateNote.id !== geoNote.id) return; // Another note has been loaded while geoloc was being retrieved
|
||||
|
||||
@ -183,7 +201,7 @@ shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, fold
|
||||
};
|
||||
|
||||
const modNote = { ...stateNote, ...geoInfo };
|
||||
const modLastSavedNote = { ...comp.state.lastSavedNote, ...geoInfo };
|
||||
const modLastSavedNote = { ...state.lastSavedNote, ...geoInfo };
|
||||
|
||||
comp.setState({ note: modNote, lastSavedNote: modLastSavedNote });
|
||||
};
|
||||
@ -206,7 +224,7 @@ shared.saveOneProperty = async function(comp: BaseNoteScreenComponent, name: str
|
||||
let toSave: any = { id: note.id };
|
||||
toSave[name] = value;
|
||||
toSave = await Note.save(toSave);
|
||||
note[name] = toSave[name];
|
||||
(note as Record<string, unknown>)[name] = toSave[name];
|
||||
|
||||
comp.setState({
|
||||
lastSavedNote: { ...note },
|
||||
@ -220,11 +238,11 @@ shared.noteComponent_change = function(comp: BaseNoteScreenComponent, propName:
|
||||
const newState: any = {};
|
||||
|
||||
const note = { ...comp.state.note };
|
||||
note[propName] = propValue;
|
||||
(note as Record<string, unknown>)[propName] = propValue;
|
||||
newState.note = note;
|
||||
|
||||
comp.setState(newState);
|
||||
comp.scheduleSave();
|
||||
comp.scheduleSave(newState);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
|
Loading…
x
Reference in New Issue
Block a user