Merge commit '3c1afe4014bf770c30c745dc58864aae8682ee5b'

This commit is contained in:
Ali 2022-01-07 21:03:48 +04:00
commit bcd23c4250
50 changed files with 1081 additions and 811 deletions

Binary file not shown.

View File

@ -7228,3 +7228,6 @@ Sorry for the inconvenience.";
"Contacts.Sort" = "Sort"; "Contacts.Sort" = "Sort";
"Contacts.Sort.ByName" = "by Name"; "Contacts.Sort.ByName" = "by Name";
"Contacts.Sort.ByLastSeen" = "by Last Seen"; "Contacts.Sort.ByLastSeen" = "by Last Seen";
"ClearCache.Progress" = "Clearing the Cache • %d%";
"ClearCache.KeepOpenedDescription" = "Please keep this window open until the clearing is completed.";

View File

@ -869,6 +869,9 @@ public final class AnimatedStickerNode: ASDisplayNode {
public var isPlayingChanged: (Bool) -> Void = { _ in } public var isPlayingChanged: (Bool) -> Void = { _ in }
private var overlayColor: (UIColor?, Bool)? = nil
private var size: CGSize?
override public init() { override public init() {
self.queue = sharedQueue self.queue = sharedQueue
self.eventsNode = AnimatedStickerNodeDisplayEvents() self.eventsNode = AnimatedStickerNodeDisplayEvents()
@ -900,10 +903,13 @@ public final class AnimatedStickerNode: ASDisplayNode {
self.renderer = SoftwareAnimationRenderer() self.renderer = SoftwareAnimationRenderer()
//self.renderer = MetalAnimationRenderer() //self.renderer = MetalAnimationRenderer()
#endif #endif
self.renderer?.frame = CGRect(origin: CGPoint(), size: self.bounds.size) self.renderer?.frame = CGRect(origin: CGPoint(), size: self.size ?? self.bounds.size)
if let contents = self.nodeToCopyFrameFrom?.renderer?.contents { if let contents = self.nodeToCopyFrameFrom?.renderer?.contents {
self.renderer?.contents = contents self.renderer?.contents = contents
} }
if let (overlayColor, replace) = self.overlayColor {
self.renderer?.setOverlayColor(overlayColor, replace: replace, animated: false)
}
self.nodeToCopyFrameFrom = nil self.nodeToCopyFrameFrom = nil
self.addSubnode(self.renderer!) self.addSubnode(self.renderer!)
} }
@ -1347,10 +1353,12 @@ public final class AnimatedStickerNode: ASDisplayNode {
} }
public func updateLayout(size: CGSize) { public func updateLayout(size: CGSize) {
self.size = size
self.renderer?.frame = CGRect(origin: CGPoint(), size: size) self.renderer?.frame = CGRect(origin: CGPoint(), size: size)
} }
public func setOverlayColor(_ color: UIColor?, animated: Bool) { public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
self.renderer?.setOverlayColor(color, animated: animated) self.overlayColor = (color, replace)
self.renderer?.setOverlayColor(color, replace: replace, animated: animated)
} }
} }

View File

@ -10,5 +10,5 @@ public enum AnimationRendererFrameType {
protocol AnimationRenderer { protocol AnimationRenderer {
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void)
func setOverlayColor(_ color: UIColor?, animated: Bool) func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool)
} }

View File

@ -8,6 +8,7 @@ import YuvConversion
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
private var highlightedContentNode: ASDisplayNode? private var highlightedContentNode: ASDisplayNode?
private var highlightedColor: UIColor? private var highlightedColor: UIColor?
private var highlightReplacesContent = false
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) { func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
assert(bytesPerRow > 0) assert(bytesPerRow > 0)
@ -53,20 +54,29 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
} }
strongSelf.contents = image?.cgImage strongSelf.contents = image?.cgImage
strongSelf.updateHighlightedContentNode() strongSelf.updateHighlightedContentNode()
if strongSelf.highlightedContentNode?.frame != strongSelf.bounds {
strongSelf.highlightedContentNode?.frame = strongSelf.bounds
}
completion() completion()
} }
} }
} }
private func updateHighlightedContentNode() { private func updateHighlightedContentNode() {
guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor, let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID else { guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor else {
return return
} }
if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID {
(highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate) (highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
}
highlightedContentNode.tintColor = highlightedColor highlightedContentNode.tintColor = highlightedColor
if self.highlightReplacesContent {
self.contents = nil
}
} }
func setOverlayColor(_ color: UIColor?, animated: Bool) { func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
self.highlightReplacesContent = replace
var updated = false var updated = false
if let current = self.highlightedColor, let color = color { if let current = self.highlightedColor, let color = color {
updated = !current.isEqual(color) updated = !current.isEqual(color)

View File

@ -1330,7 +1330,7 @@ public final class CalendarMessageScreen: ViewController {
selectionToolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: toolbarText, isEnabled: true, color: .custom(self.selectionState?.dayRange != nil ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemDisabledTextColor))), transition: transition) selectionToolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: Toolbar(leftAction: nil, rightAction: nil, middleAction: ToolbarAction(title: toolbarText, isEnabled: true, color: .custom(self.selectionState?.dayRange != nil ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemDisabledTextColor))), transition: transition)
} else { } else {
selectionToolbarNode = ToolbarNode( selectionToolbarNode = ToolbarNode(
theme: TabBarControllerTheme( theme: ToolbarTheme(
rootControllerTheme: self.presentationData.theme), rootControllerTheme: self.presentationData.theme),
displaySeparator: true, displaySeparator: true,
left: { left: {

View File

@ -115,6 +115,7 @@ public final class CallListController: TelegramBaseController {
self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle
self.tabBarItem.image = icon self.tabBarItem.image = icon
self.tabBarItem.selectedImage = icon self.tabBarItem.selectedImage = icon
self.tabBarItem.animationName = "TabCalls"
} }
self.segmentedTitleView.indexUpdated = { [weak self] index in self.segmentedTitleView.indexUpdated = { [weak self] index in

View File

@ -206,6 +206,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.tabBarItem.image = icon self.tabBarItem.image = icon
self.tabBarItem.selectedImage = icon self.tabBarItem.selectedImage = icon
self.tabBarItem.animationName = "TabChats"
let leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) let leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit

View File

@ -1116,7 +1116,7 @@ final class ChatListControllerNode: ASDisplayNode {
self.searchDisplayController?.updatePresentationData(presentationData) self.searchDisplayController?.updatePresentationData(presentationData)
if let toolbarNode = self.toolbarNode { if let toolbarNode = self.toolbarNode {
toolbarNode.updateTheme(TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) toolbarNode.updateTheme(ToolbarTheme(rootControllerTheme: self.presentationData.theme))
} }
} }
@ -1149,7 +1149,7 @@ final class ChatListControllerNode: ASDisplayNode {
transition.updateFrame(node: toolbarNode, frame: tabBarFrame) transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
} else { } else {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in let toolbarNode = ToolbarNode(theme: ToolbarTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in
self?.toolbarActionSelected?(.left) self?.toolbarActionSelected?(.left)
}, right: { [weak self] in }, right: { [weak self] in
self?.toolbarActionSelected?(.right) self?.toolbarActionSelected?(.right)

View File

@ -192,6 +192,7 @@ public class ContactsController: ViewController {
self.tabBarItem.image = icon self.tabBarItem.image = icon
self.tabBarItem.selectedImage = icon self.tabBarItem.selectedImage = icon
self.tabBarItem.animationName = "TabContacts"
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)

View File

@ -940,7 +940,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers)) entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground)) entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
entries.append(.snow(experimentalSettings.snow))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
} }

View File

@ -1,5 +1,10 @@
import Foundation import Foundation
import UIKit import UIKit
import AsyncDisplayKit
public protocol ActionSheetGroupOverlayNode: ASDisplayNode {
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
}
open class ActionSheetController: ViewController, PresentableController, StandalonePresentableController { open class ActionSheetController: ViewController, PresentableController, StandalonePresentableController {
private var actionSheetNode: ActionSheetControllerNode { private var actionSheetNode: ActionSheetControllerNode {
@ -83,4 +88,10 @@ open class ActionSheetController: ViewController, PresentableController, Standal
self.actionSheetNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f) self.actionSheetNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f)
} }
} }
public func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) {
if self.isViewLoaded {
self.actionSheetNode.setItemGroupOverlayNode(groupIndex: groupIndex, node: node)
}
}
} }

View File

@ -219,7 +219,11 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.itemGroupsContainerNode.setGroups(groups) self.itemGroupsContainerNode.setGroups(groups)
} }
public func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) { func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) {
self.itemGroupsContainerNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f) self.itemGroupsContainerNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f)
} }
func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) {
self.itemGroupsContainerNode.setItemGroupOverlayNode(groupIndex: groupIndex, node: node)
}
} }

View File

@ -18,6 +18,8 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
private var validLayout: CGSize? private var validLayout: CGSize?
private var overlayNode: ActionSheetGroupOverlayNode?
init(theme: ActionSheetControllerTheme) { init(theme: ActionSheetControllerTheme) {
self.theme = theme self.theme = theme
@ -66,6 +68,31 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
self.addSubnode(self.clippingNode) self.addSubnode(self.clippingNode)
} }
func setOverlayNode(_ overlayNode: ActionSheetGroupOverlayNode?) {
guard self.overlayNode == nil else {
return
}
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
overlayNode?.alpha = 0.0
self.overlayNode = overlayNode
if let overlayNode = overlayNode {
transition.updateAlpha(node: overlayNode, alpha: 1.0)
self.clippingNode.addSubnode(overlayNode)
} else if let overlayNode = self.overlayNode {
overlayNode.removeFromSupernode()
}
if let size = self.validLayout, let overlayNode = overlayNode {
overlayNode.updateLayout(size: size, transition: .immediate)
}
for node in self.itemNodes {
transition.updateAlpha(node: node, alpha: 0.0)
}
}
func updateItemNodes(_ nodes: [ActionSheetItemNode], leadingVisibleNodeCount: CGFloat = 1000.0) { func updateItemNodes(_ nodes: [ActionSheetItemNode], leadingVisibleNodeCount: CGFloat = 1000.0) {
for node in self.itemNodes { for node in self.itemNodes {
if !nodes.contains(where: { $0 === node }) { if !nodes.contains(where: { $0 === node }) {
@ -140,6 +167,10 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -scrollViewContentInsets.top) self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -scrollViewContentInsets.top)
} }
if let overlayNode = self.overlayNode {
overlayNode.updateLayout(size: size, transition: transition)
}
self.updateOverscroll(size: size, transition: transition) self.updateOverscroll(size: size, transition: transition)
return size return size

View File

@ -112,4 +112,8 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
self.groups[groupIndex] = ActionSheetItemGroup(items: groupItems) self.groups[groupIndex] = ActionSheetItemGroup(items: groupItems)
} }
func setItemGroupOverlayNode(groupIndex: Int, node: ActionSheetGroupOverlayNode) {
self.groupNodes[groupIndex].setOverlayNode(node)
}
} }

View File

