diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 5415ebf1aa..fec0eab8b7 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -732,12 +732,12 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: }))) } - items.append(.separator) - items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Select, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { _, f in - f(.default) - - - }))) +// items.append(.separator) +// items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Select, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { _, f in +// f(.default) +// +// +// }))) return .single(items) } diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index e77b9eea85..3954c7131c 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -371,32 +371,42 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } func transitionToPeerTopics(_ peer: EngineRenderedPeer) { - guard let context = self.context, let mainPeer = peer.chatMainPeer else { + guard let context = self.context, let mainPeer = peer.chatMainPeer, let controllerInteraction = self.controllerInteraction else { return } - let _ = (threadList(context: context, peerId: mainPeer.id) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] threads in - guard let strongSelf = self, let context = strongSelf.context, let controllerInteraction = strongSelf.controllerInteraction else { + var didPresent = false + var presentImpl: (() -> Void)? + let threads = threadList(context: context, peerId: mainPeer.id) + |> deliverOnMainQueue + |> beforeNext { _ in + if !didPresent { + didPresent = true + presentImpl?() + } + } + + let topicsContentNode = ShareTopicsContainerNode( + sharedContext: self.sharedContext, + context: context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + peer: mainPeer, + topics: threads, + controllerInteraction: controllerInteraction + ) + topicsContentNode.backPressed = { [weak self] in + if let strongSelf = self { + strongSelf.closePeerTopics(peer.peerId, selected: false) + } + } + self.topicsContentNode = topicsContentNode + + presentImpl = { [weak self] in + guard let strongSelf = self else { return } - let topicsContentNode = ShareTopicsContainerNode( - sharedContext: strongSelf.sharedContext, - context: context, - theme: strongSelf.presentationData.theme, - strings: strongSelf.presentationData.strings, - peer: mainPeer, - topics: threads.items.reversed(), - controllerInteraction: controllerInteraction - ) - topicsContentNode.backPressed = { [weak self] in - if let strongSelf = self { - strongSelf.closePeerTopics(peer.peerId, selected: false) - } - } - strongSelf.topicsContentNode = topicsContentNode strongSelf.contentNode?.supernode?.addSubnode(topicsContentNode) if let (layout, navigationBarHeight, _) = strongSelf.containerLayout { @@ -423,7 +433,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate strongSelf.contentNodeOffsetUpdated(topicsContentNode.contentGridNode.scrollView.contentOffset.y, transition: .animated(duration: 0.4, curve: .spring)) strongSelf.view.endEditing(true) - }) + } } func closePeerTopics(_ peerId: EnginePeer.Id, selected: Bool) { @@ -1259,6 +1269,16 @@ private func threadList(context: AccountContext, peerId: EnginePeer.Id) -> Signa ) return context.account.postbox.combinedView(keys: [viewKey]) + |> mapToSignal { view -> Signal in + return context.account.postbox.transaction { transaction -> CombinedView in + if let peer = transaction.getPeer(context.account.peerId) { + transaction.updatePeersInternal([peer]) { current, _ in + return current ?? peer + } + } + return view + } + } |> map { views -> EngineChatList in guard let view = views.views[viewKey] as? MessageHistoryThreadIndexView else { preconditionFailure() @@ -1299,7 +1319,7 @@ private func threadList(context: AccountContext, peerId: EnginePeer.Id) -> Signa } let list = EngineChatList( - items: items.reversed(), + items: items, groupItems: [], additionalItems: [], hasEarlier: false, diff --git a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift index eebaac0dc3..a417ebb502 100644 --- a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift +++ b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift @@ -176,20 +176,23 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { private var validLayout: (CGSize, CGFloat)? private var overrideGridOffsetTransition: ContainedViewLayoutTransition? - let peersValue = Promise<[EngineChatList.Item]>() + let topicsValue = Promise<[EngineChatList.Item]>() var backPressed: () -> Void = {} - init(sharedContext: SharedAccountContext, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, topics: [EngineChatList.Item], controllerInteraction: ShareControllerInteraction) { + init(sharedContext: SharedAccountContext, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, topics: Signal, controllerInteraction: ShareControllerInteraction) { self.sharedContext = sharedContext self.context = context self.theme = theme self.strings = strings self.controllerInteraction = controllerInteraction - self.peersValue.set(.single(topics)) + self.topicsValue.set(topics + |> map { + return $0.items + }) - let items: Signal<[ShareTopicEntry], NoError> = self.peersValue.get() + let items: Signal<[ShareTopicEntry], NoError> = self.topicsValue.get() |> map { topics -> [ShareTopicEntry] in var entries: [ShareTopicEntry] = [] var index: Int32 = 0 diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index edae928325..98d5af0c50 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1394,6 +1394,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { isSelectionEnabled = false } else if case .pinnedMessages = self.chatPresentationInterfaceState.subject { isSelectionEnabled = false + } else if case .forum = self.chatLocation { + isSelectionEnabled = false } self.historyNode.isSelectionGestureEnabled = isSelectionEnabled diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index d4ac151ef2..26c6eb62b3 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -411,6 +411,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private let context: AccountContext private let chatLocation: ChatLocation private let chatLocationContextHolder: Atomic + private let source: ChatHistoryListSource private let subject: ChatControllerSubject? private let tagMask: MessageTags? private let controllerInteraction: ChatControllerInteraction @@ -556,6 +557,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } + private let justSentTextMessagePromise = ValuePromise(false) + var justSentTextMessage: Bool = false { + didSet { + if self.justSentTextMessage != oldValue { + self.justSentTextMessagePromise.set(self.justSentTextMessage) + } + } + } + private var appliedScrollToMessageId: MessageIndex? = nil private let scrollToMessageIdPromise = Promise(nil) @@ -619,6 +629,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.context = context self.chatLocation = chatLocation self.chatLocationContextHolder = chatLocationContextHolder + self.source = source self.subject = subject self.tagMask = tagMask self.controllerInteraction = controllerInteraction @@ -1044,10 +1055,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { topicAuthorId = .single(nil) } - let suggestAudioTranscription = context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.audioTranscriptionSuggestionKey())) - |> map { entry -> Int32 in - return entry?.get(ApplicationSpecificCounterNotice.self)?.value ?? 0 - } + let audioTranscriptionSuggestion = combineLatest( + ApplicationSpecificNotice.getAudioTranscriptionSuggestion(accountManager: context.sharedContext.accountManager), + self.justSentTextMessagePromise.get() + ) let promises = combineLatest( self.historyAppearsClearedPromise.get(), @@ -1071,7 +1082,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { availableReactions, defaultReaction, accountPeer, - suggestAudioTranscription, + audioTranscriptionSuggestion, promises, topicAuthorId ).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, adMessages, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId in @@ -1209,7 +1220,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { isPremium = true } - let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: suggestAudioTranscription < 2, accountPeer: accountPeer, topicAuthorId: topicAuthorId) + let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: suggestAudioTranscription.0 < 2, accountPeer: accountPeer, topicAuthorId: topicAuthorId) let filteredEntries = chatHistoryEntriesForView( location: chatLocation, @@ -2551,7 +2562,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { strongSelf.historyView = transition.historyView let loadState: ChatHistoryNodeLoadState - if let historyView = strongSelf.historyView { + if case .custom = strongSelf.source { + loadState = .messages + } else if let historyView = strongSelf.historyView { if historyView.filteredEntries.isEmpty { if let firstEntry = historyView.originalView.entries.first { var emptyType = ChatHistoryNodeLoadState.EmptyType.generic diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 7fa53b66aa..bfd50b14af 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -542,45 +542,32 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false, isDownloadList: false) } + + var isAudio = false + var audioWaveform: AudioWaveform? + var isVoice = false + var audioDuration: Int32 = 0 + var isConsumed: Bool? var consumableContentIcon: UIImage? for attribute in arguments.message.attributes { if let attribute = attribute as? ConsumableContentMessageAttribute { - let isConsumed = attribute.consumed - if !isConsumed { + if !attribute.consumed { if arguments.incoming { consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentIncomingIcon(arguments.presentationData.theme.theme) } else { consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentOutgoingIcon(arguments.presentationData.theme.theme) } } + isConsumed = attribute.consumed break } } - + var candidateTitleString: NSAttributedString? var candidateDescriptionString: NSAttributedString? - var isAudio = false - var audioWaveform: AudioWaveform? - var isVoice = false - var audioDuration: Int32 = 0 - - let displayTranscribe: Bool - if arguments.message.id.peerId.namespace != Namespaces.Peer.SecretChat { - if arguments.associatedData.isPremium { - displayTranscribe = true - } else if arguments.associatedData.alwaysDisplayTranscribeButton { - displayTranscribe = true - } else { - displayTranscribe = false - } - } else { - displayTranscribe = false - } - let messageTheme = arguments.incoming ? arguments.presentationData.theme.theme.chat.message.incoming : arguments.presentationData.theme.theme.chat.message.outgoing - let isInstantVideo = arguments.file.isInstantVideo for attribute in arguments.file.attributes { if case let .Video(videoDuration, _, flags) = attribute, flags.contains(.instantRoundVideo) { @@ -635,7 +622,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } } } - + var titleString: NSAttributedString? var descriptionString: NSAttributedString? @@ -680,6 +667,25 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { var textString: NSAttributedString? var updatedAudioTranscriptionState: AudioTranscriptionButtonComponent.TranscriptionState? + let displayTranscribe: Bool + if arguments.message.id.peerId.namespace != Namespaces.Peer.SecretChat { + if arguments.associatedData.isPremium { + displayTranscribe = true + } else if arguments.associatedData.alwaysDisplayTranscribeButton { + if audioDuration >= 60 { + displayTranscribe = true + } else if isConsumed == false { + displayTranscribe = true + } else { + displayTranscribe = false + } + } else { + displayTranscribe = false + } + } else { + displayTranscribe = false + } + let transcribedText = forcedAudioTranscriptionText ?? transcribedText(message: arguments.message) switch audioTranscriptionState { @@ -1180,6 +1186,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { animation.animator.updateFrame(layer: waveformView.componentView!.layer, frame: CGRect(origin: CGPoint(), size: scrubbingFrame.size), completion: nil) if displayTranscribe { + var added = false let audioTranscriptionButton: ComponentHostView if let current = strongSelf.audioTranscriptionButton { audioTranscriptionButton = current @@ -1187,6 +1194,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { audioTranscriptionButton = ComponentHostView() strongSelf.audioTranscriptionButton = audioTranscriptionButton strongSelf.view.addSubview(audioTranscriptionButton) + added = true } let audioTranscriptionButtonSize = audioTranscriptionButton.update( transition: animation.isAnimated ? .easeInOut(duration: 0.3) : .immediate, @@ -1203,7 +1211,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { environment: {}, containerSize: CGSize(width: 30.0, height: 30.0) ) - animation.animator.updateFrame(layer: audioTranscriptionButton.layer, frame: CGRect(origin: CGPoint(x: boundingWidth - 30.0 + 3.0, y: -6.0), size: audioTranscriptionButtonSize), completion: nil) + + let audioTranscriptionButtonFrame = CGRect(origin: CGPoint(x: boundingWidth - 30.0 + 3.0, y: -6.0), size: audioTranscriptionButtonSize) + if added { + audioTranscriptionButton.layer.frame = audioTranscriptionButtonFrame + audioTranscriptionButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } else { + animation.animator.updateFrame(layer: audioTranscriptionButton.layer, frame: audioTranscriptionButtonFrame, completion: nil) + } } else { if let audioTranscriptionButton = strongSelf.audioTranscriptionButton { strongSelf.audioTranscriptionButton = nil diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index e2c4688367..0181e13718 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -600,6 +600,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } if displayTranscribe, let durationBlurColor = durationBlurColor { + var added = false let audioTranscriptionButton: ComponentHostView if let current = strongSelf.audioTranscriptionButton { audioTranscriptionButton = current @@ -607,6 +608,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { audioTranscriptionButton = ComponentHostView() strongSelf.audioTranscriptionButton = audioTranscriptionButton strongSelf.view.addSubview(audioTranscriptionButton) + added = true } let audioTranscriptionButtonSize = audioTranscriptionButton.update( transition: animation.isAnimated ? .easeInOut(duration: 0.3) : .immediate, @@ -637,8 +639,12 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - animation.animator.updateFrame(layer: audioTranscriptionButton.layer, frame: audioTranscriptionButtonFrame, completion: nil) - + if animation.isAnimated && added { + audioTranscriptionButton.layer.frame = audioTranscriptionButtonFrame + audioTranscriptionButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } else { + animation.animator.updateFrame(layer: audioTranscriptionButton.layer, frame: audioTranscriptionButtonFrame, completion: nil) + } animation.animator.updateAlpha(layer: audioTranscriptionButton.layer, alpha: scaleProgress.isZero ? 1.0 : 0.0, completion: nil) if !scaleProgress.isZero { displayTranscribe = false