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.ByName" = "by Name";
"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 }
private var overlayColor: (UIColor?, Bool)? = nil
private var size: CGSize?
override public init() {
self.queue = sharedQueue
self.eventsNode = AnimatedStickerNodeDisplayEvents()
@ -900,10 +903,13 @@ public final class AnimatedStickerNode: ASDisplayNode {
self.renderer = SoftwareAnimationRenderer()
//self.renderer = MetalAnimationRenderer()
#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 {
self.renderer?.contents = contents
}
if let (overlayColor, replace) = self.overlayColor {
self.renderer?.setOverlayColor(overlayColor, replace: replace, animated: false)
}
self.nodeToCopyFrameFrom = nil
self.addSubnode(self.renderer!)
}
@ -1347,10 +1353,12 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
public func updateLayout(size: CGSize) {
self.size = size
self.renderer?.frame = CGRect(origin: CGPoint(), size: size)
}
public func setOverlayColor(_ color: UIColor?, animated: Bool) {
self.renderer?.setOverlayColor(color, animated: animated)
public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) {
self.overlayColor = (color, replace)
self.renderer?.setOverlayColor(color, replace: replace, animated: animated)
}
}

View File

@ -10,5 +10,5 @@ public enum AnimationRendererFrameType {
protocol AnimationRenderer {
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,7 +8,8 @@ import YuvConversion
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
private var highlightedContentNode: ASDisplayNode?
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) {
assert(bytesPerRow > 0)
queue.async { [weak self] in
@ -53,20 +54,29 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
}
strongSelf.contents = image?.cgImage
strongSelf.updateHighlightedContentNode()
if strongSelf.highlightedContentNode?.frame != strongSelf.bounds {
strongSelf.highlightedContentNode?.frame = strongSelf.bounds
}
completion()
}
}
}
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
}
(highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID {
(highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
}
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
if let current = self.highlightedColor, let color = 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)
} else {
selectionToolbarNode = ToolbarNode(
theme: TabBarControllerTheme(
theme: ToolbarTheme(
rootControllerTheme: self.presentationData.theme),
displaySeparator: true,
left: {

View File

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

View File

@ -206,6 +206,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.tabBarItem.image = 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))
leftBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit

View File

@ -1116,7 +1116,7 @@ final class ChatListControllerNode: ASDisplayNode {
self.searchDisplayController?.updatePresentationData(presentationData)
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)
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
} 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)
}, right: { [weak self] in
self?.toolbarActionSelected?(.right)

View File

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

View File

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

View File

@ -1,5 +1,10 @@
import Foundation
import UIKit
import AsyncDisplayKit
public protocol ActionSheetGroupOverlayNode: ASDisplayNode {
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
}
open class ActionSheetController: ViewController, PresentableController, StandalonePresentableController {
private var actionSheetNode: ActionSheetControllerNode {
@ -83,4 +88,10 @@ open class ActionSheetController: ViewController, PresentableController, Standal
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)
}
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)
}
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 overlayNode: ActionSheetGroupOverlayNode?
init(theme: ActionSheetControllerTheme) {
self.theme = theme
@ -66,6 +68,31 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
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) {
for node in self.itemNodes {
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)
}
if let overlayNode = self.overlayNode {
overlayNode.updateLayout(size: size, transition: transition)
}
self.updateOverscroll(size: size, transition: transition)
return size

View File

@ -112,4 +112,8 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
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 SwiftSignalKit
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 enum TabBarItemSwipeDirection {
case left
case right
}
public final class TabBarItemInfo: NSObject {
public let previewing: Bool
public protocol TabBarController: ViewController {
var currentController: ViewController? { get }
var controllers: [ViewController] { get }
var selectedIndex: Int { get set }
public init(previewing: Bool) {
self.previewing = previewing
super.init()
}
func setControllers(_ controllers: [ViewController], selectedIndex: Int?)
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
}
}
func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition)
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 TabBarController: ViewController {
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()
}
}
}
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()
}
}
func frameForControllerTab(controller: ViewController) -> CGRect?
func isPointInsideContentArea(point: CGPoint) -> Bool
func sourceNodesForController(at index: Int) -> [ASDisplayNode]?
func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition)
func updateIsTabBarHidden(_ value: Bool, transition: ContainedViewLayoutTransition)
func updateLayout(transition: ContainedViewLayoutTransition)
}

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

