diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 0cbd837165..12b26eb8cd 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1243,7 +1243,7 @@ public final class ChatListNode: ListView { super.init() - //self.useMainQueueTransactions = true + self.useMainQueueTransactions = true self.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor self.verticalScrollIndicatorFollowsOverscroll = true diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 1d5728626b..8bba3f95ce 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2440,6 +2440,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U self.requestUpdate(transition: .easeInOut(duration: 0.2)) } }, + onTextEditingEnded: { _ in }, getCurrentImage: { [weak controller] in return controller?.getCurrentImage() }, @@ -2954,6 +2955,8 @@ public final class DrawingToolsInteraction { private let updateColor: (DrawingColor) -> Void private let onInteractionUpdated: (Bool) -> Void + private let onTextEditingEnded: (Bool) -> Void + private let getCurrentImage: () -> UIImage? private let getControllerNode: () -> ASDisplayNode? private let present: (ViewController, PresentationContextType, Any?) -> Void @@ -2980,6 +2983,7 @@ public final class DrawingToolsInteraction { updateVideoPlayback: @escaping (Bool) -> Void, updateColor: @escaping (DrawingColor) -> Void, onInteractionUpdated: @escaping (Bool) -> Void, + onTextEditingEnded: @escaping (Bool) -> Void, getCurrentImage: @escaping () -> UIImage?, getControllerNode: @escaping () -> ASDisplayNode?, present: @escaping (ViewController, PresentationContextType, Any?) -> Void, @@ -2994,6 +2998,7 @@ public final class DrawingToolsInteraction { self.updateVideoPlayback = updateVideoPlayback self.updateColor = updateColor self.onInteractionUpdated = onInteractionUpdated + self.onTextEditingEnded = onTextEditingEnded self.getCurrentImage = getCurrentImage self.getControllerNode = getControllerNode self.present = present @@ -3123,6 +3128,7 @@ public final class DrawingToolsInteraction { public func endTextEditing(reset: Bool) { if let entityView = self.entitiesView.selectedEntityView as? DrawingTextEntityView { entityView.endEditing(reset: reset) + self.onTextEditingEnded(reset) } } diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index 34aab4b781..c9791cb8ed 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -500,12 +500,70 @@ public class StickerPickerScreen: ViewController { self.content = content content.emoji.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak self] _, item, _, _, _, _ in - guard let strongSelf = self else { + performItemAction: { [weak self] groupId, item, _, _, _, _ in + guard let strongSelf = self, let controller = strongSelf.controller else { return } - if let file = item.itemFile { + let context = controller.context + if groupId == AnyHashable("featuredTop"), let file = item.itemFile { + let _ = ( + combineLatest( + ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: controller.context.account.peerId, premiumIfSavedMessages: true), + ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: controller.context.account.peerId, premiumIfSavedMessages: false) + ) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] hasPremium, hasGlobalPremium in + guard let self else { + return + } + + let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedEmojiPacks) + let _ = (combineLatest( + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), + context.account.postbox.combinedView(keys: [viewKey]) + ) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] emojiPacksView, views in + guard let view = views.views[viewKey] as? OrderedItemListView else { + return + } + guard let self else { + return + } + + var installedCollectionIds = Set() + for (id, _, _) in emojiPacksView.collectionInfos { + installedCollectionIds.insert(id) + } + + let stickerPacks = view.items.map({ $0.contents.get(FeaturedStickerPackItem.self)! }).filter({ + !installedCollectionIds.contains($0.info.id) + }) + + for featuredStickerPack in stickerPacks { + if featuredStickerPack.topItems.contains(where: { $0.file.fileId == file.fileId }) { + if let componentView = self.hostView.componentView as? StickerSelectionComponent.View { + if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View, let emojiInputInteraction = self.content?.emoji.inputInteractionHolder.inputInteraction { + pagerView.openCustomSearch(content: EmojiSearchContent( + context: context, + forceTheme: defaultDarkPresentationTheme, + items: stickerPacks, + initialFocusId: featuredStickerPack.info.id, + hasPremiumForUse: hasPremium, + hasPremiumForInstallation: hasGlobalPremium, + parentInputInteraction: emojiInputInteraction + )) + } + } + + break + } + } + }) + }) + } else if let file = item.itemFile { strongSelf.controller?.completion(.file(file)) + strongSelf.controller?.dismiss(animated: true) } else if case let .staticEmoji(emoji) = item.content { if let image = generateImage(CGSize(width: 256.0, height: 256.0), scale: 1.0, rotatedContext: { size, context in context.clear(CGRect(origin: .zero, size: size)) @@ -528,8 +586,8 @@ public class StickerPickerScreen: ViewController { }) { strongSelf.controller?.completion(.image(image)) } + strongSelf.controller?.dismiss(animated: true) } - strongSelf.controller?.dismiss(animated: true) }, deleteBackwards: nil, openStickerSettings: nil, @@ -870,12 +928,42 @@ public class StickerPickerScreen: ViewController { } content.stickers?.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak self] _, item, _, _, _, _ in - guard let strongSelf = self, let file = item.itemFile else { + performItemAction: { [weak self] groupId, item, _, _, _, _ in + guard let self, let controller = self.controller, let file = item.itemFile else { return } - strongSelf.controller?.completion(.file(file)) - strongSelf.controller?.dismiss(animated: true) + if groupId == AnyHashable("featuredTop") { + let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedStickerPacks) + let _ = (controller.context.account.postbox.combinedView(keys: [viewKey]) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] views in + guard let self, let controller = self.controller, let view = views.views[viewKey] as? OrderedItemListView else { + return + } + for featuredStickerPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) { + if featuredStickerPack.topItems.contains(where: { $0.file.fileId == file.fileId }) { + controller.push(FeaturedStickersScreen( + context: controller.context, + highlightedPackId: featuredStickerPack.info.id, + forceTheme: defaultDarkPresentationTheme, + sendSticker: { [weak self] fileReference, _, _ in + guard let self else { + return false + } + self.controller?.completion(.file(fileReference.media)) + self.controller?.dismiss(animated: true) + return true + } + )) + + break + } + } + }) + } else { + self.controller?.completion(.file(file)) + self.controller?.dismiss(animated: true) + } }, deleteBackwards: nil, openStickerSettings: nil, diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index c350abe96f..727b0facf7 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -840,8 +840,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.controlsContainerNode.addSubnode(self.stripContainerNode) self.controlsClippingNode.addSubnode(self.controlsContainerNode) self.controlsClippingOffsetNode.addSubnode(self.controlsClippingNode) - self.addSubnode(self.setByYouNode) - self.addSubnode(self.setByYouImageNode) + self.stripContainerNode.addSubnode(self.setByYouNode) + self.stripContainerNode.addSubnode(self.setByYouImageNode) self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in guard let strongSelf = self else { @@ -1435,7 +1435,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { transition.updateAlpha(node: self.setByYouNode, alpha: 0.7) self.setByYouNode.attributedText = NSAttributedString(string: photoTitle, font: Font.regular(12.0), textColor: UIColor.white) let setByYouSize = self.setByYouNode.updateLayout(size) - self.setByYouNode.frame = CGRect(origin: CGPoint(x: size.width - setByYouSize.width - 14.0, y: size.height - setByYouSize.height - 18.0), size: setByYouSize) + self.setByYouNode.frame = CGRect(origin: CGPoint(x: size.width - setByYouSize.width - 14.0, y: size.height - setByYouSize.height - 58.0), size: setByYouSize) self.setByYouNode.isUserInteractionEnabled = hasLink } else { transition.updateAlpha(node: self.setByYouNode, alpha: 0.0) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index b056ce2cf9..a4fd22e857 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -284,8 +284,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { public var isReactionSearchActive: Bool = false - public var reduceMotion: Bool = false - public static func randomGenericReactionEffect(context: AccountContext) -> Signal { return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false) |> map { result -> [TelegramMediaFile]? in @@ -909,7 +907,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } if animateIn { - itemNode.appear(animated: !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion && !self.reduceMotion) + itemNode.appear(animated: !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion) } if self.getEmojiContent != nil, i == itemLayout.visibleItemCount - 1, let itemNode = itemNode as? ReactionNode { @@ -1199,7 +1197,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - if let animateInFromAnchorRect = animateInFromAnchorRect, !self.reduceMotion { + if let animateInFromAnchorRect = animateInFromAnchorRect { let springDuration: Double = 0.5 let springDamping: CGFloat = 104.0 let springScaleDelay: Double = 0.1 @@ -1608,13 +1606,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { let mainCircleDelay: Double = 0.01 - if !self.presentationData.reduceMotion && !self.reduceMotion { - self.backgroundNode.animateIn() - } + self.backgroundNode.animateIn() self.didAnimateIn = true - if !self.presentationData.reduceMotion && !self.reduceMotion { + if !self.presentationData.reduceMotion { for i in 0 ..< self.items.count { guard let itemNode = self.visibleItemNodes[i] else { continue diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index f7ada43e54..4861eac853 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -466,18 +466,17 @@ public final class PeerStoryListContext { self.peerId = peerId self.isArchived = isArchived - self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0, isCached: true, allEntityFiles: [:]) + self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0, isCached: true) - let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], Int, [MediaId: TelegramMediaFile]) in + let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], Int) in let key = ValueBoxKey(length: 8 + 1) key.setInt64(0, value: peerId.toInt64()) key.setInt8(8, value: isArchived ? 1 : 0) let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) guard let cached = cached else { - return (nil, [], 0, [:]) + return (nil, [], 0) } var items: [EngineStoryItem] = [] - var allEntityFiles: [MediaId: TelegramMediaFile] = [:] for storedItem in cached.items { if case let .item(item) = storedItem, let media = item.media { let mappedItem = EngineStoryItem( @@ -505,30 +504,19 @@ public final class PeerStoryListContext { isEdited: item.isEdited ) items.append(mappedItem) - - for entity in mappedItem.entities { - if case let .CustomEmoji(_, fileId) = entity.type { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if allEntityFiles[mediaId] == nil { - if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { - allEntityFiles[file.fileId] = file - } - } - } - } } } let peerReference = transaction.getPeer(peerId).flatMap(PeerReference.init) - return (peerReference, items, Int(cached.totalCount), allEntityFiles) + return (peerReference, items, Int(cached.totalCount)) } - |> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, totalCount, allEntityFiles in + |> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, totalCount in guard let `self` = self else { return } - self.stateValue = State(peerReference: peerReference, items: items, totalCount: totalCount, loadMoreToken: 0, isCached: true, allEntityFiles: allEntityFiles) + self.stateValue = State(peerReference: peerReference, items: items, totalCount: totalCount, loadMoreToken: 0, isCached: true) self.loadMore() }) } @@ -840,22 +828,19 @@ public final class PeerStoryListContext { public var totalCount: Int public var loadMoreToken: Int? public var isCached: Bool - public var allEntityFiles: [MediaId: TelegramMediaFile] init( peerReference: PeerReference?, items: [EngineStoryItem], totalCount: Int, loadMoreToken: Int?, - isCached: Bool, - allEntityFiles: [MediaId: TelegramMediaFile] + isCached: Bool ) { self.peerReference = peerReference self.items = items self.totalCount = totalCount self.loadMoreToken = loadMoreToken self.isCached = isCached - self.allEntityFiles = allEntityFiles } } diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 272fcb8010..eda214b65d 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -841,6 +841,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { if let pagerView = self.entityKeyboardView.componentView as? EntityKeyboardComponent.View, let emojiInputInteraction = self.emojiInputInteraction { pagerView.openCustomSearch(content: EmojiSearchContent( context: self.context, + forceTheme: self.interaction?.forceTheme, items: stickerPacks, initialFocusId: featuredStickerPack.info.id, hasPremiumForUse: hasPremium, diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift index 82925bfecd..9f8317ce2c 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift @@ -145,7 +145,7 @@ public final class ChatListNavigationBar: Component { public static let searchScrollHeight: CGFloat = 52.0 public static let storiesScrollHeight: CGFloat = { - return 83.0 + return 79.0 }() public final class View: UIView { @@ -404,7 +404,7 @@ public final class ChatListNavigationBar: Component { if component.statusBarHeight < 1.0 { headerContentY = 0.0 } else { - headerContentY = component.statusBarHeight + 5.0 + headerContentY = component.statusBarHeight + 12.0 } } let headerContentFrame = CGRect(origin: CGPoint(x: 0.0, y: headerContentY), size: headerContentSize) @@ -579,7 +579,7 @@ public final class ChatListNavigationBar: Component { var contentHeight = component.statusBarHeight if component.statusBarHeight >= 1.0 { - contentHeight += 3.0 + contentHeight += 10.0 } contentHeight += 44.0 diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 207ec957db..97b5bf614e 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -374,7 +374,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { let isTemplate = file.isCustomTemplateEmoji let context = self.context - if file.isAnimatedSticker || file.isVideoEmoji { + if file.isAnimatedSticker || file.isVideoSticker || file.isVideoEmoji { let keyframeOnly = self.pixelSize.width >= 120.0 self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil)) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift index 9210c7c64c..ca897dbb75 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchContent.swift @@ -41,6 +41,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode } private let context: AccountContext + private let forceTheme: PresentationTheme? private var initialFocusId: ItemCollectionId? private let hasPremiumForUse: Bool private let hasPremiumForInstallation: Bool @@ -70,6 +71,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode public init( context: AccountContext, + forceTheme: PresentationTheme?, items: [FeaturedStickerPackItem], initialFocusId: ItemCollectionId?, hasPremiumForUse: Bool, @@ -77,12 +79,17 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode parentInputInteraction: EmojiPagerContentComponent.InputInteraction ) { self.context = context + self.forceTheme = forceTheme self.initialFocusId = initialFocusId self.hasPremiumForUse = hasPremiumForUse self.hasPremiumForInstallation = hasPremiumForInstallation self.parentInputInteraction = parentInputInteraction - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if let forceTheme { + presentationData = presentationData.withUpdated(theme: forceTheme) + } + self.presentationData = presentationData self.panelHostView = PagerExternalTopPanelContainer() self.inputInteractionHolder = EmojiPagerContentComponent.InputInteractionHolder() diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 4df77e79f9..76dc4ad2b3 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -916,7 +916,6 @@ public final class EntityKeyboardComponent: Component { if component.useExternalSearchContainer, let containerNode = component.makeSearchContainerNode(contentType) { let controller = EntitySearchContainerController(containerNode: containerNode) - self.component?.emojiContent?.inputInteractionHolder.inputInteraction?.pushController(controller) } else { self.searchComponent = EntitySearchContentComponent( @@ -941,14 +940,19 @@ public final class EntityKeyboardComponent: Component { return } - self.searchComponent = EntitySearchContentComponent( - makeContainerNode: { - return content - }, - dismissSearch: { [weak self] in - self?.closeSearch() - } - ) + if component.useExternalSearchContainer { + let controller = EntitySearchContainerController(containerNode: content) + self.component?.emojiContent?.inputInteractionHolder.inputInteraction?.pushController(controller) + } else { + self.searchComponent = EntitySearchContentComponent( + makeContainerNode: { + return content + }, + dismissSearch: { [weak self] in + self?.closeSearch() + } + ) + } component.hideInputUpdated(true, true, Transition(animation: .curve(duration: 0.3, curve: .spring))) } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index c1e17605ad..eb4a4efe49 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -227,9 +227,6 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { let cropRect = CGRect(origin: CGPoint(x: floor((ciImage.extent.size.width - minSide) / 2.0), y: floor((ciImage.extent.size.height - minSide) / 2.0)), size: CGSize(width: minSide, height: minSide)) ciImage = ciImage.cropped(to: cropRect).samplingLinear() ciImage = ciImage.transformed(by: CGAffineTransform(translationX: 0.0, y: -420.0)) - // ciImage = ciImage.transformed(by: CGAffineTransform(translationX: -ciImage.extent.midX, y: -ciImage.extent.midY)) - // ciImage = ciImage.transformed(by: CGAffineTransform(rotationAngle: -.pi / 2.0)) - // ciImage = ciImage.transformed(by: CGAffineTransform(translationX: ciImage.extent.midX, y: ciImage.extent.midY)) var circleMaskFilter: CIFilter? if let current = self.circleMaskFilter { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 43a6dacf83..34785fefb4 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -363,7 +363,13 @@ final class MediaEditorScreenComponent: Component { self.environment?.controller()?.presentInGlobalOverlay(c, with: a) } }, - getNavigationController: { return nil }, + getNavigationController: { [weak self] in + if let self { + return self.environment?.controller()?.navigationController as? NavigationController + } else { + return nil + } + }, requestLayout: { [weak self] transition in if let self { (self.environment?.controller() as? MediaEditorScreen)?.node.requestLayout(forceUpdate: true, transition: Transition(transition)) @@ -985,6 +991,7 @@ final class MediaEditorScreenComponent: Component { stateContext: self.inputMediaNodeStateContext ) inputMediaNode.externalTopPanelContainerImpl = nil + inputMediaNode.useExternalSearchContainer = true if let inputPanelView = self.inputPanel.view, inputMediaNode.view.superview == nil { self.insertSubview(inputMediaNode.view, belowSubview: inputPanelView) } @@ -1135,6 +1142,9 @@ final class MediaEditorScreenComponent: Component { forwardAction: nil, moreAction: nil, presentVoiceMessagesUnavailableTooltip: nil, + paste: { data in + let _ = data + }, audioRecorder: nil, videoRecordingStatus: nil, isRecordingLocked: false, @@ -2057,6 +2067,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.requestUpdate(transition: .easeInOut(duration: 0.2)) } }, + onTextEditingEnded: { [weak self] reset in + if let self, !reset, let entity = self.entitiesView.selectedEntityView?.entity as? DrawingTextEntity, !entity.text.string.isEmpty { + let _ = updateMediaEditorStoredStateInteractively(engine: self.context.engine, { current in + let textSettings = MediaEditorStoredTextSettings(style: entity.style, font: entity.font, fontSize: entity.fontSize, alignment: entity.alignment) + if let current { + return current.withUpdatedTextSettings(textSettings) + } else { + return MediaEditorStoredState(privacy: nil, textSettings: textSettings) + } + }).start() + } + }, getCurrentImage: { return nil }, @@ -2202,13 +2224,33 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let layout = self.validLayout, (layout.inputHeight ?? 0.0) > 0.0 { self.view.endEditing(true) } else { - let textEntity = DrawingTextEntity(text: NSAttributedString(), style: .filled, animation: .none, font: .sanFrancisco, alignment: .center, fontSize: 1.0, color: DrawingColor(color: .white)) - self.interaction?.insertEntity(textEntity) + self.insertTextEntity() } } } } + private func insertTextEntity() { + let _ = (mediaEditorStoredState(engine: self.context.engine) + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let self else { + return + } + var style: DrawingTextEntity.Style = .filled + var font: DrawingTextEntity.Font = .sanFrancisco + var alignment: DrawingTextEntity.Alignment = .center + var fontSize: CGFloat = 1.0 + if let textSettings = state?.textSettings { + style = textSettings.style + font = textSettings.font + alignment = textSettings.alignment + fontSize = textSettings.fontSize + } + let textEntity = DrawingTextEntity(text: NSAttributedString(), style: style, animation: .none, font: font, alignment: alignment, fontSize: fontSize, color: DrawingColor(color: .white)) + self.interaction?.insertEntity(textEntity) + }) + } + private func setupTransitionImage(_ image: UIImage) { self.previewContainerView.alpha = 1.0 @@ -2804,8 +2846,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.controller?.present(controller, in: .window(.root)) return case .text: - let textEntity = DrawingTextEntity(text: NSAttributedString(), style: .filled, animation: .none, font: .sanFrancisco, alignment: .center, fontSize: 1.0, color: DrawingColor(color: .white)) - self.interaction?.insertEntity(textEntity) + self.insertTextEntity() self.hasAnyChanges = true self.controller?.isSavingAvailable = true @@ -2957,6 +2998,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.interaction?.containerLayoutUpdated(layout: layout, transition: transition) var layout = layout + layout.intrinsicInsets.top = topInset layout.intrinsicInsets.bottom = bottomInset + 60.0 controller.presentationContext.containerLayoutUpdated(layout, transition: transition.containedViewLayoutTransition) @@ -3172,7 +3214,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let initialPrivacy = privacy.privacy let timeout = privacy.timeout - let controller = ShareWithPeersScreen( + let controller = ShareWithPeersScreen( context: self.context, initialPrivacy: initialPrivacy, allowScreenshots: !privacy.isForwardingDisabled, @@ -3569,7 +3611,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } if !self.isEditingStory { - let _ = updateMediaEditorStoredStateInteractively(engine: self.context.engine, state: MediaEditorStoredState(privacy: self.state.privacy)).start() + let privacy = self.state.privacy + let _ = updateMediaEditorStoredStateInteractively(engine: self.context.engine, { current in + if let current { + return current.withUpdatedPrivacy(privacy) + } else { + return MediaEditorStoredState(privacy: privacy, textSettings: nil) + } + }).start() } if mediaEditor.resultIsVideo { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorStoredState.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorStoredState.swift index f3b0bef743..b1fc320f57 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorStoredState.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorStoredState.swift @@ -5,15 +5,62 @@ import TelegramCore import TelegramUIPreferences import MediaEditor +public final class MediaEditorStoredTextSettings: Codable { + private enum CodingKeys: String, CodingKey { + case style + case font + case fontSize + case alignment + } + + public let style: DrawingTextEntity.Style + public let font: DrawingTextEntity.Font + public let fontSize: CGFloat + public let alignment: DrawingTextEntity.Alignment + + public init( + style: DrawingTextEntity.Style, + font: DrawingTextEntity.Font, + fontSize: CGFloat, + alignment: DrawingTextEntity.Alignment + ) { + self.style = style + self.font = font + self.fontSize = fontSize + self.alignment = alignment + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.style = try container.decode(DrawingTextEntity.Style.self, forKey: .style) + self.font = try container.decode(DrawingTextEntity.Font.self, forKey: .font) + self.fontSize = try container.decode(CGFloat.self, forKey: .fontSize) + self.alignment = try container.decode(DrawingTextEntity.Alignment.self, forKey: .alignment) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.style, forKey: .style) + try container.encode(self.font, forKey: .font) + try container.encode(self.fontSize, forKey: .fontSize) + try container.encode(self.alignment, forKey: .alignment) + } +} + public final class MediaEditorStoredState: Codable { private enum CodingKeys: String, CodingKey { case privacy + case textSettings } public let privacy: MediaEditorResultPrivacy? + public let textSettings: MediaEditorStoredTextSettings? - public init(privacy: MediaEditorResultPrivacy?) { + public init(privacy: MediaEditorResultPrivacy?, textSettings: MediaEditorStoredTextSettings?) { self.privacy = privacy + self.textSettings = textSettings } public init(from decoder: Decoder) throws { @@ -24,12 +71,17 @@ public final class MediaEditorStoredState: Codable { } else { self.privacy = nil } + if let data = try container.decodeIfPresent(Data.self, forKey: .textSettings), let privacy = try? JSONDecoder().decode(MediaEditorStoredTextSettings.self, from: data) { + self.textSettings = privacy + } else { + self.textSettings = nil + } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - if let privacy = self .privacy { + if let privacy = self.privacy { if let data = try? JSONEncoder().encode(privacy) { try container.encode(data, forKey: .privacy) } else { @@ -38,6 +90,24 @@ public final class MediaEditorStoredState: Codable { } else { try container.encodeNil(forKey: .privacy) } + + if let textSettings = self.textSettings { + if let data = try? JSONEncoder().encode(textSettings) { + try container.encode(data, forKey: .textSettings) + } else { + try container.encodeNil(forKey: .textSettings) + } + } else { + try container.encodeNil(forKey: .textSettings) + } + } + + public func withUpdatedPrivacy(_ privacy: MediaEditorResultPrivacy) -> MediaEditorStoredState { + return MediaEditorStoredState(privacy: privacy, textSettings: self.textSettings) + } + + public func withUpdatedTextSettings(_ textSettings: MediaEditorStoredTextSettings) -> MediaEditorStoredState { + return MediaEditorStoredState(privacy: self.privacy, textSettings: textSettings) } } @@ -51,13 +121,19 @@ func mediaEditorStoredState(engine: TelegramEngine) -> Signal Signal { +func updateMediaEditorStoredStateInteractively(engine: TelegramEngine, _ f: @escaping (MediaEditorStoredState?) -> MediaEditorStoredState?) -> Signal { let key = EngineDataBuffer(length: 4) key.setInt32(0, value: 0) - if let state = state { - return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.mediaEditorState, id: key, item: state) - } else { - return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.mediaEditorState, id: key) + return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.mediaEditorState, id: key)) + |> map { entry -> MediaEditorStoredState? in + return entry?.get(MediaEditorStoredState.self) + } + |> mapToSignal { state -> Signal in + if let updatedState = f(state) { + return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.mediaEditorState, id: key, item: updatedState) + } else { + return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.mediaEditorState, id: key) + } } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift index e24325cd66..3d844f84a7 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift @@ -268,6 +268,7 @@ final class StoryPreviewComponent: Component { forwardAction: {}, moreAction: { _, _ in }, presentVoiceMessagesUnavailableTooltip: nil, + paste: { _ in }, audioRecorder: nil, videoRecordingStatus: nil, isRecordingLocked: false, diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 5fa23b3f60..af07a5cc74 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -82,6 +82,7 @@ public final class MessageInputPanelComponent: Component { public let forwardAction: (() -> Void)? public let moreAction: ((UIView, ContextGesture?) -> Void)? public let presentVoiceMessagesUnavailableTooltip: ((UIView) -> Void)? + public let paste: (TextFieldComponent.PasteData) -> Void public let audioRecorder: ManagedAudioRecorder? public let videoRecordingStatus: InstantVideoControllerRecordingStatus? public let isRecordingLocked: Bool @@ -121,6 +122,7 @@ public final class MessageInputPanelComponent: Component { forwardAction: (() -> Void)?, moreAction: ((UIView, ContextGesture?) -> Void)?, presentVoiceMessagesUnavailableTooltip: ((UIView) -> Void)?, + paste: @escaping (TextFieldComponent.PasteData) -> Void, audioRecorder: ManagedAudioRecorder?, videoRecordingStatus: InstantVideoControllerRecordingStatus?, isRecordingLocked: Bool, @@ -159,6 +161,7 @@ public final class MessageInputPanelComponent: Component { self.forwardAction = forwardAction self.moreAction = moreAction self.presentVoiceMessagesUnavailableTooltip = presentVoiceMessagesUnavailableTooltip + self.paste = paste self.audioRecorder = audioRecorder self.videoRecordingStatus = videoRecordingStatus self.isRecordingLocked = isRecordingLocked @@ -521,6 +524,9 @@ public final class MessageInputPanelComponent: Component { hideKeyboard: component.hideKeyboard, present: { c in component.presentController(c) + }, + paste: { data in + component.paste(data) } )), environment: {}, diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/StickersResultPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/StickersResultPanelComponent.swift index ca7cf3ac4b..008a524612 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/StickersResultPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/StickersResultPanelComponent.swift @@ -79,7 +79,8 @@ final class StickersResultPanelComponent: Component { self.itemsPerRow = itemsPerRow self.itemCount = itemCount - self.contentSize = CGSize(width: containerSize.width, height: topInset + CGFloat(itemCount) * itemSize.height + bottomInset) + let rowsCount = ceil(CGFloat(itemCount) / CGFloat(itemsPerRow)) + self.contentSize = CGSize(width: containerSize.width, height: topInset + rowsCount * (itemSize.height + itemSpacing) - itemSpacing + bottomInset) } func visibleItems(for rect: CGRect) -> Range? { @@ -445,7 +446,7 @@ final class StickersResultPanelComponent: Component { let itemLayout = ItemLayout( containerSize: CGSize(width: availableSize.width, height: minimizedHeight), - bottomInset: 9.0, + bottomInset: 40.0, topInset: 9.0, sideInset: sideInset, itemSize: CGSize(width: itemSize, height: itemSize), diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index 563fd3d1c9..5662bfb783 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -367,14 +367,17 @@ public final class PeerListItemComponent: Component { ) let titleSpacing: CGFloat = 2.0 + var titleVerticalOffset: CGFloat = 0.0 let centralContentHeight: CGFloat if labelSize.height > 0.0, case .generic = component.style { centralContentHeight = titleSize.height + labelSize.height + titleSpacing + titleVerticalOffset = -1.0 } else { centralContentHeight = titleSize.height } - let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: -1.0 + floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: titleSize) + + let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: titleVerticalOffset + floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { titleView.isUserInteractionEnabled = false @@ -396,46 +399,8 @@ public final class PeerListItemComponent: Component { transition.animateAlpha(view: titleView, from: 0.0, to: 1.0) } } - if let labelView = self.label.view { - var iconLabelOffset: CGFloat = 0.0 - - if case .checks = component.subtitleAccessory { - let iconView: UIImageView - if let current = self.iconView { - iconView = current - } else { - iconView = UIImageView(image: readIconImage) - iconView.tintColor = component.theme.list.itemSecondaryTextColor - self.iconView = iconView - self.containerButton.addSubview(iconView) - } - - if let image = iconView.image { - iconLabelOffset = image.size.width + 4.0 - transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing + 3.0 + floor((labelSize.height - image.size.height) * 0.5)), size: image.size)) - } - } else if let iconView = self.iconView { - self.iconView = nil - iconView.removeFromSuperview() - } - - if labelView.superview == nil { - labelView.isUserInteractionEnabled = false - self.containerButton.addSubview(labelView) - } - - let labelFrame: CGRect - switch component.style { - case .generic: - labelFrame = CGRect(origin: CGPoint(x: titleFrame.minX + iconLabelOffset, y: titleFrame.maxY + titleSpacing), size: labelSize) - case .compact: - labelFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: labelSize) - } - - transition.setFrame(view: labelView, frame: labelFrame) - } - if let statusIcon { + if let statusIcon, case .generic = component.style { let animationCache = component.context.animationCache let animationRenderer = component.context.animationRenderer @@ -477,6 +442,45 @@ public final class PeerListItemComponent: Component { avatarIcon.view?.removeFromSuperview() } + if let labelView = self.label.view { + var iconLabelOffset: CGFloat = 0.0 + + if case .checks = component.subtitleAccessory { + let iconView: UIImageView + if let current = self.iconView { + iconView = current + } else { + iconView = UIImageView(image: readIconImage) + iconView.tintColor = component.theme.list.itemSecondaryTextColor + self.iconView = iconView + self.containerButton.addSubview(iconView) + } + + if let image = iconView.image { + iconLabelOffset = image.size.width + 4.0 + transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing + 3.0 + floor((labelSize.height - image.size.height) * 0.5)), size: image.size)) + } + } else if let iconView = self.iconView { + self.iconView = nil + iconView.removeFromSuperview() + } + + if labelView.superview == nil { + labelView.isUserInteractionEnabled = false + self.containerButton.addSubview(labelView) + } + + let labelFrame: CGRect + switch component.style { + case .generic: + labelFrame = CGRect(origin: CGPoint(x: titleFrame.minX + iconLabelOffset, y: titleFrame.maxY + titleSpacing), size: labelSize) + case .compact: + labelFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: labelSize) + } + + transition.setFrame(view: labelView, frame: labelFrame) + } + if themeUpdated { self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 2a048f019a..dcc296e21b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -63,11 +63,9 @@ public final class StoryContentContextImpl: StoryContentContext { ), context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()) ) - |> mapToSignal { _, views, globalNotificationSettings -> Signal<(CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile]), NoError> in - return context.account.postbox.transaction { transaction -> (CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings, [MediaId: TelegramMediaFile]) in + |> mapToSignal { _, views, globalNotificationSettings -> Signal<(CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings), NoError> in + return context.account.postbox.transaction { transaction -> (CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings) in var peers: [PeerId: Peer] = [:] - var allEntityFiles: [MediaId: TelegramMediaFile] = [:] - if let itemsView = views.views[PostboxViewKey.storyItems(peerId: peerId)] as? StoryItemsView { for item in itemsView.items { if let item = item.value.get(Stories.StoredItem.self), case let .item(itemValue) = item { @@ -78,24 +76,13 @@ public final class StoryContentContextImpl: StoryContentContext { } } } - for entity in itemValue.entities { - if case let .CustomEmoji(_, fileId) = entity.type { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if allEntityFiles[mediaId] == nil { - if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { - allEntityFiles[file.fileId] = file - } - } - } - } } } } - - return (views, peers, globalNotificationSettings, allEntityFiles) + return (views, peers, globalNotificationSettings) } } - |> deliverOnMainQueue).start(next: { [weak self] views, peers, globalNotificationSettings, allEntityFiles in + |> deliverOnMainQueue).start(next: { [weak self] views, peers, globalNotificationSettings in guard let self else { return } @@ -270,8 +257,7 @@ public final class StoryContentContextImpl: StoryContentContext { return StoryContentItem( position: nil, peerId: peer.id, - storyItem: item, - entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles) + storyItem: item ) } @@ -282,8 +268,7 @@ public final class StoryContentContextImpl: StoryContentContext { item: StoryContentItem( position: mappedFocusedIndex ?? focusedIndex, peerId: peer.id, - storyItem: mappedItem, - entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) + storyItem: mappedItem ), totalCount: totalCount, previousItemId: previousItemId, @@ -922,12 +907,11 @@ public final class SingleStoryContentContextImpl: StoryContentContext { TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global() ), - context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile]) in + context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer]) in guard let item = transaction.getStory(id: storyId)?.get(Stories.StoredItem.self) else { - return (nil, [:], [:]) + return (nil, [:]) } var peers: [PeerId: Peer] = [:] - var allEntityFiles: [MediaId: TelegramMediaFile] = [:] if case let .item(item) = item { if let views = item.views { for id in views.seenPeerIds { @@ -936,18 +920,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { } } } - for entity in item.entities { - if case let .CustomEmoji(_, fileId) = entity.type { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if allEntityFiles[mediaId] == nil { - if let file = transaction.getMedia(mediaId) as? TelegramMediaFile { - allEntityFiles[file.fileId] = file - } - } - } - } } - return (item, peers, allEntityFiles) + return (item, peers) } ) |> deliverOnMainQueue).start(next: { [weak self] data, itemAndPeers in @@ -956,7 +930,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { } let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data - let (item, peers, allEntityFiles) = itemAndPeers + let (item, peers) = itemAndPeers guard let peer else { return @@ -1007,8 +981,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { let mainItem = StoryContentItem( position: 0, peerId: peer.id, - storyItem: mappedItem, - entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) + storyItem: mappedItem ) let stateValue = StoryContentContextState( slice: StoryContentContextState.FocusedSlice( @@ -1157,8 +1130,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { return StoryContentItem( position: nil, peerId: peer.id, - storyItem: stateItem, - entityFiles: extractItemEntityFiles(item: stateItem, allEntityFiles: state.allEntityFiles) + storyItem: stateItem ) } @@ -1169,8 +1141,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { item: StoryContentItem( position: nil, peerId: peer.id, - storyItem: item, - entityFiles: extractItemEntityFiles(item: item, allEntityFiles: state.allEntityFiles) + storyItem: item ), totalCount: state.totalCount, previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id, @@ -1355,16 +1326,3 @@ public func preloadStoryMedia(context: AccountContext, peer: PeerReference, stor return combineLatest(signals) |> ignoreValues } - -func extractItemEntityFiles(item: EngineStoryItem, allEntityFiles: [MediaId: TelegramMediaFile]) -> [MediaId: TelegramMediaFile] { - var result: [MediaId: TelegramMediaFile] = [:] - for entity in item.entities { - if case let .CustomEmoji(_, fileId) = entity.type { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if let file = allEntityFiles[mediaId] { - result[file.fileId] = file - } - } - } - return result -} diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 248b9bfaa0..42f41f0fb7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -843,7 +843,7 @@ private final class StoryContainerScreenComponent: Component { context: component.context, chatPeerId: nil, areCustomEmojiEnabled: true, - hasTrending: false, + hasTrending: true, hasSearch: true, hideBackground: true, sendGif: nil @@ -1123,19 +1123,10 @@ private final class StoryContainerScreenComponent: Component { switch self.audioMode { case .ambient: - if self.isMuteSwitchOn { - self.audioMode = .off - for (_, itemSetView) in self.visibleItemSetViews { - if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - componentView.enterAmbientMode(ambient: !self.isMuteSwitchOn) - } - } - } else { - self.audioMode = .on - for (_, itemSetView) in self.visibleItemSetViews { - if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - componentView.leaveAmbientMode() - } + self.audioMode = .on + for (_, itemSetView) in self.visibleItemSetViews { + if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + componentView.leaveAmbientMode() } } case .on: diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index f4f6add294..fdf0210e1e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -80,18 +80,15 @@ public final class StoryContentItem: Equatable { public let position: Int? public let peerId: EnginePeer.Id? public let storyItem: EngineStoryItem - public let entityFiles: [EngineMedia.Id: TelegramMediaFile] public init( position: Int?, peerId: EnginePeer.Id?, - storyItem: EngineStoryItem, - entityFiles: [EngineMedia.Id: TelegramMediaFile] + storyItem: EngineStoryItem ) { self.position = position self.peerId = peerId self.storyItem = storyItem - self.entityFiles = entityFiles } public static func ==(lhs: StoryContentItem, rhs: StoryContentItem) -> Bool { @@ -104,9 +101,6 @@ public final class StoryContentItem: Equatable { if lhs.storyItem != rhs.storyItem { return false } - if lhs.entityFiles != rhs.entityFiles { - return false - } return true } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift index 83b14615f3..a957c5d8e9 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift @@ -43,7 +43,6 @@ final class StoryContentCaptionComponent: Component { let context: AccountContext let text: String let entities: [MessageTextEntity] - let entityFiles: [EngineMedia.Id: TelegramMediaFile] let action: (Action) -> Void init( @@ -51,14 +50,12 @@ final class StoryContentCaptionComponent: Component { context: AccountContext, text: String, entities: [MessageTextEntity], - entityFiles: [EngineMedia.Id: TelegramMediaFile], action: @escaping (Action) -> Void ) { self.externalState = externalState self.context = context self.text = text self.entities = entities - self.entityFiles = entityFiles self.action = action } @@ -75,9 +72,6 @@ final class StoryContentCaptionComponent: Component { if lhs.entities != rhs.entities { return false } - if lhs.entityFiles != rhs.entityFiles { - return false - } return true } @@ -365,8 +359,7 @@ final class StoryContentCaptionComponent: Component { boldItalicFont: Font.semiboldItalic(16.0), fixedFont: Font.monospace(16.0), blockQuoteFont: Font.monospace(16.0), - message: nil, - entityFiles: component.entityFiles + message: nil ) let makeLayout = TextNodeWithEntities.asyncLayout(self.textNode) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index c37c2a9aa3..96d944a42e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1795,6 +1795,8 @@ public final class StoryItemSetContainerComponent: Component { self.voiceMessagesRestrictedTooltipController = controller self.state?.updated(transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut))) }, + paste: { _ in + }, audioRecorder: self.sendMessageContext.audioRecorderValue, videoRecordingStatus: !self.sendMessageContext.hasRecordedVideoPreview ? self.sendMessageContext.videoRecorderValue?.audioStatus : nil, isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked, @@ -2513,7 +2515,6 @@ public final class StoryItemSetContainerComponent: Component { context: component.context, text: component.slice.item.storyItem.text, entities: component.slice.item.storyItem.entities, - entityFiles: component.slice.item.entityFiles, action: { [weak self] action in guard let self, let component = self.component else { return @@ -2645,7 +2646,7 @@ public final class StoryItemSetContainerComponent: Component { reactionContextNode.displayTail = false self.reactionContextNode = reactionContextNode - reactionContextNode.reactionSelected = { [weak self, weak reactionContextNode] updateReaction, _ in + reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in guard let self, let component = self.component else { return } @@ -2669,24 +2670,23 @@ public final class StoryItemSetContainerComponent: Component { targetView.isUserInteractionEnabled = false self.addSubview(targetView) - if let reactionContextNode { - reactionContextNode.willAnimateOutToReaction(value: updateReaction.reaction) - reactionContextNode.animateOutToReaction(value: updateReaction.reaction, targetView: targetView, hideNode: false, animateTargetContainer: nil, addStandaloneReactionAnimation: "".isEmpty ? nil : { [weak self] standaloneReactionAnimation in - guard let self else { - return - } - standaloneReactionAnimation.frame = self.bounds - self.addSubview(standaloneReactionAnimation.view) - }, completion: { [weak targetView, weak reactionContextNode] in - targetView?.removeFromSuperview() - if let reactionContextNode { - reactionContextNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.3, removeOnCompletion: false) - reactionContextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak reactionContextNode] _ in - reactionContextNode?.view.removeFromSuperview() - }) - } - }) - } + reactionContextNode.willAnimateOutToReaction(value: updateReaction.reaction) + reactionContextNode.animateOutToReaction(value: updateReaction.reaction, targetView: targetView, hideNode: false, animateTargetContainer: nil, addStandaloneReactionAnimation: "".isEmpty ? nil : { [weak self] standaloneReactionAnimation in + guard let self else { + return + } + standaloneReactionAnimation.frame = self.bounds + self.addSubview(standaloneReactionAnimation.view) + }, completion: { [weak targetView, weak reactionContextNode] in + targetView?.removeFromSuperview() + if let reactionContextNode { + reactionContextNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.3, removeOnCompletion: false) + reactionContextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak reactionContextNode] _ in + reactionContextNode?.view.removeFromSuperview() + }) + } + }) + if hasFirstResponder(self) { self.sendMessageContext.currentInputMode = .text @@ -2797,7 +2797,6 @@ public final class StoryItemSetContainerComponent: Component { if animateReactionsIn { reactionContextNode.animateIn(from: reactionsAnchorRect) - reactionContextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } } } else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index f61e854c25..49f271b36a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -159,8 +159,12 @@ final class StoryItemSetContainerSendMessage { self.view?.component?.controller()?.presentInGlobalOverlay(c, with: a) } }, - getNavigationController: { - return self.view?.component?.controller()?.navigationController as? NavigationController + getNavigationController: { [weak self] in + if let self { + return self.view?.component?.controller()?.navigationController as? NavigationController + } else { + return nil + } }, requestLayout: { [weak self] transition in if let self { diff --git a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift index 7114f82d76..47141b8e51 100644 --- a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift @@ -345,7 +345,7 @@ public final class StorySetIndicatorComponent: Component { if component.hasUnseen { borderColors = [component.theme.chatList.storyUnseenColors.topColor.argb, component.theme.chatList.storyUnseenColors.bottomColor.argb] } else { - borderColors = [UIColor(white: 1.0, alpha: 0.3).argb, UIColor(white: 1.0, alpha: 0.3).argb] + borderColors = [component.theme.chatList.storySeenColors.topColor.argb, component.theme.chatList.storySeenColors.bottomColor.argb] } let imageSize = CGSize(width: maxItemsWidth, height: outerDiameter) diff --git a/submodules/TelegramUI/Components/TextFieldComponent/BUILD b/submodules/TelegramUI/Components/TextFieldComponent/BUILD index ebc6337942..db908db94c 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/BUILD +++ b/submodules/TelegramUI/Components/TextFieldComponent/BUILD @@ -18,7 +18,9 @@ swift_library( "//submodules/AccountContext", "//submodules/InvisibleInkDustNode", "//submodules/TelegramUI/Components/EmojiTextAttachmentView", - "//submodules/ChatTextLinkEditUI" + "//submodules/ChatTextLinkEditUI", + "//submodules/Pasteboard", + "//submodules/ImageTransparency", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift index d5f2dde083..eb33df920e 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift +++ b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift @@ -9,7 +9,10 @@ import InvisibleInkDustNode import EmojiTextAttachmentView import AccountContext import TextFormat +import Pasteboard import ChatTextLinkEditUI +import MobileCoreServices +import ImageTransparency public final class EmptyInputView: UIView, UIInputViewAudioFeedback { public var enableInputClicksWhenVisible: Bool { @@ -51,6 +54,14 @@ public final class TextFieldComponent: Component { } } + public enum PasteData { + case sticker(image: UIImage, isMemoji: Bool) + case images([UIImage]) + case video(Data) + case gif(Data) + } + + public final class AnimationHint { public enum Kind { case textChanged @@ -72,6 +83,7 @@ public final class TextFieldComponent: Component { public let insets: UIEdgeInsets public let hideKeyboard: Bool public let present: (ViewController) -> Void + public let paste: (PasteData) -> Void public init( context: AccountContext, @@ -81,7 +93,8 @@ public final class TextFieldComponent: Component { textColor: UIColor, insets: UIEdgeInsets, hideKeyboard: Bool, - present: @escaping (ViewController) -> Void + present: @escaping (ViewController) -> Void, + paste: @escaping (PasteData) -> Void ) { self.context = context self.strings = strings @@ -91,6 +104,7 @@ public final class TextFieldComponent: Component { self.insets = insets self.hideKeyboard = hideKeyboard self.present = present + self.paste = paste } public static func ==(lhs: TextFieldComponent, rhs: TextFieldComponent) -> Bool { @@ -131,11 +145,21 @@ public final class TextFieldComponent: Component { } } + final class TextView: UITextView { + var onPaste: () -> Bool = { return true } + + override func paste(_ sender: Any?) { + if self.onPaste() { + super.paste(sender) + } + } + } + public final class View: UIView, UITextViewDelegate, UIScrollViewDelegate { private let textContainer: NSTextContainer private let textStorage: NSTextStorage private let layoutManager: NSLayoutManager - private let textView: UITextView + private let textView: TextView private var spoilerView: InvisibleInkDustView? private var customEmojiContainerView: CustomEmojiContainerView? @@ -163,7 +187,7 @@ public final class TextFieldComponent: Component { self.layoutManager.addTextContainer(self.textContainer) self.textStorage.addLayoutManager(self.layoutManager) - self.textView = UITextView(frame: CGRect(), textContainer: self.textContainer) + self.textView = TextView(frame: CGRect(), textContainer: self.textContainer) self.textView.translatesAutoresizingMaskIntoConstraints = false self.textView.backgroundColor = nil self.textView.layer.isOpaque = false @@ -185,6 +209,10 @@ public final class TextFieldComponent: Component { NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: UIColor.white ] + + self.textView.onPaste = { [weak self] in + return self?.onPaste() ?? false + } } required init?(coder: NSCoder) { @@ -224,6 +252,81 @@ public final class TextFieldComponent: Component { self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) } + private func onPaste() -> Bool { + guard let component = self.component else { + return false + } + let pasteboard = UIPasteboard.general + + var attributedString: NSAttributedString? + if let data = pasteboard.data(forPasteboardType: kUTTypeRTF as String) { + attributedString = chatInputStateStringFromRTF(data, type: NSAttributedString.DocumentType.rtf) + } else if let data = pasteboard.data(forPasteboardType: "com.apple.flat-rtfd") { + attributedString = chatInputStateStringFromRTF(data, type: NSAttributedString.DocumentType.rtfd) + } + + if let attributedString = attributedString { + self.updateInputState { current in + if let inputText = current.inputText.mutableCopy() as? NSMutableAttributedString { + inputText.replaceCharacters(in: NSMakeRange(current.selectionRange.lowerBound, current.selectionRange.count), with: attributedString) + let updatedRange = current.selectionRange.lowerBound + attributedString.length + return InputState(inputText: inputText, selectionRange: updatedRange ..< updatedRange) + } else { + return InputState(inputText: attributedString) + } + } + self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged))) + return false + } + + var images: [UIImage] = [] + if let data = pasteboard.data(forPasteboardType: "com.compuserve.gif") { + component.paste(.gif(data)) + return false + } else if let data = pasteboard.data(forPasteboardType: "public.mpeg-4") { + component.paste(.video(data)) + return false + } else { + var isPNG = false + var isMemoji = false + for item in pasteboard.items { + if let image = item["com.apple.png-sticker"] as? UIImage { + images.append(image) + isPNG = true + isMemoji = true + } else if let image = item[kUTTypePNG as String] as? UIImage { + images.append(image) + isPNG = true + } else if let image = item["com.apple.uikit.image"] as? UIImage { + images.append(image) + isPNG = true + } else if let image = item[kUTTypeJPEG as String] as? UIImage { + images.append(image) + } else if let image = item[kUTTypeGIF as String] as? UIImage { + images.append(image) + } + } + + if isPNG && images.count == 1, let image = images.first, let cgImage = image.cgImage { + let maxSide = max(image.size.width, image.size.height) + if maxSide.isZero { + return false + } + let aspectRatio = min(image.size.width, image.size.height) / maxSide + if isMemoji || (imageHasTransparency(cgImage) && aspectRatio > 0.2) { + component.paste(.sticker(image: image, isMemoji: isMemoji)) + return false + } + } + + if !images.isEmpty { + component.paste(.images(images)) + return false + } + } + return true + } + public func textViewDidChange(_ textView: UITextView) { guard let component = self.component else { return @@ -257,7 +360,7 @@ public final class TextFieldComponent: Component { public func textViewDidEndEditing(_ textView: UITextView) { self.state?.updated(transition: Transition(animation: .curve(duration: 0.5, curve: .spring)).withUserData(AnimationHint(kind: .textFocusChanged))) } - + @available(iOS 16.0, *) public func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { let filteredActions: Set = Set([ diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift index dc5858ffd2..7d2c2ea41c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift @@ -254,10 +254,6 @@ class ChatMessageBubbleContentNode: ASDisplayNode { return nil } - func targetForStoryTransition(id: StoryId) -> UIView? { - return nil - } - func getStatusNode() -> ASDisplayNode? { return nil } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index c5e210744b..89893bbeab 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -4589,11 +4589,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode guard let item = self.item else { return nil } - for contentNode in self.contentNodes { - if let value = contentNode.targetForStoryTransition(id: id) { - return value - } - } for attribute in item.message.attributes { if let attribute = attribute as? ReplyStoryAttribute { if attribute.storyId == id { diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift index a236a18bac..4f9bfd4deb 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -429,10 +429,6 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { return nil } - override func targetForStoryTransition(id: StoryId) -> UIView? { - return self.interactiveVideoNode.targetForStoryTransition(id: id) - } - override var disablesClipping: Bool { return true } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 93b6519861..7917a3d28f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -431,8 +431,6 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } - var replyMessage: Message? - var replyStory: StoryId? for attribute in item.message.attributes { if let attribute = attribute as? InlineBotMessageAttribute { var inlineBotNameString: String? @@ -469,34 +467,28 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } - if let replyAttribute = attribute as? ReplyMessageAttribute { + if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { } else { - replyMessage = item.message.associatedMessages[replyAttribute.messageId] + replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments( + presentationData: item.presentationData, + strings: item.presentationData.strings, + context: item.context, + type: .standalone, + message: replyMessage, + story: nil, + parentMessage: item.message, + constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), + animationCache: item.controllerInteraction.presentationContext.animationCache, + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData + )) } - } else if let attribute = attribute as? ReplyStoryAttribute { - replyStory = attribute.storyId } else if let _ = attribute as? InlineBotMessageAttribute { } else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty { replyMarkup = attribute } } - - if replyMessage != nil || replyStory != nil { - replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments( - presentationData: item.presentationData, - strings: item.presentationData.strings, - context: item.context, - type: .standalone, - message: replyMessage, - story: replyStory, - parentMessage: item.message, - constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), - animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, - associatedData: item.associatedData - )) - } var updatedShareButtonNode: ChatMessageShareButton? if needsShareButton { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index b7ae99114b..c6f3efe3db 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -316,9 +316,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let availableContentWidth = width - bubbleEdgeInset * 2.0 - bubbleContentInsetsLeft - 20.0 if !ignoreHeaders { - var replyMessage: Message? - var replyStory: StoryId? - for attribute in item.message.attributes { if let attribute = attribute as? InlineBotMessageAttribute { var inlineBotNameString: String? @@ -341,32 +338,23 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - if let replyAttribute = attribute as? ReplyMessageAttribute { + if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { } else { - replyMessage = item.message.associatedMessages[replyAttribute.messageId] + replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments( + presentationData: item.presentationData, + strings: item.presentationData.strings, + context: item.context, + type: .standalone, + message: replyMessage, + story: nil, + parentMessage: item.message, + constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), + animationCache: item.controllerInteraction.presentationContext.animationCache, + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData + )) } - } else if let attribute = attribute as? ReplyStoryAttribute { - replyStory = attribute.storyId - } - } - - if replyMessage != nil || replyStory != nil { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyMessage?.id { - } else { - replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments( - presentationData: item.presentationData, - strings: item.presentationData.strings, - context: item.context, - type: .standalone, - message: replyMessage, - story: replyStory, - parentMessage: item.message, - constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), - animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, - associatedData: item.associatedData - )) } } } @@ -1271,9 +1259,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let attribute = attribute as? ReplyMessageAttribute { item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId) return - } else if let attribute = attribute as? ReplyStoryAttribute { - item.controllerInteraction.navigateToStory(item.message, attribute.storyId) - return } } } @@ -1830,20 +1815,4 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.canAttachContent = false } - - func targetForStoryTransition(id: StoryId) -> UIView? { - guard let item = self.item else { - return nil - } - for attribute in item.message.attributes { - if let attribute = attribute as? ReplyStoryAttribute { - if attribute.storyId == id { - if let replyInfoNode = self.replyInfoNode { - return replyInfoNode.mediaTransitionView() - } - } - } - } - return nil - } } diff --git a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift index a8cf179e0e..ec6f8826a0 100644 --- a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift +++ b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift @@ -52,7 +52,7 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M return string } -public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:]) -> NSAttributedString { +public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?) -> NSAttributedString { var nsString: NSString? let string = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: baseFont, NSAttributedString.Key.foregroundColor: baseColor]) var skipEntity = false @@ -252,14 +252,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti } } case let .CustomEmoji(_, fileId): - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - var emojiFile: TelegramMediaFile? - if let file = message?.associatedMedia[mediaId] as? TelegramMediaFile { - emojiFile = file - } else { - emojiFile = entityFiles[mediaId] - } - string.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: fileId, file: emojiFile), range: range) + string.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: fileId, file: message?.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile), range: range) default: break }