Merge branch 'master' into postbox-refactoring-1

This commit is contained in:
Ali 2021-09-09 23:26:15 +04:00
commit 3f61ff85f0
55 changed files with 958 additions and 289 deletions

Binary file not shown.

View File

@ -6766,11 +6766,16 @@ Sorry for the inconvenience.";
"Channel.AdminLog.MessageChangedThemeRemove" = "%1$@ disabled chat theme"; "Channel.AdminLog.MessageChangedThemeRemove" = "%1$@ disabled chat theme";
"SponsoredMessageMenu.Info" = "What are sponsored\nmessages?"; "SponsoredMessageMenu.Info" = "What are sponsored\nmessages?";
"SponsoredMessageInfo.Text" = "Unlike other apps, Telegram never uses your private data to target ads. No user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message. We believe that everyone has the right to privacy, and technological platforms should respect that. "SponsoredMessageInfoScreen.Title" = "What are sponsored messages?";
"SponsoredMessageInfoScreen.Text" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message.
More at: https://ads.telegram.org"; Unline other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can't spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.
Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible adverticers at:
[url]
Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
"SponsoredMessageInfo.Action" = "Learn More"; "SponsoredMessageInfo.Action" = "Learn More";
"SponsoredMessageInfo.ActionUrl" = "https://telegram.org"; "SponsoredMessageInfo.ActionUrl" = "https://telegram.org/ads";
"Chat.NavigationNoChannels" = "You have no unread channels"; "Chat.NavigationNoChannels" = "You have no unread channels";
@ -6798,3 +6803,14 @@ More at: https://ads.telegram.org";
"Conversation.ContextMenuSeen_1" = "1 Seen"; "Conversation.ContextMenuSeen_1" = "1 Seen";
"Conversation.ContextMenuSeen_any" = "%@ Seen"; "Conversation.ContextMenuSeen_any" = "%@ Seen";
"Conversation.ContextMenuListened_1" = "1 Listened";
"Conversation.ContextMenuListened_any" = "%@ Listened";
"Conversation.ContextMenuWatched_1" = "1 Watched";
"Conversation.ContextMenuWatched_any" = "%@ Watched";
"Conversation.ContextMenuNoViews" = "Nobody Viewed";
"Conversation.ContextMenuNobodyListened" = "Nobody Listened";
"Conversation.ContextMenuNobodyWatched" = "Nobody Watched";
"VideoChat.RecordingSaved" = "Video chat recording saved to **Saved Messages**.";
"LiveStream.RecordingSaved" = "Live stream recording saved to **Saved Messages**.";

25
submodules/AdUI/BUILD Normal file
View File

@ -0,0 +1,25 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "AdUI",
module_name = "AdUI",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/AccountContext:AccountContext",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,223 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
public final class AdInfoScreen: ViewController {
private final class Node: ViewControllerTracingNode {
private weak var controller: AdInfoScreen?
private let context: AccountContext
private var presentationData: PresentationData
private let titleNode: ImmediateTextNode
private final class LinkNode: HighlightableButtonNode {
private let backgroundNode: ASImageNode
private let textNode: ImmediateTextNode
private let action: () -> Void
init(text: String, color: UIColor, action: @escaping () -> Void) {
self.action = action
self.backgroundNode = ASImageNode()
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 10.0, color: nil, strokeColor: color, strokeWidth: 1.0, backgroundColor: nil)
self.textNode = ImmediateTextNode()
self.textNode.maximumNumberOfLines = 1
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: color)
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.textNode)
self.addTarget(self, action:#selector(self.pressed), forControlEvents: .touchUpInside)
}
@objc private func pressed() {
self.action()
}
func update(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
let size = CGSize(width: width, height: 44.0)
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
let textSize = self.textNode.updateLayout(CGSize(width: width - 8.0 * 2.0, height: 44.0))
transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize))
return size.height
}
}
private enum Item {
case text(ImmediateTextNode)
case link(LinkNode)
}
private let items: [Item]
private let scrollNode: ASScrollNode
init(controller: AdInfoScreen, context: AccountContext) {
self.controller = controller
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 1
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor)
self.scrollNode = ASScrollNode()
self.scrollNode.view.showsVerticalScrollIndicator = true
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.scrollsToTop = true
self.scrollNode.view.delaysContentTouches = false
self.scrollNode.view.canCancelContentTouches = true
if #available(iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
var openUrl: (() -> Void)?
let rawText = self.presentationData.strings.SponsoredMessageInfoScreen_Text
var items: [Item] = []
var didAddUrl = false
for component in rawText.components(separatedBy: "[url]") {
var itemText = component
if itemText.hasPrefix("\n") {
itemText = String(itemText[itemText.index(itemText.startIndex, offsetBy: 1)...])
}
if itemText.hasSuffix("\n") {
itemText = String(itemText[..<itemText.index(itemText.endIndex, offsetBy: -1)])
}
let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 0
textNode.attributedText = NSAttributedString(string: itemText, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
items.append(.text(textNode))
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_ActionUrl, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
})))
}
}
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_ActionUrl, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
})))
}
self.items = items
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.scrollNode)
for item in self.items {
switch item {
case let .text(text):
self.scrollNode.addSubnode(text)
case let .link(link):
self.scrollNode.addSubnode(link)
}
}
openUrl = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_ActionUrl)
}
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
if self.titleNode.supernode == nil {
self.addSubnode(self.titleNode)
}
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left * 2.0 - 80.0 - 16.0 * 2.0, height: 100.0))
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + 16.0, y: floor((navigationHeight - titleSize.height) / 2.0)), size: titleSize))
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: navigationHeight, left: 0.0, bottom: 0.0, right: 0.0)
let sideInset: CGFloat = layout.safeInsets.left + 16.0
let maxWidth: CGFloat = layout.size.width - sideInset * 2.0
var contentHeight: CGFloat = navigationHeight + 16.0
for item in self.items {
switch item {
case let .text(text):
let textSize = text.updateLayout(CGSize(width: maxWidth, height: .greatestFiniteMagnitude))
transition.updateFrameAdditive(node: text, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: textSize))
contentHeight += textSize.height
case let .link(link):
let linkHeight = link.update(width: maxWidth, transition: transition)
let linkSize = CGSize(width: maxWidth, height: linkHeight)
contentHeight += 16.0
transition.updateFrame(node: link, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: linkSize))
contentHeight += linkSize.height
contentHeight += 16.0
}
}
contentHeight += 16.0
contentHeight += layout.intrinsicInsets.bottom
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: contentHeight)
}
}
private var node: Node {
return self.displayNode as! Node
}
private let context: AccountContext
private var presentationData: PresentationData
public init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationPresentation = .modal
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: "", style: .plain, target: self, action: #selector(self.noAction)), animated: false)
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)), animated: false)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func noAction() {
}
@objc private func donePressed() {
self.dismiss()
}
override public func loadDisplayNode() {
self.displayNode = Node(controller: self, context: self.context)
super.displayNodeDidLoad()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.node.containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
}

View File

@ -1122,6 +1122,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} }
let merchantId: String let merchantId: String
var countryCode: String = "US"
if nativeProvider.name == "stripe" { if nativeProvider.name == "stripe" {
merchantId = "merchant.ph.telegra.Telegraph" merchantId = "merchant.ph.telegra.Telegraph"
} else if let paramsId = nativeParams["apple_pay_merchant_id"] as? String { } else if let paramsId = nativeParams["apple_pay_merchant_id"] as? String {
@ -1129,6 +1130,9 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
} else { } else {
return return
} }
if let paramsCountryCode = nativeParams["acquirer_bank_country"] as? String {
countryCode = paramsCountryCode
}
let botPeerId = self.messageId.peerId let botPeerId = self.messageId.peerId
let _ = (context.engine.data.get( let _ = (context.engine.data.get(
@ -1141,9 +1145,9 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
request.merchantIdentifier = merchantId request.merchantIdentifier = merchantId
request.supportedNetworks = [.visa, .amex, .masterCard] request.supportedNetworks = [.visa, .amex, .masterCard]
request.merchantCapabilities = [.capability3DS] request.merchantCapabilities = [.capability3DS]
request.countryCode = "US" request.countryCode = countryCode
request.currencyCode = paymentForm.invoice.currency.uppercased() request.currencyCode = paymentForm.invoice.currency.uppercased()
var items: [PKPaymentSummaryItem] = [] var items: [PKPaymentSummaryItem] = []
var totalAmount: Int64 = 0 var totalAmount: Int64 = 0

View File

@ -250,10 +250,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in }, action: { c, _ in
c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined)) c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined), minHeight: nil)
}))) })))
c.setItems(.single(updatedItems)) c.setItems(.single(updatedItems), minHeight: nil)
}))) })))
} }
} }

View File

