mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Temp
This commit is contained in:
parent
6ee1edc8b0
commit
ebcd0557e5
76 changed files with 1352 additions and 1090 deletions
|
|
@ -16,7 +16,7 @@ private func generateIndefiniteActivityIndicatorImage(color: UIColor, diameter:
|
|||
}
|
||||
|
||||
private func convertIndicatorColor(_ color: UIColor) -> UIColor {
|
||||
if color.isEqual(UIColor(rgb: 0x007aff)) {
|
||||
if color.isEqual(UIColor(rgb: 0x007aff)) || color.isEqual(UIColor(rgb: 0x0088ff)) {
|
||||
return .gray
|
||||
} else if color.isEqual(UIColor(rgb: 0x2ea6ff)) {
|
||||
return .white
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ final class BlobNode: ASDisplayNode {
|
|||
|
||||
if let backgroundView = self.backgroundView, let color = self.color {
|
||||
let halfWidth = floor(self.bounds.width * self.minScale)
|
||||
backgroundView.update(size: CGSize(width: halfWidth, height: halfWidth), cornerRadius: halfWidth * 0.5, isDark: false, tintColor: color, transition: .immediate)
|
||||
backgroundView.update(size: CGSize(width: halfWidth, height: halfWidth), cornerRadius: halfWidth * 0.5, isDark: false, tintColor: .init(kind: .custom, color: color), transition: .immediate)
|
||||
backgroundView.frame = CGRect(origin: CGPoint(x: (self.bounds.width - halfWidth) * 0.5, y: (self.bounds.height - halfWidth) * 0.5), size: CGSize(width: halfWidth, height: halfWidth))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Ads/AdsReportScreen",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import Postbox
|
|||
import ChatListHeaderComponent
|
||||
import ActionPanelComponent
|
||||
import ChatFolderLinkPreviewScreen
|
||||
import EdgeEffect
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
final class ChatListContainerItemNode: ASDisplayNode {
|
||||
private final class TopPanelItem {
|
||||
|
|
@ -37,6 +39,8 @@ final class ChatListContainerItemNode: ASDisplayNode {
|
|||
|
||||
private var floatingHeaderOffset: CGFloat?
|
||||
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
|
||||
private(set) var emptyNode: ChatListEmptyNode?
|
||||
var emptyShimmerEffectNode: ChatListShimmerNode?
|
||||
private var shimmerNodeOffset: CGFloat = 0.0
|
||||
|
|
@ -74,9 +78,12 @@ final class ChatListContainerItemNode: ASDisplayNode {
|
|||
self.listNode.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight + ChatListNavigationBar.storiesScrollHeight
|
||||
}
|
||||
|
||||
self.edgeEffectView = EdgeEffectView()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.listNode)
|
||||
self.view.addSubview(self.edgeEffectView)
|
||||
|
||||
self.listNode.isEmptyUpdated = { [weak self] isEmptyState, _, transition in
|
||||
guard let strongSelf = self else {
|
||||
|
|
@ -442,6 +449,11 @@ final class ChatListContainerItemNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
self.layoutAdditionalPanels(transition: transition)
|
||||
|
||||
let edgeEffectHeight: CGFloat = insets.bottom
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight), size: CGSize(width: size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, containerSize: size, transition: ComponentTransition(transition))
|
||||
}
|
||||
|
||||
func updateScrollingOffset(navigationHeight: CGFloat, offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import ChatFolderLinkPreviewScreen
|
|||
import ChatListHeaderComponent
|
||||
import StoryPeerListComponent
|
||||
import TelegramNotices
|
||||
import EdgeEffect
|
||||
|
||||
public enum ChatListContainerNodeFilter: Equatable {
|
||||
case all
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ swift_library(
|
|||
"//submodules/UndoUI",
|
||||
"//submodules/TelegramIntents",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import ChatListTitleView
|
|||
import ComponentFlow
|
||||
import SwiftUI
|
||||
import ContactsUI
|
||||
import EdgeEffect
|
||||
|
||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||
let controller: ViewController
|
||||
|
|
@ -48,6 +49,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
|
|||
|
||||
final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
let contactListNode: ContactListNode
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
|
||||
private let context: AccountContext
|
||||
private(set) var searchDisplayController: SearchDisplayController?
|
||||
|
|
@ -116,6 +118,8 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
contextAction?(peer, node, gesture, location, isStories)
|
||||
})
|
||||
|
||||
self.edgeEffectView = EdgeEffectView()
|
||||
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
|
|
@ -125,6 +129,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
self.addSubnode(self.contactListNode)
|
||||
self.view.addSubview(self.edgeEffectView)
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
|
|
@ -442,6 +447,11 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
|
||||
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
let edgeEffectHeight: CGFloat = layout.intrinsicInsets.bottom
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, containerSize: layout.size, transition: ComponentTransition(transition))
|
||||
|
||||
self.updateNavigationScrolling(transition: transition)
|
||||
|
||||
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class NavigationBackButtonNode: ASControlNode {
|
|||
}
|
||||
}
|
||||
|
||||
public var color: UIColor = UIColor(rgb: 0x007aff) {
|
||||
public var color: UIColor = UIColor(rgb: 0x0088ff) {
|
||||
didSet {
|
||||
self.label.attributedText = NSAttributedString(string: self._text, attributes: self.attributesForCurrentState())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode {
|
|||
}
|
||||
}
|
||||
|
||||
public var color: UIColor = UIColor(rgb: 0x007aff) {
|
||||
public var color: UIColor = UIColor(rgb: 0x0088ff) {
|
||||
didSet {
|
||||
if let text = self._text {
|
||||
self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
|
||||
|
|
@ -347,7 +347,7 @@ public final class NavigationButtonNode: ContextControllerSourceNode {
|
|||
public var pressed: (Int) -> () = { _ in }
|
||||
public var highlightChanged: (Int, Bool) -> () = { _, _ in }
|
||||
|
||||
public var color: UIColor = UIColor(rgb: 0x007aff) {
|
||||
public var color: UIColor = UIColor(rgb: 0x0088ff) {
|
||||
didSet {
|
||||
if !self.color.isEqual(oldValue) {
|
||||
for node in self.nodes {
|
||||
|
|
|
|||
|
|
@ -13,16 +13,12 @@ public protocol TabBarController: ViewController {
|
|||
var controllers: [ViewController] { get }
|
||||
var selectedIndex: Int { get set }
|
||||
|
||||
var cameraItemAndAction: (item: UITabBarItem, action: () -> Void)? { get set }
|
||||
|
||||
func setControllers(_ controllers: [ViewController], selectedIndex: Int?)
|
||||
|
||||
func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition)
|
||||
|
||||
func viewForCameraItem() -> UIView?
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -193,11 +193,7 @@ public final class WindowHostView {
|
|||
}
|
||||
|
||||
fileprivate var onScreenNavigationHeight: CGFloat? {
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
return self.eventView.safeAreaInsets.bottom.isLessThanOrEqualTo(0.0) ? nil : self.eventView.safeAreaInsets.bottom
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return self.eventView.safeAreaInsets.bottom.isLessThanOrEqualTo(0.0) ? nil : self.eventView.safeAreaInsets.bottom
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -544,7 +544,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
|||
self.textView.frameColor = nil
|
||||
case .filled:
|
||||
self.textView.textColor = color.lightness > 0.99 ? UIColor.black : UIColor.white
|
||||
cursorColor = color.lightness > 0.99 ? UIColor(rgb: 0x007aff) : UIColor.white
|
||||
cursorColor = color.lightness > 0.99 ? UIColor(rgb: 0x0088ff) : UIColor.white
|
||||
self.textView.strokeColor = nil
|
||||
self.textView.frameColor = color
|
||||
case .semi:
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn
|
|||
|
||||
public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol {
|
||||
public static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
|
||||
public static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x007aff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
|
||||
public static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x0088ff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
|
||||
|
||||
private var galleryNode: GalleryControllerNode {
|
||||
return self.displayNode as! GalleryControllerNode
|
||||
|
|
|
|||
|
|
@ -776,7 +776,7 @@ final class SettingsHeaderButton: HighlightableButtonNode {
|
|||
component: AnyComponent(BadgeComponent(
|
||||
text: badgeText,
|
||||
font: self.badgeFont,
|
||||
cornerRadius: 3.0,
|
||||
cornerRadius: .custom(3.0),
|
||||
insets: UIEdgeInsets(top: 1.33, left: 1.66, bottom: 1.33, right: 1.66),
|
||||
outerInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
|
||||
)),
|
||||
|
|
@ -818,7 +818,7 @@ final class SettingsHeaderButton: HighlightableButtonNode {
|
|||
component: AnyComponent(BadgeComponent(
|
||||
text: badgeText,
|
||||
font: self.badgeFont,
|
||||
cornerRadius: 3.0,
|
||||
cornerRadius: .custom(3.0),
|
||||
insets: UIEdgeInsets(top: 1.33, left: 1.66, bottom: 1.33, right: 1.66),
|
||||
outerInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ class ChartStackSection: UIView, ChartThemeContainer {
|
|||
backButton.addTarget(self, action: #selector(self.didTapBackButton), for: .touchUpInside)
|
||||
backButton.setTitle("Zoom Out", for: .normal)
|
||||
backButton.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
backButton.setTitleColor(UIColor(rgb: 0x007aff), for: .normal)
|
||||
backButton.setTitleColor(UIColor(rgb: 0x0088ff), for: .normal)
|
||||
backButton.setImage(UIImage(bundleImageName: "Chart/arrow_left"), for: .normal)
|
||||
backButton.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 3.0)
|
||||
backButton.imageView?.tintColor = UIColor(rgb: 0x007aff)
|
||||
backButton.imageView?.tintColor = UIColor(rgb: 0x0088ff)
|
||||
backButton.adjustsImageWhenHighlighted = false
|
||||
|
||||
backButton.setVisible(false, animated: false)
|
||||
|
|
|
|||
|
|
@ -911,7 +911,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
if let current = self.linkHighlightingNode {
|
||||
linkHighlightingNode = current
|
||||
} else {
|
||||
let highlightColor = self.theme?.linkHighlightColor ?? UIColor(rgb: 0x007aff).withAlphaComponent(0.4)
|
||||
let highlightColor = self.theme?.linkHighlightColor ?? UIColor(rgb: 0x0088ff).withAlphaComponent(0.4)
|
||||
linkHighlightingNode = LinkHighlightingNode(color: highlightColor)
|
||||
linkHighlightingNode.isUserInteractionEnabled = false
|
||||
self.linkHighlightingNode = linkHighlightingNode
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ private let lightTheme = InstantPageSettingsItemTheme(
|
|||
separatorColor: UIColor(rgb: 0xc8c7cc),
|
||||
primaryColor: .black,
|
||||
secondaryColor: UIColor(rgb: 0xa8a8a8),
|
||||
accentColor: UIColor(rgb: 0x007aff)
|
||||
accentColor: UIColor(rgb: 0x0088ff)
|
||||
)
|
||||
|
||||
private let sepiaTheme = InstantPageSettingsItemTheme(
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ final class InstantPageSettingsThemeItemNode: InstantPageSettingsItemNode {
|
|||
selectedIndex = 3
|
||||
}
|
||||
|
||||
let selectionColor = UIColor(rgb: 0x007aff)
|
||||
let selectionColor = UIColor(rgb: 0x0088ff)
|
||||
self.themeNodes = [
|
||||
InstantPageSettingsThemeSelectorNode(color: .white, edgeColor: (selectedIndex == 1 || selectedIndex == 2) ? UIColor.lightGray : UIColor.white, selectionColor: selectionColor),
|
||||
InstantPageSettingsThemeSelectorNode(color: UIColor(rgb: 0xcbb98e), edgeColor: UIColor(rgb: 0xcbb98e), selectionColor: selectionColor),
|
||||
|
|
|
|||
|
|
@ -153,15 +153,15 @@ private let lightTheme = InstantPageTheme(
|
|||
),
|
||||
serif: false,
|
||||
codeBlockBackgroundColor: UIColor(rgb: 0xf5f8fc),
|
||||
linkColor: UIColor(rgb: 0x007aff),
|
||||
linkColor: UIColor(rgb: 0x0088ff),
|
||||
textHighlightColor: UIColor(rgb: 0, alpha: 0.12),
|
||||
linkHighlightColor: UIColor(rgb: 0x007aff, alpha: 0.07),
|
||||
linkHighlightColor: UIColor(rgb: 0x0088ff, alpha: 0.07),
|
||||
markerColor: UIColor(rgb: 0xfef3bc),
|
||||
panelBackgroundColor: UIColor(rgb: 0xf3f4f5),
|
||||
panelHighlightedBackgroundColor: UIColor(rgb: 0xe7e7e7),
|
||||
panelPrimaryColor: .black,
|
||||
panelSecondaryColor: UIColor(rgb: 0x79828b),
|
||||
panelAccentColor: UIColor(rgb: 0x007aff),
|
||||
panelAccentColor: UIColor(rgb: 0x0088ff),
|
||||
tableBorderColor: UIColor(rgb: 0xe2e2e2),
|
||||
tableHeaderColor: UIColor(rgb: 0xf4f4f4),
|
||||
controlColor: UIColor(rgb: 0xc7c7cd),
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
int32_t hex = 0x29c519;
|
||||
UIColor *greenColor = [[UIColor alloc] initWithRed:(((hex >> 16) & 0xff) / 255.0f) green:(((hex >> 8) & 0xff) / 255.0f) blue:(((hex) & 0xff) / 255.0f) alpha:1.0f];
|
||||
|
||||
hex = 0x007aff;
|
||||
hex = 0x0088ff;
|
||||
UIColor *blueColor = [[UIColor alloc] initWithRed:(((hex >> 16) & 0xff) / 255.0f) green:(((hex >> 8) & 0xff) / 255.0f) blue:(((hex) & 0xff) / 255.0f) alpha:1.0f];
|
||||
|
||||
hex = 0xcacacf;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ UIColor *TGAccentColor()
|
|||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
color = TGColorWithHex(0x007aff);
|
||||
color = TGColorWithHex(0x0088ff);
|
||||
});
|
||||
return color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@
|
|||
_saveCoverButton.clipsToBounds = true;
|
||||
_saveCoverButton.layer.cornerRadius = 10.0;
|
||||
_saveCoverButton.hidden = true;
|
||||
[_saveCoverButton setBackgroundColor:UIColorRGB(0x007aff)];
|
||||
[_saveCoverButton setBackgroundColor:UIColorRGB(0x0088ff)];
|
||||
_saveCoverButton.titleLabel.font = TGBoldSystemFontOfSize(17.0);
|
||||
[_saveCoverButton setTitle:TGLocalized(@"Media.SaveCover") forState:UIControlStateNormal];
|
||||
[_saveCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
|
|
|||
|
|
@ -719,7 +719,7 @@ private class SendStarsButtonView: HighlightTrackingButton, TGPhotoSendStarsButt
|
|||
let backgroundSize = CGSize(width: width - 11.0, height: 33.0)
|
||||
transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - backgroundSize.width) / 2.0), y: floorToScreenPixels((buttonSize.height - backgroundSize.height) / 2.0)), size: backgroundSize))
|
||||
self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0
|
||||
self.backgroundView.backgroundColor = UIColor(rgb: 0x007aff)
|
||||
self.backgroundView.backgroundColor = UIColor(rgb: 0x0088ff)
|
||||
|
||||
return buttonSize;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ private func generateHeadingArrowImage() -> UIImage? {
|
|||
context.clip()
|
||||
|
||||
var locations: [CGFloat] = [0.0, 0.4, 1.0]
|
||||
let colors: [CGColor] = [UIColor(rgb: 0x007aff, alpha: 0.5).cgColor, UIColor(rgb: 0x007aff, alpha: 0.3).cgColor, UIColor(rgb: 0x007aff, alpha: 0.0).cgColor]
|
||||
let colors: [CGColor] = [UIColor(rgb: 0x0088ff, alpha: 0.5).cgColor, UIColor(rgb: 0x0088ff, alpha: 0.3).cgColor, UIColor(rgb: 0x0088ff, alpha: 0.0).cgColor]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode {
|
|||
let pulseImage: UIImage?
|
||||
let arrowImage: UIImage?
|
||||
if hasPulse {
|
||||
pulseImage = currentPulseImage ?? generateFilledCircleImage(diameter: 120.0, color: UIColor(rgb: 0x007aff, alpha: 0.27))
|
||||
pulseImage = currentPulseImage ?? generateFilledCircleImage(diameter: 120.0, color: UIColor(rgb: 0x0088ff, alpha: 0.27))
|
||||
} else {
|
||||
pulseImage = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ public class LocationPinAnnotationView: MKAnnotationView {
|
|||
|
||||
self.pulseNode = ASImageNode()
|
||||
self.pulseNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 120.0, height: 120.0))
|
||||
self.pulseNode.image = generateFilledCircleImage(diameter: 120.0, color: UIColor(rgb: 0x007aff, alpha: 0.27))
|
||||
self.pulseNode.image = generateFilledCircleImage(diameter: 120.0, color: UIColor(rgb: 0x0088ff, alpha: 0.27))
|
||||
self.pulseNode.isHidden = true
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func generateHeadingArrowImage() -> UIImage? {
|
|||
context.clip()
|
||||
|
||||
var locations: [CGFloat] = [0.0, 0.4, 1.0]
|
||||
let colors: [CGColor] = [UIColor(rgb: 0x007aff, alpha: 0.5).cgColor, UIColor(rgb: 0x007aff, alpha: 0.3).cgColor, UIColor(rgb: 0x007aff, alpha: 0.0).cgColor]
|
||||
let colors: [CGColor] = [UIColor(rgb: 0x0088ff, alpha: 0.5).cgColor, UIColor(rgb: 0x0088ff, alpha: 0.3).cgColor, UIColor(rgb: 0x0088ff, alpha: 0.0).cgColor]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ private final class BusinessListComponent: CombinedComponent {
|
|||
UIColor(rgb: 0x9b4fed),
|
||||
UIColor(rgb: 0x8958ff),
|
||||
UIColor(rgb: 0x676bff),
|
||||
UIColor(rgb: 0x007aff)
|
||||
UIColor(rgb: 0x0088ff)
|
||||
]
|
||||
|
||||
let titleColor = theme.list.itemPrimaryTextColor
|
||||
|
|
|
|||
|
|
@ -2306,7 +2306,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||
UIColor(rgb: 0x9b4fed),
|
||||
UIColor(rgb: 0x8958ff),
|
||||
UIColor(rgb: 0x676bff),
|
||||
UIColor(rgb: 0x007aff)
|
||||
UIColor(rgb: 0x0088ff)
|
||||
]
|
||||
|
||||
var i = 0
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ private final class StoriesListComponent: CombinedComponent {
|
|||
let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
|
||||
let colors = [
|
||||
UIColor(rgb: 0x007aff),
|
||||
UIColor(rgb: 0x0088ff),
|
||||
UIColor(rgb: 0x798aff),
|
||||
UIColor(rgb: 0xac64f3),
|
||||
UIColor(rgb: 0xc456ae),
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
public final class TabBarAccountSwitchController: ViewController {
|
||||
private var controllerNode: TabBarAccountSwitchControllerNode {
|
||||
return self.displayNode as! TabBarAccountSwitchControllerNode
|
||||
}
|
||||
|
||||
private let _ready = Promise<Bool>(true)
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let accounts: (primary: (Account, Peer), other: [(Account, Peer, Int32)])
|
||||
private let canAddAccounts: Bool
|
||||
private let switchToAccount: (AccountRecordId) -> Void
|
||||
private let addAccount: () -> Void
|
||||
private let sourceNodes: [ASDisplayNode]
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var didPlayPresentationAnimation = false
|
||||
private var changedAccount = false
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
public init(sharedContext: SharedAccountContext, accounts: (primary: (Account, Peer), other: [(Account, Peer, Int32)]), canAddAccounts: Bool, switchToAccount: @escaping (AccountRecordId) -> Void, addAccount: @escaping () -> Void, sourceNodes: [ASDisplayNode]) {
|
||||
self.sharedContext = sharedContext
|
||||
self.accounts = accounts
|
||||
self.canAddAccounts = canAddAccounts
|
||||
self.switchToAccount = switchToAccount
|
||||
self.addAccount = addAccount
|
||||
self.sourceNodes = sourceNodes
|
||||
|
||||
self.presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.statusBar.ignoreInCall = true
|
||||
|
||||
self.lockOrientation = true
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = TabBarAccountSwitchControllerNode(sharedContext: self.sharedContext, accounts: self.accounts, presentationData: self.presentationData, canAddAccounts: self.canAddAccounts, switchToAccount: { [weak self] id in
|
||||
guard let strongSelf = self, !strongSelf.changedAccount else {
|
||||
return
|
||||
}
|
||||
strongSelf.changedAccount = true
|
||||
strongSelf.switchToAccount(id)
|
||||
}, addAccount: { [weak self] in
|
||||
guard let strongSelf = self, !strongSelf.changedAccount else {
|
||||
return
|
||||
}
|
||||
strongSelf.addAccount()
|
||||
}, cancel: { [weak self] in
|
||||
self?.dismiss()
|
||||
}, sourceNodes: self.sourceNodes)
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.didPlayPresentationAnimation {
|
||||
self.didPlayPresentationAnimation = true
|
||||
|
||||
self.hapticFeedback.impact()
|
||||
self.controllerNode.animateIn()
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.changedAccount = false
|
||||
self.dismiss(sourceNodes: [])
|
||||
}
|
||||
|
||||
public func dismiss(sourceNodes: [ASDisplayNode]) {
|
||||
self.controllerNode.animateOut(sourceNodes: sourceNodes, changedAccount: self.changedAccount, completion: { [weak self] in
|
||||
self?.didPlayPresentationAnimation = false
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,568 +0,0 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AvatarNode
|
||||
import AccountContext
|
||||
import LocalizedPeerData
|
||||
|
||||
private let animationDurationFactor: Double = 1.0
|
||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
private protocol AbstractSwitchAccountItemNode {
|
||||
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void)
|
||||
}
|
||||
|
||||
private final class AddAccountItemNode: ASDisplayNode, AbstractSwitchAccountItemNode {
|
||||
private let action: () -> Void
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let plusNode: ASImageNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
init(displaySeparator: Bool, presentationData: PresentationData, action: @escaping () -> Void) {
|
||||
self.action = action
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor
|
||||
self.separatorNode.isHidden = !displaySeparator
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.Settings_AddAccount, font: Font.regular(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
self.plusNode = ASImageNode()
|
||||
self.plusNode.image = generateItemListPlusIcon(presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.plusNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.highlightedBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void) {
|
||||
let leftInset: CGFloat = 56.0
|
||||
let rightInset: CGFloat = 10.0
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: maxWidth - leftInset - rightInset, height: .greatestFiniteMagnitude))
|
||||
let height: CGFloat = 61.0
|
||||
|
||||
return (titleSize.width + leftInset + rightInset, height, { width in
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
|
||||
if let image = self.plusNode.image {
|
||||
self.plusNode.frame = CGRect(origin: CGPoint(x: floor((leftInset - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: height - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))
|
||||
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.action()
|
||||
}
|
||||
}
|
||||
|
||||
private final class SwitchAccountItemNode: ASDisplayNode, AbstractSwitchAccountItemNode {
|
||||
private let context: AccountContext
|
||||
private let peer: Peer
|
||||
private let isCurrent: Bool
|
||||
private let unreadCount: Int32
|
||||
private let presentationData: PresentationData
|
||||
private let action: () -> Void
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let checkNode: ASImageNode
|
||||
|
||||
private let badgeBackgroundNode: ASImageNode
|
||||
private let badgeTitleNode: ImmediateTextNode
|
||||
|
||||
init(context: AccountContext, peer: Peer, isCurrent: Bool, unreadCount: Int32, displaySeparator: Bool, presentationData: PresentationData, action: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.isCurrent = isCurrent
|
||||
self.unreadCount = unreadCount
|
||||
self.presentationData = presentationData
|
||||
self.action = action
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor
|
||||
self.separatorNode.isHidden = !displaySeparator
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
self.titleNode.attributedText = NSAttributedString(string: EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.regular(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
self.checkNode = ASImageNode()
|
||||
self.checkNode.image = generateItemListCheckIcon(color: presentationData.theme.actionSheet.primaryTextColor)
|
||||
self.checkNode.isHidden = !isCurrent
|
||||
|
||||
self.badgeBackgroundNode = ASImageNode()
|
||||
self.badgeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 20.0, color: presentationData.theme.list.itemCheckColors.fillColor)
|
||||
self.badgeTitleNode = ImmediateTextNode()
|
||||
if unreadCount > 0 {
|
||||
let countString = compactNumericCountString(Int(unreadCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
||||
self.badgeTitleNode.attributedText = NSAttributedString(string: countString, font: Font.regular(14.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
} else {
|
||||
self.badgeBackgroundNode.isHidden = true
|
||||
self.badgeTitleNode.isHidden = true
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.checkNode)
|
||||
self.addSubnode(self.badgeBackgroundNode)
|
||||
self.addSubnode(self.badgeTitleNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.highlightedBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void) {
|
||||
let leftInset: CGFloat = 56.0
|
||||
|
||||
let badgeTitleSize = self.badgeTitleNode.updateLayout(CGSize(width: 100.0, height: .greatestFiniteMagnitude))
|
||||
let badgeMinSize = self.badgeBackgroundNode.image?.size.width ?? 20.0
|
||||
let badgeSize = CGSize(width: max(badgeMinSize, badgeTitleSize.width + 12.0), height: badgeMinSize)
|
||||
|
||||
let rightInset: CGFloat = max(60.0, badgeSize.width + 40.0)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: maxWidth - leftInset - rightInset, height: .greatestFiniteMagnitude))
|
||||
|
||||
let height: CGFloat = 61.0
|
||||
|
||||
return (titleSize.width + leftInset + rightInset, height, { width in
|
||||
let avatarSize = CGSize(width: 30.0, height: 30.0)
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: floor((leftInset - avatarSize.width) / 2.0), y: floor((height - avatarSize.height) / 2.0)), size: avatarSize)
|
||||
self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme, peer: EnginePeer(self.peer))
|
||||
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
|
||||
if let image = self.checkNode.image {
|
||||
self.checkNode.frame = CGRect(origin: CGPoint(x: width - rightInset + floor((rightInset - image.size.width) / 2.0), y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
let badgeBackgroundFrame = CGRect(origin: CGPoint(x: width - rightInset + floor((rightInset - badgeSize.width) / 2.0), y: floor((height - badgeSize.height) / 2.0)), size: badgeSize)
|
||||
self.badgeBackgroundNode.frame = badgeBackgroundFrame
|
||||
self.badgeTitleNode.frame = CGRect(origin: CGPoint(x: badgeBackgroundFrame.minX + floor((badgeBackgroundFrame.width - badgeTitleSize.width) / 2.0), y: badgeBackgroundFrame.minY + floor((badgeBackgroundFrame.height - badgeTitleSize.height) / 2.0)), size: badgeTitleSize)
|
||||
|
||||
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: height - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))
|
||||
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.action()
|
||||
}
|
||||
}
|
||||
|
||||
final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode {
|
||||
private let presentationData: PresentationData
|
||||
private let cancel: () -> Void
|
||||
|
||||
private let effectView: UIVisualEffectView
|
||||
private var propertyAnimator: AnyObject?
|
||||
private var displayLinkAnimator: DisplayLinkAnimator?
|
||||
private let dimNode: ASDisplayNode
|
||||
|
||||
private let contentContainerNode: ASDisplayNode
|
||||
private let contentNodes: [ASDisplayNode & AbstractSwitchAccountItemNode]
|
||||
|
||||
private var sourceNodes: [ASDisplayNode]
|
||||
private var snapshotViews: [UIView] = []
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(sharedContext: SharedAccountContext, accounts: (primary: (Account, Peer), other: [(Account, Peer, Int32)]), presentationData: PresentationData, canAddAccounts: Bool, switchToAccount: @escaping (AccountRecordId) -> Void, addAccount: @escaping () -> Void, cancel: @escaping () -> Void, sourceNodes: [ASDisplayNode]) {
|
||||
self.presentationData = presentationData
|
||||
self.cancel = cancel
|
||||
self.sourceNodes = sourceNodes
|
||||
|
||||
self.effectView = UIVisualEffectView()
|
||||
if #available(iOS 9.0, *) {
|
||||
} else {
|
||||
if presentationData.theme.rootController.keyboardColor == .dark {
|
||||
self.effectView.effect = UIBlurEffect(style: .dark)
|
||||
} else {
|
||||
self.effectView.effect = UIBlurEffect(style: .light)
|
||||
}
|
||||
self.effectView.alpha = 0.0
|
||||
}
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.alpha = 1.0
|
||||
if presentationData.theme.rootController.keyboardColor == .light {
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04)
|
||||
} else {
|
||||
self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2)
|
||||
}
|
||||
|
||||
self.contentContainerNode = ASDisplayNode()
|
||||
self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
|
||||
self.contentContainerNode.cornerRadius = 20.0
|
||||
self.contentContainerNode.clipsToBounds = true
|
||||
|
||||
var contentNodes: [ASDisplayNode & AbstractSwitchAccountItemNode] = []
|
||||
if canAddAccounts {
|
||||
contentNodes.append(AddAccountItemNode(displaySeparator: true, presentationData: presentationData, action: {
|
||||
addAccount()
|
||||
cancel()
|
||||
}))
|
||||
}
|
||||
contentNodes.append(SwitchAccountItemNode(context: sharedContext.makeTempAccountContext(account: accounts.primary.0), peer: accounts.primary.1, isCurrent: true, unreadCount: 0, displaySeparator: !accounts.other.isEmpty, presentationData: presentationData, action: {
|
||||
cancel()
|
||||
}))
|
||||
for i in 0 ..< accounts.other.count {
|
||||
let (account, peer, count) = accounts.other[i]
|
||||
let id = account.id
|
||||
contentNodes.append(SwitchAccountItemNode(context: sharedContext.makeTempAccountContext(account: account), peer: peer, isCurrent: false, unreadCount: count, displaySeparator: i != accounts.other.count - 1, presentationData: presentationData, action: {
|
||||
switchToAccount(id)
|
||||
}))
|
||||
}
|
||||
self.contentNodes = contentNodes
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.effectView)
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.contentContainerNode)
|
||||
self.contentNodes.forEach(self.contentContainerNode.addSubnode)
|
||||
|
||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor, curve: .easeInOut, animations: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.effectView.effect = makeCustomZoomBlurEffect(isLight: !strongSelf.presentationData.theme.overallDarkAppearance)
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.2 * animationDurationFactor, animations: {
|
||||
self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance)
|
||||
}, completion: { _ in
|
||||
})
|
||||
}
|
||||
|
||||
self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let _ = self.validLayout, let sourceNode = self.sourceNodes.first {
|
||||
let sourceFrame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
self.contentContainerNode.layer.animateFrame(from: sourceFrame, to: self.contentContainerNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
||||
for sourceNode in self.sourceNodes {
|
||||
if let imageNode = sourceNode as? ASImageNode {
|
||||
let snapshot = UIImageView()
|
||||
snapshot.image = imageNode.image
|
||||
snapshot.frame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
snapshot.isUserInteractionEnabled = false
|
||||
self.view.addSubview(snapshot)
|
||||
self.snapshotViews.append(snapshot)
|
||||
} else if let snapshot = sourceNode.view.snapshotContentTree() {
|
||||
snapshot.frame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
snapshot.isUserInteractionEnabled = false
|
||||
self.view.addSubview(snapshot)
|
||||
self.snapshotViews.append(snapshot)
|
||||
}
|
||||
sourceNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut(sourceNodes: [ASDisplayNode], changedAccount: Bool, completion: @escaping () -> Void) {
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
var completedEffect = false
|
||||
var completedSourceNodes = false
|
||||
|
||||
let intermediateCompletion: () -> Void = {
|
||||
if completedEffect && completedSourceNodes {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = nil
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
for sourceNode in strongSelf.sourceNodes {
|
||||
sourceNode.alpha = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
completedEffect = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
self.effectView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.05 * animationDurationFactor, delay: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.21 * animationDurationFactor, animations: {
|
||||
if #available(iOS 9.0, *) {
|
||||
self.effectView.effect = nil
|
||||
} else {
|
||||
self.effectView.alpha = 0.0
|
||||
}
|
||||
}, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
for sourceNode in strongSelf.sourceNodes {
|
||||
sourceNode.alpha = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
completedEffect = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { _ in
|
||||
})
|
||||
if let _ = self.validLayout, let sourceNode = self.sourceNodes.first {
|
||||
let sourceFrame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
self.contentContainerNode.layer.animateFrame(from: self.contentContainerNode.frame, to: sourceFrame, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
if changedAccount {
|
||||
for sourceNode in self.sourceNodes {
|
||||
sourceNode.alpha = 1.0
|
||||
}
|
||||
|
||||
var previousImage: UIImage?
|
||||
for i in 0 ..< self.snapshotViews.count {
|
||||
let view = self.snapshotViews[i]
|
||||
if view.bounds.size.width.isEqual(to: 42.0) {
|
||||
if i == 0, let imageView = view as? UIImageView {
|
||||
previousImage = imageView.image
|
||||
}
|
||||
view.removeFromSuperview()
|
||||
} else {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak view] _ in
|
||||
view?.removeFromSuperview()
|
||||
})
|
||||
view.layer.animateScale(from: 1.0, to: 0.2, duration: 0.25, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
let previousSnapshotViews = self.snapshotViews
|
||||
self.snapshotViews = []
|
||||
|
||||
self.sourceNodes = sourceNodes
|
||||
|
||||
var hadBounce = false
|
||||
for i in 0 ..< self.sourceNodes.count {
|
||||
let sourceNode = self.sourceNodes[i]
|
||||
var snapshot: UIView?
|
||||
if let imageNode = sourceNode as? ASImageNode {
|
||||
let snapshotView = UIImageView()
|
||||
snapshotView.image = imageNode.image
|
||||
snapshotView.frame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
snapshotView.isUserInteractionEnabled = false
|
||||
self.view.addSubview(snapshotView)
|
||||
self.snapshotViews.append(snapshotView)
|
||||
snapshot = snapshotView
|
||||
} else if let genericSnapshot = sourceNode.view.snapshotContentTree() {
|
||||
genericSnapshot.frame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
|
||||
genericSnapshot.isUserInteractionEnabled = false
|
||||
self.view.addSubview(genericSnapshot)
|
||||
self.snapshotViews.append(genericSnapshot)
|
||||
snapshot = genericSnapshot
|
||||
}
|
||||
|
||||
if let snapshot = snapshot {
|
||||
if snapshot.bounds.size.width.isEqual(to: 42.0) {
|
||||
if i == 0, let imageView = snapshot as? UIImageView {
|
||||
hadBounce = true
|
||||
let updatedImage = imageView.image
|
||||
imageView.image = previousImage
|
||||
setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0.3), forView: imageView)
|
||||
imageView.layer.animateScale(from: 1.0, to: 0.6, duration: 0.1, removeOnCompletion: false, completion: { [weak imageView] _ in
|
||||
guard let imageView = imageView else {
|
||||
return
|
||||
}
|
||||
imageView.image = updatedImage
|
||||
if let previousContents = previousImage?.cgImage, let updatedContents = updatedImage?.cgImage {
|
||||
imageView.layer.animate(from: previousContents as AnyObject, to: updatedContents as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.15)
|
||||
}
|
||||
imageView.layer.animateSpring(from: 0.6 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6, completion: { _ in
|
||||
completedSourceNodes = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
snapshot.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
snapshot.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
sourceNode.alpha = 0.0
|
||||
}
|
||||
|
||||
previousSnapshotViews.forEach { view in
|
||||
self.view.bringSubviewToFront(view)
|
||||
}
|
||||
|
||||
if !hadBounce {
|
||||
completedSourceNodes = true
|
||||
}
|
||||
} else {
|
||||
completedSourceNodes = true
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let sideInset: CGFloat = 18.0
|
||||
|
||||
var contentSize = CGSize()
|
||||
contentSize.width = min(layout.size.width - 40.0, 250.0)
|
||||
var applyNodes: [(ASDisplayNode, CGFloat, (CGFloat) -> Void)] = []
|
||||
for itemNode in self.contentNodes {
|
||||
let (width, height, apply) = itemNode.updateLayout(maxWidth: layout.size.width - sideInset * 2.0)
|
||||
applyNodes.append((itemNode, height, apply))
|
||||
contentSize.width = max(contentSize.width, width)
|
||||
contentSize.height += height
|
||||
}
|
||||
|
||||
let insets = layout.insets(options: .input)
|
||||
|
||||
let contentOrigin: CGPoint
|
||||
if let sourceNode = self.sourceNodes.first, let screenFrame = sourceNode.supernode?.convert(sourceNode.frame, to: nil) {
|
||||
contentOrigin = CGPoint(x: screenFrame.maxX - contentSize.width + 8.0, y: layout.size.height - 66.0 - insets.bottom - contentSize.height)
|
||||
} else {
|
||||
contentOrigin = CGPoint(x: layout.size.width - sideInset - contentSize.width, y: layout.size.height - 66.0 - layout.intrinsicInsets.bottom - contentSize.height)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: CGRect(origin: contentOrigin, size: contentSize))
|
||||
var nextY: CGFloat = 0.0
|
||||
for (itemNode, height, apply) in applyNodes {
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: nextY), size: CGSize(width: contentSize.width, height: height)))
|
||||
apply(contentSize.width)
|
||||
nextY += height
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
|
||||
var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x,
|
||||
y: view.bounds.size.height * anchorPoint.y)
|
||||
|
||||
|
||||
var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x,
|
||||
y: view.bounds.size.height * view.layer.anchorPoint.y)
|
||||
|
||||
newPoint = newPoint.applying(view.transform)
|
||||
oldPoint = oldPoint.applying(view.transform)
|
||||
|
||||
var position = view.layer.position
|
||||
position.x -= oldPoint.x
|
||||
position.x += newPoint.x
|
||||
|
||||
position.y -= oldPoint.y
|
||||
position.y += newPoint.y
|
||||
|
||||
view.layer.position = position
|
||||
view.layer.anchorPoint = anchorPoint
|
||||
}
|
||||
|
|
@ -31,14 +31,14 @@ var dayClassicColorPresets: [PresentationThemeAccentColor] = [
|
|||
]
|
||||
|
||||
var dayColorPresets: [PresentationThemeAccentColor] = [
|
||||
PresentationThemeAccentColor(index: 101, baseColor: .preset, accentColor: 0x007aff, bubbleColors: [0x007aff, 0xff53f4], wallpaper: nil),
|
||||
PresentationThemeAccentColor(index: 101, baseColor: .preset, accentColor: 0x0088ff, bubbleColors: [0x0088ff, 0xff53f4], wallpaper: nil),
|
||||
PresentationThemeAccentColor(index: 102, baseColor: .preset, accentColor: 0x00b09b, bubbleColors: [0xaee946, 0x00b09b], wallpaper: nil),
|
||||
PresentationThemeAccentColor(index: 103, baseColor: .preset, accentColor: 0xd33213, bubbleColors: [0xf9db00, 0xd33213], wallpaper: nil),
|
||||
PresentationThemeAccentColor(index: 104, baseColor: .preset, accentColor: 0xea8ced, bubbleColors: [0xea8ced, 0x00c2ed], wallpaper: nil)
|
||||
]
|
||||
|
||||
var nightColorPresets: [PresentationThemeAccentColor] = [
|
||||
// PresentationThemeAccentColor(index: 101, baseColor: .preset, accentColor: 0x007aff, bubbleColors: [0x007aff, 0xff53f4], wallpaper: patternWallpaper(data: .variant4, colors: [0xe4b2ea, 0x8376c2, 0xeab9d9, 0xb493e6], intensity: -35, rotation: nil)),
|
||||
// PresentationThemeAccentColor(index: 101, baseColor: .preset, accentColor: 0x0088ff, bubbleColors: [0x0088ff, 0xff53f4], wallpaper: patternWallpaper(data: .variant4, colors: [0xe4b2ea, 0x8376c2, 0xeab9d9, 0xb493e6], intensity: -35, rotation: nil)),
|
||||
PresentationThemeAccentColor(index: 102, baseColor: .preset, accentColor: 0x00b09b, bubbleColors: [0xaee946, 0x00b09b], wallpaper: patternWallpaper(data: .variant9, colors: [0xe4b2ea, 0x8376c2, 0xeab9d9, 0xb493e6], intensity: -35, rotation: nil)),
|
||||
PresentationThemeAccentColor(index: 103, baseColor: .preset, accentColor: 0xd33213, bubbleColors: [0xf9db00, 0xd33213], wallpaper: patternWallpaper(data: .variant2, colors: [0xfec496, 0xdd6cb9, 0x962fbf, 0x4f5bd5], intensity: -40, rotation: nil)),
|
||||
PresentationThemeAccentColor(index: 104, baseColor: .preset, accentColor: 0xea8ced, bubbleColors: [0xea8ced, 0x00c2ed], wallpaper: patternWallpaper(data: .variant6, colors: [0x8adbf2, 0x888dec, 0xe39fea, 0x679ced], intensity: -30, rotation: nil))
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
|
|||
topColor = bubbleColor
|
||||
bottomColor = bubbleColor
|
||||
} else {
|
||||
fillColor = UIColor(rgb: 0x007aff)
|
||||
fillColor = UIColor(rgb: 0x0088ff)
|
||||
strokeColor = fillColor
|
||||
topColor = UIColor(rgb: 0xe1ffc7)
|
||||
bottomColor = topColor
|
||||
|
|
|
|||
|
|
@ -10,12 +10,15 @@ swift_library(
|
|||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/TelegramUI/Components/TabBarComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -2,24 +2,72 @@ import Foundation
|
|||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import TabBarComponent
|
||||
|
||||
private extension ToolbarTheme {
|
||||
convenience init(tabBarTheme theme: TabBarControllerTheme) {
|
||||
self.init(barBackgroundColor: theme.tabBarBackgroundColor, barSeparatorColor: theme.tabBarSeparatorColor, barTextColor: theme.tabBarTextColor, barSelectedTextColor: theme.tabBarSelectedTextColor)
|
||||
convenience init(theme: PresentationTheme) {
|
||||
self.init(barBackgroundColor: theme.rootController.tabBar.backgroundColor, barSeparatorColor: .clear, barTextColor: theme.rootController.tabBar.textColor, barSelectedTextColor: theme.rootController.tabBar.selectedTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
final class TabBarControllerNode: ASDisplayNode {
|
||||
private var navigationBarPresentationData: NavigationBarPresentationData
|
||||
private var theme: TabBarControllerTheme
|
||||
let tabBarNode: TabBarNode
|
||||
private struct Params: Equatable {
|
||||
let layout: ContainerViewLayout
|
||||
let toolbar: Toolbar?
|
||||
|
||||
init(
|
||||
layout: ContainerViewLayout,
|
||||
toolbar: Toolbar?
|
||||
) {
|
||||
self.layout = layout
|
||||
self.toolbar = toolbar
|
||||
}
|
||||
}
|
||||
|
||||
private struct LayoutResult {
|
||||
let params: Params
|
||||
let bottomInset: CGFloat
|
||||
|
||||
init(params: Params, bottomInset: CGFloat) {
|
||||
self.params = params
|
||||
self.bottomInset = bottomInset
|
||||
}
|
||||
}
|
||||
|
||||
private final class View: UIView {
|
||||
var onLayout: (() -> Void)?
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.onLayout?()
|
||||
}
|
||||
}
|
||||
|
||||
private var theme: PresentationTheme
|
||||
private let itemSelected: (Int, Bool, [ASDisplayNode]) -> Void
|
||||
private let contextAction: (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void
|
||||
|
||||
private let tabBarNode: TabBarNode
|
||||
private let tabBarView = ComponentView<Empty>()
|
||||
|
||||
private let disabledOverlayNode: ASDisplayNode
|
||||
private var toolbarNode: ToolbarNode?
|
||||
private let toolbarActionSelected: (ToolbarActionOption) -> Void
|
||||
private let disabledPressed: () -> Void
|
||||
|
||||
private(set) var tabBarItems: [TabBarNodeItem] = []
|
||||
private(set) var selectedIndex: Int = 0
|
||||
|
||||
var currentControllerNode: ASDisplayNode?
|
||||
|
||||
private var layoutResult: LayoutResult?
|
||||
private var isUpdateRequested: Bool = false
|
||||
private var isChangingSelectedIndex: Bool = false
|
||||
|
||||
func setCurrentControllerNode(_ node: ASDisplayNode?) -> () -> Void {
|
||||
guard node !== self.currentControllerNode else {
|
||||
return {}
|
||||
|
|
@ -42,12 +90,13 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
init(theme: TabBarControllerTheme, navigationBarPresentationData: NavigationBarPresentationData, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) {
|
||||
init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.navigationBarPresentationData = navigationBarPresentationData
|
||||
self.itemSelected = itemSelected
|
||||
self.contextAction = contextAction
|
||||
self.tabBarNode = TabBarNode(theme: theme, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction)
|
||||
self.disabledOverlayNode = ASDisplayNode()
|
||||
self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5)
|
||||
self.disabledOverlayNode.backgroundColor = theme.rootController.tabBar.backgroundColor.withAlphaComponent(0.5)
|
||||
self.disabledOverlayNode.alpha = 0.0
|
||||
self.toolbarActionSelected = toolbarActionSelected
|
||||
self.disabledPressed = disabledPressed
|
||||
|
|
@ -55,13 +104,25 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
return UITracingLayerView()
|
||||
return View(frame: CGRect())
|
||||
})
|
||||
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
(self.view as? View)?.onLayout = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.isUpdateRequested {
|
||||
self.isUpdateRequested = false
|
||||
if let layoutResult = self.layoutResult {
|
||||
let _ = self.updateImpl(params: layoutResult.params, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.addSubnode(self.tabBarNode)
|
||||
self.addSubnode(self.disabledOverlayNode)
|
||||
self.backgroundColor = theme.list.plainBackgroundColor
|
||||
|
||||
//self.addSubnode(self.tabBarNode)
|
||||
//self.addSubnode(self.disabledOverlayNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
|
@ -76,14 +137,14 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: TabBarControllerTheme, navigationBarPresentationData: NavigationBarPresentationData) {
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
self.navigationBarPresentationData = navigationBarPresentationData
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.backgroundColor = theme.list.plainBackgroundColor
|
||||
|
||||
self.tabBarNode.updateTheme(theme)
|
||||
self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5)
|
||||
self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme))
|
||||
self.disabledOverlayNode.backgroundColor = theme.rootController.tabBar.backgroundColor.withAlphaComponent(0.5)
|
||||
self.toolbarNode?.updateTheme(ToolbarTheme(theme: theme))
|
||||
self.requestUpdate()
|
||||
}
|
||||
|
||||
func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
|
||||
|
|
@ -92,32 +153,91 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
|
||||
var tabBarHidden = false
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, toolbar: Toolbar?, transition: ContainedViewLayoutTransition) {
|
||||
var tabBarHeight: CGFloat
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, toolbar: Toolbar?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
let params = Params(layout: layout, toolbar: toolbar)
|
||||
if let layoutResult = self.layoutResult, layoutResult.params == params {
|
||||
return layoutResult.bottomInset
|
||||
} else {
|
||||
let bottomInset = self.updateImpl(params: params, transition: transition)
|
||||
self.layoutResult = LayoutResult(params: params, bottomInset: bottomInset)
|
||||
return bottomInset
|
||||
}
|
||||
}
|
||||
|
||||
private func requestUpdate() {
|
||||
self.isUpdateRequested = true
|
||||
self.view.setNeedsLayout()
|
||||
}
|
||||
|
||||
private func updateImpl(params: Params, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
var options: ContainerViewLayoutInsetOptions = []
|
||||
if layout.metrics.widthClass == .regular {
|
||||
if params.layout.metrics.widthClass == .regular {
|
||||
options.insert(.input)
|
||||
}
|
||||
let bottomInset: CGFloat = layout.insets(options: options).bottom
|
||||
if !layout.safeInsets.left.isZero {
|
||||
tabBarHeight = 34.0 + bottomInset
|
||||
|
||||
var bottomInset: CGFloat = params.layout.insets(options: options).bottom
|
||||
if bottomInset == 0.0 {
|
||||
bottomInset = 8.0
|
||||
} else {
|
||||
tabBarHeight = 49.0 + bottomInset
|
||||
bottomInset = max(bottomInset - 13.0, 8.0)
|
||||
}
|
||||
let sideInset: CGFloat = 21.0
|
||||
|
||||
var selectedId: AnyHashable?
|
||||
if self.selectedIndex < self.tabBarItems.count {
|
||||
selectedId = ObjectIdentifier(self.tabBarItems[self.selectedIndex].item)
|
||||
}
|
||||
var tabBarTransition = ComponentTransition(transition)
|
||||
if self.isChangingSelectedIndex {
|
||||
self.isChangingSelectedIndex = false
|
||||
tabBarTransition = .spring(duration: 0.4)
|
||||
}
|
||||
if self.tabBarView.view == nil {
|
||||
tabBarTransition = .immediate
|
||||
}
|
||||
let tabBarSize = self.tabBarView.update(
|
||||
transition: tabBarTransition,
|
||||
component: AnyComponent(TabBarComponent(
|
||||
theme: self.theme,
|
||||
items: self.tabBarItems.map { item in
|
||||
let itemId = AnyHashable(ObjectIdentifier(item.item))
|
||||
return TabBarComponent.Item(
|
||||
item: item.item,
|
||||
action: { [weak self] isLongTap in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let index = self.tabBarItems.firstIndex(where: { AnyHashable(ObjectIdentifier($0.item)) == itemId }) {
|
||||
self.itemSelected(index, isLongTap, [])
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
selectedId: selectedId
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: params.layout.size.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
let tabBarFrame = CGRect(origin: CGPoint(x: floor((params.layout.size.width - tabBarSize.width) * 0.5), y: params.layout.size.height - (self.tabBarHidden ? 0.0 : (tabBarSize.height + bottomInset))), size: tabBarSize)
|
||||
|
||||
if let tabBarComponentView = self.tabBarView.view {
|
||||
if tabBarComponentView.superview == nil {
|
||||
self.view.addSubview(tabBarComponentView)
|
||||
}
|
||||
transition.updateFrame(view: tabBarComponentView, frame: tabBarFrame)
|
||||
}
|
||||
|
||||
let tabBarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - (self.tabBarHidden ? 0.0 : tabBarHeight)), size: CGSize(width: layout.size.width, height: tabBarHeight))
|
||||
|
||||
transition.updateFrame(node: self.tabBarNode, frame: tabBarFrame)
|
||||
self.tabBarNode.updateLayout(size: layout.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, transition: transition)
|
||||
//transition.updateFrame(node: self.tabBarNode, frame: tabBarFrame)
|
||||
//self.tabBarNode.updateLayout(size: params.layout.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.disabledOverlayNode, frame: tabBarFrame)
|
||||
|
||||
if let toolbar = toolbar {
|
||||
if let toolbar = params.toolbar {
|
||||
if let toolbarNode = self.toolbarNode {
|
||||
transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
|
||||
} else {
|
||||
let toolbarNode = ToolbarNode(theme: ToolbarTheme(tabBarTheme: self.theme), displaySeparator: true, left: { [weak self] in
|
||||
let toolbarNode = ToolbarNode(theme: ToolbarTheme(theme: self.theme), displaySeparator: true, left: { [weak self] in
|
||||
self?.toolbarActionSelected(.left)
|
||||
}, right: { [weak self] in
|
||||
self?.toolbarActionSelected(.right)
|
||||
|
|
@ -125,7 +245,7 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
self?.toolbarActionSelected(.middle)
|
||||
})
|
||||
toolbarNode.frame = tabBarFrame
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate)
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate)
|
||||
self.addSubnode(toolbarNode)
|
||||
self.toolbarNode = toolbarNode
|
||||
if transition.isAnimated {
|
||||
|
|
@ -138,5 +258,31 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
toolbarNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
return params.layout.size.height - tabBarFrame.minY
|
||||
}
|
||||
|
||||
func frameForControllerTab(at index: Int) -> CGRect? {
|
||||
return self.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarNode.view.convert($0, to: self.view) }
|
||||
}
|
||||
|
||||
func isPointInsideContentArea(point: CGPoint) -> Bool {
|
||||
if point.y < self.tabBarNode.frame.minY {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func updateTabBarItems(items: [TabBarNodeItem]) {
|
||||
self.tabBarItems = items
|
||||
self.tabBarNode.tabBarItems = items
|
||||
self.requestUpdate()
|
||||
}
|
||||
|
||||
func updateSelectedIndex(index: Int) {
|
||||
self.selectedIndex = index
|
||||
self.tabBarNode.selectedIndex = index
|
||||
self.isChangingSelectedIndex = true
|
||||
self.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,41 +5,6 @@ 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
|
||||
|
||||
|
|
@ -127,13 +92,9 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
|
||||
private let pendingControllerDisposable = MetaDisposable()
|
||||
|
||||
private var navigationBarPresentationData: NavigationBarPresentationData
|
||||
private var theme: TabBarControllerTheme
|
||||
private var theme: PresentationTheme
|
||||
|
||||
public var cameraItemAndAction: (item: UITabBarItem, action: () -> Void)?
|
||||
|
||||
public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
|
||||
self.navigationBarPresentationData = navigationBarPresentationData
|
||||
public init(theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
|
@ -156,54 +117,27 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
self.pendingControllerDisposable.dispose()
|
||||
}
|
||||
|
||||
public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
|
||||
public func updateTheme(theme: PresentationTheme) {
|
||||
if self.theme !== theme {
|
||||
self.theme = theme
|
||||
self.navigationBarPresentationData = navigationBarPresentationData
|
||||
if self.isNodeLoaded {
|
||||
self.tabBarControllerNode.updateTheme(theme, navigationBarPresentationData: navigationBarPresentationData)
|
||||
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 viewForCameraItem() -> UIView? {
|
||||
if let (cameraItem, _) = self.cameraItemAndAction {
|
||||
if let cameraItemIndex = self.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
return self.tabBarControllerNode.tabBarNode.viewForControllerTab(at: cameraItemIndex)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func frameForControllerTab(controller: ViewController) -> CGRect? {
|
||||
if let index = self.controllers.firstIndex(of: controller) {
|
||||
var index = index
|
||||
if let (cameraItem, _) = self.cameraItemAndAction {
|
||||
if let cameraItemIndex = self.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
if index == cameraItemIndex {
|
||||
|
||||
} else if index > cameraItemIndex {
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.tabBarControllerNode.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarControllerNode.tabBarNode.view.convert($0, to: self.view) }
|
||||
return self.tabBarControllerNode.frameForControllerTab(at: index)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func isPointInsideContentArea(point: CGPoint) -> Bool {
|
||||
if point.y < self.tabBarControllerNode.tabBarNode.frame.minY {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return self.tabBarControllerNode.isPointInsideContentArea(point: point)
|
||||
}
|
||||
|
||||
public func updateIsTabBarEnabled(_ value: Bool, transition: ContainedViewLayoutTransition) {
|
||||
|
|
@ -218,19 +152,8 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
self.displayNode = TabBarControllerNode(theme: self.theme, navigationBarPresentationData: self.navigationBarPresentationData, itemSelected: { [weak self] index, longTap, itemNodes in
|
||||
self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in
|
||||
if let strongSelf = self {
|
||||
var index = index
|
||||
if let (cameraItem, cameraAction) = strongSelf.cameraItemAndAction {
|
||||
if let cameraItemIndex = strongSelf.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
if index == cameraItemIndex {
|
||||
cameraAction()
|
||||
return
|
||||
} else if index > cameraItemIndex {
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController {
|
||||
controller.presentTabBarPreviewingController(sourceNodes: itemNodes)
|
||||
return
|
||||
|
|
@ -298,34 +221,14 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if index >= 0 && index < strongSelf.tabBarControllerNode.tabBarNode.tabBarItems.count {
|
||||
var index = index
|
||||
if let (cameraItem, _) = strongSelf.cameraItemAndAction {
|
||||
if let cameraItemIndex = strongSelf.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
if index == cameraItemIndex {
|
||||
return
|
||||
} else if index > cameraItemIndex {
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if index >= 0 && index < strongSelf.tabBarControllerNode.tabBarItems.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.tabBarControllerNode.tabBarNode.tabBarItems.count {
|
||||
var index = index
|
||||
if let (cameraItem, _) = strongSelf.cameraItemAndAction {
|
||||
if let cameraItemIndex = strongSelf.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
if index == cameraItemIndex {
|
||||
return
|
||||
} else if index > cameraItemIndex {
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if index >= 0 && index < strongSelf.tabBarControllerNode.tabBarItems.count {
|
||||
strongSelf.controllers[index].tabBarItemSwipeAction(direction: direction)
|
||||
}
|
||||
}, toolbarActionSelected: { [weak self] action in
|
||||
|
|
@ -339,9 +242,6 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
}
|
||||
|
||||
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.1)
|
||||
transition.updateAlpha(node: self.tabBarControllerNode.tabBarNode.separatorNode, alpha: alpha, delay: 0.1)
|
||||
}
|
||||
|
||||
private func updateSelectedIndex(animated: Bool = false) {
|
||||
|
|
@ -354,15 +254,8 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
animated = false
|
||||
}
|
||||
|
||||
var tabBarSelectedIndex = self.selectedIndex
|
||||
if let (cameraItem, _) = self.cameraItemAndAction {
|
||||
if let cameraItemIndex = self.tabBarControllerNode.tabBarNode.tabBarItems.firstIndex(where: { $0.item === cameraItem }) {
|
||||
if tabBarSelectedIndex >= cameraItemIndex {
|
||||
tabBarSelectedIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
self.tabBarControllerNode.tabBarNode.selectedIndex = tabBarSelectedIndex
|
||||
let tabBarSelectedIndex = self.selectedIndex
|
||||
self.tabBarControllerNode.updateSelectedIndex(index: tabBarSelectedIndex)
|
||||
|
||||
var transitionScale: CGFloat = 0.998
|
||||
if let currentView = self.currentController?.view {
|
||||
|
|
@ -428,26 +321,14 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
|
||||
self.validLayout = layout
|
||||
|
||||
self.tabBarControllerNode.containerLayoutUpdated(layout, toolbar: self.currentController?.toolbar, transition: transition)
|
||||
let bottomInset = 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
|
||||
}
|
||||
if !self.tabBarControllerNode.tabBarHidden {
|
||||
updatedLayout.intrinsicInsets.bottom = tabBarHeight
|
||||
updatedLayout.intrinsicInsets.bottom = bottomInset
|
||||
}
|
||||
|
||||
currentController.containerLayoutUpdated(updatedLayout, transition: transition)
|
||||
|
|
@ -525,12 +406,9 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
}
|
||||
self.controllers = controllers
|
||||
|
||||
var tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) })
|
||||
if let (cameraItem, _) = self.cameraItemAndAction {
|
||||
tabBarItems.insert(TabBarNodeItem(item: cameraItem, contextActionType: .none), at: Int(floor(CGFloat(controllers.count) / 2)))
|
||||
}
|
||||
let tabBarItems = self.controllers.map({ TabBarNodeItem(item: $0.tabBarItem, contextActionType: $0.tabBarItemContextActionType) })
|
||||
|
||||
self.tabBarControllerNode.tabBarNode.tabBarItems = tabBarItems
|
||||
self.tabBarControllerNode.updateTabBarItems(items: tabBarItems)
|
||||
|
||||
let signals = combineLatest(self.controllers.map({ $0.tabBarItem }).map { tabBarItem -> Signal<Bool, NoError> in
|
||||
if let tabBarItem = tabBarItem, tabBarItem.image == nil {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import Display
|
|||
import UIKitRuntimeUtils
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import TelegramPresentationData
|
||||
|
||||
private extension CGRect {
|
||||
var center: CGPoint {
|
||||
|
|
@ -343,7 +344,7 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
private let contextAction: (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void
|
||||
private let swipeAction: (Int, TabBarItemSwipeDirection) -> Void
|
||||
|
||||
private var theme: TabBarControllerTheme
|
||||
private var theme: PresentationTheme
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, UIEdgeInsets, CGFloat)?
|
||||
private var horizontal: Bool = false
|
||||
private var centered: Bool = false
|
||||
|
|
@ -351,25 +352,19 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
private var badgeImage: UIImage
|
||||
|
||||
let backgroundNode: NavigationBackgroundNode
|
||||
let separatorNode: ASDisplayNode
|
||||
private var tabBarNodeContainers: [TabBarNodeContainer] = []
|
||||
|
||||
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
|
||||
init(theme: TabBarControllerTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) {
|
||||
init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) {
|
||||
self.itemSelected = itemSelected
|
||||
self.contextAction = contextAction
|
||||
self.swipeAction = swipeAction
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor)
|
||||
self.backgroundNode = NavigationBackgroundNode(color: theme.rootController.tabBar.backgroundColor)
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
|
||||
self.separatorNode.isOpaque = true
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
self.badgeImage = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.tabBarBadgeBackgroundColor, strokeColor: theme.tabBarBadgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)!
|
||||
self.badgeImage = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.rootController.tabBar.badgeBackgroundColor, strokeColor: theme.rootController.tabBar.badgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)!
|
||||
|
||||
super.init()
|
||||
|
||||
|
|
@ -382,7 +377,6 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
self.isExclusiveTouch = true
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
|
@ -417,17 +411,16 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: TabBarControllerTheme) {
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
if self.theme !== theme {
|
||||
self.theme = theme
|
||||
|
||||
self.separatorNode.backgroundColor = theme.tabBarSeparatorColor
|
||||
self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate)
|
||||
self.backgroundNode.updateColor(color: theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||
|
||||
self.badgeImage = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.tabBarBadgeBackgroundColor, strokeColor: theme.tabBarBadgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)!
|
||||
self.badgeImage = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.rootController.tabBar.badgeBackgroundColor, strokeColor: theme.rootController.tabBar.badgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)!
|
||||
for container in self.tabBarNodeContainers {
|
||||
if let attributedText = container.badgeTextNode.attributedText, !attributedText.string.isEmpty {
|
||||
container.badgeTextNode.attributedText = NSAttributedString(string: attributedText.string, font: badgeFont, textColor: self.theme.tabBarBadgeTextColor)
|
||||
container.badgeTextNode.attributedText = NSAttributedString(string: attributedText.string, font: badgeFont, textColor: theme.rootController.tabBar.badgeTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -443,11 +436,6 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func sourceNodesForController(at index: Int) -> [ASDisplayNode]? {
|
||||
let container = self.tabBarNodeContainers[index]
|
||||
return [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode]
|
||||
}
|
||||
|
||||
func frameForControllerTab(at index: Int) -> CGRect? {
|
||||
let container = self.tabBarNodeContainers[index]
|
||||
return container.imageNode.frame
|
||||
|
|
@ -463,7 +451,7 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
node.imageNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
self.centered = self.theme.tabBarTextColor == .clear
|
||||
self.centered = self.theme.rootController.tabBar.textColor == .clear
|
||||
|
||||
var tabBarNodeContainers: [TabBarNodeContainer] = []
|
||||
for i in 0 ..< self.tabBarItems.count {
|
||||
|
|
@ -484,13 +472,13 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
self?.swipeAction(i, direction)
|
||||
})
|
||||
if item.item.ringSelection {
|
||||
node.ringColor = self.theme.tabBarSelectedIconColor
|
||||
node.ringColor = self.theme.rootController.tabBar.selectedIconColor
|
||||
} else {
|
||||
node.ringColor = nil
|
||||
}
|
||||
|
||||
if let selectedIndex = self.selectedIndex, selectedIndex == i {
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.selectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (image, imageContentWidth): (UIImage, CGFloat)
|
||||
|
||||
if let _ = item.item.animationName {
|
||||
|
|
@ -502,17 +490,17 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
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.setOverlayColor(self.theme.rootController.tabBar.selectedIconColor, 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)
|
||||
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.selectedIconColor, 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)
|
||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
node.textImageNode.image = textImage
|
||||
node.imageNode.image = image
|
||||
node.contextTextImageNode.image = contextTextImage
|
||||
|
|
@ -522,10 +510,10 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||
node.isSelected = true
|
||||
} else {
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.textColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.iconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
|
||||
node.animationNode.isHidden = true
|
||||
node.animationNode.visibility = false
|
||||
|
|
@ -555,10 +543,10 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
let node = self.tabBarNodeContainers[index].imageNode
|
||||
let item = self.tabBarItems[index]
|
||||
|
||||
self.centered = self.theme.tabBarTextColor == .clear
|
||||
self.centered = self.theme.rootController.tabBar.textColor == .clear
|
||||
|
||||
if item.item.ringSelection {
|
||||
node.ringColor = self.theme.tabBarSelectedIconColor
|
||||
node.ringColor = self.theme.rootController.tabBar.selectedIconColor
|
||||
} else {
|
||||
node.ringColor = nil
|
||||
}
|
||||
|
|
@ -566,7 +554,7 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
let previousImageSize = node.imageNode.image?.size ?? CGSize()
|
||||
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 (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.selectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (image, imageContentWidth): (UIImage, CGFloat)
|
||||
if let _ = item.item.animationName {
|
||||
(image, imageContentWidth) = (UIImage(), 0.0)
|
||||
|
|
@ -577,21 +565,21 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
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.setOverlayColor(self.theme.rootController.tabBar.selectedIconColor, replace: true, animated: false)
|
||||
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
|
||||
} else {
|
||||
if item.item.ringSelection {
|
||||
(image, imageContentWidth) = (item.item.selectedImage ?? UIImage(), item.item.selectedImage?.size.width ?? 0.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)
|
||||
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.selectedIconColor, 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)
|
||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
node.textImageNode.image = textImage
|
||||
node.accessibilityLabel = item.item.title
|
||||
node.accessibilityTraits = [.button, .selected]
|
||||
|
|
@ -610,16 +598,16 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
})
|
||||
}
|
||||
} else {
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.textColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
|
||||
let (image, imageContentWidth): (UIImage, CGFloat)
|
||||
if item.item.ringSelection {
|
||||
(image, imageContentWidth) = (item.item.image ?? UIImage(), item.item.image?.size.width ?? 0.0)
|
||||
} else {
|
||||
(image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
(image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.rootController.tabBar.iconColor, 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)
|
||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.contextMenu.extractedContentTintColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||
|
||||
node.animationNode.stop()
|
||||
node.animationNode.isHidden = true
|
||||
|
|
@ -672,8 +660,6 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundNode.update(size: size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: separatorHeight)))
|
||||
|
||||
let horizontal = !leftInset.isZero
|
||||
if self.horizontal != horizontal {
|
||||
self.horizontal = horizontal
|
||||
|
|
@ -759,7 +745,7 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
if container.badgeValue != container.appliedBadgeValue {
|
||||
container.appliedBadgeValue = container.badgeValue
|
||||
if let badgeValue = container.badgeValue, !badgeValue.isEmpty {
|
||||
container.badgeTextNode.attributedText = NSAttributedString(string: badgeValue, font: badgeFont, textColor: self.theme.tabBarBadgeTextColor)
|
||||
container.badgeTextNode.attributedText = NSAttributedString(string: badgeValue, font: badgeFont, textColor: self.theme.rootController.tabBar.badgeTextColor)
|
||||
container.badgeContainerNode.isHidden = false
|
||||
} else {
|
||||
container.badgeContainerNode.isHidden = true
|
||||
|
|
@ -797,7 +783,6 @@ class TabBarNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||
return
|
||||
}
|
||||
var closestNode: (Int, CGFloat)?
|
||||
|
||||
for i in 0 ..< self.tabBarNodeContainers.count {
|
||||
let node = self.tabBarNodeContainers[i].imageNode
|
||||
if !node.isUserInteractionEnabled {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import PresentationDataUtils
|
|||
import UIKitRuntimeUtils
|
||||
import ReplayKit
|
||||
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x0088ff)
|
||||
|
||||
protocol PreviewVideoNode: ASDisplayNode {
|
||||
var ready: Signal<Bool, NoError> { get }
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ private let videoCornerRadius: CGFloat = 23.0
|
|||
private let avatarSize: CGFloat = 50.0
|
||||
private let videoSize = CGSize(width: 180.0, height: 180.0)
|
||||
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x0088ff)
|
||||
private let constructiveColor: UIColor = UIColor(rgb: 0x34c759)
|
||||
private let destructiveColor: UIColor = UIColor(rgb: 0xff3b30)
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ private let tileSize = CGSize(width: 84.0, height: 84.0)
|
|||
private let backgroundCornerRadius: CGFloat = 14.0
|
||||
private let avatarSize: CGFloat = 40.0
|
||||
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x0088ff)
|
||||
private let constructiveColor: UIColor = UIColor(rgb: 0x34c759)
|
||||
private let destructiveColor: UIColor = UIColor(rgb: 0xff3b30)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import SolidRoundedButtonNode
|
|||
import PresentationDataUtils
|
||||
import VoiceChatActionButton
|
||||
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
|
||||
private let accentColor: UIColor = UIColor(rgb: 0x0088ff)
|
||||
|
||||
final class VoiceChatRecordingSetupController: ViewController {
|
||||
private var controllerNode: VoiceChatRecordingSetupControllerNode {
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||
outgoing: PresentationThemePartedColors(
|
||||
bubble: PresentationThemeBubbleColor(
|
||||
withWallpaper: PresentationThemeBubbleColorComponents(
|
||||
fill: [UIColor(rgb: 0x61BCF9), UIColor(rgb: 0x007AFF)],
|
||||
fill: [UIColor(rgb: 0x61BCF9), UIColor(rgb: 0x0088ff)],
|
||||
highlightedFill: UIColor(rgb: 0x61BCF9),
|
||||
stroke: .clear,
|
||||
shadow: nil,
|
||||
|
|
@ -577,7 +577,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||
reactionActiveMediaPlaceholder: UIColor(rgb: 0x000000, alpha: 0.1)
|
||||
),
|
||||
withoutWallpaper: PresentationThemeBubbleColorComponents(
|
||||
fill: [UIColor(rgb: 0x61BCF9), UIColor(rgb: 0x007AFF)],
|
||||
fill: [UIColor(rgb: 0x61BCF9), UIColor(rgb: 0x0088ff)],
|
||||
highlightedFill: UIColor(rgb: 0x61BCF9),
|
||||
stroke: .clear,
|
||||
shadow: nil,
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public func dateFillNeedsBlur(theme: PresentationTheme, wallpaper: TelegramWallp
|
|||
|
||||
public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.2)
|
||||
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false)
|
||||
public let defaultDayAccentColor = UIColor(rgb: 0x007aff)
|
||||
public let defaultDayAccentColor = UIColor(rgb: 0x0088ff)
|
||||
|
||||
public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, outgoingAccentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme {
|
||||
if (theme.referenceTheme != .day && theme.referenceTheme != .dayClassic) {
|
||||
|
|
@ -436,7 +436,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
|||
separatorColor: UIColor(rgb: 0xb2b2b2),
|
||||
iconColor: UIColor(rgb: 0x959595),
|
||||
selectedIconColor: defaultDayAccentColor,
|
||||
textColor: UIColor(rgb: 0x959595),
|
||||
textColor: UIColor(rgb: 0x000000, alpha: 0.8),
|
||||
selectedTextColor: defaultDayAccentColor,
|
||||
badgeBackgroundColor: UIColor(rgb: 0xff3b30),
|
||||
badgeStrokeColor: UIColor(rgb: 0xff3b30),
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ public struct PresentationResourcesSettings {
|
|||
context.addPath(path.cgPath)
|
||||
context.clip()
|
||||
|
||||
context.setFillColor(UIColor(rgb: 0x007aff).cgColor)
|
||||
context.setFillColor(UIColor(rgb: 0x0088ff).cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Bot"), color: UIColor(rgb: 0xffffff)), let cgImage = image.cgImage {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,21 @@ import RasterizedCompositionComponent
|
|||
import ComponentFlow
|
||||
|
||||
public final class BadgeComponent: Component {
|
||||
public enum CornerRadius: Equatable {
|
||||
case automatic
|
||||
case custom(CGFloat)
|
||||
}
|
||||
|
||||
public let text: String
|
||||
public let font: UIFont
|
||||
public let cornerRadius: CGFloat
|
||||
public let cornerRadius: CornerRadius
|
||||
public let insets: UIEdgeInsets
|
||||
public let outerInsets: UIEdgeInsets
|
||||
|
||||
public init(
|
||||
text: String,
|
||||
font: UIFont,
|
||||
cornerRadius: CGFloat,
|
||||
cornerRadius: CornerRadius,
|
||||
insets: UIEdgeInsets,
|
||||
outerInsets: UIEdgeInsets
|
||||
) {
|
||||
|
|
@ -144,12 +149,6 @@ public final class BadgeComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if component.cornerRadius != previousComponent?.cornerRadius {
|
||||
self.backgroundLayer.image = generateStretchableFilledCircleImage(diameter: component.cornerRadius * 2.0, color: .white)
|
||||
|
||||
self.backgroundInsetLayer.image = generateStretchableFilledCircleImage(diameter: component.cornerRadius * 2.0, color: .black)
|
||||
}
|
||||
|
||||
let textSize = self.textLayout?.size ?? CGSize(width: 1.0, height: 1.0)
|
||||
|
||||
let size = CGSize(width: textSize.width + component.insets.left + component.insets.right, height: textSize.height + component.insets.top + component.insets.bottom)
|
||||
|
|
@ -170,6 +169,19 @@ public final class BadgeComponent: Component {
|
|||
transition.setPosition(layer: self.textContentsLayer, position: textFrame.origin)
|
||||
self.textContentsLayer.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
|
||||
|
||||
if component.cornerRadius != previousComponent?.cornerRadius {
|
||||
let cornerRadius: CGFloat
|
||||
switch component.cornerRadius {
|
||||
case let .custom(value):
|
||||
cornerRadius = value
|
||||
case .automatic:
|
||||
cornerRadius = floor(min(size.width, size.height) * 0.5)
|
||||
}
|
||||
|
||||
self.backgroundLayer.image = generateStretchableFilledCircleImage(diameter: cornerRadius * 2.0, color: .white)
|
||||
self.backgroundInsetLayer.image = generateStretchableFilledCircleImage(diameter: cornerRadius * 2.0, color: .black)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,9 +113,9 @@ final class PlaceholderComponent: Component {
|
|||
component: AnyComponent(
|
||||
ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: UIColor(rgb: 0x007aff),
|
||||
color: UIColor(rgb: 0x0088ff),
|
||||
foreground: .white,
|
||||
pressedColor: UIColor(rgb: 0x007aff, alpha: 0.55)
|
||||
pressedColor: UIColor(rgb: 0x0088ff, alpha: 0.55)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: buttonTitle,
|
||||
|
|
|
|||
|
|
@ -718,7 +718,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
if self.visibility != oldValue {
|
||||
self.visibilityStatus = self.visibility != .none
|
||||
|
||||
self.updateVisibility()
|
||||
self.updateVisibility(isScroll: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -743,6 +743,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
typealias Params = (item: ChatMessageItem, params: ListViewItemLayoutParams, mergedTop: ChatMessageMerge, mergedBottom: ChatMessageMerge, dateHeaderAtBottom: ChatMessageHeaderSpec)
|
||||
private var currentInputParams: Params?
|
||||
private var currentApplyParams: ListViewItemApply?
|
||||
private var contentLayoutInsets = UIEdgeInsets()
|
||||
|
||||
required public init(rotated: Bool) {
|
||||
self.mainContextSourceNode = ContextExtractedContentContainingNode()
|
||||
|
|
@ -3338,7 +3339,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
}
|
||||
}
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets)
|
||||
layoutSize.height += layoutInsets.top + layoutInsets.bottom
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: UIEdgeInsets())
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||
|
||||
|
|
@ -3364,6 +3367,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
params: params,
|
||||
applyInfo: applyInfo,
|
||||
layout: layout,
|
||||
layoutInsets: layoutInsets,
|
||||
item: item,
|
||||
forwardSource: forwardSource,
|
||||
forwardAuthorSignature: forwardAuthorSignature,
|
||||
|
|
@ -3377,12 +3381,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
graphics: graphics,
|
||||
presentationContext: item.controllerInteraction.presentationContext,
|
||||
bubbleContentWidth: bubbleContentWidth,
|
||||
backgroundFrame: backgroundFrame,
|
||||
backgroundFrame: backgroundFrame.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
deliveryFailedInset: deliveryFailedInset,
|
||||
nameNodeSizeApply: nameNodeSizeApply,
|
||||
viaWidth: viaWidth,
|
||||
contentOrigin: contentOrigin,
|
||||
nameNodeOriginY: nameNodeOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
contentOrigin: contentOrigin.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
nameNodeOriginY: layoutInsets.top + nameNodeOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
hasTitleAvatar: hasTitleAvatar,
|
||||
hasTitleTopicNavigation: hasTitleTopicNavigation,
|
||||
authorNameColor: authorNameColor,
|
||||
|
|
@ -3392,25 +3396,27 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
boostNodeSizeApply: boostNodeSizeApply,
|
||||
contentUpperRightCorner: contentUpperRightCorner,
|
||||
threadInfoSizeApply: threadInfoSizeApply,
|
||||
threadInfoOriginY: threadInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
threadInfoOriginY: layoutInsets.top + threadInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
forwardInfoSizeApply: forwardInfoSizeApply,
|
||||
forwardInfoOriginY: forwardInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
forwardInfoOriginY: layoutInsets.top + forwardInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
replyInfoSizeApply: replyInfoSizeApply,
|
||||
replyInfoOriginY: replyInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
replyInfoOriginY: layoutInsets.top + replyInfoOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||
removedContentNodeIndices: removedContentNodeIndices,
|
||||
updatedContentNodeOrder: updatedContentNodeOrder,
|
||||
addedContentNodes: addedContentNodes,
|
||||
contentNodeMessagesAndClasses: contentNodeMessagesAndClasses,
|
||||
contentNodeFramesPropertiesAndApply: contentNodeFramesPropertiesAndApply,
|
||||
contentContainerNodeFrames: contentContainerNodeFrames,
|
||||
mosaicStatusOrigin: mosaicStatusOrigin,
|
||||
contentContainerNodeFrames: contentContainerNodeFrames.map { containerGroupId, containerFrame, currentItemSelection, currentContainerGroupOverlap in
|
||||
return (containerGroupId, containerFrame.offsetBy(dx: 0.0, dy: layoutInsets.top), currentItemSelection, currentContainerGroupOverlap)
|
||||
},
|
||||
mosaicStatusOrigin: mosaicStatusOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
|
||||
unlockButtonPosition: unlockButtonPosition,
|
||||
unlockButtonPosition: unlockButtonPosition?.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
unlockButtonSizeAndApply: unlockButtonSizeApply,
|
||||
mediaInfoOrigin: mediaInfoOrigin,
|
||||
mediaInfoOrigin: mediaInfoOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
mediaInfoSizeAndApply: mediaInfoSizeApply,
|
||||
needsShareButton: needsShareButton,
|
||||
shareButtonOffset: shareButtonOffset,
|
||||
shareButtonOffset: shareButtonOffset?.offsetBy(dx: 0.0, dy: layoutInsets.top),
|
||||
avatarOffset: avatarOffset,
|
||||
hidesHeaders: hidesHeaders,
|
||||
disablesComments: disablesComments,
|
||||
|
|
@ -3428,6 +3434,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
params: ListViewItemLayoutParams,
|
||||
applyInfo: ListViewItemApply,
|
||||
layout: ListViewItemNodeLayout,
|
||||
layoutInsets: UIEdgeInsets,
|
||||
item: ChatMessageItem,
|
||||
forwardSource: Peer?,
|
||||
forwardAuthorSignature: String?,
|
||||
|
|
@ -3488,6 +3495,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
|
||||
strongSelf.currentInputParams = inputParams
|
||||
strongSelf.currentApplyParams = applyInfo
|
||||
strongSelf.contentLayoutInsets = layoutInsets
|
||||
|
||||
if item.message.id.namespace == Namespaces.Message.Local || item.message.id.namespace == Namespaces.Message.ScheduledLocal || item.message.id.namespace == Namespaces.Message.QuickReplyLocal {
|
||||
strongSelf.wasPending = true
|
||||
|
|
@ -3570,7 +3578,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
strongSelf.suggestedPostInfoNode = suggestedPostInfoNode
|
||||
strongSelf.addSubnode(suggestedPostInfoNode)
|
||||
}
|
||||
let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize)
|
||||
let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: layoutInsets.top + 4.0), size: suggestedPostInfoSize)
|
||||
suggestedPostInfoNode.frame = suggestedPostInfoFrame
|
||||
} else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode {
|
||||
strongSelf.suggestedPostInfoNode = nil
|
||||
|
|
@ -4999,7 +5007,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
|
||||
strongSelf.updateSearchTextHighlightState()
|
||||
|
||||
strongSelf.updateVisibility()
|
||||
strongSelf.updateVisibility(isScroll: false)
|
||||
|
||||
if let (_, f) = strongSelf.awaitingAppliedReaction {
|
||||
strongSelf.awaitingAppliedReaction = nil
|
||||
|
|
@ -6332,10 +6340,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
if let swipeToReplyNode = self.swipeToReplyNode {
|
||||
if translation.x < 0.0 {
|
||||
swipeToReplyNode.bounds = CGRect(origin: .zero, size: CGSize(width: 33.0, height: 33.0))
|
||||
swipeToReplyNode.position = CGPoint(x: bounds.size.width + offset + 33.0 * 0.5, y: self.contentSize.height / 2.0)
|
||||
swipeToReplyNode.position = CGPoint(x: bounds.size.width + offset + 33.0 * 0.5, y: self.contentLayoutInsets.top + (self.contentSize.height - self.contentLayoutInsets.top - self.contentLayoutInsets.bottom) / 2.0)
|
||||
} else {
|
||||
swipeToReplyNode.bounds = CGRect(origin: .zero, size: CGSize(width: 33.0, height: 33.0))
|
||||
swipeToReplyNode.position = CGPoint(x: leftOffset - 33.0 * 0.5, y: self.contentSize.height / 2.0)
|
||||
swipeToReplyNode.position = CGPoint(x: leftOffset - 33.0 * 0.5, y: self.contentLayoutInsets.top + (self.contentSize.height - self.contentLayoutInsets.top - self.contentLayoutInsets.bottom) / 2.0)
|
||||
}
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
|
|
@ -6561,7 +6569,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
contentNode.unreadMessageRangeUpdated()
|
||||
}
|
||||
|
||||
self.updateVisibility()
|
||||
self.updateVisibility(isScroll: false)
|
||||
}
|
||||
|
||||
public func animateQuizInvalidOptionSelected() {
|
||||
|
|
@ -6761,10 +6769,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
|
||||
override public func updateStickerSettings(forceStopAnimations: Bool) {
|
||||
self.forceStopAnimations = forceStopAnimations
|
||||
self.updateVisibility()
|
||||
self.updateVisibility(isScroll: false)
|
||||
}
|
||||
|
||||
private func updateVisibility() {
|
||||
private func updateVisibility(isScroll: Bool) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
|
|
@ -6841,6 +6849,15 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
self.playMessageEffect(force: false)
|
||||
}
|
||||
}
|
||||
|
||||
if item.message.adAttribute != nil {
|
||||
let transition: ContainedViewLayoutTransition = isScroll ? .animated(duration: 0.4, curve: .spring) : .immediate
|
||||
if case let .visible(_, rect) = self.visibility, rect.height >= 1.0 {
|
||||
transition.updateSublayerTransformOffset(layer: self.layer, offset: CGPoint(x: 0.0, y: 0.0))
|
||||
} else {
|
||||
transition.updateSublayerTransformOffset(layer: self.layer, offset: CGPoint(x: 0.0, y: 200.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func messageEffectTargetView() -> UIView? {
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ final class PlayButtonNode: ASDisplayNode {
|
|||
|
||||
let backgroundFrame = buttonSize.centered(in: CGRect(origin: .zero, size: size))
|
||||
transition.updateFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.4), transition: ComponentTransition(transition))
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.4)), transition: ComponentTransition(transition))
|
||||
|
||||
self.playPauseIconNode.frame = CGRect(origin: CGPoint(x: 3.0, y: 1.0 - UIScreenPixel), size: CGSize(width: 21.0, height: 21.0))
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public final class ChatRecordingViewOnceButtonNode: HighlightTrackingButtonNode
|
|||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - innerSize.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - innerSize.height / 2.0)), size: innerSize)
|
||||
self.backgroundView.frame = backgroundFrame
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: .immediate)
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate)
|
||||
|
||||
if let iconImage = self.iconNode.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - iconImage.size.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - iconImage.size.height / 2.0)), size: iconImage.size)
|
||||
|
|
|
|||
|
|
@ -334,13 +334,13 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
|
|||
}
|
||||
|
||||
transition.updateFrame(view: self.micButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.micButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: ComponentTransition(transition))
|
||||
self.micButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition))
|
||||
|
||||
transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.micButton.layoutItems()
|
||||
|
||||
transition.updateFrame(view: self.sendButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: innerSize))
|
||||
self.sendButtonBackgroundView.update(size: innerSize, cornerRadius: innerSize.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: interfaceState.theme.chat.inputPanel.actionControlFillColor, transition: ComponentTransition(transition))
|
||||
self.sendButtonBackgroundView.update(size: innerSize, cornerRadius: innerSize.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.actionControlFillColor), transition: ComponentTransition(transition))
|
||||
transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: innerSize))
|
||||
transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: innerSize))
|
||||
|
||||
|
|
@ -349,7 +349,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
|
|||
|
||||
transition.updateFrame(view: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(view: self.expandMediaInputButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: ComponentTransition(transition))
|
||||
self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition))
|
||||
if let image = self.expandMediaInputButtonIcon.image {
|
||||
let expandIconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)
|
||||
transition.updatePosition(layer: self.expandMediaInputButtonIcon.layer, position: expandIconFrame.center)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ final class AccessoryItemIconButton: HighlightTrackingButton, GlassBackgroundVie
|
|||
if let text {
|
||||
if self.textView == nil {
|
||||
let textView = ImmediateTextView()
|
||||
textView.isUserInteractionEnabled = false
|
||||
self.textView = textView
|
||||
self.addSubview(textView)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
public let textInputNodeClippingContainer: ASDisplayNode
|
||||
public let textInputSeparator: GlassBackgroundView.ContentColorView
|
||||
public var textInputNode: ChatInputTextNode?
|
||||
private var textInputNodeLayout: (frame: CGRect, insets: UIEdgeInsets)?
|
||||
public var dustNode: InvisibleInkDustNode?
|
||||
public var customEmojiContainerView: CustomEmojiContainerView?
|
||||
|
||||
|
|
@ -925,11 +926,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState: presentationInterfaceState, accessoryButtonsWidth: accessoryButtonsWidth)
|
||||
}
|
||||
|
||||
if !self.textInputContainer.bounds.size.width.isZero {
|
||||
let textInputFrame = self.textInputContainer.frame
|
||||
|
||||
textInputNode.frame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right), height: textInputFrame.size.height - self.textInputViewInternalInsets.top - self.textInputViewInternalInsets.bottom))
|
||||
textInputNode.updateLayout(size: textInputNode.bounds.size)
|
||||
if let textInputNodeLayout = self.textInputNodeLayout {
|
||||
textInputNode.textContainerInset = textInputNodeLayout.insets
|
||||
textInputNode.frame = textInputNodeLayout.frame
|
||||
textInputNode.updateLayout(size: textInputNodeLayout.frame.size)
|
||||
textInputNode.view.layoutIfNeeded()
|
||||
self.updateSpoiler()
|
||||
}
|
||||
|
|
@ -1865,7 +1865,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
let menuButtonFrame = CGRect(x: leftInset + 8.0, y: menuButtonOriginY, width: menuButtonExpanded ? menuButtonWidth : menuCollapsedButtonWidth, height: menuButtonHeight)
|
||||
transition.updateFrameAsPositionAndBounds(node: self.menuButton, frame: menuButtonFrame)
|
||||
transition.updateFrame(view: self.menuButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: menuButtonFrame.size))
|
||||
self.menuButtonBackgroundView.update(size: menuButtonFrame.size, cornerRadius: menuButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: interfaceState.theme.chat.inputPanel.actionControlFillColor, transition: ComponentTransition(transition))
|
||||
self.menuButtonBackgroundView.update(size: menuButtonFrame.size, cornerRadius: menuButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.actionControlFillColor), transition: ComponentTransition(transition))
|
||||
transition.updateFrame(node: self.menuButtonClippingNode, frame: CGRect(origin: CGPoint(x: 19.0, y: 0.0), size: CGSize(width: menuButtonWidth - 19.0, height: menuButtonFrame.height)))
|
||||
var menuButtonTitleTransition = transition
|
||||
if buttonTitleUpdated {
|
||||
|
|
@ -2334,7 +2334,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
transition.updateFrame(node: self.textInputContainer, frame: textInputContainerBackgroundFrame)
|
||||
transition.updateFrame(view: self.textInputContainerBackgroundView, frame: CGRect(origin: CGPoint(), size: textInputContainerBackgroundFrame.size))
|
||||
|
||||
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: ComponentTransition(transition))
|
||||
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition))
|
||||
|
||||
if let removedAccessoryPanelView {
|
||||
if let removedAccessoryPanelView = removedAccessoryPanelView as? ChatInputAccessoryPanelView {
|
||||
|
|
@ -2352,17 +2352,21 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
})
|
||||
}
|
||||
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top + textFieldTopContentOffset), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right), height: textInputHeight - self.textInputViewInternalInsets.top - textInputViewInternalInsets.bottom))
|
||||
let textInputNodeClippingContainerFrame = CGRect(origin: CGPoint(x: textFieldFrame.minX - self.textInputViewInternalInsets.left, y: textFieldFrame.minY - self.textInputViewInternalInsets.top), size: CGSize(width: textFieldFrame.width + self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right, height: textFieldFrame.height + self.textInputViewInternalInsets.top + self.textInputViewInternalInsets.bottom))
|
||||
let shouldUpdateLayout = textInputNodeClippingContainerFrame.size != self.textInputNodeClippingContainer.frame.size
|
||||
transition.updateFrame(node: self.textInputNodeClippingContainer, frame: textInputNodeClippingContainerFrame)
|
||||
|
||||
transition.updateFrame(view: self.textInputSeparator, frame: CGRect(origin: CGPoint(x: 15.0, y: textFieldTopContentOffset - UIScreenPixel), size: CGSize(width: textFieldFrame.width, height: UIScreenPixel)))
|
||||
self.textInputSeparator.backgroundColor = interfaceState.theme.chat.inputPanel.inputPlaceholderColor
|
||||
transition.updateAlpha(layer: self.textInputSeparator.layer, alpha: isTextFieldOverflow ? 1.0 : 0.0)
|
||||
|
||||
let actualTextFieldFrame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: textFieldFrame.size)
|
||||
self.textInputNodeLayout = (actualTextFieldFrame, textInputViewRealInsets)
|
||||
|
||||
if let textInputNode = self.textInputNode {
|
||||
textInputNode.textContainerInset = textInputViewRealInsets
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top + textFieldTopContentOffset), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right), height: textInputHeight - self.textInputViewInternalInsets.top - textInputViewInternalInsets.bottom))
|
||||
let shouldUpdateLayout = textFieldFrame.size != textInputNode.frame.size
|
||||
transition.updateFrame(node: self.textInputNodeClippingContainer, frame: CGRect(origin: CGPoint(x: textFieldFrame.minX - self.textInputViewInternalInsets.left, y: textFieldFrame.minY - self.textInputViewInternalInsets.top), size: CGSize(width: textFieldFrame.width + self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right, height: textFieldFrame.height + self.textInputViewInternalInsets.top + self.textInputViewInternalInsets.bottom)))
|
||||
|
||||
transition.updateFrame(view: self.textInputSeparator, frame: CGRect(origin: CGPoint(x: 15.0, y: textFieldTopContentOffset - UIScreenPixel), size: CGSize(width: textFieldFrame.width, height: UIScreenPixel)))
|
||||
self.textInputSeparator.backgroundColor = interfaceState.theme.chat.inputPanel.inputPlaceholderColor
|
||||
transition.updateAlpha(layer: self.textInputSeparator.layer, alpha: isTextFieldOverflow ? 1.0 : 0.0)
|
||||
|
||||
textInputNode.frame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: textFieldFrame.size)
|
||||
textInputNode.frame = actualTextFieldFrame
|
||||
textInputNode.updateLayout(size: textFieldFrame.size)
|
||||
self.updateInputField(textInputFrame: textFieldFrame, transition: ComponentTransition(transition))
|
||||
if shouldUpdateLayout {
|
||||
|
|
@ -2463,15 +2467,21 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
let textPlaceholderSize: CGSize
|
||||
let textPlaceholderMaxWidth: CGFloat = max(1.0, nextButtonTopRight.x - 12.0)
|
||||
|
||||
let placeholderColor: UIColor = interfaceState.theme.chat.inputPanel.inputPlaceholderColor
|
||||
if #available(iOS 26.0, *) {
|
||||
//placeholderColor = placeholderColor.withProminence(.tertiary)
|
||||
}
|
||||
//self.textPlaceholderNode.view.tintColor = .red
|
||||
|
||||
if (updatedPlaceholder != nil && self.currentPlaceholder != updatedPlaceholder) || themeUpdated {
|
||||
let currentPlaceholder = updatedPlaceholder ?? self.currentPlaceholder ?? ""
|
||||
self.currentPlaceholder = currentPlaceholder
|
||||
let baseFontSize = max(minInputFontSize, interfaceState.fontSize.baseDisplaySize)
|
||||
|
||||
let attributedPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: interfaceState.theme.chat.inputPanel.inputPlaceholderColor)
|
||||
let attributedPlaceholder = NSMutableAttributedString(string: currentPlaceholder, font: Font.regular(baseFontSize), textColor: placeholderColor)
|
||||
if placeholderHasStar, let range = attributedPlaceholder.string.range(of: "#") {
|
||||
attributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(interfaceState.theme)!, range: NSRange(range, in: attributedPlaceholder.string))
|
||||
attributedPlaceholder.addAttribute(.foregroundColor, value: interfaceState.theme.chat.inputPanel.inputPlaceholderColor, range: NSRange(range, in: attributedPlaceholder.string))
|
||||
attributedPlaceholder.addAttribute(.foregroundColor, value: placeholderColor, range: NSRange(range, in: attributedPlaceholder.string))
|
||||
attributedPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedPlaceholder.string))
|
||||
}
|
||||
|
||||
|
|
@ -2615,7 +2625,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
|
||||
let attachmentButtonFrame = CGRect(origin: CGPoint(x: attachmentButtonX, y: textInputFrame.maxY - 40.0), size: CGSize(width: 40.0, height: 40.0))
|
||||
self.attachmentButtonBackground.frame = CGRect(origin: CGPoint(), size: attachmentButtonFrame.size)
|
||||
self.attachmentButtonBackground.update(size: attachmentButtonFrame.size, cornerRadius: attachmentButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: isEditingMedia ? interfaceState.theme.chat.inputPanel.actionControlFillColor : interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: ComponentTransition(transition))
|
||||
self.attachmentButtonBackground.update(size: attachmentButtonFrame.size, cornerRadius: attachmentButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: isEditingMedia ? .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.actionControlFillColor) : .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: ComponentTransition(transition))
|
||||
|
||||
transition.updateFrame(layer: self.attachmentButton.layer, frame: attachmentButtonFrame)
|
||||
transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButton.frame)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import ObjCRuntimeUtils
|
|||
private let innerCircleDiameter: CGFloat = 110.0
|
||||
private let outerCircleDiameter = innerCircleDiameter + 50.0
|
||||
private let outerCircleMinScale = innerCircleDiameter / outerCircleDiameter
|
||||
private let innerCircleImage = generateFilledCircleImage(diameter: innerCircleDiameter, color: UIColor(rgb: 0x007aff))
|
||||
private let outerCircleImage = generateFilledCircleImage(diameter: outerCircleDiameter, color: UIColor(rgb: 0x007aff, alpha: 0.2))
|
||||
private let innerCircleImage = generateFilledCircleImage(diameter: innerCircleDiameter, color: UIColor(rgb: 0x0088ff))
|
||||
private let outerCircleImage = generateFilledCircleImage(diameter: outerCircleDiameter, color: UIColor(rgb: 0x0088ff, alpha: 0.2))
|
||||
private let micIcon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: .white)!
|
||||
|
||||
private final class ChatTextInputAudioRecordingOverlayDisplayLinkTarget: NSObject {
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ private class WrapperBlurrredBackgroundView: UIView, TGModernConversationInputMi
|
|||
|
||||
let view = GlassBackgroundView()
|
||||
view.frame = CGRect(origin: CGPoint(), size: size)
|
||||
view.update(size: size, cornerRadius: min(size.width, size.height) * 0.5, isDark: self.isDark, tintColor: self.glassTintColor, transition: .immediate)
|
||||
view.update(size: size, cornerRadius: min(size.width, size.height) * 0.5, isDark: self.isDark, tintColor: .init(kind: .panel, color: self.glassTintColor), transition: .immediate)
|
||||
self.view = view
|
||||
|
||||
super.init(frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
|
@ -652,13 +652,13 @@ private class WrapperBlurrredBackgroundView: UIView, TGModernConversationInputMi
|
|||
} set {
|
||||
super.frame = newValue
|
||||
self.view.frame = CGRect(origin: CGPoint(), size: newValue.size)
|
||||
self.view.update(size: newValue.size, cornerRadius: min(newValue.width, newValue.height) * 0.5, isDark: self.isDark, tintColor: self.glassTintColor, transition: .immediate)
|
||||
self.view.update(size: newValue.size, cornerRadius: min(newValue.width, newValue.height) * 0.5, isDark: self.isDark, tintColor: .init(kind: .panel, color: self.glassTintColor), transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func update(_ size: CGSize) {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateFrame(view: self.view, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.view.update(size: size, cornerRadius: min(size.width, size.height) * 0.5, isDark: self.isDark, tintColor: self.glassTintColor, transition: ComponentTransition(transition))
|
||||
self.view.update(size: size, cornerRadius: min(size.width, size.height) * 0.5, isDark: self.isDark, tintColor: .init(kind: .panel, color: self.glassTintColor), transition: ComponentTransition(transition))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
submodules/TelegramUI/Components/EdgeEffect/BUILD
Normal file
19
submodules/TelegramUI/Components/EdgeEffect/BUILD
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "EdgeEffect",
|
||||
module_name = "EdgeEffect",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
|
||||
public final class EdgeEffectView: UIView {
|
||||
public enum Edge {
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
private let contentView: UIView
|
||||
private let contentMaskView: UIImageView
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.contentView = UIView()
|
||||
self.contentMaskView = UIImageView()
|
||||
self.contentView.mask = self.contentMaskView
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func update(content: UIColor, rect: CGRect, edge: Edge, edgeSize: CGFloat, containerSize: CGSize, transition: ComponentTransition) {
|
||||
self.contentView.backgroundColor = content
|
||||
|
||||
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: rect.size))
|
||||
transition.setFrame(view: self.contentMaskView, frame: CGRect(origin: CGPoint(), size: rect.size))
|
||||
|
||||
if self.contentMaskView.image?.size.height != edgeSize {
|
||||
let baseGradientAlpha: CGFloat = 0.65
|
||||
let numSteps = 8
|
||||
let firstStep = 1
|
||||
let firstLocation = 0.0
|
||||
let colors: [UIColor] = (0 ..< numSteps).map { i in
|
||||
if i < firstStep {
|
||||
return UIColor(white: 1.0, alpha: 1.0)
|
||||
} else {
|
||||
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
||||
let value: CGFloat = bezierPoint(0.42, 0.0, 0.58, 1.0, step)
|
||||
return UIColor(white: 1.0, alpha: baseGradientAlpha * value)
|
||||
}
|
||||
}
|
||||
let locations: [CGFloat] = (0 ..< numSteps).map { i in
|
||||
if i < firstStep {
|
||||
return 0.0
|
||||
} else {
|
||||
let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1)
|
||||
return (firstLocation + (1.0 - firstLocation) * step)
|
||||
}
|
||||
}
|
||||
|
||||
if edgeSize > 0.0 {
|
||||
self.contentMaskView.image = generateGradientImage(
|
||||
size: CGSize(width: 8.0, height: edgeSize),
|
||||
colors: colors,
|
||||
locations: locations
|
||||
)?.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(edgeSize))
|
||||
} else {
|
||||
self.contentMaskView.image = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1050,7 +1050,7 @@ private let tonImage: UIImage? = {
|
|||
generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
|
||||
if let image = generateTintedImage(image: UIImage(bundleImageName: "Ads/TonBig"), color: UIColor(rgb: 0x007aff)), let cgImage = image.cgImage {
|
||||
if let image = generateTintedImage(image: UIImage(bundleImageName: "Ads/TonBig"), color: UIColor(rgb: 0x0088ff)), let cgImage = image.cgImage {
|
||||
context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 4.0), byTiling: false)
|
||||
}
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
|
|
|
|||
|
|
@ -313,12 +313,27 @@ public final class GlassBackgroundView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
public struct TintColor: Equatable {
|
||||
public enum Kind {
|
||||
case panel
|
||||
case custom
|
||||
}
|
||||
|
||||
public let kind: Kind
|
||||
public let color: UIColor
|
||||
|
||||
public init(kind: Kind, color: UIColor) {
|
||||
self.kind = kind
|
||||
self.color = color
|
||||
}
|
||||
}
|
||||
|
||||
private struct Params: Equatable {
|
||||
let cornerRadius: CGFloat
|
||||
let isDark: Bool
|
||||
let tintColor: UIColor
|
||||
let tintColor: TintColor
|
||||
|
||||
init(cornerRadius: CGFloat, isDark: Bool, tintColor: UIColor) {
|
||||
init(cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor) {
|
||||
self.cornerRadius = cornerRadius
|
||||
self.isDark = isDark
|
||||
self.tintColor = tintColor
|
||||
|
|
@ -329,12 +344,18 @@ public final class GlassBackgroundView: UIView {
|
|||
private let nativeView: UIVisualEffectView?
|
||||
|
||||
private let foregroundView: UIImageView?
|
||||
private let shadowView: UIImageView?
|
||||
private let shadowMaskView: UIImageView?
|
||||
|
||||
public let maskContentView: UIView
|
||||
private let contentContainer: ContentContainer
|
||||
|
||||
public var contentView: UIView {
|
||||
return self.contentContainer
|
||||
if let nativeView = self.nativeView {
|
||||
return nativeView.contentView
|
||||
} else {
|
||||
return self.contentContainer
|
||||
}
|
||||
}
|
||||
|
||||
private var params: Params?
|
||||
|
|
@ -350,10 +371,14 @@ public final class GlassBackgroundView: UIView {
|
|||
nativeView.traitOverrides.userInterfaceStyle = .light
|
||||
//self.foregroundView = UIImageView()
|
||||
self.foregroundView = nil
|
||||
self.shadowView = nil
|
||||
self.shadowMaskView = nil
|
||||
} else {
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 5.0)
|
||||
self.nativeView = nil
|
||||
self.foregroundView = UIImageView()
|
||||
self.shadowView = UIImageView()
|
||||
self.shadowMaskView = UIImageView()
|
||||
}
|
||||
|
||||
self.maskContentView = UIView()
|
||||
|
|
@ -366,6 +391,12 @@ public final class GlassBackgroundView: UIView {
|
|||
|
||||
super.init(frame: frame)
|
||||
|
||||
if let shadowView = self.shadowView {
|
||||
self.addSubview(shadowView)
|
||||
if let shadowMaskView = self.shadowMaskView {
|
||||
shadowView.mask = shadowMaskView
|
||||
}
|
||||
}
|
||||
if let nativeView = self.nativeView {
|
||||
self.addSubview(nativeView)
|
||||
}
|
||||
|
|
@ -383,7 +414,7 @@ public final class GlassBackgroundView: UIView {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: UIColor, transition: ComponentTransition) {
|
||||
public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, transition: ComponentTransition) {
|
||||
if let nativeView = self.nativeView {
|
||||
let previousFrame = nativeView.frame
|
||||
|
||||
|
|
@ -399,7 +430,7 @@ public final class GlassBackgroundView: UIView {
|
|||
}
|
||||
}
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.alpha != 1.0, transition: transition.containedViewLayoutTransition)
|
||||
backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition)
|
||||
backgroundNode.update(size: size, cornerRadius: cornerRadius, transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
|
@ -408,13 +439,38 @@ public final class GlassBackgroundView: UIView {
|
|||
if self.params != params {
|
||||
self.params = params
|
||||
|
||||
if let shadowView = self.shadowView {
|
||||
shadowView.layer.shadowRadius = 10.0
|
||||
shadowView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
|
||||
shadowView.layer.shadowOpacity = 0.08
|
||||
shadowView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
|
||||
if let shadowMaskView = self.shadowMaskView {
|
||||
let shadowInset: CGFloat = 32.0
|
||||
let shadowInnerInset: CGFloat = 0.5
|
||||
shadowMaskView.image = generateImage(CGSize(width: shadowInset * 2.0 + cornerRadius * 2.0, height: shadowInset * 2.0 + cornerRadius * 2.0), rotatedContext: { size, context in
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.setBlendMode(.copy)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0)))
|
||||
})?.stretchableImage(withLeftCapWidth: Int(shadowInset + cornerRadius), topCapHeight: Int(shadowInset + cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
if let foregroundView = self.foregroundView {
|
||||
foregroundView.image = generateForegroundImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), isDark: isDark, fillColor: tintColor)
|
||||
foregroundView.image = generateForegroundImage(size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0), isDark: isDark, fillColor: tintColor.color)
|
||||
} else {
|
||||
if let nativeView {
|
||||
if #available(iOS 26.0, *) {
|
||||
let glassEffect = UIGlassEffect(style: .regular)
|
||||
//glassEffect.tintColor = tintColor.withMultipliedAlpha(0.1)
|
||||
switch tintColor.kind {
|
||||
case .panel:
|
||||
glassEffect.tintColor = nil
|
||||
case .custom:
|
||||
glassEffect.tintColor = tintColor.color
|
||||
}
|
||||
glassEffect.isInteractive = false
|
||||
|
||||
nativeView.effect = glassEffect
|
||||
|
|
@ -424,9 +480,19 @@ public final class GlassBackgroundView: UIView {
|
|||
}
|
||||
|
||||
transition.setFrame(view: self.maskContentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
if let foregroundView {
|
||||
if let foregroundView = self.foregroundView {
|
||||
transition.setFrame(view: foregroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
if let shadowView = self.shadowView {
|
||||
if shadowView.bounds.size != size {
|
||||
shadowView.layer.shadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath
|
||||
}
|
||||
transition.setFrame(view: shadowView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
if let shadowMaskView = self.shadowMaskView {
|
||||
transition.setFrame(view: shadowMaskView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -32.0, dy: -32.0))
|
||||
}
|
||||
}
|
||||
transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -888,7 +888,7 @@ final class MediaEditorScreenComponent: Component {
|
|||
transition: transition,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(DoneButtonContentComponent(
|
||||
backgroundColor: UIColor(rgb: 0x007aff),
|
||||
backgroundColor: UIColor(rgb: 0x0088ff),
|
||||
icon: doneButtonIcon,
|
||||
title: doneButtonTitle)),
|
||||
effectAlignment: .center,
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ public final class MediaScrubberComponent: Component {
|
|||
self.coverDotWrapper.isUserInteractionEnabled = false
|
||||
self.coverDotWrapper.isHidden = true
|
||||
|
||||
self.coverDotView = UIImageView(image: generateFilledCircleImage(diameter: 7.0, color: UIColor(rgb: 0x007aff)))
|
||||
self.coverDotView = UIImageView(image: generateFilledCircleImage(diameter: 7.0, color: UIColor(rgb: 0x0088ff)))
|
||||
|
||||
self.coverImageView = UIImageView()
|
||||
self.coverImageView.clipsToBounds = true
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ public final class HashtagListItemComponent: Component {
|
|||
}
|
||||
|
||||
if themeUpdated {
|
||||
let accentColor = UIColor(rgb: 0x007aff)
|
||||
let accentColor = UIColor(rgb: 0x0088ff)
|
||||
self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||
self.iconBackgroundLayer.backgroundColor = accentColor.cgColor
|
||||
self.iconLayer.layerTintColor = UIColor.white.cgColor
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/BottomButtonPanelComponent",
|
||||
"//submodules/TelegramUI/Components/MarqueeComponent",
|
||||
"//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ import UrlHandling
|
|||
import VerifyAlertController
|
||||
import GiftViewScreen
|
||||
import PeerMessagesMediaPlaylist
|
||||
import EdgeEffect
|
||||
|
||||
public enum PeerInfoAvatarEditingMode {
|
||||
case generic
|
||||
|
|
@ -2927,6 +2928,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
fileprivate let cachedDataPromise = Promise<CachedPeerData?>()
|
||||
|
||||
let scrollNode: ASScrollNode
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
|
||||
let headerNode: PeerInfoHeaderNode
|
||||
private var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
|
||||
|
|
@ -3063,6 +3065,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.canCancelAllTouchesInViews = true
|
||||
|
||||
self.edgeEffectView = EdgeEffectView()
|
||||
|
||||
var forumTopicThreadId: Int64?
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
forumTopicThreadId = message.threadId
|
||||
|
|
@ -3930,6 +3934,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.paneContainerNode)
|
||||
|
||||
self.view.addSubview(self.edgeEffectView)
|
||||
|
||||
self.addSubnode(self.headerNode)
|
||||
self.scrollNode.view.isScrollEnabled = !self.isMediaOnly
|
||||
|
||||
|
|
@ -12296,6 +12302,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
if self.isSettings {
|
||||
let edgeEffectHeight: CGFloat = layout.intrinsicInsets.bottom
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
|
||||
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: self.presentationData.theme.list.blocksBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, containerSize: layout.size, transition: ComponentTransition(transition))
|
||||
}
|
||||
|
||||
let sectionSpacing: CGFloat = 24.0
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import MediaResources
|
|||
import WallpaperGalleryScreen
|
||||
import GenerateThemeName
|
||||
|
||||
private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
|
||||
private let randomBackgroundColors: [Int32] = [0x0088ff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
|
||||
|
||||
public extension TelegramThemeSettings {
|
||||
convenience init(baseTheme: TelegramBaseTheme, accentColor: UIColor, outgoingAccentColor: UIColor?, messageColors: [UInt32], animateMessageColors: Bool, wallpaper: TelegramWallpaper?) {
|
||||
|
|
|
|||
|
|
@ -1521,7 +1521,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
)),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1,
|
||||
highlightColor: UIColor(rgb: 0x007aff, alpha: 0.2),
|
||||
highlightColor: UIColor(rgb: 0x0088ff, alpha: 0.2),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
|
||||
return NSAttributedString.Key(rawValue: "URL")
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ final class DataUsageScreenComponent: Component {
|
|||
case .photos:
|
||||
return UIColor(rgb: 0x5AC8FA)
|
||||
case .videos:
|
||||
return UIColor(rgb: 0x007AFF)
|
||||
return UIColor(rgb: 0x0088ff)
|
||||
case .files:
|
||||
return UIColor(rgb: 0x34C759)
|
||||
case .music:
|
||||
|
|
|
|||
27
submodules/TelegramUI/Components/TabBarComponent/BUILD
Normal file
27
submodules/TelegramUI/Components/TabBarComponent/BUILD
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "TabBarComponent",
|
||||
module_name = "TabBarComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/TextBadgeComponent",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,623 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import GlassBackgroundComponent
|
||||
import MultilineTextComponent
|
||||
import LottieComponent
|
||||
import UIKitRuntimeUtils
|
||||
import BundleIconComponent
|
||||
import TextBadgeComponent
|
||||
|
||||
public final class TabBarComponent: Component {
|
||||
public final class Item: Equatable {
|
||||
public let item: UITabBarItem
|
||||
public let action: (Bool) -> Void
|
||||
|
||||
fileprivate var id: AnyHashable {
|
||||
return AnyHashable(ObjectIdentifier(self.item))
|
||||
}
|
||||
|
||||
public init(item: UITabBarItem, action: @escaping (Bool) -> Void) {
|
||||
self.item = item
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if lhs.item !== rhs.item {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let items: [Item]
|
||||
public let selectedId: AnyHashable?
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
items: [Item],
|
||||
selectedId: AnyHashable?
|
||||
) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
self.selectedId = selectedId
|
||||
}
|
||||
|
||||
public static func ==(lhs: TabBarComponent, rhs: TabBarComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.selectedId != rhs.selectedId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView, UITabBarDelegate, UIGestureRecognizerDelegate {
|
||||
private let backgroundView: GlassBackgroundView
|
||||
private let selectionView: GlassBackgroundView.ContentImageView
|
||||
private let nativeTabBar: UITabBar?
|
||||
|
||||
private var itemViews: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
private var selectedItemViews: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var component: TabBarComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.backgroundView = GlassBackgroundView(frame: CGRect())
|
||||
self.selectionView = GlassBackgroundView.ContentImageView()
|
||||
|
||||
if #available(iOS 26.0, *) {
|
||||
self.nativeTabBar = UITabBar()
|
||||
} else {
|
||||
self.nativeTabBar = nil
|
||||
}
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
self.addSubview(nativeTabBar)
|
||||
nativeTabBar.delegate = self
|
||||
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.onLongPressGesture(_:)))
|
||||
longPressGesture.delegate = self
|
||||
self.addGestureRecognizer(longPressGesture)
|
||||
} else {
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
if let index = tabBar.items?.firstIndex(where: { $0 === item }) {
|
||||
if index < component.items.count {
|
||||
component.items[index].action(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@objc private func onLongPressGesture(_ recognizer: UILongPressGestureRecognizer) {
|
||||
if case .began = recognizer.state {
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
func cancelGestures(view: UIView) {
|
||||
for recognizer in view.gestureRecognizers ?? [] {
|
||||
if NSStringFromClass(type(of: recognizer)).contains("sSelectionGestureRecognizer") {
|
||||
recognizer.state = .cancelled
|
||||
}
|
||||
}
|
||||
for subview in view.subviews {
|
||||
cancelGestures(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
cancelGestures(view: nativeTabBar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
if case .ended = recognizer.state {
|
||||
let point = recognizer.location(in: self)
|
||||
var closestItemView: (AnyHashable, CGFloat)?
|
||||
for (id, itemView) in self.itemViews {
|
||||
guard let itemView = itemView.view else {
|
||||
continue
|
||||
}
|
||||
let distance = abs(point.x - itemView.center.x)
|
||||
if let previousClosestItemView = closestItemView {
|
||||
if previousClosestItemView.1 > distance {
|
||||
closestItemView = (id, distance)
|
||||
}
|
||||
} else {
|
||||
closestItemView = (id, distance)
|
||||
}
|
||||
}
|
||||
|
||||
if let (id, _) = closestItemView {
|
||||
guard let item = component.items.first(where: { $0.id == id }) else {
|
||||
return
|
||||
}
|
||||
item.action(false)
|
||||
/*if previousSelectedIndex != closestNode.0 {
|
||||
if let selectedIndex = self.selectedIndex, let _ = self.tabBarItems[selectedIndex].item.animationName {
|
||||
container.imageNode.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
func update(component: TabBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let innerInset: CGFloat = 3.0
|
||||
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
if nativeTabBar.items?.count != component.items.count {
|
||||
nativeTabBar.items = (0 ..< component.items.count).map { i in
|
||||
return UITabBarItem(title: " ", image: nil, tag: i)
|
||||
}
|
||||
for (_, itemView) in self.itemViews {
|
||||
itemView.view?.removeFromSuperview()
|
||||
}
|
||||
for (_, selectedItemView) in self.selectedItemViews {
|
||||
selectedItemView.view?.removeFromSuperview()
|
||||
}
|
||||
if let index = component.items.firstIndex(where: { $0.id == component.selectedId }) {
|
||||
nativeTabBar.selectedItem = nativeTabBar.items?[index]
|
||||
}
|
||||
}
|
||||
|
||||
let nativeSize = nativeTabBar.sizeThatFits(availableSize)
|
||||
nativeTabBar.bounds = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: nativeSize.height))
|
||||
nativeTabBar.layoutSubviews()
|
||||
}
|
||||
|
||||
var nativeItemContainers: [Int: UIView] = [:]
|
||||
var nativeSelectedItemContainers: [Int: UIView] = [:]
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
for subview in nativeTabBar.subviews {
|
||||
if NSStringFromClass(type(of: subview)).contains("PlatterView") {
|
||||
for subview in subview.subviews {
|
||||
if NSStringFromClass(type(of: subview)).hasSuffix("SelectedContentView") {
|
||||
for subview in subview.subviews {
|
||||
if NSStringFromClass(type(of: subview)).hasSuffix("TabButton") {
|
||||
nativeSelectedItemContainers[nativeSelectedItemContainers.count] = subview
|
||||
}
|
||||
}
|
||||
} else if NSStringFromClass(type(of: subview)).hasSuffix("ContentView") {
|
||||
for subview in subview.subviews {
|
||||
if NSStringFromClass(type(of: subview)).hasSuffix("TabButton") {
|
||||
nativeItemContainers[nativeItemContainers.count] = subview
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var itemSize = CGSize(width: floor((availableSize.width - innerInset * 2.0) / CGFloat(component.items.count)), height: 56.0)
|
||||
itemSize.width = min(94.0, itemSize.width)
|
||||
|
||||
if let itemContainer = nativeItemContainers[0] {
|
||||
itemSize = itemContainer.bounds.size
|
||||
}
|
||||
|
||||
let contentHeight = itemSize.height + innerInset * 2.0
|
||||
var contentWidth: CGFloat = innerInset
|
||||
|
||||
if self.selectionView.image?.size.height != itemSize.height {
|
||||
self.selectionView.image = generateStretchableFilledCircleImage(radius: itemSize.height * 0.5, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
self.selectionView.tintColor = component.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05)
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var selectionFrame: CGRect?
|
||||
for index in 0 ..< component.items.count {
|
||||
let item = component.items[index]
|
||||
validIds.append(item.id)
|
||||
|
||||
let itemView: ComponentView<Empty>
|
||||
var itemTransition = transition
|
||||
|
||||
if let current = self.itemViews[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = itemTransition.withAnimation(.none)
|
||||
itemView = ComponentView()
|
||||
self.itemViews[item.id] = itemView
|
||||
}
|
||||
|
||||
let selectedItemView: ComponentView<Empty>
|
||||
if let current = self.selectedItemViews[item.id] {
|
||||
selectedItemView = current
|
||||
} else {
|
||||
selectedItemView = ComponentView()
|
||||
self.selectedItemViews[item.id] = selectedItemView
|
||||
}
|
||||
|
||||
let isItemSelected = component.selectedId == item.id
|
||||
|
||||
let _ = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(ItemComponent(
|
||||
item: item,
|
||||
theme: component.theme,
|
||||
isSelected: self.nativeTabBar == nil ? isItemSelected : false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: itemSize
|
||||
)
|
||||
let _ = selectedItemView.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(ItemComponent(
|
||||
item: item,
|
||||
theme: component.theme,
|
||||
isSelected: true
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: itemSize
|
||||
)
|
||||
|
||||
let itemFrame = CGRect(origin: CGPoint(x: contentWidth, y: floor((contentHeight - itemSize.height) * 0.5)), size: itemSize)
|
||||
if let itemComponentView = itemView.view as? ItemComponent.View, let selectedItemComponentView = selectedItemView.view as? ItemComponent.View {
|
||||
if itemComponentView.superview == nil {
|
||||
itemComponentView.isUserInteractionEnabled = false
|
||||
selectedItemComponentView.isUserInteractionEnabled = false
|
||||
|
||||
if self.nativeTabBar != nil {
|
||||
if let itemContainer = nativeItemContainers[index] {
|
||||
itemContainer.addSubview(itemComponentView)
|
||||
}
|
||||
if let itemContainer = nativeSelectedItemContainers[index] {
|
||||
itemContainer.addSubview(selectedItemComponentView)
|
||||
}
|
||||
} else {
|
||||
self.addSubview(itemComponentView)
|
||||
}
|
||||
}
|
||||
if self.nativeTabBar != nil {
|
||||
if let parentView = itemComponentView.superview {
|
||||
let itemFrame = CGRect(origin: CGPoint(x: floor((parentView.bounds.width - itemSize.width) * 0.5), y: floor((parentView.bounds.height - itemSize.height) * 0.5)), size: itemSize)
|
||||
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
itemTransition.setFrame(view: selectedItemComponentView, frame: itemFrame)
|
||||
}
|
||||
} else {
|
||||
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
}
|
||||
|
||||
if let previousComponent, previousComponent.selectedId != item.id, isItemSelected {
|
||||
itemComponentView.playSelectionAnimation()
|
||||
selectedItemComponentView.playSelectionAnimation()
|
||||
}
|
||||
}
|
||||
if isItemSelected {
|
||||
selectionFrame = itemFrame
|
||||
}
|
||||
|
||||
contentWidth += itemFrame.width
|
||||
}
|
||||
contentWidth += innerInset
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
itemView.view?.removeFromSuperview()
|
||||
self.selectedItemViews[id]?.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
self.selectedItemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if let selectionFrame, self.nativeTabBar == nil {
|
||||
var selectionViewTransition = transition
|
||||
if self.selectionView.superview == nil {
|
||||
selectionViewTransition = selectionViewTransition.withAnimation(.none)
|
||||
self.backgroundView.contentView.addSubview(self.selectionView)
|
||||
}
|
||||
selectionViewTransition.setFrame(view: self.selectionView, frame: selectionFrame)
|
||||
} else if self.selectionView.superview != nil {
|
||||
self.selectionView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let size = CGSize(width: min(availableSize.width, contentWidth), height: contentHeight)
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: component.theme.list.plainBackgroundColor.withMultipliedAlpha(0.75)), transition: transition)
|
||||
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
transition.setFrame(view: nativeTabBar, frame: CGRect(origin: CGPoint(x: floor((size.width - nativeTabBar.bounds.width) * 0.5), y: 0.0), size: nativeTabBar.bounds.size))
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ItemComponent: Component {
|
||||
let item: TabBarComponent.Item
|
||||
let theme: PresentationTheme
|
||||
let isSelected: Bool
|
||||
|
||||
init(item: TabBarComponent.Item, theme: PresentationTheme, isSelected: Bool) {
|
||||
self.item = item
|
||||
self.theme = theme
|
||||
self.isSelected = isSelected
|
||||
}
|
||||
|
||||
static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool {
|
||||
if lhs.item != rhs.item {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.isSelected != rhs.isSelected {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var imageIcon: ComponentView<Empty>?
|
||||
private var animationIcon: ComponentView<Empty>?
|
||||
private let title = ComponentView<Empty>()
|
||||
private var badge: ComponentView<Empty>?
|
||||
|
||||
private var component: ItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
private var setImageListener: Int?
|
||||
private var setSelectedImageListener: Int?
|
||||
private var setBadgeListener: Int?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let component = self.component {
|
||||
if let setImageListener = self.setImageListener {
|
||||
component.item.item.removeSetImageListener(setImageListener)
|
||||
}
|
||||
if let setSelectedImageListener = self.setSelectedImageListener {
|
||||
component.item.item.removeSetSelectedImageListener(setSelectedImageListener)
|
||||
}
|
||||
if let setBadgeListener = self.setBadgeListener {
|
||||
component.item.item.removeSetBadgeListener(setBadgeListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func playSelectionAnimation() {
|
||||
if let animationIconView = self.animationIcon?.view as? LottieComponent.View {
|
||||
animationIconView.playOnce()
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
|
||||
if previousComponent?.item.item !== component.item.item {
|
||||
if let setImageListener = self.setImageListener {
|
||||
self.component?.item.item.removeSetImageListener(setImageListener)
|
||||
}
|
||||
if let setSelectedImageListener = self.setSelectedImageListener {
|
||||
self.component?.item.item.removeSetSelectedImageListener(setSelectedImageListener)
|
||||
}
|
||||
if let setBadgeListener = self.setBadgeListener {
|
||||
self.component?.item.item.removeSetBadgeListener(setBadgeListener)
|
||||
}
|
||||
self.setImageListener = component.item.item.addSetImageListener { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
self.setSelectedImageListener = component.item.item.addSetSelectedImageListener { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
self.setBadgeListener = UITabBarItem_addSetBadgeListener(component.item.item) { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
if let animationName = component.item.item.animationName {
|
||||
if let imageIcon = self.imageIcon {
|
||||
self.imageIcon = nil
|
||||
imageIcon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let animationIcon: ComponentView<Empty>
|
||||
var iconTransition = transition
|
||||
if let current = self.animationIcon {
|
||||
animationIcon = current
|
||||
} else {
|
||||
iconTransition = iconTransition.withAnimation(.none)
|
||||
animationIcon = ComponentView()
|
||||
self.animationIcon = animationIcon
|
||||
}
|
||||
|
||||
let iconSize = animationIcon.update(
|
||||
transition: iconTransition,
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(
|
||||
name: animationName
|
||||
),
|
||||
color: component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor,
|
||||
placeholderColor: nil,
|
||||
startingPosition: .end,
|
||||
size: CGSize(width: 48.0, height: 48.0),
|
||||
loop: false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 48.0, height: 48.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: -4.0), size: iconSize).offsetBy(dx: component.item.item.animationOffset.x, dy: component.item.item.animationOffset.y)
|
||||
if let animationIconView = animationIcon.view {
|
||||
if animationIconView.superview == nil {
|
||||
if let badgeView = self.badge?.view {
|
||||
self.insertSubview(animationIconView, belowSubview: badgeView)
|
||||
} else {
|
||||
self.addSubview(animationIconView)
|
||||
}
|
||||
}
|
||||
iconTransition.setFrame(view: animationIconView, frame: iconFrame)
|
||||
}
|
||||
} else {
|
||||
if let animationIcon = self.animationIcon {
|
||||
self.animationIcon = nil
|
||||
animationIcon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let imageIcon: ComponentView<Empty>
|
||||
var iconTransition = transition
|
||||
if let current = self.imageIcon {
|
||||
imageIcon = current
|
||||
} else {
|
||||
iconTransition = iconTransition.withAnimation(.none)
|
||||
imageIcon = ComponentView()
|
||||
self.imageIcon = imageIcon
|
||||
}
|
||||
|
||||
let iconSize = imageIcon.update(
|
||||
transition: iconTransition,
|
||||
component: AnyComponent(Image(
|
||||
image: component.isSelected ? component.item.item.selectedImage : component.item.item.image,
|
||||
tintColor: nil,
|
||||
contentMode: .center
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: 3.0), size: iconSize)
|
||||
if let imageIconView = imageIcon.view {
|
||||
if imageIconView.superview == nil {
|
||||
if let badgeView = self.badge?.view {
|
||||
self.insertSubview(imageIconView, belowSubview: badgeView)
|
||||
} else {
|
||||
self.addSubview(imageIconView)
|
||||
}
|
||||
}
|
||||
iconTransition.setFrame(view: imageIconView, frame: iconFrame)
|
||||
}
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.item.item.title ?? " ", font: Font.semibold(10.0), textColor: component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 100.0)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: availableSize.height - 9.0 - titleSize.height), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
if let badgeText = component.item.item.badgeValue, !badgeText.isEmpty {
|
||||
let badge: ComponentView<Empty>
|
||||
var badgeTransition = transition
|
||||
if let current = self.badge {
|
||||
badge = current
|
||||
} else {
|
||||
badgeTransition = badgeTransition.withAnimation(.none)
|
||||
badge = ComponentView()
|
||||
self.badge = badge
|
||||
}
|
||||
let badgeSize = badge.update(
|
||||
transition: badgeTransition,
|
||||
component: AnyComponent(TextBadgeComponent(
|
||||
text: badgeText,
|
||||
font: Font.regular(13.0),
|
||||
background: component.theme.rootController.tabBar.badgeBackgroundColor,
|
||||
foreground: component.theme.rootController.tabBar.badgeTextColor,
|
||||
insets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 1.0, right: 6.0)
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
let contentWidth: CGFloat = 25.0
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: floor(availableSize.width / 2.0) + contentWidth - badgeSize.width - 5.0, y: -1.0), size: badgeSize)
|
||||
if let badgeView = badge.view {
|
||||
if badgeView.superview == nil {
|
||||
self.addSubview(badgeView)
|
||||
}
|
||||
badgeTransition.setFrame(view: badgeView, frame: badgeFrame)
|
||||
}
|
||||
} else if let badge = self.badge {
|
||||
self.badge = nil
|
||||
badge.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
@ -1232,24 +1232,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||
}
|
||||
})
|
||||
}
|
||||
self.mainWindow.forEachViewController({ controller in
|
||||
if let controller = controller as? TabBarAccountSwitchController {
|
||||
if let rootController = self.mainWindow.viewController as? TelegramRootController {
|
||||
if let tabsController = rootController.viewControllers.first as? TabBarController {
|
||||
for i in 0 ..< tabsController.controllers.count {
|
||||
if let _ = tabsController.controllers[i] as? (SettingsController & ViewController) {
|
||||
let sourceNodes = tabsController.sourceNodesForController(at: i)
|
||||
if let sourceNodes = sourceNodes {
|
||||
controller.dismiss(sourceNodes: sourceNodes)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
self.mainWindow.topLevelOverlayControllers = [context.sharedApplicationContext.overlayMediaController, context.notificationController]
|
||||
(context.context.sharedContext as? SharedAccountContextImpl)?.notificationController = context.notificationController
|
||||
var authorizeNotifications = true
|
||||
|
|
|
|||
|
|
@ -916,7 +916,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||
}
|
||||
|
||||
self.dynamicBounceEnabled = !self.currentPresentationData.disableAnimations
|
||||
self.experimentalSnapScrollToItem = true
|
||||
self.experimentalSnapScrollToItem = false
|
||||
|
||||
//self.debugInfo = true
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
|||
|
||||
self.buttonNode.view.addSubview(self.backgroundView)
|
||||
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: .immediate)
|
||||
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate)
|
||||
self.imageView.tintColor = theme.chat.inputPanel.inputControlColor
|
||||
|
||||
self.backgroundView.contentView.addSubview(self.imageView)
|
||||
|
|
@ -110,7 +110,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
|||
if self.theme !== theme {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundView.update(size: self.backgroundView.bounds.size, cornerRadius: self.backgroundView.bounds.size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65), transition: .immediate)
|
||||
self.backgroundView.update(size: self.backgroundView.bounds.size, cornerRadius: self.backgroundView.bounds.size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.65)), transition: .immediate)
|
||||
self.imageView.tintColor = theme.chat.inputPanel.inputControlColor
|
||||
|
||||
switch self.type {
|
||||
|
|
@ -124,7 +124,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
|||
self.imageView.image = PresentationResourcesChat.chatHistoryReactionsButtonImage(theme)
|
||||
}
|
||||
|
||||
self.badgeBackgroundView.update(size: self.badgeBackgroundView.bounds.size, cornerRadius: self.badgeBackgroundView.bounds.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: theme.chat.inputPanel.actionControlFillColor, transition: .immediate)
|
||||
self.badgeBackgroundView.update(size: self.badgeBackgroundView.bounds.size, cornerRadius: self.badgeBackgroundView.bounds.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: theme.chat.inputPanel.actionControlFillColor), transition: .immediate)
|
||||
|
||||
var segments: [AnimatedCountLabelNode.Segment] = []
|
||||
if let value = Int(self.badge) {
|
||||
|
|
@ -174,7 +174,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
|||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
self.badgeBackgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: self.theme.chat.inputPanel.actionControlFillColor, transition: ComponentTransition(transition))
|
||||
self.badgeBackgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: self.theme.chat.inputPanel.actionControlFillColor), transition: ComponentTransition(transition))
|
||||
|
||||
self.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.width - badgeSize.width) / 2.0), y: 2.0), size: badgeSize)
|
||||
|
||||
|
|
|
|||
|
|
@ -418,24 +418,6 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
|||
}
|
||||
}
|
||||
|
||||
var isScheduledMessages = false
|
||||
if case .scheduledMessages = chatPresentationInterfaceState.subject {
|
||||
isScheduledMessages = true
|
||||
}
|
||||
var displayBotStartPanel = false
|
||||
|
||||
if !isScheduledMessages {
|
||||
if let _ = chatPresentationInterfaceState.botStartPayload {
|
||||
if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil {
|
||||
displayBotStartPanel = true
|
||||
}
|
||||
} else if let chatHistoryState = chatPresentationInterfaceState.chatHistoryState, case .loaded(true, _) = chatHistoryState {
|
||||
if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil {
|
||||
displayBotStartPanel = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
displayInputTextPanel = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ private final class LegacyDataImportSplashImpl: WindowCoveringView, LegacyDataIm
|
|||
self.updateLayout(size)
|
||||
}
|
||||
}
|
||||
self.progressNode.transitionToState(.progress(color: self.theme?.list.itemAccentColor ?? UIColor(rgb: 0x007aff), lineWidth: 2.0, value: CGFloat(max(0.025, self.progress.1)), cancelEnabled: false, animateRotation: true), animated: false, completion: {})
|
||||
self.progressNode.transitionToState(.progress(color: self.theme?.list.itemAccentColor ?? UIColor(rgb: 0x0088ff), lineWidth: 2.0, value: CGFloat(max(0.025, self.progress.1)), cancelEnabled: false, animateRotation: true), animated: false, completion: {})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
let previousTheme = strongSelf.presentationData.theme
|
||||
strongSelf.presentationData = presentationData
|
||||
if previousTheme !== presentationData.theme {
|
||||
(strongSelf.rootTabController as? TabBarControllerImpl)?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme))
|
||||
(strongSelf.rootTabController as? TabBarControllerImpl)?.updateTheme(theme: presentationData.theme)
|
||||
strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +188,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
}
|
||||
|
||||
public func addRootControllers(showCallsTab: Bool) {
|
||||
let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
||||
let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme)
|
||||
tabBarController.navigationPresentation = .master
|
||||
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, location: .chatList(groupId: .root), controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
|
|||
let value: UInt32
|
||||
switch self {
|
||||
case .blue:
|
||||
value = 0x007aff
|
||||
value = 0x0088ff
|
||||
case .cyan:
|
||||
value = 0x00c2ed
|
||||
case .green:
|
||||
|
|
|
|||
|
|
@ -123,6 +123,39 @@ public protocol WallpaperBackgroundNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOverlayLayer {
|
||||
final class CloneLayer: SimpleLayer {
|
||||
private weak var parentLayer: EffectImageLayer?
|
||||
private var index: SparseBag<Weak<CloneLayer>>.Index?
|
||||
|
||||
init(parentLayer: EffectImageLayer) {
|
||||
self.parentLayer = parentLayer
|
||||
|
||||
super.init()
|
||||
|
||||
self.index = parentLayer.cloneLayers.add(Weak(self))
|
||||
|
||||
self.backgroundColor = parentLayer.backgroundColor
|
||||
self.contents = parentLayer.contents
|
||||
self.compositingFilter = parentLayer.compositingFilter
|
||||
self.opacity = parentLayer.opacity
|
||||
self.isOpaque = parentLayer.isOpaque
|
||||
}
|
||||
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let parentLayer = self.parentLayer, let index = self.index {
|
||||
parentLayer.cloneLayers.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SoftlightMode {
|
||||
case whileAnimating
|
||||
case always
|
||||
|
|
@ -141,6 +174,12 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||
} else {
|
||||
self.backgroundColor = nil
|
||||
}
|
||||
|
||||
for cloneLayer in self.cloneLayers {
|
||||
if let value = cloneLayer.value {
|
||||
value.backgroundColor = self.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +223,8 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||
var suspendCompositionUpdates: Bool = false
|
||||
private var needsCompositionUpdate: Bool = false
|
||||
|
||||
fileprivate let cloneLayers = SparseBag<Weak<CloneLayer>>()
|
||||
|
||||
private func updateFilters() {
|
||||
let useSoftlight: Bool
|
||||
let useFilter: Bool
|
||||
|
|
@ -208,6 +249,12 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||
self.compositingFilter = nil
|
||||
}
|
||||
|
||||
for cloneLayer in self.cloneLayers {
|
||||
if let value = cloneLayer.value {
|
||||
value.compositingFilter = self.compositingFilter
|
||||
}
|
||||
}
|
||||
|
||||
self.updateContents()
|
||||
self.updateOpacity()
|
||||
}
|
||||
|
|
@ -330,6 +377,13 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||
self.allowSettingContents = false
|
||||
|
||||
self.backgroundColor = nil
|
||||
|
||||
for cloneLayer in self.cloneLayers {
|
||||
if let value = cloneLayer.value {
|
||||
value.contents = self.contents
|
||||
value.backgroundColor = self.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +399,13 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver
|
|||
self.allowSettingOpacity = false
|
||||
self.isOpaque = true
|
||||
}
|
||||
|
||||
for cloneLayer in self.cloneLayers {
|
||||
if let value = cloneLayer.value {
|
||||
value.opacity = self.opacity
|
||||
value.isOpaque = self.isOpaque
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -726,6 +787,8 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||
|
||||
private let contentNode: ASDisplayNode
|
||||
|
||||
fileprivate let edgeEffectNodes = SparseBag<Weak<WallpaperEdgeEffectNodeImpl>>()
|
||||
|
||||
private var blurredBackgroundContents: UIImage?
|
||||
|
||||
private var freeBackgroundPortalSourceView: PortalSourceView?
|
||||
|
|
@ -773,9 +836,9 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||
}
|
||||
}
|
||||
|
||||
private var gradientBackgroundNode: GradientBackgroundNode?
|
||||
fileprivate var gradientBackgroundNode: GradientBackgroundNode?
|
||||
private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode?
|
||||
private let patternImageLayer: EffectImageLayer
|
||||
fileprivate let patternImageLayer: EffectImageLayer
|
||||
private let dimLayer: SimpleLayer
|
||||
private var isGeneratingPatternImage: Bool = false
|
||||
|
||||
|
|
@ -786,8 +849,6 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||
|
||||
private var modelStickerNode: DefaultAnimatedStickerNodeImpl?
|
||||
|
||||
fileprivate let edgeEffectNodes = SparseBag<Weak<WallpaperEdgeEffectNodeImpl>>()
|
||||
|
||||
private var isSettingUpWallpaper: Bool = false
|
||||
|
||||
private struct CachedValidPatternImage {
|
||||
|
|
@ -1057,6 +1118,12 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||
if self.isLooping {
|
||||
scheduleLoopingEvent = true
|
||||
}
|
||||
|
||||
for edgeEffectNode in self.edgeEffectNodes {
|
||||
if let edgeEffectNode = edgeEffectNode.value {
|
||||
edgeEffectNode.updateGradientNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.gradientBackgroundNode?.updateColors(colors: mappedColors)
|
||||
|
||||
|
|
@ -1712,31 +1779,12 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||
}
|
||||
|
||||
public func makeEdgeEffectNode() -> WallpaperEdgeEffectNode? {
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
let node = WallpaperEdgeEffectNodeImpl(parentNode: self)
|
||||
node.cloneNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false)
|
||||
return node
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
let node = WallpaperEdgeEffectNodeImpl(parentNode: self)
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode {
|
||||
var cloneNode: GradientBackgroundNode.CloneNode? {
|
||||
didSet {
|
||||
if self.cloneNode !== oldValue {
|
||||
if let cloneNode = self.cloneNode {
|
||||
self.containerNode.insertSubnode(cloneNode, at: 0)
|
||||
|
||||
if let params = self.params {
|
||||
self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct Params: Equatable {
|
||||
let rect: CGRect
|
||||
let edge: WallpaperEdgeEffectEdge
|
||||
|
|
@ -1749,6 +1797,9 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff
|
|||
}
|
||||
}
|
||||
|
||||
private var gradientNode: GradientBackgroundNode.CloneNode?
|
||||
private let patternImageLayer: EffectImageLayer.CloneLayer
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private let containerMaskingNode: ASDisplayNode
|
||||
private let overlayNode: ASDisplayNode
|
||||
|
|
@ -1763,6 +1814,14 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff
|
|||
init(parentNode: WallpaperBackgroundNodeImpl) {
|
||||
self.parentNode = parentNode
|
||||
|
||||
if let gradientBackgroundNode = parentNode.gradientBackgroundNode {
|
||||
self.gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false)
|
||||
} else {
|
||||
self.gradientNode = nil
|
||||
}
|
||||
|
||||
self.patternImageLayer = EffectImageLayer.CloneLayer(parentLayer: parentNode.patternImageLayer)
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
self.containerNode.anchorPoint = CGPoint()
|
||||
self.containerNode.clipsToBounds = true
|
||||
|
|
@ -1776,6 +1835,11 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff
|
|||
|
||||
super.init()
|
||||
|
||||
if let gradientNode = self.gradientNode {
|
||||
self.containerNode.addSubnode(gradientNode)
|
||||
}
|
||||
//self.layer.addSublayer(self.patternImageLayer)
|
||||
|
||||
self.addSubnode(self.containerMaskingNode)
|
||||
self.containerMaskingNode.view.mask = self.maskView
|
||||
|
||||
|
|
@ -1790,6 +1854,25 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff
|
|||
}
|
||||
}
|
||||
|
||||
func updateGradientNode() {
|
||||
if let gradientBackgroundNode = self.parentNode?.gradientBackgroundNode {
|
||||
if self.gradientNode == nil {
|
||||
let gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false)
|
||||
self.gradientNode = gradientNode
|
||||
self.containerNode.insertSubnode(gradientNode, at: 0)
|
||||
|
||||
if let params = self.params {
|
||||
self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let gradientNode = self.gradientNode {
|
||||
self.gradientNode = nil
|
||||
gradientNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updatePattern(isInverted: Bool) {
|
||||
if self.isInverted != isInverted {
|
||||
self.isInverted = isInverted
|
||||
|
|
@ -1844,9 +1927,10 @@ private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEff
|
|||
|
||||
transition.updateFrame(node: self.overlayNode, frame: CGRect(origin: CGPoint(), size: containerSize))
|
||||
|
||||
if let cloneNode = self.cloneNode {
|
||||
transition.updateFrame(node: cloneNode, frame: CGRect(origin: CGPoint(), size: containerSize))
|
||||
if let gradientNode = self.gradientNode {
|
||||
transition.updateFrame(node: gradientNode, frame: CGRect(origin: CGPoint(), size: containerSize))
|
||||
}
|
||||
transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: containerSize))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue