Skip to content
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

feat: Expo 52 Upgrade #1452

Merged
merged 9 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules/
.expo/
dist/
expo-env.d.ts
npm-debug.*
*.jks
*.p8
Expand Down
6 changes: 3 additions & 3 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { queryClient } from "@queries/queryClient";
import { MaterialDarkTheme, MaterialLightTheme } from "@styles/colors";
import { QueryClientProvider } from "@tanstack/react-query";
import { useAppTheme, useThemeProvider } from "@theme/useAppTheme";

Check warning on line 14 in App.tsx

View workflow job for this annotation

GitHub Actions / lint

'useAppTheme' is defined but never used
import { useCoinbaseWalletListener } from "@utils/coinbaseWallet";
import { converseEventEmitter } from "@utils/events";
import logger from "@utils/logger";
Expand Down Expand Up @@ -77,10 +77,10 @@
}, []);

const showDebugMenu = useCallback(() => {
if (!debugRef.current || !(debugRef.current as any).showDebugMenu) {

Check warning on line 80 in App.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
return;
}
(debugRef.current as any).showDebugMenu();

Check warning on line 83 in App.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
}, []);

useEffect(() => {
Expand Down Expand Up @@ -124,7 +124,7 @@
const AppKeyboardProvider =
Platform.OS === "ios" ? KeyboardProvider : React.Fragment;

export default function AppWithProviders() {

Check warning on line 127 in App.tsx

View workflow job for this annotation

GitHub Actions / lint

Prefer named exports
const colorScheme = useColorScheme();

const paperTheme = useMemo(() => {
Expand All @@ -142,7 +142,7 @@
<ActionSheetProvider>
<ThemeProvider value={{ themeScheme, setThemeContextOverride }}>
<PaperProvider theme={paperTheme}>
<GestureHandlerRootView style={{ flex: 1 }}>

Check warning on line 145 in App.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { flex: 1 }
<BottomSheetModalProvider>
<PortalProvider>
<App />
Expand All @@ -161,16 +161,16 @@
}

const useStyles = () => {
const { theme } = useAppTheme();
// const { theme } = useAppTheme();

return useMemo(
() =>
StyleSheet.create({
safe: {
flex: 1,
backgroundColor: theme.colors.background.surface,
// backgroundColor: theme.colors.background.surface,
},
}),
[theme]
[]
);
};
38 changes: 38 additions & 0 deletions __mocks__/expo-haptics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Jest mock for expo-haptics

/*
During Expo 52 upgrade haptics were failing in tests, so mocked it for now
*/

enum ImpactFeedbackStyle {
Light = "light",
Medium = "medium",
Heavy = "heavy",
Soft = "soft",
Rigid = "rigid",
}

enum NotificationFeedbackType {
Success = "success",
Warning = "warning",
Error = "error",
}

const impactAsync = jest.fn(async (style: ImpactFeedbackStyle) => {});
const notificationAsync = jest.fn(async (type: NotificationFeedbackType) => {});

const MockedExpoHaptics = {
impactAsync,
notificationAsync,
ImpactFeedbackStyle,
NotificationFeedbackType,
};

export {
impactAsync,
notificationAsync,
ImpactFeedbackStyle,
NotificationFeedbackType,
};

export default MockedExpoHaptics;

Check warning on line 38 in __mocks__/expo-haptics.ts

View workflow job for this annotation

GitHub Actions / lint

Prefer named exports
Comment on lines +31 to +38
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Remove named exports to match the original module's pattern

The codebase consistently uses namespace imports (import * as) with expo-haptics, indicating the mock should only have a default export to match the original module's pattern. Remove the named exports and keep only the default export.

  • Remove lines 31-35 containing the named exports
  • Keep only the default export of MockedExpoHaptics
🔗 Analysis chain

Verify export pattern matches the original module.

While the static analysis suggests preferring named exports, the mock should maintain the same export pattern as the original expo-haptics module for consistency.

Run this script to check the original module's export pattern:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the export pattern of the original expo-haptics module

# Search for expo-haptics import statements in the codebase
rg "import.*from ['\"]expo-haptics['\"]" -A 1

Length of output: 546


Script:

#!/bin/bash
# Search for any other import patterns of expo-haptics
# Using a more flexible pattern to catch different import styles
rg "from ['\"]expo-haptics['\"]" --type ts --type tsx

Length of output: 87

🧰 Tools
🪛 GitHub Check: lint

[warning] 38-38:
Prefer named exports

6 changes: 6 additions & 0 deletions __mocks__/expo-image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
During Expo 52 upgrade expo-image was failing in tests, so mocked it for now
*/
import { Image } from "react-native";

export { Image };
2 changes: 1 addition & 1 deletion app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import appBuildNumbers from "./app.json";

const env = process.env as any;

Check warning on line 8 in app.config.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
const isDev = env.EXPO_ENV === "dev";

warnOnce(
isDev && !(process.env as any).EXPO_PUBLIC_DEV_API_URI,

Check warning on line 12 in app.config.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
"\n\n🚧 Running the app without EXPO_PUBLIC_DEV_API_URI setup\n\n"
);

Expand All @@ -36,7 +36,7 @@
? "preview.getconverse.app"
: "getconverse.app";

export default ({ config }: ConfigContext): ExpoConfig => ({

Check warning on line 39 in app.config.ts

View workflow job for this annotation

GitHub Actions / lint

Prefer named exports
...config,
name: isDev ? "Converse DEV" : isPreview ? "Converse PREVIEW" : "Converse",
scheme: isDev ? "converse-dev" : isPreview ? "converse-preview" : "converse",
Expand Down Expand Up @@ -150,7 +150,7 @@
"expo-build-properties",
{
android: {
compileSdkVersion: 34,
compileSdkVersion: 35,
targetSdkVersion: 34,
buildToolsVersion: "34.0.0",
minSdkVersion: 26,
Expand Down
1 change: 1 addition & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"expo": {
"newArchEnabled": false,
"version": "2.1.0",
"ios": {
"buildNumber": "57"
Expand Down
1 change: 0 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ module.exports = {
{
alias: {
"fast-text-encoding": "text-encoding",
// crypto: "react-native-quick-crypto",
crypto: "crypto-browserify",
// This entrypoint mapping is done in @xmtp/user-preferences-bindings-wasm's "exports" in package.json
// but we don't want to enable unstable_enablePackageExports for now in metro.config.js
Expand Down
2 changes: 1 addition & 1 deletion components/AndroidBackAction.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HeaderBackButton } from "@react-navigation/elements";
import { NativeStackNavigationProp } from "@react-navigation/native-stack/lib/typescript/src/types";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { textSecondaryColor } from "@styles/colors";
import { useColorScheme } from "react-native";

Expand Down
17 changes: 8 additions & 9 deletions components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { actionSecondaryColor, textSecondaryColor } from "@styles/colors";
import { AvatarSizes } from "@styles/sizes";
import { getFirstLetterForAvatar } from "@utils/getFirstLetterForAvatar";
import { ImageBackground } from "expo-image";
import { Image } from "expo-image";
import React, { useCallback, useState } from "react";
import {
ColorSchemeName,
ImageStyle,
Platform,
StyleProp,
StyleSheet,
Text,
useColorScheme,
View,
ViewStyle,
} from "react-native";

import Picto from "./Picto/Picto";

export type AvatarProps = {
uri?: string | undefined;
size?: number | undefined;
style?: StyleProp<ImageStyle>;
style?: StyleProp<ViewStyle>;
color?: boolean;
name?: string | undefined;
// Inverts the color of the place holder
Expand Down Expand Up @@ -47,18 +47,17 @@ function Avatar({
setDidError(false);
}, []);
return uri && !didError ? (
<>
<ImageBackground
<View style={[styles.imageContainer, style]}>
<Image
onLoad={handleImageLoad}
onError={handleImageError}
key={`${uri}-${color}-${colorScheme}`}
source={{ uri }}
style={[styles.imageContainer, style]}
imageStyle={styles.image}
style={styles.image}
cachePolicy="memory-disk"
testID="avatar-image"
></ImageBackground>
</>
/>
</View>
) : (
<View
style={StyleSheet.flatten([
Expand Down
3 changes: 2 additions & 1 deletion components/SvgImageUri.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
ImageSourcePropType,
ImageStyle,
StyleProp,
ViewStyle,
} from "react-native";
import { NumberProp, SvgXml } from "react-native-svg";

type Props = {
width?: NumberProp;
height?: NumberProp;
uri: string | null;
style?: StyleProp<ImageStyle>;
style?: StyleProp<ViewStyle & ImageStyle>;
defaultSource?: ImageSourcePropType;
};

Expand Down
7 changes: 6 additions & 1 deletion components/__tests__/Avatar.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import React from "react";
import { measureRenders } from "reassure";

import Avatar from "../Avatar";
import { TestThemeProvider } from "@/design-system/test-utils/renderWithThemeProvider";

jest.setTimeout(600_000);

const TestComponent = ({ uri }: { uri?: string }) => <Avatar uri={uri} />;
const TestComponent = ({ uri }: { uri?: string }) => (
<TestThemeProvider>
<Avatar uri={uri} />
</TestThemeProvider>
);

test("Empty Avatar 10 runs", async () => {
const scenario = async () => {
Expand Down
11 changes: 7 additions & 4 deletions components/__tests__/Avatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { render } from "@testing-library/react-native";
import React from "react";

import Avatar from "../Avatar"; // Adjust the path as needed
import { renderWithThemeProvider } from "@/design-system/test-utils/renderWithThemeProvider";

// Mock the useColorScheme hook to always return 'light' for consistency in tests
jest.mock("react-native/Libraries/Utilities/useColorScheme", () => ({
Expand All @@ -13,25 +14,27 @@ jest.mock("react-native/Libraries/Utilities/useColorScheme", () => ({

describe("Avatar Component", () => {
it("renders correctly with image URI", () => {
const { getByTestId } = render(
const { getByTestId } = renderWithThemeProvider(
<Avatar uri="http://placebacon.net/400/300" size={50} />
);

// Check if the ImageBackground is rendered with the correct URI
const image = getByTestId("avatar-image");
expect(image.props.source[0].uri).toBe("http://placebacon.net/400/300");
expect(image.props.source.uri).toBe("http://placebacon.net/400/300");
});

it("renders correctly with name and without image URI", () => {
const { getByText } = render(<Avatar name="John Doe" size={50} />);
const { getByText } = renderWithThemeProvider(
<Avatar name="John Doe" size={50} />
);

// Check if the first letter of the name is rendered
const letter = getByText("J");
expect(letter).toBeTruthy();
});

it("renders placeholder picto if no image URI or name is provided", () => {
const { getByTestId } = render(<Avatar size={50} />);
const { getByTestId } = renderWithThemeProvider(<Avatar size={50} />);

// Check if the Picto component is rendered
const picto = getByTestId("avatar-placeholder");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// __tests__/Button.test.tsx

import { render, fireEvent } from "@testing-library/react-native";
import { fireEvent } from "@testing-library/react-native";
import React from "react";
import { Platform } from "react-native";

import Button from "../Button/Button";
import { Button } from "./Button";
import { renderWithThemeProvider } from "@/design-system/test-utils/renderWithThemeProvider";

// Mock useColorScheme for consistent testing
jest.mock("react-native/Libraries/Utilities/useColorScheme", () => ({
Expand All @@ -17,7 +16,7 @@ describe("Button Component", () => {
it("renders correctly with primary variant", () => {
Platform.OS = "ios"; // Set the platform to iOS

const { toJSON, getByText } = render(
const { toJSON, getByText } = renderWithThemeProvider(
<Button title="Primary Button" action="primary" />
);

Expand All @@ -32,7 +31,7 @@ describe("Button Component", () => {
Platform.OS = "ios"; // Set the platform to iOS

const onPressMock = jest.fn();
const { getByText } = render(
const { getByText } = renderWithThemeProvider(
<Button
title="Clickable Button"
action="primary"
Expand All @@ -51,7 +50,7 @@ describe("Button Component", () => {
it("renders correctly with danger variant", () => {
Platform.OS = "android"; // Set the platform to Android (or default)

const { toJSON, getByText } = render(
const { toJSON, getByText } = renderWithThemeProvider(
<Button title="Secondary Danger Button" action="danger" />
);

Expand All @@ -65,7 +64,7 @@ describe("Button Component", () => {
it("renders correctly with picto", () => {
Platform.OS = "android";

const { toJSON, getByText } = render(
const { toJSON, getByText } = renderWithThemeProvider(
<Button title="Button with Picto" picto="star" />
);

Expand Down
11 changes: 10 additions & 1 deletion design-system/Icon/Icon.android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useAppTheme } from "@theme/useAppTheme";
import logger from "@utils/logger";

import { IIconName, IIconProps } from "./Icon.types";
import { StyleProp, TextStyle } from "react-native";

export const iconRegistry: Record<
IIconName,
Expand Down Expand Up @@ -94,5 +95,13 @@ export function Icon(props: IIconProps) {
return null;
}

return <MaterialIcons name={iconName} size={size} color={color} {...rest} />;
return (
<MaterialIcons
{...rest}
style={rest.style as StyleProp<TextStyle>}
name={iconName}
size={size}
color={color}
/>
);
}
2 changes: 1 addition & 1 deletion design-system/Icon/Icon.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ColorValue, StyleProp, ViewStyle } from "react-native";
import { ColorValue, StyleProp, TextStyle, ViewStyle } from "react-native";
import { RequireExactlyOne } from "../../types/general";

export type IIconName =
Expand Down
9 changes: 5 additions & 4 deletions design-system/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
GestureResponderEvent,
PressableStateCallbackType,
StyleProp,
TextStyle,
ViewStyle,
} from "react-native";
import { useAppTheme } from "../../theme/useAppTheme";
Expand Down Expand Up @@ -62,7 +63,7 @@ export function IconButton(props: IIconButtonProps) {
);

const iconStyle = useCallback(
({ pressed }: PressableStateCallbackType): StyleProp<ViewStyle> =>
({ pressed }: PressableStateCallbackType): StyleProp<TextStyle> =>
themed(
getIconStyle({
variant,
Expand Down Expand Up @@ -110,13 +111,13 @@ export function IconButton(props: IIconButtonProps) {
onPress={handlePress}
{...rest}
>
{({ pressed }) => {
{({ pressed, hovered }) => {
if (iconName) {
return (
<Icon
picto={iconName}
style={iconStyle({ pressed })}
{...iconProps({ pressed })}
style={iconStyle({ pressed, hovered })}
{...iconProps({ pressed, hovered })}
/>
);
}
Expand Down
Loading
Loading