@ -3,442 +3,25 @@ import UIKit
import AsyncDisplayKit import AsyncDisplayKit
import SwiftSignalKit import SwiftSignalKit
public final class TabBarControllerTheme { public enum TabBarItemSwipeDirection {
public let backgroundColor: UIColor case left
public let tabBarBackgroundColor: UIColor case right
public let tabBarSeparatorColor: UIColor
public let tabBarIconColor: UIColor
public let tabBarSelectedIconColor: UIColor
public let tabBarTextColor: UIColor
public let tabBarSelectedTextColor: UIColor
public let tabBarBadgeBackgroundColor: UIColor
public let tabBarBadgeStrokeColor: UIColor
public let tabBarBadgeTextColor: UIColor
public let tabBarExtractedIconColor: UIColor
public let tabBarExtractedTextColor: UIColor
public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarIconColor: UIColor, tabBarSelectedIconColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor, tabBarExtractedIconColor: UIColor, tabBarExtractedTextColor: UIColor) {
self.backgroundColor = backgroundColor
self.tabBarBackgroundColor = tabBarBackgroundColor
self.tabBarSeparatorColor = tabBarSeparatorColor
self.tabBarIconColor = tabBarIconColor
self.tabBarSelectedIconColor = tabBarSelectedIconColor
self.tabBarTextColor = tabBarTextColor
self.tabBarSelectedTextColor = tabBarSelectedTextColor
self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor
self.tabBarBadgeStrokeColor = tabBarBadgeStrokeColor
self.tabBarBadgeTextColor = tabBarBadgeTextColor
self.tabBarExtractedIconColor = tabBarExtractedIconColor
self.tabBarExtractedTextColor = tabBarExtractedTextColor
}
}
public final class TabBarItemInfo: NSObject {
public let previewing: Bool
public init(previewing: Bool) {
self.previewing = previewing
super.init()
}
override public func isEqual(_ object: Any?) -> Bool {
if let object = object as? TabBarItemInfo {
if self.previewing != object.previewing {
return false
}
return true
} else {
return false
}
} }
public static func ==(lhs: TabBarItemInfo, rhs: TabBarItemInfo) -> Bool { public protocol TabBarController: ViewController {
if lhs.previewing != rhs.previewing { var currentController: ViewController? { get }
return false var controllers: [ViewController] { get }
} var selectedIndex: Int { get set }
return true
}
}
public enum TabBarContainedControllerPresentationUpdate {
case dismiss
case present
case progress(CGFloat)
}
public protocol TabBarContainedController { func setControllers(_ controllers: [ViewController], selectedIndex: Int?)
func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode])
func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate)
}
open class TabBarController: ViewController { func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition)
private var validLayout: ContainerViewLayout?
private var tabBarControllerNode: TabBarControllerNode { func frameForControllerTab(controller: ViewController) -> CGRect?
get { func isPointInsideContentArea(point: CGPoint) -> Bool
return super.displayNode as! TabBarControllerNode func sourceNodesForController(at index: Int) -> [ASDisplayNode]?
}
}
open override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition)
for controller in self.controllers { func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition)
controller.updateNavigationCustomData(data, progress: progress, transition: transition) func updateLayout(transition: ContainedViewLayoutTransition)
}
}
public private(set) var controllers: [ViewController] = []
private let _ready = Promise<Bool>()
override open var ready: Promise<Bool> {
return self._ready
}
private var _selectedIndex: Int?
public var selectedIndex: Int {
get {
if let _selectedIndex = self._selectedIndex {
return _selectedIndex
} else {
return 0
}
} set(value) {
let index = max(0, min(self.controllers.count - 1, value))
if _selectedIndex != index {
_selectedIndex = index
self.updateSelectedIndex()
}
}
}
var currentController: ViewController?
private let pendingControllerDisposable = MetaDisposable()
private var theme: TabBarControllerTheme
public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
self.theme = theme
super.init(navigationBarPresentationData: nil)
self.scrollToTop = { [weak self] in
guard let strongSelf = self else {
return
}
if let controller = strongSelf.currentController {
controller.scrollToTop?()
}
}
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.pendingControllerDisposable.dispose()
}
public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
if self.theme !== theme {
self.theme = theme
if self.isNodeLoaded {
self.tabBarControllerNode.updateTheme(theme)
}
}
}
private var debugTapCounter: (Double, Int) = (0.0, 0)
public func sourceNodesForController(at index: Int) -> [ASDisplayNode]? {
return self.tabBarControllerNode.tabBarNode.sourceNodesForController(at: index)
}
public func frameForControllerTab(controller: ViewController) -> CGRect? {
if let index = self.controllers.firstIndex(of: controller) {
return self.tabBarControllerNode.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarControllerNode.tabBarNode.view.convert($0, to: self.view) }
} else {
return nil
}
}
public func isPointInsideContentArea(point: CGPoint) -> Bool {
if point.y < self.tabBarControllerNode.tabBarNode.frame.minY {
return true
}
return false
}
public func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
self.tabBarControllerNode.updateIsTabBarEnabled(value, transition: transition)
}
public func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) {
self.tabBarControllerNode.tabBarHidden = value
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .slide))
}
}
override open func loadDisplayNode() {
self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in
if let strongSelf = self {
if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController {
controller.presentTabBarPreviewingController(sourceNodes: itemNodes)
return
}
if strongSelf.selectedIndex == index {
let timestamp = CACurrentMediaTime()
if strongSelf.debugTapCounter.0 < timestamp - 0.4 {
strongSelf.debugTapCounter.0 = timestamp
strongSelf.debugTapCounter.1 = 0
}
if strongSelf.debugTapCounter.0 >= timestamp - 0.4 {
strongSelf.debugTapCounter.0 = timestamp
strongSelf.debugTapCounter.1 += 1
}
if strongSelf.debugTapCounter.1 >= 10 {
strongSelf.debugTapCounter.1 = 0
strongSelf.controllers[index].tabBarItemDebugTapAction?()
}
}
if let validLayout = strongSelf.validLayout {
var updatedLayout = validLayout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if validLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = validLayout.insets(options: options).bottom
if !validLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
strongSelf.controllers[index].containerLayoutUpdated(updatedLayout, transition: .immediate)
}
let startTime = CFAbsoluteTimeGetCurrent()
strongSelf.pendingControllerDisposable.set((strongSelf.controllers[index].ready.get()
|> deliverOnMainQueue).start(next: { _ in
if let strongSelf = self {
let readyTime = CFAbsoluteTimeGetCurrent() - startTime
if readyTime > 0.5 {
print("TabBarController: controller took \(readyTime) to become ready")
}
if strongSelf.selectedIndex == index {
if let controller = strongSelf.currentController {
if longTap {
controller.longTapWithTabBar?()
} else {
controller.scrollToTopWithTabBar?()
}
}
} else {
strongSelf.selectedIndex = index
}
}
}))
}
}, contextAction: { [weak self] index, node, gesture in
guard let strongSelf = self else {
return
}
if index >= 0 && index < strongSelf.controllers.count {
strongSelf.controllers[index].tabBarItemContextAction(sourceNode: node, gesture: gesture)
}
}, swipeAction: { [weak self] index, direction in
guard let strongSelf = self else {
return
}
if index >= 0 && index < strongSelf.controllers.count {
strongSelf.controllers[index].tabBarItemSwipeAction(direction: direction)
}
}, toolbarActionSelected: { [weak self] action in
self?.currentController?.toolbarActionSelected(action: action)
}, disabledPressed: { [weak self] in
self?.currentController?.tabBarDisabledAction()
})
self.updateSelectedIndex()
self.displayNodeDidLoad()
}
public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
let alpha = max(0.0, min(1.0, alpha))
transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.backgroundNode, alpha: alpha, delay: 0.15)
transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.15)
}
private func updateSelectedIndex() {
if !self.isNodeLoaded {
return
}
self.tabBarControllerNode.tabBarNode.selectedIndex = self.selectedIndex
if let currentController = self.currentController {
currentController.willMove(toParent: nil)
self.tabBarControllerNode.currentControllerNode = nil
currentController.removeFromParent()
currentController.didMove(toParent: nil)
self.currentController = nil
}
if let _selectedIndex = self._selectedIndex, _selectedIndex < self.controllers.count {
self.currentController = self.controllers[_selectedIndex]
}
if let currentController = self.currentController {
currentController.willMove(toParent: self)
self.tabBarControllerNode.currentControllerNode = currentController.displayNode
self.addChild(currentController)
currentController.didMove(toParent: self)
currentController.displayNode.recursivelyEnsureDisplaySynchronously(true)
self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle
} else {
}
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .immediate)
}
}
public func updateLayout(transition: ContainedViewLayoutTransition = .immediate) {
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: transition)
}
}
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.validLayout = layout
self.tabBarControllerNode.containerLayoutUpdated(layout, toolbar: self.currentController?.toolbar, transition: transition)
if let currentController = self.currentController {
currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size)
var updatedLayout = layout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if updatedLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = updatedLayout.insets(options: options).bottom
if !updatedLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
currentController.containerLayoutUpdated(updatedLayout, transition: transition)
}
}
override open func navigationStackConfigurationUpdated(next: [ViewController]) {
super.navigationStackConfigurationUpdated(next: next)
for controller in self.controllers {
controller.navigationStackConfigurationUpdated(next: next)
}
}
override open func viewWillDisappear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewWillDisappear(animated)
}
}
override open func viewWillAppear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewWillAppear(animated)
}
}
override open func viewDidAppear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewDidAppear(animated)
}
}
override open func viewDidDisappear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewDidDisappear(animated)
}
}
public func setControllers(_ controllers: [ViewController], selectedIndex: Int?) {
var updatedSelectedIndex: Int? = selectedIndex
if updatedSelectedIndex == nil, let selectedIndex = self._selectedIndex, selectedIndex < self.controllers.count {
if let index = controllers.firstIndex(where: { $0 === self.controllers[selectedIndex] }) {
updatedSelectedIndex = index
} else {
updatedSelectedIndex = 0
}
}
self.controllers = controllers
self.tabBarControllerNode.tabBarNode.tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) })
let signals = combineLatest(self.controllers.map({ $0.tabBarItem }).map { tabBarItem -> Signal<Bool, NoError> in
if let tabBarItem = tabBarItem, tabBarItem.image == nil {
return Signal { [weak tabBarItem] subscriber in
let index = tabBarItem?.addSetImageListener({ image in
if image != nil {
subscriber.putNext(true)
subscriber.putCompletion()
}
})
return ActionDisposable {
Queue.mainQueue().async {
if let index = index {
tabBarItem?.removeSetImageListener(index)
}
}
}
}
|> runOn(.mainQueue())
} else {
return .single(true)
}
})
|> map { items -> Bool in
for item in items {
if !item {
return false
}
}
return true
}
|> filter { $0 }
|> take(1)
let allReady = signals
|> deliverOnMainQueue
|> mapToSignal { _ -> Signal<Bool, NoError> in
// wait for tab bar items to be applied
return .single(true)
|> delay(0.0, queue: Queue.mainQueue())
}
self._ready.set(allReady)
if let updatedSelectedIndex = updatedSelectedIndex {
self.selectedIndex = updatedSelectedIndex
self.updateSelectedIndex()
}
}
} }

View File

