mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 03:40:18 +00:00
Improve feedback handling
- Messages deleted on the server will be internally marked as being archived - Pre-send a token for each new message for easier identification and detection of double sending
This commit is contained in:
parent
c8c1ad8e10
commit
d884936029
@ -45,6 +45,7 @@
|
|||||||
#define kBITFeedbackToken @"HockeyFeedbackToken"
|
#define kBITFeedbackToken @"HockeyFeedbackToken"
|
||||||
#define kBITFeedbackName @"HockeyFeedbackName"
|
#define kBITFeedbackName @"HockeyFeedbackName"
|
||||||
#define kBITFeedbackEmail @"HockeyFeedbackEmail"
|
#define kBITFeedbackEmail @"HockeyFeedbackEmail"
|
||||||
|
#define kBITFeedbackLastMessageID @"HockeyFeedbackLastMessageID"
|
||||||
|
|
||||||
|
|
||||||
@implementation BITFeedbackManager {
|
@implementation BITFeedbackManager {
|
||||||
@ -144,6 +145,20 @@
|
|||||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private methods
|
||||||
|
|
||||||
|
- (NSString *)uuidString {
|
||||||
|
CFUUIDRef theToken = CFUUIDCreate(NULL);
|
||||||
|
CFStringRef stringUUID = CFUUIDCreateString(NULL, theToken);
|
||||||
|
CFRelease(theToken);
|
||||||
|
|
||||||
|
return [(NSString *)stringUUID autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)uuidAsLowerCaseAndShortened {
|
||||||
|
return [[[self uuidString] lowercaseString] stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Feedback Modal UI
|
#pragma mark - Feedback Modal UI
|
||||||
|
|
||||||
- (BITFeedbackListViewController *)feedbackListViewController:(BOOL)modal {
|
- (BITFeedbackListViewController *)feedbackListViewController:(BOOL)modal {
|
||||||
@ -332,13 +347,18 @@
|
|||||||
NSDate *date1 = [obj1 date];
|
NSDate *date1 = [obj1 date];
|
||||||
NSDate *date2 = [obj2 date];
|
NSDate *date2 = [obj2 date];
|
||||||
|
|
||||||
// not send and send in progress messages on top, sorted by date
|
// not send, in conflict and send in progress messages on top, sorted by date
|
||||||
// read and unread on bottom, sorted by date
|
// read and unread on bottom, sorted by date
|
||||||
|
// archived on the very bottom
|
||||||
|
|
||||||
if ([obj1 status] >= BITFeedbackMessageStatusSendInProgress && [obj2 status] < BITFeedbackMessageStatusSendInProgress) {
|
if ([obj1 status] >= BITFeedbackMessageStatusSendInProgress && [obj2 status] < BITFeedbackMessageStatusSendInProgress) {
|
||||||
return NSOrderedAscending;
|
return NSOrderedAscending;
|
||||||
} else if ([obj1 status] < BITFeedbackMessageStatusSendInProgress && [obj2 status] >= BITFeedbackMessageStatusSendInProgress) {
|
} else if ([obj1 status] < BITFeedbackMessageStatusSendInProgress && [obj2 status] >= BITFeedbackMessageStatusSendInProgress) {
|
||||||
return NSOrderedDescending;
|
return NSOrderedDescending;
|
||||||
|
} else if ([obj1 status] == BITFeedbackMessageStatusArchived && [obj2 status] < BITFeedbackMessageStatusArchived) {
|
||||||
|
return NSOrderedDescending;
|
||||||
|
} else if ([obj1 status] < BITFeedbackMessageStatusArchived && [obj2 status] == BITFeedbackMessageStatusArchived) {
|
||||||
|
return NSOrderedAscending;
|
||||||
} else {
|
} else {
|
||||||
return (NSInteger)[date2 compare:date1];
|
return (NSInteger)[date2 compare:date1];
|
||||||
}
|
}
|
||||||
@ -390,6 +410,14 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)markSendInProgressMessagesAsInConflict {
|
||||||
|
// make sure message that may have not been send successfully, get back into the right state to be send again
|
||||||
|
[_feedbackList enumerateObjectsUsingBlock:^(id objMessage, NSUInteger messagesIdx, BOOL *stop) {
|
||||||
|
if ([(BITFeedbackMessage *)objMessage status] == BITFeedbackMessageStatusSendInProgress)
|
||||||
|
[(BITFeedbackMessage *)objMessage setStatus:BITFeedbackMessageStatusInConflict];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - User
|
#pragma mark - User
|
||||||
|
|
||||||
@ -479,19 +507,21 @@
|
|||||||
BITFeedbackMessage *thisMessage = [self messageWithID:messageID];
|
BITFeedbackMessage *thisMessage = [self messageWithID:messageID];
|
||||||
if (!thisMessage) {
|
if (!thisMessage) {
|
||||||
// check if this is a message that was sent right now
|
// check if this is a message that was sent right now
|
||||||
__block BITFeedbackMessage *matchingSendInProgressMessage = nil;
|
__block BITFeedbackMessage *matchingSendInProgressOrInConflictMessage = nil;
|
||||||
|
|
||||||
|
// TODO: match messages in state conflict
|
||||||
|
|
||||||
[messagesSendInProgress enumerateObjectsUsingBlock:^(id objSendInProgressMessage, NSUInteger messagesSendInProgressIdx, BOOL *stop) {
|
[messagesSendInProgress enumerateObjectsUsingBlock:^(id objSendInProgressMessage, NSUInteger messagesSendInProgressIdx, BOOL *stop) {
|
||||||
if ([[(NSDictionary *)objMessage objectForKey:@"text"] isEqualToString:[(BITFeedbackMessage *)objSendInProgressMessage text]]) {
|
if ([[(NSDictionary *)objMessage objectForKey:@"token"] isEqualToString:[(BITFeedbackMessage *)objSendInProgressMessage token]]) {
|
||||||
matchingSendInProgressMessage = objSendInProgressMessage;
|
matchingSendInProgressOrInConflictMessage = objSendInProgressMessage;
|
||||||
*stop = YES;
|
*stop = YES;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (matchingSendInProgressMessage) {
|
if (matchingSendInProgressOrInConflictMessage) {
|
||||||
matchingSendInProgressMessage.date = [self parseRFC3339Date:[(NSDictionary *)objMessage objectForKey:@"created_at"]];
|
matchingSendInProgressOrInConflictMessage.date = [self parseRFC3339Date:[(NSDictionary *)objMessage objectForKey:@"created_at"]];
|
||||||
matchingSendInProgressMessage.id = messageID;
|
matchingSendInProgressOrInConflictMessage.id = messageID;
|
||||||
matchingSendInProgressMessage.status = BITFeedbackMessageStatusRead;
|
matchingSendInProgressOrInConflictMessage.status = BITFeedbackMessageStatusRead;
|
||||||
} else {
|
} else {
|
||||||
BITFeedbackMessage *message = [[[BITFeedbackMessage alloc] init] autorelease];
|
BITFeedbackMessage *message = [[[BITFeedbackMessage alloc] init] autorelease];
|
||||||
message.text = [(NSDictionary *)objMessage objectForKey:@"text"];
|
message.text = [(NSDictionary *)objMessage objectForKey:@"text"];
|
||||||
@ -554,7 +584,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withText:(NSString *)text completionHandler:(void (^)(NSError *err))completionHandler {
|
- (void)sendNetworkRequestWithHTTPMethod:(NSString *)httpMethod withMessage:(BITFeedbackMessage *)message completionHandler:(void (^)(NSError *err))completionHandler {
|
||||||
NSString *boundary = @"----FOO";
|
NSString *boundary = @"----FOO";
|
||||||
|
|
||||||
_networkRequestInProgress = YES;
|
_networkRequestInProgress = YES;
|
||||||
@ -582,7 +612,7 @@
|
|||||||
[request setValue:@"Hockey/iOS" forHTTPHeaderField:@"User-Agent"];
|
[request setValue:@"Hockey/iOS" forHTTPHeaderField:@"User-Agent"];
|
||||||
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
|
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
|
||||||
|
|
||||||
if (text) {
|
if (message) {
|
||||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
|
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
|
||||||
[request setValue:contentType forHTTPHeaderField:@"Content-type"];
|
[request setValue:contentType forHTTPHeaderField:@"Content-type"];
|
||||||
|
|
||||||
@ -593,7 +623,8 @@
|
|||||||
[postBody appendData:[self appendPostValue:[self getDevicePlatform] forKey:@"model"]];
|
[postBody appendData:[self appendPostValue:[self getDevicePlatform] forKey:@"model"]];
|
||||||
[postBody appendData:[self appendPostValue:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0] forKey:@"lang"]];
|
[postBody appendData:[self appendPostValue:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0] forKey:@"lang"]];
|
||||||
[postBody appendData:[self appendPostValue:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:@"bundle_version"]];
|
[postBody appendData:[self appendPostValue:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:@"bundle_version"]];
|
||||||
[postBody appendData:[self appendPostValue:text forKey:@"text"]];
|
[postBody appendData:[self appendPostValue:[message text] forKey:@"text"]];
|
||||||
|
[postBody appendData:[self appendPostValue:[message token] forKey:@"message_token"]];
|
||||||
|
|
||||||
if (self.userName) {
|
if (self.userName) {
|
||||||
[postBody appendData:[self appendPostValue:self.userName forKey:@"name"]];
|
[postBody appendData:[self appendPostValue:self.userName forKey:@"name"]];
|
||||||
@ -619,6 +650,28 @@
|
|||||||
if (statusCode == 404) {
|
if (statusCode == 404) {
|
||||||
// thread has been deleted, we archive it
|
// thread has been deleted, we archive it
|
||||||
[self updateMessageListFromResponse:nil];
|
[self updateMessageListFromResponse:nil];
|
||||||
|
} else if (statusCode == 409) {
|
||||||
|
// we submitted a message that is already on the server, mark it as being in conflict and resolve it with another fetch
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[self markSendInProgressMessagesAsInConflict];
|
||||||
|
[self saveMessages];
|
||||||
|
[self performSelector:@selector(fetchMessageUpdates) withObject:nil afterDelay:0.2];
|
||||||
} else if ([responseData length]) {
|
} else if ([responseData length]) {
|
||||||
NSString *responseString = [[[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding: NSUTF8StringEncoding] autorelease];
|
NSString *responseString = [[[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding: NSUTF8StringEncoding] autorelease];
|
||||||
BITHockeyLog(@"INFO: Received API response: %@", responseString);
|
BITHockeyLog(@"INFO: Received API response: %@", responseString);
|
||||||
@ -662,7 +715,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self sendNetworkRequestWithHTTPMethod:@"GET"
|
[self sendNetworkRequestWithHTTPMethod:@"GET"
|
||||||
withText:nil
|
withMessage:nil
|
||||||
completionHandler:^(NSError *err){
|
completionHandler:^(NSError *err){
|
||||||
// inform the UI to update its data in case the list is already showing
|
// inform the UI to update its data in case the list is already showing
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:BITHockeyFeedbackMessagesLoadingFinished object:nil];
|
||||||
@ -696,7 +749,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self sendNetworkRequestWithHTTPMethod:httpMethod
|
[self sendNetworkRequestWithHTTPMethod:httpMethod
|
||||||
withText:[messageToSend text]
|
withMessage:messageToSend
|
||||||
completionHandler:^(NSError *err){
|
completionHandler:^(NSError *err){
|
||||||
if (err) {
|
if (err) {
|
||||||
[self markSendInProgressMessagesAsPending];
|
[self markSendInProgressMessagesAsPending];
|
||||||
@ -714,6 +767,7 @@
|
|||||||
BITFeedbackMessage *message = [[[BITFeedbackMessage alloc] init] autorelease];
|
BITFeedbackMessage *message = [[[BITFeedbackMessage alloc] init] autorelease];
|
||||||
message.text = text;
|
message.text = text;
|
||||||
[message setStatus:BITFeedbackMessageStatusSendPending];
|
[message setStatus:BITFeedbackMessageStatusSendPending];
|
||||||
|
[message setToken:[self uuidAsLowerCaseAndShortened]];
|
||||||
[message setUserMessage:YES];
|
[message setUserMessage:YES];
|
||||||
|
|
||||||
[_feedbackList addObject:message];
|
[_feedbackList addObject:message];
|
||||||
|
|||||||
@ -32,14 +32,16 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
// default and new messages from SDK per default
|
// default and new messages from SDK per default
|
||||||
BITFeedbackMessageStatusSendPending = 0,
|
BITFeedbackMessageStatusSendPending = 0,
|
||||||
|
// message is in conflict, happens if the message is already stored on the server and tried sending it again
|
||||||
|
BITFeedbackMessageStatusInConflict = 1,
|
||||||
// sending of message is in progress
|
// sending of message is in progress
|
||||||
BITFeedbackMessageStatusSendInProgress = 1,
|
BITFeedbackMessageStatusSendInProgress = 2,
|
||||||
// new messages from server
|
// new messages from server
|
||||||
BITFeedbackMessageStatusUnread = 2,
|
BITFeedbackMessageStatusUnread = 3,
|
||||||
// messages from server once read and new local messages once successful send from SDK
|
// messages from server once read and new local messages once successful send from SDK
|
||||||
BITFeedbackMessageStatusRead = 3,
|
BITFeedbackMessageStatusRead = 4,
|
||||||
// message is archived, happens if the thread is deleted from the server
|
// message is archived, happens if the thread is deleted from the server
|
||||||
BITFeedbackMessageStatusArchived = 4
|
BITFeedbackMessageStatusArchived = 5
|
||||||
} BITFeedbackMessageStatus;
|
} BITFeedbackMessageStatus;
|
||||||
|
|
||||||
@interface BITFeedbackMessage : NSObject {
|
@interface BITFeedbackMessage : NSObject {
|
||||||
@ -50,6 +52,7 @@ typedef enum {
|
|||||||
@property (nonatomic, copy) NSString *email;
|
@property (nonatomic, copy) NSString *email;
|
||||||
@property (nonatomic, copy) NSDate *date;
|
@property (nonatomic, copy) NSDate *date;
|
||||||
@property (nonatomic, copy) NSNumber *id;
|
@property (nonatomic, copy) NSNumber *id;
|
||||||
|
@property (nonatomic, copy) NSString *token;
|
||||||
@property (nonatomic) BITFeedbackMessageStatus status;
|
@property (nonatomic) BITFeedbackMessageStatus status;
|
||||||
@property (nonatomic) BOOL userMessage;
|
@property (nonatomic) BOOL userMessage;
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
_name = nil;
|
_name = nil;
|
||||||
_email = nil;
|
_email = nil;
|
||||||
_date = nil;
|
_date = nil;
|
||||||
|
_token = nil;
|
||||||
_id = [[NSNumber alloc] initWithInteger:0];
|
_id = [[NSNumber alloc] initWithInteger:0];
|
||||||
_status = BITFeedbackMessageStatusSendPending;
|
_status = BITFeedbackMessageStatusSendPending;
|
||||||
_userMessage = NO;
|
_userMessage = NO;
|
||||||
@ -53,6 +54,7 @@
|
|||||||
[_email release], _email = nil;
|
[_email release], _email = nil;
|
||||||
[_date release], _date = nil;
|
[_date release], _date = nil;
|
||||||
[_id release], _id = nil;
|
[_id release], _id = nil;
|
||||||
|
[_token release], _token = nil;
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
@ -68,6 +70,7 @@
|
|||||||
[encoder encodeObject:self.id forKey:@"id"];
|
[encoder encodeObject:self.id forKey:@"id"];
|
||||||
[encoder encodeInteger:self.status forKey:@"status"];
|
[encoder encodeInteger:self.status forKey:@"status"];
|
||||||
[encoder encodeBool:self.userMessage forKey:@"userMessage"];
|
[encoder encodeBool:self.userMessage forKey:@"userMessage"];
|
||||||
|
[encoder encodeObject:self.token forKey:@"token"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithCoder:(NSCoder *)decoder {
|
- (id)initWithCoder:(NSCoder *)decoder {
|
||||||
@ -79,6 +82,7 @@
|
|||||||
self.id = [decoder decodeObjectForKey:@"id"];
|
self.id = [decoder decodeObjectForKey:@"id"];
|
||||||
self.status = [decoder decodeIntegerForKey:@"status"];
|
self.status = [decoder decodeIntegerForKey:@"status"];
|
||||||
self.userMessage = [decoder decodeBoolForKey:@"userMessage"];
|
self.userMessage = [decoder decodeBoolForKey:@"userMessage"];
|
||||||
|
self.token = [decoder decodeObjectForKey:@"token"];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user