@ -1063,9 +1063,17 @@ public class TextNode: ASDisplayNode {
let tokenString = "\u{2026}"
let truncatedTokenString = NSAttributedString(string: tokenString, attributes: truncationTokenAttributes)
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
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 {
brokenLineRange.length = attributedString.length - brokenLineRange.location
}
@ -1330,7 +1338,9 @@ public class TextNode: ASDisplayNode {
context.saveGState()
var clipRects: [CGRect] = []
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)
}
@ -1365,7 +1375,9 @@ public class TextNode: ASDisplayNode {
context.restoreGState()
} else {
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 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 {
private var theme: TabBarControllerTheme
private var theme: ToolbarTheme
private let displaySeparator: Bool
public var left: () -> Void
public var right: () -> Void
@ -18,14 +38,14 @@ public final class ToolbarNode: ASDisplayNode {
private let middleTitle: ImmediateTextNode
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.displaySeparator = displaySeparator
self.left = left
self.right = right
self.middle = middle
self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor)
self.backgroundNode = NavigationBackgroundNode(color: theme.barBackgroundColor)
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
@ -100,9 +120,9 @@ public final class ToolbarNode: ASDisplayNode {
self.middleButton.accessibilityTraits = .button
}
public func updateTheme(_ theme: TabBarControllerTheme) {
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate)
public func updateTheme(_ theme: ToolbarTheme) {
self.separatorNode.backgroundColor = theme.barSeparatorColor
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) {
@ -112,10 +132,10 @@ public final class ToolbarNode: ASDisplayNode {
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.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
let middleColor: UIColor
@ -123,15 +143,15 @@ public final class ToolbarNode: ASDisplayNode {
if middleAction.isEnabled {
switch middleAction.color {
case .accent:
middleColor = self.theme.tabBarSelectedTextColor
middleColor = self.theme.barSelectedTextColor
case let .custom(color):
middleColor = color
}
} else {
middleColor = self.theme.tabBarTextColor
middleColor = self.theme.barTextColor
}
} 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.middleButton.accessibilityLabel = toolbar.middleAction?.title

View File

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

View File

@ -501,7 +501,7 @@ open class ItemListControllerNode: ASDisplayNode {
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)
} 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.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)

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
self.dataQueue.async {
var keepPrefixes: [String] = []
@ -1254,14 +1254,31 @@ public final class MediaBox {
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 {
for prefix in keepPrefixes {
if path.starts(with: prefix) {
count += 1
continue outer
}
}
count += 1
unlink(self.basePath + "/" + path)
reportProgress(count)
}
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
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 })
var pathsToDelete: [String] = []
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 {
unlink(path)
count += 1
reportProgress(count)
}
subscriber.putCompletion()

View File

@ -1,6 +1,7 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
@ -14,6 +15,8 @@ import AccountContext
import ItemListPeerItem
import DeleteChatPeerActionSheetItem
import UndoUI
import AnimatedStickerNode
import TelegramAnimatedStickerNode
private func totalDiskSpace() -> Int64 {
do {
@ -275,15 +278,15 @@ private enum StorageUsageEntry: ItemListNodeEntry {
}
}
private struct StoragUsageState: Equatable {
private struct StorageUsageState: Equatable {
let peerIdWithRevealedOptions: PeerId?
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StoragUsageState {
return StoragUsageState(peerIdWithRevealedOptions: peerIdWithRevealedOptions)
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> StorageUsageState {
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] = []
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 {
let statePromise = ValuePromise(StoragUsageState(peerIdWithRevealedOptions: nil))
let stateValue = Atomic(value: StoragUsageState(peerIdWithRevealedOptions: nil))
let updateState: ((StoragUsageState) -> StoragUsageState) -> Void = { f in
let statePromise = ValuePromise(StorageUsageState(peerIdWithRevealedOptions: nil))
let stateValue = Atomic(value: StorageUsageState(peerIdWithRevealedOptions: nil))
let updateState: ((StorageUsageState) -> StorageUsageState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
@ -546,7 +549,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
selectedSize = totalSize
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 {
let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 })
@ -582,20 +587,37 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
var updatedTempPaths = stats.tempPaths
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 {
let removeTempFiles: Signal<Void, NoError> = Signal { subscriber in
let removeTempFiles: Signal<Float, NoError> = Signal { subscriber in
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 {
let _ = try? fileManager.removeItem(atPath: path)
count += 1
reportProgress(count)
}
subscriber.putCompletion()
return EmptyDisposable
} |> runOn(Queue.concurrentDefaultQueue())
signal = signal
|> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths))
|> then(removeTempFiles)
signal = (signal |> map { $0 * 0.7 })
|> then(context.account.postbox.mediaBox.removeOtherCachedResources(paths: stats.otherPaths) |> map { 0.7 + 0.2 * $0 })
|> then(removeTempFiles |> map { 0.9 + 0.1 * $0 })
}
if otherSize.0 {
@ -606,49 +628,39 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
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)
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 = {
clearDisposable.set(nil)
resetStats()
}
statsPromise.set(.single(.result(resultStats)))
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
|> deliverOnMainQueue).start(next: { progress in
progressPromise.set(progress)
}, completed: {
statsPromise.set(.single(.result(resultStats)))
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)
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)
}
}))
}
dismissAction()
}))
controller.setItemGroups([
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))
}
}
@ -743,7 +755,9 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
selectedSize = totalSize
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 {
let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 })
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)
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 = {
clearDisposable.set(nil)
resetStats()
}
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
|> deliverOnMainQueue).start(next: { progress in
progressPromise.set(progress)
}, completed: {
statsPromise.set(.single(.result(resultStats)))
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)
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)
}
}))
}
dismissAction()
}))
controller.setItemGroups([
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))
}
@ -995,3 +998,105 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
}
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
}
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 UIKit
import AsyncDisplayKit
import Display
public enum ToolbarActionOption {
case left
case right
case middle
private extension ToolbarTheme {
convenience init(tabBarTheme theme: TabBarControllerTheme) {
self.init(barBackgroundColor: theme.tabBarBackgroundColor, barSeparatorColor: theme.tabBarSeparatorColor, barTextColor: theme.tabBarTextColor, barSelectedTextColor: theme.tabBarSelectedTextColor)
}
}
final class TabBarControllerNode: ASDisplayNode {
@ -65,7 +66,7 @@ final class TabBarControllerNode: ASDisplayNode {
self.tabBarNode.updateTheme(theme)
self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5)
self.toolbarNode?.updateTheme(theme)
self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme))
}
func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
@ -99,7 +100,7 @@ final class TabBarControllerNode: ASDisplayNode {
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)
} 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)
}, right: { [weak self] in
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 AsyncDisplayKit
import SwiftSignalKit
import Display
import UIKitRuntimeUtils
import AnimatedStickerNode
import TelegramAnimatedStickerNode
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) {
@ -41,35 +44,24 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
context.fill(CGRect(origin: CGPoint(), size: size))
if let image = image, imageMode {
let imageRect: CGRect
if horizontal {
let imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.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()
imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)
} 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()
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)
public enum TabBarItemSwipeDirection {
case left
case right
}
private final class TabBarItemNode: ASDisplayNode {
let extractedContainerNode: ContextExtractedContentContainingNode
let containerNode: ContextControllerSourceNode
let imageNode: ASImageNode
let animationContainerNode: ASDisplayNode
let animationNode: AnimatedStickerNode
let textImageNode: ASImageNode
let contextImageNode: ASImageNode
let contextTextImageNode: ASImageNode
@ -117,6 +106,12 @@ private final class TabBarItemNode: ASDisplayNode {
self.imageNode.displayWithoutProcessing = true
self.imageNode.displaysAsynchronously = false
self.imageNode.isAccessibilityElement = false
self.animationContainerNode = ASDisplayNode()
self.animationNode = AnimatedStickerNode()
self.animationNode.automaticallyLoadFirstFrame = true
self.textImageNode = ASImageNode()
self.textImageNode.isUserInteractionEnabled = false
self.textImageNode.displayWithoutProcessing = true
@ -142,6 +137,8 @@ private final class TabBarItemNode: ASDisplayNode {
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
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.contextImageNode)
self.containerNode.addSubnode(self.extractedContainerNode)
@ -451,7 +448,26 @@ class TabBarNode: ASDisplayNode {
})
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 (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 (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
@ -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 (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)
node.animationNode.isHidden = true
node.animationNode.visibility = false
node.textImageNode.image = textImage
node.accessibilityLabel = item.item.title
node.imageNode.image = image
@ -496,7 +516,25 @@ class TabBarNode: ASDisplayNode {
let previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
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 (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 (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
@ -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 (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)
node.animationNode.stop()
node.animationNode.isHidden = true
node.animationNode.visibility = false
node.textImageNode.image = textImage
node.accessibilityLabel = item.item.title
node.imageNode.image = image
@ -614,6 +657,14 @@ class TabBarNode: ASDisplayNode {
node.contextImageNode.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 {
container.appliedBadgeValue = container.badgeValue
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 backgroundFrame: CGRect
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 {
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)
}
transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame)
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.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 {
let container = self.tabBarNodeContainers[closestNode.0]
let previousSelectedIndex = self.selectedIndex
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)
}

View File

@ -142,7 +142,7 @@ public extension TelegramEngine {
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)
}

View File

@ -79,7 +79,7 @@ func mediaBubbleCornerImage(incoming: Bool, radius: CGFloat, inset: CGFloat) ->
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 topRightRadius: CGFloat
let bottomLeftRadius: CGFloat
@ -346,6 +346,12 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa
if !onlyOutline {
context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: formImage.cgImage!)
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 {
context.setFillColor(strokeColor.cgColor)
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) {
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
}
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:)))
if lightnessColor.lightness > 0.705 {

View File

@ -250,6 +250,13 @@ public final class PrincipalThemeEssentialGraphics {
let incomingKnockout = self.incomingBubbleGradientImage != 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 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
self.checkBubbleFullImage = generateCheckImage(partial: false, 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/ReactionImageComponent:ReactionImageComponent",
"//submodules/Translate:Translate",
"//submodules/TabBarUI:TabBarUI",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@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

@ -2041,7 +2041,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
if result {
Queue.mainQueue().async {
let reply = UNTextInputNotificationAction(identifier: "reply", title: replyString, options: [], textInputButtonTitle: replyString, textInputPlaceholder: messagePlaceholderString)
let unknownMessageCategory: UNNotificationCategory
let replyMessageCategory: UNNotificationCategory
let replyLegacyMessageCategory: UNNotificationCategory

View File

@ -32,7 +32,7 @@ private let inlineBotPrefixFont = Font.regular(14.0)
private let inlineBotNameFont = nameFont
protocol GenericAnimatedStickerNode: ASDisplayNode {
func setOverlayColor(_ color: UIColor?, animated: Bool)
func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool)
var currentFrameIndex: Int { get }
func setFrameIndex(_ frameIndex: Int)
@ -58,7 +58,7 @@ private class VideoStickerNode: ASDisplayNode, GenericAnimatedStickerNode {
private var layerHolder: SampleBufferLayer?
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 {
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 {
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) {

View File

@ -279,7 +279,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
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))
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 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.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")
}
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)
|> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?) in
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?, Bool) in
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
let phoneNumberWarning = suggestions.contains(.validatePhoneNumber)
let passwordWarning = suggestions.contains(.validatePassword)
@ -7179,14 +7179,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
if accountTabBarAvatarBadge > 0 {
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 {
strongSelf.tabBarItem.title = title
strongSelf.tabBarItem.image = image
strongSelf.tabBarItem.selectedImage = selectedImage
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
strongSelf.tabBarItem.badgeValue = badgeValue
}
})

View File

@ -14,6 +14,7 @@ import SettingsUI
import AppBundle
import DatePickerNode
import DebugSettingsUI
import TabBarUI
public final class TelegramRootController: NavigationController {
private let context: AccountContext
@ -64,7 +65,7 @@ public final class TelegramRootController: NavigationController {
let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData
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
}
}
@ -81,7 +82,7 @@ public final class TelegramRootController: NavigationController {
}
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
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 {
@ -131,7 +132,7 @@ public final class TelegramRootController: NavigationController {
}
public func updateRootControllers(showCallsTab: Bool) {
guard let rootTabController = self.rootTabController else {
guard let rootTabController = self.rootTabController as? TabBarControllerImpl else {
return
}
var controllers: [ViewController] = []

View File

@ -728,44 +728,68 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu
var pre = match.range(at: 3)
if pre.location != NSNotFound {
let text = string.substring(with: pre)
stringOffset -= match.range(at: 2).length + match.range(at: 4).length
let substring = string.substring(with: match.range(at: 1)) + text + string.substring(with: match.range(at: 5))
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6))
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)
stringOffset -= match.range(at: 2).length + match.range(at: 4).length
let substring = string.substring(with: match.range(at: 1)) + text + string.substring(with: match.range(at: 5))
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 1).length, text.count), 6))
}
}
pre = match.range(at: 8)
if pre.location != NSNotFound {
let text = string.substring(with: pre)
let entity = string.substring(with: match.range(at: 7))
let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9))
let textInputAttribute: NSAttributedString.Key?
switch entity {
case "`":
textInputAttribute = ChatTextInputAttributes.monospace
case "**":
textInputAttribute = ChatTextInputAttributes.bold
case "__":
textInputAttribute = ChatTextInputAttributes.italic
case "~~":
textInputAttribute = ChatTextInputAttributes.strikethrough
case "||":
textInputAttribute = ChatTextInputAttributes.spoiler
default:
textInputAttribute = nil
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 entity = string.substring(with: match.range(at: 7))
let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9))
let textInputAttribute: NSAttributedString.Key?
switch entity {
case "`":
textInputAttribute = ChatTextInputAttributes.monospace
case "**":
textInputAttribute = ChatTextInputAttributes.bold
case "__":
textInputAttribute = ChatTextInputAttributes.italic
case "~~":
textInputAttribute = ChatTextInputAttributes.strikethrough
case "||":
textInputAttribute = ChatTextInputAttributes.spoiler
default:
textInputAttribute = nil
}
if let textInputAttribute = textInputAttribute {
result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
}
stringOffset -= match.range(at: 7).length * 2
}
if let textInputAttribute = textInputAttribute {
result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber]))
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
}
stringOffset -= match.range(at: 7).length * 2
}
string = string.substring(from: match.range.location + match.range(at: 0).length) as NSString

View File

@ -59,7 +59,7 @@ public func translateText(context: AccountContext, text: String) {
return
}
if #available(iOS 15.0, *) {
let text = text.unicodeScalars.filter { !$0.properties.isEmojiPresentation}.reduce("") { $0 + String($1) }
let text = text.unicodeScalars.filter { !$0.properties.isEmojiPresentation }.reduce("") { $0 + String($1) }
let textView = UITextView()
textView.text = text

View File

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

View File

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

View File

@ -432,9 +432,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
var isReady: Signal<Bool, NoError> {
return self._isReady.get()
}
private var newYearNode: WallpaperNewYearNode?
init(context: AccountContext, useSharedAnimationPhase: Bool) {
self.context = context
self.useSharedAnimationPhase = useSharedAnimationPhase
@ -800,16 +798,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
if isFirstLayout && !self.frame.isEmpty {
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) {
@ -1720,47 +1709,3 @@ public func createWallpaperBackgroundNode(context: AccountContext, forChatDispla
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)
}
}
}