@ -1,83 +0,0 @@
import Foundation
import UIKit
import SwiftSignalKit
final class TabBarTapRecognizer: UIGestureRecognizer {
private let tap: (CGPoint) -> Void
private let longTap: (CGPoint) -> Void
private var initialLocation: CGPoint?
private var longTapTimer: SwiftSignalKit.Timer?
init(tap: @escaping (CGPoint) -> Void, longTap: @escaping (CGPoint) -> Void) {
self.tap = tap
self.longTap = longTap
super.init(target: nil, action: nil)
}
override func reset() {
super.reset()
self.initialLocation = nil
self.longTapTimer?.invalidate()
self.longTapTimer = nil
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if self.initialLocation == nil {
self.initialLocation = touches.first?.location(in: self.view)
let longTapTimer = SwiftSignalKit.Timer(timeout: 0.4, repeat: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
if let initialLocation = strongSelf.initialLocation {
strongSelf.initialLocation = nil
strongSelf.longTap(initialLocation)
strongSelf.state = .ended
}
}, queue: Queue.mainQueue())
self.longTapTimer?.invalidate()
self.longTapTimer = longTapTimer
longTapTimer.start()
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
if let initialLocation = self.initialLocation {
self.initialLocation = nil
self.longTapTimer?.invalidate()
self.longTapTimer = nil
self.tap(initialLocation)
self.state = .ended
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if let initialLocation = self.initialLocation, let location = touches.first?.location(in: self.view) {
let deltaX = initialLocation.x - location.x
let deltaY = initialLocation.y - location.y
if deltaX * deltaX + deltaY * deltaY > 4.0 {
self.longTapTimer?.invalidate()
self.longTapTimer = nil
self.initialLocation = nil
self.state = .failed
}
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
self.initialLocation = nil
self.longTapTimer?.invalidate()
self.longTapTimer = nil
self.state = .failed
}
}

View File

@ -1065,7 +1065,15 @@ public class TextNode: ASDisplayNode {
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString) let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(lineConstrainedSize.width), truncationType, truncationToken) ?? truncationToken coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(lineConstrainedSize.width), truncationType, truncationToken) ?? truncationToken
brokenLineRange.length = CTLineGetGlyphCount(coreTextLine) - 1 let runs = (CTLineGetGlyphRuns(coreTextLine) as [AnyObject]) as! [CTRun]
for run in runs {
let runAttributes: NSDictionary = CTRunGetAttributes(run)
if let _ = runAttributes["CTForegroundColorFromContext"] {
brokenLineRange.length = CTRunGetStringRange(run).location
break
}
}
// brokenLineRange.length = CTLineGetGlyphCount(coreTextLine) - 1
if brokenLineRange.location + brokenLineRange.length > attributedString.length { if brokenLineRange.location + brokenLineRange.length > attributedString.length {
brokenLineRange.length = attributedString.length - brokenLineRange.location brokenLineRange.length = attributedString.length - brokenLineRange.location
} }
@ -1330,7 +1338,9 @@ public class TextNode: ASDisplayNode {
context.saveGState() context.saveGState()
var clipRects: [CGRect] = [] var clipRects: [CGRect] = []
for spoiler in line.spoilerWords { for spoiler in line.spoilerWords {
clipRects.append(spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) var spoilerClipRect = spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
spoilerClipRect.size.height += 1.0 + UIScreenPixel
clipRects.append(spoilerClipRect)
} }
context.clip(to: clipRects) context.clip(to: clipRects)
} }
@ -1365,7 +1375,9 @@ public class TextNode: ASDisplayNode {
context.restoreGState() context.restoreGState()
} else { } else {
for spoiler in line.spoilerWords { for spoiler in line.spoilerWords {
clearRects.append(spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) var spoilerClearRect = spoiler.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
spoilerClearRect.size.height += 1.0 + UIScreenPixel
clearRects.append(spoilerClearRect)
} }
} }
} }

View File

@ -2,8 +2,28 @@ import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
public enum ToolbarActionOption {
case left
case right
case middle
}
public final class ToolbarTheme {
public let barBackgroundColor: UIColor
public let barSeparatorColor: UIColor
public let barTextColor: UIColor
public let barSelectedTextColor: UIColor
public init(barBackgroundColor: UIColor, barSeparatorColor: UIColor, barTextColor: UIColor, barSelectedTextColor: UIColor) {
self.barBackgroundColor = barBackgroundColor
self.barSeparatorColor = barSeparatorColor
self.barTextColor = barTextColor
self.barSelectedTextColor = barSelectedTextColor
}
}
public final class ToolbarNode: ASDisplayNode { public final class ToolbarNode: ASDisplayNode {
private var theme: TabBarControllerTheme private var theme: ToolbarTheme
private let displaySeparator: Bool private let displaySeparator: Bool
public var left: () -> Void public var left: () -> Void
public var right: () -> Void public var right: () -> Void
@ -18,14 +38,14 @@ public final class ToolbarNode: ASDisplayNode {
private let middleTitle: ImmediateTextNode private let middleTitle: ImmediateTextNode
private let middleButton: HighlightTrackingButtonNode private let middleButton: HighlightTrackingButtonNode
public init(theme: TabBarControllerTheme, displaySeparator: Bool = false, left: @escaping () -> Void = {}, right: @escaping () -> Void = {}, middle: @escaping () -> Void = {}) { public init(theme: ToolbarTheme, displaySeparator: Bool = false, left: @escaping () -> Void = {}, right: @escaping () -> Void = {}, middle: @escaping () -> Void = {}) {
self.theme = theme self.theme = theme
self.displaySeparator = displaySeparator self.displaySeparator = displaySeparator
self.left = left self.left = left
self.right = right self.right = right
self.middle = middle self.middle = middle
self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor) self.backgroundNode = NavigationBackgroundNode(color: theme.barBackgroundColor)
self.separatorNode = ASDisplayNode() self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true self.separatorNode.isLayerBacked = true
@ -100,9 +120,9 @@ public final class ToolbarNode: ASDisplayNode {
self.middleButton.accessibilityTraits = .button self.middleButton.accessibilityTraits = .button
} }
public func updateTheme(_ theme: TabBarControllerTheme) { public func updateTheme(_ theme: ToolbarTheme) {
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor self.separatorNode.backgroundColor = theme.barSeparatorColor
self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate) self.backgroundNode.updateColor(color: theme.barBackgroundColor, transition: .immediate)
} }
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, bottomInset: CGFloat, toolbar: Toolbar, transition: ContainedViewLayoutTransition) { public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, bottomInset: CGFloat, toolbar: Toolbar, transition: ContainedViewLayoutTransition) {
@ -112,10 +132,10 @@ public final class ToolbarNode: ASDisplayNode {
var sideInset: CGFloat = 16.0 var sideInset: CGFloat = 16.0
self.leftTitle.attributedText = NSAttributedString(string: toolbar.leftAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.leftAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor) self.leftTitle.attributedText = NSAttributedString(string: toolbar.leftAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.leftAction?.isEnabled ?? false) ? self.theme.barSelectedTextColor : self.theme.barTextColor)
self.leftButton.accessibilityLabel = toolbar.leftAction?.title self.leftButton.accessibilityLabel = toolbar.leftAction?.title
self.rightTitle.attributedText = NSAttributedString(string: toolbar.rightAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.rightAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor) self.rightTitle.attributedText = NSAttributedString(string: toolbar.rightAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.rightAction?.isEnabled ?? false) ? self.theme.barSelectedTextColor : self.theme.barTextColor)
self.rightButton.accessibilityLabel = toolbar.rightAction?.title self.rightButton.accessibilityLabel = toolbar.rightAction?.title
let middleColor: UIColor let middleColor: UIColor
@ -123,15 +143,15 @@ public final class ToolbarNode: ASDisplayNode {
if middleAction.isEnabled { if middleAction.isEnabled {
switch middleAction.color { switch middleAction.color {
case .accent: case .accent:
middleColor = self.theme.tabBarSelectedTextColor middleColor = self.theme.barSelectedTextColor
case let .custom(color): case let .custom(color):
middleColor = color middleColor = color
} }
} else { } else {
middleColor = self.theme.tabBarTextColor middleColor = self.theme.barTextColor
} }
} else { } else {
middleColor = self.theme.tabBarTextColor middleColor = self.theme.barTextColor
} }
self.middleTitle.attributedText = NSAttributedString(string: toolbar.middleAction?.title ?? "", font: Font.regular(17.0), textColor: middleColor) self.middleTitle.attributedText = NSAttributedString(string: toolbar.middleAction?.title ?? "", font: Font.regular(17.0), textColor: middleColor)
self.middleButton.accessibilityLabel = toolbar.middleAction?.title self.middleButton.accessibilityLabel = toolbar.middleAction?.title

View File

@ -191,7 +191,7 @@ public enum TabBarItemContextActionType {
public let statusBar: StatusBar public let statusBar: StatusBar
public let navigationBar: NavigationBar? public let navigationBar: NavigationBar?
private(set) var toolbar: Toolbar? public private(set) var toolbar: Toolbar?
public var displayNavigationBar = true public var displayNavigationBar = true
open var navigationBarRequiresEntireLayoutUpdate: Bool { open var navigationBarRequiresEntireLayoutUpdate: Bool {

View File

@ -501,7 +501,7 @@ open class ItemListControllerNode: ASDisplayNode {
transition.updateFrame(node: toolbarNode, frame: toolbarFrame) transition.updateFrame(node: toolbarNode, frame: toolbarFrame)
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: transition) toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: transition)
} else if let theme = self.theme { } else if let theme = self.theme {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: theme), displaySeparator: true) let toolbarNode = ToolbarNode(theme: ToolbarTheme(rootControllerTheme: theme), displaySeparator: true)
toolbarNode.frame = toolbarFrame toolbarNode.frame = toolbarFrame
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: .immediate) toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: .immediate)
self.addSubnode(toolbarNode) self.addSubnode(toolbarNode)

View File

