shlist

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

commit a82fa5b6703fa3704e0ceb7e416ebd0d39e666c8
parent 932e525815e5c8d9b527c549986cb1db5399b912
Author: Kyle Milz <kyle@green.krwm.net>
Date:   Wed, 27 Jan 2016 23:13:17 -0700

ios: get leaving/joining lists working

Diffstat:
Mios/shlist/DataStructures.h | 1+
Mios/shlist/MainTableViewController.h | 4++--
Mios/shlist/MainTableViewController.m | 110+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mios/shlist/Network.m | 149+++++++++++++++++++++----------------------------------------------------------
Mios/shlist/main.m | 3++-
5 files changed, 115 insertions(+), 152 deletions(-)

diff --git a/ios/shlist/DataStructures.h b/ios/shlist/DataStructures.h @@ -11,6 +11,7 @@ @property NSNumber *num; @property NSNumber *items_total; @property NSArray *members_phone_nums; +@property NSNumber *num_members; @property bool deadline; @property NSDate *date; @property NSNumber *items_ready; diff --git a/ios/shlist/MainTableViewController.h b/ios/shlist/MainTableViewController.h @@ -9,8 +9,8 @@ - (void) lists_get_finished:(NSArray *)lists; - (void) lists_get_other_finished:(NSArray *)other_lists; - (void) finished_new_list_request:(SharedList *) shlist; -- (void) finished_join_list_request:(SharedList *) shlist; -- (void) finished_leave_list_request:(SharedList *) shlist; +- (void) finished_join_list_request:(NSDictionary *) shlist; +- (void) finished_leave_list_request:(NSDictionary *) shlist; - (IBAction)unwindToList:(UIStoryboardSegue *)segue; diff --git a/ios/shlist/MainTableViewController.m b/ios/shlist/MainTableViewController.m @@ -243,77 +243,104 @@ clickedButtonAtIndex:(NSInteger)buttonIndex // the response for this does all of the heavy row moving work NSMutableDictionary *request = [[NSMutableDictionary alloc] init]; - [request setObject:list.num forKey:@"num"]; + [request setObject:list.num forKey:@"list_num"]; [network_connection send_message:list_join contents:request]; } -- (void) finished_join_list_request:(SharedList *) shlist +- (void) finished_join_list_request:(NSDictionary *) shlist { + NSMutableArray *lists = [_lists objectAtIndex:0]; + NSMutableArray *other_lists = [_lists objectAtIndex:1]; + + // Find the list number we received a response for SharedList *needle = nil; - for (SharedList *temp in [_lists objectAtIndex:1]) { - if (temp.num == shlist.num) { + for (SharedList *temp in other_lists) { + if (temp.num == shlist[@"num"]) { needle = temp; break; } } - // if we received an update from a list id we don't know about, do nothing + // If we received an update from a list id we don't know about, do nothing if (needle == nil) return; - // this has to be done before row moving - [[_lists objectAtIndex:0] addObject:needle]; - [[_lists objectAtIndex:1] removeObject:needle]; - - // [_shared_lists addObject:needle]; - // [_indirect_lists removeObject:needle]; + // Swap between data structures first before moving rows + [lists addObject:needle]; + [other_lists removeObject:needle]; - // get the original cells index path from the matched cell + // Get the cell index path from the matched list cell NSIndexPath *orig_index_path = [self.tableView indexPathForCell:needle.cell]; - // compute new position and start moving row as soon as possible + // Compute new position and start moving row as soon as possible // XXX: sorting - int section_0_rows = [[_lists objectAtIndex:0] count]; - NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:section_0_rows - 1 inSection:0]; - + int new_row_pos = [lists count] - 1; + NSIndexPath *new_index_path = [NSIndexPath indexPathForRow:new_row_pos inSection:0]; [self.tableView moveRowAtIndexPath:orig_index_path toIndexPath:new_index_path]; - // add > accessory indicator, fill in and show completion fraction + // Put any new values into data structs + NSData *name_data = [shlist[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; + needle.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; + // needle.date = + needle.items_ready = shlist[@"items_complete"]; + needle.items_total = shlist[@"items_total"]; + needle.num_members = shlist[@"num_members"]; + + needle.members_phone_nums = shlist[@"members"]; + [self process_members_array:shlist[@"members"] cell:needle.cell]; + + // Add > accessory indicator needle.cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + + // Find fraction UILAbel, populate it and then show it UILabel *fraction = (UILabel *)[needle.cell viewWithTag:4]; - fraction.text = [self fraction:shlist.items_ready denominator:shlist.items_total]; + fraction.text = [self fraction:needle.items_ready denominator:needle.items_total]; fraction.hidden = NO; } -- (void) finished_leave_list_request:(SharedList *) shlist +- (void) finished_leave_list_request:(NSDictionary *) response { + NSMutableArray *lists = [_lists objectAtIndex:0]; + NSMutableArray *other_lists = [_lists objectAtIndex:1]; + SharedList *list = nil; - for (SharedList *temp in [_lists objectAtIndex:0]) { - if (temp.num == shlist.num) { + for (SharedList *temp in lists) { + if (temp.num == response[@"list_num"]) { list = temp; break; } } - if (list == nil) return; - // insert the new object at the beginning to match gui moving below - [[_lists objectAtIndex:1] insertObject:list atIndex:0]; - [[_lists objectAtIndex:0] removeObject:list]; + NSNumber *list_empty = response[@"list_empty"]; + if ([list_empty intValue] == 1) { + // List was empty, delete instead of moving it + [lists removeObject:list]; + + NSIndexPath *old_path = [self.tableView indexPathForCell:list.cell]; + [self.tableView deleteRowsAtIndexPaths:@[old_path] withRowAnimation:UITableViewRowAnimationAutomatic]; + + return; + } + + // Insert the new object at the beginning to match gui moving below + [other_lists insertObject:list atIndex:0]; + [lists removeObject:list]; - // perform row move, the destination is the top of "other lists" + // Perform row move, the destination is the top of "other lists" NSIndexPath *old_path = [self.tableView indexPathForCell:list.cell]; NSIndexPath *new_path = [NSIndexPath indexPathForRow:0 inSection:1]; [self.tableView moveRowAtIndexPath:old_path toIndexPath:new_path]; - // remove > accessory and hide the completion fraction + // Remove > accessory and hide the completion fraction list.cell.accessoryType = UITableViewCellAccessoryNone; UILabel *fraction = (UILabel *)[list.cell viewWithTag:4]; fraction.hidden = YES; - // reset editing state back to the default - [self.tableView setEditing:FALSE animated:TRUE]; + // XXX: update members array to disclude yourself (maybe send it back in response?) + // XXX: Maybe clear out list data that's no longer needed + // XXX: give some visual feedback here what's happening } @@ -360,19 +387,19 @@ clickedButtonAtIndex:(NSInteger)buttonIndex } UILabel *main_label = (UILabel *)[cell viewWithTag:1]; - UILabel *members_label = (UILabel *)[cell viewWithTag:2]; - - // show name and members main_label.text = shared_list.name; - members_label.text = [self process_members_array:shared_list.members_phone_nums]; + + [self process_members_array:shared_list.members_phone_nums cell:cell]; // hang on to a reference, this is needed in the networking gui callbacks shared_list.cell = cell; return cell; } -- (NSString *) process_members_array:(NSArray *)phnum_array +- (void) process_members_array:(NSArray *)phnum_array cell:(UITableViewCell *)cell { + UILabel *members_label = (UILabel *)[cell viewWithTag:2]; + if (!OSAtomicAnd32(0xffff, &_address_book->ready)) { // not ready NSMutableString *output = [[NSMutableString alloc] init]; @@ -384,7 +411,8 @@ clickedButtonAtIndex:(NSInteger)buttonIndex [output appendFormat:@", %@", tmp_phone_number]; } - return output; + members_label.text = output; + return; } // we can do phone number to name mappings @@ -420,7 +448,8 @@ clickedButtonAtIndex:(NSInteger)buttonIndex others, plural]; [members_str appendString:buf]; } - return members_str; + + members_label.text = members_str; } // section header titles @@ -457,7 +486,7 @@ clickedButtonAtIndex:(NSInteger)buttonIndex return UITableViewCellEditingStyleDelete; } -// this functions called when delete has been prompted and ok'd +// This functions called when delete has been prompted and ok'd - (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath @@ -466,10 +495,13 @@ clickedButtonAtIndex:(NSInteger)buttonIndex SharedList *list = [[_lists objectAtIndex:0] objectAtIndex:[indexPath row]]; NSLog(@"info: leaving '%@' list num '%@'", list.name, list.num); - // send leave list message, response will do all heavy lifting + // Send leave list message, response will do all heavy lifting NSMutableDictionary *request = [[NSMutableDictionary alloc] init]; - [request setObject:list.num forKey:@"num"]; + [request setObject:list.num forKey:@"list_num"]; [network_connection send_message:list_leave contents:request]; + + // Reset editing state back to the default + [self.tableView setEditing:FALSE animated:TRUE]; } // customize deletion label text diff --git a/ios/shlist/Network.m b/ios/shlist/Network.m @@ -46,7 +46,7 @@ - (void) connect { - [self info:@"network: connect()"]; + NSLog(@"network: connect()"); connected = 1; CFReadStreamRef readStream; @@ -74,7 +74,7 @@ - (void) disconnect { - [self info:@"network: disconnect()"]; + NSLog(@"network: disconnect()"); connected = 0; [inputShlistStream close]; @@ -130,7 +130,7 @@ [msg appendData:json]; [outputShlistStream write:[msg bytes] maxLength:[msg length]]; - [self info:@"register: sent request"]; + NSLog(@"register: sent request"); // we don't have a device id so we can't do anything yet return false; @@ -141,8 +141,6 @@ if (!connected) [self connect]; - NSMutableData *msg = [NSMutableData data]; - [request setObject:device_id forKey:@"device_id"]; NSError *error = nil; @@ -156,22 +154,23 @@ uint16_t msg_type_network = htons(send_msg_type); uint16_t length = htons([json length]); + NSMutableData *msg = [NSMutableData data]; [msg appendBytes:&version length:2]; [msg appendBytes:&msg_type_network length:2]; [msg appendBytes:&length length:2]; [msg appendData:json]; - [self info:@"network: send_message: type %i, %i bytes", - send_msg_type, [msg length]]; + NSLog(@"network: send_message: type %i, %i bytes", + send_msg_type, [msg length]); if ([outputShlistStream write:[msg bytes] maxLength:[msg length]] == -1) { - [self warn:@"network: write error occurred, trying reconnect"]; + NSLog(@"network: write error occurred, trying reconnect"); if (connected) [self disconnect]; [self connect]; if ([outputShlistStream write:[msg bytes] maxLength:[msg length]] == -1) { - [self warn:@"network: resend failed after reconnect, giving up"]; + NSLog(@"network: resend failed after reconnect, giving up"); return false; } } @@ -190,19 +189,19 @@ switch (eventCode) { case NSStreamEventNone: { - [self debug:@"network: NSStreamEventNone occurred"]; + NSLog(@"network: NSStreamEventNone occurred"); break; } case NSStreamEventOpenCompleted: { - [self debug:@"network: %@ opened", stream_name]; + NSLog(@"network: %@ opened", stream_name); break; } case NSStreamEventHasBytesAvailable: { - [self debug:@"network: %@ has bytes available", stream_name]; + NSLog(@"network: %@ has bytes available", stream_name); if (stream == inputShlistStream) { if (![inputShlistStream hasBytesAvailable]) { - [self warn:@"read: input stream had no bytes available"]; + NSLog(@"read: input stream had no bytes available"); break; } @@ -211,7 +210,7 @@ break; } case NSStreamEventHasSpaceAvailable: { - [self debug:@"network: %@ has space available", stream_name]; + NSLog(@"network: %@ has space available", stream_name); break; } case NSStreamEventErrorOccurred: { @@ -226,15 +225,15 @@ break; NSError *theError = [error_stream streamError]; - [self info:@"network: %@", [NSString stringWithFormat:@"%@ error %i: %@", - stream_name, [theError code], [theError localizedDescription]]]; + NSLog(@"network: %@", [NSString stringWithFormat:@"%@ error %i: %@", + stream_name, [theError code], [theError localizedDescription]]); [self disconnect]; break; } case NSStreamEventEndEncountered: { - [self debug:@"network: %@ end encountered", stream_name]; + NSLog(@"network: %@ end encountered", stream_name); [self disconnect]; break; @@ -251,7 +250,7 @@ buffer_len = [inputShlistStream read:(uint8_t *)header maxLength:6]; if (buffer_len != 6) { - [self error:@"read: didn't return 6 bytes"]; + NSLog(@"read: didn't return 6 bytes"); } uint16_t version = ntohs(header[0]); @@ -259,28 +258,25 @@ uint16_t payload_size = ntohs(header[2]); if (version != 0) { - [self error:@"read: invalid version %i", version]; - } - if (msg_type > 10) { - [self error:@"read: invalid message type %i", msg_type]; - } - if (payload_size > 4095) { - [self error:@"read: %i bytes payload too large", payload_size]; + NSLog(@"read: invalid version %i", version); + return; } - if (payload_size == 0) { - // Payload doesn't contain anything, that's ok + if (msg_type > 11) { + NSLog(@"read: invalid message type %i", msg_type); return; } uint8_t *payload = malloc(payload_size); + + // Accept up to 64KB of data, the maximum size of payload_size buffer_len = [inputShlistStream read:payload maxLength:payload_size]; if (buffer_len != payload_size) { - [self error:@"read: expected %i byte payload but got %i", payload_size, buffer_len]; + NSLog(@"read: expected %i byte payload but got %i", payload_size, buffer_len); return; } - [self info:@"read: payload is %i bytes", buffer_len]; + NSLog(@"read: payload is %i bytes", buffer_len); - NSData *data = [NSData dataWithBytes:payload length:payload_size]; + NSData *data = [NSData dataWithBytesNoCopy:payload length:payload_size]; NSError *error = nil; NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data @@ -290,13 +286,13 @@ return; } - NSString *status = [response objectForKey:@"status"]; + NSString *status = response[@"status"]; if (status == nil) { NSLog(@"read: response did not contain 'status' key"); return; } if ([status compare:@"err"] == 0) { - NSLog(@"read: response error, reason = '%@'", [response valueForKey:@"reason"]); + NSLog(@"read: response error, reason = '%@'", response[@"reason"]); return; } @@ -308,18 +304,18 @@ [self lists_get:response]; } else if (msg_type == list_join) { [self list_join:response]; + } else if (msg_type == list_leave) { + [self list_leave:response]; } else if (msg_type == lists_get_other) { [self lists_get_other:response]; } - - // free((void *)payload); } - (void) device_add:(NSDictionary *)response { device_id = [response objectForKey:@"device_id"]; - [self info:@"device_add: writing new key '%@' to file", device_id]; + NSLog(@"device_add: writing new key '%@' to file", device_id); NSError *error = nil; [device_id writeToFile:device_id_file atomically:YES encoding:NSUTF8StringEncoding error:&error]; @@ -343,7 +339,7 @@ if ([self check_tvc:shlist_tvc]) [shlist_tvc finished_new_list_request:shlist]; - [self info:@"list_add: successfully added new list '%@'", shlist.name]; + NSLog(@"list_add: successfully added new list '%@'", shlist.name); } - (void) lists_get:(NSDictionary *)response @@ -374,97 +370,30 @@ - (void) list_join:(NSDictionary *)response { - SharedList *shlist = [[SharedList alloc] init]; - shlist.num = [response objectForKey:@"num"]; - - // XXX: these need to be sent from the server - // shlist.items_ready = 0; - // shlist.items_total = 99; - // shlist.list_name = <network>; - // shlist.members = <network>; + NSDictionary *list = response[@"list"]; + NSLog(@"network: joined list %@", list[@"num"]); if ([self check_tvc:shlist_tvc]) - [shlist_tvc finished_join_list_request:shlist]; - [self info:@"list_join: joined list %i", shlist.num]; + [shlist_tvc finished_join_list_request:list]; } - (void) list_leave:(NSDictionary *)response { - SharedList *shlist = [[SharedList alloc] init]; - shlist.num = [response objectForKey:@"num"]; - - // XXX: these need to be sent from the server - // shlist.list_name = <network>; - // shlist.members = <network>; + NSNumber *list_num = response[@"list_num"]; + NSLog(@"network: left list %@", list_num); if ([self check_tvc:shlist_tvc]) - [shlist_tvc finished_leave_list_request:shlist]; - [self info:@"list_leave: left list", shlist.num]; + [shlist_tvc finished_leave_list_request:response]; } - (bool) check_tvc:(MainTableViewController *) tvc { if (tvc) return true; - [self warn:@"network: trying to update main_tvc before it's ready, ignoring!"]; + NSLog(@"network: trying to update main_tvc before it's ready, ignoring!"); return false; } -#define LOG_LEVEL_ERROR 0 -#define LOG_LEVEL_WARN 1 -#define LOG_LEVEL_INFO 2 -#define LOG_LEVEL_DEBUG 3 - -#define LOG_LEVEL LOG_LEVEL_INFO - -- (void) debug:(NSString *)fmt, ... -{ - va_list va; - va_start(va, fmt); - NSString *string = [[NSString alloc] initWithFormat:fmt - arguments:va]; - va_end(va); - if (LOG_LEVEL < LOG_LEVEL_DEBUG) - return; - NSLog(@"debug: %@", string); -} - -- (void) info:(NSString *)fmt, ... -{ - va_list va; - va_start(va, fmt); - NSString *string = [[NSString alloc] initWithFormat:fmt - arguments:va]; - va_end(va); - if (LOG_LEVEL < LOG_LEVEL_INFO) - return; - NSLog(@"info: %@", string); -} - -- (void) warn:(NSString *)fmt, ... -{ - va_list va; - va_start(va, fmt); - NSString *string = [[NSString alloc] initWithFormat:fmt - arguments:va]; - va_end(va); - if (LOG_LEVEL < LOG_LEVEL_WARN) - return; - NSLog(@"warn: %@", string); -} - -- (void) error:(NSString *)fmt, ... -{ - va_list va; - va_start(va, fmt); - NSString *string = [[NSString alloc] initWithFormat:fmt - arguments:va]; - va_end(va); - if (LOG_LEVEL < LOG_LEVEL_ERROR) - return; - NSLog(@"error: %@", string); -} - - (void) dealloc { [self disconnect]; diff --git a/ios/shlist/main.m b/ios/shlist/main.m @@ -5,4 +5,4 @@ int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } -} +} +\ No newline at end of file