Various improvements

This commit is contained in:
Ilya Laktyushin
2025-01-18 13:44:42 +04:00
parent a8fd8c6085
commit ef6c097f6f
57 changed files with 1892 additions and 485 deletions

View File

@@ -19,7 +19,7 @@ import LegacyComponents
import LegacyMediaPickerUI
extension PeerInfoScreenImpl {
func newopenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }, completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView? = { _, _ in nil }) {
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }, completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView? = { _, _ in nil }) {
guard let data = self.controllerNode.data, let peer = data.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: data.threadData) else {
return
}
@@ -170,7 +170,6 @@ extension PeerInfoScreenImpl {
default:
break
}
dismissImpl?()
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
)
@@ -200,6 +199,7 @@ extension PeerInfoScreenImpl {
}
navigationController.setViewControllers(viewControllers, animated: false)
}
}
mainController.navigationPresentation = .flatModal
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
@@ -284,12 +284,11 @@ extension PeerInfoScreenImpl {
(self.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root))
}
public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
private func setupProfilePhotoUpload(image: UIImage, mode: PeerInfoAvatarEditingMode, indefiniteProgress: Bool) -> LocalFileMediaResource? {
guard let data = image.jpegData(compressionQuality: 0.6) else {
uploadStatus?.set(.single(.done))
return
return nil
}
if self.controllerNode.headerNode.isAvatarExpanded {
self.controllerNode.headerNode.ignoreCollapse = true
self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
@@ -303,14 +302,25 @@ extension PeerInfoScreenImpl {
if [.suggest, .fallback].contains(mode) {
} else {
if indefiniteProgress {
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
}
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
}
if let (layout, navigationHeight) = self.controllerNode.validLayout {
self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false)
}
self.controllerNode.headerNode.ignoreCollapse = false
return resource
}
public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
guard let resource = setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: false) else {
uploadStatus?.set(.single(.done))
return
}
let postbox = self.context.account.postbox
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
if self.isSettings || self.isMyProfile {
@@ -405,23 +415,228 @@ extension PeerInfoScreenImpl {
}
}))
}
// public func updateProfileVideo(_ image: UIImage, video: MediaEditorScreenImpl.MediaResult.VideoResult, values: MediaEditorValues, mode: PeerInfoAvatarEditingMode) {
// var markup: UploadPeerPhotoMarkup? = nil
// if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
// if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
// markup = .sticker(packReference: .id(id: packId, accessHash: accessHash), fileId: fileId, backgroundColors: backgroundColors)
// } else {
// markup = .emoji(fileId: fileId, backgroundColors: backgroundColors)
// }
// }
//
// var videoStartTimestamp: Double? = nil
// if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
// videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
// }
//
// var uploadVideo = true
// if let _ = markup {
// if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue {
// uploadVideo = true
// } else {
// uploadVideo = false
// }
// }
//
// guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
// return
// }
//
// let context = self.context
//
// let videoResource: Signal<TelegramMediaResource?, UploadPeerPhotoError>
// if uploadVideo {
// let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4"
// let videoExport = MediaEditorVideoExport(
// postbox: context.account.postbox,
// subject: .image(image: image),
// configuration: configuration,
// outputPath: path
// )
//
// videoResource = Signal<TelegramMediaResource?, UploadPeerPhotoError> { [weak self] subscriber in
// let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
// if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
// return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments)
// } else {
// return nil
// }
// }
//
// let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
// let uploadInterface = LegacyLiveUploadInterface(context: context)
// let signal: SSignal
// if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
// let durationSignal: SSignal = SSignal(generator: { subscriber in
// let disposable = (entityRenderer.duration()).start(next: { duration in
// subscriber.putNext(duration)
// subscriber.putCompletion()
// })
//
// return SBlockDisposable(block: {
// disposable.dispose()
// })
// })
// signal = durationSignal.map(toSignal: { duration -> SSignal in
// if let duration = duration as? Double {
// return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
// } else {
// return SSignal.single(nil)
// }
// })
// } else if let asset = asset as? AVAsset {
// signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
// } else {
// signal = SSignal.complete()
// }
//
// let signalDisposable = signal.start(next: { next in
// if let result = next as? TGMediaVideoConversionResult {
// if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
// account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
// }
//
// if let timestamp = videoStartTimestamp {
// videoStartTimestamp = max(0.0, min(timestamp, result.duration - 0.05))
// }
//
// var value = stat()
// if stat(result.fileURL.path, &value) == 0 {
// if let data = try? Data(contentsOf: result.fileURL) {
// let resource: TelegramMediaResource
// if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
// resource = LocalFileMediaResource(fileId: liveUploadData.id)
// } else {
// resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
// }
// account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
// subscriber.putNext(resource)
//
// EngineTempBox.shared.dispose(tempFile)
// }
// }
// subscriber.putCompletion()
// } else if let strongSelf = self, let progress = next as? NSNumber {
// Queue.mainQueue().async {
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(progress.floatValue * 0.45)))
// if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout {
// strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
// }
// }
// }
// }, error: { _ in
// }, completed: nil)
//
// let disposable = ActionDisposable {
// signalDisposable?.dispose()
// }
//
// return ActionDisposable {
// disposable.dispose()
// }
// }
// } else {
// videoResource = .single(nil)
// }
//
// var dismissStatus: (() -> Void)?
// if [.suggest, .fallback, .accept].contains(mode) {
// let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in
// self?.controllerNode.updateAvatarDisposable.set(nil)
// dismissStatus?()
// }))
// dismissStatus = { [weak statusController] in
// statusController?.dismiss()
// }
// if let topController = self.navigationController?.topViewController as? ViewController {
// topController.presentInGlobalOverlay(statusController)
// } else if let topController = self.parentController?.topViewController as? ViewController {
// topController.presentInGlobalOverlay(statusController)
// } else {
// self.presentInGlobalOverlay(statusController)
// }
// }
//
// let peerId = self.peerId
// let isSettings = self.isSettings
// let isMyProfile = self.isMyProfile
// self.controllerNode.updateAvatarDisposable.set((videoResource
// |> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
// if isSettings || isMyProfile {
// if case .fallback = mode {
// return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
// })
// } else {
// return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
// })
// }
// } else if case .custom = mode {
// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .custom, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
// })
// } else if case .suggest = mode {
// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
// })
// } else {
// return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: context.engine.peers.uploadedPeerPhoto(resource: photoResource), video: videoResource.flatMap { context.engine.peers.uploadedPeerVideo(resource: $0) |> map(Optional.init) }, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
// })
// }
// }
// |> deliverOnMainQueue).startStrict(next: { [weak self] result in
// guard let strongSelf = self else {
// return
// }
// switch result {
// case .complete:
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil)
// case let .progress(value):
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(0.45 + value * 0.55)))
// }
// if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout {
// strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
// }
//
// if case .complete = result {
// dismissStatus?()
//
// let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId))
// |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
// if let strongSelf = self, let peer {
// switch mode {
// case .fallback:
// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
// case .custom:
// strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
//
// let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
// case .suggest:
// if let navigationController = (strongSelf.navigationController as? NavigationController) {
// strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in
// }))
// }
// case .accept:
// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
// if case .info = action {
// self?.parentController?.openSettings()
// }
// return false
// }), in: .current)
// default:
// break
// }
// }
// })
// }
// }))
// }
public func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, mode: PeerInfoAvatarEditingMode) {
guard let data = image.jpegData(compressionQuality: 0.6) else {
return
}
if self.controllerNode.headerNode.isAvatarExpanded {
self.controllerNode.headerNode.ignoreCollapse = true
self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
self.controllerNode.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
}
self.controllerNode.scrollNode.view.setContentOffset(CGPoint(), animated: false)
let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false)
var markup: UploadPeerPhotoMarkup? = nil
if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
@@ -439,19 +654,9 @@ extension PeerInfoScreenImpl {
uploadVideo = false
}
}
if [.suggest, .fallback].contains(mode) {
} else {
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
if !uploadVideo {
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
}
guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
return
}
if let (layout, navigationHeight) = self.controllerNode.validLayout {
self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false)
}
self.controllerNode.headerNode.ignoreCollapse = false
var videoStartTimestamp: Double? = nil
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {