shlist

share and manage lists between multiple people
Log | Files | Refs

commit 61ab8aa79b907d4b93c279c6cc6b7555c6dd5000
parent 1022463dc33325984e837b505a8f68b91a77cc6b
Author: Kyle Milz <kyle@Kyles-MacBook-Pro.local>
Date:   Thu, 17 Sep 2015 20:27:59 -0600

ios: get animated row insert/deletes going

Diffstat:
Mios-ng/shlist.xcodeproj/project.pbxproj | 10----------
Mios-ng/shlist/AddressBook.m | 20+++++++++-----------
Mios-ng/shlist/Base.lproj/Main.storyboard | 20--------------------
Mios-ng/shlist/SharedListsTableViewController.m | 175++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mios-ng/shlist/ShlistServer.m | 19+++++++++----------
5 files changed, 125 insertions(+), 119 deletions(-)

diff --git a/ios-ng/shlist.xcodeproj/project.pbxproj b/ios-ng/shlist.xcodeproj/project.pbxproj @@ -21,8 +21,6 @@ 27C70F2A1B33D1C900DADEB3 /* SharedList.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F291B33D1C900DADEB3 /* SharedList.m */; }; 27C70F2D1B33F3C300DADEB3 /* NewListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F2C1B33F3C300DADEB3 /* NewListViewController.m */; }; 27C70F301B33F4FA00DADEB3 /* SharedListsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F2F1B33F4FA00DADEB3 /* SharedListsTableViewController.m */; }; - 27C70F321B3650CB00DADEB3 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 27C70F311B3650CB00DADEB3 /* libsqlite3.dylib */; }; - 27C70F351B36513200DADEB3 /* DBManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F341B36513200DADEB3 /* DBManager.m */; }; 27D805731BA2649D00867494 /* ContactsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D805721BA2649D00867494 /* ContactsTableViewController.m */; }; 27DCC9DE1B8A98D400207340 /* dollar103-2.png in Resources */ = {isa = PBXBuildFile; fileRef = 27DCC9DD1B8A98D400207340 /* dollar103-2.png */; }; 27DCC9E81B9EB4E800207340 /* information15-3.png in Resources */ = {isa = PBXBuildFile; fileRef = 27DCC9E71B9EB4E800207340 /* information15-3.png */; }; @@ -67,9 +65,6 @@ 27C70F2C1B33F3C300DADEB3 /* NewListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewListViewController.m; sourceTree = "<group>"; }; 27C70F2E1B33F4FA00DADEB3 /* SharedListsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharedListsTableViewController.h; sourceTree = "<group>"; }; 27C70F2F1B33F4FA00DADEB3 /* SharedListsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharedListsTableViewController.m; sourceTree = "<group>"; }; - 27C70F311B3650CB00DADEB3 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; - 27C70F331B36513200DADEB3 /* DBManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBManager.h; sourceTree = "<group>"; }; - 27C70F341B36513200DADEB3 /* DBManager.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = DBManager.m; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; }; 27D805711BA2649D00867494 /* ContactsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsTableViewController.h; sourceTree = SOURCE_ROOT; }; 27D805721BA2649D00867494 /* ContactsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsTableViewController.m; sourceTree = SOURCE_ROOT; }; 27DCC9DD1B8A98D400207340 /* dollar103-2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "dollar103-2.png"; path = "../../../Downloads/dollar103-2.png"; sourceTree = "<group>"; }; @@ -89,7 +84,6 @@ files = ( 27B03A021B43B8660054B6D2 /* AddressBook.framework in Frameworks */, 27B03A031B43B8660054B6D2 /* AddressBookUI.framework in Frameworks */, - 27C70F321B3650CB00DADEB3 /* libsqlite3.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -110,11 +104,8 @@ 27AAC22B1B50ABAF00D99171 /* NewItemTableViewController.m */, 27B03A001B43B8660054B6D2 /* AddressBook.framework */, 27B03A011B43B8660054B6D2 /* AddressBookUI.framework */, - 27C70F331B36513200DADEB3 /* DBManager.h */, - 27C70F341B36513200DADEB3 /* DBManager.m */, 27C70F091B32AF8000DADEB3 /* ShlistServer.h */, 27C70F0A1B32AF8000DADEB3 /* ShlistServer.m */, - 27C70F311B3650CB00DADEB3 /* libsqlite3.dylib */, 27C70F011B32AF8000DADEB3 /* shlist */, 27C70F1B1B32AF8000DADEB3 /* shlistTests */, 27C70F001B32AF8000DADEB3 /* Products */, @@ -291,7 +282,6 @@ buildActionMask = 2147483647; files = ( BF7776B91B38928D00526CB0 /* ListDetailTableViewController.m in Sources */, - 27C70F351B36513200DADEB3 /* DBManager.m in Sources */, 27AAC22C1B50ABAF00D99171 /* NewItemTableViewController.m in Sources */, BF7776BC1B38D0DB00526CB0 /* ListItem.m in Sources */, 27C70F0B1B32AF8000DADEB3 /* ShlistServer.m in Sources */, diff --git a/ios-ng/shlist/AddressBook.m b/ios-ng/shlist/AddressBook.m @@ -92,31 +92,29 @@ - (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook { NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook)); - NSInteger numberOfPeople = [allPeople count]; - for (NSInteger i = 0; i < numberOfPeople; i++) { + NSCharacterSet *want =[[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet]; + + for (NSInteger i = 0; i < [allPeople count]; i++) { ABRecordRef person = (__bridge ABRecordRef)allPeople[i]; Contact *contact = [[Contact alloc] init]; - // don't enforce these existing on purpose + // don't enforce these existing contact.first_name = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty)); contact.last_name = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty)); + contact.phone_numbers = [[NSMutableArray alloc] init]; ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty); CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers); - contact.phone_numbers = [[NSMutableArray alloc] init]; for (CFIndex i = 0; i < numberOfPhoneNumbers; i++) { - NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i)); + NSString *pn = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i)); - if (phoneNumber == nil) + if (pn == nil) continue; - phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@" " withString:@""]; - phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@"(" withString:@""]; - phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@")" withString:@""]; - phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@"-" withString:@""]; + NSString *cleaned = [[pn componentsSeparatedByCharactersInSet: want] componentsJoinedByString:@""]; - [contact.phone_numbers addObject:phoneNumber]; + [contact.phone_numbers addObject:cleaned]; } CFRelease(phoneNumbers); diff --git a/ios-ng/shlist/Base.lproj/Main.storyboard b/ios-ng/shlist/Base.lproj/Main.storyboard @@ -283,26 +283,6 @@ <segue destination="pMK-tA-j4s" kind="show" identifier="show list segue" id="ORk-KR-Twe"/> </connections> </tableViewCell> - <tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="IndirectListPrototypeCell" textLabel="WuW-Sd-sxB" detailTextLabel="rf3-DG-Fgi" style="IBUITableViewCellStyleSubtitle" id="kbj-EX-eeF"> - <autoresizingMask key="autoresizingMask"/> - <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="kbj-EX-eeF" id="fgg-hw-Da6"> - <autoresizingMask key="autoresizingMask"/> - <subviews> - <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="WuW-Sd-sxB"> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <fontDescription key="fontDescription" type="system" pointSize="16"/> - <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> - <nil key="highlightedColor"/> - </label> - <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="rf3-DG-Fgi"> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <fontDescription key="fontDescription" type="system" pointSize="11"/> - <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> - <nil key="highlightedColor"/> - </label> - </subviews> - </tableViewCellContentView> - </tableViewCell> </prototypes> <sections/> <connections> diff --git a/ios-ng/shlist/SharedListsTableViewController.m b/ios-ng/shlist/SharedListsTableViewController.m @@ -14,19 +14,6 @@ @implementation SharedListsTableViewController -- (void) load_initial_data -{ - // create one and only server instance, this gets passed around - _server = [[ShlistServer alloc] init]; - _server->shlist_tvc = self; - - if ([_server prepare]) { - NSLog(@"info: server connection prepared"); - // bulk update, doesn't take a payload - [_server send_message:3 contents:nil]; - } -} - - (IBAction) unwindToList:(UIStoryboardSegue *)segue { NewListViewController *source = [segue sourceViewController]; @@ -50,17 +37,21 @@ { [super viewDidLoad]; - // Uncomment the following line to preserve selection between - // presentations. - // self.clearsSelectionOnViewWillAppear = NO; - // display an Edit button in the navigation bar for this view controller self.navigationItem.leftBarButtonItem = self.editButtonItem; self.shared_lists = [[NSMutableArray alloc] init]; self.indirect_lists = [[NSMutableArray alloc] init]; - [self load_initial_data]; + // create one and only server instance, this gets passed around + _server = [[ShlistServer alloc] init]; + _server->shlist_tvc = self; + + if ([_server prepare]) { + NSLog(@"info: server connection prepared"); + // bulk update, doesn't take a payload + [_server send_message:3 contents:nil]; + } } - (void) didReceiveMemoryWarning @@ -73,6 +64,7 @@ - (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { + // "lists you're in" and "other lists" return 2; } @@ -82,26 +74,55 @@ return [self.shared_lists count]; else if (section == 1) return [self.indirect_lists count]; - return 0; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +// major thing here is join list requests +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - NSLog(@"did cell selection"); - [tableView deselectRowAtIndexPath:indexPath animated:NO]; + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + // section 0 is going to segue to the list items screen + if ([indexPath section] == 0) + return; + + // we're in section 1 now, a tap down here means we're doing a join list request + NSIndexPath *path = [self.tableView indexPathForSelectedRow]; + SharedList *list = [self.indirect_lists objectAtIndex:[path row]]; + NSLog(@"info: joining '%@'", list.list_name); + + // this has to be done before row moving + [_shared_lists addObject:list]; + [_indirect_lists removeObject:list]; + + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + + // compute new position and start moving row as soon as possible + // XXX: sorting + NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:[_shared_lists count] - 1 inSection:0]; + [tableView moveRowAtIndexPath:indexPath toIndexPath:new_index_path]; + + // add > accessory indicator + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + UILabel *fraction = (UILabel *)[cell viewWithTag:1]; + fraction.hidden = NO; + fraction.text = [self fraction:list.items_ready denominator:list.items_total]; + + // send this last because when the response comes in the list should be in + // it's expected place + [_server send_message:4 contents:list.list_id]; } - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; + cell = [tableView dequeueReusableCellWithIdentifier:@"SharedListPrototypeCell" forIndexPath:indexPath]; int row = [indexPath row]; if ([indexPath section] == 0) { - cell = [tableView dequeueReusableCellWithIdentifier:@"SharedListPrototypeCell" forIndexPath:indexPath]; - SharedList *shared_list = [self.shared_lists objectAtIndex:row]; cell.textLabel.text = shared_list.list_name; cell.detailTextLabel.text = shared_list.list_members; @@ -111,6 +132,7 @@ completion_fraction = (UILabel *)[cell viewWithTag:1]; // set color based on how complete the list is + /* float frac = (float) shared_list.items_ready / shared_list.items_total; if (frac == 0.0f) completion_fraction.textColor = [UIColor blackColor]; @@ -120,16 +142,25 @@ completion_fraction.textColor = [UIColor orangeColor]; else completion_fraction.textColor = [UIColor greenColor]; + */ completion_fraction.text = [self fraction:shared_list.items_ready denominator:shared_list.items_total]; } else if ([indexPath section] == 1) { - cell = [tableView dequeueReusableCellWithIdentifier:@"IndirectListPrototypeCell" forIndexPath:indexPath]; - SharedList *shared_list = [self.indirect_lists objectAtIndex:row]; cell.textLabel.text = shared_list.list_name; cell.detailTextLabel.text = shared_list.list_members; + + // Modify the look of the off the shelf cell + // Note, a separate prototype cell isn't used here because we + // can potentially swap cells a large number of times, and moving + // is more efficient than recreating. + + // remove the > accessory and the completion fraction + cell.accessoryType = UITableViewCellAccessoryNone; + UILabel *fraction = (UILabel *)[cell viewWithTag:1]; + fraction.hidden = YES; } return cell; @@ -137,7 +168,8 @@ // taken from http://stackoverflow.com/questions/30859359/display-fraction-number-in-uilabel --(NSString *)fraction:(int)numerator denominator:(int)denominator { +-(NSString *)fraction:(int)numerator denominator:(int)denominator +{ NSMutableString *result = [NSMutableString string]; @@ -168,69 +200,64 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - if (section == 0) { - if ([self.shared_lists count] == 0) - return @"you're not in any lists"; - else if ([self.shared_lists count] == 1) - return @"shared list"; - return @"shared lists"; - } - else if (section == 1) { - if ([self.indirect_lists count] == 0) - return @"no other shared lists"; - else if ([self.indirect_lists count] == 1) - return @"other shared list"; - return @"other shared lists"; - } + if (section == 0) + return @"Lists you're in"; + else if (section == 1) + return @"Other lists"; return @""; } - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { - if ([indexPath section] == 0) - return UITableViewCellEditingStyleDelete; - - return UITableViewCellEditingStyleInsert; + // don't have to check the section here because canEditRowAtIndexPath + // already said the section can't be edited + return UITableViewCellEditingStyleDelete; } - (BOOL) tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { - // all lists are editable - return YES; + if ([indexPath section] == 0) + return YES; + return NO; +} + +- (NSString *)tableView:(UITableView *)tableView + titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return @"Leave"; } +// this functions called when delete has been prompted and ok'd - (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { - if (editingStyle == UITableViewCellEditingStyleDelete) { - // Delete the row from the data source - // [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; - - //NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:0 inSection:1]; - //[tableView moveRowAtIndexPath:indexPath toIndexPath:new_index_path]; - - NSIndexPath *path = [self.tableView indexPathForSelectedRow]; - SharedList *selected_list = [self.shared_lists objectAtIndex:[path row]]; + // remove the row from the "lists you're in" section and put it in the + // "other lists" section + NSIndexPath *path = [self.tableView indexPathForSelectedRow]; + SharedList *list = [self.shared_lists objectAtIndex:[path row]]; + NSLog(@"info: leaving '%@'", list.list_name); - NSLog(@"info: leaving list '%@'", selected_list.list_name); + [self.indirect_lists addObject:list]; + [self.shared_lists removeObject:list]; - // send leave list message - [_server send_message:5 contents:selected_list.list_id]; + UITableViewCell *new_cell = [tableView cellForRowAtIndexPath:indexPath]; - // [self.shared_lists removeObjectAtIndex:[indexPath row]]; - } else if (editingStyle == UITableViewCellEditingStyleInsert) { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + // perform row move, the destination is the top of "other lists" + NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:0 inSection:1]; + [tableView moveRowAtIndexPath:indexPath toIndexPath:new_index_path]; - NSIndexPath *path = [self.tableView indexPathForSelectedRow]; - SharedList *selected_list = [self.indirect_lists objectAtIndex:[path row]]; + // remove > accessory and hide the completion fraction + new_cell.accessoryType = UITableViewCellAccessoryNone; + UILabel *fraction = (UILabel *)[new_cell viewWithTag:1]; + fraction.hidden = YES; - NSLog(@"info: joining list '%@'", selected_list.list_name); + // reset editing state back to the default + [tableView setEditing:FALSE animated:TRUE]; - // send join list message - [_server send_message:4 contents:selected_list.list_id]; - } + // send leave list message + [_server send_message:5 contents:list.list_id]; } // In a storyboard-based application, you will often want to do a little preparation before navigation @@ -242,6 +269,7 @@ if ([[segue identifier] isEqualToString:@"show list segue"]) { NSIndexPath *path = [self.tableView indexPathForSelectedRow]; + SharedList *list = [self.shared_lists objectAtIndex:[path row]]; // only list detail table view controller has this method @@ -261,4 +289,15 @@ NSLog(@"preparing for segue"); } +// prevent segues from occurring when non member lists are selected +// this isn't needed if we use 2 different prototype cells +- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender +{ + NSIndexPath *path = [self.tableView indexPathForSelectedRow]; + + if ([path section] == 0) + return YES; + return NO; +} + @end diff --git a/ios-ng/shlist/ShlistServer.m b/ios-ng/shlist/ShlistServer.m @@ -236,19 +236,18 @@ if (msg_type == 4) { NSLog(@"info: got response from join list request, '%@'", output); - for (SharedList *list in shlist_tvc.indirect_lists) { - if (list.list_name == output) { - [shlist_tvc.shared_lists addObject:list]; - [shlist_tvc.indirect_lists removeObject:list]; - break; - } - } - [shlist_tvc.tableView reloadData]; + + // update the already existing row entry with fresh info + // [shlist_tvc.tableView reloadRowsAtIndexPaths: withRowAnimation:]; + } if (msg_type == 5) { - NSLog(@"info: got response from leave list request"); + NSLog(@"info: leave list response '%@'", output); + + // [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; + /* for (SharedList *list in shlist_tvc.shared_lists) { if (list.list_name == output) { [shlist_tvc.indirect_lists addObject:list]; @@ -258,6 +257,7 @@ } } [shlist_tvc.tableView reloadData]; + */ } } break; @@ -348,7 +348,6 @@ // try to find the list member in our address book NSString *name = _phnum_to_name_map[phone_number]; - // NSLog(@"info: %@ -> %@", phone_number, name); if (name) [members addObject:name];