@ -124,13 +124,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var validLayout: (ContainerViewLayout, CGFloat)? private var validLayout: (ContainerViewLayout, CGFloat)?
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
self.context = context self.context = context
self.peersFilter = filter self.peersFilter = filter
self.groupId = groupId self.groupId = groupId
self.displaySearchFilters = displaySearchFilters self.displaySearchFilters = displaySearchFilters
self.navigationController = navigationController self.navigationController = navigationController
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.selectedFilterKey = .filter(initialFilter.id) self.selectedFilterKey = .filter(initialFilter.id)
self.selectedFilterKeyPromise.set(.single(self.selectedFilterKey)) self.selectedFilterKeyPromise.set(.single(self.selectedFilterKey))
@ -140,7 +140,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.presentInGlobalOverlay = presentInGlobalOverlay self.presentInGlobalOverlay = presentInGlobalOverlay
self.filterContainerNode = ChatListSearchFiltersContainerNode() self.filterContainerNode = ChatListSearchFiltersContainerNode()
self.paneContainerNode = ChatListSearchPaneContainerNode(context: context, peersFilter: self.peersFilter, groupId: groupId, searchQuery: self.searchQuery.get(), searchOptions: self.searchOptions.get(), navigationController: navigationController) self.paneContainerNode = ChatListSearchPaneContainerNode(context: context, updatedPresentationData: updatedPresentationData, peersFilter: self.peersFilter, groupId: groupId, searchQuery: self.searchQuery.get(), searchOptions: self.searchOptions.get(), navigationController: navigationController)
self.paneContainerNode.clipsToBounds = true self.paneContainerNode.clipsToBounds = true
super.init() super.init()
@ -397,7 +397,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
} }
})) }))
self.presentationDataDisposable = (context.sharedContext.presentationData self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme

View File

@ -714,7 +714,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
private var hiddenMediaDisposable: Disposable? private var hiddenMediaDisposable: Disposable?
init(context: AccountContext, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, groupId: PeerGroupId?, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) { init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, groupId: PeerGroupId?, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
self.context = context self.context = context
self.interaction = interaction self.interaction = interaction
self.key = key self.key = key
@ -739,7 +739,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} }
self.tagMask = tagMask self.tagMask = tagMask
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData self.presentationData = presentationData
self.presentationDataPromise.set(.single(ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true))) self.presentationDataPromise.set(.single(ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)))
@ -1501,7 +1501,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} }
})) }))
self.presentationDataDisposable = (context.sharedContext.presentationData self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData

View File

@ -72,6 +72,7 @@ private final class ChatListSearchPendingPane {
init( init(
context: AccountContext, context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
interaction: ChatListSearchInteraction, interaction: ChatListSearchInteraction,
navigationController: NavigationController?, navigationController: NavigationController?,
peersFilter: ChatListNodePeersFilter, peersFilter: ChatListNodePeersFilter,
@ -81,7 +82,7 @@ private final class ChatListSearchPendingPane {
key: ChatListSearchPaneKey, key: ChatListSearchPaneKey,
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
) { ) {
let paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, key: key, peersFilter: key == .chats ? peersFilter : [], groupId: groupId, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) let paneNode = ChatListSearchListPaneNode(context: context, updatedPresentationData: updatedPresentationData, interaction: interaction, key: key, peersFilter: key == .chats ? peersFilter : [], groupId: groupId, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode) self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
self.disposable = (paneNode.isReady self.disposable = (paneNode.isReady
@ -99,6 +100,7 @@ private final class ChatListSearchPendingPane {
final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private let context: AccountContext private let context: AccountContext
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
private let peersFilter: ChatListNodePeersFilter private let peersFilter: ChatListNodePeersFilter
private let groupId: PeerGroupId private let groupId: PeerGroupId
private let searchQuery: Signal<String?, NoError> private let searchQuery: Signal<String?, NoError>
@ -134,8 +136,9 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
private var currentAvailablePanes: [ChatListSearchPaneKey]? private var currentAvailablePanes: [ChatListSearchPaneKey]?
init(context: AccountContext, peersFilter: ChatListNodePeersFilter, groupId: PeerGroupId, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) { init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peersFilter: ChatListNodePeersFilter, groupId: PeerGroupId, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
self.context = context self.context = context
self.updatedPresentationData = updatedPresentationData
self.peersFilter = peersFilter self.peersFilter = peersFilter
self.groupId = groupId self.groupId = groupId
self.searchQuery = searchQuery self.searchQuery = searchQuery
@ -361,6 +364,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
var leftScope = false var leftScope = false
let pane = ChatListSearchPendingPane( let pane = ChatListSearchPendingPane(
context: self.context, context: self.context,
updatedPresentationData: self.updatedPresentationData,
interaction: self.interaction!, interaction: self.interaction!,
navigationController: self.navigationController, navigationController: self.navigationController,
peersFilter: self.peersFilter, peersFilter: self.peersFilter,

View File

@ -875,14 +875,14 @@ public final class ContactListNode: ASDisplayNode {
public var multipleSelection = false public var multipleSelection = false
public init(context: AccountContext, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) {
self.context = context self.context = context
self.filters = filters self.filters = filters
self.displayPermissionPlaceholder = displayPermissionPlaceholder self.displayPermissionPlaceholder = displayPermissionPlaceholder
self.contextAction = contextAction self.contextAction = contextAction
self.multipleSelection = multipleSelection self.multipleSelection = multipleSelection
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData self.presentationData = presentationData
self.listNode = ListView() self.listNode = ListView()
@ -1361,7 +1361,7 @@ public final class ContactListNode: ASDisplayNode {
self?.enqueueTransition(transition) self?.enqueueTransition(transition)
})) }))
self.presentationDataDisposable = (context.sharedContext.presentationData self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme

View File

@ -221,13 +221,13 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
return true return true
} }
public init(context: AccountContext, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) {
self.context = context self.context = context
self.addContact = addContact self.addContact = addContact
self.openPeer = openPeer self.openPeer = openPeer
self.contextAction = contextAction self.contextAction = contextAction
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData self.presentationData = presentationData
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings))

View File

@ -13,8 +13,10 @@ private let animationDurationFactor: Double = 1.0
public protocol ContextControllerProtocol { public protocol ContextControllerProtocol {
var useComplexItemsTransitionAnimation: Bool { get set } var useComplexItemsTransitionAnimation: Bool { get set }
var immediateItemsTransitionAnimation: Bool { get set } var immediateItemsTransitionAnimation: Bool { get set }
func setItems(_ items: Signal<[ContextMenuItem], NoError>) func getActionsMinHeight() -> CGFloat?
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?)
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition)
func dismiss(completion: (() -> Void)?) func dismiss(completion: (() -> Void)?)
} }
@ -130,6 +132,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let contentReady = Promise<Bool>() let contentReady = Promise<Bool>()
private var currentItems: [ContextMenuItem]? private var currentItems: [ContextMenuItem]?
private var currentActionsMinHeight: CGFloat?
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
@ -448,7 +451,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.itemsDisposable.set((items self.itemsDisposable.set((items
|> deliverOnMainQueue).start(next: { [weak self] items in |> deliverOnMainQueue).start(next: { [weak self] items in
self?.setItems(items: items) self?.setItems(items: items, minHeight: nil, previousActionsTransition: .scale)
})) }))
switch source { switch source {
@ -1167,24 +1170,33 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
intermediateCompletion() intermediateCompletion()
}) })
} }
func getActionsMinHeight() -> CGFloat? {
if !self.actionsContainerNode.bounds.height.isZero {
return self.actionsContainerNode.bounds.height
} else {
return nil
}
}
func setItemsSignal(items: Signal<[ContextMenuItem], NoError>) { func setItemsSignal(items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
self.items = items self.items = items
self.itemsDisposable.set((items self.itemsDisposable.set((items
|> deliverOnMainQueue).start(next: { [weak self] items in |> deliverOnMainQueue).start(next: { [weak self] items in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.setItems(items: items) strongSelf.setItems(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)
})) }))
} }
private func setItems(items: [ContextMenuItem]) { private func setItems(items: [ContextMenuItem], minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true { if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true {
return return
} }
self.currentItems = items self.currentItems = items
self.currentActionsMinHeight = minHeight
let previousActionsContainerNode = self.actionsContainerNode let previousActionsContainerNode = self.actionsContainerNode
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in
@ -1197,7 +1209,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode) self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
if let layout = self.validLayout { if let layout = self.validLayout {
self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode) self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode, previousActionsTransition: previousActionsTransition)
} else { } else {
previousActionsContainerNode.removeFromSupernode() previousActionsContainerNode.removeFromSupernode()
} }
@ -1220,7 +1232,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
} }
} }
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?) { func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
if self.isAnimatingOut { if self.isAnimatingOut {
return return
} }
@ -1282,17 +1294,19 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view) let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize) let adjustedActionsSize = CGSize(width: realActionsSize.width, height: max(realActionsSize.height, self.currentActionsMinHeight ?? 0.0))
self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize)
let contentSize = originalProjectedContentViewFrame.1.size let contentSize = originalProjectedContentViewFrame.1.size
self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition) self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition)
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height) let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - adjustedActionsSize.height)
let originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) let originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)
let preferredActionsX = originalProjectedContentViewFrame.1.minX let preferredActionsX = originalProjectedContentViewFrame.1.minX
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize) var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - adjustedActionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: realActionsSize)
let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX
let originalContentY = originalProjectedContentViewFrame.1.minY let originalContentY = originalProjectedContentViewFrame.1.minY
@ -1307,7 +1321,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
} }
var contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset) var contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
contentHeight = max(contentHeight, actionsSize.height + originalActionsFrame.minY + actionsBottomInset) contentHeight = max(contentHeight, adjustedActionsSize.height + originalActionsFrame.minY + actionsBottomInset)
var overflowOffset: CGFloat var overflowOffset: CGFloat
var contentContainerFrame: CGRect var contentContainerFrame: CGRect
@ -1360,26 +1374,28 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view) let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition) let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize) let adjustedActionsSize = CGSize(width: realActionsSize.width, height: max(realActionsSize.height, self.currentActionsMinHeight ?? 0.0))
self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize)
let contentSize = originalProjectedContentViewFrame.1.size let contentSize = originalProjectedContentViewFrame.1.size
self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition) self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition)
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height) let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - adjustedActionsSize.height)
let preferredActionsX: CGFloat let preferredActionsX: CGFloat
let originalActionsY: CGFloat let originalActionsY: CGFloat
if centerVertically { if centerVertically {
originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)
preferredActionsX = originalProjectedContentViewFrame.1.maxX - actionsSize.width preferredActionsX = originalProjectedContentViewFrame.1.maxX - adjustedActionsSize.width
} else if keepInPlace { } else if keepInPlace {
originalActionsY = originalProjectedContentViewFrame.1.minY - contentActionsSpacing - actionsSize.height originalActionsY = originalProjectedContentViewFrame.1.minY - contentActionsSpacing - adjustedActionsSize.height
preferredActionsX = max(actionsSideInset, originalProjectedContentViewFrame.1.maxX - actionsSize.width) preferredActionsX = max(actionsSideInset, originalProjectedContentViewFrame.1.maxX - adjustedActionsSize.width)
} else { } else {
originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin) originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)
preferredActionsX = originalProjectedContentViewFrame.1.minX preferredActionsX = originalProjectedContentViewFrame.1.minX
} }
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize) var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - adjustedActionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: realActionsSize)
let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX
let originalContentY: CGFloat let originalContentY: CGFloat
if keepInPlace { if keepInPlace {
@ -1634,13 +1650,44 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}) })
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} else { } else {
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.1) switch previousActionsTransition {
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in case .scale:
previousActionsContainerNode?.removeFromSupernode() transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.1)
}) previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
previousActionsContainerNode?.removeFromSupernode()
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1) })
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1)
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
case let .slide(forward):
if case .compact = layout.metrics.widthClass {
if forward {
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: -previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
previousActionsContainerNode?.removeFromSupernode()
})
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: layout.size.width + self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
} else {
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: layout.size.width + previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
previousActionsContainerNode?.removeFromSupernode()
})
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: -self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
}
} else {
let offset: CGFloat
if forward {
offset = previousActionsContainerNode.bounds.width
} else {
offset = -previousActionsContainerNode.bounds.width
}
transition.updatePosition(node: previousActionsContainerNode, position: previousActionsContainerNode.position.offsetBy(dx: -offset, dy: 0.0))
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
previousActionsContainerNode?.removeFromSupernode()
})
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: offset, y: 0.0))
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} }
} else { } else {
previousActionsContainerNode.removeFromSupernode() previousActionsContainerNode.removeFromSupernode()
@ -1816,6 +1863,11 @@ public enum ContextContentSource {
} }
public final class ContextController: ViewController, StandalonePresentableController, ContextControllerProtocol { public final class ContextController: ViewController, StandalonePresentableController, ContextControllerProtocol {
public enum PreviousActionsTransition {
case scale
case slide(forward: Bool)
}
private let account: Account private let account: Account
private var presentationData: PresentationData private var presentationData: PresentationData
private let source: ContextContentSource private let source: ContextContentSource
@ -1957,11 +2009,25 @@ public final class ContextController: ViewController, StandalonePresentableContr
self.controllerNode.animateIn() self.controllerNode.animateIn()
} }
} }
public func getActionsMinHeight() -> CGFloat? {
if self.isNodeLoaded {
return self.controllerNode.getActionsMinHeight()
}
return nil
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>) { public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) {
self.items = items self.items = items
if self.isNodeLoaded { if self.isNodeLoaded {
self.controllerNode.setItemsSignal(items: items) self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: .scale)
}
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
self.items = items
if self.isNodeLoaded {
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)
} }
} }

View File

@ -33,9 +33,15 @@ extension PeekControllerTheme {
public final class PeekController: ViewController, ContextControllerProtocol { public final class PeekController: ViewController, ContextControllerProtocol {
public var useComplexItemsTransitionAnimation: Bool = false public var useComplexItemsTransitionAnimation: Bool = false
public var immediateItemsTransitionAnimation = false public var immediateItemsTransitionAnimation = false
public func getActionsMinHeight() -> CGFloat? {
return nil
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>) { public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) {
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
} }
private var controllerNode: PeekControllerNode { private var controllerNode: PeekControllerNode {

View File

@ -151,7 +151,7 @@ private func cancelContextGestures(view: UIView) {
open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate { open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate {
public final let scroller: ListViewScroller public final let scroller: ListViewScroller
private final var visibleSize: CGSize = CGSize() public private(set) final var visibleSize: CGSize = CGSize()
public private(set) final var insets = UIEdgeInsets() public private(set) final var insets = UIEdgeInsets()
public final var visualInsets: UIEdgeInsets? public final var visualInsets: UIEdgeInsets?
public private(set) final var headerInsets = UIEdgeInsets() public private(set) final var headerInsets = UIEdgeInsets()

View File

@ -1,8 +1,6 @@
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
private var backArrowImageCache: [Int32: UIImage] = [:] private var backArrowImageCache: [Int32: UIImage] = [:]
public final class SparseNode: ASDisplayNode { public final class SparseNode: ASDisplayNode {
@ -236,6 +234,8 @@ open class NavigationBar: ASDisplayNode {
public static var defaultSecondaryContentHeight: CGFloat { public static var defaultSecondaryContentHeight: CGFloat {
return 38.0 return 38.0
} }
public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
var presentationData: NavigationBarPresentationData var presentationData: NavigationBarPresentationData
@ -388,7 +388,7 @@ open class NavigationBar: ASDisplayNode {
private var title: String? { private var title: String? {
didSet { didSet {
if let title = self.title { if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title self.titleNode.accessibilityLabel = title
if self.titleNode.supernode == nil { if self.titleNode.supernode == nil {
self.buttonsContainerNode.addSubnode(self.titleNode) self.buttonsContainerNode.addSubnode(self.titleNode)
@ -837,7 +837,7 @@ open class NavigationBar: ASDisplayNode {
self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05)
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title { if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title self.titleNode.accessibilityLabel = title
} }
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
@ -919,7 +919,7 @@ open class NavigationBar: ASDisplayNode {
self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05)
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title { if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.presentationData.theme.primaryTextColor) self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title self.titleNode.accessibilityLabel = title
} }
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
@ -1191,7 +1191,7 @@ open class NavigationBar: ASDisplayNode {
} }
} else if let title = self.title { } else if let title = self.title {
let node = ImmediateTextNode() let node = ImmediateTextNode()
node.attributedText = NSAttributedString(string: title, font: titleFont, textColor: foregroundColor) node.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: foregroundColor)
return node return node
} else { } else {
return nil return nil

View File

@ -2087,7 +2087,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
return return
} }
c.setItems(strongSelf.contextMenuSpeedItems()) c.setItems(strongSelf.contextMenuSpeedItems(), minHeight: nil)
}))) })))
if let (message, _, _) = strongSelf.contentInfo() { if let (message, _, _) = strongSelf.contentInfo() {
@ -2206,7 +2206,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
c.dismiss(completion: nil) c.dismiss(completion: nil)
return return
} }
c.setItems(strongSelf.contextMenuMainItems()) c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
}))) })))
return items return items

View File

@ -158,7 +158,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
backAction(c) backAction(c)
}))) })))
} }
contextController.setItems(.single(items)) contextController.setItems(.single(items), minHeight: nil)
} else { } else {
contextController?.dismiss(completion: nil) contextController?.dismiss(completion: nil)
parent.view.endEditing(true) parent.view.endEditing(true)

View File

@ -315,7 +315,7 @@ private struct NotificationExceptionPeerState : Equatable {
} }
public func notificationPeerExceptionController(context: AccountContext, peer: Peer, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController { public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
let initialState = NotificationExceptionPeerState(canRemove: false) let initialState = NotificationExceptionPeerState(canRemove: false)
let statePromise = Promise(initialState) let statePromise = Promise(initialState)
let stateValue = Atomic(value: initialState) let stateValue = Atomic(value: initialState)
@ -365,7 +365,7 @@ public func notificationPeerExceptionController(context: AccountContext, peer: P
}) })
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get() |> distinctUntilChanged) let signal = combineLatest(queue: .mainQueue(), (updatedPresentationData?.signal ?? context.sharedContext.presentationData), statePromise.get() |> distinctUntilChanged)
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
arguments.cancel() arguments.cancel()

View File

