From 3e50a2dd56070bd7064302d6bd806e7fc57633ba Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 24 Oct 2025 23:06:10 +0400 Subject: [PATCH] Various improvements --- submodules/Camera/Sources/Camera.swift | 2 +- .../Sources/ComposePollScreen.swift | 20 -------- submodules/Display/Source/SwitchNode.swift | 8 ++- .../Sources/Items/VideoAdComponent.swift | 18 ++++--- .../Sources/ComposeTodoScreen.swift | 28 ----------- .../MetalResources/EditorOutline.metal | 49 ------------------- .../Sources/SearchInputPanelComponent.swift | 12 +++-- .../Chat/ChatControllerOpenWebApp.swift | 46 ++++++++++++----- .../WebUI/Sources/WebAppController.swift | 16 +++++- 9 files changed, 76 insertions(+), 123 deletions(-) delete mode 100644 submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorOutline.metal diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index f2d5eeb60f..2312b8353a 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -102,7 +102,7 @@ final class CameraDeviceContext { return 30.0 } switch DeviceModel.current { - case .iPhone15ProMax, .iPhone14ProMax, .iPhone13ProMax, .iPhone16ProMax: + case .iPhone15ProMax, .iPhone14ProMax, .iPhone13ProMax, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax: return 60.0 default: return 30.0 diff --git a/submodules/ComposePollUI/Sources/ComposePollScreen.swift b/submodules/ComposePollUI/Sources/ComposePollScreen.swift index f77a496c6e..90fdff3d04 100644 --- a/submodules/ComposePollUI/Sources/ComposePollScreen.swift +++ b/submodules/ComposePollUI/Sources/ComposePollScreen.swift @@ -132,7 +132,6 @@ final class ComposePollScreenComponent: Component { private var reactionSelectionControl: ComponentView? private var isUpdating: Bool = false - private var ignoreScrolling: Bool = false private var previousHadInputHeight: Bool = false private var component: ComposePollScreenComponent? @@ -467,21 +466,6 @@ final class ComposePollScreenComponent: Component { self.endEditing(true) } - func scrollViewDidScroll(_ scrollView: UIScrollView) { - if !self.ignoreScrolling { - self.updateScrolling(transition: .immediate) - } - } - - private func updateScrolling(transition: ComponentTransition) { - let navigationAlphaDistance: CGFloat = 16.0 - let navigationAlpha: CGFloat = max(0.0, min(1.0, self.scrollView.contentOffset.y / navigationAlphaDistance)) - if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { - transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha) - transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha) - } - } - func isPanGestureEnabled() -> Bool { if self.inputMediaNode != nil { return false @@ -1627,7 +1611,6 @@ final class ComposePollScreenComponent: Component { } self.previousHadInputHeight = (inputHeight > 0.0) - self.ignoreScrolling = true let previousBounds = self.scrollView.bounds let contentSize = CGSize(width: availableSize.width, height: contentHeight) if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) { @@ -1762,9 +1745,6 @@ final class ComposePollScreenComponent: Component { transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true) } } - self.ignoreScrolling = false - - self.updateScrolling(transition: transition) if isEditing { if let controller = environment.controller() as? ComposePollScreen { diff --git a/submodules/Display/Source/SwitchNode.swift b/submodules/Display/Source/SwitchNode.swift index 532727cbeb..1204a3a585 100644 --- a/submodules/Display/Source/SwitchNode.swift +++ b/submodules/Display/Source/SwitchNode.swift @@ -23,7 +23,9 @@ open class SwitchNode: ASDisplayNode { public var frameColor = UIColor(rgb: 0xe0e0e0) { didSet { if self.isNodeLoaded { - (self.view as! UISwitch).tintColor = self.frameColor + if oldValue != self.frameColor { + (self.view as! UISwitch).tintColor = self.frameColor + } } } } @@ -37,7 +39,9 @@ open class SwitchNode: ASDisplayNode { public var contentColor = UIColor(rgb: 0x42d451) { didSet { if self.isNodeLoaded { - (self.view as! UISwitch).onTintColor = self.contentColor + if oldValue != self.contentColor { + (self.view as! UISwitch).onTintColor = self.contentColor + } } } } diff --git a/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift b/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift index 0f47b793bc..5bdb216175 100644 --- a/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift +++ b/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift @@ -129,8 +129,17 @@ final class VideoAdComponent: Component { self.imageNode.setSignal(signal) } + let color = UIColor(rgb: 0x64d2ff) + if self.adIcon == nil { + self.adIcon = generateAdIcon(color: color, strings: component.strings) + } + let leftInset: CGFloat = media != nil ? 51.0 : 16.0 let rightInset: CGFloat = 60.0 + var titleRightInset: CGFloat = 0.0 + if let adIcon = self.adIcon { + titleRightInset += adIcon.size.width + 16.0 + } let titleSize = self.title.update( transition: .immediate, @@ -138,7 +147,7 @@ final class VideoAdComponent: Component { MultilineTextComponent(text: .plain(NSAttributedString(string: titleString, font: Font.semibold(14.0), textColor: .white))) ), environment: {}, - containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: availableSize.height) + containerSize: CGSize(width: availableSize.width - leftInset - rightInset - titleRightInset, height: availableSize.height) ) let textColor = UIColor.white @@ -193,12 +202,7 @@ final class VideoAdComponent: Component { } textView.frame = textFrame } - - let color = UIColor(rgb: 0x64d2ff) - if self.adIcon == nil { - self.adIcon = generateAdIcon(color: color, strings: component.strings) - } - + let buttonSize = self.button.update( transition: .immediate, component: AnyComponent( diff --git a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift index 258b4baa9a..bac2dae08b 100644 --- a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift +++ b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift @@ -85,7 +85,6 @@ final class ComposeTodoScreenComponent: Component { private let doneButton = ComponentView() private var isUpdating: Bool = false - private var ignoreScrolling: Bool = false private var previousHadInputHeight: Bool = false private var component: ComposeTodoScreenComponent? @@ -381,25 +380,10 @@ final class ComposeTodoScreenComponent: Component { return true } - func scrollViewDidScroll(_ scrollView: UIScrollView) { - if !self.ignoreScrolling { - self.updateScrolling(transition: .immediate) - } - } - func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { self.endEditing(true) } - private func updateScrolling(transition: ComponentTransition) { - let navigationAlphaDistance: CGFloat = 16.0 - let navigationAlpha: CGFloat = max(0.0, min(1.0, self.scrollView.contentOffset.y / navigationAlphaDistance)) - if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { - transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha) - transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha) - } - } - func isPanGestureEnabled() -> Bool { if self.inputMediaNode != nil { return false @@ -1511,7 +1495,6 @@ final class ComposeTodoScreenComponent: Component { } self.previousHadInputHeight = (inputHeight > 0.0) - self.ignoreScrolling = true let previousBounds = self.scrollView.bounds let contentSize = CGSize(width: availableSize.width, height: contentHeight) if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) { @@ -1548,9 +1531,6 @@ final class ComposeTodoScreenComponent: Component { transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true) } } - self.ignoreScrolling = false - - self.updateScrolling(transition: transition) if isEditing { if let controller = environment.controller() as? ComposeTodoScreen { @@ -1662,14 +1642,6 @@ final class ComposeTodoScreenComponent: Component { self.addSubview(doneButtonView) } transition.setFrame(view: doneButtonView, frame: doneButtonFrame) - - if isValid { - doneButtonView.layer.filters = [] - } else { - if (doneButtonView.layer.filters ?? []).isEmpty, let monochrome = CALayer.monochrome() { - doneButtonView.layer.filters = [monochrome] - } - } } if let currentEditingTag = self.currentEditingTag, previousEditingTag !== currentEditingTag, self.currentInputMode != .keyboard { diff --git a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorOutline.metal b/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorOutline.metal deleted file mode 100644 index 49cc4e835f..0000000000 --- a/submodules/TelegramUI/Components/MediaEditor/MetalResources/EditorOutline.metal +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include "EditorCommon.h" -#include "EditorUtils.h" - -using namespace metal; - -kernel void morphologyMaximumFilter(texture2d inputTexture [[texture(0)]], - texture2d outputTexture [[texture(1)]], - constant float& radius [[buffer(0)]], - uint2 gid [[thread_position_in_grid]]) { - uint2 size = uint2(inputTexture.get_width(), inputTexture.get_height()); - uint2 pos = gid; - - float maxIntensity = 0.0; - int kernelRadius = int(radius); - - for (int y = -kernelRadius; y <= kernelRadius; ++y) { - for (int x = -kernelRadius; x <= kernelRadius; ++x) { - uint2 samplePos = pos + uint2(x, y); - - if (samplePos.x >= 0 && samplePos.y >= 0 && samplePos.x < size.x && samplePos.y < size.y) { - float intensity = inputTexture.read(samplePos).a; - if (intensity > maxIntensity) { - maxIntensity = intensity; - } - } - } - } - outputTexture.write(maxIntensity, gid); -} - -fragment half4 stickerOutlineFragmentShader(RasterizerData in [[stage_in]], - texture2d sourceTexture [[texture(0)]], - texture2d maskTexture [[texture(1)]] - ) -{ - constexpr sampler colorSampler(min_filter::linear, mag_filter::linear, address::clamp_to_zero); - constexpr sampler maskSampler(min_filter::linear, mag_filter::linear, address::clamp_to_zero); - - half4 color = sourceTexture.sample(colorSampler, in.texCoord); - half intensity = maskTexture.sample(maskSampler, in.texCoord).r; - - half4 result = half4(intensity, intensity, intensity, max(color.a, intensity)); - result.r = mix(result.r, color.r, color.a); - result.g = mix(result.g, color.g, color.a); - result.b = mix(result.b, color.b, color.a); - - return result; -} diff --git a/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift b/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift index 824d55d56c..53daccb590 100644 --- a/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift @@ -85,6 +85,7 @@ public final class SearchInputPanelComponent: Component { public final class View: UIView, UITextFieldDelegate { private let edgeEffectView: EdgeEffectView + private let containerView: GlassBackgroundContainerView private let backgroundView: GlassBackgroundView private let icon = ComponentView() @@ -106,13 +107,15 @@ public final class SearchInputPanelComponent: Component { override init(frame: CGRect) { self.edgeEffectView = EdgeEffectView() + self.containerView = GlassBackgroundContainerView() self.backgroundView = GlassBackgroundView() self.textField = TextField() super.init(frame: frame) self.addSubview(self.edgeEffectView) - self.addSubview(self.backgroundView) + self.addSubview(self.containerView) + self.containerView.contentView.addSubview(self.backgroundView) } required init?(coder: NSCoder) { @@ -196,7 +199,7 @@ public final class SearchInputPanelComponent: Component { let fieldFrame = CGRect(origin: CGPoint(x: edgeInsets.left, y: edgeInsets.top), size: CGSize(width: availableSize.width - edgeInsets.left - edgeInsets.right - fieldHeight - buttonSpacing, height: fieldHeight)) let cancelButtonFrame = CGRect(origin: CGPoint(x: edgeInsets.left + fieldFrame.width + buttonSpacing, y: edgeInsets.top), size: CGSize(width: fieldHeight, height: fieldHeight)) - self.backgroundView.update(size: fieldFrame.size, cornerRadius: fieldFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: backgroundColor), transition: transition) + self.backgroundView.update(size: fieldFrame.size, cornerRadius: fieldFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: backgroundColor), isInteractive: true, transition: transition) transition.setFrame(view: self.backgroundView, frame: fieldFrame) let fieldSideInset: CGFloat = 41.0 @@ -299,7 +302,7 @@ public final class SearchInputPanelComponent: Component { ) if let cancelButtonView = self.cancelButton.view { if cancelButtonView.superview == nil { - self.addSubview(cancelButtonView) + self.containerView.contentView.addSubview(cancelButtonView) } transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame) } @@ -312,6 +315,9 @@ public final class SearchInputPanelComponent: Component { let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight + 30.0), size: CGSize(width: size.width, height: edgeEffectHeight)) transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) self.edgeEffectView.update(content: edgeColor, blur: true, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition) + + transition.setFrame(view: self.containerView, frame: CGRect(origin: .zero, size: size)) + self.containerView.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition) return size } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index c0b5ced09e..0690ff8f3d 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -42,6 +42,16 @@ func openWebAppImpl( presentationData = context.sharedContext.currentPresentationData.with({ $0 }) } + var skipTermsOfService = skipTermsOfService + if let whiteListedBots = context.currentAppConfiguration.with({ $0 }).data?["whitelisted_bots"] as? [Double] { + let botId = botPeer.id.id._internalGetInt64Value() + for bot in whiteListedBots { + if Int64(bot) == botId { + skipTermsOfService = true + } + } + } + let botName: String let botAddress: String let botVerified: Bool @@ -529,6 +539,16 @@ public extension ChatControllerImpl { } else { peerId = botPeer.id } + + var skipTermsOfService = false + if let whiteListedBots = context.currentAppConfiguration.with({ $0 }).data?["whitelisted_bots"] as? [Double] { + let botId = botPeer.id.id._internalGetInt64Value() + for bot in whiteListedBots { + if Int64(bot) == botId { + skipTermsOfService = true + } + } + } chatController?.attachmentController?.dismiss(animated: true, completion: nil) @@ -657,17 +677,21 @@ public extension ChatControllerImpl { openBotApp(false, false, appSettings) } } else { - let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, requestWriteAccess: botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in - let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone() - openBotApp(allowWrite, false, appSettings) - }, showMore: chatController == nil ? nil : { [weak chatController] in - if let chatController { - chatController.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil) - } - }, openTerms: { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: presentationData, navigationController: parentController?.navigationController as? NavigationController, dismissInput: {}) - }) - parentController?.present(controller, in: .window(.root)) + if skipTermsOfService { + openBotApp(true, false, appSettings) + } else { + let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, requestWriteAccess: botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in + let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone() + openBotApp(allowWrite, false, appSettings) + }, showMore: chatController == nil ? nil : { [weak chatController] in + if let chatController { + chatController.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil) + } + }, openTerms: { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: presentationData, navigationController: parentController?.navigationController as? NavigationController, dismissInput: {}) + }) + parentController?.present(controller, in: .window(.root)) + } } } else { openBotApp(false, false, appSettings) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index bb20434165..2219c4e942 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1378,14 +1378,14 @@ public final class WebAppController: ViewController, AttachmentContainable { let _ = (self.context.engine.messages.attachMenuBots() |> take(1) |> deliverOnMainQueue).startStandalone(next: { [weak self] attachMenuBots in - guard let self else { + guard let self, let controller = self.controller else { return } let currentTimestamp = CACurrentMediaTime() var fillData = false let attachMenuBot = attachMenuBots.first(where: { $0.peer.id == botId && !$0.flags.contains(.notActivated) }) - if isAttachMenu || attachMenuBot != nil { + if isAttachMenu || attachMenuBot != nil || controller.isWhiteListedBot { if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 { self.webView?.lastTouchTimestamp = nil fillData = true @@ -3492,6 +3492,18 @@ public final class WebAppController: ViewController, AttachmentContainable { return false } + private var isWhiteListedBot: Bool { + if let whiteListedBots = self.context.currentAppConfiguration.with({ $0 }).data?["whitelisted_bots"] as? [Double] { + let botId = self.botId.id._internalGetInt64Value() + for bot in whiteListedBots { + if Int64(bot) == botId { + return true + } + } + } + return false + } + public func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) { switch self.source { case .generic, .settings: