From 7ba7fcc76080385813a823abc1fdffe923177a50 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 4 Jul 2023 23:45:40 +0200 Subject: [PATCH] Various improvements --- .../DrawingUI/Sources/DrawingScreen.swift | 7 +- .../Sources/DrawingStickerEntity.swift | 18 +- .../Sources/StickerPickerScreen.swift | 2 +- .../Sources/LegacyPaintStickersContext.swift | 2 +- .../TelegramEngine/Messages/Stories.swift | 28 ++- .../Messages/TelegramEngineMessages.swift | 4 +- .../Drawing/DrawingStickerEntity.swift | 26 ++- .../Sources/MediaEditorComposerEntity.swift | 2 +- .../Sources/MediaEditorScreen.swift | 26 ++- .../Sources/StoryContainerScreen.swift | 88 ++++----- .../StoryItemSetContainerComponent.swift | 173 ++++++++++++++++-- .../Sources/TextFieldComponent.swift | 7 + 12 files changed, 291 insertions(+), 92 deletions(-) diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 919efe80ac..3cc9d73740 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2929,7 +2929,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U } let images = imageItems as! [UIImage] if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 { - let entity = DrawingStickerEntity(content: .image(image)) + let entity = DrawingStickerEntity(content: .image(image, false)) strongSelf.node.insertEntity.invoke(entity) } } @@ -3110,8 +3110,11 @@ public final class DrawingToolsInteraction { self.isActive = false } - public func insertEntity(_ entity: DrawingEntity) { + public func insertEntity(_ entity: DrawingEntity, scale: CGFloat? = nil) { self.entitiesView.prepareNewEntity(entity) + if let scale { + entity.scale = scale + } self.entitiesView.add(entity) self.entitiesView.selectEntity(entity) diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift index df85c6e943..0895c171ac 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift @@ -102,7 +102,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { } private var image: UIImage? { - if case let .image(image) = self.stickerEntity.content { + if case let .image(image, _) = self.stickerEntity.content { return image } else { return nil @@ -121,7 +121,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { switch self.stickerEntity.content { case let .file(file): return file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) - case let .image(image): + case let .image(image, _): return image.size case let .video(_, image, _): if let image { @@ -605,6 +605,10 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { } override func layoutSubviews() { + guard let entityView = self.entityView, let entity = entityView.entity as? DrawingStickerEntity else { + return + } + let inset = self.selectionInset - 10.0 let bounds = CGRect(origin: .zero, size: CGSize(width: entitySelectionViewHandleSize.width / self.scale, height: entitySelectionViewHandleSize.height / self.scale)) @@ -635,7 +639,15 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber] self.border.lineWidth = 2.0 / self.scale - self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath + + if entity.isRectangle { + let width: CGFloat = self.bounds.width - inset * 2.0 + let height: CGFloat = self.bounds.height - inset * 2.0 + let cornerRadius: CGFloat = 12.0 - self.scale + self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath + } else { + self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath + } } } diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index c9791cb8ed..6e2b26c765 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -584,7 +584,7 @@ public class StickerPickerScreen: ViewController { CTLineDraw(line, context) context.translateBy(x: -lineOrigin.x, y: -lineOrigin.y) }) { - strongSelf.controller?.completion(.image(image)) + strongSelf.controller?.completion(.image(image, false)) } strongSelf.controller?.dismiss(animated: true) } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index 0a43f7a909..1cef0c71f2 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -143,7 +143,7 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { } })) } - case let .image(image): + case let .image(image, _): self.file = nil self.imagePromise.set(.single(image)) case .video: diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index e6b2828e23..7829f42b90 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -623,10 +623,15 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput } } -private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, passFetchProgress: Bool) -> (signal: Signal, media: Media) { +private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, embeddedStickers: [TelegramMediaFile], accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, passFetchProgress: Bool) -> (signal: Signal, media: Media) { let originalMedia: Media = media let contentToUpload: MessageContentToUpload + var attributes: [MessageAttribute] = [] + if !embeddedStickers.isEmpty { + attributes.append(EmbeddedMediaStickersMessageAttribute(files: embeddedStickers)) + } + contentToUpload = messageContentToUpload( accountPeerId: accountPeerId, network: network, @@ -640,7 +645,7 @@ private func uploadedStoryContent(postbox: Postbox, network: Network, media: Med passFetchProgress: passFetchProgress, peerId: accountPeerId, messageId: nil, - attributes: [], + attributes: attributes, text: "", media: [media] ) @@ -776,7 +781,7 @@ private func _internal_putPendingStoryIdMapping(accountPeerId: PeerId, stableId: func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId: PeerId, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, stableId: Int32, media: Media, text: String, entities: [MessageTextEntity], embeddedStickers: [TelegramMediaFile], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { let passFetchProgress = media is TelegramMediaFile - let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) + let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, embeddedStickers: embeddedStickers, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) return contentSignal |> mapToSignal { result -> Signal in switch result { @@ -818,18 +823,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId if isForwardingDisabled { flags |= 1 << 4 } - - var inputMedia = inputMedia - if !embeddedStickers.isEmpty { - var stickersValue: [Api.InputDocument] = [] - for file in embeddedStickers { - if let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference { - stickersValue.append(Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference))) - } - } - inputMedia = inputMedia.withUpdatedStickers(stickersValue) - } - + return network.request(Api.functions.stories.sendStory( flags: flags, media: inputMedia, @@ -922,7 +916,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId } } -func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: Int32, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal { +func _internal_editStory(account: Account, id: Int32, media: EngineStoryInputMedia?, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal { let contentSignal: Signal let originalMedia: Media? if let media = media { @@ -930,7 +924,7 @@ func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: In if case .video = media { passFetchProgress = true } - (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods, passFetchProgress: passFetchProgress) + (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), embeddedStickers: media.embeddedStickers, accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods, passFetchProgress: passFetchProgress) } else { contentSignal = .single(nil) originalMedia = nil diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 897f3352d7..342ec69989 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -1017,8 +1017,8 @@ public extension TelegramEngine { _internal_cancelStoryUpload(account: self.account, stableId: stableId) } - public func editStory(media: EngineStoryInputMedia?, id: Int32, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal { - return _internal_editStory(account: self.account, media: media, id: id, text: text, entities: entities, privacy: privacy) + public func editStory(id: Int32, media: EngineStoryInputMedia?, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal { + return _internal_editStory(account: self.account, id: id, media: media, text: text, entities: entities, privacy: privacy) } public func editStoryPrivacy(id: Int32, privacy: EngineStoryPrivacy) -> Signal { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift index bc71ff6acb..75c72ed53a 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift @@ -15,7 +15,7 @@ private func fullEntityMediaPath(_ path: String) -> String { public final class DrawingStickerEntity: DrawingEntity, Codable { public enum Content: Equatable { case file(TelegramMediaFile) - case image(UIImage) + case image(UIImage, Bool) case video(String, UIImage?, Bool) case dualVideoReference @@ -27,9 +27,9 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } else { return false } - case let .image(lhsImage): - if case let .image(rhsImage) = rhs { - return lhsImage === rhsImage + case let .image(lhsImage, lhsIsRectangle): + if case let .image(rhsImage, rhsIsRectangle) = rhs { + return lhsImage === rhsImage && lhsIsRectangle == rhsIsRectangle } else { return false } @@ -55,6 +55,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { case videoPath case videoImagePath case videoMirrored + case isRectangle case dualVideo case referenceDrawingSize case position @@ -71,7 +72,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { public var scale: CGFloat public var rotation: CGFloat public var mirrored: Bool - + public var color: DrawingColor = DrawingColor.clear public var lineWidth: CGFloat = 0.0 @@ -97,6 +98,15 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } } + public var isRectangle: Bool { + switch self.content { + case let .image(_, isRectangle): + return isRectangle + default: + return false + } + } + public var isMedia: Bool { return false } @@ -123,7 +133,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } else if let file = try container.decodeIfPresent(TelegramMediaFile.self, forKey: .file) { self.content = .file(file) } else if let imagePath = try container.decodeIfPresent(String.self, forKey: .imagePath), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) { - self.content = .image(image) + let isRectangle = try container.decodeIfPresent(Bool.self, forKey: .isRectangle) ?? false + self.content = .image(image, isRectangle) } else if let videoPath = try container.decodeIfPresent(String.self, forKey: .videoPath) { var imageValue: UIImage? if let imagePath = try container.decodeIfPresent(String.self, forKey: .videoImagePath), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) { @@ -147,7 +158,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { switch self.content { case let .file(file): try container.encode(file, forKey: .file) - case let .image(image): + case let .image(image, isRectangle): let imagePath = "\(self.uuid).png" let fullImagePath = fullEntityMediaPath(imagePath) if let imageData = image.pngData() { @@ -155,6 +166,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { try? imageData.write(to: URL(fileURLWithPath: fullImagePath)) try container.encodeIfPresent(imagePath, forKey: .imagePath) } + try container.encode(isRectangle, forKey: .isRectangle) case let .video(path, image, videoMirrored): try container.encode(path, forKey: .videoPath) let imagePath = "\(self.uuid).jpg" diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index eb4a4efe49..a928dfca2f 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -18,7 +18,7 @@ func composerEntitiesForDrawingEntity(account: Account, entity: DrawingEntity, c switch entity.content { case let .file(file): content = .file(file) - case let .image(image): + case let .image(image, _): content = .image(image) case let .video(path, _, _): content = .video(path) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 2efaa6ff43..cf7f9399cf 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1153,8 +1153,26 @@ final class MediaEditorScreenComponent: Component { forwardAction: nil, moreAction: nil, presentVoiceMessagesUnavailableTooltip: nil, - paste: { data in - let _ = data + paste: { [weak self] data in + guard let self, let environment = self.environment, let controller = environment.controller() as? MediaEditorScreen else { + return + } + switch data { + case let .sticker(image, _): + if max(image.size.width, image.size.height) > 1.0 { + let entity = DrawingStickerEntity(content: .image(image, false)) + controller.node.interaction?.insertEntity(entity, scale: 1.0) + self.deactivateInput() + } + case let .images(images): + if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 { + let entity = DrawingStickerEntity(content: .image(image, true)) + controller.node.interaction?.insertEntity(entity, scale: 2.5) + self.deactivateInput() + } + default: + break + } }, audioRecorder: nil, videoRecordingStatus: nil, @@ -1899,7 +1917,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate context.draw(cgImage, in: CGRect(origin: CGPoint(x: (size.width - additionalImage.size.width) / 2.0, y: (size.height - additionalImage.size.height) / 2.0), size: additionalImage.size)) } }) - let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage)) + let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage, false)) imageEntity.referenceDrawingSize = storyDimensions imageEntity.scale = 1.49 imageEntity.position = position.getPosition(storyDimensions) @@ -4017,7 +4035,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } let images = imageItems as! [UIImage] if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 { - self.node.interaction?.insertEntity(DrawingStickerEntity(content: .image(image))) + self.node.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, false)), scale: 2.5) } } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index fbd495efd1..974627f4bf 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -641,20 +641,16 @@ private final class StoryContainerScreenComponent: Component { } @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - guard let component = self.component, let environment = self.environment, let stateValue = component.content.stateValue, case .recognized = recognizer.state else { + guard case .recognized = recognizer.state else { return } let location = recognizer.location(in: recognizer.view) if let currentItemView = self.visibleItemSetViews.first?.value { if location.x < currentItemView.frame.minX { - component.content.navigate(navigation: .item(.previous)) + self.navigate(direction: .previous) } else if location.x > currentItemView.frame.maxX { - if stateValue.nextSlice == nil { - environment.controller()?.dismiss() - } else { - component.content.navigate(navigation: .item(.next)) - } + self.navigate(direction: .next) } } } @@ -814,6 +810,47 @@ private final class StoryContainerScreenComponent: Component { } } + private func navigate(direction: StoryItemSetContainerComponent.NavigationDirection) { + guard let component = self.component, let environment = self.environment else { + return + } + + if let stateValue = component.content.stateValue, let slice = stateValue.slice { + if case .next = direction, slice.nextItemId == nil, (slice.item.position == nil || slice.item.position == slice.totalCount - 1) { + if stateValue.nextSlice == nil { + environment.controller()?.dismiss() + } else { + self.beginHorizontalPan(translation: CGPoint()) + self.updateHorizontalPan(translation: CGPoint()) + self.commitHorizontalPan(velocity: CGPoint(x: -200.0, y: 0.0)) + } + } else if case .previous = direction, slice.previousItemId == nil { + if stateValue.previousSlice == nil { + if let itemSetView = self.visibleItemSetViews[slice.peer.id] { + if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + componentView.rewindCurrentItem() + } + } + } else { + self.beginHorizontalPan(translation: CGPoint()) + self.updateHorizontalPan(translation: CGPoint()) + self.commitHorizontalPan(velocity: CGPoint(x: 200.0, y: 0.0)) + } + } else { + let mappedDirection: StoryContentContextNavigation.ItemDirection + switch direction { + case .previous: + mappedDirection = .previous + case .next: + mappedDirection = .next + case let .id(id): + mappedDirection = .id(id) + } + component.content.navigate(navigation: .item(mappedDirection)) + } + } + } + func update(component: StoryContainerScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { if self.didAnimateOut { return availableSize @@ -1052,44 +1089,11 @@ private final class StoryContainerScreenComponent: Component { environment.controller()?.dismiss() }, navigate: { [weak self] direction in - guard let self, let component = self.component, let environment = self.environment else { + guard let self else { return } - if let stateValue = component.content.stateValue, let slice = stateValue.slice { - if case .next = direction, slice.nextItemId == nil, (slice.item.position == nil || slice.item.position == slice.totalCount - 1) { - if stateValue.nextSlice == nil { - environment.controller()?.dismiss() - } else { - self.beginHorizontalPan(translation: CGPoint()) - self.updateHorizontalPan(translation: CGPoint()) - self.commitHorizontalPan(velocity: CGPoint(x: -200.0, y: 0.0)) - } - } else if case .previous = direction, slice.previousItemId == nil { - if stateValue.previousSlice == nil { - if let itemSetView = self.visibleItemSetViews[slice.peer.id] { - if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - componentView.rewindCurrentItem() - } - } - } else { - self.beginHorizontalPan(translation: CGPoint()) - self.updateHorizontalPan(translation: CGPoint()) - self.commitHorizontalPan(velocity: CGPoint(x: 200.0, y: 0.0)) - } - } else { - let mappedDirection: StoryContentContextNavigation.ItemDirection - switch direction { - case .previous: - mappedDirection = .previous - case .next: - mappedDirection = .next - case let .id(id): - mappedDirection = .id(id) - } - component.content.navigate(navigation: .item(mappedDirection)) - } - } + self.navigate(direction: direction) }, delete: { [weak self] in guard let self else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 3ecc876e07..6223a9b341 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -32,6 +32,8 @@ import BundleIconComponent import PeerListItemComponent import PremiumUI import AttachmentUI +import StickerPackPreviewUI +import TextNodeWithEntities public final class StoryAvailableReactions: Equatable { let reactionItems: [ReactionItem] @@ -3212,7 +3214,7 @@ public final class StoryItemSetContainerComponent: Component { updateProgressImpl?(0.0) if let imageData = compressImageToJPEG(image, quality: 0.7) { - let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData, stickers: stickers), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) + let _ = (context.engine.messages.editStory(id: id, media: .image(dimensions: dimensions, data: imageData, stickers: stickers), text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { return @@ -3251,7 +3253,7 @@ public final class StoryItemSetContainerComponent: Component { } let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) } - let _ = (context.engine.messages.editStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: firstFrameImageData, stickers: stickers), id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) + let _ = (context.engine.messages.editStory(id: id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: firstFrameImageData, stickers: stickers), text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { return @@ -3273,7 +3275,7 @@ public final class StoryItemSetContainerComponent: Component { } } } else if updatedText != nil || updatedPrivacy != nil { - let _ = (context.engine.messages.editStory(media: nil, id: id, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) + let _ = (context.engine.messages.editStory(id: id, media: nil, text: updatedText, entities: updatedEntities, privacy: updatedPrivacy) |> deliverOnMainQueue).start(next: { [weak self] result in switch result { case .completed: @@ -3342,6 +3344,62 @@ public final class StoryItemSetContainerComponent: Component { } } + private func openAttachedStickers(packs: Signal<[StickerPackReference], NoError>) { + guard let component = self.component else { + return + } + + guard let parentController = component.controller() as? StoryContainerScreen else { + return + } + let context = component.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) + let progressSignal = Signal { [weak parentController] subscriber in + let progressController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + parentController?.present(progressController, in: .window(.root), with: nil) + return ActionDisposable { [weak progressController] in + Queue.mainQueue().async() { + progressController?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.15, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + let signal = packs + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + let _ = (signal + |> deliverOnMainQueue).start(next: { [weak parentController] packs in + guard !packs.isEmpty else { + return + } + let controller = StickerPackScreen(context: context, updatedPresentationData: (presentationData, .single(presentationData)), mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { actions in + if let (info, items, action) = actions.first { + let animateInAsReplacement = false + switch action { + case .add: + parentController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in + return true + }), in: .window(.root)) + case let .remove(positionInList): + parentController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in + if case .undo = action { + let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start() + } + return true + }), in: .window(.root)) + } + } + }) + parentController?.present(controller, in: .window(.root), with: nil) + }) + } + private func performMoreAction(sourceView: UIView, gesture: ContextGesture?) { guard let component = self.component else { return @@ -3367,10 +3425,9 @@ public final class StoryItemSetContainerComponent: Component { return true } + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) var items: [ContextMenuItem] = [] - - let additionalCount = component.slice.item.storyItem.privacy?.additionallyIncludePeers.count ?? 0 - + var hasLinkedStickers = false let media = component.slice.item.storyItem.media._asMedia() if let image = media as? TelegramMediaImage { @@ -3379,6 +3436,7 @@ public final class StoryItemSetContainerComponent: Component { hasLinkedStickers = file.hasLinkedStickers } + let additionalCount = component.slice.item.storyItem.privacy?.additionallyIncludePeers.count ?? 0 let privacyText: String switch component.slice.item.storyItem.privacy?.base { case .closeFriends: @@ -3493,7 +3551,6 @@ public final class StoryItemSetContainerComponent: Component { if let link { UIPasteboard.general.string = link - let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) component.presentController(UndoOverlayController( presentationData: presentationData, content: .linkCopied(text: "Link copied."), @@ -3516,10 +3573,93 @@ public final class StoryItemSetContainerComponent: Component { }))) } - let _ = hasLinkedStickers - - let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + var tip: ContextController.Tip? + var tipSignal: Signal? + + if hasLinkedStickers { + let context = component.context + tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) + + let packsPromise = Promise<[StickerPackReference]>() + packsPromise.set(context.engine.stickers.stickerPacksAttachedToMedia(media: .standalone(media: media))) + + let action: () -> Void = { [weak self] in + self?.openAttachedStickers(packs: packsPromise.get() |> take(1)) + } + tipSignal = packsPromise.get() + |> mapToSignal { packReferences -> Signal in + if packReferences.count > 1 { + return .single(.animatedEmoji(text: "This story contains stickers from [\(packReferences.count) packs]().", arguments: nil, file: nil, action: action)) + } else if let reference = packReferences.first { + return context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false) + |> filter { result in + if case .result = result { + return true + } else { + return false + } + } + |> mapToSignal { result -> Signal in + if case let .result(info, items, _) = result { + let tip: ContextController.Tip = .animatedEmoji( + text: "This story contains\n#[\(info.title)]() stickers.", + arguments: TextNodeWithEntities.Arguments( + context: context, + cache: context.animationCache, + renderer: context.animationRenderer, + placeholderColor: .clear, + attemptSynchronous: true + ), + file: items.first?.file, + action: action) + return .single(tip) + } else { + return .complete() + } + } + } else { + return .complete() + } + } + +// if packReferences.count > 1 { +// items.tip = .animatedEmoji(text: presentationData.strings.ChatContextMenu_EmojiSet(Int32(packReferences.count)), arguments: nil, file: nil, action: action) +// } else if let reference = packReferences.first { +// var tipSignal: Signal +// tipSignal = context.engine.stickers.loadedStickerPack(reference: reference, forceActualized: false) +// +// items.tipSignal = tipSignal +// |> filter { result in +// if case .result = result { +// return true +// } else { +// return false +// } +// } +// |> mapToSignal { result -> Signal in +// if case let .result(info, items, _) = result { +// let tip: ContextController.Tip = .animatedEmoji( +// text: presentationData.strings.ChatContextMenu_ReactionEmojiSetSingle(info.title).string, +// arguments: TextNodeWithEntities.Arguments( +// context: context, +// cache: presentationContext.animationCache, +// renderer: presentationContext.animationRenderer, +// placeholderColor: .clear, +// attemptSynchronous: true +// ), +// file: items.first?.file, +// action: action) +// return .single(tip) +// } else { +// return .complete() +// } +// } +// } + } + + let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal) + + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { return @@ -3555,6 +3695,15 @@ public final class StoryItemSetContainerComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) var items: [ContextMenuItem] = [] + var hasLinkedStickers = false + let media = component.slice.item.storyItem.media._asMedia() + if let image = media as? TelegramMediaImage { + hasLinkedStickers = image.flags.contains(.hasStickers) + } else if let file = media as? TelegramMediaFile { + hasLinkedStickers = file.hasLinkedStickers + } + let _ = hasLinkedStickers + let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.peer._asPeer(), peerSettings: settings._asNotificationSettings()) items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in @@ -3679,7 +3828,7 @@ public final class StoryItemSetContainerComponent: Component { } ) }))) - + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { diff --git a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift index 947c316756..2c69d927b7 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift +++ b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift @@ -154,6 +154,13 @@ public final class TextFieldComponent: Component { super.paste(sender) } } + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + if action == #selector(self.paste(_:)) { + return true + } + return super.canPerformAction(action, withSender: sender) + } } public final class View: UIView, UITextViewDelegate, UIScrollViewDelegate {