@ -703,7 +703,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let maximumNumberOfColors: Int let maximumNumberOfColors: Int
switch self.state.section { switch self.state.section {
case .accent: case .accent:
maximumNumberOfColors = 2 if [.classic, .day].contains(self.theme.referenceTheme.baseTheme) {
maximumNumberOfColors = 2
} else {
maximumNumberOfColors = 1
}
case .background: case .background:
maximumNumberOfColors = 4 maximumNumberOfColors = 4
case .messages: case .messages:

View File

@ -2473,7 +2473,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuDisplayAsItems()) c.setItems(strongSelf.contextMenuDisplayAsItems(), minHeight: nil)
}))) })))
items.append(.separator) items.append(.separator)
break break
@ -2506,7 +2506,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuAudioItems()) c.setItems(strongSelf.contextMenuAudioItems(), minHeight: nil)
}))) })))
} }
@ -2543,7 +2543,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuPermissionItems()) c.setItems(strongSelf.contextMenuPermissionItems(), minHeight: nil)
}))) })))
} }
} }
@ -2613,7 +2613,15 @@ public final class VoiceChatController: ViewController {
if let strongSelf = self { if let strongSelf = self {
strongSelf.call.setShouldBeRecording(false, title: nil, videoOrientation: nil) strongSelf.call.setShouldBeRecording(false, title: nil, videoOrientation: nil)
strongSelf.presentUndoOverlay(content: .forward(savedMessages: true, text: strongSelf.presentationData.strings.VoiceChat_RecordingSaved), action: { [weak self] value in
let text: String
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
text = strongSelf.presentationData.strings.LiveStream_RecordingSaved
} else {
text = strongSelf.presentationData.strings.VideoChat_RecordingSaved
}
strongSelf.presentUndoOverlay(content: .forward(savedMessages: true, text: text), action: { [weak self] value in
if case .info = value, let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { if case .info = value, let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
let context = strongSelf.context let context = strongSelf.context
strongSelf.controller?.dismiss(completion: { strongSelf.controller?.dismiss(completion: {
@ -2795,7 +2803,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuMainItems()) c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
}))) })))
return .single(items) return .single(items)
} }
@ -2890,7 +2898,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuMainItems()) c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
}))) })))
return items return items
} }
@ -2936,7 +2944,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
c.setItems(strongSelf.contextMenuMainItems()) c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
}))) })))
} }
return .single(items) return .single(items)

View File

@ -117,7 +117,7 @@ public extension TelegramChannel {
if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banChangeInfo) { if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banChangeInfo) {
return false return false
} }
return true return false
} }
case .addAdmins: case .addAdmins:
if let adminRights = self.adminRights, adminRights.rights.contains(.canAddAdmins) { if let adminRights = self.adminRights, adminRights.rights.contains(.canAddAdmins) {

View File

@ -189,7 +189,21 @@ private func validatePeerReadState(network: Network, postbox: Postbox, stateMana
} }
} }
} }
transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: readState]]) var updatedReadState = readState
if case let .idBased(updatedMaxIncomingReadId, updatedMaxOutgoingReadId, updatedMaxKnownId, updatedCount, updatedMarkedUnread) = readState, let readStates = transaction.getPeerReadStates(peerId) {
for (namespace, state) in readStates {
if namespace == Namespaces.Message.Cloud {
switch state {
case let .idBased(_, maxOutgoingReadId, _, _, _):
updatedReadState = .idBased(maxIncomingReadId: updatedMaxIncomingReadId, maxOutgoingReadId: max(updatedMaxOutgoingReadId, maxOutgoingReadId), maxKnownId: updatedMaxKnownId, count: updatedCount, markedUnread: updatedMarkedUnread)
case .indexBased:
break
}
break
}
}
}
transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: updatedReadState]])
return nil return nil
} }
|> mapToSignalPromotingError { error -> Signal<Never, PeerReadStateValidationError> in |> mapToSignalPromotingError { error -> Signal<Never, PeerReadStateValidationError> in

View File

@ -260,7 +260,7 @@ private class AdMessagesHistoryContextImpl {
self.state.set(CachedState.getCached(postbox: account.postbox, peerId: peerId) self.state.set(CachedState.getCached(postbox: account.postbox, peerId: peerId)
|> mapToSignal { cachedState -> Signal<State, NoError> in |> mapToSignal { cachedState -> Signal<State, NoError> in
if let cachedState = cachedState, cachedState.timestamp >= Int32(Date().timeIntervalSince1970) - 5 * 60 { if false, let cachedState = cachedState, cachedState.timestamp >= Int32(Date().timeIntervalSince1970) - 5 * 60 {
return account.postbox.transaction { transaction -> State in return account.postbox.transaction { transaction -> State in
return State(messages: cachedState.messages.map { message in return State(messages: cachedState.messages.map { message in
return message.toMessage(peerId: peerId, transaction: transaction) return message.toMessage(peerId: peerId, transaction: transaction)

View File

@ -34,18 +34,8 @@ public func customizePresentationTheme(_ theme: PresentationTheme, specialMode:
} }
public func makePresentationTheme(settings: TelegramThemeSettings, specialMode: Bool = false, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? { public func makePresentationTheme(settings: TelegramThemeSettings, specialMode: Bool = false, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? {
var baseTheme: TelegramBaseTheme = settings.baseTheme let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false)
var chatWallpaper = settings.wallpaper return customizePresentationTheme(defaultTheme, specialMode: specialMode, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(argb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
if specialMode && baseTheme == .tinted {
baseTheme = .night
if let wallpaper = settings.wallpaper {
let colors = (wallpaper.settings?.colors ?? []).map { UIColor(rgb: $0).withMultiplied(hue: 1.0, saturation: 1.0, brightness: 2.25).rgb }
chatWallpaper = settings.wallpaper?.withUpdatedSettings(WallpaperSettings(blur: wallpaper.settings?.blur ?? false, motion: wallpaper.settings?.blur ?? false, colors: colors, intensity: wallpaper.settings?.intensity.flatMap({ -(max(55, $0)) }), rotation: wallpaper.settings?.rotation))
}
}
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false)
return customizePresentationTheme(defaultTheme, specialMode: specialMode, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(argb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: chatWallpaper)
} }
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, outgoingAccentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], animateBubbleColors: Bool? = nil, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, specialMode: Bool = false, preview: Bool = false) -> PresentationTheme? { public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, outgoingAccentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], animateBubbleColors: Bool? = nil, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, specialMode: Bool = false, preview: Bool = false) -> PresentationTheme? {

View File

@ -1274,6 +1274,11 @@ public final class PresentationTheme: Equatable {
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache() public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
public init(name: PresentationThemeName, index: Int64, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, chart: PresentationThemeChart, preview: Bool = false) { public init(name: PresentationThemeName, index: Int64, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, chart: PresentationThemeChart, preview: Bool = false) {
var overallDarkAppearance = overallDarkAppearance
if [.night, .tinted].contains(referenceTheme.baseTheme) {
overallDarkAppearance = true
}
self.name = name self.name = name
self.index = index self.index = index
self.referenceTheme = referenceTheme self.referenceTheme = referenceTheme

View File

@ -159,8 +159,11 @@ public enum PresentationResourceKey: Int32 {
case chatInputTextFieldBackgroundImage case chatInputTextFieldBackgroundImage
case chatInputTextFieldClearImage case chatInputTextFieldClearImage
case chatInputPanelSendIconImage
case chatInputPanelSendButtonImage case chatInputPanelSendButtonImage
case chatInputPanelApplyIconImage
case chatInputPanelApplyButtonImage case chatInputPanelApplyButtonImage
case chatInputPanelScheduleIconImage
case chatInputPanelScheduleButtonImage case chatInputPanelScheduleButtonImage
case chatInputPanelVoiceButtonImage case chatInputPanelVoiceButtonImage
case chatInputPanelVideoButtonImage case chatInputPanelVideoButtonImage

View File

@ -428,6 +428,27 @@ public struct PresentationResourcesChat {
}) })
} }
public static func chatInputPanelSendIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelSendIconImage.rawValue, { theme in
return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let color: UIColor
if [.day, .night].contains(theme.referenceTheme.baseTheme) && theme.chat.message.outgoing.bubble.withWallpaper.fill.count > 1 {
color = .white
} else {
color = theme.chat.inputPanel.actionControlForegroundColor
}
context.setStrokeColor(color.cgColor)
context.setFillColor(color.cgColor)
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setLineJoin(.round)
let _ = try? drawSvgPath(context, path: "M11,14.6666667 L16.4310816,9.40016333 L16.4310816,9.40016333 C16.4694824,9.36292619 16.5305176,9.36292619 16.5689184,9.40016333 L22,14.6666667 S ")
let _ = try? drawSvgPath(context, path: "M16.5,9.33333333 C17.0522847,9.33333333 17.5,9.78104858 17.5,10.3333333 L17.5,24 C17.5,24.5522847 17.0522847,25 16.5,25 C15.9477153,25 15.5,24.5522847 15.5,24 L15.5,10.3333333 C15.5,9.78104858 15.9477153,9.33333333 16.5,9.33333333 Z ")
})
})
}
public static func chatInputPanelApplyButtonImage(_ theme: PresentationTheme) -> UIImage? { public static func chatInputPanelApplyButtonImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelApplyButtonImage.rawValue, { theme in return theme.image(PresentationResourceKey.chatInputPanelApplyButtonImage.rawValue, { theme in
return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
@ -444,6 +465,26 @@ public struct PresentationResourcesChat {
}) })
} }
public static func chatInputPanelApplyIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelApplyIconImage.rawValue, { theme in
return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let color: UIColor
if [.day, .night].contains(theme.referenceTheme.baseTheme) && theme.chat.message.outgoing.bubble.withWallpaper.fill.count > 1 {
color = .white
} else {
color = theme.chat.inputPanel.actionControlForegroundColor
}
context.setStrokeColor(color.cgColor)
context.setFillColor(color.cgColor)
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setLineJoin(.round)
let _ = try? drawSvgPath(context, path: "M9.33333333,17.2686567 L14.1849216,22.120245 L14.1849216,22.120245 C14.2235835,22.1589069 14.2862668,22.1589069 14.3249287,22.120245 C14.3261558,22.1190179 14.3273504,22.1177588 14.3285113,22.1164689 L24.3333333,11 S ")
})
})
}
public static func chatInputPanelScheduleButtonImage(_ theme: PresentationTheme) -> UIImage? { public static func chatInputPanelScheduleButtonImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelScheduleButtonImage.rawValue, { theme in return theme.image(PresentationResourceKey.chatInputPanelScheduleButtonImage.rawValue, { theme in
return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
@ -463,6 +504,30 @@ public struct PresentationResourcesChat {
}) })
} }
public static func chatInputPanelScheduleIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelScheduleIconImage.rawValue, { theme in
return generateImage(CGSize(width: 33.0, height: 33.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
let imageRect = CGRect(origin: CGPoint(), size: size)
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
let color: UIColor
if [.day, .night].contains(theme.referenceTheme.baseTheme) && theme.chat.message.outgoing.bubble.withWallpaper.fill.count > 1 {
color = .white
} else {
color = theme.chat.inputPanel.actionControlForegroundColor
}
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/ScheduleIcon"), color: color) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) / 2.0), y: floorToScreenPixels((size.height - image.size.height) / 2.0)), size: image.size))
}
})
})
}
public static func chatInputPanelVoiceButtonImage(_ theme: PresentationTheme) -> UIImage? { public static func chatInputPanelVoiceButtonImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputPanelVoiceButtonImage.rawValue, { theme in return theme.image(PresentationResourceKey.chatInputPanelVoiceButtonImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlColor)

View File

@ -242,6 +242,7 @@ swift_library(
"//submodules/GradientBackground:GradientBackground", "//submodules/GradientBackground:GradientBackground",
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
"//submodules/ComponentFlow:ComponentFlow", "//submodules/ComponentFlow:ComponentFlow",
"//submodules/AdUI:AdUI",
] + select({ ] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -354,8 +354,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var canReadHistoryValue = false private var canReadHistoryValue = false
private var canReadHistoryDisposable: Disposable? private var canReadHistoryDisposable: Disposable?
private var themeEmoticonPreviewPromise = ValuePromise<String?>(nil) private var themeEmoticonAndDarkAppearancePreviewPromise = Promise<(String?, Bool?)>((nil, nil))
private var themeDarkAppearancePreviewPromise = ValuePromise<Bool?>(nil)
private var didSetPresentationData = false private var didSetPresentationData = false
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataPromise = Promise<PresentationData>() private var presentationDataPromise = Promise<PresentationData>()
@ -417,6 +416,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private weak var sendMessageActionsController: ChatSendMessageActionSheetController? private weak var sendMessageActionsController: ChatSendMessageActionSheetController?
private var searchResultsController: ChatSearchResultsController? private var searchResultsController: ChatSearchResultsController?
private weak var themeSceen: ChatThemeScreen?
private weak var currentPinchController: PinchController? private weak var currentPinchController: PinchController?
private weak var currentPinchSourceItemNode: ListViewItemNode? private weak var currentPinchSourceItemNode: ListViewItemNode?
@ -3438,7 +3439,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
if case .standard(previewing: false) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { if case .standard(previewing: false) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
if strongSelf.nextChannelToReadDisposable == nil { var isRegularChat = false
if let subject = subject {
if case .message = subject {
isRegularChat = true
}
} else {
isRegularChat = true
}
if isRegularChat, strongSelf.nextChannelToReadDisposable == nil {
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, chatListFilterId: strongSelf.currentChatListFilter, getFilterPredicate: chatListFilterPredicate), strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, chatListFilterId: strongSelf.currentChatListFilter, getFilterPredicate: chatListFilterPredicate),
ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager)
@ -3898,8 +3907,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
let accountManager = context.sharedContext.accountManager let accountManager = context.sharedContext.accountManager
self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, themeSettings, context.engine.themes.getChatThemes(accountManager: accountManager, onlyCached: false), themeEmoticon, self.themeEmoticonPreviewPromise.get(), self.themeDarkAppearancePreviewPromise.get()).start(next: { [weak self] presentationData, themeSettings, chatThemes, themeEmoticon, themeEmoticonPreview, darkAppearancePreview in self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, themeSettings, context.engine.themes.getChatThemes(accountManager: accountManager, onlyCached: false), themeEmoticon, self.themeEmoticonAndDarkAppearancePreviewPromise.get()).start(next: { [weak self] presentationData, themeSettings, chatThemes, themeEmoticon, themeEmoticonAndDarkAppearance in
if let strongSelf = self { if let strongSelf = self {
let (themeEmoticonPreview, darkAppearancePreview) = themeEmoticonAndDarkAppearance
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings let previousStrings = strongSelf.presentationData.strings
let previousChatWallpaper = strongSelf.presentationData.chatWallpaper let previousChatWallpaper = strongSelf.presentationData.chatWallpaper
@ -3995,6 +4006,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controllerInteraction.updatedPresentationData = strongSelf.updatedPresentationData controllerInteraction.updatedPresentationData = strongSelf.updatedPresentationData
strongSelf.presentationDataPromise.set(.single(strongSelf.presentationData)) strongSelf.presentationDataPromise.set(.single(strongSelf.presentationData))
if !isFirstTime && previousTheme !== presentationData.theme {
strongSelf.presentCrossfadeSnapshot(delay: 0.2)
}
} }
strongSelf.presentationReady.set(.single(true)) strongSelf.presentationReady.set(.single(true))
} }
@ -6487,7 +6502,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
}))) })))
contextController.setItems(.single(contextItems)) contextController.setItems(.single(contextItems), minHeight: nil)
} }
return return
} else { } else {
@ -6506,7 +6521,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
}))) })))
contextController.setItems(.single(contextItems)) contextController.setItems(.single(contextItems), minHeight: nil)
return return
} else { } else {
@ -8009,6 +8024,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.dismissAllTooltips() self.dismissAllTooltips()
self.sendMessageActionsController?.dismiss() self.sendMessageActionsController?.dismiss()
self.themeSceen?.dismiss()
if let _ = self.peekData { if let _ = self.peekData {
self.peekTimerDisposable.set(nil) self.peekTimerDisposable.set(nil)
@ -9577,7 +9593,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
if let strongSelf = self { if let strongSelf = self {
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState, silentPosting in let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState, silentPosting in
if let legacyController = legacyController { if let legacyController = legacyController {
legacyController.dismiss() legacyController.dismiss()
} }
@ -9684,7 +9700,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
|> deliverOnMainQueue).start(next: { [weak self] configuration in |> deliverOnMainQueue).start(next: { [weak self] configuration in
if let strongSelf = self { if let strongSelf = self {
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, configuration: configuration, mode: .media(completion: { [weak self] results, selectionState, editingState, silentPosting in let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, chatLocation: strongSelf.chatLocation, configuration: configuration, mode: .media(completion: { [weak self] results, selectionState, editingState, silentPosting in
legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { [weak self] result in legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { [weak self] result in
if let strongSelf = self { if let strongSelf = self {
strongSelf.enqueueChatContextResult(results, result, hideVia: true) strongSelf.enqueueChatContextResult(results, result, hideVia: true)
@ -12618,7 +12634,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if canDisplayContextMenu, let contextController = contextController { if canDisplayContextMenu, let contextController = contextController {
contextController.setItems(.single(contextItems)) contextController.setItems(.single(contextItems), minHeight: nil)
} else { } else {
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
@ -13303,13 +13319,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.push(controller) self.push(controller)
} }
private var crossfading = false
private func presentCrossfadeSnapshot(delay: Double) { private func presentCrossfadeSnapshot(delay: Double) {
guard let snapshotView = self.view.snapshotView(afterScreenUpdates: false) else { guard !self.crossfading, let snapshotView = self.view.snapshotView(afterScreenUpdates: false) else {
return return
} }
self.crossfading = true
self.view.addSubview(snapshotView) self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak self, weak snapshotView] _ in
self?.crossfading = false
snapshotView?.removeFromSuperview() snapshotView?.removeFromSuperview()
}) })
} }
@ -13345,25 +13364,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
selectedEmoticon = nil selectedEmoticon = nil
} }
let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initiallySelectedEmoticon: selectedEmoticon, previewTheme: { [weak self] emoticon in let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initiallySelectedEmoticon: selectedEmoticon, previewTheme: { [weak self] emoticon, dark in
if let strongSelf = self { if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot(delay: 0.2) strongSelf.presentCrossfadeSnapshot(delay: 0.2)
strongSelf.themeEmoticonPreviewPromise.set(emoticon) strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark)))
if emoticon == nil {
strongSelf.themeDarkAppearancePreviewPromise.set(nil)
}
}
}, previewDarkTheme: { dark in
if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot(delay: 0.0)
strongSelf.themeDarkAppearancePreviewPromise.set(dark)
} }
}, completion: { [weak self] emoticon in }, completion: { [weak self] emoticon in
strongSelf.presentCrossfadeSnapshot(delay: 0.2) strongSelf.presentCrossfadeSnapshot(delay: 0.2)
strongSelf.themeDarkAppearancePreviewPromise.set(nil) strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, nil)))
let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).start(completed: { [weak self] in let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).start(completed: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.themeEmoticonPreviewPromise.set(nil) strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((nil, nil)))
} }
}) })
}) })
@ -13375,6 +13386,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
strongSelf.themeSceen = controller
}) })
} }

