PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Classes/UVCommentListViewController.m

https://github.com/appsocial/uservoice-iphone-sdk
Objective C | 446 lines | 331 code | 76 blank | 39 comment | 22 complexity | 101ba87392928f1cc9481befc7eed477 MD5 | raw file
  1. //
  2. // UVCommentListViewController.m
  3. // UserVoice
  4. //
  5. // Created by UserVoice on 11/10/09.
  6. // Copyright 2009 UserVoice Inc. All rights reserved.
  7. //
  8. #import <QuartzCore/QuartzCore.h>
  9. #import "UVCommentListViewController.h"
  10. #import "UVComment.h"
  11. #import "UVSuggestion.h"
  12. #import "UVStyleSheet.h"
  13. #import "UVProfileViewController.h"
  14. #import "UVUserChickletView.h"
  15. #import "UVCellViewWithIndex.h"
  16. #import "UVUserButton.h"
  17. #import "UVTextEditor.h"
  18. #import "UVClientConfig.h"
  19. #define UV_COMMENT_LIST_TAG_CELL_NAME 1
  20. #define UV_COMMENT_LIST_TAG_CELL_DATE 2
  21. #define UV_COMMENT_LIST_TAG_CELL_COMMENT 3
  22. #define UV_COMMENT_LIST_TAG_CELL_CHICKLET 4
  23. #define UV_COMMENT_LIST_TAG_CELL_BUTTON 5
  24. #define COMMENTS_PAGE_SIZE 10
  25. @implementation UVCommentListViewController
  26. @synthesize suggestion;
  27. @synthesize comments;
  28. @synthesize commentToFlag;
  29. @synthesize text;
  30. @synthesize textEditor;
  31. @synthesize prevLeftBarButton;
  32. @synthesize prevRightBarButton;
  33. - (id)initWithSuggestion:(UVSuggestion *)theSuggestion {
  34. if (self = [super init]) {
  35. self.suggestion = theSuggestion;
  36. }
  37. return self;
  38. }
  39. - (NSString *)backButtonTitle {
  40. return @"Comments";
  41. }
  42. - (void)retrieveMoreComments {
  43. NSInteger page = ([self.comments count] / 10) + 1;
  44. [self showActivityIndicator];
  45. [UVComment getWithSuggestion:self.suggestion page:page delegate:self];
  46. }
  47. - (void)didCreateComment:(UVComment *)comment {
  48. [self hideActivityIndicator];
  49. // Insert new comment at the beginning
  50. [self.comments insertObject:comment atIndex:0];
  51. [self.tableView reloadData];
  52. // Update comment count
  53. self.suggestion.commentsCount += 1;
  54. if (self.suggestion.commentsCount == 1) {
  55. self.navigationItem.title = @"1 Comment";
  56. } else {
  57. self.navigationItem.title = [NSString stringWithFormat:@"%d Comments", self.suggestion.commentsCount];
  58. }
  59. // Clear text editor
  60. self.textEditor.text = @"";
  61. // For some reason setting the text activates the editor and brings up the
  62. // keyboard, so we need to manually deactivate it.
  63. [self.textEditor resignFirstResponder];
  64. }
  65. - (void)didRetrieveComments:(NSArray *)theComments {
  66. [self hideActivityIndicator];
  67. if ([theComments count] > 0) {
  68. [self.comments addObjectsFromArray:theComments];
  69. if ([self.comments count] >= self.suggestion.commentsCount) {
  70. allCommentsRetrieved = YES;
  71. }
  72. } else {
  73. allCommentsRetrieved = YES;
  74. }
  75. [self.tableView reloadData];
  76. }
  77. - (CGSize)sizeForComment:(UVComment *)comment {
  78. CGFloat labelWidth = 210;
  79. return [comment.text sizeWithFont:[UIFont systemFontOfSize:13]
  80. constrainedToSize:CGSizeMake(labelWidth, 10000)
  81. lineBreakMode:UILineBreakModeWordWrap];
  82. }
  83. - (void)dismissTextEditor:(id)sender {
  84. [self.textEditor resignFirstResponder];
  85. [self.navigationItem setLeftBarButtonItem:self.prevLeftBarButton animated:YES];
  86. [self.navigationItem setRightBarButtonItem:self.prevRightBarButton animated:YES];
  87. editing = NO;
  88. }
  89. - (void)saveTextEditor:(id)sender {
  90. [self dismissTextEditor:sender];
  91. [self showActivityIndicator];
  92. [UVComment createWithSuggestion:self.suggestion text:self.text delegate:self];
  93. }
  94. - (void)promptForFlagWithIndex:(NSInteger)index {
  95. self.commentToFlag = [self.comments objectAtIndex:index];
  96. UIActionSheet *action = [[UIActionSheet alloc] initWithTitle:@"Flag Comment?"
  97. delegate:self
  98. cancelButtonTitle:@"Cancel"
  99. destructiveButtonTitle:@"Flag as inappropriate"
  100. otherButtonTitles:nil];
  101. [action showInView:self.view];
  102. [action release];
  103. }
  104. - (void)didFlagComment:(UVComment *)theComment {
  105. [self hideActivityIndicator];
  106. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success"
  107. message:@"You have successfully flagged this comment as inappropriate."
  108. delegate:nil
  109. cancelButtonTitle:nil
  110. otherButtonTitles:@"OK", nil];
  111. [alert show];
  112. [alert release];
  113. }
  114. #pragma mark ===== UIActionSheetDelegate Methods =====
  115. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
  116. if (buttonIndex != actionSheet.cancelButtonIndex) {
  117. [self showActivityIndicator];
  118. [self.commentToFlag flag:@"inappropriate" suggestion:self.suggestion delegate:self];
  119. }
  120. }
  121. #pragma mark ===== UVTextEditorDelegate Methods =====
  122. - (void) textEditorDidBeginEditing:(UVTextEditor *)theTextEditor
  123. {
  124. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  125. // Change right bar button to Done and left to Cancel, as there's no built-in
  126. // way to dismiss the text editor's keyboard.
  127. UIBarButtonItem *saveItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
  128. target:self
  129. action:@selector(saveTextEditor:)];
  130. UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  131. target:self
  132. action:@selector(dismissTextEditor:)];
  133. self.prevLeftBarButton = self.navigationItem.leftBarButtonItem;
  134. self.prevRightBarButton = self.navigationItem.rightBarButtonItem;
  135. [self.navigationItem setLeftBarButtonItem:cancelItem animated:YES];
  136. [self.navigationItem setRightBarButtonItem:saveItem animated:YES];
  137. [saveItem release];
  138. [cancelItem release];
  139. // Maximize header view to allow text editor to grow (leaving room for keyboard)
  140. [UIView beginAnimations:@"growHeader" context:nil];
  141. NSInteger height = self.view.bounds.size.height - 216;
  142. CGRect frame = CGRectMake(0, 0, screenWidth, height);
  143. UIView *textBar = (UIView *)self.tableView.tableHeaderView;
  144. textBar.frame = frame;
  145. textBar.backgroundColor = [UIColor whiteColor];
  146. theTextEditor.frame = frame; // (may not actually need to change this, since bg is white)
  147. [UIView commitAnimations];
  148. }
  149. - (void)textEditorDidEndEditing:(UVTextEditor *)theTextEditor
  150. {
  151. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  152. self.text = theTextEditor.text;
  153. // Minimize text editor and header
  154. [UIView beginAnimations:@"shrinkHeader" context:nil];
  155. theTextEditor.frame = CGRectMake(5, 0, (screenWidth-5), 40);
  156. UIView *textBar = (UIView *)self.tableView.tableHeaderView;
  157. textBar.frame = CGRectMake(0, 0, screenWidth, 40);
  158. theTextEditor.frame = CGRectMake(5, 0, (screenWidth-5), 40);
  159. [UIView commitAnimations];
  160. }
  161. - (BOOL)textEditorShouldEndEditing:(UVTextEditor *)theTextEditor {
  162. return YES;
  163. }
  164. #pragma mark ===== table cells =====
  165. - (void)initCellForPrompt:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
  166. cell.textLabel.text = @"Add Comment";
  167. cell.textLabel.textColor = [UIColor blueColor];
  168. cell.textLabel.textAlignment = UITextAlignmentCenter;
  169. }
  170. - (void)initCellForComment:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
  171. cell.backgroundView = [[[UIView alloc] initWithFrame:cell.frame] autorelease];
  172. [self addHighlightToCell:cell];
  173. // Username
  174. UVUserButton *userButton = [UVUserButton buttonWithcontroller:self
  175. font:[UIFont boldSystemFontOfSize:14]
  176. color:[UVStyleSheet tableViewHeaderColor]];
  177. userButton.tag = UV_COMMENT_LIST_TAG_CELL_NAME;
  178. [cell.contentView addSubview:userButton];
  179. // Date
  180. UILabel *label = [[UILabel alloc] init];
  181. label.tag = UV_COMMENT_LIST_TAG_CELL_DATE;
  182. label.backgroundColor = [UIColor clearColor];
  183. label.textColor = [UIColor lightGrayColor];
  184. label.font = [UIFont systemFontOfSize:14];
  185. [cell.contentView addSubview:label];
  186. [label release];
  187. // Comment Text
  188. label = [[UILabel alloc] init];
  189. label.tag = UV_COMMENT_LIST_TAG_CELL_COMMENT;
  190. label.lineBreakMode = UILineBreakModeWordWrap;
  191. label.numberOfLines = 0;
  192. label.font = [UIFont systemFontOfSize:13];
  193. label.backgroundColor = [UIColor clearColor];
  194. [cell.contentView addSubview:label];
  195. [label release];
  196. // Chicklet
  197. UVUserChickletView *chicklet = [UVUserChickletView userChickletViewWithOrigin:CGPointMake(10, 10) controller:self admin:NO];
  198. chicklet.tag = UV_COMMENT_LIST_TAG_CELL_CHICKLET;
  199. [cell.contentView addSubview:chicklet];
  200. // TODO: despite the class name, this is now actually a UIView subclass, not a UIButton (let's change the class name later)
  201. UVCellViewWithIndex *cellView = [[[UVCellViewWithIndex alloc] init] autorelease];
  202. cellView.tag = UV_COMMENT_LIST_TAG_CELL_BUTTON;
  203. cellView.index = indexPath.row;
  204. cellView.frame = CGRectMake(290, 10, 20, 21);
  205. //[button addTarget:self action:@selector(promptForFlag:) forControlEvents:UIControlEventTouchUpInside];
  206. [cell.contentView addSubview:cellView];
  207. }
  208. - (void)customizeCellForComment:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
  209. UVComment *comment = [self.comments objectAtIndex:indexPath.row];
  210. BOOL darkZebra = indexPath.row % 2 == 0;
  211. cell.backgroundView.backgroundColor = [UVStyleSheet zebraBgColor:darkZebra];
  212. // Username + Date
  213. NSInteger days = ABS([comment.createdAt timeIntervalSinceNow]) / (60 * 60 * 24);
  214. NSString *daysAgo = [NSString stringWithFormat:@"%d days ago", days];
  215. UVUserButton *userButton = (UVUserButton *)[cell.contentView viewWithTag:UV_COMMENT_LIST_TAG_CELL_NAME];
  216. UILabel *dateLabel = (UILabel *)[cell.contentView viewWithTag:UV_COMMENT_LIST_TAG_CELL_DATE];
  217. CGSize dateSize = [daysAgo sizeWithFont:dateLabel.font forWidth:210 lineBreakMode:UILineBreakModeTailTruncation];
  218. [userButton updateWithUserId:comment.userId
  219. name:comment.userName
  220. origin:CGPointMake(70, 10)
  221. maxWidth:(210 - dateSize.width)];
  222. dateLabel.frame = CGRectMake(70 + 5 + userButton.bounds.size.width, 10, dateSize.width, dateSize.height);
  223. dateLabel.text = daysAgo;
  224. // Comment Text
  225. CGSize size = [self sizeForComment:comment];
  226. UILabel *label = (UILabel *)[cell.contentView viewWithTag:UV_COMMENT_LIST_TAG_CELL_COMMENT];
  227. label.frame = CGRectMake(70, 28, 210, size.height);
  228. label.text = comment.text;
  229. // Chicklet
  230. UVUserChickletView *chicklet = (UVUserChickletView *)[cell.contentView viewWithTag:UV_COMMENT_LIST_TAG_CELL_CHICKLET];
  231. UVUserChickletStyle style = darkZebra ? UVUserChickletStyleDark : UVUserChickletStyleLight;
  232. [chicklet updateWithStyle:style userId:comment.userId name:comment.userName avatarUrl:comment.avatarUrl karmaScore:comment.karmaScore];
  233. }
  234. - (void)initCellForLoad:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath
  235. {
  236. cell.backgroundView = [[[UIView alloc] initWithFrame:cell.frame] autorelease];
  237. [self addHighlightToCell:cell];
  238. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  239. // Can't use built-in textLabel, as this forces a white background
  240. UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 27, screenWidth, 16)];
  241. textLabel.text = @"Load more comments...";
  242. textLabel.textColor = [UIColor darkGrayColor];
  243. textLabel.backgroundColor = [UIColor clearColor];
  244. textLabel.font = [UIFont systemFontOfSize:16];
  245. textLabel.textAlignment = UITextAlignmentCenter;
  246. [cell.contentView addSubview:textLabel];
  247. [textLabel release];
  248. }
  249. - (void)customizeCellForLoad:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
  250. UIColor *bgColor = indexPath.row % 2 == 0 ? [UVStyleSheet darkZebraBgColor] : [UVStyleSheet lightZebraBgColor];
  251. cell.backgroundView.backgroundColor = bgColor;
  252. }
  253. #pragma mark ===== UITableViewDataSource Methods =====
  254. - (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  255. NSString *identifier;
  256. UITableViewCellStyle style = UITableViewCellStyleDefault;
  257. BOOL selectable = YES;
  258. if (indexPath.row < [self.comments count]) {
  259. identifier = @"Comment";
  260. selectable = NO;
  261. } else {
  262. identifier = @"Load";
  263. }
  264. return [self createCellForIdentifier:identifier
  265. tableView:theTableView
  266. indexPath:indexPath
  267. style:style
  268. selectable:selectable];
  269. }
  270. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  271. return [self.comments count] + (allCommentsRetrieved ? 0 : 1);
  272. }
  273. #pragma mark ===== UITableViewDelegate Methods =====
  274. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  275. if (indexPath.row < [self.comments count]) {
  276. UVComment *comment = [self.comments objectAtIndex:indexPath.row];
  277. CGSize size = [self sizeForComment:comment];
  278. // Add some extra margin. Also need to ensure a certain minimum height, to
  279. // ensure user chicklets fit.
  280. return MAX(size.height + 20 + 18, [UVUserChickletView heightForView] + 20);
  281. } else {
  282. return 71; // Load More
  283. }
  284. }
  285. - (void)tableView:(UITableView *)theTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  286. [theTableView deselectRowAtIndexPath:indexPath animated:YES];
  287. if (indexPath.row == [self.comments count])
  288. {
  289. // This is the last row in the table, so it's the "Load more comments" cell
  290. [self retrieveMoreComments];
  291. }
  292. else
  293. {
  294. // For all other rows, prompt for flag
  295. [self promptForFlagWithIndex:indexPath.row];
  296. }
  297. }
  298. #pragma mark ===== Basic View Methods =====
  299. // Implement loadView to create a view hierarchy programmatically, without using a nib.
  300. - (void)loadView
  301. {
  302. [super loadView];
  303. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  304. CGFloat screenHeight = [UVClientConfig getScreenHeight];
  305. if (self.suggestion.commentsCount == 1) {
  306. self.navigationItem.title = @"1 Comment";
  307. } else {
  308. self.navigationItem.title = [NSString stringWithFormat:@"%d Comments", self.suggestion.commentsCount];
  309. }
  310. CGRect frame = [self contentFrame];
  311. UIView *contentView = [[UIView alloc] initWithFrame:frame];
  312. UITableView *theTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight-44)];
  313. theTableView.dataSource = self;
  314. theTableView.delegate = self;
  315. theTableView.backgroundColor = [UIColor clearColor];
  316. [self addShadowSeparatorToTableView:theTableView];
  317. // Add text editor to table header
  318. UIView *textBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 40)];
  319. textBar.backgroundColor = [UIColor whiteColor];
  320. // TTSTYLE(commentTextBar)
  321. UVTextEditor *theTextEditor = [[UVTextEditor alloc] initWithFrame:CGRectMake(5, 0, (screenWidth-5), 40)];
  322. theTextEditor.delegate = self;
  323. theTextEditor.autocorrectionType = UITextAutocorrectionTypeYes;
  324. theTextEditor.minNumberOfLines = 1;
  325. theTextEditor.maxNumberOfLines = 8;
  326. theTextEditor.autoresizesToText = YES;
  327. theTextEditor.backgroundColor = [UIColor clearColor];
  328. //theTextEditor.style = TTSTYLE(commentTextBarTextField);
  329. theTextEditor.placeholder = @"Add a comment...";
  330. [textBar addSubview:theTextEditor];
  331. self.textEditor = theTextEditor;
  332. [theTextEditor release];
  333. theTableView.tableHeaderView = textBar;
  334. [textBar release];
  335. // Add empty footer, to suppress blank cells (with separators) after actual content
  336. UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 0)];
  337. theTableView.tableFooterView = footer;
  338. [footer release];
  339. self.tableView = theTableView;
  340. [contentView addSubview:theTableView];
  341. [theTableView release];
  342. self.view = contentView;
  343. [contentView release];
  344. [self addGradientBackground];
  345. }
  346. - (void)viewWillAppear:(BOOL)animated {
  347. [super viewWillAppear:animated];
  348. if (!self.comments) {
  349. allCommentsRetrieved = NO;
  350. self.comments = [NSMutableArray arrayWithCapacity:10];
  351. [self showActivityIndicator];
  352. [self retrieveMoreComments];
  353. }
  354. }
  355. - (void)didReceiveMemoryWarning {
  356. // Releases the view if it doesn't have a superview.
  357. [super didReceiveMemoryWarning];
  358. // Release any cached data, images, etc that aren't in use.
  359. }
  360. - (void)viewDidUnload {
  361. // Release any retained subviews of the main view.
  362. // e.g. self.myOutlet = nil;
  363. self.textEditor = nil;
  364. self.prevLeftBarButton = nil;
  365. self.prevRightBarButton = nil;
  366. }
  367. - (void)dealloc {
  368. [super dealloc];
  369. }
  370. @end