Merge branch 'temp-513'

# Conflicts:
#	versions.json
This commit is contained in:
Isaac 2026-04-10 14:30:58 +02:00
commit d6d0ab1a7f
26 changed files with 445 additions and 178 deletions

View file

@ -1 +1 @@
06de25b179c80e59
c27f02bf6e413fdc

View file

@ -34,7 +34,7 @@ private enum InnerState: Equatable {
public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, accentButtonColor: theme.list.itemCheckColors.fillColor, accentForegroundColor: theme.list.itemCheckColors.foregroundColor, style: .glass)
return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, accentButtonColor: theme.list.itemCheckColors.fillColor, accentDisabledButtonColor: theme.chat.inputPanel.panelControlDisabledColor, accentForegroundColor: theme.list.itemCheckColors.foregroundColor, style: .glass)
}
private let sharedContext: SharedAccountContext

View file

@ -3136,20 +3136,11 @@ open class ListViewImpl: ASDisplayNode, ListView, ASScrollViewDelegate, ASGestur
} else if self.snapToBottomInsetUntilFirstInteraction {
offsetFix = -updateSizeAndInsets.insets.bottom + self.insets.bottom
} else {
/*if let visualInsets = self.visualInsets, animated, (visualInsets.top == updateSizeAndInsets.insets.top || visualInsets.top == self.insets.top) {
offsetFix = 0.0
} else {*/
offsetFix = updateSizeAndInsets.insets.top - self.insets.top
//}
offsetFix = updateSizeAndInsets.insets.top - self.insets.top
}
offsetFix += additionalScrollDistance
/*if let topItemNode = self.itemNodes.first(where: { $0.index == 0 }) {
let topEdge = self.scroller.contentOffset.y + updateSizeAndInsets.insets.top
offsetFix = -(topEdge - topItemNode.apparentFrame.minY)
}*/
self.insets = updateSizeAndInsets.insets
self.headerInsets = updateSizeAndInsets.headerInsets ?? self.insets
self.scrollIndicatorInsets = updateSizeAndInsets.scrollIndicatorInsets ?? self.insets
@ -3239,16 +3230,18 @@ open class ListViewImpl: ASDisplayNode, ListView, ASScrollViewDelegate, ASGestur
animation = basicAnimation
}
deferredUpdateVisible = true
animation.completion = { [weak self] _ in
self?.updateItemNodesVisibilities(onlyPositive: false)
}
self.layer.add(animation, forKey: nil)
if !completeOffset.isZero {
for itemNode in self.itemNodes {
itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: animationCurve, duration: animationDuration)
if customAnimationTransition == nil {
deferredUpdateVisible = true
animation.completion = { [weak self] _ in
self?.updateItemNodesVisibilities(onlyPositive: false)
}
self.layer.add(animation, forKey: nil)
if !completeOffset.isZero {
for itemNode in self.itemNodes {
itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: animationCurve, duration: animationDuration)
}
self.didScrollWithOffset?(-completeOffset, ContainedViewLayoutTransition.animated(duration: animationDuration, curve: animationCurve), nil, self.isTrackingOrDecelerating)
}
self.didScrollWithOffset?(-completeOffset, ContainedViewLayoutTransition.animated(duration: animationDuration, curve: animationCurve), nil, self.isTrackingOrDecelerating)
}
} else {
self.didScrollWithOffset?(-completeOffset, .immediate, nil, self.isTrackingOrDecelerating)

View file

@ -29,11 +29,12 @@ public final class NavigationBarTheme {
public let badgeTextColor: UIColor
public let edgeEffectColor: UIColor?
public let accentButtonColor: UIColor
public let accentDisabledButtonColor: UIColor
public let accentForegroundColor: UIColor
public let style: NavigationBar.Style
public let glassStyle: NavigationBar.GlassStyle
public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, accentButtonColor: UIColor, accentForegroundColor: UIColor, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) {
public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, accentButtonColor: UIColor, accentDisabledButtonColor: UIColor, accentForegroundColor: UIColor, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) {
self.overallDarkAppearance = overallDarkAppearance
self.buttonColor = buttonColor
self.disabledButtonColor = disabledButtonColor
@ -47,17 +48,18 @@ public final class NavigationBarTheme {
self.badgeTextColor = badgeTextColor
self.edgeEffectColor = edgeEffectColor
self.accentButtonColor = accentButtonColor
self.accentDisabledButtonColor = accentDisabledButtonColor
self.accentForegroundColor = accentForegroundColor
self.style = style
self.glassStyle = glassStyle
}
public func withUpdatedBackgroundColor(_ color: UIColor) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentDisabledButtonColor: self.accentDisabledButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
}
public func withUpdatedSeparatorColor(_ color: UIColor) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentDisabledButtonColor: self.accentDisabledButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
}
}

