From f3772cffa747841f9d3b9783acb769ff6bfdf923 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 19:44:18 +0300 Subject: [PATCH 1/5] Video avatar fixes --- .../Sources/LegacyAvatarPicker.swift | 2 +- .../Sources/LegacyPaintStickersContext.swift | 6 +- .../TelegramCore/Sources/Authorization.swift | 6 +- .../Sources/PeerPhotoUpdater.swift | 24 +++++-- .../AuthorizationSequenceController.swift | 65 ++++++++++++++++++- ...uthorizationSequenceSignUpController.swift | 13 +++- 6 files changed, 98 insertions(+), 18 deletions(-) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift index 4e49a364b2..1b33615447 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift @@ -32,7 +32,7 @@ public func presentLegacyAvatarEditor(theme: PresentationTheme, image: UIImage?, }) } -public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) { +public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void, videoCompletion: @escaping (UIImage, URL, TGVideoEditAdjustments?) -> Void = { _, _, _ in}) { let legacyController = LegacyController(presentation: .custom, theme: theme) legacyController.statusBar.statusBarStyle = .Ignore diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index 81cc38e395..0d8c690267 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -269,14 +269,14 @@ private class LegacyPaintTextEntity: LegacyPaintEntity { } public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRenderer { - private let account: Account + private let account: Account? private let queue = Queue() private let entities: [LegacyPaintEntity] private let originalSize: CGSize private let cropRect: CGRect? - public init(account: Account, adjustments: TGMediaEditAdjustments) { + public init(account: Account?, adjustments: TGMediaEditAdjustments) { self.account = account self.originalSize = adjustments.originalSize self.cropRect = adjustments.cropRect.isEmpty ? nil : adjustments.cropRect @@ -285,7 +285,7 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender if let paintingData = adjustments.paintingData, let paintingEntities = paintingData.entities { for paintingEntity in paintingEntities { if let sticker = paintingEntity as? TGPhotoPaintStickerEntity { - if let entity = LegacyPaintStickerEntity(account: account, entity: sticker) { + if let account = account, let entity = LegacyPaintStickerEntity(account: account, entity: sticker) { entities.append(entity) } } else if let text = paintingEntity as? TGPhotoPaintTextEntity { diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 1334516529..9657f9b5f9 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -430,7 +430,7 @@ public enum SignUpError { case invalidLastName } -public func signUpWithName(accountManager: AccountManager, account: UnauthorizedAccount, firstName: String, lastName: String, avatarData: Data?) -> Signal { +public func signUpWithName(accountManager: AccountManager, account: UnauthorizedAccount, firstName: String, lastName: String, avatarData: Data?, avatarVideo: Signal?, videoStartTimestamp: Double?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let state = transaction.getState() as? UnauthorizedAccountState, case let .signUp(number, codeHash, _, _, _, syncContacts) = state.contents { return account.network.request(Api.functions.auth.signUp(phoneNumber: number, phoneCodeHash: codeHash, firstName: firstName, lastName: lastName)) @@ -470,7 +470,7 @@ public func signUpWithName(accountManager: AccountManager, account: Unauthorized let resource = LocalFileMediaResource(fileId: arc4random64()) account.postbox.mediaBox.storeResourceData(resource.id, data: avatarData) - return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), video: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }) + return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), video: avatarVideo, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { _, _ in .single([:]) }) |> `catch` { _ -> Signal in return .complete() } @@ -486,7 +486,7 @@ public func signUpWithName(accountManager: AccountManager, account: Unauthorized |> then(switchedAccounts) } else { return appliedState - |> then(switchedAccounts) + |> then(switchedAccounts) } case .authorizationSignUpRequired: return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift index 1ab7a723fb..d8b93c3aa2 100644 --- a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift +++ b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift @@ -47,13 +47,23 @@ public func uploadedPeerPhoto(postbox: Postbox, network: Network, resource: Medi } } -public func uploadedPeerVideo(postbox: Postbox, network: Network, messageMediaPreuploadManager: MessageMediaPreuploadManager, resource: MediaResource) -> Signal { - return messageMediaPreuploadManager.upload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) - |> map { result -> UploadedPeerPhotoData in - return UploadedPeerPhotoData(resource: resource, content: .result(result)) - } - |> `catch` { _ -> Signal in - return .single(UploadedPeerPhotoData(resource: resource, content: .error)) +public func uploadedPeerVideo(postbox: Postbox, network: Network, messageMediaPreuploadManager: MessageMediaPreuploadManager?, resource: MediaResource) -> Signal { + if let messageMediaPreuploadManager = messageMediaPreuploadManager { + return messageMediaPreuploadManager.upload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedPeerPhotoData in + return UploadedPeerPhotoData(resource: resource, content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedPeerPhotoData(resource: resource, content: .error)) + } + } else { + return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedPeerPhotoData in + return UploadedPeerPhotoData(resource: resource, content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedPeerPhotoData(resource: resource, content: .error)) + } } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift index fca4df758f..63caa0c921 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift @@ -15,6 +15,8 @@ import AccountContext import CountrySelectionUI import SettingsUI import PhoneNumberFormat +import LegacyComponents +import LegacyMediaPickerUI private enum InnerState: Equatable { case state(UnauthorizedAccountStateContents) @@ -684,11 +686,70 @@ public final class AuthorizationSequenceController: NavigationController, MFMail transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))) }).start() }, displayCancel: displayCancel) - controller.signUpWithName = { [weak self, weak controller] firstName, lastName, avatarData in + controller.signUpWithName = { [weak self, weak controller] firstName, lastName, avatarData, avatarUrl, avatarAdjustments in if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData) + var videoStartTimestamp: Double? = nil + if let adjustments = avatarAdjustments, adjustments.videoStartValue > 0.0 { + videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue + } + + let avatarVideo: Signal? + if let avatarUrl = avatarUrl { + let account = strongSelf.account + avatarVideo = Signal { subscriber in + var filteredPath = avatarUrl.path + if filteredPath.hasPrefix("file://") { + filteredPath = String(filteredPath[filteredPath.index(filteredPath.startIndex, offsetBy: "file://".count)]) + } + + let avAsset = AVURLAsset(url: URL(fileURLWithPath: filteredPath)) + let entityRenderer: LegacyPaintEntityRenderer? = avatarAdjustments.flatMap { adjustments in + if let paintingData = adjustments.paintingData, paintingData.hasAnimation { + return LegacyPaintEntityRenderer(account: nil, adjustments: adjustments) + } else { + return nil + } + } + + let signal = TGMediaVideoConverter.convert(avAsset, adjustments: avatarAdjustments, watcher: nil, entityRenderer: entityRenderer)! + + let signalDisposable = signal.start(next: { next in + if let result = next as? TGMediaVideoConversionResult { + var value = stat() + if stat(result.fileURL.path, &value) == 0 { + if let data = try? Data(contentsOf: result.fileURL) { + let resource = LocalFileMediaResource(fileId: arc4random64()) + account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) + subscriber.putNext(resource) + } + } + subscriber.putCompletion() + } + }, error: { _ in + }, completed: nil) + + let disposable = ActionDisposable { + signalDisposable?.dispose() + } + + return ActionDisposable { + disposable.dispose() + } + } + |> mapToSignal { resource -> Signal in + if let resource = resource { + return uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: nil, resource: resource) |> map(Optional.init) + } else { + return .single(nil) + } + } + } else { + avatarVideo = nil + } + + strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData, avatarVideo: avatarVideo, videoStartTimestamp: videoStartTimestamp) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift index 0c4d3c8240..6120dfa3f7 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift @@ -22,7 +22,10 @@ final class AuthorizationSequenceSignUpController: ViewController { var initialName: (String, String) = ("", "") private var termsOfService: UnauthorizedAccountTermsOfService? - var signUpWithName: ((String, String, Data?) -> Void)? + var signUpWithName: ((String, String, Data?, URL?, TGVideoEditAdjustments?) -> Void)? + + var avatarUrl: URL? + var avatarAdjustments: TGVideoEditAdjustments? private let hapticFeedback = HapticFeedback() @@ -88,6 +91,12 @@ final class AuthorizationSequenceSignUpController: ViewController { self?.present(c, in: .window(.root), with: a) }, openCurrent: nil, completion: { image in self?.controllerNode.currentPhoto = image + self?.avatarUrl = nil + self?.avatarAdjustments = nil + }, videoCompletion: { image, url, adjustments in + self?.controllerNode.currentPhoto = image + self?.avatarUrl = url + self?.avatarAdjustments = adjustments }) }) self.displayNodeDidLoad() @@ -150,7 +159,7 @@ final class AuthorizationSequenceSignUpController: ViewController { if let name = name { self.signUpWithName?(name.0, name.1, self.controllerNode.currentPhoto.flatMap({ image in return compressImageToJPEG(image, quality: 0.7) - })) + }), self.avatarUrl, self.avatarAdjustments) } } } From 1781ffb1471920925149cbcb7fe583b4733b9cad Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 20:03:20 +0300 Subject: [PATCH 2/5] Video avatar fixes --- submodules/TelegramUI/Sources/OpenChatMessage.swift | 2 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 6 +++++- .../Sources/PeerInfo/PeerInfoScreen.swift | 13 +++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index bc4e7f81a0..ef911f0b17 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -47,7 +47,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: case let .photoUpdated(image): if let peer = messageMainPeer(message), let image = image { let promise: Promise<[AvatarGalleryEntry]> = Promise([AvatarGalleryEntry.image(image.imageId, image.reference, image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: .media(media: .message(message: MessageReference(message), media: media), resource: $0.resource)) }), image.videoRepresentations, peer, message.timestamp, nil, message.id, image.immediateThumbnailData, "action")]) - let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: promise, replaceRootController: { controller, ready in + let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: promise, skipInitial: true, replaceRootController: { controller, ready in }) return .chatAvatars(galleryController, image) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index b5bcb810ab..e4247da6d3 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2310,6 +2310,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)? var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)? var requestOpenAvatarForEditing: ((Bool) -> Void)? + var cancelUpload: (() -> Void)? var requestUpdateLayout: (() -> Void)? var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? @@ -2358,6 +2359,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.editingContentNode.alpha = 0.0 self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context) + self.avatarOverlayNode.isUserInteractionEnabled = false self.navigationBackgroundNode = ASDisplayNode() self.navigationBackgroundNode.isUserInteractionEnabled = false @@ -2445,9 +2447,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { if let currentEntry = self.avatarListNode.listContainerNode.currentEntry { self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, self.avatarListNode.listContainerNode.currentEntry, self.avatarTransitionArguments(entry: currentEntry)) } - } else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first{ + } else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first { let _ = self.avatarListNode.avatarContainerNode.avatarNode self.requestAvatarExpansion?(false, self.avatarListNode.listContainerNode.galleryEntries, nil, self.avatarTransitionArguments(entry: entry)) + } else { + self.cancelUpload?() } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 11fb1333fe..9f0fd15801 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2062,6 +2062,19 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD self?.performButtonAction(key: key) } + self.headerNode.cancelUpload = { [weak self] in + guard let strongSelf = self else { + return + } + if strongSelf.state.updatingAvatar != nil { + strongSelf.updateAvatarDisposable.set(nil) + strongSelf.state = strongSelf.state.withUpdatingAvatar(nil) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + self.headerNode.requestAvatarExpansion = { [weak self] gallery, entries, centralEntry, _ in guard let strongSelf = self, let peer = strongSelf.data?.peer, peer.smallProfileImage != nil else { return From 13464f8dfdb25131dea7797e1e1dd29e7abbf4e2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 20:18:51 +0300 Subject: [PATCH 3/5] Video avatar fixes --- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index e4247da6d3..6fd66d0417 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1310,22 +1310,32 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode { transition.updateAlpha(node: self.iconNode, alpha: iconHidden ? 0.0 : 1.0) transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0) } else { + var immediately = self.currentRepresentation == nil if isEditing { - iconHidden = peer.profileImageRepresentations.isEmpty - overlayHidden = peer.profileImageRepresentations.isEmpty + immediately = peer.profileImageRepresentations.isEmpty + iconHidden = immediately + overlayHidden = immediately } else { iconHidden = true overlayHidden = true } - Queue.mainQueue().after(0.3) { [weak self] in - guard let strongSelf = self else { - return + + let targetAlpha: CGFloat = overlayHidden ? 0.0 : 1.0 + if self.updatingAvatarOverlay.alpha != targetAlpha { + let update = { + self.statusNode.transitionToState(.none) + self.currentRepresentation = nil + self.imageNode.setSignal(.single(nil)) + transition.updateAlpha(node: self.iconNode, alpha: iconHidden ? 0.0 : 1.0) + transition.updateAlpha(node: self.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0) + } + if immediately { + update() + } else { + Queue.mainQueue().after(0.3) { + update() + } } - strongSelf.statusNode.transitionToState(.none) - strongSelf.currentRepresentation = nil - strongSelf.imageNode.setSignal(.single(nil)) - transition.updateAlpha(node: strongSelf.iconNode, alpha: iconHidden ? 0.0 : 1.0) - transition.updateAlpha(node: strongSelf.updatingAvatarOverlay, alpha: overlayHidden ? 0.0 : 1.0) } } if !overlayHidden && self.updatingAvatarOverlay.image == nil { From 068ba88f46ac63adf4711ae2a0d2f8d8bfeb0907 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 20:22:16 +0300 Subject: [PATCH 4/5] Video avatar fixes --- .../TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 6fd66d0417..58061cbe9d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1312,9 +1312,9 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode { } else { var immediately = self.currentRepresentation == nil if isEditing { - immediately = peer.profileImageRepresentations.isEmpty - iconHidden = immediately - overlayHidden = immediately + immediately = immediately || peer.profileImageRepresentations.isEmpty + iconHidden = peer.profileImageRepresentations.isEmpty + overlayHidden = peer.profileImageRepresentations.isEmpty } else { iconHidden = true overlayHidden = true From 20b54cc260c70a5df6a785946bb078682ff267a6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 20:34:37 +0300 Subject: [PATCH 5/5] Video avatar fixes --- .../LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift index 1b33615447..4006965852 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift @@ -53,6 +53,12 @@ public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, t } completion(image) } + mixin.didFinishWithVideo = { image, url, adjustments in + guard let image = image, let url = url else { + return + } + videoCompletion(image, url, adjustments) + } mixin.didFinishWithView = { openCurrent?() }