@ -1244,7 +1244,7 @@ public final class MediaBox {
} }
} }
public func removeOtherCachedResources(paths: [String]) -> Signal<Void, NoError> { public func removeOtherCachedResources(paths: [String]) -> Signal<Float, NoError> {
return Signal { subscriber in return Signal { subscriber in
self.dataQueue.async { self.dataQueue.async {
var keepPrefixes: [String] = [] var keepPrefixes: [String] = []
@ -1254,14 +1254,31 @@ public final class MediaBox {
keepPrefixes.append(resourcePaths.complete) keepPrefixes.append(resourcePaths.complete)
} }
var count: Int = 0
let totalCount = paths.count
if totalCount == 0 {
subscriber.putNext(1.0)
subscriber.putCompletion()
return
}
let reportProgress: (Int) -> Void = { count in
Queue.mainQueue().async {
subscriber.putNext(min(1.0, Float(count) / Float(totalCount)))
}
}
outer: for path in paths { outer: for path in paths {
for prefix in keepPrefixes { for prefix in keepPrefixes {
if path.starts(with: prefix) { if path.starts(with: prefix) {
count += 1
continue outer continue outer
} }
} }
count += 1
unlink(self.basePath + "/" + path) unlink(self.basePath + "/" + path)
reportProgress(count)
} }
subscriber.putCompletion() subscriber.putCompletion()
} }
@ -1269,27 +1286,10 @@ public final class MediaBox {
} }
} }
public func removeCachedResources(_ ids: Set<MediaResourceId>, force: Bool = false) -> Signal<Void, NoError> { public func removeCachedResources(_ ids: Set<MediaResourceId>, force: Bool = false) -> Signal<Float, NoError> {
return Signal { subscriber in return Signal { subscriber in
self.dataQueue.async { self.dataQueue.async {
for id in ids {
if !force {
if self.fileContexts[id] != nil {
continue
}
if self.keepResourceContexts[id] != nil {
continue
}
}
let paths = self.storePathsForId(id)
unlink(paths.complete)
unlink(paths.partial)
unlink(paths.partial + ".meta")
self.fileContexts.removeValue(forKey: id)
}
let uniqueIds = Set(ids.map { $0.stringRepresentation }) let uniqueIds = Set(ids.map { $0.stringRepresentation })
var pathsToDelete: [String] = [] var pathsToDelete: [String] = []
for cacheType in ["cache", "short-cache"] { for cacheType in ["cache", "short-cache"] {
@ -1309,8 +1309,46 @@ public final class MediaBox {
} }
} }
var count: Int = 0
let totalCount = ids.count * 3 + pathsToDelete.count
if totalCount == 0 {
subscriber.putNext(1.0)
subscriber.putCompletion()
return
}
let reportProgress: (Int) -> Void = { count in
Queue.mainQueue().async {
subscriber.putNext(min(1.0, Float(count) / Float(totalCount)))
}
}
for id in ids {
if !force {
if self.fileContexts[id] != nil {
count += 3
reportProgress(count)
continue
}
if self.keepResourceContexts[id] != nil {
count += 3
reportProgress(count)
continue
}
}
let paths = self.storePathsForId(id)
unlink(paths.complete)
unlink(paths.partial)
unlink(paths.partial + ".meta")
self.fileContexts.removeValue(forKey: id)
count += 3
reportProgress(count)
}
for path in pathsToDelete { for path in pathsToDelete {
unlink(path) unlink(path)
count += 1
reportProgress(count)
} }
subscriber.putCompletion() subscriber.putCompletion()

View File

@ -1,6 +1,7 @@
import Foundation import Foundation
import UIKit import UIKit
import Display import Display
import AsyncDisplayKit
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramCore import TelegramCore
@ -14,6 +15,8 @@ import AccountContext
import ItemListPeerItem import ItemListPeerItem
import DeleteChatPeerActionSheetItem import DeleteChatPeerActionSheetItem
import UndoUI import UndoUI
import AnimatedStickerNode
import TelegramAnimatedStickerNode
private func totalDiskSpace() -> Int64 { private func totalDiskSpace() -> Int64 {
do { do {
@ -275,15 +278,15 @@ private enum StorageUsageEntry: ItemListNodeEntry {
} }
} }
private struct StoragUsageState: Equatable { private struct StorageUsageState: Equatable {
let peerIdWithRevealedOptions: PeerId? let peerIdWithRevealedOptions: PeerId?
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StoragUsageState { func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StorageUsageState {
return StoragUsageState(peerIdWithRevealedOptions: peerIdWithRevealedOptions) return StorageUsageState(peerIdWithRevealedOptions: peerIdWithRevealedOptions)
} }
} }
private func storageUsageControllerEntries(presentationData: PresentationData, cacheSettings: CacheStorageSettings, cacheStats: CacheUsageStatsResult?, state: StoragUsageState) -> [StorageUsageEntry] { private func storageUsageControllerEntries(presentationData: PresentationData, cacheSettings: CacheStorageSettings, cacheStats: CacheUsageStatsResult?, state: StorageUsageState) -> [StorageUsageEntry] {
var entries: [StorageUsageEntry] = [] var entries: [StorageUsageEntry] = []
entries.append(.keepMediaHeader(presentationData.theme, presentationData.strings.Cache_KeepMedia.uppercased())) entries.append(.keepMediaHeader(presentationData.theme, presentationData.strings.Cache_KeepMedia.uppercased()))
@ -398,9 +401,9 @@ func cacheUsageStats(context: AccountContext) -> Signal<CacheUsageStatsResult?,
} }
public func storageUsageController(context: AccountContext, cacheUsagePromise: Promise<CacheUsageStatsResult?>? = nil, isModal: Bool = false) -> ViewController { public func storageUsageController(context: AccountContext, cacheUsagePromise: Promise<CacheUsageStatsResult?>? = nil, isModal: Bool = false) -> ViewController {
let statePromise = ValuePromise(StoragUsageState(peerIdWithRevealedOptions: nil)) let statePromise = ValuePromise(StorageUsageState(peerIdWithRevealedOptions: nil))
let stateValue = Atomic(value: StoragUsageState(peerIdWithRevealedOptions: nil)) let stateValue = Atomic(value: StorageUsageState(peerIdWithRevealedOptions: nil))
let updateState: ((StoragUsageState) -> StoragUsageState) -> Void = { f in let updateState: ((StorageUsageState) -> StorageUsageState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) }) statePromise.set(stateValue.modify { f($0) })
} }
@ -546,7 +549,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
selectedSize = totalSize selectedSize = totalSize
if !items.isEmpty { if !items.isEmpty {
items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { var cancelImpl: (() -> Void)?
items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { [weak controller] in
if let statsPromise = statsPromise { if let statsPromise = statsPromise {
let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 })
@ -582,20 +587,37 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
var updatedTempPaths = stats.tempPaths var updatedTempPaths = stats.tempPaths
var updatedTempSize = stats.tempSize var updatedTempSize = stats.tempSize
var signal: Signal<Void, NoError> = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) var signal: Signal<Float, NoError> = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds)
if otherSize.0 { if otherSize.0 {
let removeTempFiles: Signal<Void, NoError> = Signal { subscriber in let removeTempFiles: Signal<Float, NoError> = Signal { subscriber in
let fileManager = FileManager.default let fileManager = FileManager.default
var count: Int = 0
let totalCount = stats.tempPaths.count
let reportProgress: (Int) -> Void = { count in
Queue.mainQueue().async {
subscriber.putNext(min(1.0, Float(count) / Float(totalCount)))
}
}
if totalCount == 0 {
subscriber.putNext(1.0)
subscriber.putCompletion()
return EmptyDisposable
}
for path in stats.tempPaths { for path in stats.tempPaths {
let _ = try? fileManager.removeItem(atPath: path) let _ = try? fileManager.removeItem(atPath: path)
count += 1
reportProgress(count)
} }
subscriber.putCompletion() subscriber.putCompletion()
return EmptyDisposable return EmptyDisposable
} |> runOn(Queue.concurrentDefaultQueue()) } |> runOn(Queue.concurrentDefaultQueue())
signal = signal signal = (signal |> map { $0 * 0.7 })
|> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths)) |> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths) |> map { 0.7 + 0.2 * $0 })
|> then(removeTempFiles) |> then(removeTempFiles |> map { 0.9 + 0.1 * $0 })
} }
if otherSize.0 { if otherSize.0 {
@ -606,48 +628,38 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
updatedTempSize = 0 updatedTempSize = 0
} }
let progressPromise = ValuePromise<Float>(0.0)
let overlayNode = StorageUsageClearProgressOverlayNode(presentationData: presentationData)
overlayNode.setProgressSignal(progressPromise.get())
controller?.setItemGroupOverlayNode(groupIndex: 0, node: overlayNode)
let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: updatedOtherSize, otherPaths: updatedOtherPaths, cacheSize: updatedCacheSize, tempPaths: updatedTempPaths, tempSize: updatedTempSize, immutableSize: stats.immutableSize) let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: updatedOtherSize, otherPaths: updatedOtherPaths, cacheSize: updatedCacheSize, tempPaths: updatedTempPaths, tempSize: updatedTempSize, immutableSize: stats.immutableSize)
var cancelImpl: (() -> Void)?
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
signal = signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
cancelImpl = { cancelImpl = {
clearDisposable.set(nil) clearDisposable.set(nil)
resetStats() resetStats()
} }
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
statsPromise.set(.single(.result(resultStats))) statsPromise.set(.single(.result(resultStats)))
clearDisposable.set((signal
|> deliverOnMainQueue).start(next: { progress in
progressPromise.set(progress)
}, completed: {
statsPromise.set(.single(.result(resultStats)))
progressPromise.set(1.0)
Queue.mainQueue().after(1.0) {
dismissAction()
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil)
}
})) }))
} }
dismissAction()
})) }))
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: {
cancelImpl?()
dismissAction()
})])
]) ])
presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} }
@ -743,7 +755,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
selectedSize = totalSize selectedSize = totalSize
if !items.isEmpty { if !items.isEmpty {
items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { var cancelImpl: (() -> Void)?
items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))").string, action: { [weak controller] in
if let statsPromise = statsPromise { if let statsPromise = statsPromise {
let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 })
var clearMediaIds = Set<MediaId>() var clearMediaIds = Set<MediaId>()
@ -785,50 +799,39 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
} }
} }
var signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds) let signal = context.engine.resources.clearCachedMediaResources(mediaResourceIds: clearResourceIds)
let progressPromise = ValuePromise<Float>(0.0)
let overlayNode = StorageUsageClearProgressOverlayNode(presentationData: presentationData)
overlayNode.setProgressSignal(progressPromise.get())
controller?.setItemGroupOverlayNode(groupIndex: 0, node: overlayNode)
let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: stats.otherSize, otherPaths: stats.otherPaths, cacheSize: stats.cacheSize, tempPaths: stats.tempPaths, tempSize: stats.tempSize, immutableSize: stats.immutableSize) let resultStats = CacheUsageStats(media: media, mediaResourceIds: stats.mediaResourceIds, peers: stats.peers, otherSize: stats.otherSize, otherPaths: stats.otherPaths, cacheSize: stats.cacheSize, tempPaths: stats.tempPaths, tempSize: stats.tempSize, immutableSize: stats.immutableSize)
var cancelImpl: (() -> Void)?
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
signal = signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
cancelImpl = { cancelImpl = {
clearDisposable.set(nil) clearDisposable.set(nil)
resetStats() resetStats()
} }
clearDisposable.set((signal clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(next: { progress in
progressPromise.set(progress)
}, completed: {
statsPromise.set(.single(.result(resultStats))) statsPromise.set(.single(.result(resultStats)))
progressPromise.set(1.0)
Queue.mainQueue().after(1.0) {
dismissAction()
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil) presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, formatting: DataSizeStringFormatting(presentationData: presentationData)))", stringForDeviceType()).string), elevatedLayout: false, action: { _ in return false }), .current, nil)
}
})) }))
} }
dismissAction()
})) }))
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: {
cancelImpl?()
dismissAction()
})])
]) ])
presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} }
@ -995,3 +998,105 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
} }
return controller return controller
} }
private class StorageUsageClearProgressOverlayNode: ASDisplayNode, ActionSheetGroupOverlayNode {
private let presentationData: PresentationData
private let animationNode: AnimatedStickerNode
private let progressTextNode: ImmediateTextNode
private let descriptionTextNode: ImmediateTextNode
private let progressBackgroundNode: ASDisplayNode
private let progressForegroundNode: ASDisplayNode
private let progressDisposable = MetaDisposable()
private var validLayout: CGSize?
init(presentationData: PresentationData) {
self.presentationData = presentationData
self.animationNode = AnimatedStickerNode()
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearCache"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true
self.progressTextNode = ImmediateTextNode()
self.progressTextNode.textAlignment = .center
self.descriptionTextNode = ImmediateTextNode()
self.descriptionTextNode.textAlignment = .center
self.descriptionTextNode.maximumNumberOfLines = 0
self.progressBackgroundNode = ASDisplayNode()
self.progressBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.controlAccentColor.withMultipliedAlpha(0.2)
self.progressBackgroundNode.cornerRadius = 3.0
self.progressForegroundNode = ASDisplayNode()
self.progressForegroundNode.backgroundColor = self.presentationData.theme.actionSheet.controlAccentColor
self.progressForegroundNode.cornerRadius = 3.0
super.init()
self.addSubnode(self.animationNode)
self.addSubnode(self.progressTextNode)
self.addSubnode(self.descriptionTextNode)
self.addSubnode(self.progressBackgroundNode)
self.addSubnode(self.progressForegroundNode)
}
deinit {
self.progressDisposable.dispose()
}
func setProgressSignal(_ signal: Signal<Float, NoError>) {
self.progressDisposable.set((signal
|> deliverOnMainQueue).start(next: { [weak self] progress in
if let strongSelf = self {
strongSelf.setProgress(progress)
}
}))
}
private var progress: Float = 0.0
private func setProgress(_ progress: Float) {
self.progress = progress
if let size = self.validLayout {
self.updateLayout(size: size, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
let inset: CGFloat = 24.0
let progressHeight: CGFloat = 6.0
let spacing: CGFloat = 16.0
let progressFrame = CGRect(x: inset, y: size.height - inset - progressHeight, width: size.width - inset * 2.0, height: progressHeight)
self.progressBackgroundNode.frame = progressFrame
let progressForegroundFrame = CGRect(x: inset, y: size.height - inset - progressHeight, width: floorToScreenPixels(progressFrame.width * CGFloat(self.progress)), height: progressHeight)
if !self.progressForegroundNode.frame.width.isZero {
transition.updateFrame(node: self.progressForegroundNode, frame: progressForegroundFrame)
} else {
self.progressForegroundNode.frame = progressForegroundFrame
}
self.descriptionTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_KeepOpenedDescription, font: Font.regular(15.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor)
let descriptionTextSize = self.descriptionTextNode.updateLayout(CGSize(width: size.width - inset * 3.0, height: size.height))
let descriptionTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - descriptionTextSize.width) / 2.0), y: progressFrame.minY - spacing - 9.0 - descriptionTextSize.height), size: descriptionTextSize)
self.descriptionTextNode.frame = descriptionTextFrame
self.progressTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_Progress(Int(progress * 100.0)).string, font: Font.with(size: 17.0, design: .regular, weight: .bold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
let progressTextSize = self.progressTextNode.updateLayout(size)
let progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: descriptionTextFrame.minY - spacing - progressTextSize.height), size: progressTextSize)
self.progressTextNode.frame = progressTextFrame
let availableHeight = progressTextFrame.minY
let imageSide = min(160.0, availableHeight - 30.0)
let imageSize = CGSize(width: imageSide, height: imageSide)
let animationFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floorToScreenPixels((progressTextFrame.minY - imageSize.height) / 2.0)), size: imageSize)
self.animationNode.frame = animationFrame
self.animationNode.updateLayout(size: imageSize)
}
}

View File

@ -246,7 +246,7 @@ public final class SlotMachineAnimationNode: ASDisplayNode {
} }
} }
public func setOverlayColor(_ color: UIColor?, animated: Bool) { public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
} }
} }
@ -352,6 +352,6 @@ class DiceAnimatedStickerNode: ASDisplayNode {
self.animationNode.frame = self.bounds self.animationNode.frame = self.bounds
} }
public func setOverlayColor(_ color: UIColor?, animated: Bool) { public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
} }
} }

23
submodules/TabBarUI/BUILD Normal file
View File

@ -0,0 +1,23 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "TabBarUI",
module_name = "TabBarUI",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/TelegramPresentationData:TelegramPresentationData",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,11 +1,12 @@
import Foundation import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
import Display
public enum ToolbarActionOption { private extension ToolbarTheme {
case left convenience init(tabBarTheme theme: TabBarControllerTheme) {
case right self.init(barBackgroundColor: theme.tabBarBackgroundColor, barSeparatorColor: theme.tabBarSeparatorColor, barTextColor: theme.tabBarTextColor, barSelectedTextColor: theme.tabBarSelectedTextColor)
case middle }
} }
final class TabBarControllerNode: ASDisplayNode { final class TabBarControllerNode: ASDisplayNode {
@ -65,7 +66,7 @@ final class TabBarControllerNode: ASDisplayNode {
self.tabBarNode.updateTheme(theme) self.tabBarNode.updateTheme(theme)
self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5)
self.toolbarNode?.updateTheme(theme) self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme))
} }
func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) { func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
@ -99,7 +100,7 @@ final class TabBarControllerNode: ASDisplayNode {
transition.updateFrame(node: toolbarNode, frame: tabBarFrame) transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
} else { } else {
let toolbarNode = ToolbarNode(theme: self.theme, left: { [weak self] in let toolbarNode = ToolbarNode(theme: ToolbarTheme(tabBarTheme: self.theme), left: { [weak self] in
self?.toolbarActionSelected(.left) self?.toolbarActionSelected(.left)
}, right: { [weak self] in }, right: { [weak self] in
self?.toolbarActionSelected(.right) self?.toolbarActionSelected(.right)

View File

@ -0,0 +1,451 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
import Display
import TelegramPresentationData
public final class TabBarControllerTheme {
public let backgroundColor: UIColor
public let tabBarBackgroundColor: UIColor
public let tabBarSeparatorColor: UIColor
public let tabBarIconColor: UIColor
public let tabBarSelectedIconColor: UIColor
public let tabBarTextColor: UIColor
public let tabBarSelectedTextColor: UIColor
public let tabBarBadgeBackgroundColor: UIColor
public let tabBarBadgeStrokeColor: UIColor
public let tabBarBadgeTextColor: UIColor
public let tabBarExtractedIconColor: UIColor
public let tabBarExtractedTextColor: UIColor
public init(backgroundColor: UIColor, tabBarBackgroundColor: UIColor, tabBarSeparatorColor: UIColor, tabBarIconColor: UIColor, tabBarSelectedIconColor: UIColor, tabBarTextColor: UIColor, tabBarSelectedTextColor: UIColor, tabBarBadgeBackgroundColor: UIColor, tabBarBadgeStrokeColor: UIColor, tabBarBadgeTextColor: UIColor, tabBarExtractedIconColor: UIColor, tabBarExtractedTextColor: UIColor) {
self.backgroundColor = backgroundColor
self.tabBarBackgroundColor = tabBarBackgroundColor
self.tabBarSeparatorColor = tabBarSeparatorColor
self.tabBarIconColor = tabBarIconColor
self.tabBarSelectedIconColor = tabBarSelectedIconColor
self.tabBarTextColor = tabBarTextColor
self.tabBarSelectedTextColor = tabBarSelectedTextColor
self.tabBarBadgeBackgroundColor = tabBarBadgeBackgroundColor
self.tabBarBadgeStrokeColor = tabBarBadgeStrokeColor
self.tabBarBadgeTextColor = tabBarBadgeTextColor
self.tabBarExtractedIconColor = tabBarExtractedIconColor
self.tabBarExtractedTextColor = tabBarExtractedTextColor
}
public convenience init(rootControllerTheme: PresentationTheme) {
let theme = rootControllerTheme.rootController.tabBar
self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarIconColor: theme.iconColor, tabBarSelectedIconColor: theme.selectedIconColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedTextColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor, tabBarExtractedIconColor: rootControllerTheme.contextMenu.extractedContentTintColor, tabBarExtractedTextColor: rootControllerTheme.contextMenu.extractedContentTintColor)
}
}
public final class TabBarItemInfo: NSObject {
public let previewing: Bool
public init(previewing: Bool) {
self.previewing = previewing
super.init()
}
override public func isEqual(_ object: Any?) -> Bool {
if let object = object as? TabBarItemInfo {
if self.previewing != object.previewing {
return false
}
return true
} else {
return false
}
}
public static func ==(lhs: TabBarItemInfo, rhs: TabBarItemInfo) -> Bool {
if lhs.previewing != rhs.previewing {
return false
}
return true
}
}
public enum TabBarContainedControllerPresentationUpdate {
case dismiss
case present
case progress(CGFloat)
}
public protocol TabBarContainedController {
func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode])
func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate)
}
open class TabBarControllerImpl: ViewController, TabBarController {
private var validLayout: ContainerViewLayout?
private var tabBarControllerNode: TabBarControllerNode {
get {
return super.displayNode as! TabBarControllerNode
}
}
open override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
for controller in self.controllers {
controller.updateNavigationCustomData(data, progress: progress, transition: transition)
}
}
public private(set) var controllers: [ViewController] = []
private let _ready = Promise<Bool>()
override open var ready: Promise<Bool> {
return self._ready
}
private var _selectedIndex: Int?
public var selectedIndex: Int {
get {
if let _selectedIndex = self._selectedIndex {
return _selectedIndex
} else {
return 0
}
} set(value) {
let index = max(0, min(self.controllers.count - 1, value))
if _selectedIndex != index {
_selectedIndex = index
self.updateSelectedIndex()
}
}
}
public var currentController: ViewController?
private let pendingControllerDisposable = MetaDisposable()
private var theme: TabBarControllerTheme
public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
self.theme = theme
super.init(navigationBarPresentationData: nil)
self.scrollToTop = { [weak self] in
guard let strongSelf = self else {
return
}
if let controller = strongSelf.currentController {
controller.scrollToTop?()
}
}
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.pendingControllerDisposable.dispose()
}
public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
if self.theme !== theme {
self.theme = theme
if self.isNodeLoaded {
self.tabBarControllerNode.updateTheme(theme)
}
}
}
private var debugTapCounter: (Double, Int) = (0.0, 0)
public func sourceNodesForController(at index: Int) -> [ASDisplayNode]? {
return self.tabBarControllerNode.tabBarNode.sourceNodesForController(at: index)
}
public func frameForControllerTab(controller: ViewController) -> CGRect? {
if let index = self.controllers.firstIndex(of: controller) {
return self.tabBarControllerNode.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarControllerNode.tabBarNode.view.convert($0, to: self.view) }
} else {
return nil
}
}
public func isPointInsideContentArea(point: CGPoint) -> Bool {
if point.y < self.tabBarControllerNode.tabBarNode.frame.minY {
return true
}
return false
}
public func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
self.tabBarControllerNode.updateIsTabBarEnabled(value, transition: transition)
}
public func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition) {
self.tabBarControllerNode.tabBarHidden = value
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .slide))
}
}
override open func loadDisplayNode() {
self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in
if let strongSelf = self {
if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController {
controller.presentTabBarPreviewingController(sourceNodes: itemNodes)
return
}
if strongSelf.selectedIndex == index {
let timestamp = CACurrentMediaTime()
if strongSelf.debugTapCounter.0 < timestamp - 0.4 {
strongSelf.debugTapCounter.0 = timestamp
strongSelf.debugTapCounter.1 = 0
}
if strongSelf.debugTapCounter.0 >= timestamp - 0.4 {
strongSelf.debugTapCounter.0 = timestamp
strongSelf.debugTapCounter.1 += 1
}
if strongSelf.debugTapCounter.1 >= 10 {
strongSelf.debugTapCounter.1 = 0
strongSelf.controllers[index].tabBarItemDebugTapAction?()
}
}
if let validLayout = strongSelf.validLayout {
var updatedLayout = validLayout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if validLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = validLayout.insets(options: options).bottom
if !validLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
strongSelf.controllers[index].containerLayoutUpdated(updatedLayout, transition: .immediate)
}
let startTime = CFAbsoluteTimeGetCurrent()
strongSelf.pendingControllerDisposable.set((strongSelf.controllers[index].ready.get()
|> deliverOnMainQueue).start(next: { _ in
if let strongSelf = self {
let readyTime = CFAbsoluteTimeGetCurrent() - startTime
if readyTime > 0.5 {
print("TabBarController: controller took \(readyTime) to become ready")
}
if strongSelf.selectedIndex == index {
if let controller = strongSelf.currentController {
if longTap {
controller.longTapWithTabBar?()
} else {
controller.scrollToTopWithTabBar?()
}
}
} else {
strongSelf.selectedIndex = index
}
}
}))
}
}, contextAction: { [weak self] index, node, gesture in
guard let strongSelf = self else {
return
}
if index >= 0 && index < strongSelf.controllers.count {
strongSelf.controllers[index].tabBarItemContextAction(sourceNode: node, gesture: gesture)
}
}, swipeAction: { [weak self] index, direction in
guard let strongSelf = self else {
return
}
if index >= 0 && index < strongSelf.controllers.count {
strongSelf.controllers[index].tabBarItemSwipeAction(direction: direction)
}
}, toolbarActionSelected: { [weak self] action in
self?.currentController?.toolbarActionSelected(action: action)
}, disabledPressed: { [weak self] in
self?.currentController?.tabBarDisabledAction()
})
self.updateSelectedIndex()
self.displayNodeDidLoad()
}
public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
let alpha = max(0.0, min(1.0, alpha))
transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.backgroundNode, alpha: alpha, delay: 0.15)
transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.15)
}
private func updateSelectedIndex() {
if !self.isNodeLoaded {
return
}
self.tabBarControllerNode.tabBarNode.selectedIndex = self.selectedIndex
if let currentController = self.currentController {
currentController.willMove(toParent: nil)
self.tabBarControllerNode.currentControllerNode = nil
currentController.removeFromParent()
currentController.didMove(toParent: nil)
self.currentController = nil
}
if let _selectedIndex = self._selectedIndex, _selectedIndex < self.controllers.count {
self.currentController = self.controllers[_selectedIndex]
}
if let currentController = self.currentController {
currentController.willMove(toParent: self)
self.tabBarControllerNode.currentControllerNode = currentController.displayNode
self.addChild(currentController)
currentController.didMove(toParent: self)
currentController.displayNode.recursivelyEnsureDisplaySynchronously(true)
self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle
} else {
}
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .immediate)
}
}
public func updateLayout(transition: ContainedViewLayoutTransition = .immediate) {
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: transition)
}
}
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.validLayout = layout
self.tabBarControllerNode.containerLayoutUpdated(layout, toolbar: self.currentController?.toolbar, transition: transition)
if let currentController = self.currentController {
currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size)
var updatedLayout = layout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if updatedLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = updatedLayout.insets(options: options).bottom
if !updatedLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
currentController.containerLayoutUpdated(updatedLayout, transition: transition)
}
}
override open func navigationStackConfigurationUpdated(next: [ViewController]) {
super.navigationStackConfigurationUpdated(next: next)
for controller in self.controllers {
controller.navigationStackConfigurationUpdated(next: next)
}
}
override open func viewWillDisappear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewWillDisappear(animated)
}
}
override open func viewWillAppear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewWillAppear(animated)
}
}
override open func viewDidAppear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewDidAppear(animated)
}
}
override open func viewDidDisappear(_ animated: Bool) {
if let currentController = self.currentController {
currentController.viewDidDisappear(animated)
}
}
public func setControllers(_ controllers: [ViewController], selectedIndex: Int?) {
var updatedSelectedIndex: Int? = selectedIndex
if updatedSelectedIndex == nil, let selectedIndex = self._selectedIndex, selectedIndex < self.controllers.count {
if let index = controllers.firstIndex(where: { $0 === self.controllers[selectedIndex] }) {
updatedSelectedIndex = index
} else {
updatedSelectedIndex = 0
}
}
self.controllers = controllers
self.tabBarControllerNode.tabBarNode.tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) })
let signals = combineLatest(self.controllers.map({ $0.tabBarItem }).map { tabBarItem -> Signal<Bool, NoError> in
if let tabBarItem = tabBarItem, tabBarItem.image == nil {
return Signal { [weak tabBarItem] subscriber in
let index = tabBarItem?.addSetImageListener({ image in
if image != nil {
subscriber.putNext(true)
subscriber.putCompletion()
}
})
return ActionDisposable {
Queue.mainQueue().async {
if let index = index {
tabBarItem?.removeSetImageListener(index)
}
}
}
}
|> runOn(.mainQueue())
} else {
return .single(true)
}
})
|> map { items -> Bool in
for item in items {
if !item {
return false
}
}
return true
}
|> filter { $0 }
|> take(1)
let allReady = signals
|> deliverOnMainQueue
|> mapToSignal { _ -> Signal<Bool, NoError> in
// wait for tab bar items to be applied
return .single(true)
|> delay(0.0, queue: Queue.mainQueue())
}
self._ready.set(allReady)
if let updatedSelectedIndex = updatedSelectedIndex {
self.selectedIndex = updatedSelectedIndex
self.updateSelectedIndex()
}
}
}

