Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2022-09-14 23:37:38 +03:00
commit 26d6e52a10
43 changed files with 1346 additions and 299 deletions

View File

@ -1958,7 +1958,7 @@ ios_application(
}), }),
watch_application = select({ watch_application = select({
":disableExtensionsSetting": None, ":disableExtensionsSetting": None,
"//conditions:default": ":TelegramWatchApp", "//conditions:default": None,#":TelegramWatchApp",
}) if telegram_enable_watch else None, }) if telegram_enable_watch else None,
deps = [ deps = [
":Main", ":Main",

View File

@ -1537,7 +1537,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isFake { } else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor) currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor)
} }
@ -1553,7 +1553,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isFake { } else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor) currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor)
} }

View File

@ -280,6 +280,29 @@ public struct Transition {
} }
} }
public func setBoundsSize(view: UIView, size: CGSize, completion: ((Bool) -> Void)? = nil) {
if view.bounds.size == size {
completion?(true)
return
}
switch self.animation {
case .none:
view.bounds.size = size
view.layer.removeAnimation(forKey: "bounds.size")
completion?(true)
case .curve:
let previousBounds: CGRect
if view.layer.animation(forKey: "bounds.size") != nil, let presentation = view.layer.presentation() {
previousBounds = presentation.bounds
} else {
previousBounds = view.layer.bounds
}
view.bounds = CGRect(origin: view.bounds.origin, size: size)
self.animateBoundsSize(view: view, from: previousBounds.size, to: size, completion: completion)
}
}
public func setPosition(view: UIView, position: CGPoint, completion: ((Bool) -> Void)? = nil) { public func setPosition(view: UIView, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if view.center == position { if view.center == position {
completion?(true) completion?(true)
@ -552,6 +575,10 @@ public struct Transition {
self.animateBoundsOrigin(layer: view.layer, from: fromValue, to: toValue, additive: additive, completion: completion) self.animateBoundsOrigin(layer: view.layer, from: fromValue, to: toValue, additive: additive, completion: completion)
} }
public func animateBoundsSize(view: UIView, from fromValue: CGSize, to toValue: CGSize, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
self.animateBoundsSize(layer: view.layer, from: fromValue, to: toValue, additive: additive, completion: completion)
}
public func animatePosition(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { public func animatePosition(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self.animation { switch self.animation {
case .none: case .none:
@ -609,6 +636,25 @@ public struct Transition {
} }
} }
public func animateBoundsSize(layer: CALayer, from fromValue: CGSize, to toValue: CGSize, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self.animation {
case .none:
break
case let .curve(duration, curve):
layer.animate(
from: NSValue(cgSize: fromValue),
to: NSValue(cgSize: toValue),
keyPath: "bounds.size",
duration: duration,
delay: 0.0,
curve: curve,
removeOnCompletion: true,
additive: additive,
completion: completion
)
}
}
public func setCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)? = nil) { public func setCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)? = nil) {
if layer.cornerRadius == cornerRadius { if layer.cornerRadius == cornerRadius {
return return

View File

@ -44,13 +44,16 @@ public final class PagerComponentChildEnvironment: Equatable {
public let containerInsets: UIEdgeInsets public let containerInsets: UIEdgeInsets
public let onChildScrollingUpdate: (ContentScrollingUpdate) -> Void public let onChildScrollingUpdate: (ContentScrollingUpdate) -> Void
public let onWantsExclusiveModeUpdated: (Bool) -> Void
init( init(
containerInsets: UIEdgeInsets, containerInsets: UIEdgeInsets,
onChildScrollingUpdate: @escaping (ContentScrollingUpdate) -> Void onChildScrollingUpdate: @escaping (ContentScrollingUpdate) -> Void,
onWantsExclusiveModeUpdated: @escaping (Bool) -> Void
) { ) {
self.containerInsets = containerInsets self.containerInsets = containerInsets
self.onChildScrollingUpdate = onChildScrollingUpdate self.onChildScrollingUpdate = onChildScrollingUpdate
self.onWantsExclusiveModeUpdated = onWantsExclusiveModeUpdated
} }
public static func ==(lhs: PagerComponentChildEnvironment, rhs: PagerComponentChildEnvironment) -> Bool { public static func ==(lhs: PagerComponentChildEnvironment, rhs: PagerComponentChildEnvironment) -> Bool {
@ -180,6 +183,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
public let bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>? public let bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?
public let panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)? public let panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)?
public let isTopPanelExpandedUpdated: (Bool, Transition) -> Void public let isTopPanelExpandedUpdated: (Bool, Transition) -> Void
public let isTopPanelHiddenUpdated: (Bool, Transition) -> Void
public let panelHideBehavior: PagerComponentPanelHideBehavior public let panelHideBehavior: PagerComponentPanelHideBehavior
public init( public init(
@ -196,6 +200,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?, bottomPanel: AnyComponent<PagerComponentPanelEnvironment<TopPanelEnvironment>>?,
panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)?, panelStateUpdated: ((PagerComponentPanelState, Transition) -> Void)?,
isTopPanelExpandedUpdated: @escaping (Bool, Transition) -> Void, isTopPanelExpandedUpdated: @escaping (Bool, Transition) -> Void,
isTopPanelHiddenUpdated: @escaping (Bool, Transition) -> Void,
panelHideBehavior: PagerComponentPanelHideBehavior panelHideBehavior: PagerComponentPanelHideBehavior
) { ) {
self.contentInsets = contentInsets self.contentInsets = contentInsets
@ -211,6 +216,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.bottomPanel = bottomPanel self.bottomPanel = bottomPanel
self.panelStateUpdated = panelStateUpdated self.panelStateUpdated = panelStateUpdated
self.isTopPanelExpandedUpdated = isTopPanelExpandedUpdated self.isTopPanelExpandedUpdated = isTopPanelExpandedUpdated
self.isTopPanelHiddenUpdated = isTopPanelHiddenUpdated
self.panelHideBehavior = panelHideBehavior self.panelHideBehavior = panelHideBehavior
} }
@ -255,6 +261,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var scrollingPanelOffsetToTopEdge: CGFloat = 0.0 var scrollingPanelOffsetToTopEdge: CGFloat = 0.0
var scrollingPanelOffsetToBottomEdge: CGFloat = .greatestFiniteMagnitude var scrollingPanelOffsetToBottomEdge: CGFloat = .greatestFiniteMagnitude
var scrollingPanelOffsetFraction: CGFloat = 0.0 var scrollingPanelOffsetFraction: CGFloat = 0.0
var wantsExclusiveMode: Bool = false
init(view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>) { init(view: ComponentHostView<(ChildEnvironmentType, PagerComponentChildEnvironment)>) {
self.view = view self.view = view
@ -431,9 +438,12 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var contentInsets = component.contentInsets var contentInsets = component.contentInsets
contentInsets.bottom = 0.0 contentInsets.bottom = 0.0
var contentInsetTopPanelValue: CGFloat = 0.0
var scrollingPanelOffsetFraction: CGFloat var scrollingPanelOffsetFraction: CGFloat
if case .show = component.panelHideBehavior { if let centralId = centralId, let centralContentView = self.contentViews[centralId], centralContentView.wantsExclusiveMode {
scrollingPanelOffsetFraction = 1.0
} else if case .show = component.panelHideBehavior {
scrollingPanelOffsetFraction = 0.0 scrollingPanelOffsetFraction = 0.0
} else if let centralId = centralId, let centralContentView = self.contentViews[centralId] { } else if let centralId = centralId, let centralContentView = self.contentViews[centralId] {
scrollingPanelOffsetFraction = centralContentView.scrollingPanelOffsetFraction scrollingPanelOffsetFraction = centralContentView.scrollingPanelOffsetFraction
@ -514,6 +524,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
panelStateTransition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelOffset), size: topPanelSize)) panelStateTransition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelOffset), size: topPanelSize))
} }
contentInsetTopPanelValue = topPanelSize.height
contentInsets.top += topPanelSize.height contentInsets.top += topPanelSize.height
} else { } else {
if let topPanelView = self.topPanelView { if let topPanelView = self.topPanelView {
@ -668,13 +679,25 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
} }
let childContentInsets = contentInsets
if contentView.wantsExclusiveMode {
let _ = contentInsetTopPanelValue
//childContentInsets.top -= contentInsetTopPanelValue
}
let pagerChildEnvironment = PagerComponentChildEnvironment( let pagerChildEnvironment = PagerComponentChildEnvironment(
containerInsets: contentInsets, containerInsets: childContentInsets,
onChildScrollingUpdate: { [weak self] update in onChildScrollingUpdate: { [weak self] update in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.onChildScrollingUpdate(id: id, update: update) strongSelf.onChildScrollingUpdate(id: id, update: update)
},
onWantsExclusiveModeUpdated: { [weak self] wantsExclusiveMode in
guard let strongSelf = self else {
return
}
strongSelf.onChildWantsExclusiveModeUpdated(id: id, wantsExclusiveMode: wantsExclusiveMode)
} }
) )
@ -810,6 +833,18 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
} }
} }
private func onChildWantsExclusiveModeUpdated(id: AnyHashable, wantsExclusiveMode: Bool) {
guard let contentView = self.contentViews[id] else {
return
}
if contentView.wantsExclusiveMode != wantsExclusiveMode {
contentView.wantsExclusiveMode = wantsExclusiveMode
//self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
self.component?.isTopPanelHiddenUpdated(wantsExclusiveMode, Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
}
private func isTopPanelExpandedUpdated(isExpanded: Bool, transition: Transition) { private func isTopPanelExpandedUpdated(isExpanded: Bool, transition: Transition) {
if self.isTopPanelExpanded == isExpanded { if self.isTopPanelExpanded == isExpanded {
return return

View File

@ -650,7 +650,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified { } else if peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor)
} }

View File

@ -1473,41 +1473,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
} }
} }
func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) { func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, reducedCurve: Bool, completion: @escaping () -> Void) {
if let presentationNode = self.presentationNode { if let presentationNode = self.presentationNode {
presentationNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, completion: completion) presentationNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, reducedCurve: reducedCurve, completion: completion)
return
} }
guard let reactionContextNode = self.reactionContextNode else {
self.animateOut(result: .default, completion: completion)
return
}
var contentCompleted = false
var reactionCompleted = false
let intermediateCompletion: () -> Void = {
if contentCompleted && reactionCompleted {
completion()
}
}
self.reactionContextNodeIsAnimatingOut = true
reactionContextNode.willAnimateOutToReaction(value: value)
reactionContextNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, completion: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.reactionContextNode?.removeFromSupernode()
strongSelf.reactionContextNode = nil
reactionCompleted = true
intermediateCompletion()
})
self.animateOut(result: .default, completion: {
contentCompleted = true
intermediateCompletion()
})
self.isUserInteractionEnabled = false
} }
@ -2521,6 +2490,8 @@ public final class ContextController: ViewController, StandalonePresentableContr
private var animatedDidAppear = false private var animatedDidAppear = false
private var wasDismissed = false private var wasDismissed = false
private var dismissOnInputClose: (result: ContextMenuActionResult, completion: (() -> Void)?)?
private var dismissToReactionOnInputClose: (value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: (() -> Void)?)?
override public var overlayWantsToBeBelowKeyboard: Bool { override public var overlayWantsToBeBelowKeyboard: Bool {
if self.isNodeLoaded { if self.isNodeLoaded {
@ -2669,6 +2640,20 @@ public final class ContextController: ViewController, StandalonePresentableContr
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.updateLayout(layout: layout, transition: transition, previousActionsContainerNode: nil) self.controllerNode.updateLayout(layout: layout, transition: transition, previousActionsContainerNode: nil)
if (layout.inputHeight ?? 0.0) == 0.0 {
if let dismissOnInputClose = self.dismissOnInputClose {
self.dismissOnInputClose = nil
DispatchQueue.main.async {
self.dismiss(result: dismissOnInputClose.result, completion: dismissOnInputClose.completion)
}
} else if let args = self.dismissToReactionOnInputClose {
self.dismissToReactionOnInputClose = nil
DispatchQueue.main.async {
self.dismissWithReactionImpl(value: args.value, targetView: args.targetView, hideNode: args.hideNode, animateTargetContainer: args.animateTargetContainer, addStandaloneReactionAnimation: args.addStandaloneReactionAnimation, reducedCurve: true, completion: args.completion)
}
}
}
} }
override public func viewDidAppear(_ animated: Bool) { override public func viewDidAppear(_ animated: Bool) {
@ -2727,8 +2712,15 @@ public final class ContextController: ViewController, StandalonePresentableContr
} }
private func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) { private func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) {
if viewTreeContainsFirstResponder(view: self.view) {
self.dismissOnInputClose = (result, completion)
self.view.endEditing(true)
return
}
if !self.wasDismissed { if !self.wasDismissed {
self.wasDismissed = true self.wasDismissed = true
self.controllerNode.animateOut(result: result, completion: { [weak self] in self.controllerNode.animateOut(result: result, completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil) self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?() completion?()
@ -2751,9 +2743,19 @@ public final class ContextController: ViewController, StandalonePresentableContr
} }
public func dismissWithReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: (() -> Void)?) { public func dismissWithReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: (() -> Void)?) {
self.dismissWithReactionImpl(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, reducedCurve: false, completion: completion)
}
private func dismissWithReactionImpl(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, reducedCurve: Bool, completion: (() -> Void)?) {
if viewTreeContainsFirstResponder(view: self.view) {
self.dismissToReactionOnInputClose = (value, targetView, hideNode, animateTargetContainer, addStandaloneReactionAnimation, completion)
self.view.endEditing(true)
return
}
if !self.wasDismissed { if !self.wasDismissed {
self.wasDismissed = true self.wasDismissed = true
self.controllerNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, completion: { [weak self] in self.controllerNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, animateTargetContainer: animateTargetContainer, addStandaloneReactionAnimation: addStandaloneReactionAnimation, reducedCurve: reducedCurve, completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil) self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?() completion?()
}) })

View File

@ -720,7 +720,15 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
reactionContextNodeTransition = .immediate reactionContextNodeTransition = .immediate
} }
reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true) reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: contentRect.offsetBy(dx: contentParentGlobalFrame.minX, dy: 0.0), isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition)
var reactionAnchorRect = contentRect.offsetBy(dx: contentParentGlobalFrame.minX, dy: 0.0)
let bottomInset = layout.insets(options: [.input]).bottom
if reactionAnchorRect.minY > layout.size.height - bottomInset {
reactionAnchorRect.origin.y = layout.size.height - bottomInset
}
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: reactionAnchorRect, isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition)
self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - 46.0 self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - 46.0
} else { } else {
@ -1145,7 +1153,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
} }
} }
func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) { func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, reducedCurve: Bool, completion: @escaping () -> Void) {
guard let reactionContextNode = self.reactionContextNode else { guard let reactionContextNode = self.reactionContextNode else {
self.requestAnimateOut(.default, completion) self.requestAnimateOut(.default, completion)
return return
@ -1162,7 +1170,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
self.reactionContextNodeIsAnimatingOut = true self.reactionContextNodeIsAnimatingOut = true
reactionContextNode.willAnimateOutToReaction(value: value) reactionContextNode.willAnimateOutToReaction(value: value)
self.requestAnimateOut(.default, { let result: ContextMenuActionResult
if reducedCurve {
result = .custom(.animated(duration: 0.5, curve: .spring))
} else {
result = .default
}
self.requestAnimateOut(result, {
contentCompleted = true contentCompleted = true
intermediateCompletion() intermediateCompletion()
}) })

View File

@ -26,7 +26,7 @@ protocol ContextControllerPresentationNode: ASDisplayNode {
stateTransition: ContextControllerPresentationNodeStateTransition? stateTransition: ContextControllerPresentationNodeStateTransition?
) )
func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) func animateOutToReaction(value: MessageReaction.Reaction, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, reducedCurve: Bool, completion: @escaping () -> Void)
func cancelReactionAnimation() func cancelReactionAnimation()
func highlightGestureMoved(location: CGPoint, hover: Bool) func highlightGestureMoved(location: CGPoint, hover: Bool)

View File

@ -408,6 +408,7 @@ open class NavigationController: UINavigationController, ContainableController,
} else { } else {
if let statusBarHost = self.statusBarHost, let keyboardWindow = statusBarHost.keyboardWindow, let keyboardView = statusBarHost.keyboardView, !keyboardView.frame.height.isZero, isViewVisibleInHierarchy(keyboardView) { if let statusBarHost = self.statusBarHost, let keyboardWindow = statusBarHost.keyboardWindow, let keyboardView = statusBarHost.keyboardView, !keyboardView.frame.height.isZero, isViewVisibleInHierarchy(keyboardView) {
if globalOverlayContainerParent.view.superview != keyboardWindow { if globalOverlayContainerParent.view.superview != keyboardWindow {
globalOverlayContainerParent.layer.zPosition = 1000.0
keyboardWindow.addSubnode(globalOverlayContainerParent) keyboardWindow.addSubnode(globalOverlayContainerParent)
} }
} else if globalOverlayContainerParent.view.superview !== self.displayNode.view { } else if globalOverlayContainerParent.view.superview !== self.displayNode.view {

View File

@ -648,7 +648,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
} else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus { } else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if item.peer.isVerified { } else if item.peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled { } else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor)
} }

