Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fe3dc15
Disable bouncing
antonis Jan 30, 2025
72eef2d
Add modal ui appearance
antonis Jan 30, 2025
ccc808b
Update snapshot tests
antonis Jan 30, 2025
fd47fd2
Fix bottom margin
antonis Jan 30, 2025
dcc5d3b
Merge branch 'feedback-ui' into antonis/feedback-modal-ui
antonis Jan 31, 2025
9ecd8a2
Fix sheet height
antonis Jan 31, 2025
05f94f8
Remove extra modal border
antonis Jan 31, 2025
d88a599
Do not expose modal styles
antonis Jan 31, 2025
ce1de86
Animate background color
antonis Jan 31, 2025
a7a4e56
Avoid keyboard in modal
antonis Jan 31, 2025
8779886
Merge branch 'feedback-ui' into antonis/feedback-modal-ui
antonis Feb 3, 2025
ae80f7d
Merge branch 'feedback-ui' into antonis/feedback-modal-ui
antonis Feb 7, 2025
488658e
Merge branch 'feedback-ui' into antonis/feedback-modal-ui
antonis Feb 10, 2025
c4d502e
Use Image Picker interface matching `expo-image-picker` and `react-na…
antonis Feb 11, 2025
1b74b45
Update samples to pass the ImagePicker library implementation
antonis Feb 11, 2025
1d8a0db
Merge branch 'feedback-ui' into antonis/feedback-ui-imagepicker-integ…
antonis Feb 11, 2025
a0f4a77
Get image data from uri
antonis Feb 11, 2025
357dea8
Add early return and dev note
antonis Feb 11, 2025
192220b
Adds tests
antonis Feb 11, 2025
c3991ff
Adds sample expo plugin configuration
antonis Feb 11, 2025
91e96de
Merge branch 'feedback-ui' into antonis/feedback-ui-imagepicker-integ…
antonis Feb 11, 2025
6666cf6
Update media type for expo
antonis Feb 12, 2025
d1e5107
Update media type for rn
antonis Feb 12, 2025
d780fc1
Add native implementation for getDataFromUri
antonis Feb 13, 2025
85dff80
Bumped to the latest react-native-image-picker version 8
antonis Feb 13, 2025
e92fea6
Add missing null in return type
antonis Feb 13, 2025
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
Prev Previous commit
Next Next commit
Use Image Picker interface matching expo-image-picker and `react-na…
…tive-image-picker`
  • Loading branch information
antonis committed Feb 11, 2025
commit c4d502edde307d60f60323078b1eddb7c135c312
43 changes: 35 additions & 8 deletions packages/core/src/js/feedback/FeedbackForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
import { sentryLogo } from './branding';
import { defaultConfiguration } from './defaults';
import defaultStyles from './FeedbackForm.styles';
import type { FeedbackFormProps, FeedbackFormState, FeedbackFormStyles,FeedbackGeneralConfiguration, FeedbackTextConfiguration } from './FeedbackForm.types';
import { isValidEmail } from './utils';
import type { FeedbackFormProps, FeedbackFormState, FeedbackFormStyles, FeedbackGeneralConfiguration, FeedbackTextConfiguration, ImagePickerConfiguration } from './FeedbackForm.types';
import { base64ToUint8Array, isValidEmail } from './utils';

/**
* @beta
Expand Down Expand Up @@ -100,12 +100,38 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
}
};

public onScreenshotButtonPress: () => void = () => {
public onScreenshotButtonPress: () => void = async () => {
if (!this.state.filename && !this.state.attachment) {
const { onAddScreenshot } = { ...defaultConfiguration, ...this.props };
onAddScreenshot((filename: string, attachement: Uint8Array) => {
this.setState({ filename, attachment: attachement });
});
const imagePickerConfiguration: ImagePickerConfiguration = this.props;
if (imagePickerConfiguration.imagePicker && imagePickerConfiguration.imagePicker.launchImageLibraryAsync) {
// expo-image-picker library is available
const result = await imagePickerConfiguration.imagePicker.launchImageLibraryAsync({
mediaTypes: ['images'], base64: true
});
if (!result.canceled) {
const filename = result.assets[0].fileName;
const attachement = base64ToUint8Array(result.assets[0].base64);
this.setState({ filename, attachment: attachement });
}
} else if (imagePickerConfiguration.imagePicker && imagePickerConfiguration.imagePicker.launchImageLibrary) {
// react-native-image-picker library is available
const result = await imagePickerConfiguration.imagePicker.launchImageLibrary({
mediaType: 'photo', includeBase64: true
});
if (!result.didCancel && !result.errorCode) {
const filename = result.assets[0].fileName;
const attachement = base64ToUint8Array(result.assets[0].base64);
this.setState({ filename, attachment: attachement });
}
} else {
logger.warn('No image picker library found. Please provide an image picker library to use this feature.');

// Defaulting to the onAddScreenshot callback
const { onAddScreenshot } = { ...defaultConfiguration, ...this.props };
onAddScreenshot((filename: string, attachement: Uint8Array) => {
this.setState({ filename, attachment: attachement });
});
}
} else {
this.setState({ filename: undefined, attachment: undefined });
}
Expand All @@ -118,6 +144,7 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
const { name, email, description } = this.state;
const { onFormClose } = this.props;
const config: FeedbackGeneralConfiguration = this.props;
const imagePickerConfiguration: ImagePickerConfiguration = this.props;
const text: FeedbackTextConfiguration = this.props;
const styles: FeedbackFormStyles = { ...defaultStyles, ...this.props.styles };
const onCancel = (): void => {
Expand Down Expand Up @@ -191,7 +218,7 @@ export class FeedbackForm extends React.Component<FeedbackFormProps, FeedbackFor
onChangeText={(value) => this.setState({ description: value })}
multiline
/>
{config.enableScreenshot && (
{(config.enableScreenshot || imagePickerConfiguration.imagePicker) && (
<TouchableOpacity style={styles.screenshotButton} onPress={this.onScreenshotButtonPress}>
<Text style={styles.screenshotText}>
{!this.state.filename && !this.state.attachment
Expand Down
45 changes: 44 additions & 1 deletion packages/core/src/js/feedback/FeedbackForm.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';
/**
* The props for the feedback form
*/
export interface FeedbackFormProps extends FeedbackGeneralConfiguration, FeedbackTextConfiguration, FeedbackCallbacks {
export interface FeedbackFormProps
extends FeedbackGeneralConfiguration,
FeedbackTextConfiguration,
FeedbackCallbacks,
ImagePickerConfiguration {
styles?: FeedbackFormStyles;
}

Expand Down Expand Up @@ -187,6 +191,45 @@ export interface FeedbackCallbacks {
onFormSubmitted?: () => void;
}

/**
* Image Picker configuration interfact matching `expo-image-picker` and `react-native-image-picker`
*/
export interface ImagePickerConfiguration {
imagePicker?: ImagePicker;
}

interface ExpoImagePickerResponse {
canceled?: boolean;
assets?: ImagePickerAsset[];
}

interface ReactNativeImagePickerResponse {
didCancel?: boolean;
errorCode?: string;
assets?: ImagePickerAsset[];
}

interface ImagePickerAsset {
fileName?: string;
base64?: string;
}

interface ExpoImageLibraryOptions {
mediaTypes?: any[];
base64?: boolean;
}

interface ReactNativeImageLibraryOptions {
mediaType: any;
includeBase64?: boolean;
}

interface ImagePicker {
launchImageLibraryAsync?: (options?: ExpoImageLibraryOptions) => Promise<ExpoImagePickerResponse>;

launchImageLibrary?: (options: ReactNativeImageLibraryOptions) => Promise<ReactNativeImagePickerResponse>;
}

/**
* The styles for the feedback form
*/
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/js/feedback/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,33 @@ export const isValidEmail = (email: string): boolean => {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email);
};

/* eslint-disable no-bitwise */
export const base64ToUint8Array = (base64?: string): Uint8Array | undefined => {
if (!base64) return undefined;

const cleanedBase64 = base64.replace(/^data:.*;base64,/, ''); // Remove any prefix before the base64 string
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const bytes: number[] = [];

let buffer = 0;
let bits = 0;

for (const char of cleanedBase64) {
if (char === '=') break;

const value = chars.indexOf(char); // Validate each character
if (value === -1) return undefined;

buffer = (buffer << 6) | value; // Shift 6 bits to the left and add the value
bits += 6;

if (bits >= 8) {
// Add a byte when we have 8 or more bits
bits -= 8;
bytes.push((buffer >> bits) & 0xff);
}
}

return new Uint8Array(bytes);
};
34 changes: 21 additions & 13 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14686,6 +14686,26 @@ __metadata:
languageName: node
linkType: hard

"expo-image-loader@npm:~5.0.0":
version: 5.0.0
resolution: "expo-image-loader@npm:5.0.0"
peerDependencies:
expo: "*"
checksum: 7741b4b926124a1f85e51b0b8c8c9adc37fccf0654eaa0e715cb55cffc716bdc149c0421120f9bec69a69643d3328ec8562899b4aa37a0fdcecfb6fe3cf6f985
languageName: node
linkType: hard

"expo-image-picker@npm:~16.0.5":
version: 16.0.5
resolution: "expo-image-picker@npm:16.0.5"
dependencies:
expo-image-loader: ~5.0.0
peerDependencies:
expo: "*"
checksum: 8391455480d07a2fa785a0246b8eb892d393e64173f4663d77ee26bc497a0f52d9e1690b0fa04094d03ada8cc78aa5477505d870bdcbdb05d5c48c791ddac168
languageName: node
linkType: hard

"expo-keep-awake@npm:~14.0.1":
version: 14.0.1
resolution: "expo-keep-awake@npm:14.0.1"
Expand Down Expand Up @@ -22988,18 +23008,6 @@ __metadata:
languageName: node
linkType: hard

"react-native-quick-base64@npm:^2.1.2":
version: 2.1.2
resolution: "react-native-quick-base64@npm:2.1.2"
dependencies:
base64-js: ^1.5.1
peerDependencies:
react: "*"
react-native: "*"
checksum: 46f3b26f48b26978686b0c043336220d681e6a02af5abcf3eb4ab7b9216251d1eb2fac5c559e984d963e93f54bd9f323651daac09762196815558abbd551729b
languageName: node
linkType: hard

"react-native-reanimated@npm:3.16.7":
version: 3.16.7
resolution: "react-native-reanimated@npm:3.16.7"
Expand Down Expand Up @@ -24599,6 +24607,7 @@ __metadata:
"@types/react": ~18.3.12
expo: ^52.0.0
expo-constants: ~17.0.3
expo-image-picker: ~16.0.5
expo-linking: ~7.0.2
expo-router: ~4.0.5
expo-status-bar: ~2.0.0
Expand Down Expand Up @@ -24695,7 +24704,6 @@ __metadata:
react-native: 0.77.0
react-native-gesture-handler: ^2.22.1
react-native-image-picker: ^7.2.2
react-native-quick-base64: ^2.1.2
react-native-reanimated: 3.16.7
react-native-safe-area-context: 5.2.0
react-native-screens: 4.6.0
Expand Down