View file

@ -690,7 +690,7 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn
}
public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol {
public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, accentButtonColor: .white, accentForegroundColor: .black, style: .glass)
public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, accentButtonColor: .white, accentDisabledButtonColor: .white, accentForegroundColor: .black, style: .glass)
private var galleryNode: GalleryControllerNode {
return self.displayNode as! GalleryControllerNode

View file

@ -39,7 +39,7 @@ public class SetupTwoStepVerificationController: ViewController {
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentDisabledButtonColor: self.presentationData.theme.chat.inputPanel.panelControlDisabledColor, accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style

View file

@ -71,7 +71,7 @@ public final class TwoFactorDataInputScreen: ViewController {
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentDisabledButtonColor: defaultTheme.accentDisabledButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -57,7 +57,7 @@ public final class TwoFactorAuthSplashScreen: ViewController {
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentDisabledButtonColor: defaultTheme.accentDisabledButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -77,7 +77,7 @@ public final class QrCodeScanScreen: ViewController {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentForegroundColor: .black)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentDisabledButtonColor: .white, accentForegroundColor: .black)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -1341,6 +1341,8 @@ extension StoreMessage {
attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote, innerSubject: innerSubject))
} else if let replyHeader = replyHeader {
attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote))
} else if let replyToTopId, peerIsForum {
threadId = Int64(replyToTopId)
}
case let .messageReplyStoryHeader(messageReplyStoryHeaderData):
let (peer, storyId) = (messageReplyStoryHeaderData.peer, messageReplyStoryHeaderData.storyId)

View file