View File

@ -2,7 +2,10 @@ import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
import SwiftSignalKit import SwiftSignalKit
import Display
import UIKitRuntimeUtils import UIKitRuntimeUtils
import AnimatedStickerNode
import TelegramAnimatedStickerNode
private let separatorHeight: CGFloat = 1.0 / UIScreen.main.scale private let separatorHeight: CGFloat = 1.0 / UIScreen.main.scale
private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: UIColor, tintColor: UIColor, horizontal: Bool, imageMode: Bool, centered: Bool = false) -> (UIImage, CGFloat) { private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: UIColor, tintColor: UIColor, horizontal: Bool, imageMode: Bool, centered: Bool = false) -> (UIImage, CGFloat) {
@ -41,8 +44,12 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
context.fill(CGRect(origin: CGPoint(), size: size)) context.fill(CGRect(origin: CGPoint(), size: size))
if let image = image, imageMode { if let image = image, imageMode {
let imageRect: CGRect
if horizontal { if horizontal {
let imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize) imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)
} else {
imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: centered ? floor((size.height - imageSize.height) / 2.0) : 0.0), size: imageSize)
}
context.saveGState() context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY) context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0) context.scaleBy(x: 1.0, y: -1.0)
@ -55,21 +62,6 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
context.fill(imageRect) context.fill(imageRect)
} }
context.restoreGState() context.restoreGState()
} else {
let imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: centered ? floor((size.height - imageSize.height) / 2.0) : 0.0), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
if image.renderingMode == .alwaysOriginal {
context.draw(image.cgImage!, in: imageRect)
} else {
context.clip(to: imageRect, mask: image.cgImage!)
context.setFillColor(tintColor.cgColor)
context.fill(imageRect)
}
context.restoreGState()
}
} }
} }
@ -89,15 +81,12 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
private let badgeFont = Font.regular(13.0) private let badgeFont = Font.regular(13.0)
public enum TabBarItemSwipeDirection {
case left
case right
}
private final class TabBarItemNode: ASDisplayNode { private final class TabBarItemNode: ASDisplayNode {
let extractedContainerNode: ContextExtractedContentContainingNode let extractedContainerNode: ContextExtractedContentContainingNode
let containerNode: ContextControllerSourceNode let containerNode: ContextControllerSourceNode
let imageNode: ASImageNode let imageNode: ASImageNode
let animationContainerNode: ASDisplayNode
let animationNode: AnimatedStickerNode
let textImageNode: ASImageNode let textImageNode: ASImageNode
let contextImageNode: ASImageNode let contextImageNode: ASImageNode
let contextTextImageNode: ASImageNode let contextTextImageNode: ASImageNode
@ -117,6 +106,12 @@ private final class TabBarItemNode: ASDisplayNode {
self.imageNode.displayWithoutProcessing = true self.imageNode.displayWithoutProcessing = true
self.imageNode.displaysAsynchronously = false self.imageNode.displaysAsynchronously = false
self.imageNode.isAccessibilityElement = false self.imageNode.isAccessibilityElement = false
self.animationContainerNode = ASDisplayNode()
self.animationNode = AnimatedStickerNode()
self.animationNode.automaticallyLoadFirstFrame = true
self.textImageNode = ASImageNode() self.textImageNode = ASImageNode()
self.textImageNode.isUserInteractionEnabled = false self.textImageNode.isUserInteractionEnabled = false
self.textImageNode.displayWithoutProcessing = true self.textImageNode.displayWithoutProcessing = true
@ -142,6 +137,8 @@ private final class TabBarItemNode: ASDisplayNode {
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode) self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
self.extractedContainerNode.contentNode.addSubnode(self.imageNode) self.extractedContainerNode.contentNode.addSubnode(self.imageNode)
self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode)
self.animationContainerNode.addSubnode(self.animationNode)
self.extractedContainerNode.contentNode.addSubnode(self.contextTextImageNode) self.extractedContainerNode.contentNode.addSubnode(self.contextTextImageNode)
self.extractedContainerNode.contentNode.addSubnode(self.contextImageNode) self.extractedContainerNode.contentNode.addSubnode(self.contextImageNode)
self.containerNode.addSubnode(self.extractedContainerNode) self.containerNode.addSubnode(self.extractedContainerNode)
@ -451,7 +448,26 @@ class TabBarNode: ASDisplayNode {
}) })
if let selectedIndex = self.selectedIndex, selectedIndex == i { if let selectedIndex = self.selectedIndex, selectedIndex == i {
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (image, imageContentWidth): (UIImage, CGFloat)
if let _ = item.item.animationName {
(image, imageContentWidth) = (UIImage(), 0.0)
node.animationNode.isHidden = false
let animationSize: Int = Int(51.0 * UIScreen.main.scale)
node.animationNode.visibility = true
if !node.isSelected {
node.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.item.animationName ?? ""), width: animationSize, height: animationSize, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
}
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
} else {
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.animationNode.isHidden = true
node.animationNode.visibility = false
}
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.textImageNode.image = textImage node.textImageNode.image = textImage
@ -466,6 +482,10 @@ class TabBarNode: ASDisplayNode {
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.animationNode.isHidden = true
node.animationNode.visibility = false
node.textImageNode.image = textImage node.textImageNode.image = textImage
node.accessibilityLabel = item.item.title node.accessibilityLabel = item.item.title
node.imageNode.image = image node.imageNode.image = image
@ -496,7 +516,25 @@ class TabBarNode: ASDisplayNode {
let previousTextImageSize = node.textImageNode.image?.size ?? CGSize() let previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
if let selectedIndex = self.selectedIndex, selectedIndex == index { if let selectedIndex = self.selectedIndex, selectedIndex == index {
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (image, imageContentWidth): (UIImage, CGFloat)
if let _ = item.item.animationName {
(image, imageContentWidth) = (UIImage(), 0.0)
node.animationNode.isHidden = false
let animationSize: Int = Int(51.0 * UIScreen.main.scale)
node.animationNode.visibility = true
if !node.isSelected {
node.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.item.animationName ?? ""), width: animationSize, height: animationSize, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
}
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
} else {
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.animationNode.isHidden = true
node.animationNode.visibility = false
}
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.textImageNode.image = textImage node.textImageNode.image = textImage
@ -511,6 +549,11 @@ class TabBarNode: ASDisplayNode {
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
node.animationNode.stop()
node.animationNode.isHidden = true
node.animationNode.visibility = false
node.textImageNode.image = textImage node.textImageNode.image = textImage
node.accessibilityLabel = item.item.title node.accessibilityLabel = item.item.title
node.imageNode.image = image node.imageNode.image = image
@ -614,6 +657,14 @@ class TabBarNode: ASDisplayNode {
node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
if horizontal {
node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0))
} else {
node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0))
}
if container.badgeValue != container.appliedBadgeValue { if container.badgeValue != container.appliedBadgeValue {
container.appliedBadgeValue = container.badgeValue container.appliedBadgeValue = container.badgeValue
if let badgeValue = container.badgeValue, !badgeValue.isEmpty { if let badgeValue = container.badgeValue, !badgeValue.isEmpty {
@ -633,14 +684,14 @@ class TabBarNode: ASDisplayNode {
let backgroundSize = CGSize(width: hasSingleLetterValue ? 18.0 : max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) let backgroundSize = CGSize(width: hasSingleLetterValue ? 18.0 : max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0)
let backgroundFrame: CGRect let backgroundFrame: CGRect
if horizontal { if horizontal {
backgroundFrame = CGRect(origin: CGPoint(x: 15.0, y: 0.0), size: backgroundSize) backgroundFrame = CGRect(origin: CGPoint(x: 13.0, y: 0.0), size: backgroundSize)
} else { } else {
let contentWidth: CGFloat = 25.0 let contentWidth: CGFloat = 25.0
backgroundFrame = CGRect(origin: CGPoint(x: floor(node.frame.width / 2.0) + contentWidth - backgroundSize.width - 5.0, y: self.centered ? 6.0 : -1.0), size: backgroundSize) backgroundFrame = CGRect(origin: CGPoint(x: floor(node.frame.width / 2.0) + contentWidth - backgroundSize.width - 5.0, y: self.centered ? 6.0 : -1.0), size: backgroundSize)
} }
transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame) transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame)
container.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size) container.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
container.badgeContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) container.badgeContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
container.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.size.width - badgeSize.width) / 2.0), y: 1.0), size: badgeSize) container.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.size.width - badgeSize.width) / 2.0), y: 1.0), size: badgeSize)
@ -673,7 +724,11 @@ class TabBarNode: ASDisplayNode {
if let closestNode = closestNode { if let closestNode = closestNode {
let container = self.tabBarNodeContainers[closestNode.0] let container = self.tabBarNodeContainers[closestNode.0]
let previousSelectedIndex = self.selectedIndex
self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode]) self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode])
if previousSelectedIndex != closestNode.0 {
container.imageNode.animationNode.play()
}
} }
} }
} }

