Implement profile picture uploading

pull/65/head
Niels Andriesse 6 years ago committed by Maxim Shishmarev
parent d29202bfff
commit 7e9c0b281e

@ -348,7 +348,6 @@
[avatarView autoSetDimension:ALDimensionHeight toSize:kLargeAvatarSize];
avatarView.contactID = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
/**
if (!localProfileAvatarImage) {
UIImage *cameraImage = [UIImage imageNamed:@"settings-avatar-camera"];
UIImageView *cameraImageView = [[UIImageView alloc] initWithImage:cameraImage];
@ -356,7 +355,6 @@
[cameraImageView autoPinTrailingToEdgeOfView:avatarView];
[cameraImageView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:avatarView];
}
*/
UIView *nameView = [UIView containerView];
[cell.contentView addSubview:nameView];

@ -154,12 +154,10 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(avatarRowTapped:)]];
avatarRow.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"avatarRow");
// Loki: Disable setting the avatar
// [rows addObject:avatarRow];
[rows addObject:avatarRow];
UILabel *avatarLabel = [UILabel new];
avatarLabel.text = NSLocalizedString(
@"PROFILE_VIEW_PROFILE_AVATAR_FIELD", @"Label for the profile avatar field of the profile view.");
avatarLabel.text = NSLocalizedString(@"Profile Picture", @"");
avatarLabel.textColor = Theme.primaryColor;
avatarLabel.font = [UIFont ows_mediumFontWithSize:fontSizePoints];
[avatarRow addSubview:avatarLabel];
@ -601,8 +599,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
- (nullable NSString *)avatarActionSheetTitle
{
return NSLocalizedString(
@"PROFILE_VIEW_AVATAR_ACTIONSHEET_TITLE", @"Action Sheet title prompting the user for a profile avatar");
return NSLocalizedString(@"Set Profile Picture", @"");
}
- (void)avatarDidChange:(UIImage *)image
@ -626,7 +623,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
- (NSString *)clearAvatarActionLabel
{
return NSLocalizedString(@"PROFILE_VIEW_CLEAR_AVATAR", @"Label for action that clear's the user's profile avatar");
return NSLocalizedString(@"Clear Profile Picture", @"");
}
- (void)clearAvatar

@ -2650,3 +2650,6 @@
"Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"Multi Device Limit Reached" = "Multi Device Limit Reached";
"It's currently not allowed to link more than one device." = "It's currently not allowed to link more than one device.";
"Profile Picture" = "Profile Picture";
"Set Profile Picture" = "Set Profile Picture";
"Clear Profile Picture" = "Clear Profile Picture";

@ -294,11 +294,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSUserProfile *userProfile = self.localUserProfile;
OWSAssertDebug(userProfile);
// Loki: We don't support avatar uploads yet
OWSLogVerbose(@"Updating local profile on service with no avatar.");
tryToUpdateService(nil, nil);
/* ========== Original code ===============
if (avatarImage) {
// If we have a new avatar image, we must first:
//
@ -343,7 +338,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSLogVerbose(@"Updating local profile on service with no avatar.");
tryToUpdateService(nil, nil);
}
*/
}
- (void)writeAvatarToDisk:(UIImage *)avatar
@ -402,6 +396,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSAssertDebug(failureBlock);
OWSAssertDebug(avatarData == nil || avatarData.length > 0);
/*
// We want to clear the local user's profile avatar as soon as
// we request the upload form, since that request clears our
// avatar on the service.
@ -412,8 +407,26 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSUserProfile *userProfile = self.localUserProfile;
[userProfile updateWithAvatarUrlPath:nil avatarFileName:nil dbConnection:self.dbConnection completion:nil];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
*/
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LKStorageAPI setProfilePicture:avatarData]
.thenOn(dispatch_get_main_queue(), ^(NSString *url) {
successBlock(url);
})
.catchOn(dispatch_get_main_queue(), ^(id result) {
// There appears to be a bug in PromiseKit that sometimes causes catchOn
// to be invoked with the fulfilled promise's value as the error. The below
// is a quick and dirty workaround.
if ([result isKindOfClass:NSString.class]) {
successBlock(result);
} else {
failureBlock(result);
}
}) retainUntilComplete];
/*
// See: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
TSRequest *formRequest = [OWSRequestFactory profileAvatarUploadFormRequest];
@ -526,7 +539,8 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSLogError(@"Failed to get profile avatar upload form: %@", error);
return failureBlock(error);
}];
});
*/
// });
}
- (void)updateServiceWithProfileName:(nullable NSString *)localProfileName