@ -4011,7 +4011,7 @@ func replayFinalState(
case cancel
}
var liveTypingDraftUpdates: [PeerAndThreadId: LiveTypingDraftUpdate] = [:]
var liveTypingDraftUpdates: [PeerAndThreadId: [LiveTypingDraftUpdate]] = [:]
for operation in finalState.state.operations {
switch operation {
@ -4226,11 +4226,11 @@ func replayFinalState(
let allKey = PeerAndThreadId(peerId: chatPeerId, threadId: nil)
if liveTypingDraftUpdates[key] != nil {
liveTypingDraftUpdates[key] = .cancel
liveTypingDraftUpdates[allKey] = .cancel
liveTypingDraftUpdates[key] = [.cancel]
liveTypingDraftUpdates[allKey] = [.cancel]
} else if let currentDraft = transaction.getCurrentTypingDraft(location: key) {
liveTypingDraftUpdates[key] = .cancel
liveTypingDraftUpdates[allKey] = .cancel
liveTypingDraftUpdates[key] = [.cancel]
liveTypingDraftUpdates[allKey] = [.cancel]
messages[i] = messages[i].withUpdatedCustomStableId(currentDraft.stableId)
}
}
@ -4930,23 +4930,30 @@ func replayFinalState(
updatedSecretChatTypingActivities.insert(chatPeerId.peerId)
}
case let .AddPeerLiveTypingDraftUpdate(peerAndThreadId, id, timestamp, authorId, text, entities):
liveTypingDraftUpdates[peerAndThreadId] = .update(LiveTypingDraftUpdate.Update(
if liveTypingDraftUpdates[peerAndThreadId] == nil {
liveTypingDraftUpdates[peerAndThreadId] = []
}
liveTypingDraftUpdates[peerAndThreadId]?.append(.update(LiveTypingDraftUpdate.Update(
id: id,
threadId: peerAndThreadId.threadId,
authorId: authorId,
timestamp: timestamp,
text: text,
entities: entities
))
)))
if peerAndThreadId.threadId != nil {
liveTypingDraftUpdates[PeerAndThreadId(peerId: peerAndThreadId.peerId, threadId: nil)] = .update(LiveTypingDraftUpdate.Update(
let allKey = PeerAndThreadId(peerId: peerAndThreadId.peerId, threadId: nil)
if liveTypingDraftUpdates[allKey] == nil {
liveTypingDraftUpdates[allKey] = []
}
liveTypingDraftUpdates[allKey]?.append(.update(LiveTypingDraftUpdate.Update(
id: id,
threadId: peerAndThreadId.threadId,
authorId: authorId,
timestamp: timestamp,
text: text,
entities: entities
))
)))
}
case let .UpdatePinnedItemIds(groupId, pinnedOperation):
switch pinnedOperation {
@ -6049,11 +6056,26 @@ func replayFinalState(
if !liveTypingDraftUpdates.isEmpty {
transaction.combineTypingDrafts(locations: Set(liveTypingDraftUpdates.keys), update: { key, current in
guard let update = liveTypingDraftUpdates[key] else {
guard let update = liveTypingDraftUpdates[key]?.max(by: { lhs, rhs in
switch lhs {
case .cancel:
return false
case let .update(lhsUpdate):
switch rhs {
case .cancel:
return true
case let .update(rhsUpdate):
return lhsUpdate.timestamp < rhsUpdate.timestamp
}
}
}) else {
return current
}
switch update {
case let .update(update):
if let current, current.id > update.id {
return current
}
return (
update.id,
Namespaces.Message.Cloud,

View file

@ -69,7 +69,7 @@ public extension NavigationBarTheme {
badgeTextColor = theme.badgeTextColor
}
self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, accentButtonColor: rootControllerTheme.list.itemCheckColors.fillColor, accentForegroundColor: rootControllerTheme.list.itemCheckColors.foregroundColor, style: style, glassStyle: glassStyle)
self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, accentButtonColor: rootControllerTheme.list.itemCheckColors.fillColor, accentDisabledButtonColor: rootControllerTheme.chat.inputPanel.panelControlDisabledColor, accentForegroundColor: rootControllerTheme.list.itemCheckColors.foregroundColor, style: style, glassStyle: glassStyle)
}
}

View file

@ -941,8 +941,8 @@ public final class ChatSideTopicsPanel: Component {
}
let size = CGSize(width: contentSize, height: availableSize.height)
let iconFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - iconSize.height) * 0.5)), size: iconSize)
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize)
let iconFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - iconSize.height) * 0.5) - 1.0), size: iconSize)
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: floor((size.height - titleSize.height) * 0.5) - 1.0), size: titleSize)
if let icon = self.icon {
if let avatarNode = self.avatarNode {
@ -1422,7 +1422,7 @@ public final class ChatSideTopicsPanel: Component {
let contentSize: CGFloat = leftInset + rightInset + titleSize.width
let size = CGSize(width: contentSize, height: availableSize.height)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) * 0.5) - 1.0), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
@ -1599,6 +1599,20 @@ public final class ChatSideTopicsPanel: Component {
}
}
if isPinned {
if !seenPinnedItems {
switch component.location {
case .side:
beforePinnedItemsPosition = item.frame.minY
case .top, .bottom:
beforePinnedItemsPosition = item.frame.minX
}
switch component.location {
case .side:
afterPinnedItemsPosition = item.frame.maxY
case .top, .bottom:
afterPinnedItemsPosition = item.frame.maxX
}
}
seenPinnedItems = true
} else {
if !seenPinnedItems {
@ -1885,7 +1899,7 @@ public final class ChatSideTopicsPanel: Component {
itemFrame = CGRect(origin: CGPoint(x: 8.0 + 4.0, y: directionContainerInset + 6.0), size: itemSize)
directionContainerInset += itemSize.height
case .top, .bottom:
itemFrame = CGRect(origin: CGPoint(x: 12.0, y: 6.0), size: itemSize)
itemFrame = CGRect(origin: CGPoint(x: 12.0, y: 5.0), size: itemSize)
directionContainerInset += itemSize.width - 20.0
}

View file

@ -818,7 +818,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
if let transitionInfo = reference.transitionInfo() {
if let referenceView = transitionInfo.referenceView as? ContextExtractableContainer {
if #available(iOS 26.2, *) {
if transitionInfo.referenceView.bounds.width == transitionInfo.referenceView.bounds.height {
if !"".isEmpty, transitionInfo.referenceView.bounds.width == transitionInfo.referenceView.bounds.height {
contextExtractableContainer = (referenceView, convertFrame(transitionInfo.referenceView.bounds.inset(by: transitionInfo.insets), from: transitionInfo.referenceView, to: self.view))
}
}

View file

@ -1020,10 +1020,10 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
switch mode {
case .create:
title = presentationData.strings.CreateTopic_CreateTitle
doneTitle = presentationData.strings.CreateTopic_Create
doneTitle = "___done"
case let .edit(threadId, topic, isHidden):
title = presentationData.strings.CreateTopic_EditTitle
doneTitle = presentationData.strings.Common_Done
doneTitle = "___done"
self.state = (topic.title, topic.icon, topic.iconColor, threadId == 1 ? isHidden : nil)
}
@ -1032,7 +1032,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true)))
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "___close", style: .plain, target: self, action: #selector(self.cancelPressed))
self.doneBarItem = UIBarButtonItem(title: doneTitle, style: .done, target: self, action: #selector(self.createPressed))
self.navigationItem.rightBarButtonItem = self.doneBarItem

View file

@ -721,6 +721,12 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
}
self.leftButtonNodeImpl.requestUpdate = { [weak self] in
guard let self else {
return
}
self.requestLayout()
}
self.rightButtonNodeImpl.pressed = { [weak self] index in
if let item = self?.item {
@ -733,6 +739,12 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
}
self.rightButtonNodeImpl.requestUpdate = { [weak self] in
guard let self else {
return
}
self.requestLayout()
}
}
public var isBackgroundVisible: Bool {
@ -906,6 +918,9 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
case .accent:
self.leftButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor
self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5)
case .accentDisabled:
self.leftButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor
self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5)
case .generic:
self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
@ -946,6 +961,9 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
case .accent:
self.rightButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor
self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5)
case .accentDisabled:
self.rightButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor
self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5)
case .generic:
self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
@ -994,6 +1012,8 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
switch self.leftButtonNodeImpl.commonContentType {
case .accent:
leftButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentButtonColor))
case .accentDisabled:
leftButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentDisabledButtonColor))
case .generic:
break
}
@ -1028,6 +1048,8 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
switch self.rightButtonNodeImpl.commonContentType {
case .accent:
rightButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentButtonColor))
case .accentDisabled:
rightButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentDisabledButtonColor))
case .generic:
break
}