View File

@ -290,6 +290,6 @@ func _internal_collectCacheUsageStats(account: Account, peerId: PeerId? = nil, a
} }
} }
func _internal_clearCachedMediaResources(account: Account, mediaResourceIds: Set<MediaResourceId>) -> Signal<Void, NoError> { func _internal_clearCachedMediaResources(account: Account, mediaResourceIds: Set<MediaResourceId>) -> Signal<Float, NoError> {
return account.postbox.mediaBox.removeCachedResources(mediaResourceIds) return account.postbox.mediaBox.removeCachedResources(mediaResourceIds)
} }

View File

@ -142,7 +142,7 @@ public extension TelegramEngine {
return _internal_collectCacheUsageStats(account: self.account, peerId: peerId, additionalCachePaths: additionalCachePaths, logFilesPath: logFilesPath) return _internal_collectCacheUsageStats(account: self.account, peerId: peerId, additionalCachePaths: additionalCachePaths, logFilesPath: logFilesPath)
} }
public func clearCachedMediaResources(mediaResourceIds: Set<MediaResourceId>) -> Signal<Void, NoError> { public func clearCachedMediaResources(mediaResourceIds: Set<MediaResourceId>) -> Signal<Float, NoError> {
return _internal_clearCachedMediaResources(account: self.account, mediaResourceIds: mediaResourceIds) return _internal_clearCachedMediaResources(account: self.account, mediaResourceIds: mediaResourceIds)
} }

View File

@ -79,7 +79,7 @@ func mediaBubbleCornerImage(incoming: Bool, radius: CGFloat, inset: CGFloat) ->
return formContext.generateImage()! return formContext.generateImage()!
} }
public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloat, incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false, onlyShadow: Bool = false) -> UIImage { public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloat, incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false, onlyShadow: Bool = false, alwaysFillColor: Bool = false) -> UIImage {
let topLeftRadius: CGFloat let topLeftRadius: CGFloat
let topRightRadius: CGFloat let topRightRadius: CGFloat
let bottomLeftRadius: CGFloat let bottomLeftRadius: CGFloat
@ -346,6 +346,12 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa
if !onlyOutline { if !onlyOutline {
context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: formImage.cgImage!) context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: formImage.cgImage!)
context.fill(CGRect(origin: CGPoint(), size: rawSize)) context.fill(CGRect(origin: CGPoint(), size: rawSize))
if alwaysFillColor && drawWithClearColor {
context.setBlendMode(.normal)
context.setFillColor(fillColor.cgColor)
context.fill(CGRect(origin: CGPoint(), size: rawSize))
}
} else { } else {
context.setFillColor(strokeColor.cgColor) context.setFillColor(strokeColor.cgColor)
context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: outlineImage.cgImage!) context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: outlineImage.cgImage!)

View File

@ -38,10 +38,10 @@ public extension PresentationFontSize {
} }
} }
public extension TabBarControllerTheme { public extension ToolbarTheme {
convenience init(rootControllerTheme: PresentationTheme) { convenience init(rootControllerTheme: PresentationTheme) {
let theme = rootControllerTheme.rootController.tabBar let theme = rootControllerTheme.rootController.tabBar
self.init(backgroundColor: rootControllerTheme.list.plainBackgroundColor, tabBarBackgroundColor: theme.backgroundColor, tabBarSeparatorColor: theme.separatorColor, tabBarIconColor: theme.iconColor, tabBarSelectedIconColor: theme.selectedIconColor, tabBarTextColor: theme.textColor, tabBarSelectedTextColor: theme.selectedTextColor, tabBarBadgeBackgroundColor: theme.badgeBackgroundColor, tabBarBadgeStrokeColor: theme.badgeStrokeColor, tabBarBadgeTextColor: theme.badgeTextColor, tabBarExtractedIconColor: rootControllerTheme.contextMenu.extractedContentTintColor, tabBarExtractedTextColor: rootControllerTheme.contextMenu.extractedContentTintColor) self.init(barBackgroundColor: theme.backgroundColor, barSeparatorColor: theme.separatorColor, barTextColor: theme.textColor, barSelectedTextColor: theme.selectedTextColor)
} }
} }

View File

@ -163,7 +163,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti
outgoingBubbleStrokeColor = .clear outgoingBubbleStrokeColor = .clear
} }
outgoingBubbleHighlightedFill = outgoingBubbleFillColors?.first?.withMultiplied(hue: 1.054, saturation: 1.589, brightness: 0.96) outgoingBubbleHighlightedFill = outgoingBubbleFillColors?.first?.withMultiplied(hue: 1.00, saturation: 1.589, brightness: 0.96)
let lightnessColor = UIColor.average(of: bubbleColors.map(UIColor.init(rgb:))) let lightnessColor = UIColor.average(of: bubbleColors.map(UIColor.init(rgb:)))
if lightnessColor.lightness > 0.705 { if lightnessColor.lightness > 0.705 {

View File

@ -250,6 +250,13 @@ public final class PrincipalThemeEssentialGraphics {
let incomingKnockout = self.incomingBubbleGradientImage != nil let incomingKnockout = self.incomingBubbleGradientImage != nil
let outgoingKnockout = self.outgoingBubbleGradientImage != nil let outgoingKnockout = self.outgoingBubbleGradientImage != nil
let highlightKnockout: Bool
if case .color = wallpaper {
highlightKnockout = true
} else {
highlightKnockout = false
}
let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper) let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper)
let maxCornerRadius = bubbleCorners.mainRadius let maxCornerRadius = bubbleCorners.mainRadius
@ -376,27 +383,27 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundIncomingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
@ -405,27 +412,27 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
@ -435,8 +442,8 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundOutgoingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true) self.chatMessageBackgroundOutgoingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: false, extendedEdges: true) self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)!
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)!

View File

@ -253,6 +253,7 @@ swift_library(
"//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent", "//submodules/Components/ReactionListContextMenuContent:ReactionListContextMenuContent",
"//submodules/Components/ReactionImageComponent:ReactionImageComponent", "//submodules/Components/ReactionImageComponent:ReactionImageComponent",
"//submodules/Translate:Translate", "//submodules/Translate:Translate",
"//submodules/TabBarUI:TabBarUI",
] + 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,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -32,7 +32,7 @@ private let inlineBotPrefixFont = Font.regular(14.0)
private let inlineBotNameFont = nameFont private let inlineBotNameFont = nameFont
protocol GenericAnimatedStickerNode: ASDisplayNode { protocol GenericAnimatedStickerNode: ASDisplayNode {
func setOverlayColor(_ color: UIColor?, animated: Bool) func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool)
var currentFrameIndex: Int { get } var currentFrameIndex: Int { get }
func setFrameIndex(_ frameIndex: Int) func setFrameIndex(_ frameIndex: Int)
@ -58,7 +58,7 @@ private class VideoStickerNode: ASDisplayNode, GenericAnimatedStickerNode {
private var layerHolder: SampleBufferLayer? private var layerHolder: SampleBufferLayer?
var manager: SoftwareVideoLayerFrameManager? var manager: SoftwareVideoLayerFrameManager?
func setOverlayColor(_ color: UIColor?, animated: Bool) { func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
} }
@ -2193,10 +2193,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if highlighted { if highlighted {
self.imageNode.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false) self.imageNode.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false)
self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, animated: false) self.animationNode?.setOverlayColor(item.presentationData.theme.theme.chat.message.mediaHighlightOverlayColor, replace: false, animated: false)
} else { } else {
self.imageNode.setOverlayColor(nil, animated: animated) self.imageNode.setOverlayColor(nil, animated: animated)
self.animationNode?.setOverlayColor(nil, animated: false) self.animationNode?.setOverlayColor(nil, replace: false, animated: false)
} }
} }
} }

