mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-08 09:49:51 +00:00
Merge branch 'master' into postbox-refactoring-1
This commit is contained in:
commit
3f61ff85f0
Binary file not shown.
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/TestHearts2.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/TestHearts2.tgs
Normal file
Binary file not shown.
@ -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
25
submodules/AdUI/BUILD
Normal 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",
|
||||||
|
],
|
||||||
|
)
|
||||||
223
submodules/AdUI/Sources/AdInfoScreen.swift
Normal file
223
submodules/AdUI/Sources/AdInfoScreen.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,7 +1145,7 @@ 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] = []
|
||||||
|
|||||||
@ -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)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -14,7 +14,9 @@ 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 {
|
||||||
@ -1168,23 +1171,32 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setItemsSignal(items: Signal<[ContextMenuItem], NoError>) {
|
func getActionsMinHeight() -> CGFloat? {
|
||||||
|
if !self.actionsContainerNode.bounds.height.isZero {
|
||||||
|
return self.actionsContainerNode.bounds.height
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1)
|
||||||
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)
|
||||||
|
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
|
||||||
@ -1958,10 +2010,24 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setItems(_ items: Signal<[ContextMenuItem], NoError>) {
|
public func getActionsMinHeight() -> CGFloat? {
|
||||||
|
if self.isNodeLoaded {
|
||||||
|
return self.controllerNode.getActionsMinHeight()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,8 +34,14 @@ 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 setItems(_ items: Signal<[ContextMenuItem], NoError>) {
|
public func getActionsMinHeight() -> CGFloat? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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 {
|
||||||
@ -237,6 +235,8 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
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
|
||||||
|
|
||||||
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
|
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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? {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
@ -519,16 +530,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
return transaction.getPeerCachedData(peerId: messages[0].id.peerId)
|
return transaction.getPeerCachedData(peerId: messages[0].id.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration), NoError> = combineLatest(
|
let readState = context.account.postbox.transaction { transaction -> CombinedPeerReadState? in
|
||||||
|
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@ -536,12 +552,17 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
|
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig)
|
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, 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
|
||||||
|
|||||||
@ -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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -247,8 +247,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||||||
|
|
||||||
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))
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
@ -775,8 +773,6 @@ 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
|
||||||
@ -857,15 +856,6 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user