View File

@ -234,6 +234,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation? private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation?
private var emojiContentDisposable: Disposable? private var emojiContentDisposable: Disposable?
private let emojiSearchDisposable = MetaDisposable()
private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable)?>(nil)
private var horizontalExpandRecognizer: UIPanGestureRecognizer? private var horizontalExpandRecognizer: UIPanGestureRecognizer?
private var horizontalExpandStartLocation: CGPoint? private var horizontalExpandStartLocation: CGPoint?
private var horizontalExpandDistance: CGFloat = 0.0 private var horizontalExpandDistance: CGFloat = 0.0
@ -249,6 +252,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private var genericReactionEffectDisposable: Disposable? private var genericReactionEffectDisposable: Disposable?
private var genericReactionEffect: String? private var genericReactionEffect: String?
private var isReactionSearchActive: Bool = false
public static func randomGenericReactionEffect(context: AccountContext) -> Signal<String?, NoError> { public static func randomGenericReactionEffect(context: AccountContext) -> Signal<String?, NoError> {
return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false) return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false)
|> map { result -> [TelegramMediaFile]? in |> map { result -> [TelegramMediaFile]? in
@ -404,12 +409,19 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
}) })
if let getEmojiContent = getEmojiContent { if let getEmojiContent = getEmojiContent {
self.emojiContentDisposable = (getEmojiContent(self.animationCache, self.animationRenderer) self.emojiContentDisposable = combineLatest(queue: .mainQueue(),
|> deliverOnMainQueue).start(next: { [weak self] emojiContent in getEmojiContent(self.animationCache, self.animationRenderer),
self.emojiSearchResult.get()
).start(next: { [weak self] emojiContent, emojiSearchResult in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
var emojiContent = emojiContent
if let emojiSearchResult = emojiSearchResult {
emojiContent = emojiContent.withUpdatedItemGroups(itemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id)
}
strongSelf.emojiContent = emojiContent strongSelf.emojiContent = emojiContent
if !strongSelf.canBeExpanded { if !strongSelf.canBeExpanded {
strongSelf.canBeExpanded = true strongSelf.canBeExpanded = true
@ -436,7 +448,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
deviceMetrics: DeviceMetrics.iPhone13, deviceMetrics: DeviceMetrics.iPhone13,
emojiContent: emojiContent, emojiContent: emojiContent,
backgroundColor: .clear, backgroundColor: .clear,
separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5) separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
hideTopPanel: strongSelf.isReactionSearchActive,
hideTopPanelUpdated: { hideTopPanel, transition in
guard let strongSelf = self else {
return
}
strongSelf.isReactionSearchActive = hideTopPanel
strongSelf.requestLayout(transition.containedViewLayoutTransition)
}
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: componentView.bounds.width, height: 300.0) containerSize: CGSize(width: componentView.bounds.width, height: 300.0)
@ -456,6 +476,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.availableReactionsDisposable?.dispose() self.availableReactionsDisposable?.dispose()
self.hasPremiumDisposable?.dispose() self.hasPremiumDisposable?.dispose()
self.genericReactionEffectDisposable?.dispose() self.genericReactionEffectDisposable?.dispose()
self.emojiSearchDisposable.dispose()
} }
override public func didLoad() { override public func didLoad() {
@ -830,7 +851,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
expandItemSize = 30.0 expandItemSize = 30.0
expandTintOffset = 0.0 expandTintOffset = 0.0
} }
let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance)) let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance))
transition.updateFrame(view: expandItemView, frame: baseNextFrame) transition.updateFrame(view: expandItemView, frame: baseNextFrame)
transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame.offsetBy(dx: 0.0, dy: expandTintOffset)) transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame.offsetBy(dx: 0.0, dy: expandTintOffset))
@ -931,7 +952,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
visibleItemCount: itemCount visibleItemCount: itemCount
) )
var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0), size: actualBackgroundFrame.size) var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0) : 0.0), size: actualBackgroundFrame.size)
scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0) scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0)
transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true) transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true)
@ -978,7 +999,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
deviceMetrics: DeviceMetrics.iPhone13, deviceMetrics: DeviceMetrics.iPhone13,
emojiContent: emojiContent, emojiContent: emojiContent,
backgroundColor: .clear, backgroundColor: .clear,
separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5) separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
hideTopPanel: self.isReactionSearchActive,
hideTopPanelUpdated: { [weak self] hideTopPanel, transition in
guard let strongSelf = self else {
return
}
strongSelf.isReactionSearchActive = hideTopPanel
strongSelf.requestLayout(transition.containedViewLayoutTransition)
}
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: actualBackgroundFrame.width, height: 300.0) containerSize: CGSize(width: actualBackgroundFrame.width, height: 300.0)
@ -1023,7 +1052,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
if let mirrorContentClippingView = emojiView.mirrorContentClippingView { if let mirrorContentClippingView = emojiView.mirrorContentClippingView {
mirrorContentClippingView.clipsToBounds = false mirrorContentClippingView.clipsToBounds = false
Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0 + 54.0 - 4.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in
mirrorContentClippingView?.clipsToBounds = true mirrorContentClippingView?.clipsToBounds = true
}) })
} }
@ -1053,7 +1082,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height)))
if animateIn { if animateIn {
transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0 + 54.0 - 4.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0)))
} }
} }
} }
@ -1247,7 +1276,116 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} }
strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition) strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition)
}, },
sendSticker: nil, updateSearchQuery: { [weak self] query in
guard let strongSelf = self else {
return
}
if query.isEmpty {
strongSelf.emojiSearchDisposable.set(nil)
strongSelf.emojiSearchResult.set(.single(nil))
} else {
let context = strongSelf.context
let languageCode = "en"
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2)
if !languageCode.lowercased().hasPrefix("en") {
signal = signal
|> mapToSignal { keywords in
return .single(keywords)
|> then(
context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3)
|> map { englishKeywords in
return keywords + englishKeywords
}
)
}
}
let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|> map { peer -> Bool in
guard case let .user(user) = peer else {
return false
}
return user.isPremium
}
|> distinctUntilChanged
let resultSignal = signal
|> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in
return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
hasPremium
)
|> map { view, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in
var result: [(String, TelegramMediaFile?, String)] = []
var allEmoticons: [String: String] = [:]
for keyword in keywords {
for emoticon in keyword.emoticons {
allEmoticons[emoticon] = keyword.keyword
}
}
for entry in view.entries {
guard let item = entry.item as? StickerPackItem else {
continue
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
if !alt.isEmpty, let keyword = allEmoticons[alt] {
if !item.file.isPremiumEmoji || hasPremium {
result.append((alt, item.file, keyword))
}
}
default:
break
}
}
}
for keyword in keywords {
for emoticon in keyword.emoticons {
result.append((emoticon, nil, keyword.keyword))
}
}
var items: [EmojiPagerContentComponent.Item] = []
var existingIds = Set<MediaId>()
for item in result {
if let itemFile = item.1 {
if existingIds.contains(itemFile.fileId) {
continue
}
existingIds.insert(itemFile.fileId)
let animationData = EntityKeyboardAnimationData(file: itemFile)
let item = EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: itemFile, subgroupId: nil,
icon: .none,
accentTint: false
)
items.append(item)
}
}
return [EmojiPagerContentComponent.ItemGroup(
supergroupId: "search", groupId: "search", title: nil, subtitle: nil, actionButtonTitle: nil, isFeatured: false, isPremiumLocked: false, isEmbedded: false, hasClear: false, collapsedLineCount: nil, displayPremiumBadges: false, headerItem: nil, items: items)]
}
}
strongSelf.emojiSearchDisposable.set((resultSignal
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
strongSelf.emojiSearchResult.set(.single((result, AnyHashable(query))))
}))
}
},
chatPeerId: nil, chatPeerId: nil,
peekBehavior: nil, peekBehavior: nil,
customLayout: emojiContentLayout, customLayout: emojiContentLayout,