View file

@ -211,6 +211,7 @@ private final class ItemComponent: Component {
private final class NavigationButtonItemNode: ImmediateTextNode {
private let isGlass: Bool
var requestUpdate: (() -> Void)?
private func fontForCurrentState() -> UIFont {
return self.bold ? Font.semibold(17.0) : Font.medium(17.0)
@ -235,7 +236,13 @@ private final class NavigationButtonItemNode: ImmediateTextNode {
if let item = self.item {
self.setEnabledListener = item.addSetEnabledListener { [weak self] value in
self?.isEnabled = value
guard let self else {
return
}
if self.isEnabled != value {
self.isEnabled = value
self.requestUpdate?()
}
}
self.accessibilityHint = item.accessibilityHint
self.accessibilityLabel = item.accessibilityLabel
@ -528,6 +535,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode {
public final class NavigationButtonNodeImpl: ContextControllerSourceNode, NavigationButtonNode {
enum ContentType {
case accent
case accentDisabled
case generic
}
@ -537,6 +545,8 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga
private var items: [UIBarButtonItem] = []
private var nodes: [NavigationButtonItemNode] = []
var requestUpdate: (() -> Void)?
private var disappearingNodes: [(frame: CGRect, size: CGSize, node: NavigationButtonItemNode)] = []
public var singleCustomNode: ASDisplayNode? {
@ -633,6 +643,9 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga
}
}
}
node.requestUpdate = { [weak self] in
self?.requestUpdate?()
}
self.nodes.append(node)
self.addSubnode(node)
}
@ -680,6 +693,9 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga
}
}
}
node.requestUpdate = { [weak self] in
self?.requestUpdate?()
}
self.nodes.append(node)
self.addSubnode(node)
}
@ -812,7 +828,11 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga
if item.title == "___close" {
} else if item.title == "___clear" {
} else if item.title == "___done" {
nodeContentType = .accent
if item.isEnabled {
nodeContentType = .accent
} else {
nodeContentType = .accentDisabled
}
}
if commonType == nil {

View file

@ -6459,6 +6459,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
badgeStrokeColor: baseNavigationBarPresentationData.theme.badgeStrokeColor,
badgeTextColor: baseNavigationBarPresentationData.theme.badgeTextColor,
accentButtonColor: baseNavigationBarPresentationData.theme.accentButtonColor,
accentDisabledButtonColor: baseNavigationBarPresentationData.theme.accentDisabledButtonColor,
accentForegroundColor: baseNavigationBarPresentationData.theme.accentForegroundColor
), strings: baseNavigationBarPresentationData.strings))

