FLEXNetworkTransactionTableViewCell.m 7.6 KB

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