View File

@ -843,7 +843,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
} else if let user = item.peer as? TelegramUser, let emojiStatus = user.emojiStatus { } else if let user = item.peer as? TelegramUser, let emojiStatus = user.emojiStatus {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if item.peer.isVerified { } else if item.peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled { } else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor)
} }

View File

@ -47,9 +47,7 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M
for user in apiUsers { for user in apiUsers {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
if telegramUser.id == peerId, let accessHash = telegramUser.accessHash, accessHash.value != 0 { if telegramUser.id == peerId, let accessHash = telegramUser.accessHash, accessHash.value != 0 {
if let presence = TelegramUserPresence(apiUser: user) { updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: [telegramUser.id: user])
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: [telegramUser.id: presence])
}
updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in
return updated return updated

View File

@ -3170,7 +3170,7 @@ func replayFinalState(
} }
} }
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: presences) updatePeerPresencesClean(transaction: transaction, accountPeerId: accountPeerId, peerPresences: presences)
case let .UpdateSecretChat(chat, _): case let .UpdateSecretChat(chat, _):
updateSecretChat(encryptionProvider: encryptionProvider, accountPeerId: accountPeerId, transaction: transaction, mediaBox: mediaBox, chat: chat, requestData: nil) updateSecretChat(encryptionProvider: encryptionProvider, accountPeerId: accountPeerId, transaction: transaction, mediaBox: mediaBox, chat: chat, requestData: nil)
case let .AddSecretMessages(messages): case let .AddSecretMessages(messages):

