Skip to content

Commit

Permalink
Merge e5ddb14 into dece451
Browse files Browse the repository at this point in the history
  • Loading branch information
armcknight authored Feb 20, 2025
2 parents dece451 + e5ddb14 commit 5baa4a5
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 59 deletions.
5 changes: 5 additions & 0 deletions Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,14 @@ - (BOOL)application:(UIApplication *)application
uiForm.submitButtonLabel = @"Report that jank";
uiForm.messagePlaceholder
= @"Describe the nature of the jank. Its essence, if you will.";
uiForm.useSentryUser = YES;
};
config.configureTheme = ^(SentryUserFeedbackThemeConfiguration *_Nonnull theme) {
theme.font = [UIFont fontWithName:@"ChalkboardSE-Regular" size:25];
theme.outlineStyle =
[[SentryFormElementOutlineStyle alloc] initWithColor:UIColor.purpleColor
cornerRadius:10
outlineWidth:4];
};
config.onSubmitSuccess = ^(NSDictionary<NSString *, id> *_Nonnull info) {
NSString *name = info[@"name"] ?: @"$shakespearean_insult_name";
Expand Down
8 changes: 3 additions & 5 deletions Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ extension UserFeedbackUITests {
try assertHookMarkersNotExist()

widgetButton.tap()

XCTAssert(nameField.waitForExistence(timeout: 1))
try assertOnlyHookMarkersExist(names: [.onFormOpen])

Expand Down Expand Up @@ -184,7 +183,6 @@ extension UserFeedbackUITests {
try assertHookMarkersNotExist()

widgetButton.tap()

XCTAssert(nameField.waitForExistence(timeout: 1))
try assertOnlyHookMarkersExist(names: [.onFormOpen])

Expand Down Expand Up @@ -248,7 +246,7 @@ extension UserFeedbackUITests {
try assertHookMarkersNotExist()

widgetButton.tap()

XCTAssert(sendButton.waitForExistence(timeout: 1))
try assertOnlyHookMarkersExist(names: [.onFormOpen])

messageTextView.tap()
Expand Down Expand Up @@ -279,7 +277,7 @@ extension UserFeedbackUITests {
try assertHookMarkersNotExist()

widgetButton.tap()

XCTAssert(sendButton.waitForExistence(timeout: 1))
try assertOnlyHookMarkersExist(names: [.onFormOpen])

messageTextView.tap()
Expand Down Expand Up @@ -316,7 +314,7 @@ extension UserFeedbackUITests {
try assertHookMarkersNotExist()

widgetButton.tap()

XCTAssert(sendButton.waitForExistence(timeout: 1))
try assertOnlyHookMarkersExist(names: [.onFormOpen])

messageTextView.tap()
Expand Down
4 changes: 2 additions & 2 deletions Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ extension SentrySDKWrapper {
}

func configureFeedbackForm(config: SentryUserFeedbackFormConfiguration) {
config.useSentryUser = !args.contains("--io.sentry.feedback.dont-use-sentry-user")
config.formTitle = "Jank Report"
config.isEmailRequired = args.contains("--io.sentry.feedback.require-email")
config.isNameRequired = args.contains("--io.sentry.feedback.require-name")
Expand Down Expand Up @@ -181,7 +182,7 @@ extension SentrySDKWrapper {
fontFamily = "ChalkboardSE-Regular"
}
config.fontFamily = fontFamily
config.outlineStyle = .init(outlineColor: .purple)
config.outlineStyle = .init(color: .purple)
config.foreground = .purple
config.background = .init(red: 0.95, green: 0.9, blue: 0.95, alpha: 1)
config.submitBackground = .orange
Expand All @@ -199,7 +200,6 @@ extension SentrySDKWrapper {
return
}

config.useSentryUser = !args.contains("--io.sentry.feedback.dont-use-sentry-user")
config.animations = !args.contains("--io.sentry.feedback.no-animations")
config.useShakeGesture = true
config.showFormForScreenshots = true
Expand Down
2 changes: 1 addition & 1 deletion Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ - (void)captureFeedback:(SentryFeedback *)feedback withScope:(SentryScope *)scop
alwaysAttachStacktrace:NO];
SentryTraceContext *traceContext = [self getTraceStateWithEvent:preparedEvent withScope:scope];
NSArray *attachments = [[self attachmentsForEvent:preparedEvent scope:scope]
arrayByAddingObjectsFromArray:[feedback attachments]];
arrayByAddingObjectsFromArray:[feedback attachmentsForEnvelope]];

[self.transportAdapter sendEvent:preparedEvent
traceContext:traceContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ public class SentryUserFeedbackConfiguration: NSObject {
*/
public var tags: [String: Any]?

/**
* Sets the email and name field text content to `SentryUser.email` and `SentryUser.name`.
* - note: Default: `true`
*/
public var useSentryUser: Bool = true

/**
* Called when the managed feedback form is opened.
* - note: Default: `nil`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import UIKit
public class SentryUserFeedbackFormConfiguration: NSObject {
// MARK: General settings

/**
* Sets the email and name field text content to `SentryUser.email` and `SentryUser.name`.
* - note: Default: `true`
*/
public var useSentryUser: Bool = true

/**
* Displays the Sentry logo inside of the form.
* - note: Default: `true`
Expand Down Expand Up @@ -41,16 +47,10 @@ public class SentryUserFeedbackFormConfiguration: NSObject {

/**
* The label shown next to an input field that is required.
* - note: Default: `"(required)"`
* - note: Default: `"(Required)"`
*/
public var isRequiredLabel: String = "(Required)"

/**
* The message displayed after a successful feedback submission.
* - note: Default: `"Thank you for your report!"`
*/
public var successMessageText: String = "Thank you for your report!"

// MARK: Screenshots

/**
Expand Down Expand Up @@ -124,7 +124,7 @@ public class SentryUserFeedbackFormConfiguration: NSObject {
*/
public var emailPlaceholder: String = "your.email@example.org"

public lazy var emailTextFieldAccessibilityLabel = emailPlaceholder
public lazy var emailTextFieldAccessibilityLabel = "Your email address"

// MARK: Buttons

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ public class SentryUserFeedbackThemeConfiguration: NSObject {
public lazy var fontFamily: String? = nil

/**
* Font for form input elements.
* Font for form input elements and the widget button label.
* - note: Defaults to `UIFont.TextStyle.callout`.
*/
lazy var font = scaledFont(style: .callout)

/**
* Font for main header title of the feedback form.
* Font for the main header title of the feedback form.
* - note: Defaults to `UIFont.TextStyle.title1`.
*/
lazy var headerFont = scaledFont(style: .title1)
Expand Down Expand Up @@ -88,24 +88,18 @@ public class SentryUserFeedbackThemeConfiguration: NSObject {
*/
public var buttonBackground: UIColor = UIColor.clear

/**
* Color used for success-related components (such as text color when feedback is submitted successfully).
* - note: Default light mode: `rgb(38, 141, 117)`; dark mode: `rgb(45, 169, 140)`
*/
public var successColor = UIScreen.main.traitCollection.userInterfaceStyle == .dark ? UIColor(red: 45 / 255, green: 169 / 255, blue: 140 / 255, alpha: 1) : UIColor(red: 38 / 255, green: 141 / 255, blue: 117 / 255, alpha: 1)

/**
* Color used for error-related components (such as text color when there's an error submitting feedback).
* - note: Default light mode: `rgb(223, 51, 56)`; dark mode: `rgb(245, 84, 89)`
*/
public var errorColor = UIScreen.main.traitCollection.userInterfaceStyle == .dark ? UIColor(red: 245 / 255, green: 84 / 255, blue: 89 / 255, alpha: 1) : UIColor(red: 223 / 255, green: 51 / 255, blue: 56 / 255, alpha: 1)

public struct OutlineStyle: Equatable {
@objc public class SentryFormElementOutlineStyle: NSObject {
/**
* Outline color for form inputs.
* - note: Default: The system default of a UITextField outline with borderStyle of .roundedRect.
*/
public var outlineColor = UIColor(white: 204 / 255, alpha: 1)
public var color = UIColor(white: 204 / 255, alpha: 1)

/**
* Outline corner radius for form input elements.
Expand All @@ -119,8 +113,8 @@ public class SentryUserFeedbackThemeConfiguration: NSObject {
*/
public var outlineWidth: CGFloat = 0.5

public init(outlineColor: UIColor = UIColor(white: 204 / 255, alpha: 1), cornerRadius: CGFloat = 5, outlineWidth: CGFloat = 0.5) {
self.outlineColor = outlineColor
@objc public init(color: UIColor = UIColor(white: 204 / 255, alpha: 1), cornerRadius: CGFloat = 5, outlineWidth: CGFloat = 0.5) {
self.color = color
self.cornerRadius = cornerRadius
self.outlineWidth = outlineWidth
}
Expand All @@ -129,17 +123,22 @@ public class SentryUserFeedbackThemeConfiguration: NSObject {
/**
* - note: We need to keep a reference to a default instance of this for comparison purposes later. We don't use the default to give UITextFields a default style, instead, we use `UITextField.BorderStyle.roundedRect` if `SentryUserFeedbackThemeConfiguration.outlineStyle == defaultOutlineStyle`.
*/
let defaultOutlineStyle = OutlineStyle()
let defaultOutlineStyle = SentryFormElementOutlineStyle()

/**
* Options for styling the outline of input elements and buttons in the feedback form.
*/
public lazy var outlineStyle: OutlineStyle = defaultOutlineStyle
public lazy var outlineStyle: SentryFormElementOutlineStyle = defaultOutlineStyle

/**
* Background color to use for text inputs in the feedback form.
*/
public var inputBackground: UIColor = UIColor.secondarySystemBackground

/**
* Background color to use for text inputs in the feedback form.
*/
public var inputForeground: UIColor = UIScreen.main.traitCollection.userInterfaceStyle == .dark ? UIColor.lightText : UIColor.darkText
}

#endif // os(iOS) && !SENTRY_NO_UIKIT
35 changes: 22 additions & 13 deletions Sources/Swift/Integrations/UserFeedback/SentryFeedback.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,41 @@ import Foundation

@objcMembers
class SentryFeedback: NSObject {
enum Source: String {
@objc enum SentryFeedbackSource: Int, CustomStringConvertible {
public var description: String {
switch self {
case .widget: return "widget"
case .custom: return "custom"
}
}

case widget
case custom
}

var name: String?
var email: String?
var message: String
var source: Source
var source: SentryFeedbackSource
let eventId: SentryId

/// PNG data for the screenshot image
var screenshot: Data?
/// Data objects for any attachments. Currently the web UI only supports showing one attached image, like for a screenshot.
private var attachments: [Data]?

/// The event id that this feedback is associated with, like a crash report.
var associatedEventId: String?
var associatedEventId: SentryId?

/// - parameter screenshot Image encoded as PNG data.
init(message: String, name: String?, email: String?, source: Source = .widget, associatedEventId: String? = nil, screenshot: Data? = nil) {
/// - parameters:
/// - associatedEventId The ID for an event you'd like associated with the feedback.
/// - attachments Data objects for any attachments. Currently the web UI only supports showing one attached image, like for a screenshot.
@objc init(message: String, name: String?, email: String?, source: SentryFeedbackSource = .widget, associatedEventId: SentryId? = nil, attachments: [Data]? = nil) {
self.eventId = SentryId()
self.name = name
self.email = email
self.message = message
self.source = source
self.associatedEventId = associatedEventId
self.screenshot = screenshot
self.attachments = attachments
super.init()
}
}
Expand All @@ -45,9 +54,9 @@ extension SentryFeedback: SentrySerializable {
dict["contact_email"] = email
}
if let associatedEventId = associatedEventId {
dict["associated_event_id"] = associatedEventId
dict["associated_event_id"] = associatedEventId.sentryIdString
}
dict["source"] = source.rawValue
dict["source"] = source.description

return dict
}
Expand All @@ -66,7 +75,7 @@ extension SentryFeedback {
if let email = email {
dict["email"] = email
}
if let screenshot = screenshot {
if let screenshot = attachments {
dict["attachments"] = [screenshot]
}
return dict
Expand All @@ -75,9 +84,9 @@ extension SentryFeedback {
/**
* - note: Currently there is only a single attachment possible, for the screenshot, of which there can be only one.
*/
func attachments() -> [Attachment] {
func attachmentsForEnvelope() -> [Attachment] {
var items = [Attachment]()
if let screenshot = screenshot {
if let screenshot = attachments?.first {
items.append(Attachment(data: screenshot, filename: "screenshot.png", contentType: "application/png"))
}
return items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class SentryUserFeedbackFormViewModel: NSObject {
field.delegate = controller
field.autocapitalizationType = .words
field.returnKeyType = .done
if config.useSentryUser {
if config.formConfig.useSentryUser {
field.text = sentry_getCurrentUser()?.name
}
return field
Expand All @@ -91,7 +91,7 @@ class SentryUserFeedbackFormViewModel: NSObject {
field.keyboardType = .emailAddress
field.autocapitalizationType = .none
field.returnKeyType = .done
if config.useSentryUser {
if config.formConfig.useSentryUser {
field.text = sentry_getCurrentUser()?.email
}
return field
Expand Down Expand Up @@ -336,19 +336,22 @@ extension SentryUserFeedbackFormViewModel {
[fullNameTextField, emailTextField].forEach {
$0.font = config.theme.font
$0.adjustsFontForContentSizeCategory = true
$0.textColor = config.theme.inputForeground
if config.theme.outlineStyle == config.theme.defaultOutlineStyle {
$0.borderStyle = .roundedRect
} else {
$0.layer.cornerRadius = config.theme.outlineStyle.cornerRadius
$0.layer.borderWidth = config.theme.outlineStyle.outlineWidth
$0.layer.borderColor = config.theme.outlineStyle.outlineColor.cgColor
$0.layer.borderColor = config.theme.outlineStyle.color.cgColor
}
}

[fullNameTextField, emailTextField, messageTextView].forEach {
$0.backgroundColor = config.theme.inputBackground
}

messageTextView.textColor = config.theme.inputForeground

[fullNameLabel, emailLabel, messageLabel].forEach {
$0.font = config.theme.titleFont
$0.adjustsFontForContentSizeCategory = true
Expand All @@ -362,7 +365,7 @@ extension SentryUserFeedbackFormViewModel {
[submitButton, removeScreenshotButton, cancelButton, messageTextView].forEach {
$0.layer.cornerRadius = config.theme.outlineStyle.cornerRadius
$0.layer.borderWidth = config.theme.outlineStyle.outlineWidth
$0.layer.borderColor = config.theme.outlineStyle.outlineColor.cgColor
$0.layer.borderColor = config.theme.outlineStyle.color.cgColor
}

[removeScreenshotButton, cancelButton].forEach {
Expand Down Expand Up @@ -453,7 +456,11 @@ extension SentryUserFeedbackFormViewModel {
}

func feedbackObject() -> SentryFeedback {
SentryFeedback(message: messageTextView.text, name: fullNameTextField.text, email: emailTextField.text, screenshot: screenshotImageView.image?.pngData())
var attachmentDatas: [Data]?
if let image = screenshotImageView.image, let data = image.pngData() {
attachmentDatas = [data]
}
return SentryFeedback(message: messageTextView.text, name: fullNameTextField.text, email: emailTextField.text, attachments: attachmentDatas)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,10 @@ class SentryUserFeedbackWidgetButtonView: UIView {

if UIScreen.main.traitCollection.userInterfaceStyle == .dark {
lozengeLayer.fillColor = config.darkTheme.background.cgColor
lozengeLayer.strokeColor = config.darkTheme.outlineStyle.outlineColor.cgColor
lozengeLayer.strokeColor = config.darkTheme.outlineStyle.color.cgColor
} else {
lozengeLayer.fillColor = config.theme.background.cgColor
lozengeLayer.strokeColor = config.theme.outlineStyle.outlineColor.cgColor
lozengeLayer.strokeColor = config.theme.outlineStyle.color.cgColor
}

let iconSizeDifference = (scaledIconSize - svgSize) / 2
Expand Down
Loading

0 comments on commit 5baa4a5

Please sign in to comment.