From ebcd0557e5f16d4cb28694c16efe9e2b7b50d241 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 12 Sep 2025 10:54:22 +0200 Subject: [PATCH] Temp --- .../Sources/ActivityIndicator.swift | 2 +- submodules/AudioBlob/Sources/BlobView.swift | 2 +- submodules/ChatListUI/BUILD | 1 + .../Sources/ChatListContainerItemNode.swift | 12 + .../Sources/ChatListControllerNode.swift | 1 + submodules/ContactListUI/BUILD | 1 + .../Sources/ContactsControllerNode.swift | 10 + .../Source/NavigationBackButtonNode.swift | 2 +- .../Display/Source/NavigationButtonNode.swift | 4 +- .../Display/Source/TabBarController.swift | 4 - submodules/Display/Source/WindowContent.swift | 6 +- .../Sources/DrawingTextEntityView.swift | 2 +- .../GalleryUI/Sources/GalleryController.swift | 2 +- .../Items/UniversalVideoGalleryItem.swift | 4 +- .../GraphUI/Sources/ChartStackSection.swift | 4 +- .../Sources/InstantPageControllerNode.swift | 2 +- .../InstantPageSettingsItemTheme.swift | 2 +- .../InstantPageSettingsThemeItemNode.swift | 2 +- .../Sources/InstantPageTheme.swift | 6 +- .../Sources/TGCheckButtonView.m | 2 +- submodules/LegacyComponents/Sources/TGColor.m | 2 +- .../TGMediaPickerGalleryInterfaceView.m | 2 +- .../Sources/LegacyPaintStickersContext.swift | 2 +- .../ChatMessageLiveLocationPositionNode.swift | 4 +- .../Sources/LocationAnnotation.swift | 2 +- .../LocationUI/Sources/LocationMapNode.swift | 2 +- .../Sources/BusinessPageComponent.swift | 2 +- .../Sources/PremiumIntroScreen.swift | 2 +- .../Sources/StoriesPageComponent.swift | 2 +- .../TabBarAccountSwitchController.swift | 105 --- .../TabBarAccountSwitchControllerNode.swift | 568 ---------------- .../Sources/Themes/ThemeColorPresets.swift | 4 +- .../Themes/ThemeSettingsAccentColorItem.swift | 2 +- submodules/TabBarUI/BUILD | 15 +- .../Sources/TabBarContollerNode.swift | 210 +++++- .../TabBarUI/Sources/TabBarController.swift | 152 +---- submodules/TabBarUI/Sources/TabBarNode.swift | 79 +-- .../VoiceChatCameraPreviewController.swift | 2 +- .../VoiceChatFullscreenParticipantItem.swift | 2 +- .../Sources/VoiceChatParticipantItem.swift | 2 +- .../VoiceChatRecordingSetupController.swift | 2 +- .../DefaultDarkPresentationTheme.swift | 4 +- .../Sources/DefaultDayPresentationTheme.swift | 4 +- .../PresentationResourcesSettings.swift | 2 +- .../Sources/BadgeComponent.swift | 28 +- .../Sources/PlaceholderComponent.swift | 4 +- .../Sources/ChatMessageBubbleItemNode.swift | 57 +- .../ChatRecordingPreviewInputPanelNode.swift | 2 +- .../ChatRecordingViewOnceButtonNode.swift | 2 +- .../ChatTextInputActionButtonsNode.swift | 6 +- .../Sources/AccessoryItemIconButton.swift | 1 + .../Sources/ChatTextInputPanelNode.swift | 48 +- ...TextInputAudioRecordingOverlayButton.swift | 4 +- .../ChatTextInputMediaRecordingButton.swift | 6 +- .../TelegramUI/Components/EdgeEffect/BUILD | 19 + .../EdgeEffect/Sources/EdgeEffect.swift | 69 ++ .../Sources/EmojiTextAttachmentView.swift | 2 +- .../Sources/GlassBackgroundComponent.swift | 82 ++- .../Sources/MediaEditorScreen.swift | 2 +- .../Sources/MediaScrubberComponent.swift | 2 +- .../Sources/HashtagListItemComponent.swift | 2 +- .../Components/PeerInfo/PeerInfoScreen/BUILD | 1 + .../Sources/PeerInfoScreen.swift | 13 + .../Sources/ThemeAccentColorController.swift | 2 +- .../Sources/ShareWithPeersScreen.swift | 2 +- .../Sources/DataUsageScreen.swift | 2 +- .../Components/TabBarComponent/BUILD | 27 + .../Sources/TabBarComponent.swift | 623 ++++++++++++++++++ .../TelegramUI/Sources/AppDelegate.swift | 18 - .../Sources/ChatHistoryListNode.swift | 2 +- .../ChatHistoryNavigationButtonNode.swift | 8 +- .../ChatInterfaceStateInputPanels.swift | 18 - .../Sources/LegacyDataImportSplash.swift | 2 +- .../Sources/TelegramRootController.swift | 4 +- .../Sources/PresentationThemeSettings.swift | 2 +- .../Sources/WallpaperBackgroundNode.swift | 138 +++- 76 files changed, 1352 insertions(+), 1090 deletions(-) delete mode 100644 submodules/SettingsUI/Sources/TabBarAccountSwitchController.swift delete mode 100644 submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift create mode 100644 submodules/TelegramUI/Components/EdgeEffect/BUILD create mode 100644 submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift create mode 100644 submodules/TelegramUI/Components/TabBarComponent/BUILD create mode 100644 submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift diff --git a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift index 00a4397777..b740ff674b 100644 --- a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift +++ b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift @@ -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 diff --git a/submodules/AudioBlob/Sources/BlobView.swift b/submodules/AudioBlob/Sources/BlobView.swift index 34d7c1c8f1..bfb43da5ee 100644 --- a/submodules/AudioBlob/Sources/BlobView.swift +++ b/submodules/AudioBlob/Sources/BlobView.swift @@ -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)) } } diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 29031671ee..a28100bbf7 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -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", diff --git a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift index c28d06df04..07d7532dbc 100644 --- a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift +++ b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift @@ -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) { diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index c413dc8a53..8fc934d5a1 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -21,6 +21,7 @@ import ChatFolderLinkPreviewScreen import ChatListHeaderComponent import StoryPeerListComponent import TelegramNotices +import EdgeEffect public enum ChatListContainerNodeFilter: Equatable { case all diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index ef6c5d49a2..0fb6ab5bde 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -46,6 +46,7 @@ swift_library( "//submodules/UndoUI", "//submodules/TelegramIntents", "//submodules/ContextUI", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 6a9ad102a4..0982b64bf1 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -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 { diff --git a/submodules/Display/Source/NavigationBackButtonNode.swift b/submodules/Display/Source/NavigationBackButtonNode.swift index b1164ebd78..12f55c986b 100644 --- a/submodules/Display/Source/NavigationBackButtonNode.swift +++ b/submodules/Display/Source/NavigationBackButtonNode.swift @@ -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()) } diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index 73d49fc855..1ebc26af8a 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -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 { diff --git a/submodules/Display/Source/TabBarController.swift b/submodules/Display/Source/TabBarController.swift index e552c5fafe..0113526a86 100644 --- a/submodules/Display/Source/TabBarController.swift +++ b/submodules/Display/Source/TabBarController.swift @@ -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) diff --git a/submodules/Display/Source/WindowContent.swift b/submodules/Display/Source/WindowContent.swift index 62de4a8e04..e5270bdae2 100644 --- a/submodules/Display/Source/WindowContent.swift +++ b/submodules/Display/Source/WindowContent.swift @@ -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 } } diff --git a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift index ef85267ba2..8d40dc69db 100644 --- a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift @@ -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: diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 242e26bca4..2b09ceb02a 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -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 diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index f949c7328c..216ad40183 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -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) )), diff --git a/submodules/GraphUI/Sources/ChartStackSection.swift b/submodules/GraphUI/Sources/ChartStackSection.swift index d0c5f41cc3..c20fbf861a 100644 --- a/submodules/GraphUI/Sources/ChartStackSection.swift +++ b/submodules/GraphUI/Sources/ChartStackSection.swift @@ -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) diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 52108faac6..c289b0b52d 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -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 diff --git a/submodules/InstantPageUI/Sources/InstantPageSettingsItemTheme.swift b/submodules/InstantPageUI/Sources/InstantPageSettingsItemTheme.swift index 21c766bdd8..017e85fb97 100644 --- a/submodules/InstantPageUI/Sources/InstantPageSettingsItemTheme.swift +++ b/submodules/InstantPageUI/Sources/InstantPageSettingsItemTheme.swift @@ -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( diff --git a/submodules/InstantPageUI/Sources/InstantPageSettingsThemeItemNode.swift b/submodules/InstantPageUI/Sources/InstantPageSettingsThemeItemNode.swift index b96c2126bd..ef6438ec7c 100644 --- a/submodules/InstantPageUI/Sources/InstantPageSettingsThemeItemNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageSettingsThemeItemNode.swift @@ -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), diff --git a/submodules/InstantPageUI/Sources/InstantPageTheme.swift b/submodules/InstantPageUI/Sources/InstantPageTheme.swift index 3706d23bc5..d1305895e4 100644 --- a/submodules/InstantPageUI/Sources/InstantPageTheme.swift +++ b/submodules/InstantPageUI/Sources/InstantPageTheme.swift @@ -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), diff --git a/submodules/LegacyComponents/Sources/TGCheckButtonView.m b/submodules/LegacyComponents/Sources/TGCheckButtonView.m index 45ee58d9a2..010d2545b0 100644 --- a/submodules/LegacyComponents/Sources/TGCheckButtonView.m +++ b/submodules/LegacyComponents/Sources/TGCheckButtonView.m @@ -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; diff --git a/submodules/LegacyComponents/Sources/TGColor.m b/submodules/LegacyComponents/Sources/TGColor.m index dd57eb0164..e9fb71f7c3 100644 --- a/submodules/LegacyComponents/Sources/TGColor.m +++ b/submodules/LegacyComponents/Sources/TGColor.m @@ -16,7 +16,7 @@ UIColor *TGAccentColor() static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { - color = TGColorWithHex(0x007aff); + color = TGColorWithHex(0x0088ff); }); return color; } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index d6e7f8039c..771dd32528 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -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]; diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index 3bad8cf5df..5a3aa9211d 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -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; } diff --git a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift index 7a65acaba8..8a2809491b 100644 --- a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift +++ b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift @@ -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 } diff --git a/submodules/LocationUI/Sources/LocationAnnotation.swift b/submodules/LocationUI/Sources/LocationAnnotation.swift index 78f0375eb0..a9957db257 100644 --- a/submodules/LocationUI/Sources/LocationAnnotation.swift +++ b/submodules/LocationUI/Sources/LocationAnnotation.swift @@ -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() diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index d3c265dc9c..206ac3c899 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -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)! diff --git a/submodules/PremiumUI/Sources/BusinessPageComponent.swift b/submodules/PremiumUI/Sources/BusinessPageComponent.swift index 46c469d7ae..667decd45c 100644 --- a/submodules/PremiumUI/Sources/BusinessPageComponent.swift +++ b/submodules/PremiumUI/Sources/BusinessPageComponent.swift @@ -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 diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 3eece91a02..b65f458d11 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -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 diff --git a/submodules/PremiumUI/Sources/StoriesPageComponent.swift b/submodules/PremiumUI/Sources/StoriesPageComponent.swift index 396da5f2b1..83e1f3cb01 100644 --- a/submodules/PremiumUI/Sources/StoriesPageComponent.swift +++ b/submodules/PremiumUI/Sources/StoriesPageComponent.swift @@ -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), diff --git a/submodules/SettingsUI/Sources/TabBarAccountSwitchController.swift b/submodules/SettingsUI/Sources/TabBarAccountSwitchController.swift deleted file mode 100644 index 6c3beaa573..0000000000 --- a/submodules/SettingsUI/Sources/TabBarAccountSwitchController.swift +++ /dev/null @@ -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(true) - override public var ready: Promise { - 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) - }) - } -} diff --git a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift b/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift deleted file mode 100644 index 72b96a4221..0000000000 --- a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift +++ /dev/null @@ -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 -} diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift index 81ca956786..afd1531610 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift @@ -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)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift index 9a18c3b9ae..2e5218a857 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift @@ -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 diff --git a/submodules/TabBarUI/BUILD b/submodules/TabBarUI/BUILD index 1abbce2193..a2bed9e07b 100644 --- a/submodules/TabBarUI/BUILD +++ b/submodules/TabBarUI/BUILD @@ -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", diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index d0b8d37680..6b6921fd92 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -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() + 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() } } diff --git a/submodules/TabBarUI/Sources/TabBarController.swift b/submodules/TabBarUI/Sources/TabBarController.swift index 0d9b77d929..5523ab5171 100644 --- a/submodules/TabBarUI/Sources/TabBarController.swift +++ b/submodules/TabBarUI/Sources/TabBarController.swift @@ -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 in if let tabBarItem = tabBarItem, tabBarItem.image == nil { diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index b5d14b5460..5b08f3eb81 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -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 { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift index 959bf6cc82..76b7e7ab54 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift @@ -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 { get } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift index 3490109501..583671503a 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift @@ -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) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index da82ee3fea..81722b4d4f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -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) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatRecordingSetupController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatRecordingSetupController.swift index e550fa3e71..d66762b760 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatRecordingSetupController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatRecordingSetupController.swift @@ -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 { diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 30f45efffa..97f506b2a3 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -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, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 9e77b7e6cd..c7af0c0ed6 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -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), diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift index 1fac92579b..785aa906b8 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift @@ -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 { diff --git a/submodules/TelegramUI/Components/BadgeComponent/Sources/BadgeComponent.swift b/submodules/TelegramUI/Components/BadgeComponent/Sources/BadgeComponent.swift index be4b3b3042..15c504cd2c 100644 --- a/submodules/TelegramUI/Components/BadgeComponent/Sources/BadgeComponent.swift +++ b/submodules/TelegramUI/Components/BadgeComponent/Sources/BadgeComponent.swift @@ -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 } } diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/PlaceholderComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/PlaceholderComponent.swift index 1fb49b88dc..9d566dd9d9 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/PlaceholderComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/PlaceholderComponent.swift @@ -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, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 79bc0e9406..8a97b497d5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -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? { diff --git a/submodules/TelegramUI/Components/Chat/ChatRecordingPreviewInputPanelNode/Sources/ChatRecordingPreviewInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecordingPreviewInputPanelNode/Sources/ChatRecordingPreviewInputPanelNode.swift index 326548d1ef..e67e93a847 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecordingPreviewInputPanelNode/Sources/ChatRecordingPreviewInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecordingPreviewInputPanelNode/Sources/ChatRecordingPreviewInputPanelNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Components/Chat/ChatRecordingViewOnceButtonNode/Sources/ChatRecordingViewOnceButtonNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecordingViewOnceButtonNode/Sources/ChatRecordingViewOnceButtonNode.swift index b94b71e7c6..d62b84c0c2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecordingViewOnceButtonNode/Sources/ChatRecordingViewOnceButtonNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecordingViewOnceButtonNode/Sources/ChatRecordingViewOnceButtonNode.swift @@ -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) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift index 7e87297a08..75380ef626 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift @@ -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) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/AccessoryItemIconButton.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/AccessoryItemIconButton.swift index ce7409da94..84218bb179 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/AccessoryItemIconButton.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/AccessoryItemIconButton.swift @@ -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) } diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 970825f001..42145a2428 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -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) diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputAudioRecordingOverlayButton.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputAudioRecordingOverlayButton.swift index 9842645bc5..4e137ed031 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputAudioRecordingOverlayButton.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputAudioRecordingOverlayButton.swift @@ -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 { diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift index 0ca1dff8b9..e2d2c0b811 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift @@ -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)) } } diff --git a/submodules/TelegramUI/Components/EdgeEffect/BUILD b/submodules/TelegramUI/Components/EdgeEffect/BUILD new file mode 100644 index 0000000000..62ff9343ac --- /dev/null +++ b/submodules/TelegramUI/Components/EdgeEffect/BUILD @@ -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", + ], +) diff --git a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift new file mode 100644 index 0000000000..2462c77960 --- /dev/null +++ b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift @@ -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 + } + } + } +} diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index e86a2fef1c..8bcb4abbb8 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -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) diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index e0437a48fe..7ac15dbe9b 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -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)) } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index de9497d768..608ae85e60 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -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, diff --git a/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift b/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift index 1c843521e7..95ac4f04d2 100644 --- a/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift +++ b/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift @@ -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 diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/HashtagListItemComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/HashtagListItemComponent.swift index ed641defc1..e51ab6cfea 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/HashtagListItemComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/HashtagListItemComponent.swift @@ -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 diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index 9e56ec5e89..4fdd989546 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -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", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 4e7c74798f..32a96f27e8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -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() 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 diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift index 89eecec894..f4c58a78c6 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift @@ -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?) { diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 438a20a320..b97533ceb3 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -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") diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift index fcc7febb34..e81a49e8d7 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift @@ -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: diff --git a/submodules/TelegramUI/Components/TabBarComponent/BUILD b/submodules/TelegramUI/Components/TabBarComponent/BUILD new file mode 100644 index 0000000000..2dc21d7382 --- /dev/null +++ b/submodules/TelegramUI/Components/TabBarComponent/BUILD @@ -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", + ], +) diff --git a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift new file mode 100644 index 0000000000..e75ae06981 --- /dev/null +++ b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift @@ -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] = [:] + private var selectedItemViews: [AnyHashable: ComponentView] = [:] + + 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, 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 + 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 + 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, 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? + private var animationIcon: ComponentView? + private let title = ComponentView() + private var badge: ComponentView? + + 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, 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 + 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 + 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 + 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, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 1bee710d4c..528d0c5843 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -1232,24 +1232,6 @@ private func extractAccountManagerState(records: AccountRecordsView>.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>() + 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>() + 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>() - 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)) } }