View File

@ -492,7 +492,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.textInputPanelNode = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak self] controller in self.textInputPanelNode = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentationContext: ChatPresentationContext(backgroundNode: backgroundNode), presentController: { [weak self] controller in
self?.interfaceInteraction?.presentController(controller, nil) self?.interfaceInteraction?.presentController(controller, nil)
}) })
self.textInputPanelNode?.storedInputLanguage = chatPresentationInterfaceState.interfaceState.inputLanguage self.textInputPanelNode?.storedInputLanguage = chatPresentationInterfaceState.interfaceState.inputLanguage
@ -1446,9 +1446,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
if inputPanelNodeHandlesTransition { if inputPanelNodeHandlesTransition {
inputPanelNode.updateAbsoluteRect(apparentInputPanelFrame, within: layout.size, transition: .immediate)
inputPanelNode.frame = apparentInputPanelFrame inputPanelNode.frame = apparentInputPanelFrame
inputPanelNode.alpha = 1.0 inputPanelNode.alpha = 1.0
} else { } else {
inputPanelNode.updateAbsoluteRect(apparentInputPanelFrame, within: layout.size, transition: transition)
transition.updateFrame(node: inputPanelNode, frame: apparentInputPanelFrame) transition.updateFrame(node: inputPanelNode, frame: apparentInputPanelFrame)
transition.updateAlpha(node: inputPanelNode, alpha: 1.0) transition.updateAlpha(node: inputPanelNode, alpha: 1.0)
} }
@ -2611,6 +2613,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
let navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState let navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState
let titleAccessoryPanelSnapshot: UIView? let titleAccessoryPanelSnapshot: UIView?
let navigationBarHeight: CGFloat let navigationBarHeight: CGFloat
let inputPanelNodeSnapshot: UIView?
let inputPanelOverscrollNodeSnapshot: UIView?
fileprivate init( fileprivate init(
historySnapshotState: ChatHistoryListNode.SnapshotState, historySnapshotState: ChatHistoryListNode.SnapshotState,
@ -2618,7 +2622,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?, avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?,
navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState, navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState,
titleAccessoryPanelSnapshot: UIView?, titleAccessoryPanelSnapshot: UIView?,
navigationBarHeight: CGFloat navigationBarHeight: CGFloat,
inputPanelNodeSnapshot: UIView?,
inputPanelOverscrollNodeSnapshot: UIView?
) { ) {
self.historySnapshotState = historySnapshotState self.historySnapshotState = historySnapshotState
self.titleViewSnapshotState = titleViewSnapshotState self.titleViewSnapshotState = titleViewSnapshotState
@ -2626,6 +2632,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.navigationButtonsSnapshotState = navigationButtonsSnapshotState self.navigationButtonsSnapshotState = navigationButtonsSnapshotState
self.titleAccessoryPanelSnapshot = titleAccessoryPanelSnapshot self.titleAccessoryPanelSnapshot = titleAccessoryPanelSnapshot
self.navigationBarHeight = navigationBarHeight self.navigationBarHeight = navigationBarHeight
self.inputPanelNodeSnapshot = inputPanelNodeSnapshot
self.inputPanelOverscrollNodeSnapshot = inputPanelOverscrollNodeSnapshot
} }
} }
@ -2638,13 +2646,25 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
snapshot.frame = titleAccessoryPanelNode.frame snapshot.frame = titleAccessoryPanelNode.frame
titleAccessoryPanelSnapshot = snapshot titleAccessoryPanelSnapshot = snapshot
} }
var inputPanelNodeSnapshot: UIView?
if let inputPanelNode = self.inputPanelNode, let snapshot = inputPanelNode.view.snapshotView(afterScreenUpdates: false) {
snapshot.frame = inputPanelNode.frame
inputPanelNodeSnapshot = snapshot
}
var inputPanelOverscrollNodeSnapshot: UIView?
if let inputPanelOverscrollNode = self.inputPanelOverscrollNode, let snapshot = inputPanelOverscrollNode.view.snapshotView(afterScreenUpdates: false) {
snapshot.frame = inputPanelOverscrollNode.frame
inputPanelOverscrollNodeSnapshot = snapshot
}
return SnapshotState( return SnapshotState(
historySnapshotState: self.historyNode.prepareSnapshotState(), historySnapshotState: self.historyNode.prepareSnapshotState(),
titleViewSnapshotState: titleViewSnapshotState, titleViewSnapshotState: titleViewSnapshotState,
avatarSnapshotState: avatarSnapshotState, avatarSnapshotState: avatarSnapshotState,
navigationButtonsSnapshotState: self.navigateButtons.prepareSnapshotState(), navigationButtonsSnapshotState: self.navigateButtons.prepareSnapshotState(),
titleAccessoryPanelSnapshot: titleAccessoryPanelSnapshot, titleAccessoryPanelSnapshot: titleAccessoryPanelSnapshot,
navigationBarHeight: self.navigationBar?.backgroundNode.bounds.height ?? 0.0 navigationBarHeight: self.navigationBar?.backgroundNode.bounds.height ?? 0.0,
inputPanelNodeSnapshot: inputPanelNodeSnapshot,
inputPanelOverscrollNodeSnapshot: inputPanelOverscrollNodeSnapshot
) )
} }
@ -2684,6 +2704,27 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
navigationBar.backgroundNode.update(size: currentFrame.size, transition: .animated(duration: 0.5, curve: .spring)) navigationBar.backgroundNode.update(size: currentFrame.size, transition: .animated(duration: 0.5, curve: .spring))
} }
} }
if let inputPanelNode = self.inputPanelNode, let inputPanelNodeSnapshot = snapshotState.inputPanelNodeSnapshot {
inputPanelNode.view.superview?.insertSubview(inputPanelNodeSnapshot, belowSubview: inputPanelNode.view)
inputPanelNodeSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputPanelNodeSnapshot] _ in
inputPanelNodeSnapshot?.removeFromSuperview()
})
inputPanelNodeSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -5.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
if let inputPanelOverscrollNodeSnapshot = snapshotState.inputPanelOverscrollNodeSnapshot {
inputPanelNode.view.superview?.insertSubview(inputPanelOverscrollNodeSnapshot, belowSubview: inputPanelNode.view)
inputPanelOverscrollNodeSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak inputPanelOverscrollNodeSnapshot] _ in
inputPanelOverscrollNodeSnapshot?.removeFromSuperview()
})
inputPanelOverscrollNodeSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -5.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
}
inputPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
inputPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 5.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
} }
private var preivousChatInputPanelOverscrollNodeTimestamp: Double = 0.0 private var preivousChatInputPanelOverscrollNodeTimestamp: Double = 0.0

View File

@ -1039,6 +1039,16 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var disableAnimations = false var disableAnimations = false
if let strongSelf = self, updatedScrollPosition == nil, case .InteractiveChanges = reason, case let .known(offset) = strongSelf.visibleContentOffset(), abs(offset) <= 0.9, let previous = previous { if let strongSelf = self, updatedScrollPosition == nil, case .InteractiveChanges = reason, case let .known(offset) = strongSelf.visibleContentOffset(), abs(offset) <= 0.9, let previous = previous {
var fillsScreen = true
switch strongSelf.visibleBottomContentOffset() {
case let .known(bottomOffset):
if bottomOffset <= strongSelf.visibleSize.height - strongSelf.insets.bottom {
fillsScreen = false
}
default:
break
}
var previousNumAds = 0 var previousNumAds = 0
for entry in previous.filteredEntries { for entry in previous.filteredEntries {
if case let .MessageEntry(message, _, _, _, _, _) = entry { if case let .MessageEntry(message, _, _, _, _, _) = entry {
@ -1062,7 +1072,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
} }
} }
if let firstNonAdIndex = firstNonAdIndex, previousNumAds == 0, updatedNumAds != 0 { if fillsScreen, let firstNonAdIndex = firstNonAdIndex, previousNumAds == 0, updatedNumAds != 0 {
updatedScrollPosition = .index(index: .message(firstNonAdIndex), position: .top(0.0), directionHint: .Up, animated: false, highlight: false) updatedScrollPosition = .index(index: .message(firstNonAdIndex), position: .top(0.0), directionHint: .Up, animated: false, highlight: false)
disableAnimations = true disableAnimations = true
} }

View File

@ -11,6 +11,9 @@ class ChatInputPanelNode: ASDisplayNode {
var interfaceInteraction: ChatPanelInterfaceInteraction? var interfaceInteraction: ChatPanelInterfaceInteraction?
var prevInputPanelNode: ChatInputPanelNode? var prevInputPanelNode: ChatInputPanelNode?
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
}
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 0.0 return 0.0
} }

View File

