From 9527b2d8bef15cb1c08c4819df16f28f68b5e5e0 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 13 Nov 2025 07:18:15 +0400 Subject: [PATCH] Various improvements --- ...AttachmentTextInputActionButtonsNode.swift | 60 +++-- .../AttachmentTextInputPanelNode.swift | 28 ++- .../Sources/AttachmentPanel.swift | 26 ++- .../ItemListFolderInviteLinkItem.swift | 2 +- submodules/PremiumUI/BUILD | 1 + .../Sources/PremiumIntroScreen.swift | 75 ++----- .../Sources/PremiumLimitsListScreen.swift | 6 +- .../Sources/PremiumPrivacyScreen.swift | 55 ++--- .../Sources/ChatScheduleTimeScreen.swift | 210 +++++++++++++++--- .../EdgeEffect/Sources/EdgeEffect.swift | 65 +++++- .../Sources/GiftViewScreen.swift | 14 +- .../Sources/MediaEditorScreen.swift | 2 +- .../ListItems/PeerInfoScreenAddressItem.swift | 2 +- .../PeerInfoScreenBusinessHoursItem.swift | 2 +- .../PeerInfoScreenContactInfoItem.swift | 2 +- .../PeerInfoScreenLabeledValueItem.swift | 2 +- .../PeerInfoScreenPersonalChannelItem.swift | 2 +- .../Sources/PeerInfoHeaderButtonNode.swift | 2 +- .../Sources/StarsIntroScreen.swift | 17 +- .../Sources/StarsTransactionScreen.swift | 60 +++-- .../Sources/StarsTransferScreen.swift | 2 - 21 files changed, 410 insertions(+), 225 deletions(-) diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift index 7d1b162759..c150b32d94 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift @@ -9,19 +9,18 @@ import ChatPresentationInterfaceState import ComponentFlow import AccountContext import AnimatedCountLabelNode -import GlassBackgroundComponent final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessageActionSheetControllerSourceSendButtonNode { private let strings: PresentationStrings private let glass: Bool let sendContainerNode: ASDisplayNode - let backgroundView: GlassBackgroundView? - let backgroundNode: ASDisplayNode? + let backgroundNode: ASDisplayNode let sendButton: HighlightTrackingButtonNode var sendButtonHasApplyIcon = false var animatingSendButton = false let textNode: ImmediateAnimatedCountLabelNode + let iconNode: ASImageNode private var theme: PresentationTheme @@ -46,21 +45,18 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage self.sendContainerNode = ASDisplayNode() self.sendContainerNode.layer.allowsGroupOpacity = true - if glass { - self.backgroundView = GlassBackgroundView() - self.backgroundNode = nil - } else { - self.backgroundNode = ASDisplayNode() - self.backgroundNode?.backgroundColor = theme.chat.inputPanel.actionControlFillColor - self.backgroundNode?.clipsToBounds = true - self.backgroundView = nil - } + self.backgroundNode = ASDisplayNode() + self.backgroundNode.backgroundColor = self.theme.chat.inputPanel.actionControlFillColor + self.backgroundNode.clipsToBounds = true self.sendButton = HighlightTrackingButtonNode(pointerStyle: nil) self.textNode = ImmediateAnimatedCountLabelNode() self.textNode.isUserInteractionEnabled = false + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + super.init() self.isAccessibilityElement = true @@ -89,13 +85,10 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage } self.addSubnode(self.sendContainerNode) - if let backgroundView = self.backgroundView { - self.sendContainerNode.view.addSubview(backgroundView) - } else if let backgroundNode = self.backgroundNode { - self.sendContainerNode.addSubnode(backgroundNode) - } + self.sendContainerNode.addSubnode(self.backgroundNode) self.sendContainerNode.addSubnode(self.sendButton) self.sendContainerNode.addSubnode(self.textNode) + self.backgroundNode.addSubnode(self.iconNode) } override func didLoad() { @@ -114,13 +107,15 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage } } - if let backgroundNode = self.backgroundNode { - self.sendButtonPointerInteraction = PointerInteraction(view: self.sendButton.view, customInteractionView: backgroundNode.view, style: .lift) - } + self.sendButtonPointerInteraction = PointerInteraction(view: self.sendButton.view, customInteractionView: self.backgroundNode.view, style: .lift) + } + + func setImage(_ image: UIImage?) { + self.iconNode.image = image } func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) { - self.backgroundNode?.backgroundColor = theme.chat.inputPanel.actionControlFillColor + self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor } private var absoluteRect: (CGRect, CGSize)? @@ -128,9 +123,15 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage self.absoluteRect = (rect, containerSize) } + public func animateIn(transition: ContainedViewLayoutTransition) { + transition.animatePositionAdditive(layer: self.iconNode.layer, offset: CGPoint(x: -22.0, y: 18.0)) + } + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, minimized: Bool, text: String, interfaceState: ChatPresentationInterfaceState) -> CGSize { self.validLayout = size + let height: CGFloat = self.glass ? 34.0 : 33.0 + let width: CGFloat var titleOffset: CGFloat = 0.0 @@ -158,7 +159,7 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage let textSize = self.textNode.updateLayout(size: CGSize(width: 100.0, height: 100.0), animated: transition.isAnimated) if minimized { - width = 53.0 + width = self.glass ? 51.0 : 53.0 } else { width = textSize.width + buttonInset * 2.0 } @@ -172,15 +173,12 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode, ChatSendMessage transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: buttonSize)) transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: buttonSize)) - if let backgroundView = self.backgroundView { - let backgroundSize = CGSize(width: width - 13.0, height: 40.0) - let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize) - transition.updateFrame(view: backgroundView, frame: backgroundFrame) - backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: false, tintColor: .init(kind: .custom, color: self.theme.chat.inputPanel.actionControlFillColor), transition: ComponentTransition(transition)) - } else if let backgroundNode { - let backgroundSize = CGSize(width: width - 11.0, height: 33.0) - transition.updateFrame(node: backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize)) - backgroundNode.cornerRadius = backgroundSize.height / 2.0 + let backgroundSize = CGSize(width: width - 11.0, height: height) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize)) + self.backgroundNode.cornerRadius = backgroundSize.height / 2.0 + + if let iconSize = self.iconNode.image?.size { + transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - iconSize.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - iconSize.height) / 2.0)), size: iconSize)) } return buttonSize diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 7374e3fd03..f74fde4062 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -329,6 +329,10 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS public var interfaceInteraction: ChatPanelInterfaceInteraction? + public func animateIn(transition: ContainedViewLayoutTransition) { + self.actionButtons.animateIn(transition: transition) + } + public func updateSendButtonEnabled(_ enabled: Bool, animated: Bool) { self.actionButtons.isUserInteractionEnabled = enabled @@ -652,7 +656,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS private func calculateTextFieldMetrics(width: CGFloat, maxHeight: CGFloat, metrics: LayoutMetrics) -> (accessoryButtonsWidth: CGFloat, textFieldHeight: CGFloat) { var textFieldInsets = self.textFieldInsets(metrics: metrics) if self.actionButtons.frame.width > 44.0 { - textFieldInsets.right = self.actionButtons.frame.width + textFieldInsets.right = self.actionButtons.frame.width - 6.0 } let fieldMaxHeight = textFieldMaxHeight(maxHeight, metrics: metrics) @@ -846,9 +850,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS } self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon if self.actionButtons.sendButtonHasApplyIcon { - self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme), for: []) + self.actionButtons.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme)) } else { - self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme), for: []) + self.actionButtons.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme)) } } } @@ -1013,17 +1017,17 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS text = "⭐️\(sendPaidMessageStars.value * Int64(count))" isPaidMessage = true } else { - isMinimized = !self.isAttachment || inputHasText + isMinimized = !self.isAttachment || inputHasText || self.glass text = presentationInterfaceState.strings.MediaPicker_Send } - actionButtonsSize = self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), transition: transition, minimized: isMinimized && !self.glass, text: text, interfaceState: presentationInterfaceState) + actionButtonsSize = self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), transition: transition, minimized: isMinimized, text: text, interfaceState: presentationInterfaceState) textBackgroundInset = actionButtonsSize.width - 44.0 } else { actionButtonsSize = CGSize(width: 44.0, height: minimalHeight) } - let actionButtonsOriginOffset: CGFloat = self.glass ? -3.0 : 0.0 - let actionButtonsFrame = CGRect(origin: CGPoint(x: width - rightInset - actionButtonsSize.width + 1.0 - UIScreenPixel + composeButtonsOffset + actionButtonsOriginOffset, y: panelHeight - minimalHeight), size: actionButtonsSize) + let actionButtonsOriginOffset: CGFloat = self.glass ? -6.0 : 0.0 + let actionButtonsFrame = CGRect(origin: CGPoint(x: width - rightInset - actionButtonsSize.width + 1.0 - UIScreenPixel + composeButtonsOffset + actionButtonsOriginOffset, y: panelHeight - minimalHeight - 1.0), size: actionButtonsSize) transition.updateFrame(node: self.actionButtons, frame: actionButtonsFrame) let textInputHeight = panelHeight - textFieldInsets.top - textFieldInsets.bottom @@ -1065,7 +1069,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS ) var inputNodeOffset: CGPoint = CGPoint(x: -1.0, y: -1.0) if self.glass { - inputNodeOffset = CGPoint(x: -4.0, y: -4.0) + inputNodeOffset = CGPoint(x: -6.0, y: -4.0) } transition.updateFrame(view: self.inputModeView, frame: CGRect(origin: CGPoint(x: textInputBackgroundFrame.maxX - inputNodeSize.width + inputNodeOffset.x, y: textInputBackgroundFrame.maxY - inputNodeSize.height + inputNodeOffset.y), size: inputNodeSize)) } @@ -1503,12 +1507,12 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS if sendButtonHasApplyIcon != self.actionButtons.sendButtonHasApplyIcon { self.actionButtons.sendButtonHasApplyIcon = sendButtonHasApplyIcon if self.actionButtons.sendButtonHasApplyIcon { - self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme), for: []) + self.actionButtons.setImage(PresentationResourcesChat.chatInputPanelApplyIconImage(interfaceState.theme)) } else { if case .scheduledMessages = interfaceState.subject { - self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelScheduleButtonImage(interfaceState.theme), for: []) + self.actionButtons.setImage(PresentationResourcesChat.chatInputPanelScheduleButtonImage(interfaceState.theme)) } else { - self.actionButtons.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme), for: []) + self.actionButtons.setImage(PresentationResourcesChat.chatInputPanelSendIconImage(interfaceState.theme)) } } } @@ -1956,7 +1960,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS public func frameForInputActionButton() -> CGRect? { if !self.actionButtons.alpha.isZero { - return self.actionButtons.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 4.0, dy: 0.0) + return self.actionButtons.frame.insetBy(dx: 0.0, dy: self.glass ? 0.0 : 6.0).offsetBy(dx: 4.0, dy: 0.0) } return nil } diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 01a4669a07..66665ad2b0 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -1890,7 +1890,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { self.textInputPanelNode?.ensureUnfocused() } - let textPanelSideInset: CGFloat = 8.0 + let textPanelSideInset: CGFloat = 16.0 let defaultPanelSideInset: CGFloat = glassPanelSideInset let panelSideInset: CGFloat = (isSelecting ? textPanelSideInset : defaultPanelSideInset) + layout.safeInsets.left var textPanelHeight: CGFloat = 0.0 @@ -1913,7 +1913,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { } else { textPanelHeight = self.panelStyle == .glass ? 40.0 : 45.0 } - textPanelWidth = layout.size.width - panelSideInset - 88.0 - 6.0 + textPanelWidth = layout.size.width - panelSideInset * 2.0 } let glassPanelHeight: CGFloat = 62.0 @@ -1937,7 +1937,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { backgroundViewColor = self.presentationData.theme.list.plainBackgroundColor.withAlphaComponent(0.75) } - let backgroundOriginX: CGFloat = isSelecting ? panelSideInset + 8.0 : floorToScreenPixels((layout.size.width - panelSize.width) / 2.0) + let backgroundOriginX: CGFloat = isSelecting ? panelSideInset : floorToScreenPixels((layout.size.width - panelSize.width) / 2.0) backgroundView.update(size: panelSize, cornerRadius: isSelecting ? 20.0 : glassPanelHeight * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: backgroundViewColor), transition: ComponentTransition(transition)) transition.updatePosition(layer: backgroundView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5)) transition.updateBounds(layer: backgroundView.layer, bounds: CGRect(origin: .zero, size: panelSize)) @@ -2021,14 +2021,19 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut) alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 1.0) - ComponentTransition.easeInOut(duration: 0.25).animateBlur(layer: self.scrollNode.layer, fromRadius: 0.0, toRadius: 10.0) + let componentTransition = ComponentTransition.easeInOut(duration: 0.25) + componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 0.0, toRadius: 10.0) - transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, from: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x + 62.0, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + let blurTransition = ComponentTransition.easeInOut(duration: 0.18) transition.animateTransformScale(node: textInputPanelNode.opaqueActionButtons, from: 0.01) - + blurTransition.animateBlur(layer: textInputPanelNode.opaqueActionButtons.layer, fromRadius: 4.0, toRadius: 0.0) + textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, from: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: CGPoint(x: 6.0, y: heightDelta)) transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: CGPoint(x: 64.0, y: heightDelta)) + textInputPanelNode.animateIn(transition: transition) } else { textInputPanelNode.alpha = 1.0 textInputPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) @@ -2048,10 +2053,15 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut) alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 0.0) - ComponentTransition.easeInOut(duration: 0.25).animateBlur(layer: self.scrollNode.layer, fromRadius: 10.0, toRadius: 0.0) + let componentTransition = ComponentTransition.easeInOut(duration: 0.25) + componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 10.0, toRadius: 0.0) - transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, to: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x + 62.0, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + let blurTransition = ComponentTransition.easeInOut(duration: 0.18) transition.animateTransformScale(layer: textInputPanelNode.opaqueActionButtons.layer, from: CGPoint(x: 1.0, y: 1.0), to: CGPoint(x: 0.01, y: 0.01)) + blurTransition.animateBlur(layer: textInputPanelNode.opaqueActionButtons.layer, fromRadius: 0.0, toRadius: 4.0) + textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, to: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: .zero, to: CGPoint(x: 6.0, y: heightDelta)) transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: .zero, to: CGPoint(x: 64.0, y: heightDelta)) } else { diff --git a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift index e7851d06bb..70cdea5871 100644 --- a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift @@ -382,7 +382,7 @@ public class ItemListFolderInviteLinkItemNode: ListViewItemNode, ItemListItemNod strongSelf.topStripeNode.backgroundColor = itemSeparatorColor strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor strongSelf.backgroundNode.backgroundColor = itemBackgroundColor - strongSelf.fieldNode.image = generateStretchableFilledCircleImage(diameter: 26.0, color: item.presentationData.theme.list.itemInputField.backgroundColor) + strongSelf.fieldNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: item.presentationData.theme.list.itemInputField.backgroundColor) strongSelf.addressButtonIconNode.image = actionButtonImage(color: item.presentationData.theme.list.itemInputField.controlColor) } diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD index a1eb569162..ac8046b628 100644 --- a/submodules/PremiumUI/BUILD +++ b/submodules/PremiumUI/BUILD @@ -122,6 +122,7 @@ swift_library( "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", "//submodules/TelegramUI/Components/Premium/PremiumCoinComponent", "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index c8f2b6b49e..4cdd3c3c78 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -36,6 +36,7 @@ import EmojiActionIconComponent import ScrollComponent import PremiumStarComponent import PremiumCoinComponent +import EdgeEffect public enum PremiumSource: Equatable { public static func == (lhs: PremiumSource, rhs: PremiumSource) -> Bool { @@ -2280,8 +2281,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) context.add(perksSection .position(CGPoint(x: availableWidth / 2.0, y: size.height + perksSection.size.height / 2.0)) - .clipsToBounds(true) - .cornerRadius(10.0) .disappear(.default(alpha: true)) ) size.height += perksSection.size.height @@ -2517,8 +2516,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) context.add(businessSection .position(CGPoint(x: availableWidth / 2.0, y: size.height + businessSection.size.height / 2.0)) - .clipsToBounds(true) - .cornerRadius(10.0) ) size.height += businessSection.size.height size.height += 23.0 @@ -2673,8 +2670,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) context.add(moreBusinessSection .position(CGPoint(x: availableWidth / 2.0, y: size.height + moreBusinessSection.size.height / 2.0)) - .clipsToBounds(true) - .cornerRadius(10.0) ) size.height += moreBusinessSection.size.height size.height += 23.0 @@ -2767,8 +2762,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) context.add(adsSettingsSection .position(CGPoint(x: availableWidth / 2.0, y: size.height + adsSettingsSection.size.height / 2.0)) - .clipsToBounds(true) - .cornerRadius(10.0) ) size.height += adsSettingsSection.size.height size.height += 23.0 @@ -3424,8 +3417,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let topSeparator = Child(Rectangle.self) let title = Child(MultilineTextComponent.self) let secondaryTitle = Child(MultilineTextWithEntitiesComponent.self) - let bottomPanel = Child(BlurredBackgroundComponent.self) - let bottomSeparator = Child(Rectangle.self) + let bottomEdgeEffect = Child(EdgeEffectComponent.self) let button = Child(SolidRoundedButtonComponent.self) var updatedInstalled: Bool? @@ -3685,9 +3677,10 @@ private final class PremiumIntroScreenComponent: CombinedComponent { transition: context.transition ) + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let bottomPanelPadding: CGFloat = 12.0 let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding - let bottomPanelHeight: CGFloat = state.isPremium == true && !state.canUpgrade ? bottomInset : bottomPanelPadding + 50.0 + bottomInset + let bottomPanelHeight: CGFloat = state.isPremium == true && !state.canUpgrade ? bottomInset : bottomPanelPadding + 52.0 + bottomInset let scrollContent = scrollContent.update( component: ScrollComponent( @@ -3837,7 +3830,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { } } - let buttonSideInset: CGFloat = 36.0 + + let controller = environment.controller let button = button.update( @@ -3857,6 +3851,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { height: 52.0, cornerRadius: 26.0, gloss: true, + glass: true, isLoading: state.inProgress, action: { if let controller = controller() as? PremiumIntroScreen, let customProceed = controller.customProceed { @@ -3867,66 +3862,42 @@ private final class PremiumIntroScreenComponent: CombinedComponent { } } ), - availableSize: CGSize(width: context.availableSize.width - buttonSideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 52.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right - environment.safeInsets.left - environment.safeInsets.right, height: 52.0), transition: context.transition) - let bottomPanel = bottomPanel.update( - component: BlurredBackgroundComponent( - color: environment.theme.rootController.tabBar.backgroundColor + let bottomEdgeEffectHeight = 13.0 + buttonInsets.bottom + button.size.height + let bottomEdgeEffect = bottomEdgeEffect.update( + component: EdgeEffectComponent( + color: environment.theme.list.blocksBackgroundColor, + blur: true, + alpha: 1.0, + size: CGSize(width: context.availableSize.width, height: bottomEdgeEffectHeight), + edge: .bottom, + edgeSize: bottomEdgeEffectHeight ), - availableSize: CGSize(width: context.availableSize.width, height: bottomPanelPadding + button.size.height + bottomInset), + availableSize: CGSize(width: context.availableSize.width, height: bottomEdgeEffectHeight), transition: context.transition ) - - let bottomSeparator = bottomSeparator.update( - component: Rectangle( - color: environment.theme.rootController.tabBar.separatorColor - ), - availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel), - transition: context.transition - ) - - let bottomPanelAlpha: CGFloat - if let bottomContentOffset = state.bottomContentOffset { - bottomPanelAlpha = min(16.0, bottomContentOffset) / 16.0 - } else { - bottomPanelAlpha = 1.0 - } - - context.add(bottomPanel - .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomPanel.size.height / 2.0)) - .opacity(bottomPanelAlpha) + context.add(bottomEdgeEffect + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomEdgeEffect.size.height / 2.0)) .disappear(ComponentTransition.Disappear { view, transition, completion in if case .none = transition.animation { completion() return } - view.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: bottomPanel.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in - completion() - }) - }) - ) - context.add(bottomSeparator - .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomPanel.size.height)) - .opacity(bottomPanelAlpha) - .disappear(ComponentTransition.Disappear { view, transition, completion in - if case .none = transition.animation { - completion() - return - } - view.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: bottomPanel.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in + view.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: bottomEdgeEffect.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in completion() }) }) ) context.add(button - .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - bottomPanel.size.height + bottomPanelPadding + button.size.height / 2.0)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - buttonInsets.bottom - button.size.height / 2.0)) .disappear(ComponentTransition.Disappear { view, transition, completion in if case .none = transition.animation { completion() return } - view.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: bottomPanel.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in + view.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: button.size.height + buttonInsets.bottom), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in completion() }) }) diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 028532c260..136bdfb94d 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -1535,7 +1535,7 @@ private class FooterNode: ASDisplayNode { self.backgroundNode = NavigationBackgroundNode(color: theme.rootController.tabBar.backgroundColor) self.separatorNode = ASDisplayNode() - self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 50.0, cornerRadius: 11.0, isShimmering: gloss) + self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 52.0, cornerRadius: 26.0, isShimmering: gloss) self.buttonNode.title = title self.coverNode = ASDisplayNode() @@ -1593,7 +1593,7 @@ private class FooterNode: ASDisplayNode { func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat { self.validLayout = layout - let buttonInset: CGFloat = 16.0 + let buttonInset: CGFloat = 30.0 let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0 let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition) let bottomPanelPadding: CGFloat = 12.0 @@ -1603,7 +1603,7 @@ private class FooterNode: ASDisplayNode { var buttonOffset: CGFloat = 20.0 if let order, order.count > 1 { panelHeight += 20.0 - buttonOffset += 20.0 + buttonOffset += 19.0 } let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight)) diff --git a/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift index ab4c36b81b..c04daa16b4 100644 --- a/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift @@ -15,6 +15,7 @@ import MultilineTextComponent import SolidRoundedButtonComponent import LottieComponent import AccountContext +import GlassBarButtonComponent private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -54,7 +55,6 @@ private final class SheetContent: CombinedComponent { } final class State: ComponentState { - var cachedCloseImage: (UIImage, PresentationTheme)? var cachedIconImage: UIImage? let playOnce = ActionSlot() @@ -95,7 +95,7 @@ private final class SheetContent: CombinedComponent { } static var body: Body { - let closeButton = Child(Button.self) + let closeButton = Child(GlassBarButtonComponent.self) let iconBackground = Child(Image.self) let icon = Child(LottieComponent.self) @@ -160,27 +160,28 @@ private final class SheetContent: CombinedComponent { let spacing: CGFloat = 8.0 var contentSize = CGSize(width: context.availableSize.width, height: 32.0) - - let closeImage: UIImage - if let (image, theme) = state.cachedCloseImage, theme === environment.theme { - closeImage = image - } else { - closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)! - state.cachedCloseImage = (closeImage, theme) - } - + let closeButton = closeButton.update( - component: Button( - content: AnyComponent(Image(image: closeImage)), - action: { [weak component] in - component?.dismiss() + component: GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + ) + )), + action: { _ in + component.dismiss() } ), - availableSize: CGSize(width: 30.0, height: 30.0), + availableSize: CGSize(width: 40.0, height: 40.0), transition: .immediate ) context.add(closeButton - .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0)) + .position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0)) ) let iconSize = CGSize(width: 90.0, height: 90.0) @@ -248,6 +249,7 @@ private final class SheetContent: CombinedComponent { contentSize.height += text.size.height contentSize.height += spacing + 5.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let actionButton = actionButton.update( component: SolidRoundedButtonComponent( title: buttonTitle, @@ -258,9 +260,10 @@ private final class SheetContent: CombinedComponent { ), font: .bold, fontSize: 17.0, - height: 50.0, - cornerRadius: 10.0, + height: 52.0, + cornerRadius: 26.0, gloss: false, + glass: true, iconName: nil, animationName: nil, iconPosition: .left, @@ -269,7 +272,7 @@ private final class SheetContent: CombinedComponent { component.dismiss() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(actionButton @@ -354,9 +357,10 @@ private final class SheetContent: CombinedComponent { ), font: .bold, fontSize: 17.0, - height: 50.0, - cornerRadius: 10.0, + height: 52.0, + cornerRadius: 26.0, gloss: false, + glass: true, iconName: nil, animationName: nil, iconPosition: .left, @@ -365,17 +369,15 @@ private final class SheetContent: CombinedComponent { component.dismiss() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(premiumButton .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + premiumButton.size.height / 2.0)) ) contentSize.height += premiumButton.size.height - contentSize.height += 14.0 + contentSize.height += buttonInsets.bottom - contentSize.height += environment.safeInsets.bottom - state.playAnimationIfNeeded() return contentSize @@ -446,6 +448,7 @@ private final class SheetContainerComponent: CombinedComponent { }) } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, externalState: sheetExternalState, diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift index 3d52740477..48db72cc9f 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import ComponentFlow +import SwiftSignalKit import TelegramCore import ViewControllerComponent import TelegramPresentationData @@ -1166,6 +1167,152 @@ private final class MenuComponent: Component { } } +private final class MenuButtonComponent: Component { + let theme: PresentationTheme + let text: String + let isSelected: Bool + let width: CGFloat? + let action: () -> Void + + init( + theme: PresentationTheme, + text: String, + isSelected: Bool, + width: CGFloat?, + action: @escaping () -> Void + ) { + self.theme = theme + self.text = text + self.isSelected = isSelected + self.width = width + self.action = action + } + + static func ==(lhs: MenuButtonComponent, rhs: MenuButtonComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.isSelected != rhs.isSelected { + return false + } + if lhs.width != rhs.width { + return false + } + return true + } + + final class View: UIView { + private var component: MenuButtonComponent? + private weak var componentState: EmptyComponentState? + + private let selectionLayer = SimpleLayer() + private let title = ComponentView() + private let icon = ComponentView() + private let button = HighlightTrackingButton() + + override init(frame: CGRect) { + super.init(frame: frame) + + self.layer.addSublayer(self.selectionLayer) + self.selectionLayer.masksToBounds = true + self.selectionLayer.opacity = 0.0 + + self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + + self.button.highligthedChanged = { [weak self] highlighted in + if let self { + if highlighted { + self.selectionLayer.opacity = 1.0 + self.selectionLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } else { + self.selectionLayer.opacity = 0.0 + self.selectionLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func buttonPressed() { + if let component = self.component { + component.action() + } + } + + func update(component: MenuButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.componentState = state + + let leftInset: CGFloat = 60.0 + let rightInset: CGFloat = 40.0 + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent( + Text(text: component.text, font: Font.regular(17.0), color: component.theme.contextMenu.primaryColor) + ), + environment: {}, + containerSize: availableSize + ) + let titleFrame = CGRect(origin: CGPoint(x: 60.0, y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + titleView.frame = titleFrame + } + + let size = CGSize(width: component.width ?? (leftInset + rightInset + titleSize.width), height: availableSize.height) + + if component.isSelected { + let iconSize = self.icon.update( + transition: .immediate, + component: AnyComponent( + BundleIconComponent( + name: "Media Gallery/Check", + tintColor: component.theme.contextMenu.primaryColor + ) + ), + environment: {}, + containerSize: CGSize(width: 44.0, height: 44.0) + ) + let iconFrame = CGRect(origin: CGPoint(x: 25.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) + if let iconView = self.icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + iconView.frame = iconFrame + } + } + + self.selectionLayer.backgroundColor = component.theme.contextMenu.itemHighlightedBackgroundColor.withMultipliedAlpha(0.5).cgColor + transition.setFrame(layer: self.selectionLayer, frame: CGRect(origin: .zero, size: size).insetBy(dx: 10.0, dy: 0.0)) + self.selectionLayer.cornerRadius = size.height / 2.0 + + if self.button.superview == nil { + self.addSubview(self.button) + } + self.button.frame = CGRect(origin: .zero, size: size) + + return size + } + } + + 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) + } +} + private final class RepeatMenuComponent: Component { let theme: PresentationTheme let strings: PresentationStrings @@ -1202,7 +1349,6 @@ private final class RepeatMenuComponent: Component { private let never = ComponentView() private let separator = SimpleLayer() private var itemViews: [Int32: ComponentView] = [:] - private let checkIcon = UIImageView() private var component: RepeatMenuComponent? @@ -1216,9 +1362,10 @@ private final class RepeatMenuComponent: Component { 365 * 86400 ] + private var width: CGFloat? + public override init(frame: CGRect) { self.backgroundView = GlassBackgroundView() - self.checkIcon.image = UIImage(bundleImageName: "Media Gallery/Check")?.withRenderingMode(.alwaysTemplate) super.init(frame: frame) @@ -1234,19 +1381,16 @@ private final class RepeatMenuComponent: Component { self.component = component let sideInset: CGFloat = 18.0 - let itemInset: CGFloat = 60.0 + let itemHeight: CGFloat = 40.0 - let textColor = component.theme.contextMenu.primaryColor - - self.checkIcon.tintColor = textColor - let neverSize = self.never.update( transition: transition, component: AnyComponent( - PlainButtonComponent( - content: AnyComponent( - Text(text: component.strings.ScheduleMessage_RepeatPeriod_Never, font: Font.regular(17.0), color: textColor) - ), + MenuButtonComponent( + theme: component.theme, + text: component.strings.ScheduleMessage_RepeatPeriod_Never, + isSelected: component.value == nil, + width: self.width, action: { [weak self] in guard let self else { return @@ -1256,22 +1400,18 @@ private final class RepeatMenuComponent: Component { ) ), environment: {}, - containerSize: availableSize + containerSize: CGSize(width: availableSize.width, height: itemHeight) ) - let neverFrame = CGRect(origin: CGPoint(x: itemInset, y: 21.0), size: neverSize) + let neverFrame = CGRect(origin: CGPoint(x: 0.0, y: 12.0), size: neverSize) if let neverView = self.never.view { if neverView.superview == nil { self.addSubview(neverView) } transition.setFrame(view: neverView, frame: neverFrame) - - if component.value == nil { - neverView.addSubview(self.checkIcon) - } } var maxWidth: CGFloat = 0.0 - var originY: CGFloat = 83.0 + var originY: CGFloat = 72.0 for value in self.values { let itemView: ComponentView if let current = self.itemViews[value] { @@ -1304,10 +1444,11 @@ private final class RepeatMenuComponent: Component { let itemSize = itemView.update( transition: transition, component: AnyComponent( - PlainButtonComponent( - content: AnyComponent( - Text(text: repeatString, font: Font.regular(17.0), color: textColor) - ), + MenuButtonComponent( + theme: component.theme, + text: repeatString, + isSelected: component.value == value, + width: self.width, action: { [weak self] in guard let self else { return @@ -1317,31 +1458,30 @@ private final class RepeatMenuComponent: Component { ) ), environment: {}, - containerSize: availableSize + containerSize: CGSize(width: availableSize.width, height: itemHeight) ) maxWidth = max(maxWidth, itemSize.width) - let itemFrame = CGRect(origin: CGPoint(x: itemInset, y: originY), size: itemSize) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: originY), size: itemSize) if let itemView = itemView.view { if itemView.superview == nil { self.addSubview(itemView) } transition.setFrame(view: itemView, frame: itemFrame) - - if component.value == value { - itemView.addSubview(self.checkIcon) - } } - originY += 42.0 + originY += 40.0 } - if let image = self.checkIcon.image { - self.checkIcon.frame = CGRect(origin: CGPoint(x: -35.0, y: floorToScreenPixels((12.0 - image.size.height) / 2.0) + 5.0), size: image.size) - } + let size = CGSize(width: maxWidth, height: originY + 8.0) - let size = CGSize(width: itemInset + maxWidth + 40.0, height: originY) - - self.separator.backgroundColor = textColor.withMultipliedAlpha(0.6).cgColor + self.separator.backgroundColor = component.theme.contextMenu.primaryColor.withMultipliedAlpha(0.5).cgColor self.separator.frame = CGRect(origin: CGPoint(x: sideInset, y: 62.0), size: CGSize(width: size.width - sideInset * 2.0, height: UIScreenPixel)) + + if self.width == nil { + self.width = maxWidth + Queue.mainQueue().justDispatch { + state.updated() + } + } return size } diff --git a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift index 9487bd960f..e48426b0ca 100644 --- a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift +++ b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift @@ -3,7 +3,7 @@ import UIKit import Display import ComponentFlow -public final class EdgeEffectView: UIView { +public class EdgeEffectView: UIView { public enum Edge { case top case bottom @@ -121,6 +121,69 @@ public final class EdgeEffectView: UIView { } } +public final class EdgeEffectComponent: Component { + private let color: UIColor + private let blur: Bool + private let alpha: CGFloat + private let size: CGSize + private let edge: EdgeEffectView.Edge + private let edgeSize: CGFloat + + public init( + color: UIColor, + blur: Bool, + alpha: CGFloat, + size: CGSize, + edge: EdgeEffectView.Edge, + edgeSize: CGFloat + ) { + self.color = color + self.blur = blur + self.alpha = alpha + self.size = size + self.edge = edge + self.edgeSize = edgeSize + } + + public static func == (lhs: EdgeEffectComponent, rhs: EdgeEffectComponent) -> Bool { + if lhs.color != rhs.color { + return false + } + if lhs.blur != rhs.blur { + return false + } + if lhs.alpha != rhs.alpha { + return false + } + if lhs.size != rhs.size { + return false + } + if lhs.edge != rhs.edge { + return false + } + if lhs.edgeSize != rhs.edgeSize { + return false + } + return true + } + + public final class View: EdgeEffectView { + func update(component: EdgeEffectComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.update(content: component.color, blur: component.blur, alpha: component.alpha, rect: CGRect(origin: .zero, size: component.size), edge: component.edge, edgeSize: component.edgeSize, transition: transition) + + return component.size + } + } + + public func makeView() -> View { + return View() + } + + 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) + } +} + public final class VariableBlurView: UIVisualEffectView { public let maxBlurRadius: CGFloat diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 9306999fb1..2a2e879880 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -4259,7 +4259,7 @@ private final class GiftViewSheetContent: CombinedComponent { transition: context.transition ) context.add(priceButton - .position(CGPoint(x: environment.safeInsets.left + 16.0 + priceButton.size.width / 2.0, y: 28.0)) + .position(CGPoint(x: environment.safeInsets.left + 16.0 + priceButton.size.width / 2.0, y: 31.0)) .appear(.default(scale: true, alpha: true)) .disappear(.default(scale: true, alpha: true)) ) @@ -4377,8 +4377,10 @@ private final class GiftViewSheetContent: CombinedComponent { originY += 16.0 } - let buttonSize = CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0) + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) + let buttonSize = CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0) let buttonBackground = ButtonComponent.Background( + style: .glass, color: theme.list.itemCheckColors.fillColor, foreground: theme.list.itemCheckColors.foregroundColor, pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) @@ -4860,7 +4862,7 @@ private final class GiftViewSheetContent: CombinedComponent { transition: context.transition ) } - let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: buttonChild.size) + let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: originY), size: buttonChild.size) var buttonAlpha: CGFloat = 1.0 if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift, !state.canSkip { @@ -4903,7 +4905,6 @@ private final class GiftViewSheetContent: CombinedComponent { context.add(buttonChild .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) - .cornerRadius(10.0) .opacity(buttonAlpha) ) originY += buttonChild.size.height @@ -4943,7 +4944,7 @@ private final class GiftViewSheetContent: CombinedComponent { } context.add(buttons - .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - 16.0 - buttons.size.width / 2.0, y: 28.0)) + .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - 16.0 - buttons.size.width / 2.0, y: 31.0)) ) let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom @@ -5035,6 +5036,7 @@ final class GiftViewSheetComponent: CombinedComponent { getController: controller )), headerContent: headerContent, + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, clipsContent: true, @@ -6056,7 +6058,7 @@ private final class HeaderButtonComponent: CombinedComponent { let background = background.update( component: RoundedRectangle( color: UIColor.white.withAlphaComponent(0.16), - cornerRadius: 10.0 + cornerRadius: 16.0 ), availableSize: context.availableSize, transition: .immediate diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 6a9f8d871e..71d2a6fedd 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -7641,7 +7641,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } let imagesReady = ValuePromise(false, ignoreRepeated: true) Queue.concurrentDefaultQueue().async { - if !isVideo, let data = try? WebP.convert(toWebP: image, quality: 97.0) { + if !isVideo, let data = try? WebP.convert(toWebP: image, quality: 90.0) { self.context.account.postbox.mediaBox.storeResourceData(isVideo ? thumbnailResource.id : resource.id, data: data, synchronous: true) } if let thumbnailImage = generateScaledImage(image: image, size: CGSize(width: 320.0, height: 320.0), opaque: false, scale: 1.0), let data = try? WebP.convert(toWebP: thumbnailImage, quality: 90.0) { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift index 02374ccfa0..c67a082d94 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift @@ -109,7 +109,7 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode { let theme = presentationData.theme if isExtracted { - strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor) + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: theme.list.plainBackgroundColor) } if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift index 0090264bb0..bd56a1ee54 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift @@ -220,7 +220,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode } if isExtracted { - strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor) + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: theme.list.plainBackgroundColor) } if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift index 1d6c028f04..a8313a54f2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenContactInfoItem.swift @@ -151,7 +151,7 @@ private final class PeerInfoScreenContactInfoItemNode: PeerInfoScreenItemNode { } if isExtracted { - strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor) + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: theme.list.plainBackgroundColor) } if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift index 6d3e856c62..71615c96c2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -331,7 +331,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { } if isExtracted { - strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor) + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: theme.list.plainBackgroundColor) } if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift index 4791187924..815b7bfaa2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift @@ -372,7 +372,7 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod } if isExtracted { - strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor) + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 52.0, color: theme.list.plainBackgroundColor) } if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift index 9c8a96680c..fefd6d83b0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderButtonNode.swift @@ -272,7 +272,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode { transition.updateSublayerTransformScale(node: self.contentNode, scale: 1.0 * fraction + 0.001 * (1.0 - fraction)) - transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: min(11.0, backgroundFrame.height * 0.5)) + transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: min(16.0, backgroundFrame.height * 0.5)) //self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: min(11.0, backgroundFrame.height * 0.5), transition: transition) //self.backgroundNode.updateColor(color: backgroundColor, transition: transition) transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: 1.0), size: iconSize)) diff --git a/submodules/TelegramUI/Components/Stars/StarsIntroScreen/Sources/StarsIntroScreen.swift b/submodules/TelegramUI/Components/Stars/StarsIntroScreen/Sources/StarsIntroScreen.swift index 3bcf7b4b6b..0c34a23f78 100644 --- a/submodules/TelegramUI/Components/Stars/StarsIntroScreen/Sources/StarsIntroScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsIntroScreen/Sources/StarsIntroScreen.swift @@ -194,16 +194,14 @@ private final class SheetContent: CombinedComponent { contentSize.height += list.size.height contentSize.height += spacing - let buttonHeight: CGFloat = 50.0 - let bottomPanelPadding: CGFloat = 12.0 - let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding - - contentSize.height += bottomPanelPadding - + let buttonHeight: CGFloat = 52.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) + let controller = environment.controller() as? StarsIntroScreen let actionButton = actionButton.update( component: ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: environment.theme.list.itemCheckColors.fillColor, foreground: environment.theme.list.itemCheckColors.foregroundColor, pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) @@ -217,14 +215,14 @@ private final class SheetContent: CombinedComponent { controller?.dismissAnimated() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: buttonHeight), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: buttonHeight), transition: context.transition ) context.add(actionButton .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0)) - .cornerRadius(10.0) ) - contentSize.height += actionButton.size.height + bottomInset + contentSize.height += actionButton.size.height + contentSize.height += buttonInsets.bottom return contentSize } @@ -279,6 +277,7 @@ private final class ContainerComponent: CombinedComponent { controller()?.dismiss() } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, clipsContent: true, diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index 6f98ee3a97..e0b9a853aa 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -1603,36 +1603,16 @@ private final class StarsTransactionSheetContent: CombinedComponent { if let cancelButtonText { let cancelButton = cancelButton.update( -// component: SolidRoundedButtonComponent( -// title: cancelButtonText, -// theme: SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: linkColor), -// font: .regular, -// fontSize: 17.0, -// height: 50.0, -// cornerRadius: 10.0, -// gloss: false, -// iconName: nil, -// animationName: nil, -// iconPosition: .left, -// isLoading: state.inProgress, -// action: { -// component.cancel(true) -// if isSubscription { -// component.updateSubscription() -// } -// } -// ), component: ButtonComponent( background: ButtonComponent.Background( style: .glass, - color: theme.list.itemCheckColors.fillColor, - foreground: theme.list.itemCheckColors.foregroundColor, - pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), - cornerRadius: 10.0, + color: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.1), + foreground: theme.list.itemCheckColors.fillColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8), ), content: AnyComponentWithIdentity( id: AnyHashable(0), - component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: cancelButtonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: cancelButtonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.fillColor, paragraphAlignment: .center)))) ), isEnabled: true, displaysProgress: state.inProgress, @@ -1662,7 +1642,6 @@ private final class StarsTransactionSheetContent: CombinedComponent { color: theme.list.itemCheckColors.fillColor, foreground: theme.list.itemCheckColors.foregroundColor, pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), - cornerRadius: 10.0, ), content: AnyComponentWithIdentity( id: AnyHashable(0), @@ -2187,6 +2166,7 @@ private final class TableComponent: CombinedComponent { } final class State: ComponentState { + var cachedLeftColumnImage: (UIImage, PresentationTheme)? var cachedBorderImage: (UIImage, PresentationTheme)? } @@ -2195,7 +2175,7 @@ private final class TableComponent: CombinedComponent { } public static var body: Body { - let leftColumnBackground = Child(Rectangle.self) + let leftColumnBackground = Child(Image.self) let verticalBorder = Child(Rectangle.self) let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) @@ -2206,9 +2186,11 @@ private final class TableComponent: CombinedComponent { let verticalPadding: CGFloat = 11.0 let horizontalPadding: CGFloat = 12.0 let borderWidth: CGFloat = 1.0 + let borderRadius: CGFloat = 14.0 let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6) + let secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor var leftColumnWidth: CGFloat = 0.0 @@ -2270,8 +2252,24 @@ private final class TableComponent: CombinedComponent { i += 1 } + let leftColumnImage: UIImage + if let (currentImage, theme) = context.state.cachedLeftColumnImage, theme === context.component.theme { + leftColumnImage = currentImage + } else { + leftColumnImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in + let bounds = CGRect(origin: .zero, size: CGSize(width: size.width + borderRadius, height: size.height)) + context.clear(bounds) + + let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) + context.setFillColor(secondaryBackgroundColor.cgColor) + context.addPath(path) + context.fillPath() + })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) + context.state.cachedLeftColumnImage = (leftColumnImage, context.component.theme) + } + let leftColumnBackground = leftColumnBackground.update( - component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor), + component: Image(image: leftColumnImage), availableSize: CGSize(width: leftColumnWidth, height: totalHeight), transition: context.transition ) @@ -2284,11 +2282,9 @@ private final class TableComponent: CombinedComponent { if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme { borderImage = currentImage } else { - let borderRadius: CGFloat = 5.0 - borderImage = generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in + borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in let bounds = CGRect(origin: .zero, size: size) - context.setFillColor(backgroundColor.cgColor) - context.fill(bounds) + context.clear(bounds) let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) context.setBlendMode(.clear) @@ -2300,7 +2296,7 @@ private final class TableComponent: CombinedComponent { context.setLineWidth(borderWidth) context.addPath(path) context.strokePath() - })!.stretchableImage(withLeftCapWidth: 5, topCapHeight: 5) + })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) context.state.cachedBorderImage = (borderImage, context.component.theme) } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index e8957414d3..050dccbccf 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -663,8 +663,6 @@ private final class SheetContent: CombinedComponent { transition: .immediate ) context.add(button - .clipsToBounds(true) - .cornerRadius(10.0) .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) ) contentSize.height += button.size.height