Skip to content

Mobile: Upgrade to React Native 0.79 #12337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app-mobile/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
minSdkVersion = 24

compileSdkVersion = 35
targetSdkVersion = 34
targetSdkVersion = 35

ndkVersion = "27.1.12297006"
kotlinVersion = "2.0.21"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
44 changes: 38 additions & 6 deletions packages/app-mobile/android/settings.gradle
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
1 change: 0 additions & 1 deletion packages/app-mobile/components/Dropdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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();
}
Comment on lines -49 to +52
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new @testing-library/react-native version removed *AccessibilityState queries.

fireEvent.press(itemCheckbox);

await waitFor(() => {
expect(itemCheckbox).toHaveAccessibilityState({ checked: finalChecked });
if (finalChecked) {
expect(itemCheckbox).toBeChecked();
} else {
expect(itemCheckbox).not.toBeChecked();
}
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


import pluginServiceSetup from './testUtils/pluginServiceSetup';
import createMockReduxStore from '../../../../utils/testing/createMockReduxStore';
Expand Down
30 changes: 13 additions & 17 deletions packages/app-mobile/components/screens/Note/Note.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -290,7 +286,7 @@ describe('screens/Note', () => {

await openNoteActionsMenu();
const deleteButton = await screen.findByText('Delete');
expect(deleteButton).toBeDisabled();
expect(deleteButton).toHaveProp('disabled', true);

cleanup();
});
Expand Down
18 changes: 9 additions & 9 deletions packages/app-mobile/components/screens/Note/Note.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,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;
Expand Down Expand Up @@ -696,9 +696,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);
};
}

Expand All @@ -709,12 +709,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();
}
Expand Down Expand Up @@ -884,7 +884,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp

void this.refreshResource(resource, newNote.body);

this.scheduleSave();
this.scheduleSave({ ...this.state, note: newNote });

return resource;
}
Expand Down Expand Up @@ -995,7 +995,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() {
Expand Down Expand Up @@ -1429,7 +1429,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 { }

Expand Down
Loading