@ -21,6 +21,7 @@ import UndoUI
import ShimmerEffect import ShimmerEffect
import AnimatedAvatarSetNode import AnimatedAvatarSetNode
import AvatarNode import AvatarNode
import AdUI
private struct MessageContextMenuData { private struct MessageContextMenuData {
let starStatus: Bool? let starStatus: Bool?
@ -139,7 +140,10 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo
return false return false
} }
private func canViewReadStats(message: Message, appConfig: AppConfiguration) -> Bool { private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
if !isMessageRead {
return false
}
if message.flags.contains(.Incoming) { if message.flags.contains(.Incoming) {
return false return false
} }
@ -149,6 +153,21 @@ private func canViewReadStats(message: Message, appConfig: AppConfiguration) ->
for media in message.media { for media in message.media {
if let _ = media as? TelegramMediaAction { if let _ = media as? TelegramMediaAction {
return false return false
} else if let file = media as? TelegramMediaFile {
if file.isVoice || file.isInstantVideo {
var hasRead = false
for attribute in message.attributes {
if let attribute = attribute as? ConsumableContentMessageAttribute {
if attribute.consumed {
hasRead = true
break
}
}
}
if !hasRead {
return false
}
}
} }
} }
@ -345,18 +364,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
var actions: [ContextMenuItem] = [] var actions: [ContextMenuItem] = []
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
}, iconSource: nil, action: { _, f in }, iconSource: nil, action: { c, _ in
f(.default) c.dismiss(completion: {
controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context))
controllerInteraction.presentController(textAlertController(context: context, title: nil, text: presentationData.strings.SponsoredMessageInfo_Text, actions: [ })
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .defaultAction, title: presentationData.strings.SponsoredMessageInfo_Action, action: {
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.SponsoredMessageInfo_ActionUrl, forceExternal: true, presentationData: presentationData, navigationController: controllerInteraction.navigationController(), dismissInput: {
controllerInteraction.navigationController()?.view.endEditing(true)
})
}),
]), nil)
}))) })))
actions.append(.separator) actions.append(.separator)
@ -518,30 +529,40 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
let cachedData = context.account.postbox.transaction { transaction -> CachedPeerData? in let cachedData = context.account.postbox.transaction { transaction -> CachedPeerData? in
return transaction.getPeerCachedData(peerId: messages[0].id.peerId) return transaction.getPeerCachedData(peerId: messages[0].id.peerId)
} }
let readState = context.account.postbox.transaction { transaction -> CombinedPeerReadState? in
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
}
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration), NoError> = combineLatest( let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool), NoError> = combineLatest(
loadLimits, loadLimits,
loadStickerSaveStatusSignal, loadStickerSaveStatusSignal,
loadResourceStatusSignal, loadResourceStatusSignal,
context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })), context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })),
context.account.pendingUpdateMessageManager.updatingMessageMedia context.account.pendingUpdateMessageManager.updatingMessageMedia
|> take(1), |> take(1),
cachedData cachedData,
readState
) )
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration) in |> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool) in
let (limitsConfiguration, appConfig) = limitsAndAppConfig let (limitsConfiguration, appConfig) = limitsAndAppConfig
var canEdit = false var canEdit = false
if !isAction { if !isAction {
let message = messages[0] let message = messages[0]
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message) canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
} }
var isMessageRead = false
if let readState = readState {
isMessageRead = readState.isOutgoingMessageIndexRead(message.index)
}
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig) return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead)
} }
return dataSignal return dataSignal
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { data, updatingMessageMedia, cachedData, appConfig -> [ContextMenuItem] in |> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead -> [ContextMenuItem] in
var actions: [ContextMenuItem] = [] var actions: [ContextMenuItem] = []
var isPinnedMessages = false var isPinnedMessages = false
@ -1103,8 +1124,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
if !isPinnedMessages, !isReplyThreadHead, data.canSelect { if !isPinnedMessages, !isReplyThreadHead, data.canSelect {
var didAddSeparator = false
if !selectAll || messages.count == 1 { if !selectAll || messages.count == 1 {
if !actions.isEmpty { if !actions.isEmpty && !didAddSeparator {
didAddSeparator = true
actions.append(.separator) actions.append(.separator)
} }
@ -1118,6 +1141,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
if messages.count > 1 { if messages.count > 1 {
if !actions.isEmpty && !didAddSeparator {
didAddSeparator = true
actions.append(.separator)
}
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSelectAll(Int32(messages.count)), icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSelectAll(Int32(messages.count)), icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SelectAll"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SelectAll"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { _, f in
@ -1128,7 +1156,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
} }
if let peer = message.peers[message.id.peerId], canViewReadStats(message: message, appConfig: appConfig) { if let peer = message.peers[message.id.peerId], canViewReadStats(message: message, isMessageRead: isMessageRead, appConfig: appConfig) {
var hasReadReports = false var hasReadReports = false
if let channel = peer as? TelegramChannel { if let channel = peer as? TelegramChannel {
if case .group = channel.info { if case .group = channel.info {
@ -1160,29 +1188,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
subActions.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, textColor: .primary, icon: { theme in subActions.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, textColor: .primary, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor)
}, action: { controller, _ in }, action: { controller, _ in
controller.setItems(contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: chatPresentationInterfaceState, context: context, messages: messages, controllerInteraction: controllerInteraction, selectAll: selectAll, interfaceInteraction: interfaceInteraction, readStats: stats)) controller.setItems(contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: chatPresentationInterfaceState, context: context, messages: messages, controllerInteraction: controllerInteraction, selectAll: selectAll, interfaceInteraction: interfaceInteraction, readStats: stats), minHeight: nil, previousActionsTransition: .slide(forward: false))
}))) })))
let debugRepeatCount: Int for peer in stats.peers {
#if DEBUG let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0))
debugRepeatCount = stats.peers.count == 1 ? 1 : 50
#else
debugRepeatCount = 1
#endif
for _ in 0 ..< debugRepeatCount { subActions.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 30.0, height: 30.0), signal: avatarSignal), action: { _, f in
for peer in stats.peers { c.dismiss(completion: {
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0)) controllerInteraction.openPeer(peer.id, .default, nil)
})
subActions.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 30.0, height: 30.0), signal: avatarSignal), action: { _, f in })))
c.dismiss(completion: {
controllerInteraction.openPeer(peer.id, .default, nil)
})
})))
}
} }
c.setItems(.single(subActions)) let minHeight = c.getActionsMinHeight()
c.setItems(.single(subActions), minHeight: minHeight, previousActionsTransition: .slide(forward: true))
} else { } else {
f(.default) f(.default)
} }
@ -1845,18 +1865,39 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
let rightTextInset: CGFloat = sideInset + 36.0 let rightTextInset: CGFloat = sideInset + 36.0
let calculatedWidth = min(constrainedWidth, 260.0) let calculatedWidth = min(constrainedWidth, 250.0)
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize) let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize)
if let currentStats = self.currentStats { if let currentStats = self.currentStats {
if currentStats.peers.isEmpty { if currentStats.peers.isEmpty {
//TODO:localize var text = self.presentationData.strings.Conversation_ContextMenuNoViews
self.textNode.attributedText = NSAttributedString(string: "No Views", font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor) for media in self.item.message.media {
if let file = media as? TelegramMediaFile {
if file.isVoice {
text = self.presentationData.strings.Conversation_ContextMenuNobodyListened
} else if file.isInstantVideo {
text = self.presentationData.strings.Conversation_ContextMenuNobodyWatched
}
}
}
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.secondaryColor)
} else if currentStats.peers.count == 1 { } else if currentStats.peers.count == 1 {
self.textNode.attributedText = NSAttributedString(string: currentStats.peers[0].displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) self.textNode.attributedText = NSAttributedString(string: currentStats.peers[0].displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor)
} else { } else {
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_ContextMenuSeen(Int32(currentStats.peers.count)), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) var text = self.presentationData.strings.Conversation_ContextMenuSeen(Int32(currentStats.peers.count))
for media in self.item.message.media {
if let file = media as? TelegramMediaFile {
if file.isVoice {
text = self.presentationData.strings.Conversation_ContextMenuListened(Int32(currentStats.peers.count))
} else if file.isInstantVideo {
text = self.presentationData.strings.Conversation_ContextMenuWatched(Int32(currentStats.peers.count))
}
}
}
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor)
} }
} else { } else {
self.textNode.attributedText = NSAttributedString(string: " ", font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) self.textNode.attributedText = NSAttributedString(string: " ", font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor)
@ -1927,8 +1968,18 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
self.performAction() self.performAction()
} }
private var actionTemporarilyDisabled: Bool = false
func performAction() { func performAction() {
guard let controller = self.getController(), let currentStats = self.currentStats else { if self.actionTemporarilyDisabled {
return
}
self.actionTemporarilyDisabled = true
Queue.mainQueue().async { [weak self] in
self?.actionTemporarilyDisabled = false
}
guard let controller = self.getController(), let currentStats = self.currentStats else {
return return
} }
self.item.action(controller, { [weak self] result in self.item.action(controller, { [weak self] result in

View File

@ -294,7 +294,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
textInputPanelNode.context = context textInputPanelNode.context = context
return (textInputPanelNode, nil) return (textInputPanelNode, nil)
} else { } else {
let panel = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak interfaceInteraction] controller in let panel = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentationContext: nil, presentController: { [weak interfaceInteraction] controller in
interfaceInteraction?.presentController(controller, nil) interfaceInteraction?.presentController(controller, nil)
}) })

View File

@ -1308,8 +1308,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self?.additionalAnimationNodes.removeAll(where: { $0 === additionalAnimationNode }) self?.additionalAnimationNodes.removeAll(where: { $0 === additionalAnimationNode })
additionalAnimationNode?.removeFromSupernode() additionalAnimationNode?.removeFromSupernode()
} }
additionalAnimationNode.frame = animationNode.frame.insetBy(dx: -animationNode.frame.width, dy: -animationNode.frame.height) var animationFrame = animationNode.frame.insetBy(dx: -animationNode.frame.width, dy: -animationNode.frame.height)
.offsetBy(dx: incoming ? animationNode.frame.width - 10.0 : -animationNode.frame.width + 10.0, dy: 0.0) .offsetBy(dx: incoming ? animationNode.frame.width - 10.0 : -animationNode.frame.width + 10.0, dy: 0.0)
animationFrame = animationFrame.offsetBy(dx: CGFloat.random(in: -30.0 ... 30.0), dy: CGFloat.random(in: -30.0 ... 30.0))
additionalAnimationNode.frame = animationFrame
if incoming { if incoming {
additionalAnimationNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) additionalAnimationNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
} }
@ -1434,7 +1436,11 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
return .optionalAction({ return .optionalAction({
if firstScalar.value == heart { if firstScalar.value == heart {
self.playAdditionalAnimation("TestHearts") if self.additionalAnimationNodes.count % 2 == 0 {
self.playAdditionalAnimation("TestHearts")
} else {
self.playAdditionalAnimation("TestHearts2")
}
} else if firstScalar.value == fireworks { } else if firstScalar.value == fireworks {
self.playAdditionalAnimation("TestFireworks") self.playAdditionalAnimation("TestFireworks")
} }

View File

@ -188,13 +188,13 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
} }
} }
func update(rect: CGRect, within containerSize: CGSize) { func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
self.absolutePosition = (rect, containerSize) self.absolutePosition = (rect, containerSize)
if let backgroundContent = self.backgroundContent { if let backgroundContent = self.backgroundContent {
var backgroundFrame = backgroundContent.frame var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize) backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
} }
} }

View File

@ -144,11 +144,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
var secretVideoPlaceholderBackgroundImage: UIImage? var secretVideoPlaceholderBackgroundImage: UIImage?
var updatedInfoBackgroundImage: UIImage? var updatedInfoBackgroundImage: UIImage?
var updatedMuteIconImage: UIImage? var updatedMuteIconImage: UIImage?
if item.presentationData.theme != currentItem?.presentationData.theme {
updatedInfoBackgroundImage = PresentationResourcesChat.chatInstantMessageInfoBackgroundImage(item.presentationData.theme.theme)
updatedMuteIconImage = PresentationResourcesChat.chatInstantMessageMuteIconImage(item.presentationData.theme.theme)
}
var updatedInstantVideoBackgroundImage: UIImage?
let instantVideoBackgroundImage: UIImage? let instantVideoBackgroundImage: UIImage?
switch statusDisplayType { switch statusDisplayType {
case .free: case .free:
@ -157,6 +154,12 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
instantVideoBackgroundImage = nil instantVideoBackgroundImage = nil
} }
if item.presentationData.theme != currentItem?.presentationData.theme {
updatedInstantVideoBackgroundImage = instantVideoBackgroundImage
updatedInfoBackgroundImage = PresentationResourcesChat.chatInstantMessageInfoBackgroundImage(item.presentationData.theme.theme)
updatedMuteIconImage = PresentationResourcesChat.chatInstantMessageMuteIconImage(item.presentationData.theme.theme)
}
let theme = item.presentationData.theme let theme = item.presentationData.theme
let isSecretMedia = item.message.containsSecretMedia let isSecretMedia = item.message.containsSecretMedia
var secretProgressIcon: UIImage? var secretProgressIcon: UIImage?
@ -333,6 +336,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
strongSelf.secretVideoPlaceholderBackground.image = secretVideoPlaceholderBackgroundImage strongSelf.secretVideoPlaceholderBackground.image = secretVideoPlaceholderBackgroundImage
} }
if let updatedInstantVideoBackgroundImage = updatedInstantVideoBackgroundImage, let decoration = strongSelf.videoNode?.decoration as? ChatBubbleInstantVideoDecoration, let decorationBackgroundNode = decoration.backgroundNode as? ASImageNode {
decorationBackgroundNode.image = updatedInstantVideoBackgroundImage
}
strongSelf.media = updatedFile strongSelf.media = updatedFile
if let infoBackgroundImage = strongSelf.infoBackgroundNode.image, let muteImage = strongSelf.muteIconNode.image { if let infoBackgroundImage = strongSelf.infoBackgroundNode.image, let muteImage = strongSelf.muteIconNode.image {

View File

@ -24,6 +24,7 @@ final class ChatRecentActionsController: TelegramBaseController {
return (self.presentationData, self.presentationDataPromise.get()) return (self.presentationData, self.presentationDataPromise.get())
} }
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private var didSetPresentationData = false
private var interaction: ChatRecentActionsInteraction! private var interaction: ChatRecentActionsInteraction!
private var panelInteraction: ChatPanelInterfaceInteraction! private var panelInteraction: ChatPanelInterfaceInteraction!
@ -185,10 +186,12 @@ final class ChatRecentActionsController: TelegramBaseController {
} }
} }
let isFirstTime = !strongSelf.didSetPresentationData
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
strongSelf.presentationDataPromise.set(.single(presentationData)) strongSelf.presentationDataPromise.set(.single(presentationData))
strongSelf.didSetPresentationData = true
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { if isFirstTime || previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
strongSelf.updateThemeAndStrings() strongSelf.updateThemeAndStrings()
} }
} }

