You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
7.2 KiB

  1. //
  2. // FLEXNetworkTransactionTableViewCell.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 2/8/15.
  6. // Copyright (c) 2015 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXColor.h"
  9. #import "FLEXNetworkTransactionTableViewCell.h"
  10. #import "FLEXNetworkTransaction.h"
  11. #import "FLEXUtility.h"
  12. #import "FLEXResources.h"
  13. NSString *const kFLEXNetworkTransactionCellIdentifier = @"kFLEXNetworkTransactionCellIdentifier";
  14. @interface FLEXNetworkTransactionTableViewCell ()
  15. @property (nonatomic) UIImageView *thumbnailImageView;
  16. @property (nonatomic) UILabel *nameLabel;
  17. @property (nonatomic) UILabel *pathLabel;
  18. @property (nonatomic) UILabel *transactionDetailsLabel;
  19. @end
  20. @implementation FLEXNetworkTransactionTableViewCell
  21. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  22. {
  23. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  24. if (self) {
  25. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  26. self.nameLabel = [UILabel new];
  27. self.nameLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
  28. [self.contentView addSubview:self.nameLabel];
  29. self.pathLabel = [UILabel new];
  30. self.pathLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
  31. self.pathLabel.textColor = [UIColor colorWithWhite:0.4 alpha:1.0];
  32. [self.contentView addSubview:self.pathLabel];
  33. self.thumbnailImageView = [UIImageView new];
  34. self.thumbnailImageView.layer.borderColor = UIColor.blackColor.CGColor;
  35. self.thumbnailImageView.layer.borderWidth = 1.0;
  36. self.thumbnailImageView.contentMode = UIViewContentModeScaleAspectFit;
  37. [self.contentView addSubview:self.thumbnailImageView];
  38. self.transactionDetailsLabel = [UILabel new];
  39. self.transactionDetailsLabel.font = [FLEXUtility defaultFontOfSize:10.0];
  40. self.transactionDetailsLabel.textColor = [UIColor colorWithWhite:0.65 alpha:1.0];
  41. [self.contentView addSubview:self.transactionDetailsLabel];
  42. }
  43. return self;
  44. }
  45. - (void)setTransaction:(FLEXNetworkTransaction *)transaction
  46. {
  47. if (_transaction != transaction) {
  48. _transaction = transaction;
  49. [self setNeedsLayout];
  50. }
  51. }
  52. - (void)layoutSubviews
  53. {
  54. [super layoutSubviews];
  55. const CGFloat kVerticalPadding = 8.0;
  56. const CGFloat kLeftPadding = 10.0;
  57. const CGFloat kImageDimension = 32.0;
  58. CGFloat thumbnailOriginY = round((self.contentView.bounds.size.height - kImageDimension) / 2.0);
  59. self.thumbnailImageView.frame = CGRectMake(kLeftPadding, thumbnailOriginY, kImageDimension, kImageDimension);
  60. self.thumbnailImageView.image = self.transaction.responseThumbnail;
  61. CGFloat textOriginX = CGRectGetMaxX(self.thumbnailImageView.frame) + kLeftPadding;
  62. CGFloat availableTextWidth = self.contentView.bounds.size.width - textOriginX;
  63. self.nameLabel.text = [self nameLabelText];
  64. CGSize nameLabelPreferredSize = [self.nameLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)];
  65. self.nameLabel.frame = CGRectMake(textOriginX, kVerticalPadding, availableTextWidth, nameLabelPreferredSize.height);
  66. self.nameLabel.textColor = (self.transaction.error || [FLEXUtility isErrorStatusCodeFromURLResponse:self.transaction.response]) ? UIColor.redColor : [FLEXColor primaryTextColor];
  67. self.pathLabel.text = [self pathLabelText];
  68. CGSize pathLabelPreferredSize = [self.pathLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)];
  69. CGFloat pathLabelOriginY = ceil((self.contentView.bounds.size.height - pathLabelPreferredSize.height) / 2.0);
  70. self.pathLabel.frame = CGRectMake(textOriginX, pathLabelOriginY, availableTextWidth, pathLabelPreferredSize.height);
  71. self.transactionDetailsLabel.text = [self transactionDetailsLabelText];
  72. CGSize transactionLabelPreferredSize = [self.transactionDetailsLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)];
  73. CGFloat transactionDetailsOriginX = textOriginX;
  74. CGFloat transactionDetailsLabelOriginY = CGRectGetMaxY(self.contentView.bounds) - kVerticalPadding - transactionLabelPreferredSize.height;
  75. CGFloat transactionDetailsLabelWidth = self.contentView.bounds.size.width - transactionDetailsOriginX;
  76. self.transactionDetailsLabel.frame = CGRectMake(transactionDetailsOriginX, transactionDetailsLabelOriginY, transactionDetailsLabelWidth, transactionLabelPreferredSize.height);
  77. }
  78. - (NSString *)nameLabelText
  79. {
  80. NSURL *url = self.transaction.request.URL;
  81. NSString *name = [url lastPathComponent];
  82. if (name.length == 0) {
  83. name = @"/";
  84. }
  85. NSString *query = [url query];
  86. if (query) {
  87. name = [name stringByAppendingFormat:@"?%@", query];
  88. }
  89. return name;
  90. }
  91. - (NSString *)pathLabelText
  92. {
  93. NSURL *url = self.transaction.request.URL;
  94. NSMutableArray<NSString *> *mutablePathComponents = [[url pathComponents] mutableCopy];
  95. if (mutablePathComponents.count > 0) {
  96. [mutablePathComponents removeLastObject];
  97. }
  98. NSString *path = [url host];
  99. for (NSString *pathComponent in mutablePathComponents) {
  100. path = [path stringByAppendingPathComponent:pathComponent];
  101. }
  102. return path;
  103. }
  104. - (NSString *)transactionDetailsLabelText
  105. {
  106. NSMutableArray<NSString *> *detailComponents = [NSMutableArray array];
  107. NSString *timestamp = [[self class] timestampStringFromRequestDate:self.transaction.startTime];
  108. if (timestamp.length > 0) {
  109. [detailComponents addObject:timestamp];
  110. }
  111. // Omit method for GET (assumed as default)
  112. NSString *httpMethod = self.transaction.request.HTTPMethod;
  113. if (httpMethod.length > 0) {
  114. [detailComponents addObject:httpMethod];
  115. }
  116. if (self.transaction.transactionState == FLEXNetworkTransactionStateFinished || self.transaction.transactionState == FLEXNetworkTransactionStateFailed) {
  117. NSString *statusCodeString = [FLEXUtility statusCodeStringFromURLResponse:self.transaction.response];
  118. if (statusCodeString.length > 0) {
  119. [detailComponents addObject:statusCodeString];
  120. }
  121. if (self.transaction.receivedDataLength > 0) {
  122. NSString *responseSize = [NSByteCountFormatter stringFromByteCount:self.transaction.receivedDataLength countStyle:NSByteCountFormatterCountStyleBinary];
  123. [detailComponents addObject:responseSize];
  124. }
  125. NSString *totalDuration = [FLEXUtility stringFromRequestDuration:self.transaction.duration];
  126. NSString *latency = [FLEXUtility stringFromRequestDuration:self.transaction.latency];
  127. NSString *duration = [NSString stringWithFormat:@"%@ (%@)", totalDuration, latency];
  128. [detailComponents addObject:duration];
  129. } else {
  130. // Unstarted, Awaiting Response, Receiving Data, etc.
  131. NSString *state = [FLEXNetworkTransaction readableStringFromTransactionState:self.transaction.transactionState];
  132. [detailComponents addObject:state];
  133. }
  134. return [detailComponents componentsJoinedByString:@" ・ "];
  135. }
  136. + (NSString *)timestampStringFromRequestDate:(NSDate *)date
  137. {
  138. static NSDateFormatter *dateFormatter = nil;
  139. static dispatch_once_t onceToken;
  140. dispatch_once(&onceToken, ^{
  141. dateFormatter = [NSDateFormatter new];
  142. dateFormatter.dateFormat = @"HH:mm:ss";
  143. });
  144. return [dateFormatter stringFromDate:date];
  145. }
  146. + (CGFloat)preferredCellHeight
  147. {
  148. return 65.0;
  149. }
  150. @end