mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Refactoring
This commit is contained in:
parent
3b49480ef3
commit
5226d2bb15
@ -0,0 +1,516 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import ChatPresentationInterfaceState
|
||||
import ChatInterfaceState
|
||||
import TelegramNotices
|
||||
import PresentationDataUtils
|
||||
import TelegramCallsUI
|
||||
import AttachmentUI
|
||||
|
||||
func updateChatPresentationInterfaceStateImpl(
|
||||
selfController: ChatControllerImpl,
|
||||
transition: ContainedViewLayoutTransition,
|
||||
interactive: Bool,
|
||||
saveInterfaceState: Bool,
|
||||
_ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState,
|
||||
completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void
|
||||
) {
|
||||
var completion = externalCompletion
|
||||
var temporaryChatPresentationInterfaceState = f(selfController.presentationInterfaceState)
|
||||
|
||||
if selfController.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup || selfController.presentationInterfaceState.keyboardButtonsMessage?.id != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.id {
|
||||
if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, let keyboardMarkup = keyboardButtonsMessage.visibleButtonKeyboardMarkup {
|
||||
if selfController.presentationInterfaceState.interfaceState.editMessage == nil && selfController.presentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.closedButtonKeyboardMessageId && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId && temporaryChatPresentationInterfaceState.botStartPayload == nil {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ _ in
|
||||
return .inputButtons(persistent: keyboardMarkup.flags.contains(.persistent))
|
||||
})
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({
|
||||
$0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
|
||||
messageId: keyboardButtonsMessage.id,
|
||||
quote: nil
|
||||
)).withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.processedSetupReplyMessageId = keyboardButtonsMessage.id
|
||||
return value
|
||||
}) })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ mode in
|
||||
if case .inputButtons = mode {
|
||||
return .text
|
||||
} else {
|
||||
return mode
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, keyboardButtonsMessage.requestsSetupReply {
|
||||
if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
|
||||
messageId: keyboardButtonsMessage.id,
|
||||
quote: nil
|
||||
)).withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.processedSetupReplyMessageId = keyboardButtonsMessage.id
|
||||
return value
|
||||
}) })
|
||||
}
|
||||
}
|
||||
|
||||
let inputTextPanelState = inputTextPanelStateForChatPresentationInterfaceState(temporaryChatPresentationInterfaceState, context: selfController.context)
|
||||
var updatedChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputTextPanelState({ _ in return inputTextPanelState })
|
||||
|
||||
let contextQueryUpdates = contextQueryResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: selfController.context, currentQueryStates: &selfController.contextQueryStates, requestBotLocationStatus: { [weak selfController] peerId in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
let _ = (ApplicationSpecificNotice.updateInlineBotLocationRequestState(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, timestamp: Int32(Date().timeIntervalSince1970 + 10 * 60))
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak selfController] value in
|
||||
guard let selfController, value else {
|
||||
return
|
||||
}
|
||||
selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Cancel, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
|
||||
})]), in: .window(.root))
|
||||
})
|
||||
})
|
||||
|
||||
for (kind, update) in contextQueryUpdates {
|
||||
switch update {
|
||||
case .remove:
|
||||
if let (_, disposable) = selfController.contextQueryStates[kind] {
|
||||
disposable.dispose()
|
||||
selfController.contextQueryStates.removeValue(forKey: kind)
|
||||
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if case .contextRequest = kind {
|
||||
selfController.performingInlineSearch.set(false)
|
||||
}
|
||||
case let .update(query, signal):
|
||||
let currentQueryAndDisposable = selfController.contextQueryStates[kind]
|
||||
currentQueryAndDisposable?.1.dispose()
|
||||
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
selfController.contextQueryStates[kind] = (query, (signal
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return result(previousResult)
|
||||
})
|
||||
})
|
||||
}
|
||||
}, error: { [weak selfController] error in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if case .contextRequest = kind {
|
||||
selfController.performingInlineSearch.set(false)
|
||||
}
|
||||
|
||||
switch error {
|
||||
case .generic:
|
||||
break
|
||||
case let .inlineBotLocationRequest(peerId):
|
||||
selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Cancel, action: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).startStandalone()
|
||||
}), TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
}, completed: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if case .contextRequest = kind {
|
||||
selfController.performingInlineSearch.set(false)
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
})
|
||||
} else {
|
||||
if case .contextRequest = kind {
|
||||
selfController.performingInlineSearch.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
if case .contextRequest = query {
|
||||
let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: selfController.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak selfController] value in
|
||||
guard let selfController, !value else {
|
||||
return
|
||||
}
|
||||
let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: selfController.context.sharedContext.accountManager).startStandalone()
|
||||
selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isBot = false
|
||||
if let peer = updatedChatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil {
|
||||
isBot = true
|
||||
} else {
|
||||
isBot = false
|
||||
}
|
||||
selfController.chatDisplayNode.historyNode.chatHasBots = updatedChatPresentationInterfaceState.hasBots || isBot
|
||||
|
||||
if let (updatedSearchQuerySuggestionState, updatedSearchQuerySuggestionSignal) = searchQuerySuggestionResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: selfController.context, currentQuery: selfController.searchQuerySuggestionState?.0) {
|
||||
selfController.searchQuerySuggestionState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
selfController.searchQuerySuggestionState = (updatedSearchQuerySuggestionState, (updatedSearchQuerySuggestionSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedSearchQuerySuggestionResult { previousResult in
|
||||
return result(previousResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedSearchQuerySuggestionResult { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (updatedUrlPreviewUrl, updatedUrlPreviewSignal) = urlPreviewStateForInputText(updatedChatPresentationInterfaceState.interfaceState.composeInputState.inputText, context: selfController.context, currentQuery: selfController.urlPreviewQueryState?.0) {
|
||||
selfController.urlPreviewQueryState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((TelegramMediaWebpage?) -> TelegramMediaWebpage?)?
|
||||
let linkPreviews: Signal<Bool, NoError>
|
||||
if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
linkPreviews = interactiveChatLinkPreviewsEnabled(accountManager: selfController.context.sharedContext.accountManager, displayAlert: { [weak selfController] f in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_SecretLinkPreviewAlert, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Yes, action: {
|
||||
f.f(true)
|
||||
}), TextAlertAction(type: .genericAction, title: selfController.presentationData.strings.Common_No, action: {
|
||||
f.f(false)
|
||||
})]), in: .window(.root))
|
||||
})
|
||||
} else {
|
||||
var bannedEmbedLinks = false
|
||||
if let channel = selfController.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.hasBannedPermission(.banEmbedLinks) != nil {
|
||||
bannedEmbedLinks = true
|
||||
} else if let group = selfController.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup, group.hasBannedPermission(.banEmbedLinks) {
|
||||
bannedEmbedLinks = true
|
||||
}
|
||||
if bannedEmbedLinks {
|
||||
linkPreviews = .single(false)
|
||||
} else {
|
||||
linkPreviews = .single(true)
|
||||
}
|
||||
}
|
||||
let filteredPreviewSignal = linkPreviews
|
||||
|> take(1)
|
||||
|> mapToSignal { value -> Signal<(TelegramMediaWebpage?) -> TelegramMediaWebpage?, NoError> in
|
||||
if value {
|
||||
return updatedUrlPreviewSignal
|
||||
} else {
|
||||
return .single({ _ in return nil })
|
||||
}
|
||||
}
|
||||
|
||||
selfController.urlPreviewQueryState = (updatedUrlPreviewUrl, (filteredPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] (result) in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = result($0.urlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: $0.urlPreview?.positionBelowText ?? true,
|
||||
largeMedia: $0.urlPreview?.largeMedia
|
||||
)
|
||||
return $0.updatedUrlPreview(updatedPreview)
|
||||
} else {
|
||||
return $0.updatedUrlPreview(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = inScopeResult(updatedChatPresentationInterfaceState.urlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: updatedChatPresentationInterfaceState.urlPreview?.positionBelowText ?? true,
|
||||
largeMedia: updatedChatPresentationInterfaceState.urlPreview?.largeMedia
|
||||
)
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(updatedPreview)
|
||||
} else {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext
|
||||
let editingUrlPreviewText: NSAttributedString? = isEditingMedia ? nil : updatedChatPresentationInterfaceState.interfaceState.editMessage?.inputState.inputText
|
||||
if let (updatedEditingUrlPreviewUrl, updatedEditingUrlPreviewSignal) = urlPreviewStateForInputText(editingUrlPreviewText, context: selfController.context, currentQuery: selfController.editingUrlPreviewQueryState?.0) {
|
||||
selfController.editingUrlPreviewQueryState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((TelegramMediaWebpage?) -> TelegramMediaWebpage?)?
|
||||
selfController.editingUrlPreviewQueryState = (updatedEditingUrlPreviewUrl, (updatedEditingUrlPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
if let updatedEditingUrlPreviewUrl = updatedEditingUrlPreviewUrl, let webpage = result($0.editingUrlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedEditingUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: $0.editingUrlPreview?.positionBelowText ?? true,
|
||||
largeMedia: $0.editingUrlPreview?.largeMedia
|
||||
)
|
||||
return $0.updatedEditingUrlPreview(updatedPreview)
|
||||
} else {
|
||||
return $0.updatedEditingUrlPreview(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
if let updatedEditingUrlPreviewUrl = updatedEditingUrlPreviewUrl, let webpage = inScopeResult(updatedChatPresentationInterfaceState.editingUrlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedEditingUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: updatedChatPresentationInterfaceState.editingUrlPreview?.positionBelowText ?? true,
|
||||
largeMedia: updatedChatPresentationInterfaceState.editingUrlPreview?.largeMedia
|
||||
)
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(updatedPreview)
|
||||
} else {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let replyMessageId = updatedChatPresentationInterfaceState.interfaceState.replyMessageSubject?.messageId {
|
||||
if selfController.replyMessageState?.0 != replyMessageId {
|
||||
selfController.replyMessageState?.1.dispose()
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
|
||||
let disposable = MetaDisposable()
|
||||
selfController.replyMessageState = (replyMessageId, disposable)
|
||||
disposable.set((selfController.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: replyMessageId))
|
||||
|> deliverOnMainQueue).start(next: { [weak selfController] message in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
if message != selfController.presentationInterfaceState.replyMessage.flatMap(EngineMessage.init) {
|
||||
selfController.updateChatPresentationInterfaceState(interactive: false, { presentationInterfaceState in
|
||||
return presentationInterfaceState.updatedReplyMessage(message?._asMessage())
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
if let replyMessageState = selfController.replyMessageState {
|
||||
selfController.replyMessageState = nil
|
||||
replyMessageState.1.dispose()
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let updated = selfController.updateSearch(updatedChatPresentationInterfaceState) {
|
||||
updatedChatPresentationInterfaceState = updated
|
||||
}
|
||||
|
||||
let recordingActivityValue: ChatRecordingActivity
|
||||
if let mediaRecordingState = updatedChatPresentationInterfaceState.inputTextPanelState.mediaRecordingState {
|
||||
switch mediaRecordingState {
|
||||
case .audio:
|
||||
recordingActivityValue = .voice
|
||||
case .video(ChatVideoRecordingStatus.recording, _):
|
||||
recordingActivityValue = .instantVideo
|
||||
default:
|
||||
recordingActivityValue = .none
|
||||
}
|
||||
} else {
|
||||
recordingActivityValue = .none
|
||||
}
|
||||
if recordingActivityValue != selfController.recordingActivityValue {
|
||||
selfController.recordingActivityValue = recordingActivityValue
|
||||
selfController.recordingActivityPromise.set(recordingActivityValue)
|
||||
}
|
||||
|
||||
if (selfController.presentationInterfaceState.interfaceState.selectionState == nil) != (updatedChatPresentationInterfaceState.interfaceState.selectionState == nil) {
|
||||
selfController.isSelectingMessagesUpdated?(updatedChatPresentationInterfaceState.interfaceState.selectionState != nil)
|
||||
selfController.updateNextChannelToReadVisibility()
|
||||
}
|
||||
|
||||
selfController.presentationInterfaceState = updatedChatPresentationInterfaceState
|
||||
|
||||
selfController.updateSlowmodeStatus()
|
||||
|
||||
switch updatedChatPresentationInterfaceState.inputMode {
|
||||
case .media:
|
||||
break
|
||||
default:
|
||||
selfController.chatDisplayNode.collapseInput()
|
||||
}
|
||||
|
||||
if selfController.isNodeLoaded {
|
||||
selfController.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion)
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
|
||||
let updatedServiceTasks = serviceTasksForChatPresentationIntefaceState(context: selfController.context, chatPresentationInterfaceState: updatedChatPresentationInterfaceState, updateState: { [weak selfController] f in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: false, f)
|
||||
|
||||
//selfController.chatDisplayNode.updateChatPresentationInterfaceState(f(selfController.chatDisplayNode.chatPresentationInterfaceState), transition: transition, interactive: false, completion: { _ in })
|
||||
})
|
||||
for (id, begin) in updatedServiceTasks {
|
||||
if selfController.stateServiceTasks[id] == nil {
|
||||
selfController.stateServiceTasks[id] = begin()
|
||||
}
|
||||
}
|
||||
var removedServiceTaskIds: [AnyHashable] = []
|
||||
for (id, _) in selfController.stateServiceTasks {
|
||||
if updatedServiceTasks[id] == nil {
|
||||
removedServiceTaskIds.append(id)
|
||||
}
|
||||
}
|
||||
for id in removedServiceTaskIds {
|
||||
selfController.stateServiceTasks.removeValue(forKey: id)?.dispose()
|
||||
}
|
||||
|
||||
if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, subject: selfController.subject, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.leftNavigationButton, target: selfController, selector: #selector(selfController.leftNavigationButtonAction)) {
|
||||
if selfController.leftNavigationButton != button {
|
||||
var animated = transition.isAnimated
|
||||
if let currentButton = selfController.leftNavigationButton?.action, currentButton == button.action {
|
||||
animated = false
|
||||
}
|
||||
animated = false
|
||||
selfController.navigationItem.setLeftBarButton(button.buttonItem, animated: animated)
|
||||
selfController.leftNavigationButton = button
|
||||
}
|
||||
} else if let _ = selfController.leftNavigationButton {
|
||||
selfController.navigationItem.setLeftBarButton(nil, animated: transition.isAnimated)
|
||||
selfController.leftNavigationButton = nil
|
||||
}
|
||||
|
||||
if let button = rightNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
|
||||
if selfController.rightNavigationButton != button {
|
||||
var animated = transition.isAnimated
|
||||
if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action {
|
||||
animated = false
|
||||
}
|
||||
if case .replyThread = selfController.chatLocation {
|
||||
animated = false
|
||||
}
|
||||
selfController.navigationItem.setRightBarButton(button.buttonItem, animated: animated)
|
||||
selfController.rightNavigationButton = button
|
||||
}
|
||||
} else if let _ = selfController.rightNavigationButton {
|
||||
selfController.navigationItem.setRightBarButton(nil, animated: transition.isAnimated)
|
||||
selfController.rightNavigationButton = nil
|
||||
}
|
||||
|
||||
if let controllerInteraction = selfController.controllerInteraction {
|
||||
if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
|
||||
controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
|
||||
let isBlackout = controllerInteraction.selectionState != nil
|
||||
let previousCompletion = completion
|
||||
completion = { [weak selfController] transition in
|
||||
previousCompletion(transition)
|
||||
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
(selfController.navigationController as? NavigationController)?.updateMasterDetailsBlackout(isBlackout ? .master : nil, transition: transition)
|
||||
}
|
||||
selfController.updateItemNodesSelectionStates(animated: transition.isAnimated)
|
||||
}
|
||||
}
|
||||
|
||||
if saveInterfaceState {
|
||||
selfController.saveInterfaceState(includeScrollState: false)
|
||||
}
|
||||
|
||||
if let navigationController = selfController.navigationController as? NavigationController, isTopmostChatController(selfController) {
|
||||
var voiceChatOverlayController: VoiceChatOverlayController?
|
||||
for controller in navigationController.globalOverlayControllers {
|
||||
if let controller = controller as? VoiceChatOverlayController {
|
||||
voiceChatOverlayController = controller
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let controller = voiceChatOverlayController {
|
||||
controller.updateVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
if let currentMenuWebAppController = selfController.currentMenuWebAppController, !selfController.presentationInterfaceState.showWebView {
|
||||
selfController.currentMenuWebAppController = nil
|
||||
if let currentMenuWebAppController = currentMenuWebAppController as? AttachmentController {
|
||||
currentMenuWebAppController.ensureUnfocused = false
|
||||
}
|
||||
currentMenuWebAppController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
selfController.presentationInterfaceStatePromise.set(selfController.presentationInterfaceState)
|
||||
}
|
||||
@ -12073,477 +12073,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, interactive: interactive, saveInterfaceState: saveInterfaceState, f, completion: completion)
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
var completion = externalCompletion
|
||||
var temporaryChatPresentationInterfaceState = f(self.presentationInterfaceState)
|
||||
|
||||
if self.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup || self.presentationInterfaceState.keyboardButtonsMessage?.id != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.id {
|
||||
if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, let keyboardMarkup = keyboardButtonsMessage.visibleButtonKeyboardMarkup {
|
||||
if self.presentationInterfaceState.interfaceState.editMessage == nil && self.presentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.closedButtonKeyboardMessageId && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId && temporaryChatPresentationInterfaceState.botStartPayload == nil {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ _ in
|
||||
return .inputButtons(persistent: keyboardMarkup.flags.contains(.persistent))
|
||||
})
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({
|
||||
$0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
|
||||
messageId: keyboardButtonsMessage.id,
|
||||
quote: nil
|
||||
)).withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.processedSetupReplyMessageId = keyboardButtonsMessage.id
|
||||
return value
|
||||
}) })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ mode in
|
||||
if case .inputButtons = mode {
|
||||
return .text
|
||||
} else {
|
||||
return mode
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, keyboardButtonsMessage.requestsSetupReply {
|
||||
if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id {
|
||||
temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
|
||||
messageId: keyboardButtonsMessage.id,
|
||||
quote: nil
|
||||
)).withUpdatedMessageActionsState({ value in
|
||||
var value = value
|
||||
value.processedSetupReplyMessageId = keyboardButtonsMessage.id
|
||||
return value
|
||||
}) })
|
||||
}
|
||||
}
|
||||
|
||||
let inputTextPanelState = inputTextPanelStateForChatPresentationInterfaceState(temporaryChatPresentationInterfaceState, context: self.context)
|
||||
var updatedChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputTextPanelState({ _ in return inputTextPanelState })
|
||||
|
||||
let contextQueryUpdates = contextQueryResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: self.context, currentQueryStates: &self.contextQueryStates, requestBotLocationStatus: { [weak self] peerId in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = (ApplicationSpecificNotice.updateInlineBotLocationRequestState(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, timestamp: Int32(Date().timeIntervalSince1970 + 10 * 60))
|
||||
|> deliverOnMainQueue).startStandalone(next: { value in
|
||||
guard let strongSelf = self, value else {
|
||||
return
|
||||
}
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
|
||||
})]), in: .window(.root))
|
||||
})
|
||||
})
|
||||
|
||||
for (kind, update) in contextQueryUpdates {
|
||||
switch update {
|
||||
case .remove:
|
||||
if let (_, disposable) = self.contextQueryStates[kind] {
|
||||
disposable.dispose()
|
||||
self.contextQueryStates.removeValue(forKey: kind)
|
||||
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if case .contextRequest = kind {
|
||||
self.performingInlineSearch.set(false)
|
||||
}
|
||||
case let .update(query, signal):
|
||||
let currentQueryAndDisposable = self.contextQueryStates[kind]
|
||||
currentQueryAndDisposable?.1.dispose()
|
||||
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
self.contextQueryStates[kind] = (query, (signal
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return result(previousResult)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}, error: { [weak self] error in
|
||||
if let strongSelf = self {
|
||||
if case .contextRequest = kind {
|
||||
strongSelf.performingInlineSearch.set(false)
|
||||
}
|
||||
|
||||
switch error {
|
||||
case .generic:
|
||||
break
|
||||
case let .inlineBotLocationRequest(peerId):
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).startStandalone()
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}, completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if case .contextRequest = kind {
|
||||
strongSelf.performingInlineSearch.set(false)
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
})
|
||||
} else {
|
||||
if case .contextRequest = kind {
|
||||
self.performingInlineSearch.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
if case .contextRequest = query {
|
||||
let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: self.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
|
||||
if let strongSelf = self, !value {
|
||||
let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).startStandalone()
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isBot = false
|
||||
if let peer = updatedChatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil {
|
||||
isBot = true
|
||||
} else {
|
||||
isBot = false
|
||||
}
|
||||
self.chatDisplayNode.historyNode.chatHasBots = updatedChatPresentationInterfaceState.hasBots || isBot
|
||||
|
||||
if let (updatedSearchQuerySuggestionState, updatedSearchQuerySuggestionSignal) = searchQuerySuggestionResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: context, currentQuery: self.searchQuerySuggestionState?.0) {
|
||||
self.searchQuerySuggestionState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
self.searchQuerySuggestionState = (updatedSearchQuerySuggestionState, (updatedSearchQuerySuggestionSignal |> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedSearchQuerySuggestionResult { previousResult in
|
||||
return result(previousResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedSearchQuerySuggestionResult { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (updatedUrlPreviewUrl, updatedUrlPreviewSignal) = urlPreviewStateForInputText(updatedChatPresentationInterfaceState.interfaceState.composeInputState.inputText, context: self.context, currentQuery: self.urlPreviewQueryState?.0) {
|
||||
self.urlPreviewQueryState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((TelegramMediaWebpage?) -> TelegramMediaWebpage?)?
|
||||
let linkPreviews: Signal<Bool, NoError>
|
||||
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
linkPreviews = interactiveChatLinkPreviewsEnabled(accountManager: self.context.sharedContext.accountManager, displayAlert: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretLinkPreviewAlert, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
|
||||
f.f(true)
|
||||
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_No, action: {
|
||||
f.f(false)
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var bannedEmbedLinks = false
|
||||
if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.hasBannedPermission(.banEmbedLinks) != nil {
|
||||
bannedEmbedLinks = true
|
||||
} else if let group = self.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup, group.hasBannedPermission(.banEmbedLinks) {
|
||||
bannedEmbedLinks = true
|
||||
}
|
||||
if bannedEmbedLinks {
|
||||
linkPreviews = .single(false)
|
||||
} else {
|
||||
linkPreviews = .single(true)
|
||||
}
|
||||
}
|
||||
let filteredPreviewSignal = linkPreviews
|
||||
|> take(1)
|
||||
|> mapToSignal { value -> Signal<(TelegramMediaWebpage?) -> TelegramMediaWebpage?, NoError> in
|
||||
if value {
|
||||
return updatedUrlPreviewSignal
|
||||
} else {
|
||||
return .single({ _ in return nil })
|
||||
}
|
||||
}
|
||||
|
||||
self.urlPreviewQueryState = (updatedUrlPreviewUrl, (filteredPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak self] (result) in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = result($0.urlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: $0.urlPreview?.positionBelowText ?? true,
|
||||
largeMedia: $0.urlPreview?.largeMedia
|
||||
)
|
||||
return $0.updatedUrlPreview(updatedPreview)
|
||||
} else {
|
||||
return $0.updatedUrlPreview(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
if let updatedUrlPreviewUrl = updatedUrlPreviewUrl, let webpage = inScopeResult(updatedChatPresentationInterfaceState.urlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: updatedChatPresentationInterfaceState.urlPreview?.positionBelowText ?? true,
|
||||
largeMedia: updatedChatPresentationInterfaceState.urlPreview?.largeMedia
|
||||
)
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(updatedPreview)
|
||||
} else {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext
|
||||
let editingUrlPreviewText: NSAttributedString? = isEditingMedia ? nil : updatedChatPresentationInterfaceState.interfaceState.editMessage?.inputState.inputText
|
||||
if let (updatedEditingUrlPreviewUrl, updatedEditingUrlPreviewSignal) = urlPreviewStateForInputText(editingUrlPreviewText, context: self.context, currentQuery: self.editingUrlPreviewQueryState?.0) {
|
||||
self.editingUrlPreviewQueryState?.1.dispose()
|
||||
var inScope = true
|
||||
var inScopeResult: ((TelegramMediaWebpage?) -> TelegramMediaWebpage?)?
|
||||
self.editingUrlPreviewQueryState = (updatedEditingUrlPreviewUrl, (updatedEditingUrlPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
if let updatedEditingUrlPreviewUrl = updatedEditingUrlPreviewUrl, let webpage = result($0.editingUrlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedEditingUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: $0.editingUrlPreview?.positionBelowText ?? true,
|
||||
largeMedia: $0.editingUrlPreview?.largeMedia
|
||||
)
|
||||
return $0.updatedEditingUrlPreview(updatedPreview)
|
||||
} else {
|
||||
return $0.updatedEditingUrlPreview(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
if let updatedEditingUrlPreviewUrl = updatedEditingUrlPreviewUrl, let webpage = inScopeResult(updatedChatPresentationInterfaceState.editingUrlPreview?.webPage) {
|
||||
let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
|
||||
url: updatedEditingUrlPreviewUrl,
|
||||
webPage: webpage,
|
||||
positionBelowText: updatedChatPresentationInterfaceState.editingUrlPreview?.positionBelowText ?? true,
|
||||
largeMedia: updatedChatPresentationInterfaceState.editingUrlPreview?.largeMedia
|
||||
)
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(updatedPreview)
|
||||
} else {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let replyMessageId = updatedChatPresentationInterfaceState.interfaceState.replyMessageSubject?.messageId {
|
||||
if self.replyMessageState?.0 != replyMessageId {
|
||||
self.replyMessageState?.1.dispose()
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
|
||||
let disposable = MetaDisposable()
|
||||
self.replyMessageState = (replyMessageId, disposable)
|
||||
disposable.set((self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: replyMessageId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] message in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if message != self.presentationInterfaceState.replyMessage.flatMap(EngineMessage.init) {
|
||||
self.updateChatPresentationInterfaceState(interactive: false, { presentationInterfaceState in
|
||||
return presentationInterfaceState.updatedReplyMessage(message?._asMessage())
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
if let replyMessageState = self.replyMessageState {
|
||||
self.replyMessageState = nil
|
||||
replyMessageState.1.dispose()
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let updated = self.updateSearch(updatedChatPresentationInterfaceState) {
|
||||
updatedChatPresentationInterfaceState = updated
|
||||
}
|
||||
|
||||
let recordingActivityValue: ChatRecordingActivity
|
||||
if let mediaRecordingState = updatedChatPresentationInterfaceState.inputTextPanelState.mediaRecordingState {
|
||||
switch mediaRecordingState {
|
||||
case .audio:
|
||||
recordingActivityValue = .voice
|
||||
case .video(ChatVideoRecordingStatus.recording, _):
|
||||
recordingActivityValue = .instantVideo
|
||||
default:
|
||||
recordingActivityValue = .none
|
||||
}
|
||||
} else {
|
||||
recordingActivityValue = .none
|
||||
}
|
||||
if recordingActivityValue != self.recordingActivityValue {
|
||||
self.recordingActivityValue = recordingActivityValue
|
||||
self.recordingActivityPromise.set(recordingActivityValue)
|
||||
}
|
||||
|
||||
if (self.presentationInterfaceState.interfaceState.selectionState == nil) != (updatedChatPresentationInterfaceState.interfaceState.selectionState == nil) {
|
||||
self.isSelectingMessagesUpdated?(updatedChatPresentationInterfaceState.interfaceState.selectionState != nil)
|
||||
self.updateNextChannelToReadVisibility()
|
||||
}
|
||||
|
||||
self.presentationInterfaceState = updatedChatPresentationInterfaceState
|
||||
|
||||
self.updateSlowmodeStatus()
|
||||
|
||||
switch updatedChatPresentationInterfaceState.inputMode {
|
||||
case .media:
|
||||
break
|
||||
default:
|
||||
self.chatDisplayNode.collapseInput()
|
||||
}
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion)
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
|
||||
let updatedServiceTasks = serviceTasksForChatPresentationIntefaceState(context: self.context, chatPresentationInterfaceState: updatedChatPresentationInterfaceState, updateState: { [weak self] f in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, f)
|
||||
|
||||
//strongSelf.chatDisplayNode.updateChatPresentationInterfaceState(f(strongSelf.chatDisplayNode.chatPresentationInterfaceState), transition: transition, interactive: false, completion: { _ in })
|
||||
})
|
||||
for (id, begin) in updatedServiceTasks {
|
||||
if self.stateServiceTasks[id] == nil {
|
||||
self.stateServiceTasks[id] = begin()
|
||||
}
|
||||
}
|
||||
var removedServiceTaskIds: [AnyHashable] = []
|
||||
for (id, _) in self.stateServiceTasks {
|
||||
if updatedServiceTasks[id] == nil {
|
||||
removedServiceTaskIds.append(id)
|
||||
}
|
||||
}
|
||||
for id in removedServiceTaskIds {
|
||||
self.stateServiceTasks.removeValue(forKey: id)?.dispose()
|
||||
}
|
||||
|
||||
if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, subject: self.subject, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) {
|
||||
if self.leftNavigationButton != button {
|
||||
var animated = transition.isAnimated
|
||||
if let currentButton = self.leftNavigationButton?.action, currentButton == button.action {
|
||||
animated = false
|
||||
}
|
||||
animated = false
|
||||
self.navigationItem.setLeftBarButton(button.buttonItem, animated: animated)
|
||||
self.leftNavigationButton = button
|
||||
}
|
||||
} else if let _ = self.leftNavigationButton {
|
||||
self.navigationItem.setLeftBarButton(nil, animated: transition.isAnimated)
|
||||
self.leftNavigationButton = nil
|
||||
}
|
||||
|
||||
if let button = rightNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.rightNavigationButton, target: self, selector: #selector(self.rightNavigationButtonAction), chatInfoNavigationButton: self.chatInfoNavigationButton, moreInfoNavigationButton: self.moreInfoNavigationButton) {
|
||||
if self.rightNavigationButton != button {
|
||||
var animated = transition.isAnimated
|
||||
if let currentButton = self.rightNavigationButton?.action, currentButton == button.action {
|
||||
animated = false
|
||||
}
|
||||
if case .replyThread = self.chatLocation {
|
||||
animated = false
|
||||
}
|
||||
self.navigationItem.setRightBarButton(button.buttonItem, animated: animated)
|
||||
self.rightNavigationButton = button
|
||||
}
|
||||
} else if let _ = self.rightNavigationButton {
|
||||
self.navigationItem.setRightBarButton(nil, animated: transition.isAnimated)
|
||||
self.rightNavigationButton = nil
|
||||
}
|
||||
|
||||
if let controllerInteraction = self.controllerInteraction {
|
||||
if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
|
||||
controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
|
||||
let isBlackout = controllerInteraction.selectionState != nil
|
||||
let previousCompletion = completion
|
||||
completion = { [weak self] transition in
|
||||
previousCompletion(transition)
|
||||
(self?.navigationController as? NavigationController)?.updateMasterDetailsBlackout(isBlackout ? .master : nil, transition: transition)
|
||||
}
|
||||
self.updateItemNodesSelectionStates(animated: transition.isAnimated)
|
||||
}
|
||||
}
|
||||
|
||||
if saveInterfaceState {
|
||||
self.saveInterfaceState(includeScrollState: false)
|
||||
}
|
||||
|
||||
if let navigationController = self.navigationController as? NavigationController, isTopmostChatController(self) {
|
||||
var voiceChatOverlayController: VoiceChatOverlayController?
|
||||
for controller in navigationController.globalOverlayControllers {
|
||||
if let controller = controller as? VoiceChatOverlayController {
|
||||
voiceChatOverlayController = controller
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let controller = voiceChatOverlayController {
|
||||
controller.updateVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
if let currentMenuWebAppController = self.currentMenuWebAppController, !self.presentationInterfaceState.showWebView {
|
||||
self.currentMenuWebAppController = nil
|
||||
if let currentMenuWebAppController = currentMenuWebAppController as? AttachmentController {
|
||||
currentMenuWebAppController.ensureUnfocused = false
|
||||
}
|
||||
currentMenuWebAppController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
self.presentationInterfaceStatePromise.set(self.presentationInterfaceState)
|
||||
func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
updateChatPresentationInterfaceStateImpl(
|
||||
selfController: self,
|
||||
transition: transition,
|
||||
interactive: interactive,
|
||||
saveInterfaceState: saveInterfaceState,
|
||||
f,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
func updateItemNodesSelectionStates(animated: Bool) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user