View file

@ -409,39 +409,45 @@ extension ChatControllerImpl {
}
})))
} else {
items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] action in
guard let self else {
return
}
action.dismissWithResult(.default)
let controller = ForumCreateTopicScreen(context: self.context, peerId: peerId, mode: .create)
controller.navigationPresentation = .modal
controller.completion = { [weak self, weak controller] title, fileId, iconColor, _ in
controller?.isInProgress = true
controller?.view.endEditing(true)
var canCreateTopics = false
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramUser, let botInfo = peer.botInfo, botInfo.flags.contains(.forumManagedByUser) {
canCreateTopics = true
}
if canCreateTopics {
items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] action in
guard let self else {
return
}
let _ = (self.context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak controller] topicId in
action.dismissWithResult(.default)
let controller = ForumCreateTopicScreen(context: self.context, peerId: peerId, mode: .create)
controller.navigationPresentation = .modal
controller.completion = { [weak self, weak controller] title, fileId, iconColor, _ in
controller?.isInProgress = true
controller?.view.endEditing(true)
guard let self else {
return
}
self.updateChatLocationThread(threadId: topicId)
controller?.dismiss()
}, error: { _ in
controller?.isInProgress = false
})
}
self.push(controller)
})))
let _ = (self.context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak controller] topicId in
guard let self else {
return
}
self.updateChatLocationThread(threadId: topicId)
controller?.dismiss()
}, error: { _ in
controller?.isInProgress = false
})
}
self.push(controller)
})))
}
}
let presentationData = self.presentationData

View file