View File

@ -105,7 +105,7 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -114,9 +114,7 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
for apiUser in users { for apiUser in users {
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
peers.append(user) peers.append(user)
if let presence = TelegramUserPresence(apiUser: apiUser) { peerPresences[user.id] = apiUser
peerPresences[user.id] = presence
}
} }
} }
@ -696,16 +694,14 @@ public final class AccountViewTracker {
return account.postbox.transaction { transaction -> [MessageId: ViewCountContextState] in return account.postbox.transaction { transaction -> [MessageId: ViewCountContextState] in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
var resultStates: [MessageId: ViewCountContextState] = [:] var resultStates: [MessageId: ViewCountContextState] = [:]
for apiUser in users { for apiUser in users {
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
peers.append(user) peers.append(user)
if let presence = TelegramUserPresence(apiUser: apiUser) { peerPresences[user.id] = apiUser
peerPresences[user.id] = presence
}
} }
} }
for chat in chats { for chat in chats {
@ -1086,7 +1082,7 @@ public final class AccountViewTracker {
|> mapToSignal { messages, chats, users -> Signal<Void, NoError> in |> mapToSignal { messages, chats, users -> Signal<Void, NoError> in
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
@ -1096,9 +1092,7 @@ public final class AccountViewTracker {
for apiUser in users { for apiUser in users {
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
peers.append(user) peers.append(user)
if let presence = TelegramUserPresence(apiUser: apiUser) { peerPresences[user.id] = apiUser
peerPresences[user.id] = presence
}
} }
} }

View File

@ -400,7 +400,7 @@ private func updateContactPresences(postbox: Postbox, network: Network, accountP
peerPresences[PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))] = TelegramUserPresence(apiStatus: status) peerPresences[PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))] = TelegramUserPresence(apiStatus: status)
} }
} }
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) updatePeerPresencesClean(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
} }
|> ignoreValues |> ignoreValues
} }

View File

