MainTableViewController.m (23636B)
1 #import "AddressBook.h" 2 #import "MainTableViewController.h" 3 #import "NewListTableViewController.h" 4 #import "Network.h" 5 #import "ListTableViewController.h" 6 #import "MsgTypes.h" 7 8 #import <AddressBook/AddressBook.h> 9 #include "libkern/OSAtomic.h" 10 11 @interface MainTableViewController () { 12 NSString *phone_number; 13 Network *network_connection; 14 NSString *phone_num_file; 15 } 16 17 // main data structure, [0] holds lists you're in, [1] is other lists 18 @property NSMutableArray *lists; 19 20 @property NSMutableDictionary *phnum_to_name_map; 21 @property (strong, retain) AddressBook *address_book; 22 23 @end 24 25 @implementation MainTableViewController 26 27 - (void) dealloc 28 { 29 [[NSNotificationCenter defaultCenter] removeObserver:self]; 30 } 31 32 - (void) viewDidLoad 33 { 34 [super viewDidLoad]; 35 36 NSNotificationCenter *default_center = [NSNotificationCenter defaultCenter]; 37 NSString *notification_name; 38 39 // Listen for push notifications 40 [default_center addObserver:self selector:@selector(push_friend_added_list:) 41 name:@"PushNotification_friend_added_list" object:nil]; 42 43 /* 44 [default_center addObserver:self selector:@selector(push_updated_list:) 45 name:@"PushNotification_updated_list" object:nil]; 46 */ 47 48 const SEL selectors[] = { 49 @selector(lists_get_finished:), 50 @selector(lists_get_other_finished:), 51 @selector(finished_new_list_request:), 52 @selector(finished_join_list_request:), 53 @selector(finished_leave_list_request:) 54 }; 55 NSUInteger count = 0; 56 57 // This object handles responses for these types of messages 58 for (id str in @[@"lists_get", @"lists_get_other", @"list_add", @"list_join", @"list_leave"]) { 59 notification_name = [NSString stringWithFormat:@"NetworkResponseFor_%@", str]; 60 [default_center addObserver:self selector:selectors[count] name:notification_name object:nil]; 61 count++; 62 } 63 64 // display an Edit button in the navigation bar for this view controller 65 self.navigationItem.leftBarButtonItem = self.editButtonItem; 66 67 network_connection = [Network shared_network_connection]; 68 69 _lists = [[NSMutableArray alloc] init]; 70 [_lists addObject:[[NSMutableArray alloc] init]]; 71 [_lists addObject:[[NSMutableArray alloc] init]]; 72 73 // store the path to the phone number file 74 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 75 NSString *documentsDirectory = [paths objectAtIndex:0]; 76 phone_num_file = [documentsDirectory stringByAppendingPathComponent:@"phone_num"]; 77 78 phone_number = nil; 79 if ([self load_phone_number]) { 80 // phone number loaded, try loading device id 81 if ([network_connection load_device_id:phone_number]) { 82 83 // Send lists_get request, no arguments required here 84 NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 85 [network_connection send_message:lists_get contents:dict]; 86 87 // Send lists_get_other request, no arguments here either 88 [dict removeAllObjects]; 89 [network_connection send_message:lists_get_other contents:dict]; 90 } 91 // else, device id request sent 92 } 93 // else, phone number entry is on screen 94 95 _phnum_to_name_map = [[NSMutableDictionary alloc] init]; 96 97 // get instance and wait for privacy window to clear 98 _address_book = [AddressBook shared_address_book]; 99 _address_book.main_tvc = self; 100 } 101 102 // Handle 'friend_added_list' message from notification service 103 - (void) push_friend_added_list:(NSNotification *) notification 104 { 105 NSDictionary *json_list = notification.userInfo; 106 107 // Server will only send back partial list information because this will 108 // always be put in the other lists section 109 SharedList *tmp = [self deserialize_light_list:json_list]; 110 111 NSMutableArray *other_lists = [_lists objectAtIndex:1]; 112 [other_lists addObject:tmp]; 113 114 NSLog(@"notify: new other list '%@', num '%@'", tmp.name, tmp.num); 115 116 NSIndexPath *new_path = [NSIndexPath indexPathForRow:[other_lists count] - 1 inSection:1]; 117 [self.tableView insertRowsAtIndexPaths:@[new_path] withRowAnimation:UITableViewRowAnimationAutomatic]; 118 119 [self update_section_headers]; 120 } 121 122 - (bool) load_phone_number 123 { 124 if ([[NSFileManager defaultManager] fileExistsAtPath:phone_num_file]) { 125 // file exists, read what it has 126 // XXX: validate length of file too 127 NSError *error = nil; 128 phone_number = [NSString stringWithContentsOfFile:phone_num_file encoding:NSASCIIStringEncoding error:&error]; 129 return true; 130 } 131 132 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Important" 133 message:@"We need this phone's number to find your mutual friends." 134 delegate:self cancelButtonTitle:@"Nope" otherButtonTitles:@"Ok", nil]; 135 136 alert.alertViewStyle = UIAlertViewStylePlainTextInput; 137 138 // it's a phone number, so only show the number pad 139 UITextField * alertTextField = [alert textFieldAtIndex:0]; 140 alertTextField.keyboardType = UIKeyboardTypeNumberPad; 141 alertTextField.placeholder = @"Enter your phone number"; 142 143 [alert show]; 144 return false; 145 } 146 147 - (void)alertView:(UIAlertView *)alertView 148 clickedButtonAtIndex:(NSInteger)buttonIndex 149 { 150 NSString *entered_phone_num = [[alertView textFieldAtIndex:0] text]; 151 NSLog(@"warn: main: writing phone num '%@' to disk", entered_phone_num); 152 NSError *error; 153 [entered_phone_num writeToFile:phone_num_file atomically:YES encoding:NSASCIIStringEncoding error:&error]; 154 155 if (error) 156 NSLog(@"warn: main: writing phone number file: %@", error); 157 158 if ([entered_phone_num compare:@""] == NSOrderedSame) { 159 NSLog(@"warn: load phone number: entered emtpy phone number"); 160 } 161 162 phone_number = entered_phone_num; 163 164 if ([network_connection load_device_id:phone_number]) { 165 NSLog(@"info: network: connection ready"); 166 // bulk update, doesn't take a payload 167 [network_connection send_message:3 contents:nil]; 168 } 169 // else, device id request sent 170 } 171 172 - (void) update_address_book 173 { 174 [_phnum_to_name_map removeAllObjects]; 175 // XXX: it'd be nice to resize phnum_to_name_map to num_contacts here 176 177 for (Contact *contact in _address_book.contacts) { 178 NSString *disp_name; 179 // show first name and last initial if possible, otherwise 180 // just show the first name or the last name or the phone number 181 if (contact.first_name && contact.last_name) 182 disp_name = [NSString stringWithFormat:@"%@ %@", 183 contact.first_name, [contact.last_name substringToIndex:1]]; 184 else if (contact.first_name) 185 disp_name = contact.first_name; 186 else if (contact.last_name) 187 disp_name = contact.last_name; 188 else if ([contact.phone_numbers count]) 189 disp_name = [contact.phone_numbers objectAtIndex:0]; 190 else 191 disp_name = @"No Name"; 192 193 // map the persons known phone number to their massaged name 194 for (NSString *tmp_phone_number in contact.phone_numbers) 195 [_phnum_to_name_map setObject:disp_name forKey:tmp_phone_number]; 196 } 197 } 198 199 - (void) didReceiveMemoryWarning 200 { 201 [super didReceiveMemoryWarning]; 202 // Dispose of any resources that can be recreated. 203 } 204 205 - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView 206 { 207 // "lists you're in" and "other lists" 208 return 2; 209 } 210 211 - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 212 { 213 if (section > 1) 214 // should never happen 215 return 0; 216 217 return [[_lists objectAtIndex:section] count]; 218 } 219 220 // new list dialogue has been saved 221 - (IBAction) unwindToList:(UIStoryboardSegue *)segue 222 { 223 } 224 225 - (void) lists_get_finished:(NSNotification *)notification 226 { 227 NSDictionary *response_dict = notification.userInfo; 228 // Already checked for existence before this function was called 229 NSArray *json_lists = response_dict[@"data"]; 230 231 NSLog(@"lists_get: got %lu lists from server", (unsigned long)[json_lists count]); 232 233 NSMutableArray *lists = [_lists objectAtIndex:0]; 234 [lists removeAllObjects]; 235 236 for (NSDictionary *list in json_lists) { 237 SharedList *tmp = [self deserialize_full_list:list]; 238 [lists addObject:tmp]; 239 240 NSLog(@"adding list '%@', num '%@'", tmp.name, tmp.num); 241 } 242 243 NSIndexSet *section = [NSIndexSet indexSetWithIndex:0]; 244 [self.tableView reloadSections:section withRowAnimation:UITableViewRowAnimationNone]; 245 } 246 247 - (void) lists_get_other_finished:(NSNotification *)notification; 248 { 249 NSDictionary *response_dict = notification.userInfo; 250 // Already checked for existence before this function was called 251 NSArray *other_json_lists = response_dict[@"data"]; 252 253 NSLog(@"lists_get_other: got %lu other lists from server", (unsigned long)[other_json_lists count]); 254 255 NSMutableArray *other_lists = [_lists objectAtIndex:1]; 256 [other_lists removeAllObjects]; 257 258 for (NSDictionary *list in other_json_lists) { 259 SharedList *tmp = [self deserialize_light_list:list]; 260 [other_lists addObject:tmp]; 261 262 NSLog(@"adding other list '%@', num '%@'", tmp.name, tmp.num); 263 } 264 265 NSIndexSet *section = [NSIndexSet indexSetWithIndex:1]; 266 [self.tableView reloadSections:section withRowAnimation:UITableViewRowAnimationNone]; 267 } 268 269 - (void) finished_new_list_request:(NSNotification *) notification 270 { 271 NSDictionary *response = notification.userInfo; 272 NSDictionary *list = [response objectForKey:@"data"]; 273 274 SharedList *shlist = [self deserialize_full_list:list]; 275 276 NSMutableArray *lists = [_lists objectAtIndex:0]; 277 [lists addObject:shlist]; 278 279 // response looks good, insert the new list 280 NSUInteger new_row_pos = [lists count] - 1; 281 NSIndexPath *index_path = [NSIndexPath indexPathForRow:new_row_pos inSection:0]; 282 [self.tableView insertRowsAtIndexPaths:@[index_path] withRowAnimation:UITableViewRowAnimationFade]; 283 284 [self update_section_headers]; 285 } 286 287 - (SharedList *) deserialize_full_list:(NSDictionary *)json_list 288 { 289 SharedList *shlist = [[SharedList alloc] init]; 290 shlist.num = [json_list objectForKey:@"num"]; 291 292 // We need some careful decoding to get a usable Unicode string 293 NSData *name_data = [json_list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; 294 shlist.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; 295 296 NSNumber *date = json_list[@"date"]; 297 if ([date intValue] != 0) { 298 shlist.date = [NSDate dateWithTimeIntervalSince1970:[date floatValue]]; 299 } 300 else { 301 shlist.date = nil; 302 } 303 304 shlist.members_phone_nums = [json_list objectForKey:@"members"]; 305 shlist.items_ready = [json_list objectForKey:@"items_complete"]; 306 shlist.items_total = [json_list objectForKey:@"items_total"]; 307 308 return shlist; 309 } 310 311 - (SharedList *) deserialize_light_list:(NSDictionary *)json_list 312 { 313 SharedList *shlist = [[SharedList alloc] init]; 314 shlist.num = [json_list objectForKey:@"num"]; 315 316 // We need some careful decoding to get a usable Unicode string 317 NSData *name_data = [json_list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; 318 shlist.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; 319 320 shlist.members_phone_nums = [json_list objectForKey:@"members"]; 321 322 return shlist; 323 } 324 325 // major thing here is join list requests 326 - (void)tableView:(UITableView *)tableView 327 didSelectRowAtIndexPath:(NSIndexPath *)indexPath 328 { 329 [tableView deselectRowAtIndexPath:indexPath animated:YES]; 330 331 // section 0 is going to segue to the list items screen 332 if ([indexPath section] == 0) 333 return; 334 335 // we're in section 1 now, a tap down here means we're doing a join list request 336 SharedList *list = [[_lists objectAtIndex:1] objectAtIndex:[indexPath row]]; 337 NSLog(@"info: joining list '%@'", list.name); 338 339 // the response for this does all of the heavy row moving work 340 NSMutableDictionary *request = [[NSMutableDictionary alloc] init]; 341 [request setObject:list.num forKey:@"list_num"]; 342 [network_connection send_message:list_join contents:request]; 343 } 344 345 - (void) finished_join_list_request:(NSNotification *) notification 346 { 347 NSDictionary *response = notification.userInfo; 348 NSDictionary *json_list = response[@"list"]; 349 NSLog(@"network: joined list %@", json_list[@"num"]); 350 351 NSMutableArray *lists = [_lists objectAtIndex:0]; 352 NSMutableArray *other_lists = [_lists objectAtIndex:1]; 353 354 // Find the list number we received a response for 355 SharedList *needle = nil; 356 for (SharedList *temp in other_lists) { 357 if (temp.num == json_list[@"num"]) { 358 needle = temp; 359 break; 360 } 361 } 362 363 // If we received an update from a list id we don't know about, do nothing 364 if (needle == nil) 365 return; 366 367 // The server sent us a full list object, make sure to copy cell reference 368 SharedList *joined_list = [self deserialize_full_list:json_list]; 369 joined_list.cell = needle.cell; 370 371 // Add completely new object to lists section and remove the old list 372 [lists addObject:joined_list]; 373 [other_lists removeObject:needle]; 374 375 // Get the cell index path from the matched list cell 376 NSIndexPath *orig_index_path = [self.tableView indexPathForCell:joined_list.cell]; 377 378 // Compute new position and start moving row as soon as possible 379 // XXX: sorting 380 NSUInteger new_row_pos = [lists count] - 1; 381 NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:new_row_pos inSection:0]; 382 [self.tableView moveRowAtIndexPath:orig_index_path toIndexPath:new_index_path]; 383 384 [self update_section_headers]; 385 386 // Update members in list row 387 [self process_members_array:joined_list.members_phone_nums cell:joined_list.cell]; 388 389 // Add > accessory indicator 390 joined_list.cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 391 392 // Find fraction UILAbel, populate it and then show it 393 UILabel *fraction = (UILabel *)[joined_list.cell viewWithTag:4]; 394 fraction.text = [self fraction:joined_list.items_ready denominator:joined_list.items_total]; 395 fraction.hidden = NO; 396 397 // Show date label if date has been set to something 398 if (joined_list.date != nil) { 399 UILabel *deadline_label = (UILabel *)[joined_list.cell viewWithTag:3]; 400 deadline_label.hidden = NO; 401 } 402 } 403 404 405 // section header titles 406 - (NSString *)tableView:(UITableView *)tableView 407 titleForHeaderInSection:(NSInteger)section 408 { 409 if (section > 1) 410 // should not happen 411 return @""; 412 413 unsigned long total = [[_lists objectAtIndex:section] count]; 414 if (section == 0) 415 return [NSString stringWithFormat:@"Your Lists (%lu)", total]; 416 else if (section == 1) 417 return [NSString stringWithFormat:@"Other Lists (%lu)", total]; 418 return @""; 419 } 420 421 - (void) update_section_headers 422 { 423 NSMutableArray *lists = [_lists objectAtIndex:0]; 424 NSMutableArray *other_lists = [_lists objectAtIndex:1]; 425 426 UITableViewHeaderFooterView *sectionZeroHeader = [self.tableView headerViewForSection:0]; 427 UITableViewHeaderFooterView *sectionOneHeader = [self.tableView headerViewForSection:1]; 428 NSString *sectionZeroLabel = [NSString stringWithFormat:@"Your Lists (%lu)", (unsigned long)[lists count]]; 429 NSString *sectionOneLabel = [NSString stringWithFormat:@"Other Lists (%lu)", (unsigned long)[other_lists count]]; 430 431 [sectionZeroHeader.textLabel setText:sectionZeroLabel]; 432 [sectionZeroHeader setNeedsLayout]; 433 [sectionOneHeader.textLabel setText:sectionOneLabel]; 434 [sectionOneHeader setNeedsLayout]; 435 } 436 437 - (void) finished_leave_list_request:(NSNotification *) notification 438 { 439 NSDictionary *response = notification.userInfo; 440 NSNumber *list_num = response[@"list_num"]; 441 NSLog(@"network: left list %@", list_num); 442 443 NSMutableArray *lists = [_lists objectAtIndex:0]; 444 NSMutableArray *other_lists = [_lists objectAtIndex:1]; 445 446 SharedList *list = nil; 447 for (SharedList *temp in lists) { 448 if (temp.num == response[@"list_num"]) { 449 list = temp; 450 break; 451 } 452 } 453 if (list == nil) 454 return; 455 456 NSNumber *list_empty = response[@"list_empty"]; 457 if ([list_empty intValue] == 1) { 458 // List was empty, delete instead of moving it 459 [lists removeObject:list]; 460 461 NSIndexPath *old_path = [self.tableView indexPathForCell:list.cell]; 462 [self.tableView deleteRowsAtIndexPaths:@[old_path] withRowAnimation:UITableViewRowAnimationAutomatic]; 463 464 [self update_section_headers]; 465 return; 466 } 467 468 // Insert the new object at the beginning to match gui moving below 469 [other_lists insertObject:list atIndex:0]; 470 [lists removeObject:list]; 471 472 // Perform row move, the destination is the top of "other lists" 473 NSIndexPath *old_path = [self.tableView indexPathForCell:list.cell]; 474 NSIndexPath *new_path = [NSIndexPath indexPathForRow:0 inSection:1]; 475 [self.tableView moveRowAtIndexPath:old_path toIndexPath:new_path]; 476 477 // Make sure section headers are accurate 478 [self update_section_headers]; 479 480 // Remove yourself from the members array 481 NSMutableArray *members = [list.members_phone_nums mutableCopy]; 482 NSUInteger index = [members indexOfObject:@"4037082094"]; 483 if (index != NSNotFound) { 484 [members removeObjectAtIndex:index]; 485 } 486 [self process_members_array:members cell:list.cell]; 487 488 // Remove > accessory 489 list.cell.accessoryType = UITableViewCellAccessoryNone; 490 491 // Hide completion fraction 492 UILabel *fraction = (UILabel *)[list.cell viewWithTag:4]; 493 fraction.hidden = YES; 494 495 // Hide date 496 UILabel *deadline_label = (UILabel *)[list.cell viewWithTag:3]; 497 deadline_label.hidden = YES; 498 499 // XXX: update members array to disclude yourself (maybe send it back in response?) 500 // XXX: Maybe clear out list data that's no longer needed 501 // XXX: give some visual feedback here what's happening 502 } 503 504 505 - (UITableViewCell *) tableView:(UITableView *)tableView 506 cellForRowAtIndexPath:(NSIndexPath *)indexPath 507 { 508 UITableViewCell *cell; 509 cell = [tableView dequeueReusableCellWithIdentifier:@"SharedListPrototypeCell" forIndexPath:indexPath]; 510 511 NSInteger section = [indexPath section]; 512 NSInteger row = [indexPath row]; 513 SharedList *shared_list = [[_lists objectAtIndex:section] objectAtIndex:row]; 514 515 UILabel *deadline_label = (UILabel *)[cell viewWithTag:3]; 516 UILabel *fraction_label = (UILabel *)[cell viewWithTag:4]; 517 518 if (section == 0) { 519 // your lists section 520 521 if (shared_list.date == nil) { 522 deadline_label.hidden = YES; 523 } else { 524 // XXX: calculate how long until deadline 525 // NSDate *date = shared_list.date; 526 deadline_label.text = @"date"; 527 } 528 529 float frac = [shared_list.items_ready floatValue] / [shared_list.items_total floatValue]; 530 if (frac > 0.80f) 531 fraction_label.textColor = [UIColor greenColor]; 532 fraction_label.hidden = NO; 533 fraction_label.text = [self fraction:shared_list.items_ready 534 denominator:shared_list.items_total]; 535 536 // Add ">" accessory to indicate you can "enter" this list 537 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 538 539 } 540 else if (section == 1) { 541 // "other lists" section 542 543 // no deadline 544 deadline_label.text = @""; 545 546 // remove the > accessory and the completion fraction 547 cell.accessoryType = UITableViewCellAccessoryNone; 548 fraction_label.hidden = YES; 549 } 550 551 UILabel *main_label = (UILabel *)[cell viewWithTag:1]; 552 main_label.text = shared_list.name; 553 554 [self process_members_array:shared_list.members_phone_nums cell:cell]; 555 556 // hang on to a reference, this is needed in the networking gui callbacks 557 shared_list.cell = cell; 558 return cell; 559 } 560 561 - (void) process_members_array:(NSArray *)phnum_array cell:(UITableViewCell *)cell 562 { 563 UILabel *members_label = (UILabel *)[cell viewWithTag:2]; 564 565 if (!OSAtomicAnd32(0xffff, &_address_book->ready)) { 566 // not ready 567 NSMutableString *output = [[NSMutableString alloc] init]; 568 for (id tmp_phone_number in phnum_array) { 569 if ([tmp_phone_number compare:phone_number] == NSOrderedSame) { 570 [output appendString:@"You"]; 571 continue; 572 } 573 574 [output appendFormat:@", %@", tmp_phone_number]; 575 } 576 members_label.text = output; 577 return; 578 } 579 580 // we can do phone number to name mappings 581 NSMutableArray *members = [[NSMutableArray alloc] init]; 582 int others = 0; 583 584 // anything past the second field are list members 585 for (id tmp_phone_number in phnum_array) { 586 587 // try to find the list member in our address book 588 NSString *name = _phnum_to_name_map[tmp_phone_number]; 589 590 if (name) 591 [members addObject:name]; 592 else if (phone_number && ([phone_number compare:tmp_phone_number] == NSOrderedSame)) 593 [members addObject:@"You"]; 594 else 595 // didn't find it, you don't know this person 596 others++; 597 } 598 599 NSMutableString *members_str = 600 [[members componentsJoinedByString:@", "] mutableCopy]; 601 602 if (others) { 603 char *plural; 604 if (others == 1) 605 plural = "other"; 606 else 607 plural = "others"; 608 609 NSString *buf = [NSString stringWithFormat:@" + %i %s", 610 others, plural]; 611 [members_str appendString:buf]; 612 } 613 614 members_label.text = members_str; 615 } 616 617 618 // only section 0 lists can be edited 619 - (BOOL) tableView:(UITableView *)tableView 620 canEditRowAtIndexPath:(NSIndexPath *)indexPath 621 { 622 if ([indexPath section] == 0) 623 return YES; 624 return NO; 625 } 626 627 // what editing style should be applied to this indexpath 628 - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView 629 editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath 630 { 631 // don't have to check the section here because canEditRowAtIndexPath 632 // already said the section can't be edited 633 return UITableViewCellEditingStyleDelete; 634 } 635 636 // This functions called when delete has been prompted and ok'd 637 - (void) tableView:(UITableView *)tableView 638 commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 639 forRowAtIndexPath:(NSIndexPath *)indexPath 640 { 641 // we don't need to check for !section 0 because of canEditRowAtIndexPath 642 SharedList *list = [[_lists objectAtIndex:0] objectAtIndex:[indexPath row]]; 643 NSLog(@"info: leaving '%@' list num '%@'", list.name, list.num); 644 645 // Send leave list message, response will do all heavy lifting 646 NSMutableDictionary *request = [[NSMutableDictionary alloc] init]; 647 [request setObject:list.num forKey:@"list_num"]; 648 [network_connection send_message:list_leave contents:request]; 649 650 // Reset editing state back to the default 651 [self.tableView setEditing:FALSE animated:TRUE]; 652 } 653 654 // customize deletion label text 655 - (NSString *)tableView:(UITableView *)tableView 656 titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath 657 { 658 return @"Leave"; 659 } 660 661 // tell incoming controllers about their environment 662 - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 663 { 664 if ([[segue identifier] isEqualToString:@"show list segue"]) { 665 // a shared list was selected, transfer into detailed view 666 667 NSIndexPath *path = [self.tableView indexPathForSelectedRow]; 668 SharedList *list = [[_lists objectAtIndex:0] objectAtIndex:[path row]]; 669 670 // make sure incoming view controller knows about itself 671 [segue.destinationViewController setMetadata:list]; 672 673 // send update list items message 674 // network_connection->shlist_ldvc = segue.destinationViewController; 675 //[network_connection send_message:6 contents:list.id]; 676 } 677 678 // DetailObject *detail = [self detailForIndexPath:path]; 679 NSLog(@"info: main: preparing for segue"); 680 } 681 682 // prevent segues from occurring when non member lists are selected 683 - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender 684 { 685 NSIndexPath *path = [self.tableView indexPathForSelectedRow]; 686 687 if ([path section] == 0) 688 return YES; 689 return NO; 690 } 691 692 // taken from http://stackoverflow.com/questions/30859359/display-fraction-number-in-uilabel 693 -(NSString *)fraction:(NSNumber *)numerator denominator:(NSNumber *)denominator 694 { 695 696 NSMutableString *result = [NSMutableString string]; 697 698 NSString *one = [numerator stringValue]; 699 for (int i = 0; i < one.length; i++) { 700 [result appendString:[self superscript:[[one substringWithRange:NSMakeRange(i, 1)] intValue]]]; 701 } 702 703 [result appendString:@"/"]; 704 705 NSString *two = [denominator stringValue]; 706 for (int i = 0; i < two.length; i++) { 707 [result appendString:[self subscript:[[two substringWithRange:NSMakeRange(i, 1)] intValue]]]; 708 } 709 710 return result; 711 } 712 713 -(NSString *)superscript:(int)num 714 { 715 NSDictionary *superscripts = @{@0: @"\u2070", @1: @"\u00B9", @2: @"\u00B2", @3: @"\u00B3", @4: @"\u2074", @5: @"\u2075", @6: @"\u2076", @7: @"\u2077", @8: @"\u2078", @9: @"\u2079"}; 716 return superscripts[@(num)]; 717 } 718 719 -(NSString *)subscript:(int)num 720 { 721 NSDictionary *subscripts = @{@0: @"\u2080", @1: @"\u2081", @2: @"\u2082", @3: @"\u2083", @4: @"\u2084", @5: @"\u2085", @6: @"\u2086", @7: @"\u2087", @8: @"\u2088", @9: @"\u2089"}; 722 return subscripts[@(num)]; 723 } 724 725 @end