View File

@ -246,9 +246,9 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.contentNodes = contentNodes self.contentNodes = contentNodes
super.init() super.init()
self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: []) // self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: [])
self.sendButtonNode.addTarget(self, action: #selector(sendButtonPressed), forControlEvents: .touchUpInside) self.sendButtonNode.addTarget(self, action: #selector(self.sendButtonPressed), forControlEvents: .touchUpInside)
if let attributedText = textInputNode.attributedText, !attributedText.string.isEmpty { if let attributedText = textInputNode.attributedText, !attributedText.string.isEmpty {
self.fromMessageTextNode.attributedText = attributedText self.fromMessageTextNode.attributedText = attributedText
@ -341,6 +341,10 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never self.scrollNode.view.contentInsetAdjustmentBehavior = .never
} }
if let snapshotView = self.sourceSendButton.view.snapshotView(afterScreenUpdates: false) {
self.sendButtonNode.view.addSubview(snapshotView)
}
} }
func updatePresentationData(_ presentationData: PresentationData) { func updatePresentationData(_ presentationData: PresentationData) {
@ -361,7 +365,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor
self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor self.contentContainerNode.backgroundColor = self.presentationData.theme.contextMenu.backgroundColor
self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: []) // self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: [])
if let toAttributedText = self.textInputNode.attributedText?.mutableCopy() as? NSMutableAttributedString { if let toAttributedText = self.textInputNode.attributedText?.mutableCopy() as? NSMutableAttributedString {
toAttributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: self.presentationData.theme.chat.message.outgoing.primaryTextColor, range: NSMakeRange(0, (toAttributedText.string as NSString).length)) toAttributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: self.presentationData.theme.chat.message.outgoing.primaryTextColor, range: NSMakeRange(0, (toAttributedText.string as NSString).length))

View File

@ -2,13 +2,18 @@ import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
import Display import Display
import TelegramCore
import TelegramPresentationData import TelegramPresentationData
import ContextUI import ContextUI
final class ChatTextInputActionButtonsNode: ASDisplayNode { final class ChatTextInputActionButtonsNode: ASDisplayNode {
private let presentationContext: ChatPresentationContext?
private let strings: PresentationStrings private let strings: PresentationStrings
let micButton: ChatTextInputMediaRecordingButton let micButton: ChatTextInputMediaRecordingButton
let sendContainerNode: ASDisplayNode
let backdropNode: ChatMessageBubbleBackdrop
let backgroundNode: ASDisplayNode
let sendButton: HighlightTrackingButtonNode let sendButton: HighlightTrackingButtonNode
var sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode? var sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode?
var sendButtonHasApplyIcon = false var sendButtonHasApplyIcon = false
@ -26,10 +31,21 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
var micButtonPointerInteraction: PointerInteraction? var micButtonPointerInteraction: PointerInteraction?
init(theme: PresentationTheme, strings: PresentationStrings, presentController: @escaping (ViewController) -> Void) { private var validLayout: CGSize?
init(presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
self.presentationContext = presentationContext
let theme = presentationInterfaceState.theme
let strings = presentationInterfaceState.strings
self.strings = strings self.strings = strings
self.micButton = ChatTextInputMediaRecordingButton(theme: theme, strings: strings, presentController: presentController) self.micButton = ChatTextInputMediaRecordingButton(theme: theme, strings: strings, presentController: presentController)
self.sendContainerNode = ASDisplayNode()
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
self.backgroundNode.clipsToBounds = true
self.backdropNode = ChatMessageBubbleBackdrop()
self.sendButton = HighlightTrackingButtonNode(pointerStyle: .lift) self.sendButton = HighlightTrackingButtonNode(pointerStyle: .lift)
self.expandMediaInputButton = HighlightableButtonNode(pointerStyle: .default) self.expandMediaInputButton = HighlightableButtonNode(pointerStyle: .default)
@ -43,24 +59,32 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled { if strongSelf.sendButtonHasApplyIcon || !strongSelf.sendButtonLongPressEnabled {
if highlighted { if highlighted {
strongSelf.layer.removeAnimation(forKey: "opacity") strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
strongSelf.alpha = 0.4 strongSelf.sendContainerNode.alpha = 0.4
} else { } else {
strongSelf.alpha = 1.0 strongSelf.sendContainerNode.alpha = 1.0
strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) strongSelf.sendContainerNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
} }
} else { } else {
if highlighted { if highlighted {
strongSelf.sendButton.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false) strongSelf.sendContainerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
} else if let presentationLayer = strongSelf.sendButton.layer.presentation() { } else if let presentationLayer = strongSelf.sendButton.layer.presentation() {
strongSelf.sendButton.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false) strongSelf.sendContainerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
} }
} }
} }
} }
self.view.addSubview(self.micButton) self.view.addSubview(self.micButton)
self.addSubnode(self.sendButton)
self.addSubnode(self.sendContainerNode)
self.sendContainerNode.addSubnode(self.backgroundNode)
if let presentationContext = presentationContext {
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper, bubbleCorners: presentationInterfaceState.bubbleCorners)
self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: presentationContext.backgroundNode)
self.backgroundNode.addSubnode(self.backdropNode)
}
self.sendContainerNode.addSubnode(self.sendButton)
self.addSubnode(self.expandMediaInputButton) self.addSubnode(self.expandMediaInputButton)
} }
@ -75,23 +99,51 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
return return
} }
if !strongSelf.sendButtonHasApplyIcon { if !strongSelf.sendButtonHasApplyIcon {
strongSelf.sendButtonLongPressed?(strongSelf.sendButton, recognizer) strongSelf.sendButtonLongPressed?(strongSelf.sendContainerNode, recognizer)
} }
} }
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle) self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle)
} }
func updateTheme(theme: PresentationTheme) { func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) {
self.micButton.updateTheme(theme: theme) self.micButton.updateTheme(theme: theme)
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: []) self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
if [.day, .night].contains(theme.referenceTheme.baseTheme) && theme.chat.message.outgoing.bubble.withWallpaper.fill.count > 1 {
self.backdropNode.isHidden = false
} else {
self.backdropNode.isHidden = true
}
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: wallpaper, bubbleCorners: .init(mainRadius: 1, auxiliaryRadius: 1, mergeBubbleCorners: false))
self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: false, backgroundNode: self.presentationContext?.backgroundNode)
}
private var absoluteRect: (CGRect, CGSize)?
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
self.absoluteRect = (rect, containerSize)
self.backdropNode.update(rect: rect, within: containerSize, transition: transition)
} }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) { func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
self.validLayout = size
transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
self.micButton.layoutItems() self.micButton.layoutItems()
transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: size))
let backgroundSize = CGSize(width: 33.0, height: 33.0)
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize))
self.backgroundNode.cornerRadius = backgroundSize.width / 2.0
transition.updateFrame(node: self.backdropNode, frame: CGRect(origin: CGPoint(x: -2.0, y: -2.0), size: CGSize(width: size.width + 12.0, height: size.height + 2.0)))
if let (rect, containerSize) = self.absoluteRect {
self.backdropNode.update(rect: rect, within: containerSize)
}
var isScheduledMessages = false var isScheduledMessages = false
if case .scheduledMessages = interfaceState.subject { if case .scheduledMessages = interfaceState.subject {
@ -104,12 +156,12 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
sendButtonRadialStatusNode = current sendButtonRadialStatusNode = current
} else { } else {
sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor) sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
sendButtonRadialStatusNode.alpha = self.sendButton.alpha sendButtonRadialStatusNode.alpha = self.sendContainerNode.alpha
self.sendButtonRadialStatusNode = sendButtonRadialStatusNode self.sendButtonRadialStatusNode = sendButtonRadialStatusNode
self.addSubnode(sendButtonRadialStatusNode) self.addSubnode(sendButtonRadialStatusNode)
} }
transition.updateSublayerTransformScale(layer: self.sendButton.layer, scale: CGPoint(x: 0.7575, y: 0.7575)) transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 0.7575, y: 0.7575))
let defaultSendButtonSize: CGFloat = 25.0 let defaultSendButtonSize: CGFloat = 25.0
let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0) let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0)
@ -123,7 +175,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.sendButtonRadialStatusNode = nil self.sendButtonRadialStatusNode = nil
sendButtonRadialStatusNode.removeFromSupernode() sendButtonRadialStatusNode.removeFromSupernode()
} }
transition.updateSublayerTransformScale(layer: self.sendButton.layer, scale: CGPoint(x: 1.0, y: 1.0)) transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 1.0, y: 1.0))
} }
transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))

View File