@ -13,7 +13,7 @@ enum FetchChatListLocation {
struct ParsedDialogs { struct ParsedDialogs {
let itemIds: [PeerId] let itemIds: [PeerId]
let peers: [Peer] let peers: [Peer]
let peerPresences: [PeerId: PeerPresence] let peerPresences: [PeerId: Api.User]
let notificationSettings: [PeerId: PeerNotificationSettings] let notificationSettings: [PeerId: PeerNotificationSettings]
let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] let readStates: [PeerId: [MessageId.Namespace: PeerReadState]]
@ -61,7 +61,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
var itemIds: [PeerId] = [] var itemIds: [PeerId] = []
var peers: [PeerId: Peer] = [:] var peers: [PeerId: Peer] = [:]
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in apiChats { for chat in apiChats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers[groupOrChannel.id] = groupOrChannel peers[groupOrChannel.id] = groupOrChannel
@ -70,9 +70,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
for user in apiUsers { for user in apiUsers {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers[telegramUser.id] = telegramUser peers[telegramUser.id] = telegramUser
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for dialog in apiDialogs { for dialog in apiDialogs {
@ -191,7 +189,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
struct FetchedChatList { struct FetchedChatList {
let chatPeerIds: [PeerId] let chatPeerIds: [PeerId]
let peers: [Peer] let peers: [Peer]
let peerPresences: [PeerId: PeerPresence] let peerPresences: [PeerId: Api.User]
let notificationSettings: [PeerId: PeerNotificationSettings] let notificationSettings: [PeerId: PeerNotificationSettings]
let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] let readStates: [PeerId: [MessageId.Namespace: PeerReadState]]
let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary]
@ -297,7 +295,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
return combineLatest(folderSignals) return combineLatest(folderSignals)
|> mapToSignal { folders -> Signal<FetchedChatList?, NoError> in |> mapToSignal { folders -> Signal<FetchedChatList?, NoError> in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]

View File

@ -566,7 +566,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -575,9 +575,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
var storeMessages: [StoreMessage] = [] var storeMessages: [StoreMessage] = []
@ -838,7 +836,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId
transaction.replaceGlobalMessageTagsHole(globalTags: [.Calls, .MissedCalls], index: holeIndex, with: updatedIndex, messages: storeMessages) transaction.replaceGlobalMessageTagsHole(globalTags: [.Calls, .MissedCalls], index: holeIndex, with: updatedIndex, messages: storeMessages)
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -847,9 +845,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId
for user in users { for user in users {
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
} }

View File

@ -136,7 +136,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox,
var remoteItemIds: [PinnedItemId] = [] var remoteItemIds: [PinnedItemId] = []
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
switch dialogs { switch dialogs {
case let .peerDialogs(dialogs, messages, chats, users, _): case let .peerDialogs(dialogs, messages, chats, users, _):
@ -148,9 +148,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox,
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
loop: for dialog in dialogs { loop: for dialog in dialogs {

View File

@ -480,14 +480,12 @@ public final class EngineMessageReactionListContext {
switch result { switch result {
case let .messageReactionsList(_, count, reactions, chats, users, nextOffset): case let .messageReactionsList(_, count, reactions, chats, users, nextOffset):
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for chat in chats { for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) { if let peer = parseTelegramGroupOrChannel(chat: chat) {

View File

@ -97,14 +97,12 @@ func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash:
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for chat in chats { for chat in chats {
@ -383,14 +381,12 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for chat in chats { for chat in chats {
@ -596,14 +592,12 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
state.adminIds = Set(peerAdminIds) state.adminIds = Set(peerAdminIds)
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in apiUsers { for user in apiUsers {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
let connectionMode: JoinGroupCallResult.ConnectionMode let connectionMode: JoinGroupCallResult.ConnectionMode

View File

@ -73,7 +73,7 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId)
transaction.replaceRemoteContactCount(totalCount) transaction.replaceRemoteContactCount(totalCount)
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) updatePeerPresencesClean(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
if wasEmpty { if wasEmpty {
var insertSignal: Signal<Void, NoError> = .complete() var insertSignal: Signal<Void, NoError> = .complete()

View File

@ -400,7 +400,7 @@ private class AdMessagesHistoryContextImpl {
switch result { switch result {
case let .sponsoredMessages(messages, chats, users): case let .sponsoredMessages(messages, chats, users):
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
@ -410,9 +410,7 @@ private class AdMessagesHistoryContextImpl {
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in

View File

@ -13,7 +13,7 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network,
switch updates { switch updates {
case let .updates(updates, users, chats, _, _): case let .updates(updates, users, chats, _, _):
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -22,9 +22,7 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network,
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for update in updates { for update in updates {
switch update { switch update {

View File

@ -94,7 +94,7 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -103,9 +103,7 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in

View File

@ -171,7 +171,7 @@ private class ReplyThreadHistoryContextImpl {
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
@ -181,9 +181,7 @@ private class ReplyThreadHistoryContextImpl {
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
let _ = transaction.addMessages(parsedMessages, location: .Random) let _ = transaction.addMessages(parsedMessages, location: .Random)
@ -608,7 +606,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
@ -618,9 +616,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
let _ = transaction.addMessages(parsedMessages, location: .Random) let _ = transaction.addMessages(parsedMessages, location: .Random)

View File

@ -925,7 +925,7 @@ public final class SparseMessageCalendar {
case let .searchResultsCalendar(_, _, minDate, minMsgId, _, periods, messages, chats, users): case let .searchResultsCalendar(_, _, minDate, minMsgId, _, periods, messages, chats, users):
var parsedMessages: [StoreMessage] = [] var parsedMessages: [StoreMessage] = []
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
@ -935,9 +935,7 @@ public final class SparseMessageCalendar {
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
for message in messages { for message in messages {

View File

@ -91,13 +91,11 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId:
switch result { switch result {
case let .channelParticipants(_, participants, chats, users): case let .channelParticipants(_, participants, chats, users):
var peers: [PeerId: Peer] = [:] var peers: [PeerId: Peer] = [:]
var presences: [PeerId: PeerPresence] = [:] var presences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let peer = TelegramUser(user: user) let peer = TelegramUser(user: user)
peers[peer.id] = peer peers[peer.id] = peer
if let presence = TelegramUserPresence(apiUser: user) { presences[peer.id] = user
presences[peer.id] = presence
}
} }
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
@ -111,7 +109,11 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId:
for participant in CachedChannelParticipants(apiParticipants: participants).participants { for participant in CachedChannelParticipants(apiParticipants: participants).participants {
if let peer = peers[participant.peerId] { if let peer = peers[participant.peerId] {
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences)) var renderedPresences: [PeerId: PeerPresence] = [:]
if let presence = transaction.getPeerPresence(peerId: participant.peerId) {
renderedPresences[participant.peerId] = presence
}
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: renderedPresences))
} }
} }
case .channelParticipantsNotModified: case .channelParticipantsNotModified:

View File

@ -633,7 +633,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
var channelStates: [PeerId: Int32] = [:] var channelStates: [PeerId: Int32] = [:]
@ -647,9 +647,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
var topMessageIds = Set<MessageId>() var topMessageIds = Set<MessageId>()

View File

@ -66,13 +66,11 @@ func _internal_managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox
switch result { switch result {
case let .topPeers(_, _, users): case let .topPeers(_, _, users):
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
updatePeers(transaction: transaction, peers: peers, update: { return $1 }) updatePeers(transaction: transaction, peers: peers, update: { return $1 })
@ -158,19 +156,17 @@ func _internal_updateRecentPeersEnabled(postbox: Postbox, network: Network, enab
func _internal_managedRecentlyUsedInlineBots(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> { func _internal_managedRecentlyUsedInlineBots(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
let remotePeers = network.request(Api.functions.contacts.getTopPeers(flags: 1 << 2, offset: 0, limit: 16, hash: 0)) let remotePeers = network.request(Api.functions.contacts.getTopPeers(flags: 1 << 2, offset: 0, limit: 16, hash: 0))
|> retryRequest |> retryRequest
|> map { result -> ([Peer], [PeerId: PeerPresence], [(PeerId, Double)])? in |> map { result -> ([Peer], [PeerId: Api.User], [(PeerId, Double)])? in
switch result { switch result {
case .topPeersDisabled: case .topPeersDisabled:
break break
case let .topPeers(categories, _, users): case let .topPeers(categories, _, users):
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
var peersWithRating: [(PeerId, Double)] = [] var peersWithRating: [(PeerId, Double)] = []
for category in categories { for category in categories {

View File

@ -79,7 +79,7 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP
|> retryRequest |> retryRequest
|> mapToSignal { peerSettings -> Signal<Bool, NoError> in |> mapToSignal { peerSettings -> Signal<Bool, NoError> in
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
let peerStatusSettings: PeerStatusSettings let peerStatusSettings: PeerStatusSettings
switch peerSettings { switch peerSettings {
@ -93,9 +93,7 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
} }
@ -198,7 +196,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
case let .userFull(fullUser, chats, users): case let .userFull(fullUser, chats, users):
var accountUser: Api.User? var accountUser: Api.User?
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) { if let peer = parseTelegramGroupOrChannel(chat: chat) {
peers.append(peer) peers.append(peer)
@ -207,9 +205,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
for user in users { for user in users {
let telegramUser = TelegramUser(user: user) let telegramUser = TelegramUser(user: user)
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
if telegramUser.id == accountPeerId { if telegramUser.id == accountPeerId {
accountUser = user accountUser = user
} }
@ -327,7 +323,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
let pinnedMessageId = chatFullPinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }) let pinnedMessageId = chatFullPinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -336,9 +332,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
for user in users { for user in users {
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
} }
@ -515,7 +509,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
} }
var peers: [Peer] = [] var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:] var peerPresences: [PeerId: Api.User] = [:]
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel) peers.append(groupOrChannel)
@ -524,9 +518,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
for user in users { for user in users {
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
} }
@ -536,9 +528,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
for user in users { for user in users {
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser) peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) { peerPresences[telegramUser.id] = user
peerPresences[telegramUser.id] = presence
}
} }
} }
for chat in chats { for chat in chats {

View File

@ -106,7 +106,40 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
}) })
} }
func updatePeerPresences(transaction: Transaction, accountPeerId: PeerId, peerPresences: [PeerId: PeerPresence]) { func updatePeerPresences(transaction: Transaction, accountPeerId: PeerId, peerPresences: [PeerId: Api.User]) {
var parsedPresences: [PeerId: PeerPresence] = [:]
for (peerId, user) in peerPresences {
guard let presence = TelegramUserPresence(apiUser: user) else {
continue
}
switch presence.status {
case .present:
parsedPresences[peerId] = presence
default:
switch user {
case let .user(flags, _, _, _, _, _, _, _, _, _, _, _, _, _):
let isMin = (flags & (1 << 20)) != 0
if isMin, let _ = transaction.getPeerPresence(peerId: peerId) {
} else {
parsedPresences[peerId] = presence
}
default:
break
}
}
}
parsedPresences.removeValue(forKey: accountPeerId)
transaction.updatePeerPresencesInternal(presences: parsedPresences, merge: { previous, updated in
if let previous = previous as? TelegramUserPresence, let updated = updated as? TelegramUserPresence, previous.lastActivity != updated.lastActivity {
return TelegramUserPresence(status: updated.status, lastActivity: max(previous.lastActivity, updated.lastActivity))
}
return updated
})
}
func updatePeerPresencesClean(transaction: Transaction, accountPeerId: PeerId, peerPresences: [PeerId: PeerPresence]) {
var peerPresences = peerPresences var peerPresences = peerPresences
if peerPresences[accountPeerId] != nil { if peerPresences[accountPeerId] != nil {
peerPresences.removeValue(forKey: accountPeerId) peerPresences.removeValue(forKey: accountPeerId)

View File

@ -37,10 +37,15 @@ public final class EmojiStatusComponent: Component {
case count(Int) case count(Int)
} }
public enum SizeType {
case compact
case large
}
public enum Content: Equatable { public enum Content: Equatable {
case none case none
case premium(color: UIColor) case premium(color: UIColor)
case verified(fillColor: UIColor, foregroundColor: UIColor) case verified(fillColor: UIColor, foregroundColor: UIColor, sizeType: SizeType)
case text(color: UIColor, string: String) case text(color: UIColor, string: String)
case animation(content: AnimationContent, size: CGSize, placeholderColor: UIColor, themeColor: UIColor?, loopMode: LoopMode) case animation(content: AnimationContent, size: CGSize, placeholderColor: UIColor, themeColor: UIColor?, loopMode: LoopMode)
} }
@ -217,8 +222,16 @@ public final class EmojiStatusComponent: Component {
} else { } else {
iconImage = nil iconImage = nil
} }
case let .verified(fillColor, foregroundColor): case let .verified(fillColor, foregroundColor, sizeType):
if let backgroundImage = UIImage(bundleImageName: "Peer Info/VerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Peer Info/VerifiedIconForeground") { let imageNamePrefix: String
switch sizeType {
case .compact:
imageNamePrefix = "Chat List/PeerVerifiedIcon"
case .large:
imageNamePrefix = "Peer Info/VerifiedIcon"
}
if let backgroundImage = UIImage(bundleImageName: "\(imageNamePrefix)Background"), let foregroundImage = UIImage(bundleImageName: "\(imageNamePrefix)Foreground") {
iconImage = generateImage(backgroundImage.size, contextGenerator: { size, context in iconImage = generateImage(backgroundImage.size, contextGenerator: { size, context in
if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage { if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage {
context.clear(CGRect(origin: CGPoint(), size: size)) context.clear(CGRect(origin: CGPoint(), size: size))
@ -342,7 +355,16 @@ public final class EmojiStatusComponent: Component {
} }
iconView.image = iconImage iconView.image = iconImage
if case .text = component.content { var useFit = false
switch component.content {
case .text:
useFit = true
case .verified(_, _, sizeType: .compact):
useFit = true
default:
break
}
if useFit {
size = CGSize(width: iconImage.size.width, height: availableSize.height) size = CGSize(width: iconImage.size.width, height: availableSize.height)
iconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconImage.size.width) / 2.0), y: floor((size.height - iconImage.size.height) / 2.0)), size: iconImage.size) iconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconImage.size.width) / 2.0), y: floor((size.height - iconImage.size.height) / 2.0)), size: iconImage.size)
} else { } else {

View File

@ -64,6 +64,8 @@ public final class EmojiStatusSelectionComponent: Component {
public let emojiContent: EmojiPagerContentComponent public let emojiContent: EmojiPagerContentComponent
public let backgroundColor: UIColor public let backgroundColor: UIColor
public let separatorColor: UIColor public let separatorColor: UIColor
public let hideTopPanel: Bool
public let hideTopPanelUpdated: (Bool, Transition) -> Void
public init( public init(
theme: PresentationTheme, theme: PresentationTheme,
@ -71,7 +73,9 @@ public final class EmojiStatusSelectionComponent: Component {
deviceMetrics: DeviceMetrics, deviceMetrics: DeviceMetrics,
emojiContent: EmojiPagerContentComponent, emojiContent: EmojiPagerContentComponent,
backgroundColor: UIColor, backgroundColor: UIColor,
separatorColor: UIColor separatorColor: UIColor,
hideTopPanel: Bool,
hideTopPanelUpdated: @escaping (Bool, Transition) -> Void
) { ) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -79,6 +83,8 @@ public final class EmojiStatusSelectionComponent: Component {
self.emojiContent = emojiContent self.emojiContent = emojiContent
self.backgroundColor = backgroundColor self.backgroundColor = backgroundColor
self.separatorColor = separatorColor self.separatorColor = separatorColor
self.hideTopPanel = hideTopPanel
self.hideTopPanelUpdated = hideTopPanelUpdated
} }
public static func ==(lhs: EmojiStatusSelectionComponent, rhs: EmojiStatusSelectionComponent) -> Bool { public static func ==(lhs: EmojiStatusSelectionComponent, rhs: EmojiStatusSelectionComponent) -> Bool {
@ -100,6 +106,9 @@ public final class EmojiStatusSelectionComponent: Component {
if lhs.separatorColor != rhs.separatorColor { if lhs.separatorColor != rhs.separatorColor {
return false return false
} }
if lhs.hideTopPanel != rhs.hideTopPanel {
return false
}
return true return true
} }
@ -111,6 +120,7 @@ public final class EmojiStatusSelectionComponent: Component {
private let panelSeparatorView: UIView private let panelSeparatorView: UIView
private var component: EmojiStatusSelectionComponent? private var component: EmojiStatusSelectionComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) { override init(frame: CGRect) {
self.keyboardView = ComponentView<Empty>() self.keyboardView = ComponentView<Empty>()
@ -131,6 +141,9 @@ public final class EmojiStatusSelectionComponent: Component {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit {
}
func update(component: EmojiStatusSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize { func update(component: EmojiStatusSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.backgroundColor = component.backgroundColor self.backgroundColor = component.backgroundColor
let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85) let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85)
@ -138,8 +151,9 @@ public final class EmojiStatusSelectionComponent: Component {
self.panelSeparatorView.backgroundColor = component.separatorColor self.panelSeparatorView.backgroundColor = component.separatorColor
self.component = component self.component = component
self.state = state
let topPanelHeight: CGFloat = 42.0 let topPanelHeight: CGFloat = component.hideTopPanel ? 0.0 : 42.0
let keyboardSize = self.keyboardView.update( let keyboardSize = self.keyboardView.update(
transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
@ -158,6 +172,12 @@ public final class EmojiStatusSelectionComponent: Component {
externalTopPanelContainer: self.panelHostView, externalTopPanelContainer: self.panelHostView,
topPanelExtensionUpdated: { _, _ in }, topPanelExtensionUpdated: { _, _ in },
hideInputUpdated: { _, _, _ in }, hideInputUpdated: { _, _, _ in },
hideTopPanelUpdated: { [weak self] hideTopPanel, transition in
guard let strongSelf = self else {
return
}
strongSelf.component?.hideTopPanelUpdated(hideTopPanel, transition)
},
switchToTextInput: {}, switchToTextInput: {},
switchToGifSubject: { _ in }, switchToGifSubject: { _ in },
reorderItems: { _, _ in }, reorderItems: { _, _ in },
@ -189,7 +209,7 @@ public final class EmojiStatusSelectionComponent: Component {
transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight))) transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight)))
self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition) self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel))) transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: component.hideTopPanel ? -UIScreenPixel : topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel)))
} }
return availableSize return availableSize
@ -343,7 +363,8 @@ public final class EmojiStatusSelectionController: ViewController {
}, },
requestUpdate: { _ in requestUpdate: { _ in
}, },
sendSticker: nil, updateSearchQuery: { _ in
},
chatPeerId: nil, chatPeerId: nil,
peekBehavior: nil, peekBehavior: nil,
customLayout: nil, customLayout: nil,
@ -639,7 +660,9 @@ public final class EmojiStatusSelectionController: ViewController {
deviceMetrics: layout.deviceMetrics, deviceMetrics: layout.deviceMetrics,
emojiContent: emojiContent, emojiContent: emojiContent,
backgroundColor: listBackgroundColor, backgroundColor: listBackgroundColor,
separatorColor: separatorColor separatorColor: separatorColor,
hideTopPanel: false,
hideTopPanelUpdated: { _, _ in }
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: componentWidth, height: min(308.0, layout.size.height)) containerSize: CGSize(width: componentWidth, height: min(308.0, layout.size.height))

View File

@ -99,6 +99,7 @@ public final class EntityKeyboardComponent: Component {
public let externalTopPanelContainer: PagerExternalTopPanelContainer? public let externalTopPanelContainer: PagerExternalTopPanelContainer?
public let topPanelExtensionUpdated: (CGFloat, Transition) -> Void public let topPanelExtensionUpdated: (CGFloat, Transition) -> Void
public let hideInputUpdated: (Bool, Bool, Transition) -> Void public let hideInputUpdated: (Bool, Bool, Transition) -> Void
public let hideTopPanelUpdated: (Bool, Transition) -> Void
public let switchToTextInput: () -> Void public let switchToTextInput: () -> Void
public let switchToGifSubject: (GifPagerContentComponent.Subject) -> Void public let switchToGifSubject: (GifPagerContentComponent.Subject) -> Void
public let reorderItems: (ReorderCategory, [EntityKeyboardTopPanelComponent.Item]) -> Void public let reorderItems: (ReorderCategory, [EntityKeyboardTopPanelComponent.Item]) -> Void
@ -123,6 +124,7 @@ public final class EntityKeyboardComponent: Component {
externalTopPanelContainer: PagerExternalTopPanelContainer?, externalTopPanelContainer: PagerExternalTopPanelContainer?,
topPanelExtensionUpdated: @escaping (CGFloat, Transition) -> Void, topPanelExtensionUpdated: @escaping (CGFloat, Transition) -> Void,
hideInputUpdated: @escaping (Bool, Bool, Transition) -> Void, hideInputUpdated: @escaping (Bool, Bool, Transition) -> Void,
hideTopPanelUpdated: @escaping (Bool, Transition) -> Void,
switchToTextInput: @escaping () -> Void, switchToTextInput: @escaping () -> Void,
switchToGifSubject: @escaping (GifPagerContentComponent.Subject) -> Void, switchToGifSubject: @escaping (GifPagerContentComponent.Subject) -> Void,
reorderItems: @escaping (ReorderCategory, [EntityKeyboardTopPanelComponent.Item]) -> Void, reorderItems: @escaping (ReorderCategory, [EntityKeyboardTopPanelComponent.Item]) -> Void,
@ -146,6 +148,7 @@ public final class EntityKeyboardComponent: Component {
self.externalTopPanelContainer = externalTopPanelContainer self.externalTopPanelContainer = externalTopPanelContainer
self.topPanelExtensionUpdated = topPanelExtensionUpdated self.topPanelExtensionUpdated = topPanelExtensionUpdated
self.hideInputUpdated = hideInputUpdated self.hideInputUpdated = hideInputUpdated
self.hideTopPanelUpdated = hideTopPanelUpdated
self.switchToTextInput = switchToTextInput self.switchToTextInput = switchToTextInput
self.switchToGifSubject = switchToGifSubject self.switchToGifSubject = switchToGifSubject
self.reorderItems = reorderItems self.reorderItems = reorderItems
@ -222,6 +225,7 @@ public final class EntityKeyboardComponent: Component {
private var topPanelExtension: CGFloat? private var topPanelExtension: CGFloat?
private var isTopPanelExpanded: Bool = false private var isTopPanelExpanded: Bool = false
private var isTopPanelHidden: Bool = false
public var centralId: AnyHashable? { public var centralId: AnyHashable? {
if let pagerView = self.pagerView.findTaggedView(tag: PagerComponentViewTag()) as? PagerComponent<EntityKeyboardChildEnvironment, EntityKeyboardTopContainerPanelEnvironment>.View { if let pagerView = self.pagerView.findTaggedView(tag: PagerComponentViewTag()) as? PagerComponent<EntityKeyboardChildEnvironment, EntityKeyboardTopContainerPanelEnvironment>.View {
@ -626,6 +630,12 @@ public final class EntityKeyboardComponent: Component {
} }
strongSelf.isTopPanelExpandedUpdated(isExpanded: isExpanded, transition: transition) strongSelf.isTopPanelExpandedUpdated(isExpanded: isExpanded, transition: transition)
}, },
isTopPanelHiddenUpdated: { [weak self] isTopPanelHidden, transition in
guard let strongSelf = self else {
return
}
strongSelf.isTopPanelHiddenUpdated(isTopPanelHidden: isTopPanelHidden, transition: transition)
},
panelHideBehavior: panelHideBehavior panelHideBehavior: panelHideBehavior
)), )),
environment: { environment: {
@ -727,6 +737,18 @@ public final class EntityKeyboardComponent: Component {
component.hideInputUpdated(self.isTopPanelExpanded, false, transition) component.hideInputUpdated(self.isTopPanelExpanded, false, transition)
} }
private func isTopPanelHiddenUpdated(isTopPanelHidden: Bool, transition: Transition) {
if self.isTopPanelHidden != isTopPanelHidden {
self.isTopPanelHidden = isTopPanelHidden
}
guard let component = self.component else {
return
}
component.hideTopPanelUpdated(self.isTopPanelHidden, transition)
}
private func openSearch() { private func openSearch() {
guard let component = self.component else { guard let component = self.component else {
return return

View File

@ -110,6 +110,10 @@ private class ApplicationStatusBarHost: StatusBarHost {
} }
var keyboardWindow: UIWindow? { var keyboardWindow: UIWindow? {
if #available(iOS 16.0, *) {
return UIApplication.shared.internalGetKeyboard()
}
for window in UIApplication.shared.windows { for window in UIApplication.shared.windows {
if isKeyboardWindow(window: window) { if isKeyboardWindow(window: window) {
return window return window

View File

@ -1237,6 +1237,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
controller?.view.endEditing(true)
let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction
let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? [] let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? []

View File

@ -535,6 +535,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
) )
}, },
itemLayoutType: .detailed, itemLayoutType: .detailed,
itemContentUniqueId: nil,
warpContentsOnEdges: false, warpContentsOnEdges: false,
displaySearch: false, displaySearch: false,
enableLongPress: false, enableLongPress: false,
@ -1120,13 +1121,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
return controllerInteraction?.navigationController() return controllerInteraction?.navigationController()
}, },
requestUpdate: { _ in requestUpdate: { _ in
}, },
sendSticker: { [weak controllerInteraction] fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets in updateSearchQuery: { _ in
guard let controllerInteraction = controllerInteraction else {
return
}
let _ = controllerInteraction.sendSticker(fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets)
}, },
chatPeerId: chatPeerId, chatPeerId: chatPeerId,
peekBehavior: nil, peekBehavior: nil,
@ -1329,13 +1325,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
return controllerInteraction?.navigationController() return controllerInteraction?.navigationController()
}, },
requestUpdate: { _ in requestUpdate: { _ in
}, },
sendSticker: { [weak controllerInteraction] fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets in updateSearchQuery: { _ in
guard let controllerInteraction = controllerInteraction else {
return
}
let _ = controllerInteraction.sendSticker(fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, bubbleUpEmojiOrStickersets)
}, },
chatPeerId: chatPeerId, chatPeerId: chatPeerId,
peekBehavior: stickerPeekBehavior, peekBehavior: stickerPeekBehavior,
@ -1592,6 +1583,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
strongSelf.hideInputUpdated?(transition.containedViewLayoutTransition) strongSelf.hideInputUpdated?(transition.containedViewLayoutTransition)
} }
}, },
hideTopPanelUpdated: { _, _ in
},
switchToTextInput: { [weak self] in switchToTextInput: { [weak self] in
self?.switchToTextInput?() self?.switchToTextInput?()
}, },
@ -1721,9 +1714,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
private func processInputData(inputData: InputData) -> InputData { private func processInputData(inputData: InputData) -> InputData {
return InputData( return InputData(
emoji: inputData.emoji.withUpdatedItemGroups(self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups)), emoji: inputData.emoji.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups), itemContentUniqueId: nil),
stickers: inputData.stickers.flatMap { stickers in stickers: inputData.stickers.flatMap { stickers in
return stickers.withUpdatedItemGroups(self.processStableItemGroupList(category: .stickers, itemGroups: stickers.itemGroups)) return stickers.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.itemGroups), itemContentUniqueId: nil)
}, },
gifs: inputData.gifs, gifs: inputData.gifs,
availableGifSearchEmojies: inputData.availableGifSearchEmojies availableGifSearchEmojies: inputData.availableGifSearchEmojies
@ -2057,10 +2050,10 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV
navigationController: { navigationController: {
return nil return nil
}, },
requestUpdate: { _ in requestUpdate: { _ in
},
updateSearchQuery: { _ in
}, },
sendSticker: nil,
chatPeerId: nil, chatPeerId: nil,
peekBehavior: nil, peekBehavior: nil,
customLayout: nil, customLayout: nil,

View File

@ -673,7 +673,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
case .premium: case .premium:
titleCredibilityContent = .premium(color: self.theme.list.itemAccentColor) titleCredibilityContent = .premium(color: self.theme.list.itemAccentColor)
case .verified: case .verified:
titleCredibilityContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor) titleCredibilityContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
case .fake: case .fake:
titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased()) titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased())
case .scam: case .scam:

View File

@ -2343,8 +2343,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
emojiRegularStatusContent = .premium(color: presentationData.theme.list.itemAccentColor) emojiRegularStatusContent = .premium(color: presentationData.theme.list.itemAccentColor)
emojiExpandedStatusContent = .premium(color: UIColor(rgb: 0xffffff, alpha: 0.75)) emojiExpandedStatusContent = .premium(color: UIColor(rgb: 0xffffff, alpha: 0.75))
case .verified: case .verified:
emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor) emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
emojiExpandedStatusContent = .verified(fillColor: UIColor(rgb: 0xffffff, alpha: 0.75), foregroundColor: .clear) emojiExpandedStatusContent = .verified(fillColor: UIColor(rgb: 0xffffff, alpha: 0.75), foregroundColor: .clear, sizeType: .large)
case .fake: case .fake:
emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased())
emojiExpandedStatusContent = emojiRegularStatusContent emojiExpandedStatusContent = emojiRegularStatusContent

View File

@ -22,6 +22,7 @@ typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) {
- (void)internalSetStatusBarStyle:(UIStatusBarStyle)style animated:(BOOL)animated; - (void)internalSetStatusBarStyle:(UIStatusBarStyle)style animated:(BOOL)animated;
- (void)internalSetStatusBarHidden:(BOOL)hidden animation:(UIStatusBarAnimation)animation; - (void)internalSetStatusBarHidden:(BOOL)hidden animation:(UIStatusBarAnimation)animation;
- (UIWindow * _Nullable)internalGetKeyboard;
@end @end

View File

@ -139,6 +139,12 @@ static bool notyfyingShiftState = false;
@end @end
@protocol UIRemoteKeyboardWindowProtocol
+ (UIWindow * _Nullable)remoteKeyboardWindowForScreen:(UIScreen * _Nullable)screen create:(BOOL)create;
@end
@implementation UIViewController (Navigation) @implementation UIViewController (Navigation)
+ (void)load + (void)load
@ -155,6 +161,8 @@ static bool notyfyingShiftState = false;
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentViewController:animated:completion:) newSelector:@selector(_65087dc8_presentViewController:animated:completion:)]; [RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentViewController:animated:completion:) newSelector:@selector(_65087dc8_presentViewController:animated:completion:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(setNeedsStatusBarAppearanceUpdate) newSelector:@selector(_65087dc8_setNeedsStatusBarAppearanceUpdate)]; [RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(setNeedsStatusBarAppearanceUpdate) newSelector:@selector(_65087dc8_setNeedsStatusBarAppearanceUpdate)];
[RuntimeUtils swizzleClassMethodOfClass:NSClassFromString(@"UIRemoteKeyboardWindow") currentSelector:NSSelectorFromString(@"remoteKeyboardWindowForScreen:create:") newSelector:NSSelectorFromString(@"_65087dc8_remoteKeyboardWindowForScreen:create:")];
if (@available(iOS 15.0, *)) { if (@available(iOS 15.0, *)) {
[RuntimeUtils swizzleInstanceMethodOfClass:[CADisplayLink class] currentSelector:@selector(setPreferredFrameRateRange:) newSelector:@selector(_65087dc8_setPreferredFrameRateRange:)]; [RuntimeUtils swizzleInstanceMethodOfClass:[CADisplayLink class] currentSelector:@selector(setPreferredFrameRateRange:) newSelector:@selector(_65087dc8_setPreferredFrameRateRange:)];
} }
@ -291,6 +299,14 @@ static bool notyfyingShiftState = false;
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
- (UIWindow * _Nullable)internalGetKeyboard {
Class windowClass = NSClassFromString(@"UIRemoteKeyboardWindow");
if (!windowClass) {
return nil;
}
return [(id<UIRemoteKeyboardWindowProtocol>)windowClass remoteKeyboardWindowForScreen:[UIScreen mainScreen] create:false];
}
@end @end
@implementation UIView (Navigation) @implementation UIView (Navigation)