@ -2281,78 +2281,112 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf else {
return
}
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil)
if clearInput {
interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString()))
}
return interfaceState
}.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
})
}
}, shouldAnimateMessageTransition ? correlationId : nil)
if shouldAnimateMessageTransition {
if let sourceNode = sourceView?.asyncdisplaykit_node as? ChatMediaInputStickerGridItemNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .inputPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: {
guard let strongSelf = self else {
return
}
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
let addToTransitionNodeIfNeeded: () -> Void = {
guard let strongSelf = self else {
return
}
if shouldAnimateMessageTransition {
if let sourceNode = sourceView?.asyncdisplaykit_node as? ChatMediaInputStickerGridItemNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .inputPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: {
guard let strongSelf = self else {
return
}
return current
})
})
} else if let sourceNode = sourceView?.asyncdisplaykit_node as? HorizontalStickerGridItemNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .mediaPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: {})
} else if let sourceNode = sourceView?.asyncdisplaykit_node as? ChatEmptyNodeStickerContentNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .emptyPanel(itemNode: sourceNode), replyPanel: nil), initiated: {})
} else if let sourceLayer, let sourceView, let sourceRect {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .universal(sourceContainerView: sourceView, sourceRect: sourceRect, sourceLayer: sourceLayer), replyPanel: replyPanel), initiated: {
guard let strongSelf = self else {
return
}
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
}
return current
})
})
})
} else if let sourceNode = sourceView?.asyncdisplaykit_node as? HorizontalStickerGridItemNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .mediaPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: {})
} else if let sourceNode = sourceView?.asyncdisplaykit_node as? ChatEmptyNodeStickerContentNode {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .emptyPanel(itemNode: sourceNode), replyPanel: nil), initiated: {})
} else if let sourceLayer, let sourceView, let sourceRect {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .universal(sourceContainerView: sourceView, sourceRect: sourceRect, sourceLayer: sourceLayer), replyPanel: replyPanel), initiated: {
guard let strongSelf = self else {
return
}
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
})
})
}
}
}
let messages: [EnqueueMessage] = [.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: fileReference.abstract, threadId: strongSelf.chatLocation.threadId, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)]
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
let messages: [EnqueueMessage] = [.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: fileReference.abstract, threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)]
if silentPosting {
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil)
if clearInput {
interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString()))
}
return interfaceState
}.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
})
}
}, shouldAnimateMessageTransition ? correlationId : nil)
addToTransitionNodeIfNeeded()
let transformedMessages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting, postpone: postpone)
strongSelf.sendMessages(transformedMessages)
} else if schedule {
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil)
if clearInput {
interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString()))
}
return interfaceState
}.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
})
}
}, shouldAnimateMessageTransition ? correlationId : nil)
strongSelf.presentScheduleTimePicker(completion: { [weak self] scheduleTime, repeatPeriod in
if let strongSelf = self {
let transformedMessages = strongSelf.transformEnqueueMessages(messages, silentPosting: false, scheduleTime: scheduleTime, repeatPeriod: repeatPeriod, postpone: postpone)
@ -2360,8 +2394,90 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
} else {
let transformedMessages = strongSelf.transformEnqueueMessages(messages, postpone: postpone)
strongSelf.sendMessages(transformedMessages)
let messages = strongSelf.transformEnqueueMessages(messages, postpone: postpone)
var targetThreadId: Int64?
var clearMainThreadForward = false
if strongSelf.chatLocation.threadId == nil, let user = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), botInfo.flags.contains(.forumManagedByUser) {
if let message = messages.first {
switch message {
case let .message(_, _, _, _, _, replyToMessageId, _, _, _, _):
if let _ = replyToMessageId {
if let replyMessage = strongSelf.presentationInterfaceState.replyMessage {
targetThreadId = replyMessage.threadId
}
} else {
targetThreadId = EngineMessage.newTopicThreadId
}
case let .forward(_, threadId, _, _, _):
if let threadId {
targetThreadId = threadId
} else {
targetThreadId = EngineMessage.newTopicThreadId
clearMainThreadForward = true
}
}
}
}
let doSend: (Int64?) -> Void = { [weak self] overrideThreadId in
guard let strongSelf = self else {
return
}
var messages = messages
if let overrideThreadId {
messages = messages.map { message in
return message.withUpdatedThreadId(overrideThreadId)
}
}
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in
var current = current
current = current.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil)
if clearInput {
interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString()))
}
return interfaceState
}.updatedInputMode { current in
if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil {
return .media(mode: mode, expanded: nil, focused: focused)
}
return current
}
return current
})
}
}, shouldAnimateMessageTransition ? correlationId : nil)
addToTransitionNodeIfNeeded()
strongSelf.sendMessages(messages.map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) })
}
if let targetThreadId {
strongSelf.chatDisplayNode.historyNode.stopHistoryUpdates()
strongSelf.updateChatLocationThread(threadId: targetThreadId, animationDirection: .right, transferInputState: true, completion: { [weak strongSelf] in
guard let strongSelf else {
return
}
doSend(targetThreadId)
if clearMainThreadForward, let peerId = strongSelf.chatLocation.peerId {
let _ = ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { current in
return current.withUpdatedForwardMessageIds(nil)
}).startStandalone()
}
})
} else {
doSend(nil)
}
}
})
return true
@ -8689,10 +8805,58 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
mappedMessages.append(message)
}
let messages = strongSelf.transformEnqueueMessages(mappedMessages, silentPosting: silentPosting, scheduleTime: scheduleTime, postpone: postpone)
let replyMessageSubject = replyToSubject ?? strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
var targetThreadId: Int64?
var clearMainThreadForward = false
if strongSelf.chatLocation.threadId == nil, let user = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), botInfo.flags.contains(.forumManagedByUser) {
if let message = messages.first {
switch message {
case let .message(_, _, _, _, _, replyToMessageId, _, _, _, _):
if let _ = replyToMessageId {
if let replyMessage = strongSelf.presentationInterfaceState.replyMessage {
targetThreadId = replyMessage.threadId
}
} else {
targetThreadId = EngineMessage.newTopicThreadId
}
case let .forward(_, threadId, _, _, _):
if let threadId {
targetThreadId = threadId
} else {
targetThreadId = EngineMessage.newTopicThreadId
clearMainThreadForward = true
}
}
}
}
if addedTransitions.count > 1 {
var transitions: [(Int64, ChatMessageTransitionNodeImpl.Source, () -> Void)] = []
for (correlationId, uniqueIds, initiated) in addedTransitions {
let addTransitionNodes: () -> Void = {
guard let strongSelf = self else {
return
}
if addedTransitions.count > 1 {
var transitions: [(Int64, ChatMessageTransitionNodeImpl.Source, () -> Void)] = []
for (correlationId, uniqueIds, initiated) in addedTransitions {
var source: ChatMessageTransitionNodeImpl.Source?
if uniqueIds.count > 1 {
source = .groupedMediaInput(ChatMessageTransitionNodeImpl.Source.GroupedMediaInput(extractSnapshots: {
return uniqueIds.compactMap({ getAnimatedTransitionSource?($0) })
}))
} else if let uniqueId = uniqueIds.first {
source = .mediaInput(ChatMessageTransitionNodeImpl.Source.MediaInput(extractSnapshot: {
return getAnimatedTransitionSource?(uniqueId)
}))
}
if let source = source {
transitions.append((correlationId, source, initiated))
}
}
strongSelf.chatDisplayNode.messageTransitionNode.add(grouped: transitions)
} else if let (correlationId, uniqueIds, initiated) = addedTransitions.first {
var source: ChatMessageTransitionNodeImpl.Source?
if uniqueIds.count > 1 {
source = .groupedMediaInput(ChatMessageTransitionNodeImpl.Source.GroupedMediaInput(extractSnapshots: {
@ -8704,26 +8868,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
}
if let source = source {
transitions.append((correlationId, source, initiated))
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: source, initiated: {
initiated()
})
}
}
strongSelf.chatDisplayNode.messageTransitionNode.add(grouped: transitions)
} else if let (correlationId, uniqueIds, initiated) = addedTransitions.first {
var source: ChatMessageTransitionNodeImpl.Source?
if uniqueIds.count > 1 {
source = .groupedMediaInput(ChatMessageTransitionNodeImpl.Source.GroupedMediaInput(extractSnapshots: {
return uniqueIds.compactMap({ getAnimatedTransitionSource?($0) })
}))
} else if let uniqueId = uniqueIds.first {
source = .mediaInput(ChatMessageTransitionNodeImpl.Source.MediaInput(extractSnapshot: {
return getAnimatedTransitionSource?(uniqueId)
}))
}
if let source = source {
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: source, initiated: {
initiated()
})
}
}
if case let .customChatContents(customChatContents) = strongSelf.presentationInterfaceState.subject, let messageLimit = customChatContents.messageLimit {
@ -8740,21 +8889,50 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
}
let messages = strongSelf.transformEnqueueMessages(mappedMessages, silentPosting: silentPosting, scheduleTime: scheduleTime, postpone: postpone)
let replyMessageSubject = replyToSubject ?? strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) }
})
let doSend: (Int64?) -> Void = { [weak strongSelf] overrideThreadId in
guard let strongSelf else {
return
}
completionImpl?()
}, usedCorrelationId)
var messages = messages
if let overrideThreadId {
messages = messages.map { message in
return message.withUpdatedThreadId(overrideThreadId)
}
}
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
if let strongSelf = self {
strongSelf.chatDisplayNode.collapseInput()
strongSelf.sendMessages(messages.map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) }, media: true)
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) }
})
}
completionImpl?()
}, usedCorrelationId)
addTransitionNodes()
strongSelf.sendMessages(messages.map { $0.withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) }, media: true)
}
if let targetThreadId {
strongSelf.chatDisplayNode.historyNode.stopHistoryUpdates()
strongSelf.updateChatLocationThread(threadId: targetThreadId, animationDirection: .right, transferInputState: true, completion: { [weak strongSelf] in
guard let strongSelf else {
return
}
doSend(targetThreadId)
if clearMainThreadForward, let peerId = strongSelf.chatLocation.peerId {
let _ = ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { current in
return current.withUpdatedForwardMessageIds(nil)
}).startStandalone()
}
})
} else {
doSend(nil)
}
if let _ = scheduleTime {
completion()

View file

@ -2051,6 +2051,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
messageTransitionNode.transform = previousMessageTransitionNode.transform
previousMessageTransitionNode.supernode?.insertSubnode(self.messageTransitionNode, aboveSubnode: previousMessageTransitionNode)
previousMessageTransitionNode.overlayContainerNode.supernode?.insertSubnode( self.messageTransitionNode.overlayContainerNode, aboveSubnode: previousMessageTransitionNode.overlayContainerNode)
self.emptyType = nil
self.isLoadingValue = false

View file

@ -484,8 +484,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else {
return .single(ContextController.Items(content: .list([])))
}
if let message = messages.first, message.id.namespace < 0 {
return .single(ContextController.Items(content: .list([])))
if let message = messages.first {
if message.id.namespace < 0 {
return .single(ContextController.Items(content: .list([])))
}
if message.id.namespace == Namespaces.Message.Local && message.attributes.contains(where: { $0 is TypingDraftMessageAttribute }) {
return .single(ContextController.Items(content: .list([])))
}
}
var isEmbeddedMode = false

View file

@ -388,9 +388,9 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
func beginAnimation() {
if let portalTargetView = self.portalTargetView {
portalTargetView.view.alpha = 0.0
portalTargetView.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
portalTargetView.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0.15)
self.portalSourceView.layer.animateAlpha(from: 0.01, to: 1.0, duration: 0.12)
self.portalSourceView.layer.animateAlpha(from: 0.01, to: 1.0, duration: 0.12, delay: 0.15)
}
let verticalDuration: Double = ChatMessageTransitionNodeImpl.animationDuration

View file

@ -60,7 +60,7 @@ final class WebSearchGalleryControllerPresentationArguments {
}
class WebSearchGalleryController: ViewController {
private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentForegroundColor: .black)
private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentDisabledButtonColor: .white, accentForegroundColor: .black)
private var galleryNode: GalleryControllerNode {
return self.displayNode as! GalleryControllerNode

View file

@ -3801,6 +3801,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
badgeStrokeColor: .clear,
badgeTextColor: .clear,
accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor,
accentDisabledButtonColor: self.presentationData.theme.chat.inputPanel.panelControlDisabledColor,
accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor
),
strings: NavigationBarStrings(back: "", close: "")

View file

@ -1,5 +1,5 @@
{
"app": "12.6.2",
"app": "12.6.3",
"xcode": "26.2",
"deploy_xcode": "26.2",
"bazel": "8.4.2:45e9388abf21d1107e146ea366ad080eb93cb6a5f3a4a3b048f78de0bc3faffa",