Various improvements

This commit is contained in:
Ilya Laktyushin 2023-07-04 23:45:40 +02:00
parent de9cf59c28
commit 7ba7fcc760
12 changed files with 291 additions and 92 deletions

View File

@ -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)

View File

@ -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,9 +639,17 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber]
self.border.lineWidth = 2.0 / self.scale
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
}
}
}
private let snapTimeout = 1.0

View File

@ -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)
}

View File

@ -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:

View File

@ -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<PendingMessageUploadedContentResult?, NoError>, 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<PendingMessageUploadedContentResult?, NoError>, 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<StoryUploadResult, NoError> {
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<StoryUploadResult, NoError> in
switch result {
@ -819,17 +824,6 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
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<StoryUploadResult, NoError> {
func _internal_editStory(account: Account, id: Int32, media: EngineStoryInputMedia?, text: String?, entities: [MessageTextEntity]?, privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {
let contentSignal: Signal<PendingMessageUploadedContentResult?, NoError>
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

View File

@ -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<StoryUploadResult, NoError> {
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<StoryUploadResult, NoError> {
return _internal_editStory(account: self.account, id: id, media: media, text: text, entities: entities, privacy: privacy)
}
public func editStoryPrivacy(id: Int32, privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {

View File

@ -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
@ -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"

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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<ViewControllerComponentContainer.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 {

View File

@ -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<Never, NoError> { [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
var tip: ContextController.Tip?
var tipSignal: Signal<ContextController.Tip?, NoError>?
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)
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<ContextController.Tip?, NoError> 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<ContextController.Tip?, NoError> 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<LoadedStickerPack, NoError>
// 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<ContextController.Tip?, NoError> 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

View File

@ -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 {