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 "ABI48_0_0RCTRedBoxExtraDataViewController.h" 9 10@interface ABI48_0_0RCTRedBoxExtraDataCell : UITableViewCell 11 12@property (nonatomic, strong) UILabel *keyLabel; 13@property (nonatomic, strong) UILabel *valueLabel; 14 15@end 16 17@implementation ABI48_0_0RCTRedBoxExtraDataCell 18 19- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 20{ 21 if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 22 self.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1]; 23 UILayoutGuide *contentLayout = self.contentView.layoutMarginsGuide; 24 25 self.keyLabel = [UILabel new]; 26 [self.contentView addSubview:self.keyLabel]; 27 28 self.keyLabel.translatesAutoresizingMaskIntoConstraints = NO; 29 [self.keyLabel.leadingAnchor constraintEqualToAnchor:contentLayout.leadingAnchor].active = YES; 30 [self.keyLabel.topAnchor constraintEqualToAnchor:contentLayout.topAnchor].active = YES; 31 [self.keyLabel.bottomAnchor constraintEqualToAnchor:contentLayout.bottomAnchor].active = YES; 32 [self.keyLabel.widthAnchor constraintEqualToAnchor:contentLayout.widthAnchor multiplier:0.3].active = YES; 33 34 self.keyLabel.textColor = [UIColor whiteColor]; 35 self.keyLabel.numberOfLines = 0; 36 self.keyLabel.lineBreakMode = NSLineBreakByWordWrapping; 37 self.keyLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; 38 self.valueLabel = [UILabel new]; 39 [self.contentView addSubview:self.valueLabel]; 40 41 self.valueLabel.translatesAutoresizingMaskIntoConstraints = NO; 42 [self.valueLabel.leadingAnchor constraintEqualToAnchor:self.keyLabel.trailingAnchor constant:10.f].active = YES; 43 [self.valueLabel.trailingAnchor constraintEqualToAnchor:contentLayout.trailingAnchor].active = YES; 44 [self.valueLabel.topAnchor constraintEqualToAnchor:contentLayout.topAnchor].active = YES; 45 [self.valueLabel.bottomAnchor constraintEqualToAnchor:contentLayout.bottomAnchor].active = YES; 46 47 self.valueLabel.textColor = [UIColor whiteColor]; 48 self.valueLabel.numberOfLines = 0; 49 self.valueLabel.lineBreakMode = NSLineBreakByWordWrapping; 50 self.valueLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; 51 } 52 return self; 53} 54 55@end 56 57@interface ABI48_0_0RCTRedBoxExtraDataViewController () 58 59@end 60 61@implementation ABI48_0_0RCTRedBoxExtraDataViewController { 62 UITableView *_tableView; 63 NSMutableArray *_extraDataTitle; 64 NSMutableArray *_extraData; 65} 66 67@synthesize actionDelegate = _actionDelegate; 68 69- (instancetype)init 70{ 71 if (self = [super init]) { 72 _extraData = [NSMutableArray new]; 73 _extraDataTitle = [NSMutableArray new]; 74 self.view.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1]; 75 76 _tableView = [UITableView new]; 77 _tableView.delegate = self; 78 _tableView.dataSource = self; 79 _tableView.backgroundColor = [UIColor clearColor]; 80 _tableView.estimatedRowHeight = 200; 81 _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 82 _tableView.rowHeight = UITableViewAutomaticDimension; 83 _tableView.allowsSelection = NO; 84 85#if TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST 86 NSString *reloadText = @"Reload JS (\u2318R)"; 87 NSString *dismissText = @"Dismiss (ESC)"; 88#else 89 NSString *reloadText = @"Reload JS"; 90 NSString *dismissText = @"Dismiss"; 91#endif 92 93 UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom]; 94 dismissButton.translatesAutoresizingMaskIntoConstraints = NO; 95 dismissButton.accessibilityIdentifier = @"redbox-extra-data-dismiss"; 96 dismissButton.titleLabel.font = [UIFont systemFontOfSize:13]; 97 [dismissButton setTitle:dismissText forState:UIControlStateNormal]; 98 [dismissButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal]; 99 [dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; 100 [dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside]; 101 102 UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeCustom]; 103 reloadButton.accessibilityIdentifier = @"redbox-reload"; 104 reloadButton.titleLabel.font = [UIFont systemFontOfSize:13]; 105 [reloadButton setTitle:reloadText forState:UIControlStateNormal]; 106 [reloadButton setTitleColor:[UIColor colorWithWhite:1 alpha:0.5] forState:UIControlStateNormal]; 107 [reloadButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; 108 [reloadButton addTarget:self action:@selector(reload) forControlEvents:UIControlEventTouchUpInside]; 109 110 UIStackView *buttonStackView = [UIStackView new]; 111 buttonStackView.axis = UILayoutConstraintAxisHorizontal; 112 buttonStackView.distribution = UIStackViewDistributionEqualSpacing; 113 buttonStackView.alignment = UIStackViewAlignmentFill; 114 buttonStackView.spacing = 20; 115 116 [buttonStackView addArrangedSubview:dismissButton]; 117 [buttonStackView addArrangedSubview:reloadButton]; 118 buttonStackView.translatesAutoresizingMaskIntoConstraints = NO; 119 120 UIStackView *mainStackView = [UIStackView new]; 121 mainStackView.axis = UILayoutConstraintAxisVertical; 122 mainStackView.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1]; 123 [mainStackView addArrangedSubview:_tableView]; 124 [mainStackView addArrangedSubview:buttonStackView]; 125 mainStackView.translatesAutoresizingMaskIntoConstraints = NO; 126 127 [self.view addSubview:mainStackView]; 128 129 CGFloat tableHeight = self.view.bounds.size.height - 60.f; 130 [_tableView.heightAnchor constraintEqualToConstant:tableHeight].active = YES; 131 [_tableView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor].active = YES; 132 133 CGFloat buttonWidth = self.view.bounds.size.width / 4; 134 [dismissButton.heightAnchor constraintEqualToConstant:60].active = YES; 135 [dismissButton.widthAnchor constraintEqualToConstant:buttonWidth].active = YES; 136 [reloadButton.heightAnchor constraintEqualToConstant:60].active = YES; 137 [reloadButton.widthAnchor constraintEqualToConstant:buttonWidth].active = YES; 138 } 139 return self; 140} 141 142- (void)viewDidAppear:(BOOL)animated 143{ 144 [super viewDidAppear:animated]; 145 [_tableView reloadData]; 146} 147 148- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(NSInteger)section 149{ 150 return [[_extraData objectAtIndex:section] count]; 151} 152 153- (CGFloat)tableView:(__unused UITableView *)tableView heightForHeaderInSection:(__unused NSInteger)section 154{ 155 return 40; 156} 157 158- (UIView *)tableView:(__unused UITableView *)tableView viewForHeaderInSection:(NSInteger)section 159{ 160 UIView *view = [UIView new]; 161 view.backgroundColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1]; 162 163 UILabel *header = [UILabel new]; 164 [view addSubview:header]; 165 166 header.translatesAutoresizingMaskIntoConstraints = NO; 167 [header.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:5].active = YES; 168 [header.trailingAnchor constraintEqualToAnchor:view.trailingAnchor].active = YES; 169 [header.topAnchor constraintEqualToAnchor:view.topAnchor].active = YES; 170 [header.bottomAnchor constraintEqualToAnchor:view.bottomAnchor].active = YES; 171 172 header.textColor = [UIColor whiteColor]; 173 header.font = [UIFont fontWithName:@"Menlo-Bold" size:14.0f]; 174 header.text = [_extraDataTitle[section] uppercaseString]; 175 176 return view; 177} 178 179- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 180{ 181 static NSString *reuseIdentifier = @"RedBoxExtraData"; 182 183 ABI48_0_0RCTRedBoxExtraDataCell *cell = 184 (ABI48_0_0RCTRedBoxExtraDataCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; 185 186 if (cell == nil) { 187 cell = [[ABI48_0_0RCTRedBoxExtraDataCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; 188 } 189 190 NSArray *dataKVPair = _extraData[indexPath.section][indexPath.row]; 191 cell.keyLabel.text = dataKVPair[0]; 192 cell.valueLabel.text = dataKVPair[1]; 193 194 return cell; 195} 196 197- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView 198{ 199 return _extraDataTitle.count; 200} 201 202- (void)addExtraData:(NSDictionary *)data forIdentifier:(NSString *)identifier 203{ 204 dispatch_async(dispatch_get_main_queue(), ^{ 205 NSMutableArray *newData = [NSMutableArray new]; 206 for (id key in data) { 207 [newData addObject:@[ 208 [NSString stringWithFormat:@"%@", key], 209 [NSString stringWithFormat:@"%@", [data objectForKey:key]] 210 ]]; 211 } 212 213 NSInteger idx = [self->_extraDataTitle indexOfObject:identifier]; 214 if (idx == NSNotFound) { 215 [self->_extraDataTitle addObject:identifier]; 216 [self->_extraData addObject:newData]; 217 } else { 218 [self->_extraData replaceObjectAtIndex:idx withObject:newData]; 219 } 220 221 [self->_tableView reloadData]; 222 }); 223} 224 225- (void)dismiss 226{ 227 [self dismissViewControllerAnimated:YES completion:nil]; 228} 229 230- (void)reload 231{ 232 [_actionDelegate reload]; 233} 234 235#pragma mark - Key commands 236 237- (NSArray<UIKeyCommand *> *)keyCommands 238{ 239 return @[ 240 // Dismiss 241 [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(dismiss)], 242 // Reload 243 [UIKeyCommand keyCommandWithInput:@"r" modifierFlags:UIKeyModifierCommand action:@selector(reload)] 244 ]; 245} 246 247@end 248