Skip to content

Commit

Permalink
Merge 2135c96 into 686b3bc
Browse files Browse the repository at this point in the history
  • Loading branch information
antonis authored Feb 17, 2025
2 parents 686b3bc + 2135c96 commit d1a10a1
Show file tree
Hide file tree
Showing 31 changed files with 3,515 additions and 1 deletion.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@
## Unreleased

### Features

- User Feedback Widget Beta ([#4435](https://github.com/getsentry/sentry-react-native/pull/4435))

To collect user feedback from inside your application call `Sentry.showFeedbackWidget()` or add the `FeedbackWidget` component.

```jsx
import { FeedbackWidget } from "@sentry/react-native";
...
<FeedbackWidget/>
```

### Fixes

- Various crashes and issues of Session Replay on Android. See the Android SDK version bump for more details. ([#4529](https://github.com/getsentry/sentry-react-native/pull/4529))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.net.Uri;
import android.util.SparseIntArray;
import androidx.core.app.FrameMetricsAggregator;
import androidx.fragment.app.FragmentActivity;
Expand Down Expand Up @@ -72,6 +73,7 @@
import io.sentry.vendor.Base64;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
Expand Down Expand Up @@ -970,6 +972,39 @@ public String fetchNativePackageName() {
return packageInfo.packageName;
}

public void getDataFromUri(String uri, Promise promise) {
try {
Uri contentUri = Uri.parse(uri);
try (InputStream is =
getReactApplicationContext().getContentResolver().openInputStream(contentUri)) {
if (is == null) {
String msg = "File not found for uri: " + uri;
logger.log(SentryLevel.ERROR, msg);
promise.reject(new Exception(msg));
return;
}

ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len;
while ((len = is.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
byte[] byteArray = byteBuffer.toByteArray();
WritableArray jsArray = Arguments.createArray();
for (byte b : byteArray) {
jsArray.pushInt(b & 0xFF);
}
promise.resolve(jsArray);
}
} catch (IOException e) {
String msg = "Error reading uri: " + uri + ": " + e.getMessage();
logger.log(SentryLevel.ERROR, msg);
promise.reject(new Exception(msg));
}
}

public void crashedLastRun(Promise promise) {
promise.resolve(Sentry.isCrashedLastRun());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,9 @@ public void crashedLastRun(Promise promise) {
public void getNewScreenTimeToDisplay(Promise promise) {
this.impl.getNewScreenTimeToDisplay(promise);
}

@Override
public void getDataFromUri(String uri, Promise promise) {
this.impl.getDataFromUri(uri, promise);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ public String fetchNativePackageName() {
return this.impl.fetchNativePackageName();
}

@ReactMethod
public void getDataFromUri(String uri, Promise promise) {
this.impl.getDataFromUri(uri, promise);
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap fetchNativeStackFramesBy(ReadableArray instructionsAddr) {
// Not used on Android
Expand Down
29 changes: 29 additions & 0 deletions packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,35 @@ - (NSDictionary *)fetchNativeStackFramesBy:(NSArray<NSNumber *> *)instructionsAd
#endif
}

RCT_EXPORT_METHOD(getDataFromUri
: (NSString *_Nonnull)uri resolve
: (RCTPromiseResolveBlock)resolve rejecter
: (RCTPromiseRejectBlock)reject)
{
#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST
NSURL *fileURL = [NSURL URLWithString:uri];
if (![fileURL isFileURL]) {
reject(@"SentryReactNative", @"The provided URI is not a valid file:// URL", nil);
return;
}
NSError *error = nil;
NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:0 error:&error];
if (error || !fileData) {
reject(@"SentryReactNative", @"Failed to read file data", error);
return;
}
NSMutableArray *byteArray = [NSMutableArray arrayWithCapacity:fileData.length];
const unsigned char *bytes = (const unsigned char *)fileData.bytes;

for (NSUInteger i = 0; i < fileData.length; i++) {
[byteArray addObject:@(bytes[i])];
}
resolve(byteArray);
#else
resolve(nil);
#endif
}

RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, getCurrentReplayId)
{
#if SENTRY_TARGET_REPLAY_SUPPORTED
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/js/NativeRNSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface Spec extends TurboModule {
captureReplay(isHardCrash: boolean): Promise<string | undefined | null>;
getCurrentReplayId(): string | undefined | null;
crashedLastRun(): Promise<boolean | undefined | null>;
getDataFromUri(uri: string): Promise<number[]>;
}

export type NativeStackFrame = {
Expand Down
111 changes: 111 additions & 0 deletions packages/core/src/js/feedback/FeedbackWidget.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { ViewStyle } from 'react-native';

import type { FeedbackWidgetStyles } from './FeedbackWidget.types';

const PURPLE = 'rgba(88, 74, 192, 1)';
const FORGROUND_COLOR = '#2b2233';
const BACKROUND_COLOR = '#ffffff';
const BORDER_COLOR = 'rgba(41, 35, 47, 0.13)';

const defaultStyles: FeedbackWidgetStyles = {
container: {
flex: 1,
padding: 20,
backgroundColor: BACKROUND_COLOR,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
textAlign: 'left',
flex: 1,
color: FORGROUND_COLOR,
},
label: {
marginBottom: 4,
fontSize: 16,
color: FORGROUND_COLOR,
},
input: {
height: 50,
borderColor: BORDER_COLOR,
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
marginBottom: 15,
fontSize: 16,
color: FORGROUND_COLOR,
},
textArea: {
height: 100,
textAlignVertical: 'top',
color: FORGROUND_COLOR,
},
screenshotButton: {
backgroundColor: '#eee',
padding: 15,
borderRadius: 5,
marginBottom: 20,
alignItems: 'center',
},
screenshotText: {
color: '#333',
fontSize: 16,
},
submitButton: {
backgroundColor: PURPLE,
paddingVertical: 15,
borderRadius: 5,
alignItems: 'center',
marginBottom: 10,
},
submitText: {
color: BACKROUND_COLOR,
fontSize: 18,
},
cancelButton: {
paddingVertical: 15,
alignItems: 'center',
},
cancelText: {
color: FORGROUND_COLOR,
fontSize: 16,
},
titleContainer: {
flexDirection: 'row',
width: '100%',
},
sentryLogo: {
width: 40,
height: 40,
},
};

export const modalWrapper: ViewStyle = {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
};

export const modalBackground: ViewStyle = {
flex: 1,
justifyContent: 'flex-end',
};

export const modalSheetContainer: ViewStyle = {
backgroundColor: '#ffffff',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
overflow: 'hidden',
alignSelf: 'stretch',
height: '92%',
shadowColor: '#000',
shadowOffset: { width: 0, height: -3 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 5,
};

export default defaultStyles;
Loading

0 comments on commit d1a10a1

Please sign in to comment.