@ -433,7 +433,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
private let accessoryButtonSpacing: CGFloat = 0.0 private let accessoryButtonSpacing: CGFloat = 0.0
private let accessoryButtonInset: CGFloat = 2.0 private let accessoryButtonInset: CGFloat = 2.0
init(presentationInterfaceState: ChatPresentationInterfaceState, presentController: @escaping (ViewController) -> Void) { init(presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
self.presentationInterfaceState = presentationInterfaceState self.presentationInterfaceState = presentationInterfaceState
self.clippingNode = ASDisplayNode() self.clippingNode = ASDisplayNode()
@ -475,7 +475,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.searchLayoutClearImageNode.isUserInteractionEnabled = false self.searchLayoutClearImageNode.isUserInteractionEnabled = false
self.searchLayoutClearButton.addSubnode(self.searchLayoutClearImageNode) self.searchLayoutClearButton.addSubnode(self.searchLayoutClearImageNode)
self.actionButtons = ChatTextInputActionButtonsNode(theme: presentationInterfaceState.theme, strings: presentationInterfaceState.strings, presentController: presentController) self.actionButtons = ChatTextInputActionButtonsNode(presentationInterfaceState: presentationInterfaceState, presentationContext: presentationContext, presentController: presentController)
self.counterTextNode = ImmediateTextNode() self.counterTextNode = ImmediateTextNode()
self.counterTextNode.textAlignment = .center self.counterTextNode.textAlignment = .center
@ -563,7 +563,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
self.actionButtons.sendButton.addTarget(self, action: #selector(self.sendButtonPressed), forControlEvents: .touchUpInside) self.actionButtons.sendButton.addTarget(self, action: #selector(self.sendButtonPressed), forControlEvents: .touchUpInside)
self.actionButtons.sendButton.alpha = 0.0 self.actionButtons.sendContainerNode.alpha = 0.0
self.actionButtons.updateAccessibility() self.actionButtons.updateAccessibility()
self.actionButtons.expandMediaInputButton.addTarget(self, action: #selector(self.expandButtonPressed), forControlEvents: .touchUpInside) self.actionButtons.expandMediaInputButton.addTarget(self, action: #selector(self.expandButtonPressed), forControlEvents: .touchUpInside)
@ -758,6 +758,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
return minimalHeight return minimalHeight
} }
private var absoluteRect: (CGRect, CGSize)?
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
self.absoluteRect = (rect, containerSize)
if !self.actionButtons.frame.width.isZero {
self.actionButtons.updateAbsoluteRect(CGRect(origin: rect.origin.offsetBy(dx: self.actionButtons.frame.minX, dy: self.actionButtons.frame.minY), size: self.actionButtons.frame.size), within: containerSize, transition: transition)
}
}
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
let previousAdditionalSideInsets = self.validLayout?.3 let previousAdditionalSideInsets = self.validLayout?.3
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
@ -866,7 +875,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.attachmentButton.setImage(PresentationResourcesChat.chatInputPanelAttachmentButtonImage(interfaceState.theme), for: []) self.attachmentButton.setImage(PresentationResourcesChat.chatInputPanelAttachmentButtonImage(interfaceState.theme), for: [])
} }
self.actionButtons.updateTheme(theme: interfaceState.theme) self.actionButtons.updateTheme(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics) let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics)
let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight
@ -968,7 +977,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if !self.actionButtons.animatingSendButton { if !self.actionButtons.animatingSendButton {
let imageNode = self.actionButtons.sendButton.imageNode let imageNode = self.actionButtons.sendButton.imageNode
if transition.isAnimated && !self.actionButtons.sendButton.alpha.isZero && self.actionButtons.sendButton.layer.animation(forKey: "opacity") == nil, let previousImage = imageNode.image { if transition.isAnimated && !self.actionButtons.sendContainerNode.alpha.isZero && self.actionButtons.sendButton.layer.animation(forKey: "opacity") == nil, let previousImage = imageNode.image {
let tempView = UIImageView(image: previousImage) let tempView = UIImageView(image: previousImage)
self.actionButtons.sendButton.view.addSubview(tempView) self.actionButtons.sendButton.view.addSubview(tempView)
tempView.frame = imageNode.frame tempView.frame = imageNode.frame
@ -982,12 +991,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon
if self.actionButtons.sendButtonHasApplyIcon { if self.actionButtons.sendButtonHasApplyIcon {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme), for: [])
} else { } else {
if isScheduledMessages { if isScheduledMessages {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelScheduleButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelScheduleButtonImage(interfaceState.theme), for: [])
} else { } else {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme), for: [])
} }
} }
} }
@ -1392,6 +1401,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
let actionButtonsFrame = CGRect(origin: CGPoint(x: width - rightInset - 43.0 - UIScreenPixel + composeButtonsOffset, y: panelHeight - minimalHeight), size: CGSize(width: 44.0, height: minimalHeight)) let actionButtonsFrame = CGRect(origin: CGPoint(x: width - rightInset - 43.0 - UIScreenPixel + composeButtonsOffset, y: panelHeight - minimalHeight), size: CGSize(width: 44.0, height: minimalHeight))
transition.updateFrame(node: self.actionButtons, frame: actionButtonsFrame) transition.updateFrame(node: self.actionButtons, frame: actionButtonsFrame)
if let (rect, containerSize) = self.absoluteRect {
self.actionButtons.updateAbsoluteRect(CGRect(x: rect.origin.x + actionButtonsFrame.origin.x, y: rect.origin.y + actionButtonsFrame.origin.y, width: actionButtonsFrame.width, height: actionButtonsFrame.height), within: containerSize, transition: transition)
}
if let presentationInterfaceState = self.presentationInterfaceState { if let presentationInterfaceState = self.presentationInterfaceState {
self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), transition: transition, interfaceState: presentationInterfaceState) self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), transition: transition, interfaceState: presentationInterfaceState)
@ -1647,9 +1659,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.menuButton.transform = CATransform3DIdentity self.menuButton.transform = CATransform3DIdentity
self.menuButton.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false) self.menuButton.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false)
} }
prevPreviewInputPanelNode.sendButton.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false)
prevPreviewInputPanelNode.sendButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
} }
var clippingDelta: CGFloat = 0.0 var clippingDelta: CGFloat = 0.0
@ -1761,23 +1770,22 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if self.extendedSearchLayout { if self.extendedSearchLayout {
hideMicButton = true hideMicButton = true
if !self.actionButtons.sendButton.alpha.isZero { if !self.actionButtons.sendContainerNode.alpha.isZero {
self.actionButtons.sendButton.alpha = 0.0 self.actionButtons.sendContainerNode.alpha = 0.0
self.actionButtons.sendButtonRadialStatusNode?.alpha = 0.0 self.actionButtons.sendButtonRadialStatusNode?.alpha = 0.0
self.actionButtons.updateAccessibility() self.actionButtons.updateAccessibility()
if animated { if animated {
self.actionButtons.animatingSendButton = true self.actionButtons.animatingSendButton = true
self.actionButtons.sendButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in self.actionButtons.sendContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.actionButtons.animatingSendButton = false strongSelf.actionButtons.animatingSendButton = false
strongSelf.applyUpdateSendButtonIcon() strongSelf.applyUpdateSendButtonIcon()
} }
}) })
self.actionButtons.sendButton.layer.animateScale(from: 1.0, to: 0.2, duration: 0.2) self.actionButtons.sendContainerNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.2)
self.actionButtons.sendButtonRadialStatusNode?.layer.animateScale(from: 1.0, to: 0.2, duration: 0.2)
self.actionButtons.sendButtonRadialStatusNode?.alpha = 0.0
self.actionButtons.sendButtonRadialStatusNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) self.actionButtons.sendButtonRadialStatusNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.actionButtons.sendButtonRadialStatusNode?.layer.animateScale(from: 1.0, to: 0.2, duration: 0.2)
} }
} }
if self.searchLayoutClearButton.alpha.isZero { if self.searchLayoutClearButton.alpha.isZero {
@ -1800,30 +1808,30 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if (hasText || self.keepSendButtonEnabled && !mediaInputIsActive) { if (hasText || self.keepSendButtonEnabled && !mediaInputIsActive) {
hideMicButton = true hideMicButton = true
if self.actionButtons.sendButton.alpha.isZero { if self.actionButtons.sendContainerNode.alpha.isZero {
self.actionButtons.sendButton.alpha = 1.0 self.actionButtons.sendContainerNode.alpha = 1.0
self.actionButtons.sendButtonRadialStatusNode?.alpha = 1.0 self.actionButtons.sendButtonRadialStatusNode?.alpha = 1.0
self.actionButtons.updateAccessibility() self.actionButtons.updateAccessibility()
if animated { if animated {
self.actionButtons.sendButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) self.actionButtons.sendContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
self.actionButtons.sendButtonRadialStatusNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) self.actionButtons.sendButtonRadialStatusNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
if animateWithBounce { if animateWithBounce {
self.actionButtons.sendButton.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) self.actionButtons.sendContainerNode.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6)
self.actionButtons.sendButtonRadialStatusNode?.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6) self.actionButtons.sendButtonRadialStatusNode?.layer.animateSpring(from: NSNumber(value: Float(0.1)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.6)
} else { } else {
self.actionButtons.sendButton.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25) self.actionButtons.sendContainerNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25)
self.actionButtons.sendButtonRadialStatusNode?.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25) self.actionButtons.sendButtonRadialStatusNode?.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25)
} }
} }
} }
} else { } else {
if !self.actionButtons.sendButton.alpha.isZero { if !self.actionButtons.sendContainerNode.alpha.isZero {
self.actionButtons.sendButton.alpha = 0.0 self.actionButtons.sendContainerNode.alpha = 0.0
self.actionButtons.sendButtonRadialStatusNode?.alpha = 0.0 self.actionButtons.sendButtonRadialStatusNode?.alpha = 0.0
self.actionButtons.updateAccessibility() self.actionButtons.updateAccessibility()
if animated { if animated {
self.actionButtons.animatingSendButton = true self.actionButtons.animatingSendButton = true
self.actionButtons.sendButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in self.actionButtons.sendContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.actionButtons.animatingSendButton = false strongSelf.actionButtons.animatingSendButton = false
strongSelf.applyUpdateSendButtonIcon() strongSelf.applyUpdateSendButtonIcon()
@ -1915,7 +1923,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
@objc func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool { @objc func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
if self.actionButtons.sendButton.supernode != nil && !self.actionButtons.sendButton.isHidden && !self.actionButtons.sendButton.alpha.isZero { if self.actionButtons.sendButton.supernode != nil && !self.actionButtons.sendButton.isHidden && !self.actionButtons.sendContainerNode.alpha.isZero {
self.sendButtonPressed() self.sendButtonPressed()
} }
return false return false
@ -1928,12 +1936,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
if sendButtonHasApplyIcon != self.actionButtons.sendButtonHasApplyIcon { if sendButtonHasApplyIcon != self.actionButtons.sendButtonHasApplyIcon {
self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon
if self.actionButtons.sendButtonHasApplyIcon { if self.actionButtons.sendButtonHasApplyIcon {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme), for: [])
} else { } else {
if case .scheduledMessages = interfaceState.subject { if case .scheduledMessages = interfaceState.subject {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelScheduleButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelScheduleIconImage(interfaceState.theme), for: [])
} else { } else {
self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(interfaceState.theme), for: []) self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme), for: [])
} }
} }
} }

View File

@ -344,7 +344,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) { if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
self.view.addSubview(snapshotView) self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview() snapshotView?.removeFromSuperview()
}) })
} }
@ -379,8 +379,7 @@ final class ChatThemeScreen: ViewController {
private let context: AccountContext private let context: AccountContext
private let initiallySelectedEmoticon: String? private let initiallySelectedEmoticon: String?
private let dismissByTapOutside: Bool private let dismissByTapOutside: Bool
private let previewTheme: (String?) -> Void private let previewTheme: (String?, Bool?) -> Void
private let previewDarkTheme: (Bool) -> Void
private let completion: (String?) -> Void private let completion: (String?) -> Void
private var presentationData: PresentationData private var presentationData: PresentationData
@ -394,13 +393,12 @@ final class ChatThemeScreen: ViewController {
} }
} }
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), initiallySelectedEmoticon: String?, dismissByTapOutside: Bool = true, previewTheme: @escaping (String?) -> Void, previewDarkTheme: @escaping (Bool) -> Void, completion: @escaping (String?) -> Void) { init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), initiallySelectedEmoticon: String?, dismissByTapOutside: Bool = true, previewTheme: @escaping (String?, Bool?) -> Void, completion: @escaping (String?) -> Void) {
self.context = context self.context = context
self.presentationData = updatedPresentationData.initial self.presentationData = updatedPresentationData.initial
self.initiallySelectedEmoticon = initiallySelectedEmoticon self.initiallySelectedEmoticon = initiallySelectedEmoticon
self.dismissByTapOutside = dismissByTapOutside self.dismissByTapOutside = dismissByTapOutside
self.previewTheme = previewTheme self.previewTheme = previewTheme
self.previewDarkTheme = previewDarkTheme
self.completion = completion self.completion = completion
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)
@ -431,21 +429,15 @@ final class ChatThemeScreen: ViewController {
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside) self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside)
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
self.controllerNode.previewTheme = { [weak self] emoticon in self.controllerNode.previewTheme = { [weak self] emoticon, dark in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.previewTheme(emoticon ?? "") strongSelf.previewTheme((emoticon ?? ""), dark)
} }
self.controllerNode.present = { [weak self] c in self.controllerNode.present = { [weak self] c in
self?.present(c, in: .current) self?.present(c, in: .current)
} }
self.controllerNode.previewDarkTheme = { [weak self] dark in
guard let strongSelf = self else {
return
}
strongSelf.previewDarkTheme(dark)
}
self.controllerNode.completion = { [weak self] emoticon in self.controllerNode.completion = { [weak self] emoticon in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -464,7 +456,7 @@ final class ChatThemeScreen: ViewController {
return return
} }
strongSelf.dismiss() strongSelf.dismiss()
strongSelf.previewTheme(nil) strongSelf.previewTheme(nil, nil)
} }
} }
@ -549,15 +541,20 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
} }
} }
private var selectedEmoticonPromise: ValuePromise<String?> private var selectedEmoticonPromise: ValuePromise<String?>
private var isDarkAppearancePromise: ValuePromise<Bool> private var isDarkAppearancePromise: ValuePromise<Bool>
private var isDarkAppearance: Bool = false {
didSet {
self.isDarkAppearancePromise.set(self.isDarkAppearance)
}
}
private var containerLayout: (ContainerViewLayout, CGFloat)? private var containerLayout: (ContainerViewLayout, CGFloat)?
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
var present: ((ViewController) -> Void)? var present: ((ViewController) -> Void)?
var previewTheme: ((String?) -> Void)? var previewTheme: ((String?, Bool?) -> Void)?
var previewDarkTheme: ((Bool) -> Void)?
var completion: ((String?) -> Void)? var completion: ((String?) -> Void)?
var dismiss: (() -> Void)? var dismiss: (() -> Void)?
var cancel: (() -> Void)? var cancel: (() -> Void)?
@ -588,6 +585,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.backgroundNode.clipsToBounds = true self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 16.0 self.backgroundNode.cornerRadius = 16.0
self.isDarkAppearance = self.presentationData.theme.overallDarkAppearance
self.isDarkAppearancePromise = ValuePromise(self.presentationData.theme.overallDarkAppearance) self.isDarkAppearancePromise = ValuePromise(self.presentationData.theme.overallDarkAppearance)
let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
@ -611,7 +609,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
self.switchThemeButton = HighlightTrackingButtonNode() self.switchThemeButton = HighlightTrackingButtonNode()
self.animationNode = AnimationNode(animation: self.presentationData.theme.overallDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme), scale: 1.0) self.animationNode = AnimationNode(animation: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme), scale: 1.0)
self.animationNode.isUserInteractionEnabled = false self.animationNode.isUserInteractionEnabled = false
self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, gloss: false) self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, gloss: false)
@ -676,7 +674,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
strongSelf.animateCrossfade(animateBackground: strongSelf.presentationData.theme.overallDarkAppearance, updateSunIcon: true) strongSelf.animateCrossfade(animateBackground: strongSelf.presentationData.theme.overallDarkAppearance, updateSunIcon: true)
strongSelf.selectedEmoticon = emoticon strongSelf.selectedEmoticon = emoticon
strongSelf.previewTheme?(emoticon) strongSelf.previewTheme?(emoticon, strongSelf.isDarkAppearance)
let _ = ensureThemeVisible(listNode: strongSelf.listNode, emoticon: emoticon, animated: true) let _ = ensureThemeVisible(listNode: strongSelf.listNode, emoticon: emoticon, animated: true)
strongSelf.doneButton.title = emoticon == nil ? strongSelf.presentationData.strings.Conversation_Theme_Reset : strongSelf.presentationData.strings.Conversation_Theme_Apply strongSelf.doneButton.title = emoticon == nil ? strongSelf.presentationData.strings.Conversation_Theme_Reset : strongSelf.presentationData.strings.Conversation_Theme_Apply
@ -774,9 +772,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
} }
let previousTheme = self.presentationData.theme let previousTheme = self.presentationData.theme
self.presentationData = presentationData self.presentationData = presentationData
self.isDarkAppearancePromise.set(presentationData.theme.overallDarkAppearance)
if let effectView = self.effectNode.view as? UIVisualEffectView { if let effectView = self.effectNode.view as? UIVisualEffectView {
effectView.effect = UIBlurEffect(style: presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark) effectView.effect = UIBlurEffect(style: presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark)
} }
@ -794,7 +790,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
if self.animationNode.isPlaying { if self.animationNode.isPlaying {
} else { } else {
self.animationNode.setAnimation(name: self.presentationData.theme.overallDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme)) self.animationNode.setAnimation(name: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme))
} }
} }
@ -814,9 +810,12 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
@objc func switchThemePressed() { @objc func switchThemePressed() {
self.animateCrossfade(animateBackground: true) self.animateCrossfade(animateBackground: true)
self.animationNode.setAnimation(name: self.presentationData.theme.overallDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme)) self.animationNode.setAnimation(name: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme))
self.animationNode.playOnce() self.animationNode.playOnce()
self.previewDarkTheme?(!self.presentationData.theme.overallDarkAppearance)
let isDarkAppearance = !self.isDarkAppearance
self.previewTheme?(self.selectedEmoticon, isDarkAppearance)
self.isDarkAppearance = isDarkAppearance
let _ = ApplicationSpecificNotice.incrementChatSpecificThemesDarkPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3).start() let _ = ApplicationSpecificNotice.incrementChatSpecificThemesDarkPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3).start()
} }
@ -828,7 +827,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
} }
private func animateCrossfade(animateBackground: Bool = true, updateSunIcon: Bool = false) { private func animateCrossfade(animateBackground: Bool = true, updateSunIcon: Bool = false) {
let delay: Double = animateBackground ? 0.0 : 0.2 let delay: Double = 0.2
if let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) { if let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.animationNode.frame snapshotView.frame = self.animationNode.frame
@ -856,16 +855,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
snapshotView?.removeFromSuperview() snapshotView?.removeFromSuperview()
}) })
} }
if animateBackground, let snapshotView = self.cancelButton.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.cancelButton.frame
self.cancelButton.view.superview?.insertSubview(snapshotView, aboveSubview: self.cancelButton.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
self.listNode.forEachVisibleItemNode { node in self.listNode.forEachVisibleItemNode { node in
if let node = node as? ThemeSettingsThemeItemIconNode { if let node = node as? ThemeSettingsThemeItemIconNode {
node.crossfade() node.crossfade()

View File

@ -39,7 +39,12 @@ final class ContactSelectionControllerNode: ASDisplayNode {
var requestMultipleAction: (() -> Void)? var requestMultipleAction: (() -> Void)?
var dismiss: (() -> Void)? var dismiss: (() -> Void)?
var presentationData: PresentationData var presentationData: PresentationData {
didSet {
self.presentationDataPromise.set(.single(self.presentationData))
}
}
private var presentationDataPromise = Promise<PresentationData>()
private let countPanelNode: ContactSelectionCountPanelNode private let countPanelNode: ContactSelectionCountPanelNode
@ -51,7 +56,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
self.displayDeviceContacts = displayDeviceContacts self.displayDeviceContacts = displayDeviceContacts
self.displayCallIcons = displayCallIcons self.displayCallIcons = displayCallIcons
self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, multipleSelection: multipleSelection) self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, multipleSelection: multipleSelection)
self.dimNode = ASDisplayNode() self.dimNode = ASDisplayNode()
@ -151,7 +156,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
} else { } else {
categories.insert(.global) categories.insert(.global)
} }
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: categories, addContact: nil, openPeer: { [weak self] peer in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, addContact: nil, openPeer: { [weak self] peer in
if let strongSelf = self { if let strongSelf = self {
var updated = false var updated = false
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in

View File

@ -590,7 +590,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
} }
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false) let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false)
let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: trendingPacks, installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme) let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme)
let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries)) let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries))
return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty)

