diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index cd168e2cf3..26cb4321d8 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -128,8 +128,9 @@ private final class PhoneAndCountryNode: ASDisplayNode { if let strongSelf = self { let _ = strongSelf.processNumberChange(number: strongSelf.phoneInputNode.number) - if strongSelf.hasCountry { - strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty) + let isServiceNumber = strongSelf.phoneInputNode.number.hasPrefix("+999") + if strongSelf.hasCountry || isServiceNumber { + strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty || isServiceNumber) } else { strongSelf.hasNumberUpdated?(false) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 12b26eb8cd..0cbd837165 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/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index 727b0facf7..c350abe96f 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.stripContainerNode.addSubnode(self.setByYouNode) - self.stripContainerNode.addSubnode(self.setByYouImageNode) + self.addSubnode(self.setByYouNode) + self.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 - 58.0), size: setByYouSize) + self.setByYouNode.frame = CGRect(origin: CGPoint(x: size.width - setByYouSize.width - 14.0, y: size.height - setByYouSize.height - 18.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 a4fd22e857..b056ce2cf9 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -284,6 +284,8 @@ 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 @@ -907,7 +909,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } if animateIn { - itemNode.appear(animated: !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion) + itemNode.appear(animated: !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion && !self.reduceMotion) } if self.getEmojiContent != nil, i == itemLayout.visibleItemCount - 1, let itemNode = itemNode as? ReactionNode { @@ -1197,7 +1199,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - if let animateInFromAnchorRect = animateInFromAnchorRect { + if let animateInFromAnchorRect = animateInFromAnchorRect, !self.reduceMotion { let springDuration: Double = 0.5 let springDamping: CGFloat = 104.0 let springScaleDelay: Double = 0.1 @@ -1606,11 +1608,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { let mainCircleDelay: Double = 0.01 - self.backgroundNode.animateIn() + if !self.presentationData.reduceMotion && !self.reduceMotion { + self.backgroundNode.animateIn() + } self.didAnimateIn = true - if !self.presentationData.reduceMotion { + if !self.presentationData.reduceMotion && !self.reduceMotion { for i in 0 ..< self.items.count { guard let itemNode = self.visibleItemNodes[i] else { continue diff --git a/submodules/TelegramCore/Sources/Network/FetchV2.swift b/submodules/TelegramCore/Sources/Network/FetchV2.swift index 830e96ddfc..652ee0df72 100644 --- a/submodules/TelegramCore/Sources/Network/FetchV2.swift +++ b/submodules/TelegramCore/Sources/Network/FetchV2.swift @@ -215,6 +215,7 @@ private final class FetchImpl { private var requiredRangesDisposable: Disposable? private var requiredRanges: [RequiredRange] = [] + private let defaultPartSize: Int64 private var state: State? private let loggingIdentifier: String @@ -263,6 +264,24 @@ private final class FetchImpl { self.updatedFileReference = Data() #endif*/ + var isStory = false + if let info = parameters?.info as? TelegramCloudMediaResourceFetchInfo { + switch info.reference { + case let .media(media, _): + if case .story = media { + isStory = true + } + default: + break + } + } + + if isStory { + self.defaultPartSize = 512 * 1024 + } else { + self.defaultPartSize = 128 * 1024 + } + if let resource = resource as? TelegramCloudMediaResource { if let apiInputLocation = resource.apiInputLocation(fileReference: Data()) { self.loggingIdentifier = "\(apiInputLocation)" @@ -297,7 +316,7 @@ private final class FetchImpl { self.state = .fetching(FetchingState( fetchLocation: .datacenter(self.datacenterId), - partSize: 128 * 1024, + partSize: self.defaultPartSize, minPartSize: 4 * 1024, maxPartSize: 1 * 1024 * 1024, partAlignment: 4 * 1024, @@ -451,9 +470,9 @@ private final class FetchImpl { } self.state = .fetching(FetchImpl.FetchingState( fetchLocation: .cdn(cdnData), - partSize: 128 * 1024, + partSize: self.defaultPartSize, minPartSize: 4 * 1024, - maxPartSize: 128 * 1024, + maxPartSize: self.defaultPartSize, partAlignment: 4 * 1024, partDivision: 1 * 1024 * 1024, maxPendingParts: 6 @@ -499,9 +518,9 @@ private final class FetchImpl { self.state = .fetching(FetchingState( fetchLocation: fetchLocation, - partSize: 128 * 1024, + partSize: self.defaultPartSize, minPartSize: 4 * 1024, - maxPartSize: 128 * 1024, + maxPartSize: self.defaultPartSize, partAlignment: 4 * 1024, partDivision: 1 * 1024 * 1024, maxPendingParts: 6 @@ -686,9 +705,9 @@ private final class FetchImpl { case let .cdnRedirect(cdnData): self.state = .fetching(FetchImpl.FetchingState( fetchLocation: .cdn(cdnData), - partSize: 128 * 1024, + partSize: self.defaultPartSize, minPartSize: 4 * 1024, - maxPartSize: 128 * 1024, + maxPartSize: self.defaultPartSize, partAlignment: 4 * 1024, partDivision: 1 * 1024 * 1024, maxPendingParts: 6 diff --git a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift index 6fe352baf3..8c49845633 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift @@ -513,7 +513,23 @@ private final class MultipartFetchManager { self.useMainConnection = useMainConnection self.completeSize = size - if let size = size { + + var isStory = false + if let info = parameters?.info as? TelegramCloudMediaResourceFetchInfo { + switch info.reference { + case let .media(media, _): + if case .story = media { + isStory = true + } + default: + break + } + } + + if isStory { + self.defaultPartSize = 512 * 1024 + self.parallelParts = 4 + } else if let size = size { if size <= 512 * 1024 { self.defaultPartSize = 16 * 1024 self.parallelParts = 4 * 4 diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 4861eac853..f7ada43e54 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -466,17 +466,18 @@ public final class PeerStoryListContext { self.peerId = peerId self.isArchived = isArchived - self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0, isCached: true) + self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0, isCached: true, allEntityFiles: [:]) - let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], Int) in + let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], Int, [MediaId: TelegramMediaFile]) 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( @@ -504,19 +505,30 @@ 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)) + return (peerReference, items, Int(cached.totalCount), allEntityFiles) } - |> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, totalCount in + |> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, totalCount, allEntityFiles in guard let `self` = self else { return } - self.stateValue = State(peerReference: peerReference, items: items, totalCount: totalCount, loadMoreToken: 0, isCached: true) + self.stateValue = State(peerReference: peerReference, items: items, totalCount: totalCount, loadMoreToken: 0, isCached: true, allEntityFiles: allEntityFiles) self.loadMore() }) } @@ -828,19 +840,22 @@ 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 + isCached: Bool, + allEntityFiles: [MediaId: TelegramMediaFile] ) { self.peerReference = peerReference self.items = items self.totalCount = totalCount self.loadMoreToken = loadMoreToken self.isCached = isCached + self.allEntityFiles = allEntityFiles } } diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index 1d92f733b4..c7e9e5a477 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -410,7 +410,7 @@ public final class ChatListHeaderComponent: Component { } } - func updateNavigationTransitionAsNext(previousView: ContentView, fraction: CGFloat, transition: Transition, completion: @escaping () -> Void) { + func updateNavigationTransitionAsNext(previousView: ContentView, storyPeerListView: StoryPeerListComponent.View?, fraction: CGFloat, transition: Transition, completion: @escaping () -> Void) { transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -(1.0 - fraction) * self.bounds.width, y: 0.0), size: self.titleOffsetContainer.bounds.size), completion: { _ in completion() }) @@ -420,7 +420,15 @@ public final class ChatListHeaderComponent: Component { transition.setScale(view: backButtonView.arrowView, scale: pow(max(0.001, fraction), 2.0)) transition.setAlpha(view: backButtonView.arrowView, alpha: pow(fraction, 2.0)) - if let previousChatListTitleView = previousView.chatListTitleView { + if let storyPeerListView { + let previousTitleFrame = storyPeerListView.titleFrame() + let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) + + let totalOffset = previousTitleFrame.minX - backButtonTitleFrame.minX + + transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -totalOffset * (1.0 - fraction), y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) + } else if let previousChatListTitleView = previousView.chatListTitleView { let previousTitleFrame = previousChatListTitleView.titleNode.view.convert(previousChatListTitleView.titleNode.bounds, to: previousView.titleOffsetContainer) let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) @@ -993,7 +1001,7 @@ public final class ChatListHeaderComponent: Component { secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: 0.0, transition: .immediate, completion: {}) } else { primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: .immediate, completion: {}) - secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, fraction: 0.0, transition: .immediate, completion: {}) + secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: .immediate, completion: {}) } } @@ -1002,7 +1010,7 @@ public final class ChatListHeaderComponent: Component { secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) } else { primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) - secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) + secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: component.secondaryTransition, transition: transition, completion: {}) } } } else if let secondaryContentView = self.secondaryContentView { @@ -1016,7 +1024,7 @@ public final class ChatListHeaderComponent: Component { }) } else { primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: transition, completion: {}) - secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in + secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in secondaryContentView?.removeFromSuperview() }) } @@ -1025,7 +1033,7 @@ public final class ChatListHeaderComponent: Component { } } - if let storyPeerList = self.storyPeerList, let storyPeerListComponentView = storyPeerList.view { + if let storyPeerList = self.storyPeerList, let storyPeerListComponentView = storyPeerList.view as? StoryPeerListComponent.View { if storyPeerListComponentView.superview == nil { self.addSubview(storyPeerListComponentView) } @@ -1035,13 +1043,15 @@ public final class ChatListHeaderComponent: Component { //let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction - var defaultStoryListX: CGFloat = 0.0 - if let primaryContentView = self.primaryContentView { - defaultStoryListX = primaryContentView.centerContentOrigin - (self.storyPeerListExternalState.collapsedWidth * 0.5 + 12.0) - availableSize.width * 0.5 + var storiesX: CGFloat = 0.0 + if let nextBackButtonView = self.secondaryContentView?.backButtonView { + let backButtonTitleFrame = nextBackButtonView.convert(nextBackButtonView.titleView.frame, to: self) + let storyListTitleFrame = storyPeerListComponentView.titleFrame() + + storiesX += (backButtonTitleFrame.minX - storyListTitleFrame.minX) * component.secondaryTransition } - let _ = defaultStoryListX - storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: -1.0 * availableSize.width * component.secondaryTransition + 0.0, y: storyPeerListMaxOffset), size: CGSize(width: availableSize.width, height: 79.0))) + storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: storiesX, y: storyPeerListMaxOffset), size: CGSize(width: availableSize.width, height: 79.0))) let storyListNormalAlpha: CGFloat = 1.0 diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift index 9f8317ce2c..82925bfecd 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 79.0 + return 83.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 + 12.0 + headerContentY = component.statusBarHeight + 5.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 += 10.0 + contentHeight += 3.0 } contentHeight += 44.0 diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 2d9eef6762..df3282815f 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -2580,7 +2580,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) - let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: isMuted ? "The story will have no sound." : "The story will have sound."), location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in + let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: isMuted ? "The story will have no sound" : "The story will have sound"), location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in return .ignore }) self.muteTooltip = tooltipController diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index dcc296e21b..2a048f019a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -63,9 +63,11 @@ public final class StoryContentContextImpl: StoryContentContext { ), context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()) ) - |> mapToSignal { _, views, globalNotificationSettings -> Signal<(CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings), NoError> in - return context.account.postbox.transaction { transaction -> (CombinedView, [PeerId: Peer], EngineGlobalNotificationSettings) in + |> 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 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 { @@ -76,13 +78,24 @@ 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) + + return (views, peers, globalNotificationSettings, allEntityFiles) } } - |> deliverOnMainQueue).start(next: { [weak self] views, peers, globalNotificationSettings in + |> deliverOnMainQueue).start(next: { [weak self] views, peers, globalNotificationSettings, allEntityFiles in guard let self else { return } @@ -257,7 +270,8 @@ public final class StoryContentContextImpl: StoryContentContext { return StoryContentItem( position: nil, peerId: peer.id, - storyItem: item + storyItem: item, + entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles) ) } @@ -268,7 +282,8 @@ public final class StoryContentContextImpl: StoryContentContext { item: StoryContentItem( position: mappedFocusedIndex ?? focusedIndex, peerId: peer.id, - storyItem: mappedItem + storyItem: mappedItem, + entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) ), totalCount: totalCount, previousItemId: previousItemId, @@ -907,11 +922,12 @@ 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]) in + context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile]) 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 { @@ -920,8 +936,18 @@ 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) + return (item, peers, allEntityFiles) } ) |> deliverOnMainQueue).start(next: { [weak self] data, itemAndPeers in @@ -930,7 +956,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { } let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data - let (item, peers) = itemAndPeers + let (item, peers, allEntityFiles) = itemAndPeers guard let peer else { return @@ -981,7 +1007,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { let mainItem = StoryContentItem( position: 0, peerId: peer.id, - storyItem: mappedItem + storyItem: mappedItem, + entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) ) let stateValue = StoryContentContextState( slice: StoryContentContextState.FocusedSlice( @@ -1130,7 +1157,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { return StoryContentItem( position: nil, peerId: peer.id, - storyItem: stateItem + storyItem: stateItem, + entityFiles: extractItemEntityFiles(item: stateItem, allEntityFiles: state.allEntityFiles) ) } @@ -1141,7 +1169,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { item: StoryContentItem( position: nil, peerId: peer.id, - storyItem: item + storyItem: item, + entityFiles: extractItemEntityFiles(item: item, allEntityFiles: state.allEntityFiles) ), totalCount: state.totalCount, previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id, @@ -1326,3 +1355,16 @@ 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 42f41f0fb7..fbd495efd1 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -1005,7 +1005,7 @@ private final class StoryContainerScreenComponent: Component { itemSetContainerInsets.bottom = floorToScreenPixels((availableSize.height - itemSetContainerSize.height) / 2.0) itemSetContainerSafeInsets.bottom = 0.0 } - + let _ = itemSetView.view.update( transition: itemSetTransition, component: AnyComponent(StoryItemSetContainerComponent( @@ -1023,7 +1023,7 @@ private final class StoryContainerScreenComponent: Component { deviceMetrics: environment.deviceMetrics, isProgressPaused: isProgressPaused || i != focusedIndex, isAudioMuted: self.audioMode == .off || (self.audioMode == .ambient && !self.isMuteSwitchOn), - useAmbientMode: self.audioMode == .ambient, + audioMode: self.audioMode, hideUI: (i == focusedIndex && (self.itemSetPanState?.didBegin == false || self.itemSetPinchState != nil)), visibilityFraction: 1.0 - abs(panFraction + cubeAdditionalRotationFraction), isPanning: self.itemSetPanState?.didBegin == true, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index fdf0210e1e..f4f6add294 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -80,15 +80,18 @@ 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 + storyItem: EngineStoryItem, + entityFiles: [EngineMedia.Id: TelegramMediaFile] ) { self.position = position self.peerId = peerId self.storyItem = storyItem + self.entityFiles = entityFiles } public static func ==(lhs: StoryContentItem, rhs: StoryContentItem) -> Bool { @@ -101,6 +104,9 @@ 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 ce43120c3d..8623c43dd7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift @@ -43,6 +43,7 @@ final class StoryContentCaptionComponent: Component { let context: AccountContext let text: String let entities: [MessageTextEntity] + let entityFiles: [EngineMedia.Id: TelegramMediaFile] let action: (Action) -> Void let longTapAction: (Action) -> Void @@ -51,6 +52,7 @@ final class StoryContentCaptionComponent: Component { context: AccountContext, text: String, entities: [MessageTextEntity], + entityFiles: [EngineMedia.Id: TelegramMediaFile], action: @escaping (Action) -> Void, longTapAction: @escaping (Action) -> Void ) { @@ -58,6 +60,7 @@ final class StoryContentCaptionComponent: Component { self.context = context self.text = text self.entities = entities + self.entityFiles = entityFiles self.action = action self.longTapAction = longTapAction } @@ -75,6 +78,9 @@ final class StoryContentCaptionComponent: Component { if lhs.entities != rhs.entities { return false } + if lhs.entityFiles != rhs.entityFiles { + return false + } return true } @@ -221,7 +227,7 @@ final class StoryContentCaptionComponent: Component { let edgeDistanceFraction = edgeDistance / 7.0 transition.setAlpha(view: self.scrollFullMaskView, alpha: 1.0 - edgeDistanceFraction) - let shadowOverflow: CGFloat = 56.0 + let shadowOverflow: CGFloat = 58.0 let shadowFrame = CGRect(origin: CGPoint(x: 0.0, y: -self.scrollView.contentOffset.y + itemLayout.containerSize.height - itemLayout.visibleTextHeight - itemLayout.verticalInset - shadowOverflow), size: CGSize(width: itemLayout.containerSize.width, height: itemLayout.visibleTextHeight + itemLayout.verticalInset + shadowOverflow)) transition.setFrame(layer: self.shadowGradientLayer, frame: shadowFrame) transition.setFrame(layer: self.shadowPlainLayer, frame: CGRect(origin: CGPoint(x: shadowFrame.minX, y: shadowFrame.maxY), size: CGSize(width: shadowFrame.width, height: self.scrollView.contentSize.height + 1000.0))) @@ -365,7 +371,8 @@ final class StoryContentCaptionComponent: Component { boldItalicFont: Font.semiboldItalic(16.0), fixedFont: Font.monospace(16.0), blockQuoteFont: Font.monospace(16.0), - message: nil + message: nil, + entityFiles: component.entityFiles ) let makeLayout = TextNodeWithEntities.asyncLayout(self.textNode) @@ -485,11 +492,11 @@ final class StoryContentCaptionComponent: Component { var locations: [NSNumber] = [] var colors: [CGColor] = [] let numStops = 10 - let baseAlpha: CGFloat = 0.5 + let baseAlpha: CGFloat = 0.6 for i in 0 ..< numStops { let step = 1.0 - CGFloat(i) / CGFloat(numStops - 1) locations.append((1.0 - step) as NSNumber) - let alphaStep: CGFloat = pow(step, 1.2) + let alphaStep: CGFloat = pow(step, 1.0) colors.append(UIColor.black.withAlphaComponent(alphaStep * baseAlpha).cgColor) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 5fae3ded54..2fca5db839 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -29,13 +29,13 @@ final class StoryItemContentComponent: Component { let context: AccountContext let peer: EnginePeer let item: EngineStoryItem - let useAmbientMode: Bool + let audioMode: StoryContentItem.AudioMode - init(context: AccountContext, peer: EnginePeer, item: EngineStoryItem, useAmbientMode: Bool) { + init(context: AccountContext, peer: EnginePeer, item: EngineStoryItem, audioMode: StoryContentItem.AudioMode) { self.context = context self.peer = peer self.item = item - self.useAmbientMode = useAmbientMode + self.audioMode = audioMode } static func ==(lhs: StoryItemContentComponent, rhs: StoryItemContentComponent) -> Bool { @@ -138,8 +138,8 @@ final class StoryItemContentComponent: Component { imageReference: nil, streamVideo: .story, loopVideo: true, - enableSound: true, - beginWithAmbientSound: component.useAmbientMode, + enableSound: component.audioMode != .off, + beginWithAmbientSound: component.audioMode == .ambient, mixWithOthers: true, useLargeThumbnail: false, autoFetchFullSizeThumbnail: false, @@ -169,12 +169,16 @@ final class StoryItemContentComponent: Component { self.environment?.presentationProgressUpdated(1.0, true) } videoNode.ownsContentNodeUpdated = { [weak self] value in - guard let self else { + guard let self, let component = self.component else { return } if value { self.videoNode?.seek(0.0) - self.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop) + if component.audioMode != .off { + self.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: .stop) + } else { + self.videoNode?.play() + } } } videoNode.canAttachContent = true diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index f2543f275a..3ecc876e07 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -86,7 +86,7 @@ public final class StoryItemSetContainerComponent: Component { public let deviceMetrics: DeviceMetrics public let isProgressPaused: Bool public let isAudioMuted: Bool - public let useAmbientMode: Bool + public let audioMode: StoryContentItem.AudioMode public let hideUI: Bool public let visibilityFraction: CGFloat public let isPanning: Bool @@ -118,7 +118,7 @@ public final class StoryItemSetContainerComponent: Component { deviceMetrics: DeviceMetrics, isProgressPaused: Bool, isAudioMuted: Bool, - useAmbientMode: Bool, + audioMode: StoryContentItem.AudioMode, hideUI: Bool, visibilityFraction: CGFloat, isPanning: Bool, @@ -149,7 +149,7 @@ public final class StoryItemSetContainerComponent: Component { self.deviceMetrics = deviceMetrics self.isProgressPaused = isProgressPaused self.isAudioMuted = isAudioMuted - self.useAmbientMode = useAmbientMode + self.audioMode = audioMode self.hideUI = hideUI self.visibilityFraction = visibilityFraction self.isPanning = isPanning @@ -201,7 +201,7 @@ public final class StoryItemSetContainerComponent: Component { if lhs.isAudioMuted != rhs.isAudioMuted { return false } - if lhs.useAmbientMode != rhs.useAmbientMode { + if lhs.audioMode != rhs.audioMode { return false } if lhs.hideUI != rhs.hideUI { @@ -1058,7 +1058,7 @@ public final class StoryItemSetContainerComponent: Component { context: component.context, peer: component.slice.peer, item: item.storyItem, - useAmbientMode: component.useAmbientMode + audioMode: component.audioMode )), environment: { itemEnvironment @@ -1379,6 +1379,9 @@ public final class StoryItemSetContainerComponent: Component { if let closeFriendIconView = self.closeFriendIcon?.view { closeFriendIconView.layer.animateAlpha(from: closeFriendIconView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false) } + if let captionView = self.captionItem?.view.view { + captionView.layer.animateAlpha(from: captionView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false) + } self.closeButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false) self.topContentGradientLayer.animateAlpha(from: CGFloat(self.topContentGradientLayer.opacity), to: 0.0, duration: 0.25, removeOnCompletion: false) @@ -2332,7 +2335,7 @@ public final class StoryItemSetContainerComponent: Component { let tooltipScreen = TooltipScreen( account: component.context.account, sharedContext: component.context.sharedContext, - text: .plain(text: tooltipText), style: .default, location: TooltipScreen.Location.point(closeFriendIconView.convert(closeFriendIconView.bounds, to: self).offsetBy(dx: 1.0, dy: 6.0), .top), displayDuration: .manual, shouldDismissOnTouch: { _, _ in + text: .plain(text: tooltipText), style: .default, location: TooltipScreen.Location.point(closeFriendIconView.convert(closeFriendIconView.bounds, to: self).offsetBy(dx: 1.0, dy: 6.0), .top), displayDuration: .infinite, shouldDismissOnTouch: { _, _ in return .dismiss(consume: true) } ) @@ -2527,6 +2530,7 @@ 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 @@ -2676,7 +2680,7 @@ public final class StoryItemSetContainerComponent: Component { reactionContextNode.displayTail = false self.reactionContextNode = reactionContextNode - reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in + reactionContextNode.reactionSelected = { [weak self, weak reactionContextNode] updateReaction, _ in guard let self, let component = self.component else { return } @@ -2700,23 +2704,24 @@ public final class StoryItemSetContainerComponent: Component { targetView.isUserInteractionEnabled = false self.addSubview(targetView) - 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 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() + }) + } + }) + } if hasFirstResponder(self) { self.sendMessageContext.currentInputMode = .text diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 580c9528cf..6f8155058e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -466,6 +466,10 @@ public final class StoryPeerListComponent: Component { return (self.collapsedButton, self.collapsedButton.bounds) } + public func titleFrame() -> CGRect { + return self.titleView.frame + } + public func transitionViewForItem(peerId: EnginePeer.Id) -> (UIView, StoryContainerScreen.TransitionView)? { if self.collapsedButton.isUserInteractionEnabled { return nil diff --git a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift index 47141b8e51..7114f82d76 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 = [component.theme.chatList.storySeenColors.topColor.argb, component.theme.chatList.storySeenColors.bottomColor.argb] + borderColors = [UIColor(white: 1.0, alpha: 0.3).argb, UIColor(white: 1.0, alpha: 0.3).argb] } let imageSize = CGSize(width: maxItemsWidth, height: outerDiameter) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift index 7d2c2ea41c..dc5858ffd2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift @@ -254,6 +254,10 @@ 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 89893bbeab..c5e210744b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -4589,6 +4589,11 @@ 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 4f9bfd4deb..a236a18bac 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -429,6 +429,10 @@ 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 7917a3d28f..93b6519861 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -431,6 +431,8 @@ 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? @@ -467,28 +469,34 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } - if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { + if let replyAttribute = attribute as? ReplyMessageAttribute { if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { } else { - 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 - )) + replyMessage = item.message.associatedMessages[replyAttribute.messageId] } + } 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 c6f3efe3db..b7ae99114b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -316,6 +316,9 @@ 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? @@ -338,23 +341,32 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { + if let replyAttribute = attribute as? ReplyMessageAttribute { if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { } else { - 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 - )) + replyMessage = item.message.associatedMessages[replyAttribute.messageId] } + } 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 + )) } } } @@ -1259,6 +1271,9 @@ 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 } } } @@ -1815,4 +1830,20 @@ 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/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 7e9318070a..60f9279f37 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -188,7 +188,9 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { case let .bubble(incoming): titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor lineImage = incoming ? (authorNameColor.flatMap({ PresentationResourcesChat.chatBubbleVerticalLineImage(color: $0) }) ?? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(arguments.presentationData.theme.theme)) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(arguments.presentationData.theme.theme) - if isMedia || isExpiredStory { + if isExpiredStory { + textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor + } else if isMedia { textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor } else { textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.primaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.primaryTextColor diff --git a/submodules/TextFormat/Sources/StringWithAppliedEntities.swift b/submodules/TextFormat/Sources/StringWithAppliedEntities.swift index ec6f8826a0..a8cf179e0e 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?) -> 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?, entityFiles: [MediaId: TelegramMediaFile] = [:]) -> NSAttributedString { var nsString: NSString? let string = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: baseFont, NSAttributedString.Key.foregroundColor: baseColor]) var skipEntity = false @@ -252,7 +252,14 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti } } case let .CustomEmoji(_, fileId): - string.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: fileId, file: message?.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile), range: range) + 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) default: break }