PageRenderTime 71ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Classes/UVSuggestionListViewController.m

https://github.com/appsocial/uservoice-iphone-sdk
Objective C | 523 lines | 385 code | 98 blank | 40 comment | 33 complexity | ee9d16df83085c454473848f0f37d054 MD5 | raw file
  1. //
  2. // UVSuggestionListViewController.m
  3. // UserVoice
  4. //
  5. // Created by UserVoice on 10/22/09.
  6. // Copyright 2009 UserVoice Inc. All rights reserved.
  7. //
  8. #import <QuartzCore/QuartzCore.h>
  9. #import "UVSuggestionListViewController.h"
  10. #import "UVClientConfig.h"
  11. #import "UVSession.h"
  12. #import "UVSuggestion.h"
  13. #import "UVSuggestionDetailsViewController.h"
  14. #import "UVNewSuggestionViewController.h"
  15. #import "UVProfileViewController.h"
  16. #import "UVInfoViewController.h"
  17. #import "UVStyleSheet.h"
  18. #import "UVUser.h"
  19. #import "UVFooterView.h"
  20. #import "UVTextEditor.h"
  21. #import "UVBaseGroupedCell.h"
  22. #import "UVCellViewWithIndex.h"
  23. #import "UVStreamPoller.h"
  24. #define SUGGESTIONS_PAGE_SIZE 10
  25. #define UV_SEARCH_TEXTBAR 1
  26. #define UV_SEARCH_RESULTS_TAG_CELL_ADD_PREFIX 100
  27. #define UV_SEARCH_RESULTS_TAG_CELL_ADD_QUERY 101
  28. #define UV_SEARCH_RESULTS_TAG_CELL_ADD_SUFFIX 102
  29. #define UV_BASE_GROUPED_CELL_BG 103
  30. #define UV_BASE_SUGGESTION_LIST_TAG_CELL_BACKGROUND 104
  31. @implementation UVSuggestionListViewController
  32. @synthesize forum = _forum;
  33. @synthesize prevLeftBarButton = _prevLeftBarButton;
  34. @synthesize textEditor = _textEditor;
  35. - (id)initWithForum:(UVForum *)theForum {
  36. if (self = [super init]) {
  37. if (theForum.currentTopic.suggestions) {
  38. self = [self initWithForum:theForum andSuggestions:theForum.currentTopic.suggestions];
  39. } else {
  40. self.forum = theForum;
  41. }
  42. _searching = NO;
  43. }
  44. return self;
  45. }
  46. - (id)initWithForum:(UVForum *)theForum andSuggestions:(NSArray *)theSuggestions {
  47. if (self = [super init]) {
  48. self.suggestions = [NSMutableArray arrayWithArray:theSuggestions];
  49. self.forum = theForum;
  50. _searching = NO;
  51. }
  52. return self;
  53. }
  54. - (NSString *)backButtonTitle {
  55. return @"Ideas";
  56. }
  57. - (void)retrieveMoreSuggestions {
  58. NSInteger page = ([self.suggestions count] / SUGGESTIONS_PAGE_SIZE) + 1;
  59. [self showActivityIndicator];
  60. [UVSuggestion getWithForum:self.forum page:page delegate:self];
  61. }
  62. // Populates the suggestions. The default implementation retrieves the 10 most recent
  63. // suggestions, but this can be overridden in subclasses (e.g. for profile idea view).
  64. - (void)populateSuggestions {
  65. self.suggestions = [NSMutableArray arrayWithCapacity:10];
  66. [UVSession currentSession].clientConfig.forum.currentTopic.suggestions = [NSMutableArray arrayWithCapacity:10];
  67. [UVSession currentSession].clientConfig.forum.currentTopic.suggestionsNeedReload = NO;
  68. [self retrieveMoreSuggestions];
  69. // gonna check and start the stream timer here too
  70. if (![UVStreamPoller instance].timerIsRunning) {
  71. [[UVStreamPoller instance] startTimer];
  72. [UVStreamPoller instance].lastPollTime = [NSDate date];
  73. }
  74. }
  75. - (void)didRetrieveSuggestions:(NSArray *)theSuggestions {
  76. [self hideActivityIndicator];
  77. if ([theSuggestions count] > 0) {
  78. //NSLog(@"Retrieved Suggestions: %@", theSuggetions);
  79. [self.suggestions addObjectsFromArray:theSuggestions];
  80. //NSLog(@"Stored Suggestions: %@", self.suggestions);
  81. }
  82. [[UVSession currentSession].clientConfig.forum.currentTopic.suggestions addObjectsFromArray:theSuggestions];
  83. [self.tableView reloadData];
  84. }
  85. - (void)didSearchSuggestions:(NSArray *)theSuggestions {
  86. [self.suggestions removeAllObjects];
  87. [self hideActivityIndicator];
  88. if ([theSuggestions count] > 0) {
  89. [self.suggestions addObjectsFromArray:theSuggestions];
  90. }
  91. [self.tableView reloadData];
  92. }
  93. - (BOOL)supportsSearch {
  94. return YES;
  95. }
  96. - (BOOL)supportsFooter {
  97. return YES;
  98. }
  99. - (void)addSuggestion:(UVCellViewWithIndex *)cellView {
  100. UVNewSuggestionViewController *next = [[UVNewSuggestionViewController alloc] initWithForum:self.forum
  101. title:_textEditor.text];
  102. [self.navigationController pushViewController:next animated:YES];
  103. [next release];
  104. [self dismissTextEditor];
  105. }
  106. #pragma mark ===== UITableViewDataSource Methods =====
  107. // Overridden from superclass. In this case the Extra cell is responsible for
  108. // creating a new suggestion.
  109. - (void)initCellForAdd:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
  110. // getting the cell size
  111. CGRect contentRect = cell.contentView.bounds;
  112. UVCellViewWithIndex *cellView = [[UVCellViewWithIndex alloc] initWithIndex:indexPath.row andFrame:contentRect];
  113. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  114. UIFont *font = [UIFont boldSystemFontOfSize:18];
  115. UILabel *label = [[UILabel alloc] init];
  116. label.tag = UV_SEARCH_RESULTS_TAG_CELL_ADD_PREFIX;
  117. label.text = @"Add \"";
  118. label.font = font;
  119. label.textAlignment = UITextAlignmentLeft;
  120. label.textColor = [UIColor blackColor];
  121. label.backgroundColor = [UIColor clearColor];
  122. [cellView addSubview:label];
  123. [label release];
  124. label = [[UILabel alloc] init];
  125. label.tag = UV_SEARCH_RESULTS_TAG_CELL_ADD_QUERY;
  126. label.text = _textEditor.text;
  127. label.font = font;
  128. label.textAlignment = UITextAlignmentLeft;
  129. label.textColor = [UVStyleSheet dimBlueColor];
  130. label.backgroundColor = [UIColor clearColor];
  131. [cellView addSubview:label];
  132. [label release];
  133. label = [[UILabel alloc] init];
  134. label.tag = UV_SEARCH_RESULTS_TAG_CELL_ADD_SUFFIX;
  135. label.text = @"\"";
  136. label.font = font;
  137. label.textAlignment = UITextAlignmentLeft;
  138. label.textColor = [UIColor blackColor];
  139. label.backgroundColor = [UIColor clearColor];
  140. [cellView addSubview:label];
  141. [label release];
  142. [cell.contentView addSubview:cellView];
  143. [cellView release];
  144. }
  145. - (void)customizeCellForAdd:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath
  146. {
  147. UIColor *bgColor = indexPath.row % 2 == 0 ? [UVStyleSheet darkZebraBgColor] : [UVStyleSheet lightZebraBgColor];
  148. cell.backgroundView.backgroundColor = bgColor;
  149. UIFont *font = [UIFont boldSystemFontOfSize:18];
  150. NSString *text = [NSString stringWithFormat:@"Add \"%@\"", _textEditor.text];
  151. CGSize size = [text sizeWithFont:font forWidth:260 lineBreakMode:UILineBreakModeTailTruncation];
  152. CGFloat startX = 30.0 + ((260.0 - size.width) / 2.0);
  153. // Prefix: Add "
  154. UILabel *label = (UILabel *)[cell.contentView viewWithTag:UV_SEARCH_RESULTS_TAG_CELL_ADD_PREFIX];
  155. size = [label.text sizeWithFont:font forWidth:260 lineBreakMode:UILineBreakModeTailTruncation];
  156. label.frame = CGRectMake(startX, 26, size.width, 20);
  157. // Query
  158. NSInteger prevEndX = label.frame.origin.x + label.frame.size.width;
  159. CGFloat maxWidth = 260 - (size.width + 10);
  160. label = (UILabel *)[cell.contentView viewWithTag:UV_SEARCH_RESULTS_TAG_CELL_ADD_QUERY];
  161. label.text = _textEditor.text;
  162. label.textColor = [UVStyleSheet dimBlueColor];
  163. size = [label.text sizeWithFont:font forWidth:maxWidth lineBreakMode:UILineBreakModeTailTruncation];
  164. label.frame = CGRectMake(prevEndX, 26, size.width, 20);
  165. // Suffix: "
  166. prevEndX = label.frame.origin.x + label.frame.size.width;
  167. label = (UILabel *)[cell.contentView viewWithTag:UV_SEARCH_RESULTS_TAG_CELL_ADD_SUFFIX];
  168. label.frame = CGRectMake(prevEndX-1, 26, 10, 20);
  169. }
  170. - (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  171. NSString *identifier;
  172. BOOL selectable = YES;
  173. UITableViewCellStyle style = UITableViewCellStyleDefault;
  174. NSInteger suggestionsCount = [UVSession currentSession].clientConfig.forum.currentTopic.suggestionsCount;
  175. //NSLog(@"%d, %d, %d", indexPath.row, [self.suggestions count], suggestionsCount);
  176. if (indexPath.row < [self.suggestions count]) {
  177. identifier = @"Suggestion";
  178. } else if (!_searching && (indexPath.row == [self.suggestions count]) && (suggestionsCount > [self.suggestions count])) {
  179. identifier = @"Load";
  180. } else {
  181. identifier = @"Add";
  182. }
  183. return [self createCellForIdentifier:identifier
  184. tableView:theTableView
  185. indexPath:indexPath
  186. style:style
  187. selectable:selectable];
  188. }
  189. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  190. NSInteger rows = 0;
  191. NSInteger loadedCount = [self.suggestions count];
  192. NSInteger suggestionsCount = [UVSession currentSession].clientConfig.forum.currentTopic.suggestionsCount;
  193. if (_searching) {
  194. NSLog(@"Adding extra row for 'add'");
  195. // One cell per suggestion + one for "add"
  196. rows = loadedCount + 1;
  197. } else {
  198. // One cell per suggestion + "Load More"
  199. rows = [self.suggestions count] + (loadedCount>=suggestionsCount || suggestionsCount<SUGGESTIONS_PAGE_SIZE ? 0 : 1);
  200. }
  201. return rows;
  202. }
  203. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  204. return 1;
  205. }
  206. #pragma mark ===== UITableViewDelegate Methods =====
  207. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  208. // Both for suggestions and Load More
  209. return 71;
  210. }
  211. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  212. {
  213. NSInteger suggestionsCount = [UVSession currentSession].clientConfig.forum.currentTopic.suggestionsCount;
  214. if (!_searching && (indexPath.row == [self.suggestions count]) && (suggestionsCount > [self.suggestions count]))
  215. {
  216. // This is the last row in the table, so it's the "Load more ideas" cell
  217. [self retrieveMoreSuggestions];
  218. }
  219. else
  220. {
  221. // For all other rows, push appropriate suggestion details
  222. [self pushSuggestionShowView:indexPath.row];
  223. }
  224. }
  225. - (void)setLeftBarButtonCancel {
  226. UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  227. target:self
  228. action:@selector(dismissTextEditor)];
  229. [self.navigationItem setLeftBarButtonItem:cancelItem animated:YES];
  230. [cancelItem release];
  231. }
  232. - (void)setLeftBarButtonClear {
  233. UIBarButtonItem *clearItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
  234. target:self
  235. action:@selector(resetList)];
  236. [self.navigationItem setLeftBarButtonItem:clearItem animated:YES];
  237. [clearItem release];
  238. }
  239. - (void)setLeftBarButtonPrevious {
  240. [self.navigationItem setLeftBarButtonItem:self.prevLeftBarButton animated:YES];
  241. }
  242. - (void)resetList {
  243. _searching = NO;
  244. _textEditor.text = @"";
  245. [self.suggestions removeAllObjects];
  246. [self.suggestions addObjectsFromArray:[UVSession currentSession].clientConfig.forum.currentTopic.suggestions];
  247. [self.tableView reloadData];
  248. [self.navigationItem setLeftBarButtonItem:nil animated:YES];
  249. }
  250. - (void)dismissTextEditor {
  251. [self.textEditor resignFirstResponder];
  252. [self resetList];
  253. }
  254. #pragma mark ===== UVTextEditorDelegate Methods =====
  255. - (void)setCellsEnabled:(BOOL)enabled {
  256. // need to stop rows being selected
  257. for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows]) {
  258. UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
  259. UIButton *button = (UIButton *)[cell.contentView viewWithTag:UV_BASE_SUGGESTION_LIST_TAG_CELL_BACKGROUND];
  260. button.enabled = enabled;
  261. }
  262. }
  263. - (BOOL)textEditorShouldBeginEditing:(UVTextEditor *)theTextEditor {
  264. //NSLog(@"textEditorShouldBeginEditing");
  265. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  266. UIView *headerView = (UIView *)self.tableView.tableHeaderView;
  267. NSInteger height = self.view.bounds.size.height - 216;
  268. CGRect frame = CGRectMake(0, 10, screenWidth, height);
  269. UIView *textBar = [headerView viewWithTag:UV_SEARCH_TEXTBAR];
  270. // Maximize header view to allow text editor to grow (leaving room for keyboard) 216
  271. [UIView beginAnimations:@"growHeader" context:nil];
  272. //NSLog(@"setLeftBarButtonCancel");
  273. [self setLeftBarButtonCancel];
  274. [self setCellsEnabled:NO];
  275. textBar.frame = frame;
  276. textBar.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
  277. frame = CGRectMake(0, 0, screenWidth, 40);
  278. theTextEditor.frame = frame; // (may not actually need to change this, since bg is white)
  279. theTextEditor.backgroundColor = [UIColor whiteColor];
  280. [UIView commitAnimations];
  281. return YES;
  282. }
  283. - (BOOL)textEditorShouldReturn:(UVTextEditor *)theTextEditor {
  284. NSLog(@"Check for: %@", self.textEditor.text);
  285. [self showActivityIndicator];
  286. [self.textEditor resignFirstResponder];
  287. _searching = YES;
  288. if (self.textEditor.text) {
  289. [UVSuggestion searchWithForum:self.forum query:self.textEditor.text delegate:self];
  290. }
  291. return NO;
  292. }
  293. - (void)textEditorDidEndEditing:(UVTextEditor *)theTextEditor {
  294. //NSLog(@"textEditorDidEndEditing");
  295. // reset nav
  296. if (_textEditor.text) {
  297. //NSLog(@"setLeftBarButtonClear");
  298. [self setLeftBarButtonClear];
  299. }
  300. UIView *headerView = (UIView *)self.tableView.tableHeaderView;
  301. UIView *textBar = [headerView viewWithTag:UV_SEARCH_TEXTBAR];
  302. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  303. // Minimize text editor and header
  304. [UIView beginAnimations:@"shrinkHeader" context:nil];
  305. textBar.frame = CGRectMake(0, 10, screenWidth, 40);
  306. textBar.backgroundColor = [UIColor whiteColor];
  307. [self setCellsEnabled:YES];
  308. [UIView commitAnimations];
  309. }
  310. - (BOOL)textEditorShouldEndEditing:(UVTextEditor *)theTextEditor {
  311. return YES;
  312. }
  313. #pragma mark ===== Basic View Methods =====
  314. // Implement loadView to create a view hierarchy programmatically, without using a nib.
  315. - (void)loadView {
  316. [super loadView];
  317. self.navigationItem.title = self.forum.currentTopic.prompt;
  318. CGRect frame = [self contentFrame]; // ??
  319. UIView *contentView = [[UIView alloc] initWithFrame:frame];
  320. CGFloat screenWidth = [UVClientConfig getScreenWidth];
  321. CGFloat screenHeight = [UVClientConfig getScreenHeight];
  322. // TODO: fix table initWithFrame here
  323. UITableView *theTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight-44) style:UITableViewStylePlain];
  324. theTableView.dataSource = self;
  325. theTableView.delegate = self;
  326. theTableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
  327. theTableView.sectionFooterHeight = 0.0;
  328. theTableView.sectionHeaderHeight = 0.0;
  329. [self addShadowSeparatorToTableView:theTableView];
  330. NSInteger headerHeight = [self supportsSearch] ? 50 : 10;
  331. UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, headerHeight)];
  332. headerView.backgroundColor = [UIColor clearColor];
  333. UIImage *shadow = [UIImage imageNamed:@"dropshadow_top_20.png"];
  334. CGFloat widthScale = screenWidth / shadow.size.width; // horizontal scaling factor to expand shadow image
  335. UIImageView *shadowView = [[UIImageView alloc] initWithImage:shadow];
  336. shadowView.transform = CGAffineTransformMakeScale(widthScale, 1.0); // rescale the shadow
  337. shadowView.center = CGPointMake(screenWidth/2, shadowView.center.y); // recenter the upscaled shadow
  338. [headerView addSubview:shadowView];
  339. [shadowView release];
  340. if ([self supportsSearch]) {
  341. // Add text editor to table header
  342. UIView *textBar = [[UIView alloc] initWithFrame:CGRectMake(0, 10, screenWidth, 40)];
  343. textBar.backgroundColor = [UIColor whiteColor];
  344. textBar.tag = UV_SEARCH_TEXTBAR;
  345. _textEditor = [[UVTextEditor alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 40)];
  346. _textEditor.delegate = self;
  347. _textEditor.autocorrectionType = UITextAutocorrectionTypeYes;
  348. _textEditor.minNumberOfLines = 1;
  349. _textEditor.maxNumberOfLines = 1;
  350. _textEditor.autoresizesToText = NO;
  351. [_textEditor setReturnKeyType:UIReturnKeyGo];
  352. _textEditor.enablesReturnKeyAutomatically = NO;
  353. _textEditor.placeholder = [self.forum example];
  354. [textBar addSubview:_textEditor];
  355. [headerView addSubview:textBar];
  356. [textBar release];
  357. }
  358. theTableView.tableHeaderView = headerView;
  359. if ([self supportsFooter]) {
  360. theTableView.tableFooterView = [UVFooterView altFooterViewForController:self];
  361. } else {
  362. UIView *bottomShadow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 10)];
  363. UIImage *shadow = [UIImage imageNamed:@"dropshadow_bottom_30.png"];
  364. CGFloat widthScale = screenWidth / shadow.size.width; // horizontal scaling factor to expand shadow image
  365. UIImageView *shadowView = [[[UIImageView alloc] initWithImage:shadow] autorelease];
  366. shadowView.transform = CGAffineTransformMakeScale(widthScale, 1.0); // rescale the shadow
  367. shadowView.center = CGPointMake(screenWidth/2, shadowView.center.y); // recenter the upscaled shadow
  368. [bottomShadow addSubview:shadowView];
  369. theTableView.tableFooterView = bottomShadow;
  370. [shadowView release];
  371. [bottomShadow release];
  372. }
  373. self.tableView = theTableView;
  374. [contentView addSubview:theTableView];
  375. [theTableView release];
  376. self.view = contentView;
  377. [contentView release];
  378. }
  379. - (void)reloadTableData {
  380. NSLog(@"UVSuggestionListViewController: reloadTableData");
  381. self.suggestions = [UVSession currentSession].clientConfig.forum.currentTopic.suggestions;
  382. [self.tableView reloadData];
  383. }
  384. - (void)viewWillAppear:(BOOL)animated {
  385. [super viewWillAppear:animated];
  386. if ([UVSession currentSession].clientConfig.forum.currentTopic.suggestionsNeedReload) {
  387. self.suggestions = nil;
  388. }
  389. if (!self.suggestions) {
  390. [self populateSuggestions];
  391. }
  392. if ([self supportsFooter]) {
  393. // Reload footer view, in case the user has changed (logged in or unlinked)
  394. UVFooterView *footer = (UVFooterView *) self.tableView.tableFooterView;
  395. [footer reloadFooter];
  396. }
  397. [self.tableView reloadData];
  398. NSLog(@"Adding observer");
  399. [[NSNotificationCenter defaultCenter] addObserver:self
  400. selector:@selector(reloadTableData)
  401. name:@"TopicSuggestionsUpdated"
  402. object:nil];
  403. }
  404. - (void)viewWillDisappear:(BOOL)animated {
  405. [super viewWillDisappear:animated];
  406. NSLog(@"Removing observer");
  407. [[NSNotificationCenter defaultCenter] removeObserver:self
  408. name:@"TopicSuggestionsUpdated"
  409. object:nil];
  410. }
  411. - (void)didReceiveMemoryWarning {
  412. // Releases the view if it doesn't have a superview.
  413. [super didReceiveMemoryWarning];
  414. // Release any cached data, images, etc that aren't in use.
  415. }
  416. - (void)viewDidUnload {
  417. // Release any retained subviews of the main view.
  418. // e.g. self.myOutlet = nil;
  419. }
  420. - (void)dealloc {
  421. [super dealloc];
  422. if (_textEditor)
  423. [_textEditor release];
  424. }
  425. @end