react-native 0.85.2 → 0.85.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Utilities/HMRClient.js +28 -1
- package/React/Base/RCTVersion.m +1 -1
- package/React/CoreModules/RCTJscSafeUrl+Internal.h +23 -0
- package/React/CoreModules/RCTJscSafeUrl.mm +38 -0
- package/React/CoreModules/RCTRedBox+Internal.h +42 -0
- package/React/CoreModules/RCTRedBox.mm +30 -454
- package/React/CoreModules/RCTRedBox2AnsiParser+Internal.h +22 -0
- package/React/CoreModules/RCTRedBox2AnsiParser.mm +55 -0
- package/React/CoreModules/RCTRedBox2Controller+Internal.h +34 -0
- package/React/CoreModules/RCTRedBox2Controller.mm +764 -0
- package/React/CoreModules/RCTRedBox2ErrorParser+Internal.h +46 -0
- package/React/CoreModules/RCTRedBox2ErrorParser.mm +57 -0
- package/React/CoreModules/RCTRedBoxController+Internal.h +31 -0
- package/React/CoreModules/RCTRedBoxController.mm +447 -0
- package/React/CoreModules/RCTRedBoxHMRClient+Internal.h +26 -0
- package/React/CoreModules/RCTRedBoxHMRClient.mm +125 -0
- package/React/CoreModules/React-CoreModules.podspec +1 -0
- package/React/DevSupport/RCTFrameTimingsObserver.h +24 -0
- package/React/DevSupport/RCTFrameTimingsObserver.mm +298 -0
- package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +40 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorFlags.kt +4 -0
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingSequence.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +127 -26
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +31 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +51 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +56 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +39 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +50 -10
- package/ReactAndroid/src/main/jni/CMakeLists.txt +7 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.cpp +22 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.h +2 -0
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +71 -1
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +16 -1
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +14 -0
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +18 -4
- package/ReactCommon/React-Fabric.podspec +6 -0
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/jsinspector-modern/HostAgent.cpp +36 -0
- package/ReactCommon/jsinspector-modern/HostTarget.cpp +7 -1
- package/ReactCommon/jsinspector-modern/HostTarget.h +25 -0
- package/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +1 -1
- package/ReactCommon/jsinspector-modern/HostTargetTracing.h +4 -4
- package/ReactCommon/jsinspector-modern/InspectorFlags.cpp +12 -0
- package/ReactCommon/jsinspector-modern/InspectorFlags.h +12 -0
- package/ReactCommon/jsinspector-modern/NetworkIOAgent.cpp +1 -1
- package/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +19 -0
- package/ReactCommon/jsinspector-modern/RuntimeAgent.h +7 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +33 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.h +6 -0
- package/ReactCommon/jsinspector-modern/tests/HostTargetTest.cpp +12 -0
- package/ReactCommon/jsinspector-modern/tests/InspectorMocks.h +3 -2
- package/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +1 -0
- package/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.cpp +10 -0
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/CMakeLists.txt +1 -0
- package/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +7 -3
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.cpp +52 -29
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.h +6 -6
- package/ReactCommon/jsinspector-modern/tracing/PerformanceTracerSection.h +113 -0
- package/ReactCommon/jsinspector-modern/tracing/React-jsinspectortracing.podspec +1 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +12 -5
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.cpp +42 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.h +7 -0
- package/ReactCommon/react/debug/CMakeLists.txt +2 -1
- package/ReactCommon/react/debug/React-debug.podspec +7 -1
- package/ReactCommon/react/debug/redbox/AnsiParser.cpp +139 -0
- package/ReactCommon/react/debug/redbox/AnsiParser.h +35 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.cpp +179 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.h +27 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.cpp +171 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.h +40 -0
- package/ReactCommon/react/debug/redbox/tests/AnsiParserTest.cpp +97 -0
- package/ReactCommon/react/debug/redbox/tests/JscSafeUrlTest.cpp +173 -0
- package/ReactCommon/react/debug/redbox/tests/RedBoxErrorParserTest.cpp +107 -0
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +26 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +135 -45
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +12 -2
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +46 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +6 -1
- package/ReactCommon/react/nativemodule/defaults/CMakeLists.txt +1 -0
- package/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp +7 -0
- package/ReactCommon/react/nativemodule/defaults/React-defaultsnativemodule.podspec +1 -0
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +26 -1
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +11 -1
- package/ReactCommon/react/nativemodule/mutationobserver/NativeMutationObserver.h +4 -0
- package/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec +66 -0
- package/ReactCommon/react/performance/timeline/PerformanceObserver.cpp +18 -6
- package/ReactCommon/react/performance/timeline/PerformanceObserver.h +2 -0
- package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +115 -0
- package/ReactCommon/{jsinspector-modern → react/utils}/Base64.h +2 -2
- package/package.json +11 -11
- package/scripts/cocoapods/utils.rb +1 -0
- package/scripts/react_native_pods.rb +1 -0
- package/scripts/replace-rncore-version.js +72 -15
- package/src/private/featureflags/ReactNativeFeatureFlags.js +26 -1
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +6 -1
- package/src/private/setup/setUpDefaultReactNativeEnvironment.js +6 -0
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#import "RCTRedBox2Controller+Internal.h"
|
|
9
|
+
|
|
10
|
+
#import <React/RCTDefines.h>
|
|
11
|
+
#import <React/RCTJSStackFrame.h>
|
|
12
|
+
#import <React/RCTReloadCommand.h>
|
|
13
|
+
#import <React/RCTUtils.h>
|
|
14
|
+
|
|
15
|
+
#include <array>
|
|
16
|
+
|
|
17
|
+
#import "RCTJscSafeUrl+Internal.h"
|
|
18
|
+
#import "RCTRedBox2AnsiParser+Internal.h"
|
|
19
|
+
#import "RCTRedBox2ErrorParser+Internal.h"
|
|
20
|
+
#import "RCTRedBoxHMRClient+Internal.h"
|
|
21
|
+
|
|
22
|
+
// @lint-ignore-every CLANGTIDY clang-diagnostic-switch-default
|
|
23
|
+
// NOTE: clang-diagnostic-switch-default conflicts with clang-diagnostic-switch-enum
|
|
24
|
+
|
|
25
|
+
#if RCT_DEV_MENU
|
|
26
|
+
|
|
27
|
+
#pragma mark - RCTRedBox2Controller
|
|
28
|
+
|
|
29
|
+
// Color Palette (matching LogBoxStyle.js)
|
|
30
|
+
static UIColor *RCTRedBox2BackgroundColor()
|
|
31
|
+
{
|
|
32
|
+
return [UIColor colorWithRed:51.0 / 255 green:51.0 / 255 blue:51.0 / 255 alpha:1.0];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static UIColor *RCTRedBox2ErrorColor()
|
|
36
|
+
{
|
|
37
|
+
return [UIColor colorWithRed:243.0 / 255 green:83.0 / 255 blue:105.0 / 255 alpha:1.0];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static UIColor *RCTRedBox2TextColor(CGFloat opacity)
|
|
41
|
+
{
|
|
42
|
+
return [UIColor colorWithWhite:1.0 alpha:opacity];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
enum class Section : uint8_t { Message, CodeFrame, CallStack, kMaxValue };
|
|
46
|
+
static constexpr size_t kSectionCount = static_cast<size_t>(Section::kMaxValue);
|
|
47
|
+
|
|
48
|
+
struct SectionState {
|
|
49
|
+
bool visible = false;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
static const NSTimeInterval kAutoRetryInterval = 20.0;
|
|
53
|
+
|
|
54
|
+
@implementation RCTRedBox2Controller {
|
|
55
|
+
UITableView *_stackTraceTableView;
|
|
56
|
+
UILabel *_headerTitleLabel;
|
|
57
|
+
UILabel *_errorCategoryLabel;
|
|
58
|
+
NSString *_lastErrorMessage;
|
|
59
|
+
NSArray<RCTJSStackFrame *> *_lastStackTrace;
|
|
60
|
+
NSArray<NSString *> *_customButtonTitles;
|
|
61
|
+
NSArray<RCTRedBox2ButtonPressHandler> *_customButtonHandlers;
|
|
62
|
+
int _lastErrorCookie;
|
|
63
|
+
RCTRedBox2ErrorData *_errorData;
|
|
64
|
+
std::array<SectionState, kSectionCount> _sectionStates;
|
|
65
|
+
NSTimer *_autoRetryTimer;
|
|
66
|
+
NSInteger _autoRetryCountdown;
|
|
67
|
+
UIButton *_reloadButton;
|
|
68
|
+
NSString *_reloadBaseText;
|
|
69
|
+
RCTRedBoxHMRClient *_hmrClient;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (instancetype)initWithCustomButtonTitles:(NSArray<NSString *> *)customButtonTitles
|
|
73
|
+
customButtonHandlers:(NSArray<RCTRedBox2ButtonPressHandler> *)customButtonHandlers
|
|
74
|
+
{
|
|
75
|
+
self = [super init];
|
|
76
|
+
if (self != nullptr) {
|
|
77
|
+
_lastErrorCookie = -1;
|
|
78
|
+
_customButtonTitles = customButtonTitles;
|
|
79
|
+
_customButtonHandlers = customButtonHandlers;
|
|
80
|
+
self.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
81
|
+
}
|
|
82
|
+
return self;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (void)viewDidLoad
|
|
86
|
+
{
|
|
87
|
+
[super viewDidLoad];
|
|
88
|
+
self.view.backgroundColor = RCTRedBox2BackgroundColor();
|
|
89
|
+
|
|
90
|
+
// Header bar (adds itself to self.view)
|
|
91
|
+
UIView *headerBar = [self createHeaderBar];
|
|
92
|
+
|
|
93
|
+
// Footer button bar
|
|
94
|
+
UIView *footerBar = [self createFooterBar];
|
|
95
|
+
|
|
96
|
+
// Stack trace table
|
|
97
|
+
_stackTraceTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
|
98
|
+
_stackTraceTableView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
99
|
+
_stackTraceTableView.delegate = self;
|
|
100
|
+
_stackTraceTableView.dataSource = self;
|
|
101
|
+
_stackTraceTableView.backgroundColor = [UIColor clearColor];
|
|
102
|
+
#if !TARGET_OS_TV
|
|
103
|
+
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
|
104
|
+
#endif
|
|
105
|
+
_stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
|
106
|
+
_stackTraceTableView.bounces = NO;
|
|
107
|
+
[self.view addSubview:_stackTraceTableView];
|
|
108
|
+
|
|
109
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
110
|
+
[_stackTraceTableView.topAnchor constraintEqualToAnchor:headerBar.bottomAnchor],
|
|
111
|
+
[_stackTraceTableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
|
|
112
|
+
[_stackTraceTableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
|
|
113
|
+
[_stackTraceTableView.bottomAnchor constraintEqualToAnchor:footerBar.topAnchor],
|
|
114
|
+
]];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#pragma mark - Header Bar
|
|
118
|
+
|
|
119
|
+
- (UIView *)createHeaderBar
|
|
120
|
+
{
|
|
121
|
+
UIView *headerContainer = [[UIView alloc] init];
|
|
122
|
+
headerContainer.translatesAutoresizingMaskIntoConstraints = NO;
|
|
123
|
+
headerContainer.backgroundColor = RCTRedBox2ErrorColor();
|
|
124
|
+
|
|
125
|
+
_headerTitleLabel = [[UILabel alloc] init];
|
|
126
|
+
_headerTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
127
|
+
_headerTitleLabel.textColor = [UIColor whiteColor];
|
|
128
|
+
_headerTitleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
|
|
129
|
+
_headerTitleLabel.textAlignment = NSTextAlignmentCenter;
|
|
130
|
+
[headerContainer addSubview:_headerTitleLabel];
|
|
131
|
+
|
|
132
|
+
[self.view addSubview:headerContainer];
|
|
133
|
+
|
|
134
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
135
|
+
[headerContainer.topAnchor constraintEqualToAnchor:self.view.topAnchor],
|
|
136
|
+
[headerContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
|
|
137
|
+
[headerContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
|
|
138
|
+
|
|
139
|
+
[_headerTitleLabel.leadingAnchor constraintEqualToAnchor:headerContainer.leadingAnchor constant:12],
|
|
140
|
+
[_headerTitleLabel.trailingAnchor constraintEqualToAnchor:headerContainer.trailingAnchor constant:-12],
|
|
141
|
+
[_headerTitleLabel.bottomAnchor constraintEqualToAnchor:headerContainer.bottomAnchor constant:-12],
|
|
142
|
+
[_headerTitleLabel.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12],
|
|
143
|
+
]];
|
|
144
|
+
|
|
145
|
+
return headerContainer;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#pragma mark - Footer Bar
|
|
149
|
+
|
|
150
|
+
- (UIView *)createFooterBar
|
|
151
|
+
{
|
|
152
|
+
const CGFloat buttonHeight = 48;
|
|
153
|
+
|
|
154
|
+
NSString *reloadText = @"Reload";
|
|
155
|
+
NSString *dismissText = @"Dismiss";
|
|
156
|
+
NSString *copyText = @"Copy";
|
|
157
|
+
|
|
158
|
+
UIButton *dismissButton = [self footerButton:dismissText
|
|
159
|
+
accessibilityIdentifier:@"redbox-dismiss"
|
|
160
|
+
selector:@selector(dismiss)];
|
|
161
|
+
_reloadBaseText = reloadText;
|
|
162
|
+
_reloadButton = [self footerButton:reloadText accessibilityIdentifier:@"redbox-reload" selector:@selector(reload)];
|
|
163
|
+
UIButton *copyButton = [self footerButton:copyText
|
|
164
|
+
accessibilityIdentifier:@"redbox-copy"
|
|
165
|
+
selector:@selector(copyStack)];
|
|
166
|
+
|
|
167
|
+
UIStackView *buttonStackView = [[UIStackView alloc] init];
|
|
168
|
+
buttonStackView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
169
|
+
buttonStackView.axis = UILayoutConstraintAxisHorizontal;
|
|
170
|
+
buttonStackView.distribution = UIStackViewDistributionFillEqually;
|
|
171
|
+
buttonStackView.alignment = UIStackViewAlignmentTop;
|
|
172
|
+
buttonStackView.backgroundColor = RCTRedBox2BackgroundColor();
|
|
173
|
+
|
|
174
|
+
[buttonStackView addArrangedSubview:dismissButton];
|
|
175
|
+
[buttonStackView addArrangedSubview:_reloadButton];
|
|
176
|
+
[buttonStackView addArrangedSubview:copyButton];
|
|
177
|
+
|
|
178
|
+
for (NSUInteger i = 0; i < [_customButtonTitles count]; i++) {
|
|
179
|
+
UIButton *button = [self footerButton:_customButtonTitles[i]
|
|
180
|
+
accessibilityIdentifier:@""
|
|
181
|
+
handler:_customButtonHandlers[i]];
|
|
182
|
+
[buttonStackView addArrangedSubview:button];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Shadow layer above footer
|
|
186
|
+
buttonStackView.layer.shadowColor = [UIColor blackColor].CGColor;
|
|
187
|
+
buttonStackView.layer.shadowOffset = CGSizeMake(0, -2);
|
|
188
|
+
buttonStackView.layer.shadowRadius = 2;
|
|
189
|
+
buttonStackView.layer.shadowOpacity = 0.5;
|
|
190
|
+
|
|
191
|
+
[self.view addSubview:buttonStackView];
|
|
192
|
+
|
|
193
|
+
CGFloat bottomInset = [self bottomSafeViewHeight];
|
|
194
|
+
|
|
195
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
196
|
+
[buttonStackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
|
|
197
|
+
[buttonStackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
|
|
198
|
+
[buttonStackView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
|
|
199
|
+
[buttonStackView.heightAnchor constraintEqualToConstant:buttonHeight + bottomInset],
|
|
200
|
+
]];
|
|
201
|
+
|
|
202
|
+
for (UIButton *btn in buttonStackView.arrangedSubviews) {
|
|
203
|
+
[btn.heightAnchor constraintEqualToConstant:buttonHeight].active = YES;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return buttonStackView;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
- (UIButton *)styledButton:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier
|
|
210
|
+
{
|
|
211
|
+
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
212
|
+
button.accessibilityIdentifier = accessibilityIdentifier;
|
|
213
|
+
button.titleLabel.font = [UIFont systemFontOfSize:14];
|
|
214
|
+
button.titleLabel.textAlignment = NSTextAlignmentCenter;
|
|
215
|
+
button.backgroundColor = RCTRedBox2BackgroundColor();
|
|
216
|
+
[button setTitle:title forState:UIControlStateNormal];
|
|
217
|
+
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
218
|
+
[button setTitleColor:RCTRedBox2TextColor(0.5) forState:UIControlStateHighlighted];
|
|
219
|
+
return button;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
- (UIButton *)footerButton:(NSString *)title
|
|
223
|
+
accessibilityIdentifier:(NSString *)accessibilityIdentifier
|
|
224
|
+
selector:(SEL)selector
|
|
225
|
+
{
|
|
226
|
+
UIButton *button = [self styledButton:title accessibilityIdentifier:accessibilityIdentifier];
|
|
227
|
+
[button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
|
|
228
|
+
return button;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
- (UIButton *)footerButton:(NSString *)title
|
|
232
|
+
accessibilityIdentifier:(NSString *)accessibilityIdentifier
|
|
233
|
+
handler:(RCTRedBox2ButtonPressHandler)handler
|
|
234
|
+
{
|
|
235
|
+
UIButton *button = [self styledButton:title accessibilityIdentifier:accessibilityIdentifier];
|
|
236
|
+
[button addAction:[UIAction actionWithHandler:^(__unused UIAction *action) {
|
|
237
|
+
handler();
|
|
238
|
+
}]
|
|
239
|
+
forControlEvents:UIControlEventTouchUpInside];
|
|
240
|
+
return button;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
- (CGFloat)bottomSafeViewHeight
|
|
244
|
+
{
|
|
245
|
+
#if TARGET_OS_MACCATALYST
|
|
246
|
+
return 0;
|
|
247
|
+
#else
|
|
248
|
+
return RCTKeyWindow().safeAreaInsets.bottom;
|
|
249
|
+
#endif
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
#pragma mark - Error Display
|
|
253
|
+
|
|
254
|
+
- (NSString *)stripAnsi:(NSString *)text
|
|
255
|
+
{
|
|
256
|
+
NSError *error = nil;
|
|
257
|
+
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\x1b\\[[0-9;]*m"
|
|
258
|
+
options:NSRegularExpressionCaseInsensitive
|
|
259
|
+
error:&error];
|
|
260
|
+
return [regex stringByReplacingMatchesInString:text options:0 range:NSMakeRange(0, [text length]) withTemplate:@""];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
- (void)showErrorMessage:(NSString *)message
|
|
264
|
+
withStack:(NSArray<RCTJSStackFrame *> *)stack
|
|
265
|
+
isUpdate:(BOOL)isUpdate
|
|
266
|
+
errorCookie:(int)errorCookie
|
|
267
|
+
{
|
|
268
|
+
// Remove ANSI color codes from the message
|
|
269
|
+
NSString *messageWithoutAnsi = [self stripAnsi:message];
|
|
270
|
+
|
|
271
|
+
BOOL isRootViewControllerPresented = self.presentingViewController != nil;
|
|
272
|
+
// Show if this is a new message, or if we're updating the previous message
|
|
273
|
+
BOOL isNew = !isRootViewControllerPresented && !isUpdate;
|
|
274
|
+
BOOL isUpdateForSameMessage = !isNew &&
|
|
275
|
+
(isRootViewControllerPresented && isUpdate &&
|
|
276
|
+
((errorCookie == -1 && [_lastErrorMessage isEqualToString:messageWithoutAnsi]) ||
|
|
277
|
+
(errorCookie == _lastErrorCookie)));
|
|
278
|
+
if (isNew || isUpdateForSameMessage) {
|
|
279
|
+
_lastStackTrace = stack;
|
|
280
|
+
// message is displayed using UILabel, which is unable to render text of
|
|
281
|
+
// unlimited length, so we truncate it
|
|
282
|
+
_lastErrorMessage = [messageWithoutAnsi substringToIndex:MIN((NSUInteger)10000, messageWithoutAnsi.length)];
|
|
283
|
+
_lastErrorCookie = errorCookie;
|
|
284
|
+
|
|
285
|
+
// Parse the message to extract structure (title, code frame, etc.)
|
|
286
|
+
_errorData = [RCTRedBox2ErrorParser parseErrorMessage:message name:nil componentStack:nil isFatal:YES];
|
|
287
|
+
[self updateSectionVisibility];
|
|
288
|
+
|
|
289
|
+
[_stackTraceTableView reloadData];
|
|
290
|
+
|
|
291
|
+
if (!isRootViewControllerPresented) {
|
|
292
|
+
[RCTKeyWindow().rootViewController presentViewController:self animated:NO completion:nil];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Update all UI from _errorData (view is now guaranteed to be loaded)
|
|
296
|
+
_headerTitleLabel.text = _errorData.isCompileError ? @"Failed to compile" : @"Error";
|
|
297
|
+
[_stackTraceTableView reloadData];
|
|
298
|
+
[_stackTraceTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
|
|
299
|
+
atScrollPosition:UITableViewScrollPositionTop
|
|
300
|
+
animated:NO];
|
|
301
|
+
|
|
302
|
+
[self startAutoRetryIfApplicable];
|
|
303
|
+
[self _startHMRClient];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
- (void)dismiss
|
|
308
|
+
{
|
|
309
|
+
[self stopAutoRetry];
|
|
310
|
+
[self dismissViewControllerAnimated:NO completion:nil];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
- (void)reload
|
|
314
|
+
{
|
|
315
|
+
[self _stopHMRClient];
|
|
316
|
+
[self stopAutoRetry];
|
|
317
|
+
if (_actionDelegate != nil) {
|
|
318
|
+
[_actionDelegate reloadFromRedBoxController:self];
|
|
319
|
+
} else {
|
|
320
|
+
// In bridgeless mode `RCTRedBox` gets deallocated, we need to notify listeners anyway.
|
|
321
|
+
RCTTriggerReloadCommandListeners(@"Redbox");
|
|
322
|
+
[self dismiss];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
#pragma mark - Native HMR Connection
|
|
327
|
+
|
|
328
|
+
- (void)_startHMRClient
|
|
329
|
+
{
|
|
330
|
+
[self _stopHMRClient];
|
|
331
|
+
if (!_bundleURL) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
__weak __typeof(self) weakSelf = self;
|
|
335
|
+
_hmrClient = [[RCTRedBoxHMRClient alloc] initWithBundleURL:_bundleURL
|
|
336
|
+
onFileChange:^{
|
|
337
|
+
[weakSelf reload];
|
|
338
|
+
}];
|
|
339
|
+
[_hmrClient start];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
- (void)_stopHMRClient
|
|
343
|
+
{
|
|
344
|
+
[_hmrClient stop];
|
|
345
|
+
_hmrClient = nil;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#pragma mark - Auto-Retry
|
|
349
|
+
|
|
350
|
+
- (void)startAutoRetryIfApplicable
|
|
351
|
+
{
|
|
352
|
+
[self stopAutoRetry];
|
|
353
|
+
if (!_errorData.isRetryable) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
_autoRetryCountdown = (NSInteger)kAutoRetryInterval;
|
|
357
|
+
[self updateReloadButtonTitle];
|
|
358
|
+
_autoRetryTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
|
|
359
|
+
target:self
|
|
360
|
+
selector:@selector(autoRetryTick)
|
|
361
|
+
userInfo:nil
|
|
362
|
+
repeats:YES];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
- (void)stopAutoRetry
|
|
366
|
+
{
|
|
367
|
+
[_autoRetryTimer invalidate];
|
|
368
|
+
_autoRetryTimer = nil;
|
|
369
|
+
if (_reloadButton) {
|
|
370
|
+
[_reloadButton setTitle:_reloadBaseText forState:UIControlStateNormal];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
- (void)autoRetryTick
|
|
375
|
+
{
|
|
376
|
+
_autoRetryCountdown--;
|
|
377
|
+
if (_autoRetryCountdown <= 0) {
|
|
378
|
+
[self stopAutoRetry];
|
|
379
|
+
[self reload];
|
|
380
|
+
} else {
|
|
381
|
+
[self updateReloadButtonTitle];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
- (void)updateReloadButtonTitle
|
|
386
|
+
{
|
|
387
|
+
NSString *title = [NSString stringWithFormat:@"%@ (%lds)", _reloadBaseText, (long)_autoRetryCountdown];
|
|
388
|
+
[_reloadButton setTitle:title forState:UIControlStateNormal];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
- (void)copyStack
|
|
392
|
+
{
|
|
393
|
+
NSMutableString *fullStackTrace;
|
|
394
|
+
|
|
395
|
+
if (_lastErrorMessage != nil) {
|
|
396
|
+
fullStackTrace = [_lastErrorMessage mutableCopy];
|
|
397
|
+
[fullStackTrace appendString:@"\n\n"];
|
|
398
|
+
} else {
|
|
399
|
+
fullStackTrace = [NSMutableString string];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (RCTJSStackFrame *stackFrame in _lastStackTrace) {
|
|
403
|
+
[fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame.methodName]];
|
|
404
|
+
if (stackFrame.file != nullptr) {
|
|
405
|
+
[fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
#if !TARGET_OS_TV
|
|
409
|
+
UIPasteboard *pb = [UIPasteboard generalPasteboard];
|
|
410
|
+
[pb setString:fullStackTrace];
|
|
411
|
+
#endif
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame
|
|
415
|
+
{
|
|
416
|
+
NSString *file = [RCTJscSafeUrl normalUrlFromJscSafeUrl:stackFrame.file];
|
|
417
|
+
// Strip query string (e.g. ?platform=ios&dev=true) before extracting the filename.
|
|
418
|
+
NSRange queryRange = [file rangeOfString:@"?"];
|
|
419
|
+
if (queryRange.location != NSNotFound) {
|
|
420
|
+
file = [file substringToIndex:queryRange.location];
|
|
421
|
+
}
|
|
422
|
+
NSString *fileName = RCTNilIfNull(file) ? [file lastPathComponent] : @"<unknown file>";
|
|
423
|
+
NSString *lineInfo = [NSString stringWithFormat:@"%@:%lld", fileName, (long long)stackFrame.lineNumber];
|
|
424
|
+
|
|
425
|
+
if (stackFrame.column != 0) {
|
|
426
|
+
lineInfo = [lineInfo stringByAppendingFormat:@":%lld", (long long)stackFrame.column];
|
|
427
|
+
}
|
|
428
|
+
return lineInfo;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
#pragma mark - Section Helpers
|
|
432
|
+
|
|
433
|
+
- (void)updateSectionVisibility
|
|
434
|
+
{
|
|
435
|
+
_sectionStates = {};
|
|
436
|
+
_sectionStates[static_cast<size_t>(Section::Message)].visible = true;
|
|
437
|
+
_sectionStates[static_cast<size_t>(Section::CodeFrame)].visible = _errorData.codeFrame.length > 0;
|
|
438
|
+
_sectionStates[static_cast<size_t>(Section::CallStack)].visible =
|
|
439
|
+
_lastStackTrace.count > 0 && _errorData.codeFrame.length == 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
- (NSInteger)visibleSectionCount
|
|
443
|
+
{
|
|
444
|
+
NSInteger count = 0;
|
|
445
|
+
for (size_t i = 0; i < kSectionCount; i++) {
|
|
446
|
+
if (_sectionStates[i].visible) {
|
|
447
|
+
count++;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return count;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
- (Section)sectionForIndex:(NSInteger)index
|
|
454
|
+
{
|
|
455
|
+
NSInteger visible = 0;
|
|
456
|
+
for (size_t i = 0; i < kSectionCount; i++) {
|
|
457
|
+
if (_sectionStates[i].visible) {
|
|
458
|
+
if (visible == index) {
|
|
459
|
+
return static_cast<Section>(i);
|
|
460
|
+
}
|
|
461
|
+
visible++;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
RCTAssert(NO, @"Invalid section index %ld", (long)index);
|
|
465
|
+
return Section::kMaxValue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
- (NSString *)displayMessage
|
|
469
|
+
{
|
|
470
|
+
return _errorData.message.length > 0 ? [self stripAnsi:_errorData.message] : _lastErrorMessage;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
#pragma mark - TableView DataSource & Delegate
|
|
474
|
+
|
|
475
|
+
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView
|
|
476
|
+
{
|
|
477
|
+
return [self visibleSectionCount];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
481
|
+
{
|
|
482
|
+
if ([self sectionForIndex:section] == Section::CallStack) {
|
|
483
|
+
return static_cast<NSInteger>(_lastStackTrace.count);
|
|
484
|
+
}
|
|
485
|
+
return 1;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
489
|
+
{
|
|
490
|
+
switch ([self sectionForIndex:indexPath.section]) {
|
|
491
|
+
case Section::Message: {
|
|
492
|
+
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"msg-cell"];
|
|
493
|
+
return [self reuseCell:cell forErrorMessage:[self displayMessage]];
|
|
494
|
+
}
|
|
495
|
+
case Section::CodeFrame: {
|
|
496
|
+
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"code-cell"];
|
|
497
|
+
return [self reuseCell:cell forCodeFrame:_errorData];
|
|
498
|
+
}
|
|
499
|
+
case Section::CallStack:
|
|
500
|
+
case Section::kMaxValue:
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
|
504
|
+
NSUInteger index = indexPath.row;
|
|
505
|
+
RCTJSStackFrame *stackFrame = _lastStackTrace[index];
|
|
506
|
+
return [self reuseCell:cell forStackFrame:stackFrame];
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forErrorMessage:(NSString *)message
|
|
510
|
+
{
|
|
511
|
+
if (cell == nullptr) {
|
|
512
|
+
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"msg-cell"];
|
|
513
|
+
cell.backgroundColor = RCTRedBox2BackgroundColor();
|
|
514
|
+
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
515
|
+
|
|
516
|
+
// Error category label (e.g. "Syntax Error", "Uncaught Error")
|
|
517
|
+
_errorCategoryLabel = [[UILabel alloc] init];
|
|
518
|
+
_errorCategoryLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
519
|
+
_errorCategoryLabel.textColor = RCTRedBox2ErrorColor();
|
|
520
|
+
_errorCategoryLabel.font = [UIFont systemFontOfSize:21 weight:UIFontWeightBold];
|
|
521
|
+
_errorCategoryLabel.numberOfLines = 1;
|
|
522
|
+
[cell.contentView addSubview:_errorCategoryLabel];
|
|
523
|
+
|
|
524
|
+
// Error message label
|
|
525
|
+
UILabel *messageLabel = [[UILabel alloc] init];
|
|
526
|
+
messageLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
527
|
+
messageLabel.accessibilityIdentifier = @"redbox-error";
|
|
528
|
+
messageLabel.textColor = [UIColor whiteColor];
|
|
529
|
+
messageLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
|
530
|
+
messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
|
531
|
+
messageLabel.numberOfLines = 0;
|
|
532
|
+
messageLabel.tag = 100;
|
|
533
|
+
[cell.contentView addSubview:messageLabel];
|
|
534
|
+
|
|
535
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
536
|
+
[_errorCategoryLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:15],
|
|
537
|
+
[_errorCategoryLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:12],
|
|
538
|
+
[_errorCategoryLabel.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-12],
|
|
539
|
+
|
|
540
|
+
[messageLabel.topAnchor constraintEqualToAnchor:_errorCategoryLabel.bottomAnchor constant:10],
|
|
541
|
+
[messageLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:12],
|
|
542
|
+
[messageLabel.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-12],
|
|
543
|
+
[messageLabel.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-15],
|
|
544
|
+
]];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
_errorCategoryLabel.text = _errorData.title;
|
|
548
|
+
UILabel *messageLabel = [cell.contentView viewWithTag:100];
|
|
549
|
+
messageLabel.text = message;
|
|
550
|
+
|
|
551
|
+
return cell;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forStackFrame:(RCTJSStackFrame *)stackFrame
|
|
555
|
+
{
|
|
556
|
+
if (cell == nullptr) {
|
|
557
|
+
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
|
|
558
|
+
cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:14];
|
|
559
|
+
cell.textLabel.lineBreakMode = NSLineBreakByCharWrapping;
|
|
560
|
+
cell.textLabel.numberOfLines = 2;
|
|
561
|
+
cell.detailTextLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight];
|
|
562
|
+
cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
|
563
|
+
cell.backgroundColor = [UIColor clearColor];
|
|
564
|
+
cell.selectedBackgroundView = [UIView new];
|
|
565
|
+
cell.selectedBackgroundView.backgroundColor = RCTRedBox2BackgroundColor();
|
|
566
|
+
cell.selectedBackgroundView.layer.cornerRadius = 5;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
cell.textLabel.text = stackFrame.methodName ?: @"(unnamed method)";
|
|
570
|
+
if (stackFrame.file != nullptr) {
|
|
571
|
+
cell.detailTextLabel.text = [self formatFrameSource:stackFrame];
|
|
572
|
+
} else {
|
|
573
|
+
cell.detailTextLabel.text = @"";
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (stackFrame.collapse) {
|
|
577
|
+
cell.textLabel.textColor = RCTRedBox2TextColor(0.4);
|
|
578
|
+
cell.detailTextLabel.textColor = RCTRedBox2TextColor(0.3);
|
|
579
|
+
} else {
|
|
580
|
+
cell.textLabel.textColor = [UIColor whiteColor];
|
|
581
|
+
cell.detailTextLabel.textColor = RCTRedBox2TextColor(0.8);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return cell;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
- (UITableViewCell *)reuseCell:(UITableViewCell *)cell forCodeFrame:(RCTRedBox2ErrorData *)errorData
|
|
588
|
+
{
|
|
589
|
+
if (cell == nullptr) {
|
|
590
|
+
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"code-cell"];
|
|
591
|
+
cell.backgroundColor = [UIColor clearColor];
|
|
592
|
+
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Remove old subviews
|
|
596
|
+
for (UIView *subview in cell.contentView.subviews) {
|
|
597
|
+
[subview removeFromSuperview];
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Code frame container with rounded corners
|
|
601
|
+
UIView *container = [[UIView alloc] init];
|
|
602
|
+
container.translatesAutoresizingMaskIntoConstraints = NO;
|
|
603
|
+
container.backgroundColor = RCTRedBox2BackgroundColor();
|
|
604
|
+
container.layer.cornerRadius = 3;
|
|
605
|
+
container.clipsToBounds = YES;
|
|
606
|
+
[cell.contentView addSubview:container];
|
|
607
|
+
|
|
608
|
+
// Render code frame with ANSI syntax highlighting
|
|
609
|
+
UIFont *codeFont = [UIFont fontWithName:@"Menlo-Regular" size:12];
|
|
610
|
+
NSAttributedString *highlighted = [RCTRedBox2AnsiParser attributedStringFromAnsiText:errorData.codeFrame
|
|
611
|
+
baseFont:codeFont
|
|
612
|
+
baseColor:[UIColor whiteColor]];
|
|
613
|
+
|
|
614
|
+
UILabel *codeLabel = [[UILabel alloc] init];
|
|
615
|
+
codeLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
616
|
+
codeLabel.attributedText = highlighted;
|
|
617
|
+
codeLabel.numberOfLines = 0;
|
|
618
|
+
codeLabel.lineBreakMode = NSLineBreakByClipping;
|
|
619
|
+
|
|
620
|
+
UIScrollView *codeScrollView = [[UIScrollView alloc] init];
|
|
621
|
+
codeScrollView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
622
|
+
codeScrollView.showsHorizontalScrollIndicator = YES;
|
|
623
|
+
codeScrollView.showsVerticalScrollIndicator = NO;
|
|
624
|
+
codeScrollView.bounces = NO;
|
|
625
|
+
[codeScrollView addSubview:codeLabel];
|
|
626
|
+
[container addSubview:codeScrollView];
|
|
627
|
+
|
|
628
|
+
// File name label below the code frame
|
|
629
|
+
UILabel *fileLabel = [[UILabel alloc] init];
|
|
630
|
+
fileLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
631
|
+
NSString *fileName = errorData.codeFrameFileName.lastPathComponent ?: errorData.codeFrameFileName;
|
|
632
|
+
if (errorData.codeFrameRow > 0) {
|
|
633
|
+
fileLabel.text = [NSString
|
|
634
|
+
stringWithFormat:@"%@ (%ld:%ld)", fileName, (long)errorData.codeFrameRow, (long)errorData.codeFrameColumn + 1];
|
|
635
|
+
} else if (fileName.length > 0) {
|
|
636
|
+
fileLabel.text = fileName;
|
|
637
|
+
}
|
|
638
|
+
fileLabel.textColor = RCTRedBox2TextColor(0.5);
|
|
639
|
+
fileLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12];
|
|
640
|
+
fileLabel.textAlignment = NSTextAlignmentCenter;
|
|
641
|
+
[cell.contentView addSubview:fileLabel];
|
|
642
|
+
|
|
643
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
644
|
+
[container.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:5],
|
|
645
|
+
[container.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:10],
|
|
646
|
+
[container.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-10],
|
|
647
|
+
|
|
648
|
+
[codeScrollView.topAnchor constraintEqualToAnchor:container.topAnchor constant:10],
|
|
649
|
+
[codeScrollView.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:10],
|
|
650
|
+
[codeScrollView.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-10],
|
|
651
|
+
[codeScrollView.bottomAnchor constraintEqualToAnchor:container.bottomAnchor constant:-10],
|
|
652
|
+
|
|
653
|
+
[codeLabel.topAnchor constraintEqualToAnchor:codeScrollView.topAnchor],
|
|
654
|
+
[codeLabel.leadingAnchor constraintEqualToAnchor:codeScrollView.leadingAnchor],
|
|
655
|
+
[codeLabel.trailingAnchor constraintEqualToAnchor:codeScrollView.trailingAnchor],
|
|
656
|
+
[codeLabel.bottomAnchor constraintEqualToAnchor:codeScrollView.bottomAnchor],
|
|
657
|
+
[codeLabel.heightAnchor constraintEqualToAnchor:codeScrollView.heightAnchor],
|
|
658
|
+
|
|
659
|
+
[fileLabel.topAnchor constraintEqualToAnchor:container.bottomAnchor constant:10],
|
|
660
|
+
[fileLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:10],
|
|
661
|
+
[fileLabel.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-10],
|
|
662
|
+
[fileLabel.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-10],
|
|
663
|
+
]];
|
|
664
|
+
|
|
665
|
+
return cell;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
- (CGFloat)tableView:(__unused UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
669
|
+
{
|
|
670
|
+
auto section = [self sectionForIndex:indexPath.section];
|
|
671
|
+
if (section == Section::Message || section == Section::CodeFrame) {
|
|
672
|
+
return UITableViewAutomaticDimension;
|
|
673
|
+
}
|
|
674
|
+
return 50;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
- (CGFloat)tableView:(__unused UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
678
|
+
{
|
|
679
|
+
switch ([self sectionForIndex:indexPath.section]) {
|
|
680
|
+
case Section::Message:
|
|
681
|
+
return 100;
|
|
682
|
+
case Section::CodeFrame:
|
|
683
|
+
return 200;
|
|
684
|
+
case Section::CallStack:
|
|
685
|
+
case Section::kMaxValue:
|
|
686
|
+
return 50;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
- (UIView *)sectionHeaderViewWithTitle:(NSString *)title
|
|
691
|
+
{
|
|
692
|
+
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 38)];
|
|
693
|
+
headerView.backgroundColor = [UIColor clearColor];
|
|
694
|
+
|
|
695
|
+
UILabel *label = [[UILabel alloc] init];
|
|
696
|
+
label.translatesAutoresizingMaskIntoConstraints = NO;
|
|
697
|
+
label.text = title;
|
|
698
|
+
label.textColor = [UIColor whiteColor];
|
|
699
|
+
label.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
|
|
700
|
+
[headerView addSubview:label];
|
|
701
|
+
|
|
702
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
703
|
+
[label.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor constant:12],
|
|
704
|
+
[label.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor constant:-12],
|
|
705
|
+
[label.bottomAnchor constraintEqualToAnchor:headerView.bottomAnchor constant:-10],
|
|
706
|
+
]];
|
|
707
|
+
|
|
708
|
+
return headerView;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
- (UIView *)tableView:(__unused UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
|
712
|
+
{
|
|
713
|
+
switch ([self sectionForIndex:section]) {
|
|
714
|
+
case Section::CodeFrame:
|
|
715
|
+
return [self sectionHeaderViewWithTitle:@"Source"];
|
|
716
|
+
case Section::CallStack:
|
|
717
|
+
return [self sectionHeaderViewWithTitle:@"Call Stack"];
|
|
718
|
+
case Section::Message:
|
|
719
|
+
case Section::kMaxValue:
|
|
720
|
+
return nil;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
- (CGFloat)tableView:(__unused UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
|
725
|
+
{
|
|
726
|
+
auto s = [self sectionForIndex:section];
|
|
727
|
+
return (s == Section::CodeFrame || s == Section::CallStack) ? 38 : 0;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
731
|
+
{
|
|
732
|
+
if ([self sectionForIndex:indexPath.section] == Section::CallStack) {
|
|
733
|
+
NSUInteger row = indexPath.row;
|
|
734
|
+
RCTJSStackFrame *stackFrame = _lastStackTrace[row];
|
|
735
|
+
[_actionDelegate redBoxController:self openStackFrameInEditor:stackFrame];
|
|
736
|
+
}
|
|
737
|
+
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
#pragma mark - Key Commands
|
|
741
|
+
|
|
742
|
+
- (NSArray<UIKeyCommand *> *)keyCommands
|
|
743
|
+
{
|
|
744
|
+
return @[
|
|
745
|
+
// Dismiss red box
|
|
746
|
+
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(dismiss)],
|
|
747
|
+
// Reload
|
|
748
|
+
[UIKeyCommand keyCommandWithInput:@"r" modifierFlags:UIKeyModifierCommand action:@selector(reload)],
|
|
749
|
+
// Copy = Cmd-Option C since Cmd-C in the simulator copies the pasteboard from
|
|
750
|
+
// the simulator to the desktop pasteboard.
|
|
751
|
+
[UIKeyCommand keyCommandWithInput:@"c"
|
|
752
|
+
modifierFlags:UIKeyModifierCommand | UIKeyModifierAlternate
|
|
753
|
+
action:@selector(copyStack)],
|
|
754
|
+
];
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
- (BOOL)canBecomeFirstResponder
|
|
758
|
+
{
|
|
759
|
+
return YES;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
@end
|
|
763
|
+
|
|
764
|
+
#endif
|