+ Added preliminary load mechanics for Feedback Images.

This commit is contained in:
moritz haarmann 2014-04-16 11:24:39 +02:00
parent 5038c3e4e9
commit 3aa45bbb9f
5 changed files with 113 additions and 49 deletions

View File

@ -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];
}
}

View File

@ -29,6 +29,8 @@
#import <Foundation/Foundation.h>
@class BITFeedbackMessageAttachment;
/**
* Status for each feedback message
*/
@ -79,5 +81,7 @@ typedef NS_ENUM(NSInteger, BITFeedbackMessageStatus) {
*/
-(void)deleteContents;
-(void)addAttachmentsObject:(BITFeedbackMessageAttachment *)object;
@end

View File

@ -90,5 +90,10 @@
[attachment deleteContents];
}
}
-(void)addAttachmentsObject:(BITFeedbackMessageAttachment *)object{
if (!self.attachments){
self.attachments = [NSArray array];
}
self.attachments = [self.attachments arrayByAddingObject:object];
}
@end

View File

@ -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

View File

@ -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.