View File

@ -203,7 +203,7 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick
} }
} }
func setOverlayColor(_ color: UIColor?, animated: Bool) { func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
} }
func setFrameIndex(_ frameIndex: Int) { func setFrameIndex(_ frameIndex: Int) {

View File

@ -279,7 +279,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
textColorValue = presentationData.theme.list.itemAccentColor textColorValue = presentationData.theme.list.itemAccentColor
} }
self.expandNode.attributedText = NSAttributedString(string: presentationData.strings.PeerInfo_BioExpand, font: Font.medium(15.0), textColor: presentationData.theme.list.itemAccentColor) self.expandNode.attributedText = NSAttributedString(string: presentationData.strings.PeerInfo_BioExpand, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor)
let expandSize = self.expandNode.updateLayout(CGSize(width: width, height: 100.0)) let expandSize = self.expandNode.updateLayout(CGSize(width: width, height: 100.0))
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor) self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
@ -341,7 +341,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: labelSize) let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: labelSize)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: labelFrame.maxY + 3.0), size: textSize) let textFrame = CGRect(origin: CGPoint(x: sideInset, y: labelFrame.maxY + 3.0), size: textSize)
let expandFrame = CGRect(origin: CGPoint(x: width - safeInsets.right - expandSize.width - 14.0 - UIScreenPixel, y: textFrame.maxY - expandSize.height - 1.0), size: expandSize) let expandFrame = CGRect(origin: CGPoint(x: width - safeInsets.right - expandSize.width - 14.0 - UIScreenPixel, y: textFrame.maxY - expandSize.height), size: expandSize)
self.expandNode.frame = expandFrame self.expandNode.frame = expandFrame
self.expandButonNode.frame = expandFrame.insetBy(dx: -8.0, dy: -8.0) self.expandButonNode.frame = expandFrame.insetBy(dx: -8.0, dy: -8.0)

View File

@ -7170,8 +7170,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings") icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings")
} }
let tabBarItem: Signal<(String, UIImage?, UIImage?, String?), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge) let tabBarItem: Signal<(String, UIImage?, UIImage?, String?, Bool), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge)
|> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?) in |> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?, Bool) in
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed) let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
let phoneNumberWarning = suggestions.contains(.validatePhoneNumber) let phoneNumberWarning = suggestions.contains(.validatePhoneNumber)
let passwordWarning = suggestions.contains(.validatePassword) let passwordWarning = suggestions.contains(.validatePassword)
@ -7179,14 +7179,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
if accountTabBarAvatarBadge > 0 { if accountTabBarAvatarBadge > 0 {
otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
} }
return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge) return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil)
} }
self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue in self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar in
if let strongSelf = self { if let strongSelf = self {
strongSelf.tabBarItem.title = title strongSelf.tabBarItem.title = title
strongSelf.tabBarItem.image = image strongSelf.tabBarItem.image = image
strongSelf.tabBarItem.selectedImage = selectedImage strongSelf.tabBarItem.selectedImage = selectedImage
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
strongSelf.tabBarItem.badgeValue = badgeValue strongSelf.tabBarItem.badgeValue = badgeValue
} }
}) })

View File

@ -14,6 +14,7 @@ import SettingsUI
import AppBundle import AppBundle
import DatePickerNode import DatePickerNode
import DebugSettingsUI import DebugSettingsUI
import TabBarUI
public final class TelegramRootController: NavigationController { public final class TelegramRootController: NavigationController {
private let context: AccountContext private let context: AccountContext
@ -64,7 +65,7 @@ public final class TelegramRootController: NavigationController {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme { if previousTheme !== presentationData.theme {
strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme)) (strongSelf.rootTabController as? TabBarControllerImpl)?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme))
strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
} }
} }
@ -81,7 +82,7 @@ public final class TelegramRootController: NavigationController {
} }
public func addRootControllers(showCallsTab: Bool) { public func addRootControllers(showCallsTab: Bool) {
let tabBarController = TabBarController(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
tabBarController.navigationPresentation = .master tabBarController.navigationPresentation = .master
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild) let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl { if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
@ -131,7 +132,7 @@ public final class TelegramRootController: NavigationController {
} }
public func updateRootControllers(showCallsTab: Bool) { public func updateRootControllers(showCallsTab: Bool) {
guard let rootTabController = self.rootTabController else { guard let rootTabController = self.rootTabController as? TabBarControllerImpl else {
return return
} }
var controllers: [ViewController] = [] var controllers: [ViewController] = []

View File

@ -728,6 +728,17 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu
var pre = match.range(at: 3) var pre = match.range(at: 3)
if pre.location != NSNotFound { if pre.location != NSNotFound {
var intersectsWithEntities = false
text.enumerateAttributes(in: pre, options: [], using: { attributes, _, _ in
for (key, _) in attributes {
if key.rawValue.hasPrefix("Attribute__") {
intersectsWithEntities = true
}
}
})
if intersectsWithEntities {
result.append(text.attributedSubstring(from: match.range(at: 0)))
} else {
let text = string.substring(with: pre) let text = string.substring(with: pre)
stringOffset -= match.range(at: 2).length + match.range(at: 4).length stringOffset -= match.range(at: 2).length + match.range(at: 4).length
@ -736,9 +747,21 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber])) result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6)) offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6))
} }
}
pre = match.range(at: 8) pre = match.range(at: 8)
if pre.location != NSNotFound { if pre.location != NSNotFound {
var intersectsWithEntities = false
text.enumerateAttributes(in: pre, options: [], using: { attributes, _, _ in
for (key, _) in attributes {
if key.rawValue.hasPrefix("Attribute__") {
intersectsWithEntities = true
}
}
})
if intersectsWithEntities {
result.append(text.attributedSubstring(from: match.range(at: 0)))
} else {
let text = string.substring(with: pre) let text = string.substring(with: pre)
let entity = string.substring(with: match.range(at: 7)) let entity = string.substring(with: match.range(at: 7))
@ -767,6 +790,7 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu
stringOffset -= match.range(at: 7).length * 2 stringOffset -= match.range(at: 7).length * 2
} }
}
string = string.substring(from: match.range.location + match.range(at: 0).length) as NSString string = string.substring(from: match.range.location + match.range(at: 0).length) as NSString
stringOffset += match.range.location + match.range(at: 0).length stringOffset += match.range.location + match.range(at: 0).length

View File

@ -48,7 +48,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa
- (NSInteger)addSetSelectedImageListener:(UINavigationItemSetImageListener _Nonnull)listener; - (NSInteger)addSetSelectedImageListener:(UINavigationItemSetImageListener _Nonnull)listener;
- (void)removeSetSelectedImageListener:(NSInteger)key; - (void)removeSetSelectedImageListener:(NSInteger)key;
- (NSObject * _Nullable)userInfo; @property (nonatomic, strong) NSString * _Nullable animationName;
- (void)setUserInfo:(NSObject * _Nullable)userInfo;
@end @end

View File

@ -16,7 +16,7 @@ static const void *setMultipleRightBarButtonItemsListenerKey = &setMultipleRight
static const void *setBackBarButtonItemListenerBagKey = &setBackBarButtonItemListenerBagKey; static const void *setBackBarButtonItemListenerBagKey = &setBackBarButtonItemListenerBagKey;
static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey; static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey;
static const void *badgeKey = &badgeKey; static const void *badgeKey = &badgeKey;
static const void *userInfoKey = &userInfoKey; static const void *animationNameKey = &animationNameKey;
@implementation UINavigationItem (Proxy) @implementation UINavigationItem (Proxy)
@ -402,12 +402,16 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa
[(NSBag *)[self associatedObjectForKey:setSelectedImageListenerBagKey] removeItem:key]; [(NSBag *)[self associatedObjectForKey:setSelectedImageListenerBagKey] removeItem:key];
} }
- (NSObject * _Nullable)userInfo { - (void)setAnimationName:(NSString *)animationName {
return [self associatedObjectForKey:userInfoKey]; [self setAssociatedObject:animationName forKey:animationNameKey];
// [(NSBag *)[self associatedObjectForKey:setBadgeListenerBagKey] enumerateItems:^(UITabBarItemSetBadgeListener listener) {
// listener(badge);
// }];
} }
- (void)setUserInfo:(NSObject * _Nullable)userInfo { - (NSString *)animationName {
[self setAssociatedObject:userInfo forKey:userInfoKey]; return [self associatedObjectForKey:animationNameKey];
} }
@end @end

View File

@ -433,8 +433,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
return self._isReady.get() return self._isReady.get()
} }
private var newYearNode: WallpaperNewYearNode?
init(context: AccountContext, useSharedAnimationPhase: Bool) { init(context: AccountContext, useSharedAnimationPhase: Bool) {
self.context = context self.context = context
self.useSharedAnimationPhase = useSharedAnimationPhase self.useSharedAnimationPhase = useSharedAnimationPhase
@ -800,18 +798,9 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
if isFirstLayout && !self.frame.isEmpty { if isFirstLayout && !self.frame.isEmpty {
self.updateScale() self.updateScale()
if self.context.sharedContext.immediateExperimentalUISettings.snow, self.newYearNode == nil {
let newYearNode = WallpaperNewYearNode()
self.addSubnode(newYearNode)
self.newYearNode = newYearNode
} }
} }
self.newYearNode?.frame = CGRect(origin: CGPoint(), size: size)
self.newYearNode?.updateLayout(size: size)
}
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) { func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {}) self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {}) self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
@ -1720,47 +1709,3 @@ public func createWallpaperBackgroundNode(context: AccountContext, forChatDispla
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase) return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
} }
private class WallpaperNewYearNode: ASDisplayNode {
private var emitterLayer: CAEmitterLayer?
func updateLayout(size: CGSize) {
if self.emitterLayer == nil {
let particlesLayer = CAEmitterLayer()
self.emitterLayer = particlesLayer
self.layer.addSublayer(particlesLayer)
self.layer.masksToBounds = true
particlesLayer.backgroundColor = UIColor.clear.cgColor
particlesLayer.emitterShape = .circle
particlesLayer.emitterMode = .surface
particlesLayer.renderMode = .oldestLast
let cell1 = CAEmitterCell()
cell1.contents = UIImage(bundleImageName: "Components/Snowflake")?.cgImage
cell1.name = "snow"
cell1.birthRate = 252.0
cell1.lifetime = 20.0
cell1.velocity = 19.0
cell1.velocityRange = -5.0
cell1.xAcceleration = 2.5
cell1.yAcceleration = 10.0
cell1.emissionRange = .pi
cell1.spin = -28.6 * (.pi / 180.0)
cell1.spinRange = 57.2 * (.pi / 180.0)
cell1.scale = 0.04
cell1.scaleRange = 0.15
cell1.color = UIColor.white.withAlphaComponent(0.58).cgColor
// cell1.alphaRange = -0.2
particlesLayer.emitterCells = [cell1]
}
if let emitterLayer = self.emitterLayer {
emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: -size.height / 2.0)
emitterLayer.emitterSize = CGSize(width: size.width * 2.5, height: size.height)
emitterLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
}
}
}