View File

@ -1492,6 +1492,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
private var presentationData: PresentationData private var presentationData: PresentationData
fileprivate let cachedDataPromise = Promise<CachedPeerData?>()
let scrollNode: ASScrollNode let scrollNode: ASScrollNode
let headerNode: PeerInfoHeaderNode let headerNode: PeerInfoHeaderNode
@ -1848,7 +1850,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
return items return items
}) }, minHeight: nil)
}))) })))
} }
if strongSelf.searchDisplayController == nil { if strongSelf.searchDisplayController == nil {
@ -1982,7 +1984,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
return items return items
}) }, minHeight: nil)
}))) })))
} }
@ -2842,6 +2844,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return return
} }
strongSelf.updateData(data) strongSelf.updateData(data)
strongSelf.cachedDataPromise.set(.single(data.cachedData))
}) })
if let _ = nearbyPeerDistance { if let _ = nearbyPeerDistance {
@ -3422,7 +3425,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue
} }
let exceptionController = notificationPeerExceptionController(context: context, peer: peer, mode: .users([:]), edit: true, updatePeerSound: { peerId, sound in let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, mode: .users([:]), edit: true, updatePeerSound: { peerId, sound in
let _ = (updatePeerSound(peer.id, sound) let _ = (updatePeerSound(peer.id, sound)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in
@ -3690,7 +3693,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, action: { [weak self] c, f in }, action: { [weak self] c, f in
self?.openReport(user: false, contextController: c, backAction: { c in self?.openReport(user: false, contextController: c, backAction: { c in
if let mainItemsImpl = mainItemsImpl { if let mainItemsImpl = mainItemsImpl {
c.setItems(mainItemsImpl()) c.setItems(mainItemsImpl(), minHeight: nil)
} }
}) })
}))) })))
@ -4432,7 +4435,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}))) })))
if let contextController = contextController { if let contextController = contextController {
contextController.setItems(.single(items)) contextController.setItems(.single(items), minHeight: nil)
} else { } else {
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
if let (layout, navigationHeight) = strongSelf.validLayout { if let (layout, navigationHeight) = strongSelf.validLayout {
@ -4528,7 +4531,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
if let contextController = contextController { if let contextController = contextController {
contextController.setItems(.single(items)) contextController.setItems(.single(items), minHeight: nil)
} else { } else {
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
if let (layout, navigationHeight) = strongSelf.validLayout { if let (layout, navigationHeight) = strongSelf.validLayout {
@ -5284,7 +5287,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: strongSelf.isSettings ? nil : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: strongSelf.isSettings ? nil : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
assetsController?.dismiss() assetsController?.dismiss()
self?.updateProfilePhoto(result) self?.updateProfilePhoto(result)
})) }))
@ -6502,6 +6505,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
fileprivate var presentationData: PresentationData fileprivate var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let cachedDataPromise = Promise<CachedPeerData?>()
private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>() private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>()
private var accountsAndPeersValue: ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])? private var accountsAndPeersValue: ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])?
@ -6756,6 +6760,38 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
self?.controllerNode.scrollToTop() self?.controllerNode.scrollToTop()
} }
let presentationDataSignal: Signal<PresentationData, NoError>
if let updatedPresentationData = updatedPresentationData {
presentationDataSignal = updatedPresentationData.signal
} else {
let themeEmoticon: Signal<String?, NoError> = self.cachedDataPromise.get()
|> map { cachedData -> String? in
if let cachedData = cachedData as? CachedUserData {
return cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedChannelData {
return cachedData.themeEmoticon
} else {
return nil
}
}
|> distinctUntilChanged
presentationDataSignal = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: false), themeEmoticon)
|> map { presentationData, chatThemes, themeEmoticon -> PresentationData in
var presentationData = presentationData
if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoji == themeEmoticon }) {
let customTheme = presentationData.theme.overallDarkAppearance ? theme.darkTheme : theme.theme
if let settings = customTheme.settings, let theme = makePresentationTheme(settings: settings) {
presentationData = presentationData.withUpdated(theme: theme)
presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper)
}
}
return presentationData
}
}
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData) self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
@ -6789,6 +6825,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages, isSettings: self.isSettings, ignoreGroupInCommon: self.ignoreGroupInCommon) self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages, isSettings: self.isSettings, ignoreGroupInCommon: self.ignoreGroupInCommon)
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 }) self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get()) self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())
self._ready.set(self.controllerNode.ready.get()) self._ready.set(self.controllerNode.ready.get())
super.displayNodeDidLoad() super.displayNodeDidLoad()

View File

@ -147,6 +147,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search)
self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.peerSelectionNode.updatePresentationData(self.presentationData)
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {

View File

@ -60,14 +60,22 @@ final class PeerSelectionControllerNode: ASDisplayNode {
var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)? var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)?
var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode, ChatInterfaceForwardOptionsState?) -> Void)? var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode, ChatInterfaceForwardOptionsState?) -> Void)?
private var presentationData: PresentationData private var presentationData: PresentationData {
private var presentationDataDisposable: Disposable? didSet {
self.presentationDataPromise.set(.single(self.presentationData))
}
}
private var presentationDataPromise = Promise<PresentationData>()
private var readyValue = Promise<Bool>() private var readyValue = Promise<Bool>()
var ready: Signal<Bool, NoError> { var ready: Signal<Bool, NoError> {
return self.readyValue.get() return self.readyValue.get()
} }
private var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) {
return (self.presentationData, self.presentationDataPromise.get())
}
init(context: AccountContext, presentationData: PresentationData, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { init(context: AccountContext, presentationData: PresentationData, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
self.context = context self.context = context
self.present = present self.present = present
@ -154,17 +162,6 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
self.addSubnode(self.chatListNode) self.addSubnode(self.chatListNode)
self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings
strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
strongSelf.updateThemeAndStrings()
}
}
})
if hasChatListSelector && hasContactSelector { if hasChatListSelector && hasContactSelector {
self.segmentedControlNode!.selectedIndexChanged = { [weak self] index in self.segmentedControlNode!.selectedIndexChanged = { [weak self] index in
@ -308,8 +305,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.readyValue.set(self.chatListNode.ready) self.readyValue.set(self.chatListNode.ready)
} }
deinit { func updatePresentationData(_ presentationData: PresentationData) {
self.presentationDataDisposable?.dispose() self.presentationData = presentationData
self.updateThemeAndStrings()
} }
private func updateChatPresentationInterfaceState(animated: Bool = true, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) { private func updateChatPresentationInterfaceState(animated: Bool = true, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
@ -520,7 +518,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
if self.chatListNode.supernode != nil { if self.chatListNode.supernode != nil {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, chatPeer, _ in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, chatPeer, _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -597,7 +595,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if self.hasGlobalSearch { if self.hasGlobalSearch {
categories.insert(.global) categories.insert(.global)
} }
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: true, categories: categories, addContact: nil, openPeer: { [weak self] peer in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: self.updatedPresentationData, onlyWriteable: true, categories: categories, addContact: nil, openPeer: { [weak self] peer in
if let strongSelf = self { if let strongSelf = self {
var updated = false var updated = false
var count = 0 var count = 0
@ -693,7 +691,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.recursivelyEnsureDisplaySynchronously(true) self.recursivelyEnsureDisplaySynchronously(true)
contactListNode.enableUpdates = true contactListNode.enableUpdates = true
} else { } else {
let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: [], includeChatList: false))) let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false)))
self.contactListNode = contactListNode self.contactListNode = contactListNode
contactListNode.enableUpdates = true contactListNode.enableUpdates = true
contactListNode.selectionStateUpdated = { [weak self] selectionState in contactListNode.selectionStateUpdated = { [weak self] selectionState in

View File

@ -228,7 +228,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDel
self.textPlaceholderNode.maximumNumberOfLines = 1 self.textPlaceholderNode.maximumNumberOfLines = 1
self.textPlaceholderNode.isUserInteractionEnabled = false self.textPlaceholderNode.isUserInteractionEnabled = false
self.actionButtons = ChatTextInputActionButtonsNode(theme: presentationInterfaceState.theme, strings: presentationInterfaceState.strings, presentController: presentController) self.actionButtons = ChatTextInputActionButtonsNode(presentationInterfaceState: presentationInterfaceState, presentationContext: nil, presentController: presentController)
self.counterTextNode = ImmediateTextNode() self.counterTextNode = ImmediateTextNode()
self.counterTextNode.textAlignment = .center self.counterTextNode.textAlignment = .center
@ -440,7 +440,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDel
self.theme = interfaceState.theme self.theme = interfaceState.theme
self.actionButtons.updateTheme(theme: interfaceState.theme) self.actionButtons.updateTheme(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics) let textFieldMinHeight = calclulateTextFieldMinHeight(interfaceState, metrics: metrics)
let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight let minimalInputHeight: CGFloat = 2.0 + textFieldMinHeight

@ -1 +1 @@
Subproject commit aaede253be4dbfdfcb2258cdb6faa843785192e6 Subproject commit f76a9290fa502a8df473dd872aedf9a553b089cc

View File

@ -215,14 +215,20 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height)) let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds) transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds)
self.contentNode.layer.contentsRect = shiftedContentsRect transition.animateView {
self.contentNode.layer.contentsRect = shiftedContentsRect
}
if let cleanWallpaperNode = self.cleanWallpaperNode { if let cleanWallpaperNode = self.cleanWallpaperNode {
transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds) transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds)
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect transition.animateView {
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect
}
} }
if let gradientWallpaperNode = self.gradientWallpaperNode { if let gradientWallpaperNode = self.gradientWallpaperNode {
transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds) transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds)
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect transition.animateView {
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect
}
} }
} }

View File

@ -7,6 +7,7 @@ import AsyncDisplayKit
import TelegramCore import TelegramCore
import LegacyComponents import LegacyComponents
import TelegramUIPreferences import TelegramUIPreferences
import TelegramPresentationData
import AccountContext import AccountContext
public func requestContextResults(context: AccountContext, botId: PeerId, query: String, peerId: PeerId, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, staleCachedResults: Bool = false, limit: Int = 60) -> Signal<RequestChatContextResultsResult?, NoError> { public func requestContextResults(context: AccountContext, botId: PeerId, query: String, peerId: PeerId, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, staleCachedResults: Bool = false, limit: Int = 60) -> Signal<RequestChatContextResultsResult?, NoError> {
@ -155,14 +156,14 @@ public final class WebSearchController: ViewController {
} }
} }
public init(context: AccountContext, peer: Peer?, chatLocation: ChatLocation?, configuration: SearchBotsConfiguration, mode: WebSearchControllerMode) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer?, chatLocation: ChatLocation?, configuration: SearchBotsConfiguration, mode: WebSearchControllerMode) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.peer = peer self.peer = peer
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.configuration = configuration self.configuration = configuration
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.interfaceState = WebSearchInterfaceState(presentationData: presentationData) self.interfaceState = WebSearchInterfaceState(presentationData: presentationData)
var searchQuery: String? var searchQuery: String?
@ -199,7 +200,7 @@ public final class WebSearchController: ViewController {
} }
|> distinctUntilChanged |> distinctUntilChanged
self.disposable = ((combineLatest(settings, context.sharedContext.presentationData, gifProvider)) self.disposable = ((combineLatest(settings, (updatedPresentationData?.signal ?? context.sharedContext.presentationData), gifProvider))
|> deliverOnMainQueue).start(next: { [weak self] settings, presentationData, gifProvider in |> deliverOnMainQueue).start(next: { [weak self] settings, presentationData, gifProvider in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -25,7 +25,7 @@
size_t height = 0; size_t height = 0;
_animation->size(width, height); _animation->size(width, height);
if (width > 1024 || height > 1024) { if (width > 1536 || height > 1536) {
return nil; return nil;
} }

View File

@ -1,5 +1,5 @@
{ {
"app": "8.0", "app": "8.0.1",
"bazel": "4.0.0", "bazel": "4.0.0",
"xcode": "12.5.1" "xcode": "12.5.1"
} }