@ -131,4 +131,124 @@ public final class LokiStorageAPI : LokiDotNetAPI {
public static func objc_getDeviceLinks(associatedWith hexEncodedPublicKey: String) -> AnyPromise {
return AnyPromise.from(getDeviceLinks(associatedWith: hexEncodedPublicKey))
}
// MARK: Attachments (Public API)
public static func uploadAttachment(_ attachment: TSAttachmentStream, attachmentID: String) -> Promise<Void> {
return Promise<Void>() { seal in
getAuthToken(for: server).done { token in
// Encrypt the attachment
guard let unencryptedAttachmentData = try? attachment.readDataFromFile() else {
print("[Loki] Couldn't read attachment data from disk.")
return seal.reject(Error.generic)
}
var encryptionKey = NSData()
var digest = NSData()
guard let encryptedAttachmentData = Cryptography.encryptAttachmentData(unencryptedAttachmentData, outKey: &encryptionKey, outDigest: &digest) else {
print("[Loki] Couldn't encrypt attachment.")
return seal.reject(Error.encryptionFailed)
}
attachment.encryptionKey = encryptionKey as Data
attachment.digest = digest as Data
// Create the request
let url = "\(server)/files"
let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ]
var error: NSError?
var request = AFHTTPRequestSerializer().multipartFormRequest(withMethod: "POST", urlString: url, parameters: parameters, constructingBodyWith: { formData in
formData.appendPart(withFileData: encryptedAttachmentData, name: "content", fileName: UUID().uuidString, mimeType: "application/binary")
}, error: &error)
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
if let error = error {
print("[Loki] Couldn't upload attachment due to error: \(error).")
throw error
}
// Send the request
let task = AFURLSessionManager(sessionConfiguration: .default).uploadTask(withStreamedRequest: request as URLRequest, progress: { rawProgress in
// Broadcast progress updates
let progress = max(0.1, rawProgress.fractionCompleted)
let userInfo: [String:Any] = [ kAttachmentUploadProgressKey : progress, kAttachmentUploadAttachmentIDKey : attachmentID ]
DispatchQueue.main.async {
NotificationCenter.default.post(name: .attachmentUploadProgress, object: nil, userInfo: userInfo)
}
}, completionHandler: { response, responseObject, error in
if let error = error {
print("[Loki] Couldn't upload attachment due to error: \(error).")
return seal.reject(error)
}
let statusCode = (response as! HTTPURLResponse).statusCode
let isSuccessful = (200...299) ~= statusCode
guard isSuccessful else {
print("[Loki] Couldn't upload attachment.")
return seal.reject(Error.generic)
}
// Parse the server ID & download URL
guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let serverID = data["id"] as? UInt64, let downloadURL = data["url"] as? String else {
print("[Loki] Couldn't parse attachment from: \(responseObject).")
return seal.reject(Error.parsingFailed)
}
// Update the attachment
attachment.serverId = serverID
attachment.isUploaded = true
attachment.downloadURL = downloadURL
attachment.save()
return seal.fulfill(())
})
task.resume()
}.catch { error in
print("[Loki] Couldn't upload attachment.")
seal.reject(error)
}
}
}
// MARK: Attachments (Public Obj-C API)
@objc(uploadAttachment:withID:)
public static func objc_uploadAttachment(_ attachment: TSAttachmentStream, attachmentID: String) -> AnyPromise {
return AnyPromise.from(uploadAttachment(attachment, attachmentID: attachmentID))
}
// MARK: Profile Pictures (Public API)
public static func setProfilePicture(_ profilePicture: Data) -> Promise<String> {
return Promise<String>() { seal in
getAuthToken(for: server).done { token in
let url = "\(server)/files"
let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ]
var error: NSError?
var request = AFHTTPRequestSerializer().multipartFormRequest(withMethod: "POST", urlString: url, parameters: parameters, constructingBodyWith: { formData in
formData.appendPart(withFileData: profilePicture, name: "content", fileName: UUID().uuidString, mimeType: "application/binary")
}, error: &error)
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
if let error = error {
print("[Loki] Couldn't upload profile picture due to error: \(error).")
throw error
}
let task = AFURLSessionManager(sessionConfiguration: .default).uploadTask(withStreamedRequest: request as URLRequest, progress: nil, completionHandler: { response, responseObject, error in
if let error = error {
print("[Loki] Couldn't upload profile picture due to error: \(error).")
return seal.reject(error)
}
let statusCode = (response as! HTTPURLResponse).statusCode
let isSuccessful = (200...299) ~= statusCode
guard isSuccessful else {
print("[Loki] Couldn't upload profile picture.")
return seal.reject(Error.generic)
}
guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let downloadURL = data["url"] as? String else {
print("[Loki] Couldn't parse profile picture from: \(responseObject).")
return seal.reject(Error.parsingFailed)
}
return seal.fulfill(downloadURL)
})
task.resume()
}.catch { error in
print("[Loki] Couldn't upload profile picture.")
seal.reject(error)
}
}
}
// MARK: Profile Pictures (Public Obj-C API)
@objc(setProfilePicture:)
public static func objc_setProfilePicture(_ profilePicture: Data) -> AnyPromise {
return AnyPromise.from(setProfilePicture(profilePicture))
}
}

Loading…
Cancel
Save