From 3aa45bbb9f909a2c717f3e64a2f8c4554d1ff6e1 Mon Sep 17 00:00:00 2001 From: moritz haarmann Date: Wed, 16 Apr 2014 11:24:39 +0200 Subject: [PATCH] + Added preliminary load mechanics for Feedback Images. --- Classes/BITFeedbackManager.m | 137 ++++++++++++++++--------- Classes/BITFeedbackMessage.h | 4 + Classes/BITFeedbackMessage.m | 7 +- Classes/BITFeedbackMessageAttachment.h | 3 + Classes/BITFeedbackMessageAttachment.m | 11 +- 5 files changed, 113 insertions(+), 49 deletions(-) diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 7d18f91c7e..5cd2091d1f 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -81,7 +81,7 @@ _requireUserEmail = BITFeedbackUserDataElementOptional; _showAlertOnIncomingMessages = YES; _showFirstRequiredPresentationModal = YES; - + _disableFeedbackManager = NO; _networkRequestInProgress = NO; _incomingMessagesAlertShowing = NO; @@ -90,9 +90,9 @@ _lastMessageID = nil; self.feedbackList = [NSMutableArray array]; - + _fileManager = [[NSFileManager alloc] init]; - + _settingsFile = [bit_settingsDir() stringByAppendingPathComponent:BITHOCKEY_FEEDBACK_SETTINGS]; _userID = nil; @@ -152,12 +152,12 @@ } if(nil == _networkDidBecomeReachableObserver) { _networkDidBecomeReachableObserver = [[NSNotificationCenter defaultCenter] addObserverForName:BITHockeyNetworkDidBecomeReachableNotification - object:nil - queue:NSOperationQueue.mainQueue - usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf didBecomeActiveActions]; - }]; + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf didBecomeActiveActions]; + }]; } } @@ -228,9 +228,9 @@ } BITFeedbackComposeViewController *composeView = [self feedbackComposeViewController]; [composeView prepareWithItems:items]; - + [self showView:composeView]; - + } - (void)showFeedbackComposeViewWithGeneratedScreenshot { @@ -242,7 +242,7 @@ - (void)startManager { if ([self isFeedbackManagerDisabled]) return; - + [self registerObservers]; // we are already delayed, so the notification already came in and this won't invoked twice @@ -289,12 +289,12 @@ userIDForHockeyManager:[BITHockeyManager sharedHockeyManager] componentManager:self]; } - + if (userID) { availableViaDelegate = YES; self.userID = userID; } - + return availableViaDelegate; } @@ -306,16 +306,16 @@ if ([BITHockeyManager sharedHockeyManager].delegate && [[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userNameForHockeyManager:componentManager:)]) { userName = [[BITHockeyManager sharedHockeyManager].delegate - userNameForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; + userNameForHockeyManager:[BITHockeyManager sharedHockeyManager] + componentManager:self]; } - + if (userName) { availableViaDelegate = YES; self.userName = userName; self.requireUserName = BITFeedbackUserDataElementDontShow; } - + return availableViaDelegate; } @@ -323,20 +323,20 @@ BOOL availableViaDelegate = NO; NSString *userEmail = [self stringValueFromKeychainForKey:kBITHockeyMetaUserEmail]; - + if ([BITHockeyManager sharedHockeyManager].delegate && [[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userEmailForHockeyManager:componentManager:)]) { userEmail = [[BITHockeyManager sharedHockeyManager].delegate userEmailForHockeyManager:[BITHockeyManager sharedHockeyManager] componentManager:self]; } - + if (userEmail) { availableViaDelegate = YES; self.userEmail = userEmail; self.requireUserEmail = BITFeedbackUserDataElementDontShow; } - + return availableViaDelegate; } @@ -344,7 +344,7 @@ [self updateUserIDUsingKeychainAndDelegate]; [self updateUserNameUsingKeychainAndDelegate]; [self updateUserEmailUsingKeychainAndDelegate]; - + // if both values are shown via the delegates, we never ever did ask and will never ever ask for user data if (self.requireUserName == BITFeedbackUserDataElementDontShow && self.requireUserEmail == BITFeedbackUserDataElementDontShow) { @@ -361,7 +361,7 @@ if (![_fileManager fileExistsAtPath:_settingsFile]) return; - + NSData *codedData = [[NSData alloc] initWithContentsOfFile:_settingsFile]; if (codedData == nil) return; @@ -373,7 +373,7 @@ @catch (NSException *exception) { return; } - + if (!userIDViaDelegate) { if ([unarchiver containsValueForKey:kBITFeedbackUserID]) { self.userID = [unarchiver decodeObjectForKey:kBITFeedbackUserID]; @@ -389,7 +389,7 @@ } self.userName = [self stringValueFromKeychainForKey:kBITFeedbackName]; } - + if (!userEmailViaDelegate) { if ([unarchiver containsValueForKey:kBITFeedbackEmail]) { self.userEmail = [unarchiver decodeObjectForKey:kBITFeedbackEmail]; @@ -409,7 +409,7 @@ if ([unarchiver containsValueForKey:kBITFeedbackAppID]) { NSString *appID = [unarchiver decodeObjectForKey:kBITFeedbackAppID]; - + // the stored thread is from another application identifier, so clear the token // which will cause the new posts to create a new thread on the server for the // current app identifier @@ -432,9 +432,9 @@ // inform the UI to update its data in case the list is already showing [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil]; } - + [unarchiver finishDecoding]; - + if (!self.lastCheck) { self.lastCheck = [NSDate distantPast]; } @@ -446,7 +446,7 @@ NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; - + if (_didAskUserData) [archiver encodeObject:[NSNumber numberWithBool:YES] forKey:kBITFeedbackUserDataAsked]; @@ -631,7 +631,7 @@ - (BOOL)isManualUserDataAvailable { [self updateAppDefinedUserData]; - + if ((self.requireUserName != BITFeedbackUserDataElementDontShow && self.userName) || (self.requireUserEmail != BITFeedbackUserDataElementDontShow && self.userEmail)) return YES; @@ -645,23 +645,23 @@ - (void)updateMessageListFromResponse:(NSDictionary *)jsonDictionary { if (!jsonDictionary) { // nil is used when the server returns 404, so we need to mark all existing threads as archives and delete the discussion token - + NSArray *messagesSendInProgress = [self messagesWithStatus:BITFeedbackMessageStatusSendInProgress]; NSInteger pendingMessagesCount = [messagesSendInProgress count] + [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + [self markSendInProgressMessagesAsPending]; [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { if ([(BITFeedbackMessage *)objMessage status] != BITFeedbackMessageStatusSendPending) [(BITFeedbackMessage *)objMessage setStatus:BITFeedbackMessageStatusArchived]; }]; - + if ([self token]) { self.token = nil; } NSInteger pendingMessagesCountAfterProcessing = [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + [self saveMessages]; // check if this request was successful and we have more messages pending and continue if positive @@ -710,7 +710,7 @@ *stop2 = YES; } }]; - + if (matchingSendInProgressOrInConflictMessage) { matchingSendInProgressOrInConflictMessage.date = [self parseRFC3339Date:[(NSDictionary *)objMessage objectForKey:@"created_at"]]; matchingSendInProgressOrInConflictMessage.id = messageID; @@ -734,6 +734,15 @@ message.id = [(NSDictionary *)objMessage objectForKey:@"id"]; message.status = BITFeedbackMessageStatusUnread; + for (NSDictionary *attachmentData in objMessage[@"attachments"]){ + BITFeedbackMessageAttachment *newAttachment = [BITFeedbackMessageAttachment new]; + newAttachment.originalFilename = attachmentData[@"file_name"]; + newAttachment.id = attachmentData[@"id"]; + newAttachment.sourceURL = attachmentData[@"url"]; + newAttachment.contentType = @"image/jpg"; + [message addAttachmentsObject:newAttachment]; + } + [_feedbackList addObject:message]; newMessage = YES; @@ -750,7 +759,7 @@ [self sortFeedbackList]; [self updateLastMessageID]; - + // we got a new incoming message, trigger user notification system if (newMessage) { // check if the latest message is from the users own email address, then don't show an alert since he answered using his own email @@ -759,12 +768,12 @@ BITFeedbackMessage *latestMessage = [self lastMessageHavingID]; if (self.userEmail && latestMessage.email && [self.userEmail compare:latestMessage.email] == NSOrderedSame) latestMessageFromUser = YES; - + if (!latestMessageFromUser) { if([self.delegate respondsToSelector:@selector(feedbackManagerDidReceiveNewFeedback:)]) { [self.delegate feedbackManagerDidReceiveNewFeedback:self]; } - + if(self.showAlertOnIncomingMessages && !self.currentFeedbackListViewController && !self.currentFeedbackComposeViewController) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageTitle") message:BITHockeyLocalizedString(@"HockeyFeedbackNewMessageText") @@ -780,7 +789,7 @@ } NSInteger pendingMessagesCountAfterProcessing = [[self messagesWithStatus:BITFeedbackMessageStatusSendPending] count]; - + // check if this request was successful and we have more messages pending and continue if positive if (pendingMessagesCount > pendingMessagesCountAfterProcessing && pendingMessagesCountAfterProcessing > 0) { [self performSelector:@selector(submitPendingMessages) withObject:nil afterDelay:0.1]; @@ -790,18 +799,52 @@ [self markSendInProgressMessagesAsPending]; } + [self synchronizeMissingAttachments]; + [self saveMessages]; - + return; } +/** + Load all attachments without any local data to have them available. + */ +-(BOOL)synchronizeMissingAttachments { + // Extract all Attachments. + NSMutableArray *allAttachments = [NSMutableArray new]; + for (int i = 0; i < [self numberOfMessages]; i++){ + BITFeedbackMessage *message = [self messageAtIndex:i]; + for (BITFeedbackMessageAttachment *attachment in message.attachments){ + if (attachment.needsLoadingFromURL){ + [allAttachments addObject:attachment]; + } + } + } + + for (BITFeedbackMessageAttachment *attachment in allAttachments){ + // we will just update the objects here and perform a save after each successful load operation. + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:attachment.sourceURL]]; + __weak BITFeedbackManager *weakSelf = self; + [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *err) { + if (responseData.length){ + [attachment replaceData:responseData]; + [weakSelf saveMessages]; + + } + }]; + + } + return NO; +} + - (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withMessage:(BITFeedbackMessage *)message completionHandler:(void (^)(NSError *err))completionHandler { NSString *boundary = @"----FOO"; _networkRequestInProgress = YES; // inform the UI to update its data in case the list is already showing [[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingStarted object:nil]; - + NSString *tokenParameter = @""; if ([self token]) { tokenParameter = [NSString stringWithFormat:@"/%@", [self token]]; @@ -879,7 +922,7 @@ } [postBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - + [request setHTTPBody:postBody]; } @@ -902,14 +945,14 @@ if (!self.token) { // set the token to the first message token, since this is identical __block NSString *token = nil; - + [_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) { if ([(BITFeedbackMessage *)objMessage status] == BITFeedbackMessageStatusSendInProgress) { token = [(BITFeedbackMessage *)objMessage token]; *stop = YES; } }]; - + if (token) { self.token = token; } @@ -982,7 +1025,7 @@ [self saveMessages]; NSArray *pendingMessages = [self messagesWithStatus:BITFeedbackMessageStatusSendPending]; - + if ([pendingMessages count] > 0) { // we send one message at a time BITFeedbackMessage *messageToSend = [pendingMessages objectAtIndex:0]; @@ -1041,11 +1084,11 @@ } } -#pragma mark - Observation Handling +#pragma mark - Observation Handling -(void)setFeedbackObservationMode:(BITFeedbackObservationMode)mode { if (mode == BITFeedbackObservationModeOnScreenshot){ - // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenshotNotificationReceived:) name:UIApplicationUserDidTakeScreenshotNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenshotNotificationReceived:) name:UIApplicationUserDidTakeScreenshotNotification object:nil]; } } diff --git a/Classes/BITFeedbackMessage.h b/Classes/BITFeedbackMessage.h index baa6d6fdef..44ff0e0d7e 100644 --- a/Classes/BITFeedbackMessage.h +++ b/Classes/BITFeedbackMessage.h @@ -29,6 +29,8 @@ #import +@class BITFeedbackMessageAttachment; + /** * Status for each feedback message */ @@ -79,5 +81,7 @@ typedef NS_ENUM(NSInteger, BITFeedbackMessageStatus) { */ -(void)deleteContents; +-(void)addAttachmentsObject:(BITFeedbackMessageAttachment *)object; + @end diff --git a/Classes/BITFeedbackMessage.m b/Classes/BITFeedbackMessage.m index eb44289600..446fb0da3c 100644 --- a/Classes/BITFeedbackMessage.m +++ b/Classes/BITFeedbackMessage.m @@ -90,5 +90,10 @@ [attachment deleteContents]; } } - +-(void)addAttachmentsObject:(BITFeedbackMessageAttachment *)object{ + if (!self.attachments){ + self.attachments = [NSArray array]; + } + self.attachments = [self.attachments arrayByAddingObject:object]; +} @end diff --git a/Classes/BITFeedbackMessageAttachment.h b/Classes/BITFeedbackMessageAttachment.h index 4a9a8ea646..1d05641971 100644 --- a/Classes/BITFeedbackMessageAttachment.h +++ b/Classes/BITFeedbackMessageAttachment.h @@ -35,6 +35,7 @@ @property (nonatomic, copy) NSNumber *id; @property (nonatomic, copy) NSString *originalFilename; @property (nonatomic, copy) NSString *contentType; +@property (nonatomic, copy) NSString *sourceURL; @property (nonatomic, readonly) NSData *data; @property (readonly) UIImage *imageRepresentation; @@ -48,4 +49,6 @@ - (void)deleteContents; +-(BOOL)needsLoadingFromURL; + @end diff --git a/Classes/BITFeedbackMessageAttachment.m b/Classes/BITFeedbackMessageAttachment.m index ba63a7f286..3350b96d24 100644 --- a/Classes/BITFeedbackMessageAttachment.m +++ b/Classes/BITFeedbackMessageAttachment.m @@ -39,6 +39,7 @@ @property (nonatomic, strong) NSData *internalData; @property (nonatomic, copy) NSString *filename; + @end @implementation BITFeedbackMessageAttachment @@ -92,12 +93,18 @@ self.thumbnailRepresentations = [NSMutableDictionary new]; } +-(BOOL)needsLoadingFromURL { + return (self.sourceURL); +} + #pragma mark NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.contentType forKey:@"contentType"]; [aCoder encodeObject:self.filename forKey:@"filename"]; [aCoder encodeObject:self.originalFilename forKey:@"originalFilename"]; + [aCoder encodeObject:self.sourceURL forKey:@"url"]; + } @@ -109,6 +116,8 @@ self.filename = [aDecoder decodeObjectForKey:@"filename"]; self.thumbnailRepresentations = [NSMutableDictionary new]; self.originalFilename = [aDecoder decodeObjectForKey:@"originalFilename"]; + self.sourceURL = [aDecoder decodeObjectForKey:@"sourceURL"]; + } return self; @@ -117,7 +126,7 @@ #pragma mark - Thubmnails / Image Representation - (UIImage *)imageRepresentation { - if ([self.contentType rangeOfString:@"image"].location != NSNotFound){ + if ([self.contentType rangeOfString:@"image"].location != NSNotFound || [self.sourceURL rangeOfString:@"jpeg"].location != NSNotFound){ return [UIImage imageWithData:self.data]; } else { return bit_imageNamed(@"feedbackActiviy.png", BITHOCKEYSDK_BUNDLE); // TODO add another placeholder.