From 8005726b3da8a9821729144422c2ccbfc5f70b5f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 5 Feb 2026 14:58:39 +0800 Subject: [PATCH 01/13] Experiment --- submodules/BrowserUI/BUILD | 1 + .../BrowserUI/Sources/BrowserWebContent.swift | 40 +++++- .../Sources/DebugController.swift | 42 +++++-- .../Sources/LegacyBuffer.swift | 15 ++- .../Sources/ExperimentalUISettings.swift | 10 +- submodules/UIKitRuntimeUtils/BUILD | 5 +- .../UIViewController+Navigation.h | 8 ++ .../UIViewController+Navigation.m | 118 ++++++++++++++++++ submodules/WebUI/Sources/WebAppWebView.swift | 8 +- 9 files changed, 216 insertions(+), 31 deletions(-) diff --git a/submodules/BrowserUI/BUILD b/submodules/BrowserUI/BUILD index 472c755041..fc57ca47eb 100644 --- a/submodules/BrowserUI/BUILD +++ b/submodules/BrowserUI/BUILD @@ -57,6 +57,7 @@ swift_library( "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/TelegramUI/Components/SearchInputPanelComponent", "//submodules/TelegramUI/Components/GlassControls", + "//submodules/UIKitRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 617ed64ed7..9bb574d5f7 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -24,6 +24,7 @@ import DeviceModel import LegacyMediaPickerUI import PassKit import AlertComponent +import UIKitRuntimeUtils private final class TonSchemeHandler: NSObject, WKURLSchemeHandler { private final class PendingTask { @@ -217,6 +218,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU var cancelInteractiveTransitionGestures: () -> Void = {} private var tempFile: TempBoxFile? + private var disposeTrustedDomain: (() -> Void)? init(context: AccountContext, presentationData: PresentationData, url: String, preferredConfiguration: WKWebViewConfiguration? = nil) { self.context = context @@ -264,6 +266,30 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU configuration.userContentController = contentController configuration.applicationNameForUserAgent = computedUserAgent() } + + if context.sharedContext.immediateExperimentalUISettings.enablePWA { + if #available(iOS 17.0, *) { + if let parsedUrl = URL(string: url), let host = parsedUrl.host { + let rootPath = context.sharedContext.applicationBindings.containerPath + "/telegram-data" + let pwaPath = rootPath + "/pwa" + let uuidPath = pwaPath + "/uuid_\(host)" + let uuid: UUID + if let value = try? String(contentsOf: URL(fileURLWithPath: uuidPath), encoding: .utf8) { + uuid = UUID(uuidString: value)! + } else { + uuid = UUID() + let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: pwaPath), withIntermediateDirectories: true) + let _ = try? uuid.uuidString.write(to: URL(fileURLWithPath: uuidPath), atomically: true, encoding: .utf8) + } + + configuration.websiteDataStore = WKWebsiteDataStore(forIdentifier: uuid) + + configuration.limitsNavigationsToAppBoundDomains = true + disposeTrustedDomain = WebHelpers.addTrustedDomain(host) + WebHelpers.forceRefreshTrustedDomains(configuration.websiteDataStore) + } + } + } self.webView = WebView(frame: CGRect(), configuration: configuration) self.webView.allowsLinkPreview = true @@ -273,18 +299,16 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU } var title: String = "" + var request: URLRequest? if url.hasPrefix("file://") { var updatedPath = url let tempFile = TempBox.shared.file(path: url.replacingOccurrences(of: "file://", with: ""), fileName: "file.xlsx") updatedPath = tempFile.path self.tempFile = tempFile - let request = URLRequest(url: URL(fileURLWithPath: updatedPath)) - self.webView.load(request) + request = URLRequest(url: URL(fileURLWithPath: updatedPath)) } else if let parsedUrl = URL(string: url) { - let request = URLRequest(url: parsedUrl) - self.webView.load(request) - + request = URLRequest(url: parsedUrl) title = getDisplayUrl(url, hostOnly: true) } @@ -299,6 +323,10 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU self.webView.backgroundColor = presentationData.theme.list.plainBackgroundColor self.webView.alpha = 0.0 + if let request { + self.webView.load(request) + } + self.webView.allowsBackForwardNavigationGestures = true self.webView.scrollView.delegate = self self.webView.scrollView.clipsToBounds = false @@ -360,6 +388,8 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU self.faviconDisposable.dispose() self.instantPageDisposable.dispose() + + self.disposeTrustedDomain?() } private func handleScriptMessage(_ message: WKScriptMessage) { diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 27725379af..afc8356b4f 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -109,7 +109,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case playerV2(Bool) case devRequests(Bool) case enableUpdates(Bool) - case fakeAds(Bool) + case pwa(Bool) case enableLocalTranslation(Bool) case preferredVideoCodec(Int, String, String?, Bool) case disableVideoAspectScaling(Bool) @@ -135,7 +135,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.web.rawValue case .keepChatNavigationStack, .skipReadHistory, .alwaysDisplayTyping, .debugRatingLayout, .crashOnSlowQueries, .crashOnMemoryPressure: return DebugControllerSection.experiments.rawValue - case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .fakeAds, .enableLocalTranslation: + case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .pwa, .enableLocalTranslation: return DebugControllerSection.experiments.rawValue case .logTranslationRecognition, .resetTranslationStates: return DebugControllerSection.translation.rawValue @@ -258,7 +258,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 54 case .devRequests: return 55 - case .fakeAds: + case .pwa: return 56 case .enableLocalTranslation: return 57 @@ -1200,7 +1200,18 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) case .resetWebViewCache: return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: "Clear Web View Cache", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { - WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{ }) + WKWebsiteDataStore.default().removeData(ofTypes: [ + WKWebsiteDataTypeDiskCache, + WKWebsiteDataTypeOfflineWebApplicationCache, + WKWebsiteDataTypeMemoryCache, + WKWebsiteDataTypeLocalStorage, + WKWebsiteDataTypeCookies, + WKWebsiteDataTypeSessionStorage, + WKWebsiteDataTypeIndexedDBDatabases, + WKWebsiteDataTypeWebSQLDatabases, + WKWebsiteDataTypeFetchCache, + WKWebsiteDataTypeServiceWorkerRegistrations + ], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{ }) }) case .optimizeDatabase: return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { @@ -1392,12 +1403,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .fakeAds(value): - return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Fake Ads", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .pwa(value): + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Test1", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings - settings.fakeAds = value + settings.enablePWA = value return PreferencesEntry(settings) }) }).start() @@ -1488,7 +1499,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } -private func debugControllerEntries(sharedContext: SharedAccountContext, presentationData: PresentationData, loggingSettings: LoggingSettings, mediaInputSettings: MediaInputSettings, experimentalSettings: ExperimentalUISettings, networkSettings: NetworkSettings?, hasLegacyAppData: Bool, useBetaFeatures: Bool) -> [DebugControllerEntry] { +private func debugControllerEntries(context: AccountContext?, sharedContext: SharedAccountContext, presentationData: PresentationData, loggingSettings: LoggingSettings, mediaInputSettings: MediaInputSettings, experimentalSettings: ExperimentalUISettings, networkSettings: NetworkSettings?, hasLegacyAppData: Bool, useBetaFeatures: Bool) -> [DebugControllerEntry] { var entries: [DebugControllerEntry] = [] let isMainApp = sharedContext.applicationBindings.isMainApp @@ -1572,7 +1583,18 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.playerV2(experimentalSettings.playerV2)) entries.append(.devRequests(experimentalSettings.devRequests)) - entries.append(.fakeAds(experimentalSettings.fakeAds)) + + if let data = context?.currentAppConfiguration.with({ $0 }).data { + var displayPwa = false + if let _ = data["ios_display_pwa"] { + displayPwa = true + } else if let isDev = data["dev"] as? Double, isDev == 1.0 { + displayPwa = true + } + if displayPwa { + entries.append(.pwa(experimentalSettings.enablePWA)) + } + } entries.append(.enableLocalTranslation(experimentalSettings.enableLocalTranslation)) entries.append(.enableUpdates(experimentalSettings.enableUpdates)) } @@ -1658,7 +1680,7 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Debug"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: debugControllerEntries(sharedContext: sharedContext, presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, networkSettings: networkSettings, hasLegacyAppData: hasLegacyAppData, useBetaFeatures: useBetaFeatures), style: .blocks) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: debugControllerEntries(context: context, sharedContext: sharedContext, presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, networkSettings: networkSettings, hasLegacyAppData: hasLegacyAppData, useBetaFeatures: useBetaFeatures), style: .blocks) return (controllerState, (listState, arguments)) } diff --git a/submodules/LegacyDataImport/Sources/LegacyBuffer.swift b/submodules/LegacyDataImport/Sources/LegacyBuffer.swift index edafb9bbea..cebc964f39 100644 --- a/submodules/LegacyDataImport/Sources/LegacyBuffer.swift +++ b/submodules/LegacyDataImport/Sources/LegacyBuffer.swift @@ -166,13 +166,16 @@ class LegacyBufferReader { if count == 0 { return 0 } - else if count > 0 && count <= 4 || self.offset + UInt(count) <= self.buffer._size { - var value: Int32 = 0 - memcpy(&value, self.buffer.data?.advanced(by: Int(self.offset)), count) - self.offset += UInt(count) - return value + guard count > 0, count <= 4, self.offset + UInt(count) <= self.buffer._size else { + return nil } - return nil + guard let bufferData = self.buffer.data else { + return nil + } + var value: Int32 = 0 + memcpy(&value, bufferData.advanced(by: Int(self.offset)), count) + self.offset += UInt(count) + return value } func readBuffer(_ count: Int) -> LegacyBuffer? { diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 77f4684457..4bf6e9ec9a 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -69,6 +69,7 @@ public struct ExperimentalUISettings: Codable, Equatable { public var allForumsHaveTabs: Bool public var debugRatingLayout: Bool public var enableUpdates: Bool + public var enablePWA: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -115,7 +116,8 @@ public struct ExperimentalUISettings: Codable, Equatable { checkSerializedData: false, allForumsHaveTabs: false, debugRatingLayout: false, - enableUpdates: false + enableUpdates: false, + enablePWA: false ) } @@ -163,7 +165,8 @@ public struct ExperimentalUISettings: Codable, Equatable { checkSerializedData: Bool, allForumsHaveTabs: Bool, debugRatingLayout: Bool, - enableUpdates: Bool + enableUpdates: Bool, + enablePWA: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -209,6 +212,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.allForumsHaveTabs = allForumsHaveTabs self.debugRatingLayout = debugRatingLayout self.enableUpdates = enableUpdates + self.enablePWA = enablePWA } public init(from decoder: Decoder) throws { @@ -258,6 +262,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.allForumsHaveTabs = try container.decodeIfPresent(Bool.self, forKey: "allForumsHaveTabs") ?? false self.debugRatingLayout = try container.decodeIfPresent(Bool.self, forKey: "debugRatingLayout") ?? false self.enableUpdates = try container.decodeIfPresent(Bool.self, forKey: "enableUpdates") ?? false + self.enablePWA = try container.decodeIfPresent(Bool.self, forKey: "enablePWA") ?? false } public func encode(to encoder: Encoder) throws { @@ -307,6 +312,7 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encodeIfPresent(self.allForumsHaveTabs, forKey: "allForumsHaveTabs") try container.encodeIfPresent(self.debugRatingLayout, forKey: "debugRatingLayout") try container.encodeIfPresent(self.enableUpdates, forKey: "enableUpdates") + try container.encodeIfPresent(self.enablePWA, forKey: "enablePWA") } } diff --git a/submodules/UIKitRuntimeUtils/BUILD b/submodules/UIKitRuntimeUtils/BUILD index 50ef9fac7d..96f6226f73 100644 --- a/submodules/UIKitRuntimeUtils/BUILD +++ b/submodules/UIKitRuntimeUtils/BUILD @@ -16,12 +16,13 @@ objc_library( "Source", ], deps = [ - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils", + "//submodules/AsyncDisplayKit", + "//submodules/ObjCRuntimeUtils", ], sdk_frameworks = [ "Foundation", "UIKit", + "WebKit", ], visibility = [ "//visibility:public", diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h index 928aa420c9..dab3ef6624 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.h @@ -1,4 +1,5 @@ #import +#import typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) { UIResponderDisableAutomaticKeyboardHandlingForward = 1 << 0, @@ -104,3 +105,10 @@ void snapshotViewByDrawingInContext(UIView * _Nonnull view); @property (nonatomic) double lumaMax; @end + +@interface WebHelpers : NSObject + ++ (dispatch_block_t _Nonnull)addTrustedDomain:(NSString * _Nonnull)domain; ++ (void)forceRefreshTrustedDomains:(WKWebsiteDataStore * _Nonnull)websiteDataStore; + +@end diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m index 2c4f273a4e..3be2d1b264 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m @@ -405,6 +405,122 @@ static void registerEffectViewOverrides(void) { } } +static NSLock *webHelpersLock() { + static NSLock *value = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + value = [[NSLock alloc] init]; + }); + return value; +} + +@interface TrustedWebRecord : NSObject + +@property (nonatomic) NSInteger nextReference; +@property (nonatomic, strong, readonly) NSMutableSet *references; + +@end + +@implementation TrustedWebRecord + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _references = [[NSMutableSet alloc] init]; + } + return self; +} + +- (NSInteger)addReference { + NSInteger reference = _nextReference; + _nextReference += 1; + [_references addObject:@(reference)]; + return reference; +} + +- (void)removeReference:(NSInteger)reference { + [_references removeObject:@(reference)]; +} + +- (bool)isEmpty { + return _references.count == 0; +} + +@end + +static NSMutableDictionary *trustedWebRecords() { + static NSMutableDictionary *value = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + value = [[NSMutableDictionary alloc] init]; + }); + return value; +} + +void WKWebsiteDataStoreReinitializeAppBoundDomains(CFTypeRef dataStoreRef); + +@implementation WebHelpers + ++ (NSArray * _Nonnull)threadSafeTrustedDomains { + [webHelpersLock() lock]; + + NSMutableArray *result = [[NSMutableArray alloc] init]; + for (NSString *domain in trustedWebRecords()) { + [result addObject:domain]; + } + [result sortUsingSelector:@selector(compare:)]; + + [webHelpersLock() unlock]; + + return result; +} + ++ (dispatch_block_t _Nonnull)addTrustedDomain:(NSString * _Nonnull)domain { + [webHelpersLock() lock]; + + TrustedWebRecord *record = trustedWebRecords()[domain]; + if (record == nil) { + record = [[TrustedWebRecord alloc] init]; + trustedWebRecords()[domain] = record; + } + NSInteger reference = [record addReference]; + __block __weak TrustedWebRecord *weakRecord = record; + + [webHelpersLock() unlock]; + + return ^{ + [webHelpersLock() lock]; + + TrustedWebRecord *strongRecord = weakRecord; + if (strongRecord && trustedWebRecords()[domain] == strongRecord) { + [strongRecord removeReference:reference]; + if ([strongRecord isEmpty]) { + [trustedWebRecords() removeObjectForKey:domain]; + } + } + + [webHelpersLock() unlock]; + }; +} + ++ (void)forceRefreshTrustedDomains:(WKWebsiteDataStore * _Nonnull)websiteDataStore { + WKWebsiteDataStoreReinitializeAppBoundDomains((__bridge CFTypeRef)(websiteDataStore)); +} + +@end + +@implementation NSBundle (Telegram) + +- (id)_65087dc8_objectForInfoDictionaryKey:(NSString *)key { + if ([key isEqualToString:@"WKAppBoundDomains"]) { + //NSLog(@"Returning trusted domains: %@", [WebHelpers threadSafeTrustedDomains]); + return [WebHelpers threadSafeTrustedDomains]; + } + return [self _65087dc8_objectForInfoDictionaryKey:key]; +} + +@end + @implementation UIViewController (Navigation) + (void)load @@ -432,6 +548,8 @@ static void registerEffectViewOverrides(void) { [RuntimeUtils swizzleInstanceMethodOfClass:[UIFocusSystem class] currentSelector:@selector(updateFocusIfNeeded) newSelector:@selector(_65087dc8_updateFocusIfNeeded)]; + [RuntimeUtils swizzleInstanceMethodOfClass:[NSBundle class] currentSelector:@selector(objectForInfoDictionaryKey:) newSelector:@selector(_65087dc8_objectForInfoDictionaryKey:)]; + if (@available(iOS 26.0, *)) { registerEffectViewOverrides(); } diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 2c82e6c57a..29ef625b2b 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -162,12 +162,8 @@ final class WebAppWebView: WKWebView { self.isOpaque = false self.backgroundColor = .clear - if #available(iOS 9.0, *) { - self.allowsLinkPreview = false - } - if #available(iOS 11.0, *) { - self.scrollView.contentInsetAdjustmentBehavior = .never - } + self.allowsLinkPreview = false + self.scrollView.contentInsetAdjustmentBehavior = .never self.interactiveTransitionGestureRecognizerTest = { point -> Bool in return point.x > 30.0 } From 44fab3078a107a425499b16e5ac501f0d83dfef9 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 11:39:09 +0400 Subject: [PATCH 02/13] Temp --- .../Sources/ChatItemGalleryFooterContentNode.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 8054669644..187ef43d59 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -1340,13 +1340,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll isOpen: false ))), action: { [weak self] in - guard let self, let buttonPanelView = self.buttonPanel.view as? GlassControlPanelComponent.View, let centerItemView = buttonPanelView.centerItemView else { + guard let self, let buttonPanelView = self.buttonPanel.view as? GlassControlPanelComponent.View else { return } - guard let itemView = centerItemView.itemView(id: AnyHashable("settings")) else { - return + if let centerItemView = buttonPanelView.centerItemView, let itemView = centerItemView.itemView(id: AnyHashable("settings")) { + self.settingsButtonPressed(sourceView: itemView) + } else if let rightItemView = buttonPanelView.rightItemView, let itemView = rightItemView.itemView(id: AnyHashable("settings")) { + self.settingsButtonPressed(sourceView: itemView) } - self.settingsButtonPressed(sourceView: itemView) } )) } From aa21a14c7dff41288ad0ef1299c1da63a7e1c5cc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 6 Feb 2026 13:35:20 +0400 Subject: [PATCH 03/13] Update API [skip ci] --- .../Sources/ChatListController.swift | 29 +++++++-- ...ListControllerLeaveChatConfirmation.swift} | 2 +- submodules/TelegramApi/Sources/Api0.swift | 1 + submodules/TelegramApi/Sources/Api28.swift | 34 ++++++++++ submodules/TelegramApi/Sources/Api40.swift | 64 +++++++++---------- .../State/AccountStateManagementUtils.swift | 20 ++++++ .../Sources/State/Serialization.swift | 2 +- .../SyncCore/SyncCore_CachedUserData.swift | 2 + .../Peers/ChannelOwnershipTransfer.swift | 46 ++++++------- .../TelegramEngine/Peers/RemovePeerChat.swift | 8 +-- .../Peers/TelegramEnginePeers.swift | 6 +- .../Peers/UpdateCachedPeerData.swift | 12 ++++ .../Sources/ChatMessageBubbleItemNode.swift | 4 +- .../Sources/ContextActionsContainerNode.swift | 10 +-- .../ChannelOwnershipTransferController.swift | 8 +-- ...annelMemberCategoriesContextsManager.swift | 6 +- 16 files changed, 171 insertions(+), 83 deletions(-) rename submodules/ChatListUI/Sources/{ChatListControllerLeaveChannelConfirmation.swift => ChatListControllerLeaveChatConfirmation.swift} (98%) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 183df4dc5a..39f4f0924a 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -5439,14 +5439,23 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) } - if let self, case let .channel(channel) = peer.peer, channel.flags.contains(.isCreator) { - let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: channel.id) + let shouldCheckFutureCreator: Bool + if case let .channel(channel) = peer.peer, channel.flags.contains(.isCreator) { + shouldCheckFutureCreator = true + } else if case let .legacyGroup(group) = peer.peer, case .creator = group.role { + shouldCheckFutureCreator = true + } else { + shouldCheckFutureCreator = false + } + + if let self, shouldCheckFutureCreator { + let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: peer.peerId) |> deliverOnMainQueue).start(next: { [weak self] nextCreator in guard let self else { return } if let nextCreator, let peer = peer.peer { - self.presentLeaveChannelConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in + self.presentLeaveChatConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in if commit { proceed() } @@ -5913,14 +5922,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController removed() }) } + + let shouldCheckFutureCreator: Bool if case let .channel(channel) = peer.peer, channel.flags.contains(.isCreator) { - let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: channel.id) + shouldCheckFutureCreator = true + } else if case let .legacyGroup(group) = peer.peer, case .creator = group.role { + shouldCheckFutureCreator = true + } else { + shouldCheckFutureCreator = false + } + + if shouldCheckFutureCreator { + let _ = (self.context.engine.peers.getFutureCreatorAfterLeave(peerId: peer.peerId) |> deliverOnMainQueue).start(next: { [weak self] nextCreator in guard let self else { return } if let nextCreator, let peer = peer.peer { - self.presentLeaveChannelConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in + self.presentLeaveChatConfirmation(peer: peer, nextCreator: nextCreator, completion: { commit in if commit { proceed() } else { diff --git a/submodules/ChatListUI/Sources/ChatListControllerLeaveChannelConfirmation.swift b/submodules/ChatListUI/Sources/ChatListControllerLeaveChatConfirmation.swift similarity index 98% rename from submodules/ChatListUI/Sources/ChatListControllerLeaveChannelConfirmation.swift rename to submodules/ChatListUI/Sources/ChatListControllerLeaveChatConfirmation.swift index e7218f17bd..a6f06052a3 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerLeaveChannelConfirmation.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerLeaveChatConfirmation.swift @@ -12,7 +12,7 @@ import PeerInfoUI import OwnershipTransferController extension ChatListControllerImpl { - func presentLeaveChannelConfirmation(peer: EnginePeer, nextCreator: EnginePeer, completion: @escaping (Bool) -> Void) { + func presentLeaveChatConfirmation(peer: EnginePeer, nextCreator: EnginePeer, completion: @escaping (Bool) -> Void) { Task { @MainActor in let accountPeer = await (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))).get() diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index c9da0e78c9..890a5abcae 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -1172,6 +1172,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1094555409] = { return Api.Update.parse_updateNotifySettings($0) } dict[-1955438642] = { return Api.Update.parse_updatePaidReactionPrivacy($0) } dict[-337610926] = { return Api.Update.parse_updatePeerBlocked($0) } + dict[1115654513] = { return Api.Update.parse_updatePeerHistoryNoForward($0) } dict[-1147422299] = { return Api.Update.parse_updatePeerHistoryTTL($0) } dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) } dict[1786671974] = { return Api.Update.parse_updatePeerSettings($0) } diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 9cd5aa8cdf..8744345e53 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -969,6 +969,14 @@ public extension Api { self.peerId = peerId } } + public class Cons_updatePeerHistoryNoForward { + public var flags: Int32 + public var peer: Api.Peer + public init(flags: Int32, peer: Api.Peer) { + self.flags = flags + self.peer = peer + } + } public class Cons_updatePeerHistoryTTL { public var flags: Int32 public var peer: Api.Peer @@ -1531,6 +1539,7 @@ public extension Api { case updateNotifySettings(Cons_updateNotifySettings) case updatePaidReactionPrivacy(Cons_updatePaidReactionPrivacy) case updatePeerBlocked(Cons_updatePeerBlocked) + case updatePeerHistoryNoForward(Cons_updatePeerHistoryNoForward) case updatePeerHistoryTTL(Cons_updatePeerHistoryTTL) case updatePeerLocated(Cons_updatePeerLocated) case updatePeerSettings(Cons_updatePeerSettings) @@ -2488,6 +2497,13 @@ public extension Api { serializeInt32(_data.flags, buffer: buffer, boxed: false) _data.peerId.serialize(buffer, true) break + case .updatePeerHistoryNoForward(let _data): + if boxed { + buffer.appendInt32(1115654513) + } + serializeInt32(_data.flags, buffer: buffer, boxed: false) + _data.peer.serialize(buffer, true) + break case .updatePeerHistoryTTL(let _data): if boxed { buffer.appendInt32(-1147422299) @@ -3192,6 +3208,8 @@ public extension Api { return ("updatePaidReactionPrivacy", [("`private`", _data.`private` as Any)]) case .updatePeerBlocked(let _data): return ("updatePeerBlocked", [("flags", _data.flags as Any), ("peerId", _data.peerId as Any)]) + case .updatePeerHistoryNoForward(let _data): + return ("updatePeerHistoryNoForward", [("flags", _data.flags as Any), ("peer", _data.peer as Any)]) case .updatePeerHistoryTTL(let _data): return ("updatePeerHistoryTTL", [("flags", _data.flags as Any), ("peer", _data.peer as Any), ("ttlPeriod", _data.ttlPeriod as Any)]) case .updatePeerLocated(let _data): @@ -5158,6 +5176,22 @@ public extension Api { return nil } } + public static func parse_updatePeerHistoryNoForward(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updatePeerHistoryNoForward(Cons_updatePeerHistoryNoForward(flags: _1!, peer: _2!)) + } + else { + return nil + } + } public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api40.swift b/submodules/TelegramApi/Sources/Api40.swift index 1cf0af77c4..8df94aa688 100644 --- a/submodules/TelegramApi/Sources/Api40.swift +++ b/submodules/TelegramApi/Sources/Api40.swift @@ -3191,23 +3191,6 @@ public extension Api.functions.channels { }) } } -public extension Api.functions.channels { - static func editCreator(channel: Api.InputChannel, userId: Api.InputUser, password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1892102881) - channel.serialize(buffer, true) - userId.serialize(buffer, true) - password.serialize(buffer, true) - return (FunctionDescription(name: "channels.editCreator", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } -} public extension Api.functions.channels { static func editLocation(channel: Api.InputChannel, geoPoint: Api.InputGeoPoint, address: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -3371,21 +3354,6 @@ public extension Api.functions.channels { }) } } -public extension Api.functions.channels { - static func getFutureCreatorAfterLeave(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1610016593) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.getFutureCreatorAfterLeave", parameters: [("channel", String(describing: channel))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in - let reader = BufferReader(buffer) - var result: Api.User? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.User - } - return result - }) - } -} public extension Api.functions.channels { static func getGroupsForDiscussion() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5742,6 +5710,23 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func editChatCreator(peer: Api.InputPeer, userId: Api.InputUser, password: Api.InputCheckPasswordSRP) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-146556841) + peer.serialize(buffer, true) + userId.serialize(buffer, true) + password.serialize(buffer, true) + return (FunctionDescription(name: "messages.editChatCreator", parameters: [("peer", String(describing: peer)), ("userId", String(describing: userId)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} public extension Api.functions.messages { static func editChatDefaultBannedRights(peer: Api.InputPeer, bannedRights: Api.ChatBannedRights) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -6781,6 +6766,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getFutureChatCreatorAfterLeave(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(998051494) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.getFutureChatCreatorAfterLeave", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.User? in + let reader = BufferReader(buffer) + var result: Api.User? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.User + } + return result + }) + } +} public extension Api.functions.messages { static func getGameHighScores(peer: Api.InputPeer, id: Int32, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 2b2fad4980..adbe27e587 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2010,6 +2010,26 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: updatedState.updateStarGiftAuctionMyState(giftId: giftId, state: GiftAuctionContext.State.MyState(apiAuctionUserState: userState)) case let .updateEmojiGameInfo(updateEmojiGameInfoData): updatedState.updateEmojiGameInfo(info: EmojiGameInfo(apiEmojiGameInfo: updateEmojiGameInfoData.info)) + case let .updatePeerHistoryNoForward(updatePeerHistoryNoForwardData): + let (flags, peer) = (updatePeerHistoryNoForwardData.flags, updatePeerHistoryNoForwardData.peer) + let peerId = peer.peerId + updatedState.updateCachedPeerData(peerId, { current in + if let previous = current as? CachedUserData { + var updatedFlags = previous.flags + if (flags & (1 << 0)) != 0 { + updatedFlags.insert(.myCopyProtectionEnabled) + } else { + updatedFlags.remove(.myCopyProtectionEnabled) + } + if (flags & (1 << 1)) != 0 { + updatedFlags.insert(.copyProtectionEnabled) + } else { + updatedFlags.remove(.copyProtectionEnabled) + } + return previous.withUpdatedFlags(updatedFlags) + } + return current + }) default: break } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index e1003fb62f..400f7e2881 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 222 + return 223 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index 726bd5726d..eba6983325 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -627,6 +627,8 @@ public struct CachedUserFlags: OptionSet { public static let canViewRevenue = CachedUserFlags(rawValue: 1 << 5) public static let botCanManageEmojiStatus = CachedUserFlags(rawValue: 1 << 6) public static let displayGiftButton = CachedUserFlags(rawValue: 1 << 7) + public static let myCopyProtectionEnabled = CachedUserFlags(rawValue: 1 << 8) + public static let copyProtectionEnabled = CachedUserFlags(rawValue: 1 << 9) } public final class EditableBotInfo: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift index ec6676b391..dc1f7012a9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift @@ -4,7 +4,7 @@ import Postbox import TelegramApi -public enum ChannelOwnershipTransferError { +public enum ChatOwnershipTransferError { case generic case twoStepAuthMissing case twoStepAuthTooFresh(Int32) @@ -20,12 +20,12 @@ public enum ChannelOwnershipTransferError { case userBlocked } -func _internal_checkOwnershipTranfserAvailability(postbox: Postbox, network: Network, accountStateManager: AccountStateManager, memberId: PeerId) -> Signal { +func _internal_checkOwnershipTranfserAvailability(postbox: Postbox, network: Network, accountStateManager: AccountStateManager, memberId: PeerId) -> Signal { return postbox.transaction { transaction -> Peer? in return transaction.getPeer(memberId) } - |> castError(ChannelOwnershipTransferError.self) - |> mapToSignal { user -> Signal in + |> castError(ChatOwnershipTransferError.self) + |> mapToSignal { user -> Signal in guard let user = user else { return .fail(.generic) } @@ -33,8 +33,8 @@ func _internal_checkOwnershipTranfserAvailability(postbox: Postbox, network: Net return .fail(.generic) } - return network.request(Api.functions.channels.editCreator(channel: .inputChannelEmpty, userId: apiUser, password: .inputCheckPasswordEmpty)) - |> mapError { error -> ChannelOwnershipTransferError in + return network.request(Api.functions.messages.editChatCreator(peer: .inputPeerEmpty, userId: apiUser, password: .inputCheckPasswordEmpty)) + |> mapError { error -> ChatOwnershipTransferError in if error.errorDescription == "PASSWORD_HASH_INVALID" { return .requestPassword } else if error.errorDescription == "PASSWORD_MISSING" { @@ -64,39 +64,39 @@ func _internal_checkOwnershipTranfserAvailability(postbox: Postbox, network: Net } return .generic } - |> mapToSignal { updates -> Signal in + |> mapToSignal { updates -> Signal in accountStateManager.addUpdates(updates) return .complete() } } } -func _internal_updateChannelOwnership(account: Account, channelId: PeerId, memberId: PeerId, password: String) -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> { +func _internal_updateChatOwnership(account: Account, peerId: PeerId, memberId: PeerId, password: String) -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> { guard !password.isEmpty else { return .fail(.invalidPassword) } - return combineLatest(_internal_fetchChannelParticipant(account: account, peerId: channelId, participantId: account.peerId), _internal_fetchChannelParticipant(account: account, peerId: channelId, participantId: memberId)) - |> mapError { _ -> ChannelOwnershipTransferError in + return combineLatest(_internal_fetchChannelParticipant(account: account, peerId: peerId, participantId: account.peerId), _internal_fetchChannelParticipant(account: account, peerId: peerId, participantId: memberId)) + |> mapError { _ -> ChatOwnershipTransferError in } - |> mapToSignal { currentCreator, currentParticipant -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in - return account.postbox.transaction { transaction -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in - if let channel = transaction.getPeer(channelId) as? TelegramChannel, let inputChannel = apiInputChannel(channel), let accountUser = transaction.getPeer(account.peerId), let user = transaction.getPeer(memberId), let inputUser = apiInputUser(user) { + |> mapToSignal { currentCreator, currentParticipant -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> in + return account.postbox.transaction { transaction -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> in + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer), let accountUser = transaction.getPeer(account.peerId), let user = transaction.getPeer(memberId), let inputUser = apiInputUser(user) { - let flags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .channel(channel)) + let flags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer)) let updatedParticipant = ChannelParticipant.creator(id: user.id, adminInfo: nil, rank: currentParticipant?.rank) let updatedPreviousCreator = ChannelParticipant.member(id: accountUser.id, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: flags), promotedBy: accountUser.id, canBeEditedByAccountPeer: false), banInfo: nil, rank: currentCreator?.rank, subscriptionUntilDate: nil) let checkPassword = _internal_twoStepAuthData(account.network) - |> mapError { error -> ChannelOwnershipTransferError in + |> mapError { error -> ChatOwnershipTransferError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded } else { return .generic } } - |> mapToSignal { authData -> Signal in + |> mapToSignal { authData -> Signal in if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData { guard let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else { return .fail(.generic) @@ -108,9 +108,9 @@ func _internal_updateChannelOwnership(account: Account, channelId: PeerId, membe } return checkPassword - |> mapToSignal { password -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in - return account.network.request(Api.functions.channels.editCreator(channel: inputChannel, userId: inputUser, password: password), automaticFloodWait: false) - |> mapError { error -> ChannelOwnershipTransferError in + |> mapToSignal { password -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> in + return account.network.request(Api.functions.messages.editChatCreator(peer: inputPeer, userId: inputUser, password: password), automaticFloodWait: false) + |> mapError { error -> ChatOwnershipTransferError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { return .limitExceeded } else if error.errorDescription == "PASSWORD_HASH_INVALID" { @@ -140,11 +140,11 @@ func _internal_updateChannelOwnership(account: Account, channelId: PeerId, membe } return .generic } - |> mapToSignal { updates -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in + |> mapToSignal { updates -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> in account.stateManager.addUpdates(updates) return account.postbox.transaction { transaction -> [(ChannelParticipant?, RenderedChannelParticipant)] in - transaction.updatePeerCachedData(peerIds: Set([channelId]), update: { _, cachedData -> CachedPeerData? in + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in if let cachedData = cachedData as? CachedChannelData, let adminCount = cachedData.participantsSummary.adminCount { var updatedAdminCount = adminCount var wasAdmin = false @@ -179,14 +179,14 @@ func _internal_updateChannelOwnership(account: Account, channelId: PeerId, membe } return [(currentCreator, RenderedChannelParticipant(participant: updatedPreviousCreator, peer: accountUser, peers: peers, presences: presences)), (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: user, peers: peers, presences: presences))] } - |> mapError { _ -> ChannelOwnershipTransferError in } + |> mapError { _ -> ChatOwnershipTransferError in } } } } else { return .fail(.generic) } } - |> mapError { _ -> ChannelOwnershipTransferError in } + |> mapError { _ -> ChatOwnershipTransferError in } |> switchToLatest } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift index 1489e30de0..e833bda00a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift @@ -83,13 +83,13 @@ func _internal_removePeerChat(account: Account, transaction: Transaction, mediaB func _internal_getFutureCreatorAfterLeave(account: Account, peerId: EnginePeer.Id) -> Signal { return account.postbox.transaction { transaction in - return transaction.getPeer(peerId).flatMap(apiInputChannel) + return transaction.getPeer(peerId).flatMap(apiInputPeer) } - |> mapToSignal { channel in - guard let channel else { + |> mapToSignal { peer in + guard let peer else { return .single(nil) } - return account.network.request(Api.functions.channels.getFutureCreatorAfterLeave(channel: channel)) + return account.network.request(Api.functions.messages.getFutureChatCreatorAfterLeave(peer: peer)) |> map(Optional.init) |> `catch` { _ in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index e1ec601d86..292c6b433f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -486,12 +486,12 @@ public extension TelegramEngine { return _internal_channelMembers(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, peerId: peerId, category: category, offset: offset, limit: limit, hash: hash) } - public func checkOwnershipTranfserAvailability(memberId: PeerId) -> Signal { + public func checkOwnershipTranfserAvailability(memberId: PeerId) -> Signal { return _internal_checkOwnershipTranfserAvailability(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, memberId: memberId) } - public func updateChannelOwnership(channelId: PeerId, memberId: PeerId, password: String) -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> { - return _internal_updateChannelOwnership(account: self.account, channelId: channelId, memberId: memberId, password: password) + public func updateChatOwnership(peerId: PeerId, memberId: PeerId, password: String) -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChatOwnershipTransferError> { + return _internal_updateChatOwnership(account: self.account, peerId: peerId, memberId: memberId, password: password) } public func searchGroupMembers(peerId: PeerId, query: String) -> Signal<[EnginePeer], NoError> { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 7e9292da48..20455a3698 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -297,6 +297,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let canViewRevenue = (userFullFlags2 & (1 << 9)) != 0 let botCanManageEmojiStatus = (userFullFlags2 & (1 << 10)) != 0 let displayGiftButton = (userFullFlags2 & (1 << 16)) != 0 + let myCopyProtectionEnabled = (userFullFlags2 & (1 << 23)) != 0 + let copyProtectionEnabled = (userFullFlags2 & (1 << 24)) != 0 var flags: CachedUserFlags = previous.flags if premiumRequired { @@ -334,6 +336,16 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } else { flags.remove(.displayGiftButton) } + if myCopyProtectionEnabled { + flags.insert(.myCopyProtectionEnabled) + } else { + flags.remove(.myCopyProtectionEnabled) + } + if copyProtectionEnabled { + flags.insert(.copyProtectionEnabled) + } else { + flags.remove(.copyProtectionEnabled) + } let callsPrivate = (userFullFlags & (1 << 5)) != 0 let canPinMessages = (userFullFlags & (1 << 7)) != 0 diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 0a6ef35dea..43e7e931d2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -3521,9 +3521,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI }, mosaicStatusOrigin: mosaicStatusOrigin, mosaicStatusSizeAndApply: mosaicStatusSizeAndApply, - unlockButtonPosition: unlockButtonPosition?.offsetBy(dx: 0.0, dy: layoutInsets.top), + unlockButtonPosition: unlockButtonPosition, unlockButtonSizeAndApply: unlockButtonSizeApply, - mediaInfoOrigin: mediaInfoOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top), + mediaInfoOrigin: mediaInfoOrigin, mediaInfoSizeAndApply: mediaInfoSizeApply, needsSummarizeButton: needsSummarizeButton, needsShareButton: needsShareButton, diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift index bc21e267ef..894ec7e72e 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift @@ -549,18 +549,18 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { shimmeringForegroundColor = presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.07) } - let textRightInset: CGFloat + let textLeftInset: CGFloat if let _ = self.iconNode.image { - textRightInset = iconSize.width - 2.0 + textLeftInset = iconSize.width + 18.0 } else { - textRightInset = 0.0 + textLeftInset = 0.0 } let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - textRightInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil)) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - textLeftInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil)) let _ = textApply(self.arguments?.withUpdatedPlaceholderColor(shimmeringForegroundColor)) - let textFrame = CGRect(origin: CGPoint(x: horizontalInset, y: topInset), size: textLayout.size) + let textFrame = CGRect(origin: CGPoint(x: horizontalInset + textLeftInset, y: topInset), size: textLayout.size) transition.updateFrame(node: self.textNode.textNode, frame: textFrame) if textFrame.size.height.isZero { self.textNode.textNode.alpha = 0.0 diff --git a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift index 5a27b12e51..d19cdba764 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift @@ -88,7 +88,7 @@ private func commitChannelOwnershipTransferController( applyImpl = { doneInProgressPromise.set(true) - let signal: Signal + let signal: Signal if case let .channel(peer) = peer { signal = context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: peer.id, memberId: member.id, password: inputState.value) |> mapToSignal { _ in return .complete() @@ -97,7 +97,7 @@ private func commitChannelOwnershipTransferController( } else if case let .legacyGroup(peer) = peer { signal = context.engine.peers.convertGroupToSupergroup(peerId: peer.id) |> map(Optional.init) - |> mapError { error -> ChannelOwnershipTransferError in + |> mapError { error -> ChatOwnershipTransferError in switch error { case .tooManyChannels: return .tooMuchJoined @@ -106,7 +106,7 @@ private func commitChannelOwnershipTransferController( } } |> deliverOnMainQueue - |> mapToSignal { upgradedPeerId -> Signal in + |> mapToSignal { upgradedPeerId -> Signal in guard let upgradedPeerId = upgradedPeerId else { return .fail(.generic) } @@ -213,7 +213,7 @@ public func channelOwnershipTransferController( peer: EnginePeer, member: TelegramUser, onLeave: Bool, - initialError: ChannelOwnershipTransferError, + initialError: ChatOwnershipTransferError, present: @escaping (ViewController, Any?) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping (EnginePeer.Id?) -> Void diff --git a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift index e60a75f17b..e933e46a9d 100644 --- a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift +++ b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift @@ -408,8 +408,8 @@ public final class PeerChannelMemberCategoriesContextsManager { } } - public func transferOwnership(engine: TelegramEngine, peerId: PeerId, memberId: PeerId, password: String) -> Signal { - return engine.peers.updateChannelOwnership(channelId: peerId, memberId: memberId, password: password) + public func transferOwnership(engine: TelegramEngine, peerId: PeerId, memberId: PeerId, password: String) -> Signal { + return engine.peers.updateChatOwnership(peerId: peerId, memberId: memberId, password: password) |> map(Optional.init) |> deliverOnMainQueue |> beforeNext { [weak self] results in @@ -423,7 +423,7 @@ public final class PeerChannelMemberCategoriesContextsManager { } } } - |> mapToSignal { _ -> Signal in + |> mapToSignal { _ -> Signal in return .complete() } } From 45b1306274cc860221fffa02eaf63e481222d995 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 15:18:17 +0400 Subject: [PATCH 04/13] Various improvements --- .../Source/_ASDisplayViewAccessiblity.mm | 125 +++++++++++------- ...uthorizationSequenceSignUpController.swift | 4 +- .../Sources/ChatListController.swift | 4 +- .../Sources/ChatListSearchListPaneNode.swift | 8 +- .../Sources/ReactionButtonListComponent.swift | 2 +- .../Items/UniversalVideoGalleryItem.swift | 6 +- .../StorageUsageExceptionsScreen.swift | 2 +- .../MediaNavigationAccessoryHeaderNode.swift | 2 +- .../Sources/VideoChatScreenMoreMenu.swift | 8 +- .../Sources/ChatTextInputPanelNode.swift | 3 - .../Sources/ContextActionsContainerNode.swift | 12 +- .../ContextControllerActionsStackNode.swift | 3 +- .../GiftAttributeListContextItem.swift | 4 +- .../Sources/GlassBackgroundComponent.swift | 8 +- .../Sources/LegacyMessageInputPanel.swift | 6 +- .../Sources/MediaEditorScreen.swift | 2 +- .../MediaNavigationAccessoryHeaderNode.swift | 2 +- .../Sources/AffiliateProgramSetupScreen.swift | 2 +- ...eerInfoScreenDisplayGiftsContextMenu.swift | 12 +- .../Sources/AddGiftsScreen.swift | 12 +- .../Sources/PeerInfoGiftsPaneNode.swift | 2 +- .../Sources/PeerSelectionController.swift | 2 +- .../Sources/PeerSelectionControllerNode.swift | 10 +- .../Sources/ShareWithPeersScreen.swift | 2 +- .../Sources/StorageUsageScreen.swift | 2 +- .../StoryItemSetContainerComponent.swift | 2 +- .../StoryItemSetViewListComponent.swift | 6 +- .../VideoPlaybackControlsComponent.swift | 2 + .../Sources/ChatHistoryListNode.swift | 4 +- .../Sources/CreateGroupController.swift | 4 +- .../NotificationItemContainerNode.swift | 4 +- .../Sources/OverlayPlayerControlsNode.swift | 2 +- .../UIViewController+Navigation.m | 6 +- 33 files changed, 158 insertions(+), 117 deletions(-) diff --git a/submodules/AsyncDisplayKit/Source/_ASDisplayViewAccessiblity.mm b/submodules/AsyncDisplayKit/Source/_ASDisplayViewAccessiblity.mm index 6e0b33541b..61b34afc75 100644 --- a/submodules/AsyncDisplayKit/Source/_ASDisplayViewAccessiblity.mm +++ b/submodules/AsyncDisplayKit/Source/_ASDisplayViewAccessiblity.mm @@ -203,56 +203,87 @@ static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, U /// Collect all accessibliity elements for a given view and view node static void CollectAccessibilityElementsForView(UIView *view, NSMutableArray *elements) { - ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); - - ASDisplayNode *node = view.asyncdisplaykit_node; - - static Class displayListViewClass = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - displayListViewClass = NSClassFromString(@"Display.ListView"); - }); - BOOL anySubNodeIsCollection = (nil != ASDisplayNodeFindFirstNode(node, - ^BOOL(ASDisplayNode *nodeToCheck) { + ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); + + ASDisplayNode *node = view.asyncdisplaykit_node; + + static Class displayListViewClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + displayListViewClass = NSClassFromString(@"Display.ListView"); + }); + BOOL anySubNodeIsCollection = (nil != ASDisplayNodeFindFirstNode(node, + ^BOOL(ASDisplayNode *nodeToCheck) { if (displayListViewClass != nil && [nodeToCheck isKindOfClass:displayListViewClass]) { - return true; + return true; + } + return false; + /*return ASDynamicCast(nodeToCheck, ASCollectionNode) != nil || + ASDynamicCast(nodeToCheck, ASTableNode) != nil;*/ + })); + + if (node.isAccessibilityContainer && !anySubNodeIsCollection) { + CollectAccessibilityElementsForContainer(node, view, elements); + return; + } + + // Handle rasterize case + if (node.rasterizesSubtree) { + CollectUIAccessibilityElementsForNode(node, node, view, elements); + return; + } + + if (!node.isLayerBacked) { + for (UIView *subview in node.view.subviews) { + ASDisplayNode *subnode = subview.asyncdisplaykit_node; + if (subnode) { + if (subnode.isAccessibilityElement) { + // An accessiblityElement can either be a UIView or a UIAccessibilityElement + if (subnode.isLayerBacked) { + // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node + UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:subnode containerNode:node]; + [elements addObject:accessiblityElement]; + } else { + // Accessiblity element is not layer backed just add the view as accessibility element + [elements addObject:subnode.view]; + } + } else if (subnode.isLayerBacked) { + // Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement + CollectUIAccessibilityElementsForNode(subnode, node, view, elements); + } else if ([subnode accessibilityElementCount] > 0) { + // UIView is itself a UIAccessibilityContainer just add it + [elements addObject:subnode.view]; + } + } else { + // Plain UIView without an associated ASDisplayNode + if (subview.isAccessibilityElement) { + [elements addObject:subview]; + } else if ([subview accessibilityElementCount] > 0) { + [elements addObject:subview]; + } + } + } + } else { + for (ASDisplayNode *subnode in node.subnodes) { + if (subnode.isAccessibilityElement) { + // An accessiblityElement can either be a UIView or a UIAccessibilityElement + if (subnode.isLayerBacked) { + // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node + UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:subnode containerNode:node]; + [elements addObject:accessiblityElement]; + } else { + // Accessiblity element is not layer backed just add the view as accessibility element + [elements addObject:subnode.view]; + } + } else if (subnode.isLayerBacked) { + // Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement + CollectUIAccessibilityElementsForNode(subnode, node, view, elements); + } else if ([subnode accessibilityElementCount] > 0) { + // UIView is itself a UIAccessibilityContainer just add it + [elements addObject:subnode.view]; + } } - return false; - /*return ASDynamicCast(nodeToCheck, ASCollectionNode) != nil || - ASDynamicCast(nodeToCheck, ASTableNode) != nil;*/ - })); - - if (node.isAccessibilityContainer && !anySubNodeIsCollection) { - CollectAccessibilityElementsForContainer(node, view, elements); - return; - } - - // Handle rasterize case - if (node.rasterizesSubtree) { - CollectUIAccessibilityElementsForNode(node, node, view, elements); - return; - } - - for (ASDisplayNode *subnode in node.subnodes) { - if (subnode.isAccessibilityElement) { - - // An accessiblityElement can either be a UIView or a UIAccessibilityElement - if (subnode.isLayerBacked) { - // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node - UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:subnode containerNode:node]; - [elements addObject:accessiblityElement]; - } else { - // Accessiblity element is not layer backed just add the view as accessibility element - [elements addObject:subnode.view]; - } - } else if (subnode.isLayerBacked) { - // Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement - CollectUIAccessibilityElementsForNode(subnode, node, view, elements); - } else if ([subnode accessibilityElementCount] > 0) { - // UIView is itself a UIAccessibilityContainer just add it - [elements addObject:subnode.view]; } - } } @interface _ASDisplayView () { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift index 74d988bf45..d09dee51a0 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift @@ -117,7 +117,7 @@ final class AuthorizationSequenceSignUpController: ViewController { items.append(.separator) items.append(.action(ContextMenuActionItem(text: presentationData.strings.Login_Announce_Notify, icon: { theme in if !announceSignUp { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] _, a in @@ -128,7 +128,7 @@ final class AuthorizationSequenceSignUpController: ViewController { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Login_Announce_DontNotify, icon: { theme in if announceSignUp { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] _, a in diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 183df4dc5a..ea091d519a 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -3742,7 +3742,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsTopics, icon: { theme in if !isViewingAsTopics { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak sourceController] _, a in @@ -3778,7 +3778,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsMessages, icon: { theme in if isViewingAsTopics { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak sourceController] _, a in diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 9f74cd0ce5..988eb0aa81 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -5587,7 +5587,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_AllChats, icon: { theme in - return scope == .everywhere ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return scope == .everywhere ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { [weak self] _, f in guard let self else { return @@ -5596,7 +5596,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.searchScopePromise.set(.everywhere) }))) items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_PrivateChats, icon: { theme in - return scope == .privateChats ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return scope == .privateChats ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { [weak self] _, f in guard let self else { return @@ -5605,7 +5605,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.searchScopePromise.set(.privateChats) }))) items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_GroupChats, icon: { theme in - return scope == .groups ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return scope == .groups ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { [weak self] _, f in guard let self else { return @@ -5614,7 +5614,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.searchScopePromise.set(.groups) }))) items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_Channels, icon: { theme in - return scope == .channels ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return scope == .channels ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { [weak self] _, f in guard let self else { return diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index 785965b4b4..36cb9dabbe 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -896,7 +896,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { ) } counterLayout = counterValue - if spec.component.count != 0 { + if spec.component.count != 0 || hasTitle { size.width += spacing + counterValue.size.width } else { size.width -= 1.0 diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 48790dabae..9faf3d495b 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -3350,7 +3350,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if isSelected && value == nil { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white) } else { - return nil + return UIImage() } }), action: { _, f in f(.default) @@ -3383,7 +3383,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if isSelected { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white) } else { - return nil + return UIImage() } }, action: { [weak strongSelf] _, f in f(.default) @@ -3437,7 +3437,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if isSelected { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white) } else { - return nil + return UIImage() } }, action: { [weak self] _, f in f(.default) diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift index 7d21c5f759..7be3cda903 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift @@ -401,7 +401,7 @@ public func storageUsageExceptionsScreen( if currentValue == value { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in applyValue(value) diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index 2e0a541e51..7e5ef1c723 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -571,7 +571,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi if isSelected && value == nil { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }), action: { [weak self] _, f in scheduleTooltip(nil) diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift index 423d5311dd..b47e6d164a 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift @@ -338,7 +338,7 @@ extension VideoChatScreenComponent.View { if isSelected { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white) } else { - return nil + return UIImage() } }, action: { [weak self] _, f in f(.default) @@ -730,7 +730,7 @@ extension VideoChatScreenComponent.View { if output == currentOutput { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) } else { - return nil + return UIImage() } }, action: { [weak self] _, f in f(.default) @@ -763,7 +763,7 @@ extension VideoChatScreenComponent.View { items.append(.separator) items.append(.action(ContextMenuActionItem(text: environment.strings.VoiceChat_SpeakPermissionEveryone, icon: { theme in if isMuted { - return nil + return UIImage() } else { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) } @@ -777,7 +777,7 @@ extension VideoChatScreenComponent.View { }))) items.append(.action(ContextMenuActionItem(text: environment.strings.VoiceChat_SpeakPermissionAdmin, icon: { theme in if !isMuted { - return nil + return UIImage() } else { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) } diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index d61d72ce43..7ffcf2916a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -1172,9 +1172,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.touchDownGestureRecognizer = recognizer textInputNode.textView.accessibilityHint = self.textPlaceholderNode.attributedText?.string - - self.isAccessibilityContainer = true - self.accessibilityElements = [textInputNode.textView] } private func textFieldMaxHeight(_ maxHeight: CGFloat, metrics: LayoutMetrics, bottomInset: CGFloat) -> CGFloat { diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift index bc21e267ef..2393e57cfc 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextActionsContainerNode.swift @@ -549,18 +549,18 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { shimmeringForegroundColor = presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.07) } - let textRightInset: CGFloat + let textRightInset: CGFloat = 8.0 + var textLeftInset: CGFloat = horizontalInset + if let _ = self.iconNode.image { - textRightInset = iconSize.width - 2.0 - } else { - textRightInset = 0.0 + textLeftInset = 60.0 } let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - horizontalInset * 2.0 - textRightInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil)) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, minimumNumberOfLines: 0, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: width - textLeftInset - textRightInset, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil)) let _ = textApply(self.arguments?.withUpdatedPlaceholderColor(shimmeringForegroundColor)) - let textFrame = CGRect(origin: CGPoint(x: horizontalInset, y: topInset), size: textLayout.size) + let textFrame = CGRect(origin: CGPoint(x: textLeftInset, y: topInset), size: textLayout.size) transition.updateFrame(node: self.textNode.textNode, frame: textFrame) if textFrame.size.height.isZero { self.textNode.textNode.alpha = 0.0 diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift index 482829fcef..cc6168d58e 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift @@ -1488,7 +1488,6 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context } } - self.sourceExtractableContainer = nil self.contentContainer.frame = CGRect(origin: CGPoint(), size: sourceSize) self.contentContainer.layer.cornerRadius = normalCornerRadius @@ -1553,7 +1552,7 @@ public final class ContextControllerActionsStackNodeImpl: ASDisplayNode, Context self.backgroundView.update(size: size, cornerRadius: min(30.0, size.height * 0.5), isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: true, transition: transition) if let sourceExtractableContainer = self.sourceExtractableContainer { - transition.setFrame(view: sourceExtractableContainer.extractableContentView, frame: CGRect(origin: CGPoint(), size: size)) + transition.setFrame(view: sourceExtractableContainer.extractableContentView, frame: CGRect(origin: CGPoint(x: self.backgroundContainerInset, y: self.backgroundContainerInset), size: size)) sourceExtractableContainer.updateState(state: .extracted(size: size, cornerRadius: min(30.0, size.height * 0.5), state: .animatedIn), transition: .transition(transition.containedViewLayoutTransition), completion: nil) } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift index 2defbe16a3..ac2c3e8a56 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftAttributeListContextItem.swift @@ -120,7 +120,7 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen return ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, customTextInsets: UIEdgeInsets(top: 0.0, left: 18.0 + 5.0, bottom: 0.0, right: 0.0), parseMarkdown: true, icon: { _ in return nil }, additionalLeftIcon: { theme in - return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in getController()?.dismiss(result: .dismissWithoutContent, completion: nil) @@ -181,7 +181,7 @@ private func actionForAttribute(attribute: StarGift.UniqueGift.Attribute, presen return ContextMenuActionItem(text: title, entities: entities, icon: { _ in return generateGradientFilledCircleImage(diameter: 24.0, colors: [UIColor(rgb: UInt32(bitPattern: innerColor)).cgColor, UIColor(rgb: UInt32(bitPattern: outerColor)).cgColor]) }, additionalLeftIcon: { theme in - return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return isSelected ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in getController()?.dismiss(result: .dismissWithoutContent, completion: nil) diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 603a4cead6..22b6ad70fc 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -598,6 +598,9 @@ public class GlassBackgroundView: UIView { } } + if let nativeParamsView = self.nativeParamsView { + transition.setFrame(view: nativeParamsView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + } transition.setFrame(view: self.maskContainerView, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width + shadowInset * 2.0, height: size.height + shadowInset * 2.0))) transition.setFrame(view: self.maskContentView, frame: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: size)) if let foregroundView = self.foregroundView { @@ -631,7 +634,7 @@ public final class GlassBackgroundContainerView: UIView { } public override init(frame: CGRect) { - if #available(iOS 26.0, *) { + if #available(iOS 26.0, *), !GlassBackgroundView.useCustomGlassImpl { let effect = UIGlassContainerEffect() effect.spacing = 7.0 let nativeView = UIVisualEffectView(effect: effect) @@ -707,6 +710,9 @@ public final class GlassBackgroundContainerView: UIView { nativeParamsView.lumaMin = 0.8 nativeParamsView.lumaMax = 0.801 } + if let nativeParamsView = self.nativeParamsView { + transition.setFrame(view: nativeParamsView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + } transition.animateView { nativeView.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index 99893fe804..783cd74b77 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -408,7 +408,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_ViewOnce, icon: { theme in - return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, a in a(.default) @@ -419,7 +419,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { for value in values { items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_Seconds(value), icon: { theme in - return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, a in a(.default) @@ -428,7 +428,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { } items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_DoNotDelete, icon: { theme in - return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, a in a(.default) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 91567bcc58..39239758ff 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -7286,7 +7286,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID color: theme.contextMenu.primaryColor ) } else { - return nil + return UIImage() } }, action: { [weak self] _, a in diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift index 01562c6935..0bddee3634 100644 --- a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -561,7 +561,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi if isSelected && value == nil { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }), action: { [weak self] _, f in scheduleTooltip(nil) diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift index 276942da8a..deb9abef7b 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift @@ -476,7 +476,7 @@ final class AffiliateProgramSetupScreenComponent: Component { if isSelected { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) } else { - return nil + return UIImage() } }, action: { [weak self] _, f in f(.default) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift index d767bdda1e..b43ac185cc 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift @@ -172,28 +172,28 @@ extension PeerInfoScreenNode { } items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in - return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.unlimited) }, longPressAction: { _, f in switchToFilter(.unlimited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in - return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.limitedNonUpgradable) }, longPressAction: { _, f in switchToFilter(.limitedNonUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in - return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.limitedUpgradable) }, longPressAction: { _, f in switchToFilter(.limitedUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in - return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.unique) }, longPressAction: { _, f in @@ -204,14 +204,14 @@ extension PeerInfoScreenNode { items.append(.separator) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in - return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.displayed) }, longPressAction: { _, f in switchToVisiblityFilter(.displayed) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in - return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.hidden) }, longPressAction: { _, f in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 5c5b6d91e1..dad0464fe4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -381,28 +381,28 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { } items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in - return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.unlimited) }, longPressAction: { _, f in switchToFilter(.unlimited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in - return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.limitedNonUpgradable) }, longPressAction: { _, f in switchToFilter(.limitedNonUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in - return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.limitedUpgradable) }, longPressAction: { _, f in switchToFilter(.limitedUpgradable) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in - return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.unique) }, longPressAction: { _, f in @@ -412,14 +412,14 @@ public final class AddGiftsScreen: ViewControllerComponentContainer { items.append(.separator) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in - return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.displayed) }, longPressAction: { _, f in switchToVisiblityFilter(.displayed) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in - return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage() }, action: { _, f in toggleFilter(.hidden) }, longPressAction: { _, f in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index e407168dcb..e12b0171b2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -1033,7 +1033,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let isAdded = gift.collectionIds?.contains(collection.id) ?? false subItems.append(.action(ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, icon: { theme in - return entities.isEmpty ? generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/Collection"), color: theme.contextMenu.primaryColor) : (isAdded ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil) + return entities.isEmpty ? generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/Collection"), color: theme.contextMenu.primaryColor) : (isAdded ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()) }, iconPosition: collection.icon == nil ? .left : .right, action: { [weak self] _, f in f(.default) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index d5361bc583..2c9cc45a15 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -306,7 +306,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon return } - if mainChannel.hasPermission(.manageDirect) { + if !mainChannel.isMonoForum || mainChannel.hasPermission(.manageDirect) { let displayPeer = EnginePeer(mainChannel) let controller = PeerSelectionControllerImpl( diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 9dee157d98..d6d8688428 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -286,10 +286,12 @@ final class PeerSelectionControllerNode: ASDisplayNode { return } self.chatListNode?.clearHighlightAnimated(true) + self.mainContainerNode?.currentItemNode.clearHighlightAnimated(true) self.requestOpenPeer?(mainPeer, peer.id.toInt64()) }) } else { self.chatListNode?.clearHighlightAnimated(true) + self.mainContainerNode?.currentItemNode.clearHighlightAnimated(true) self.requestOpenPeer?(peer, threadId) } } @@ -488,7 +490,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if canHideNames { items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in if hideNames { - return nil + return UIImage() } else { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } @@ -506,7 +508,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if hideNames { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in self?.interfaceInteraction?.updateForwardOptionsState({ current in @@ -523,7 +525,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if hasCaptions { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in if hideCaptions { - return nil + return UIImage() } else { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } @@ -543,7 +545,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if hideCaptions { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in self?.interfaceInteraction?.updateForwardOptionsState({ current in diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 822bac6fd8..120b2b2f5c 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -958,7 +958,7 @@ final class ShareWithPeersScreenComponent: Component { let isSelected = self.shareToFolders.contains(where: { $0.id == folderPreview.folder.id }) items.append(.action(ContextMenuActionItem(text: folderPreview.folder.title, icon: icon, additionalLeftIcon: { theme in if !isSelected { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, iconSource: iconSource, iconPosition: .left, action: { [weak self] c, f in diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 254f31d999..90c0a9f909 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -3241,7 +3241,7 @@ final class StorageUsageScreenComponent: Component { if currentValue == value { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in applyValue(value) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 9f984e1bc3..9c4c730618 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -6388,7 +6388,7 @@ public final class StoryItemSetContainerComponent: Component { if isSelected && value == nil { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white) } else { - return nil + return UIImage() } }), action: { [weak self] _, f in f(.default) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift index 23e3c7949a..1a1ff8c8e4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift @@ -1397,7 +1397,7 @@ public final class StoryItemSetViewListComponent: Component { return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/Repost"), color: theme.contextMenu.primaryColor) }, additionalLeftIcon: { theme in if sortMode != .repostsFirst { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -1416,7 +1416,7 @@ public final class StoryItemSetViewListComponent: Component { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: theme.contextMenu.primaryColor) }, additionalLeftIcon: { theme in if sortMode != .reactionsFirst { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -1435,7 +1435,7 @@ public final class StoryItemSetViewListComponent: Component { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Time"), color: theme.contextMenu.primaryColor) }, additionalLeftIcon: { theme in if sortMode != .recentFirst { - return nil + return UIImage() } return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in diff --git a/submodules/TelegramUI/Components/VideoPlaybackControlsComponent/Sources/VideoPlaybackControlsComponent.swift b/submodules/TelegramUI/Components/VideoPlaybackControlsComponent/Sources/VideoPlaybackControlsComponent.swift index 3804ed3694..3a9e2c9ec5 100644 --- a/submodules/TelegramUI/Components/VideoPlaybackControlsComponent/Sources/VideoPlaybackControlsComponent.swift +++ b/submodules/TelegramUI/Components/VideoPlaybackControlsComponent/Sources/VideoPlaybackControlsComponent.swift @@ -167,6 +167,7 @@ public final class VideoPlaybackControlsComponent: Component { self.leftIconView.update(size: leftButtonFrame.size) transition.setAlpha(view: self.leftIconView, alpha: areSideButtonsVisible ? 1.0 : 0.0) transition.setBlur(layer: self.leftIconView.layer, radius: areSideButtonsVisible ? 0.0 : 10.0) + self.leftButtonBackgroundView.isUserInteractionEnabled = areSideButtonsVisible transition.setFrame(view: self.rightButtonBackgroundView, frame: rightButtonFrame) self.rightButtonBackgroundView.update(size: rightButtonFrame.size, cornerRadius: rightButtonFrame.height * 0.5, isDark: true, tintColor: buttonsTintColor, isInteractive: true, isVisible: areSideButtonsVisible, transition: transition) @@ -174,6 +175,7 @@ public final class VideoPlaybackControlsComponent: Component { self.rightIconView.update(size: rightButtonFrame.size) transition.setAlpha(view: self.rightIconView, alpha: areSideButtonsVisible ? 1.0 : 0.0) transition.setBlur(layer: self.rightIconView.layer, radius: areSideButtonsVisible ? 0.0 : 10.0) + self.rightButtonBackgroundView.isUserInteractionEnabled = areSideButtonsVisible transition.setFrame(view: self.centerButtonBackgroundView, frame: centerButtonFrame) self.centerButtonBackgroundView.update(size: centerButtonFrame.size, cornerRadius: centerButtonFrame.height * 0.5, isDark: true, tintColor: buttonsTintColor, isInteractive: true, isVisible: component.isVisible, transition: transition) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index d732cdb895..3b886be589 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -3903,7 +3903,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto loadState = .messages } else if let historyView = strongSelf.historyView { if historyView.filteredEntries.isEmpty { - if let firstEntry = historyView.originalView.entries.first { + if historyView.originalView.isLoading { + loadState = .loading(false) + } else if let firstEntry = historyView.originalView.entries.first { var emptyType = ChatHistoryNodeLoadState.EmptyType.generic for media in firstEntry.message.media { if let action = media as? TelegramMediaAction { diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index b092e1c6a7..e41a064db8 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -1185,7 +1185,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] if currentValue == 0 { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in applyValue(0) @@ -1208,7 +1208,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] if currentValue == value { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }, action: { _, f in applyValue(value) diff --git a/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift b/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift index 4b8642e1f6..3dbada70bc 100644 --- a/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift +++ b/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift @@ -97,7 +97,7 @@ final class NotificationItemContainerNode: ASDisplayNode { if let contentNode = self.contentNode { let inset: CGFloat = 8.0 - var contentInsets = UIEdgeInsets(top: inset, left: inset + layout.safeInsets.left, bottom: inset, right: inset + layout.safeInsets.right) + var contentInsets = UIEdgeInsets(top: inset + layout.safeInsets.left, left: inset, bottom: inset, right: inset + layout.safeInsets.right) if let statusBarHeight = layout.statusBarHeight, statusBarHeight >= 39.0 { if layout.deviceMetrics.hasDynamicIsland { @@ -109,7 +109,7 @@ final class NotificationItemContainerNode: ASDisplayNode { } } - let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left) + let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0) let contentWidth = containerWidth - contentInsets.left - contentInsets.right let contentHeight = contentNode.updateLayout(width: contentWidth, transition: transition) diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 2c8eeada9e..86550b5cee 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -1310,7 +1310,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if isSelected && value == nil { return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) } else { - return nil + return UIImage() } }), action: { [weak self] _, f in scheduleTooltip(nil) diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m index 3be2d1b264..aa59f705f0 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m @@ -513,8 +513,10 @@ void WKWebsiteDataStoreReinitializeAppBoundDomains(CFTypeRef dataStoreRef); - (id)_65087dc8_objectForInfoDictionaryKey:(NSString *)key { if ([key isEqualToString:@"WKAppBoundDomains"]) { - //NSLog(@"Returning trusted domains: %@", [WebHelpers threadSafeTrustedDomains]); - return [WebHelpers threadSafeTrustedDomains]; + NSArray *result = [WebHelpers threadSafeTrustedDomains]; + if (result.count != 0) { + return result; + } } return [self _65087dc8_objectForInfoDictionaryKey:key]; } From 002df956d21185f5035c716a45e01a85d6a5e0d8 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 17:14:35 +0400 Subject: [PATCH 05/13] Bug fixes --- submodules/AudioBlob/Sources/BlobView.swift | 11 +++++++ .../PeerChatTopIndexableMessageIds.swift | 6 ++++ submodules/Postbox/Sources/Postbox.swift | 8 ++++- .../Sources/PeerDisplayName.swift | 2 +- .../ChatTextInputMediaRecordingButton.swift | 1 + .../ContextControllerActionsStackNode.swift | 33 ++++++++++++++++--- .../Components/ShareExtensionContext/BUILD | 1 + .../Sources/ShareExtensionContext.swift | 5 +++ .../Sources/ChatHistoryListNode.swift | 8 +++-- third-party/libyuv/BUILD | 1 + third-party/opus/BUILD | 18 ++++++++++ third-party/opus/build-opus-bazel.sh | 24 ++++++++++++-- third-party/webrtc/absl/BUILD | 8 ++++- third-party/webrtc/crc32c/BUILD | 1 + third-party/webrtc/libsrtp/BUILD | 1 + 15 files changed, 115 insertions(+), 13 deletions(-) diff --git a/submodules/AudioBlob/Sources/BlobView.swift b/submodules/AudioBlob/Sources/BlobView.swift index 11a8a47ae4..559fb39d2d 100644 --- a/submodules/AudioBlob/Sources/BlobView.swift +++ b/submodules/AudioBlob/Sources/BlobView.swift @@ -55,6 +55,8 @@ public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDeco private(set) var isAnimating = false + public var hitTestSize: CGFloat? + public typealias BlobRange = (min: CGFloat, max: CGFloat) public init( @@ -224,6 +226,15 @@ public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDeco self.updateBlobsState() } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let hitTestSize = self.hitTestSize { + if !CGSize(width: hitTestSize, height: hitTestSize).centered(in: self.bounds).contains(point) { + return nil + } + } + return super.hitTest(point, with: event) + } } final class BlobNode: ASDisplayNode { diff --git a/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift b/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift index 84e6c9969d..f4e0c55a33 100644 --- a/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift +++ b/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift @@ -83,12 +83,14 @@ final class PeerChatTopTaggedMessageIdsTable: Table { let currentTopMessageId = self.get(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace) if currentTopMessageId == nil || currentTopMessageId! < message.id { self.set(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace, id: message.id) + self.set(peerId: message.id.peerId, threadId: 0, namespace: message.id.namespace, id: message.id) } } case let .Remove(indices): for (index, _, threadId) in indices { if let messageId = self.get(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), index.id == messageId { self.set(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, id: nil) + self.set(peerId: index.id.peerId, threadId: 0, namespace: index.id.namespace, id: nil) } } default: @@ -110,8 +112,12 @@ final class PeerChatTopTaggedMessageIdsTable: Table { if let maybeMessageId = maybeMessageId { var messageIdId: Int32 = maybeMessageId.id self.valueBox.set(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false)) + if record.peerAndThreadId.threadId != nil { + self.valueBox.set(self.table, key: self.key(combinedId: PeerAndThreadId(peerId: record.peerAndThreadId.peerId, threadId: 0), namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false)) + } } else { self.valueBox.remove(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), secure: false) + self.valueBox.remove(self.table, key: self.key(combinedId: PeerAndThreadId(peerId: record.peerAndThreadId.peerId, threadId: 0), namespace: record.namespace), secure: false) } } } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 396fe12a46..232bb7dfe4 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -3507,7 +3507,13 @@ final class PostboxImpl { } if let peerId = mainPeerIdForTopTaggedMessages { for namespace in topTaggedMessageIdNamespaces { - if let messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId.peerId, threadId: peerId.threadId, namespace: namespace) { + var messageId: MessageId? + messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId.peerId, threadId: peerId.threadId, namespace: namespace) + if messageId == nil && peerId.threadId == nil { + messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId.peerId, threadId: 0, namespace: namespace) + } + + if let messageId { if let index = self.messageHistoryIndexTable.getIndex(messageId) { if let message = self.messageHistoryTable.getMessage(index) { topTaggedMessages[namespace] = MessageHistoryTopTaggedMessage.intermediate(message) diff --git a/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift b/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift index 21c26624cf..cb12788532 100644 --- a/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift +++ b/submodules/TelegramStringFormatting/Sources/PeerDisplayName.swift @@ -12,7 +12,7 @@ public func stringForFullAuthorName(message: EngineMessage, strings: Presentatio } else if author.isDeleted { authorName = strings.User_DeletedAccount } else { - authorName = author.compactDisplayTitle + authorName = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder) } if let peer = message.peers[message.id.peerId].flatMap(EnginePeer.init), author.id != peer.id { authorString = [authorName, peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)] diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift index 5dd97ec41b..88cf765598 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift @@ -322,6 +322,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM ) let theme = self.hidesOnLock ? defaultDarkColorPresentationTheme : self.theme blobView.setColor(theme.chat.inputPanel.actionControlFillColor) + blobView.hitTestSize = 110.0 self.micDecorationValue = blobView return blobView } diff --git a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift index cc6168d58e..1a1e7bb9e4 100644 --- a/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/TelegramUI/Components/ContextControllerImpl/Sources/ContextControllerActionsStackNode.swift @@ -827,6 +827,17 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio self.addSubnode(item.node) } + if let tipSignal = tipSignal { + self.tipDisposable = (tipSignal + |> deliverOnMainQueue).start(next: { [weak self] tip in + guard let strongSelf = self else { + return + } + strongSelf.tip = tip + requestUpdate(.immediate) + }).strict() + } + requestUpdateAction = { [weak self] id, action in guard let self else { return @@ -981,17 +992,26 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio if let tip = self.tip { let tipNode: InnerTextSelectionTipContainerNode var tipTransition = transition - if let current = self.tipNode { + if let current = self.tipNode, current.tip == tip { tipNode = current } else { tipTransition = .immediate + + let previousTipNode = self.tipNode tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip, isInline: true) - self.addSubnode(tipNode) - self.tipNode = tipNode let getController = self.getController tipNode.requestDismiss = { completion in getController()?.dismiss(completion: completion) } + self.addSubnode(tipNode) + self.tipNode = tipNode + + if let previousTipNode = previousTipNode { + previousTipNode.animateTransitionInside(other: tipNode) + previousTipNode.removeFromSupernode() + + tipNode.animateContentIn() + } } let tipSeparatorNode: ContextControllerActionsListSeparatorItemNode @@ -1004,12 +1024,15 @@ public final class ContextControllerActionsListStackItem: ContextControllerActio } let (tipSeparatorMinSize, tipSeparatorApply) = tipSeparatorNode.update(presentationData: presentationData, constrainedSize: CGSize(width: combinedSize.width, height: 10.0)) + tipSeparatorNode.isHidden = self.itemNodes.isEmpty let tipSeparatorSize = CGSize(width: combinedSize.width, height: tipSeparatorMinSize.height) tipSeparatorApply(tipSeparatorSize, tipTransition) let tipSeparatorFrame = CGRect(origin: nextItemOrigin, size: tipSeparatorSize) tipTransition.updateFrame(node: tipSeparatorNode, frame: tipSeparatorFrame) - nextItemOrigin.y += tipSeparatorSize.height - combinedSize.height += tipSeparatorSize.height + if !tipSeparatorNode.isHidden { + nextItemOrigin.y += tipSeparatorSize.height + combinedSize.height += tipSeparatorSize.height + } let tipSize = tipNode.updateLayout(widthClass: .compact, presentation: .inline, width: combinedSize.width, transition: tipTransition) let tipFrame = CGRect(origin: nextItemOrigin, size: tipSize) diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/BUILD b/submodules/TelegramUI/Components/ShareExtensionContext/BUILD index 8a0c3ecf8d..becdf07725 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/BUILD +++ b/submodules/TelegramUI/Components/ShareExtensionContext/BUILD @@ -39,6 +39,7 @@ swift_library( "//submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods", "//submodules/TelegramUI/Components/PeerSelectionController", "//submodules/TelegramUI/Components/ContextMenuScreen", + "//submodules/TelegramUI/Components/NavigationBarImpl", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift index d491abba5a..9d236de495 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -31,6 +31,7 @@ import TelegramUIDeclareEncodables import TelegramAccountAuxiliaryMethods import PeerSelectionController import ContextMenuScreen +import NavigationBarImpl private var installedSharedLogger = false @@ -194,6 +195,10 @@ public class ShareRootControllerImpl { public init(initializationData: ShareRootControllerInitializationData, getExtensionContext: @escaping () -> NSExtensionContext?) { self.initializationData = initializationData self.getExtensionContext = getExtensionContext + + defaultNavigationBarImpl = { presentationData in + return NavigationBarImpl(presentationData: presentationData) + } } deinit { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 3b886be589..38d8072019 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -2324,8 +2324,12 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } var keyboardButtonsMessage = view.topTaggedMessages.first - if keyboardButtonsMessage != nil && keyboardButtonsMessage?.threadId != chatLocation.threadId { - keyboardButtonsMessage = nil + if let keyboardButtonsMessageValue = keyboardButtonsMessage { + if keyboardButtonsMessageValue.threadId != chatLocation.threadId { + if chatLocation.threadId != nil { + keyboardButtonsMessage = nil + } + } } if let keyboardButtonsMessageValue = keyboardButtonsMessage, keyboardButtonsMessageValue.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with({ $0 })) { keyboardButtonsMessage = nil diff --git a/third-party/libyuv/BUILD b/third-party/libyuv/BUILD index 402dc97822..14e2255c5e 100644 --- a/third-party/libyuv/BUILD +++ b/third-party/libyuv/BUILD @@ -25,6 +25,7 @@ arch_specific_cflags = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_flags + arm64_specific_flags, "//build-system:ios_sim_arm64": common_flags + arm64_specific_flags, "@build_bazel_rules_apple//apple:ios_x86_64": common_flags + x86_64_specific_flags, + "//conditions:default": common_flags, }) cc_library( diff --git a/third-party/opus/BUILD b/third-party/opus/BUILD index 5ea4abdd86..873cdda53d 100644 --- a/third-party/opus/BUILD +++ b/third-party/opus/BUILD @@ -29,6 +29,10 @@ genrule( BUILD_ARCH="sim_arm64" elif [ "$(TARGET_CPU)" == "ios_x86_64" ]; then BUILD_ARCH="x86_64" + elif [ "$(TARGET_CPU)" == "k8" ] || [ "$(TARGET_CPU)" == "x86_64" ]; then + BUILD_ARCH="linux_x86_64" + elif [ "$(TARGET_CPU)" == "aarch64" ] || [ "$(TARGET_CPU)" == "arm64" ]; then + BUILD_ARCH="linux_arm64" else echo "Unsupported architecture $(TARGET_CPU)" fi @@ -63,6 +67,20 @@ cc_library( srcs = [":Public/opus/lib/lib" + x + ".a" for x in libs], ) +cc_library( + name = "opus_cc", + hdrs = [":Public/opus/" + x for x in headers], + includes = [ + "Public", + ], + deps = [ + ":opus_lib", + ], + visibility = [ + "//visibility:public", + ], +) + objc_library( name = "opus", module_name = "opus", diff --git a/third-party/opus/build-opus-bazel.sh b/third-party/opus/build-opus-bazel.sh index 60c2246a61..bb79c9efc6 100755 --- a/third-party/opus/build-opus-bazel.sh +++ b/third-party/opus/build-opus-bazel.sh @@ -12,8 +12,6 @@ OPT_CFLAGS="-Os -g" OPT_LDFLAGS="" OPT_CONFIG_ARGS="" -DEVELOPER=`xcode-select -print-path` - OUTPUTDIR="$BUILD_DIR/Public" # where we will keep our sources and build from. @@ -28,7 +26,27 @@ mkdir -p $INTERDIR tar zxf "$BUILD_DIR/$SOURCE_CODE_ARCHIVE" -C $SRCDIR cd "${SRCDIR}/opus-"* -if [ "${ARCH}" == "x86_64" ]; then +if [ "${ARCH}" = "linux_x86_64" ] || [ "${ARCH}" = "linux_arm64" ]; then + mkdir -p "${INTERDIR}" + + # Keep it simple and portable for container builds. + ./configure \ + --disable-shared \ + --enable-static \ + --with-pic \ + --disable-extra-programs \ + --disable-doc \ + --disable-asm \ + --enable-intrinsics \ + ${OPT_CONFIG_ARGS} \ + --prefix="${INTERDIR}" \ + CFLAGS="$CFLAGS ${OPT_CFLAGS} -fPIC" \ + LDFLAGS="$LDFLAGS ${OPT_LDFLAGS}" + + make -j"$(getconf _NPROCESSORS_ONLN || echo 4)" + make install + exit 0 +elif [ "${ARCH}" == "x86_64" ]; then PLATFORM="iphonesimulator" EXTRA_CFLAGS="-arch ${ARCH}" EXTRA_CONFIG="--host=x86_64-apple-darwin" diff --git a/third-party/webrtc/absl/BUILD b/third-party/webrtc/absl/BUILD index 62f1d938ff..5ab49294ac 100644 --- a/third-party/webrtc/absl/BUILD +++ b/third-party/webrtc/absl/BUILD @@ -355,9 +355,15 @@ absl_sources = [ "absl/types/bad_variant_access.cc", ] +absl_inc_files = [ + f + for f in glob(["absl/**/*.inc"]) + if f not in absl_headers +] + cc_library( name = "absl", - hdrs = absl_headers, + hdrs = absl_headers + absl_inc_files, srcs = absl_sources, cxxopts = [ "-std=c++17", diff --git a/third-party/webrtc/crc32c/BUILD b/third-party/webrtc/crc32c/BUILD index 52d9138c7e..f32c5e6120 100644 --- a/third-party/webrtc/crc32c/BUILD +++ b/third-party/webrtc/crc32c/BUILD @@ -6,6 +6,7 @@ arch_specific_crc32c_sources = select({ "//build-system:ios_sim_arm64": [ "third_party/crc32c/src/crc32c_arm64.cc", ], + "//conditions:default": [], }) crc32c_sources = ["third_party/crc32c/src/" + x for x in [ diff --git a/third-party/webrtc/libsrtp/BUILD b/third-party/webrtc/libsrtp/BUILD index bc29d57cdb..624b70c184 100644 --- a/third-party/webrtc/libsrtp/BUILD +++ b/third-party/webrtc/libsrtp/BUILD @@ -37,6 +37,7 @@ arm64_specific_flags = [ arch_specific_cflags = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_flags + arm64_specific_flags, "//build-system:ios_sim_arm64": common_flags + arm64_specific_flags, + "//conditions:default": [], }) optimization_flags = select({ From 2057b724cd696a7c9d5a1c26c43f60f612dff761 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 17:17:24 +0400 Subject: [PATCH 06/13] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 44a6538fb3..194875f9b1 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "12.4", + "app": "12.4.1", "xcode": "26.2", "bazel": "8.4.2:45e9388abf21d1107e146ea366ad080eb93cb6a5f3a4a3b048f78de0bc3faffa", "macos": "26" From c313a43456f5114c4088fd1d7679170e0cc70153 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 17:40:36 +0400 Subject: [PATCH 07/13] Fix forum forwarding --- .../Sources/PeerSelectionController.swift | 15 +++++++++++++++ .../Sources/PeerSelectionControllerNode.swift | 3 +++ 2 files changed, 18 insertions(+) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 2c9cc45a15..8c370154d6 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -228,6 +228,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon replaceImpl = { [weak controller] c in controller?.replace(with: c) } + strongSelf.peerSelectionNode.pushedController = controller strongSelf.push(controller) } else { strongSelf.selectTab(id: id) @@ -330,6 +331,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon ) ) controller.peerSelected = self.peerSelected + self.peerSelectionNode.pushedController = controller self.push(controller) } else { peerSelected(.channel(peer), threadId) @@ -373,6 +375,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon ) ) controller.peerSelected = strongSelf.peerSelected + strongSelf.peerSelectionNode.pushedController = controller strongSelf.push(controller) } else { peerSelected(peer, threadId) @@ -626,4 +629,16 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } }) } + + override public func dismiss(completion: (() -> Void)? = nil) { + guard let navigationController = self.navigationController as? NavigationController else { + return + } + var viewControllers = navigationController.viewControllers + viewControllers.removeAll(where: { $0 === self }) + if let pushedController = self.peerSelectionNode.pushedController { + viewControllers.removeAll(where: { $0 === pushedController }) + } + navigationController.setViewControllers(viewControllers, animated: true) + } } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index d6d8688428..1347675e91 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -102,6 +102,8 @@ final class PeerSelectionControllerNode: ASDisplayNode { private var countPanelNode: PeersCountPanelNode? + weak var pushedController: ViewController? + private var readyValue = Promise() var ready: Signal { return self.readyValue.get() @@ -350,6 +352,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { replaceImpl = { [weak controller] c in controller?.replace(with: c) } + strongSelf.pushedController = controller strongSelf.controller?.push(controller) } self.addSubnode(mainContainerNode) From a1c3b7a58865954b52bc18ec7696107a6476bd7f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 17:49:43 +0400 Subject: [PATCH 08/13] Fix send button blur --- .../Sources/ChatTextInputPanelNode.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 7ffcf2916a..cb93cff1f3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -4378,7 +4378,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } }) - blurTransitionOut.animateBlur(layer: self.sendActionButtons.sendContainerNode.layer, fromRadius: 0.0, toRadius: sendButtonBlurOut) + blurTransitionOut.setBlur(layer: self.sendActionButtons.sendContainerNode.layer, radius: sendButtonBlurOut) if let sendButtonRadialStatusNode = self.sendActionButtons.sendButtonRadialStatusNode { alphaTransition.updateAlpha(node: sendButtonRadialStatusNode, alpha: 0.0) @@ -4409,7 +4409,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if (hasText || keepSendButtonEnabled && !mediaInputIsActive && !hasSlowModeButton) { if self.sendActionButtons.sendContainerNode.alpha.isZero && self.rightSlowModeInset.isZero { alphaTransition.updateAlpha(node: self.sendActionButtons.sendContainerNode, alpha: 1.0) - blurTransitionIn.animateBlur(layer: self.sendActionButtons.sendContainerNode.layer, fromRadius: sendButtonBlurOut, toRadius: 0.0) + blurTransitionIn.setBlur(layer: self.sendActionButtons.sendContainerNode.layer, radius: 0.0) transition.animatePositionAdditive(layer: self.sendActionButtons.sendButton.imageNode.layer, offset: CGPoint(x: -22.0, y: 18.0)) if let sendButtonRadialStatusNode = self.sendActionButtons.sendButtonRadialStatusNode { alphaTransition.updateAlpha(node: sendButtonRadialStatusNode, alpha: 1.0) @@ -4424,7 +4424,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg strongSelf.applyUpdateSendButtonIcon() } }) - blurTransitionOut.animateBlur(layer: self.sendActionButtons.sendContainerNode.layer, fromRadius: 0.0, toRadius: sendButtonBlurOut) + blurTransitionOut.setBlur(layer: self.sendActionButtons.sendContainerNode.layer, radius: sendButtonBlurOut) if let sendButtonRadialStatusNode = self.sendActionButtons.sendButtonRadialStatusNode { alphaTransition.updateAlpha(node: sendButtonRadialStatusNode, alpha: 0.0) } From 7606a5a0fe10329cf9da72591b54c5edac883c86 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 6 Feb 2026 18:03:26 +0400 Subject: [PATCH 09/13] Update --- .../UIViewController+Navigation.m | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m index aa59f705f0..7aca166f99 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m @@ -5,6 +5,7 @@ #import "NSWeakReference.h" #import +#import @interface UIViewControllerPresentingProxy : UIViewController @@ -457,8 +458,6 @@ static NSMutableDictionary *trustedWebRecords() return value; } -void WKWebsiteDataStoreReinitializeAppBoundDomains(CFTypeRef dataStoreRef); - @implementation WebHelpers + (NSArray * _Nonnull)threadSafeTrustedDomains { @@ -504,7 +503,16 @@ void WKWebsiteDataStoreReinitializeAppBoundDomains(CFTypeRef dataStoreRef); } + (void)forceRefreshTrustedDomains:(WKWebsiteDataStore * _Nonnull)websiteDataStore { - WKWebsiteDataStoreReinitializeAppBoundDomains((__bridge CFTypeRef)(websiteDataStore)); + static void (*reinitializeFunction)(CFTypeRef dataStoreRef) = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *nameString = [NSString stringWithFormat:@"WK%@", @"WebsiteDataStoreReinitializeAppBoundDomains"]; + reinitializeFunction = dlsym(RTLD_DEFAULT, [nameString UTF8String]); + }); + + if (reinitializeFunction) { + reinitializeFunction((__bridge CFTypeRef)(websiteDataStore)); + } } @end From b47660b22c4ccb3dd58fc05629497f568b6765b5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 6 Feb 2026 19:35:09 +0400 Subject: [PATCH 10/13] Various fixes --- .../Sources/GiftCraftScreen.swift | 64 +++++++++++++------ .../Sources/SelectCraftGiftScreen.swift | 15 ++++- .../Sources/GiftStoreScreen.swift | 14 ++-- .../Sources/GiftViewScreen.swift | 36 ++++++++++- 4 files changed, 100 insertions(+), 29 deletions(-) diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift index 519d1fe142..eefa74f502 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift @@ -69,6 +69,7 @@ private final class CraftGiftPageContent: Component { let starsTopUpOptionsPromise: Promise<[StarsTopUpOption]?> let selectGift: (Int32, GiftItem) -> Void let removeGift: (Int32) -> Void + let craftAnotherGift: () -> Void let dismiss: () -> Void init( @@ -86,6 +87,7 @@ private final class CraftGiftPageContent: Component { starsTopUpOptionsPromise: Promise<[StarsTopUpOption]?>, selectGift: @escaping (Int32, GiftItem) -> Void, removeGift: @escaping (Int32) -> Void, + craftAnotherGift: @escaping () -> Void, dismiss: @escaping () -> Void ) { self.context = context @@ -102,6 +104,7 @@ private final class CraftGiftPageContent: Component { self.starsTopUpOptionsPromise = starsTopUpOptionsPromise self.selectGift = selectGift self.removeGift = removeGift + self.craftAnotherGift = craftAnotherGift self.dismiss = dismiss } @@ -1203,7 +1206,13 @@ private final class CraftGiftPageContent: Component { } if let _ = view { if case let .gift(gift) = component.result { - let giftController = GiftViewScreen(context: component.context, subject: .profileGift(component.context.account.peerId, gift)) + let giftController = GiftViewScreen( + context: component.context, + subject: .profileGift(component.context.account.peerId, gift), + customAction: .init(title: environment.strings.Gift_Craft_CraftingFailed_CraftAnotherGift, action: { + component.craftAnotherGift() + }) + ) if let navigationController = controller.navigationController { navigationController.pushViewController(giftController, animated: true) @@ -1548,6 +1557,36 @@ private final class SheetContainerComponent: CombinedComponent { } } + let navigationController = environment.controller()?.navigationController as? NavigationController + let profileGiftsContext = (environment.controller() as? GiftCraftScreen)?.profileGiftsContext + let resaleContext = state.resaleContext + let starsTopUpOptionsPromise = state.starsTopUpOptionsPromise + let craftAnotherGift = { [weak navigationController] in + guard let navigationController else { + return + } + if let genericGift = externalState.starGiftsMap[component.gift.giftId] { + HapticFeedback().impact(.light) + + let selectController = SelectCraftGiftScreen( + context: component.context, + craftContext: component.craftContext, + resaleContext: resaleContext, + gift: component.gift, + genericGift: genericGift, + selectedGiftIds: Set(), + starsTopUpOptions: starsTopUpOptionsPromise.get(), + selectGift: { [weak navigationController] item in + if let navigationController{ + let craftController = GiftCraftScreen(context: component.context, gift: item.gift, profileGiftsContext: profileGiftsContext) + navigationController.pushViewController(craftController) + } + } + ) + navigationController.pushViewController(selectController) + } + } + let theme = environment.theme var colors: (UIColor, UIColor, UIColor, UIColor, UIColor) = ( @@ -1720,6 +1759,7 @@ private final class SheetContainerComponent: CombinedComponent { state.selectedGiftIds[index] = nil state.updated(transition: .spring(duration: 0.4)) }, + craftAnotherGift: craftAnotherGift, dismiss: { dismiss(true) } @@ -1791,26 +1831,8 @@ private final class SheetContainerComponent: CombinedComponent { if state.displayInfo { state.displayInfo = false state.updated(transition: .spring(duration: 0.3)) - } else if state.displayFailure, let genericGift = externalState.starGiftsMap[component.gift.giftId] { - HapticFeedback().impact(.light) - - let selectController = SelectCraftGiftScreen( - context: component.context, - craftContext: component.craftContext, - resaleContext: state.resaleContext, - gift: component.gift, - genericGift: genericGift, - selectedGiftIds: Set(), - starsTopUpOptions: state.starsTopUpOptionsPromise.get(), - selectGift: { item in - if let controller = controller() as? GiftCraftScreen, let navigationController = controller.navigationController as? NavigationController { - let craftController = GiftCraftScreen(context: component.context, gift: item.gift, profileGiftsContext: controller.profileGiftsContext) - controller.dismissAnimated() - navigationController.pushViewController(craftController) - } - } - ) - environment.controller()?.push(selectController) + } else if state.displayFailure { + craftAnotherGift() } else { HapticFeedback().impact(.medium) diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/SelectCraftGiftScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/SelectCraftGiftScreen.swift index 456486acd9..e366d5df97 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/SelectCraftGiftScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/SelectCraftGiftScreen.swift @@ -153,6 +153,8 @@ final class SelectGiftPageContent: Component { } transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight - 170.0), size: loadingSize)) + let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + var itemFrame = CGRect(origin: CGPoint(x: itemSideInset, y: contentHeight), size: itemSize) var itemsHeight: CGFloat = 0.0 var validIds: [AnyHashable] = [] @@ -245,6 +247,12 @@ final class SelectGiftPageContent: Component { } } itemTransition.setFrame(view: itemView, frame: itemFrame) + + var canCraft = true + if let profileGift = self.giftMap[gift.gift.id], let canCraftDate = profileGift.canCraftAt, currentTime < canCraftDate { + canCraft = false + } + transition.setAlpha(view: itemView, alpha: canCraft ? 1.0 : 0.5) } } @@ -414,6 +422,7 @@ final class SelectGiftPageContent: Component { contentHeight += 32.0 contentHeight = self.updateScrolling(interactive: false, transition: transition) + let originalContentHeight = contentHeight let resaleCount = component.genericGift.availability?.resale ?? 0 let saleTitle = environment.strings.Gift_Craft_Select_SaleGiftsCount(Int32(clamping: resaleCount)).uppercased() @@ -483,7 +492,11 @@ final class SelectGiftPageContent: Component { } transition.setFrame(view: storeGiftsView, frame: storeGiftsFrame) - storeGiftsView.updateScrolling(bounds: CGRect(origin: .zero, size: availableSize), transition: .immediate) + var effectiveBounds = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: 1000.0)) + if let bounds = self.currentBounds { + effectiveBounds = bounds.offsetBy(dx: 0.0, dy: -originalContentHeight) + } + storeGiftsView.updateScrolling(bounds: effectiveBounds, transition: .immediate) } contentHeight += storeGiftsSize.height contentHeight += 90.0 diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index e711deca73..a9676d8f57 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -183,7 +183,7 @@ public final class GiftStoreContentComponent: Component { if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters { topInset = component.navigationHeight } - + let visibleBounds = bounds.insetBy(dx: 0.0, dy: -10.0) if let starGifts = self.effectiveGifts { let sideInset: CGFloat = 16.0 + component.safeInsets.left @@ -1269,8 +1269,10 @@ final class GiftStoreScreenComponent: Component { guard let self, let component = self.component, let environment = self.environment else { return } - let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: starsContext) - environment.controller()?.push(controller) + Queue.mainQueue().after(0.3) { + let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: starsContext) + environment.controller()?.push(controller) + } } ))) @@ -1283,8 +1285,10 @@ final class GiftStoreScreenComponent: Component { guard let self, let component = self.component, let environment = self.environment else { return } - let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: tonContext) - environment.controller()?.push(controller) + Queue.mainQueue().after(0.3) { + let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: tonContext) + environment.controller()?.push(controller) + } } ))) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 7e082fd710..ad6c98bd12 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -4677,7 +4677,26 @@ private final class GiftViewSheetContent: CombinedComponent { ) let buttonChild: _UpdatedChildComponent - if state.canSkip { + if let controller = controller() as? GiftViewScreen, let customAction = controller.customAction { + buttonChild = button.update( + component: ButtonComponent( + background: buttonBackground, + content: AnyComponentWithIdentity( + id: AnyHashable("custom"), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: customAction.title, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + isEnabled: true, + displaysProgress: state.inProgress, + action: { [weak state] in + if let state { + customAction.action() + state.dismiss(animated: true) + } + }), + availableSize: buttonSize, + transition: context.transition + ) + } else if state.canSkip { buttonChild = button.update( component: ButtonComponent( background: buttonBackground, @@ -5630,6 +5649,16 @@ public class GiftViewScreen: ViewControllerComponentContainer { } } + public struct CustomAction { + public let title: String + public let action: () -> Void + + public init(title: String, action: @escaping () -> Void) { + self.title = title + self.action = action + } + } + private let context: AccountContext private let subject: GiftViewScreen.Subject @@ -5671,6 +5700,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { fileprivate let togglePinnedToTop: ((StarGiftReference, Bool) -> Bool)? fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)? fileprivate let openChatTheme: (() -> Void)? + fileprivate let customAction: CustomAction? public var disposed: () -> Void = {} @@ -5690,7 +5720,8 @@ public class GiftViewScreen: ViewControllerComponentContainer { updateResellStars: ((StarGiftReference, CurrencyAmount?) -> Signal)? = nil, togglePinnedToTop: ((StarGiftReference, Bool) -> Bool)? = nil, shareStory: ((StarGift.UniqueGift) -> Void)? = nil, - openChatTheme: (() -> Void)? = nil + openChatTheme: (() -> Void)? = nil, + customAction: CustomAction? = nil ) { self.context = context self.subject = subject @@ -5706,6 +5737,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { self.togglePinnedToTop = togglePinnedToTop self.shareStory = shareStory self.openChatTheme = openChatTheme + self.customAction = customAction if case let .unique(gift) = subject.arguments?.gift, gift.resellForTonOnly { self.balanceCurrency = .ton From 8babb95d7e423c47f8784fa81020d0187e2489a8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 8 Feb 2026 16:10:33 +0400 Subject: [PATCH 11/13] Fix --- .../Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift index eefa74f502..def146b4ce 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift @@ -1883,6 +1883,10 @@ private final class SheetContainerComponent: CombinedComponent { } } } + + Queue.mainQueue().after(1.0) { + craftContext.reload() + } }, error: { error in switch error { case .craftFailed: From 4b9b95cbc5261522bc4157b1a8dea41f7c42530e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 8 Feb 2026 23:27:38 +0400 Subject: [PATCH 12/13] Fix --- .../Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift index def146b4ce..f7072a03c3 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftCraftScreen/Sources/GiftCraftScreen.swift @@ -1833,6 +1833,7 @@ private final class SheetContainerComponent: CombinedComponent { state.updated(transition: .spring(duration: 0.3)) } else if state.displayFailure { craftAnotherGift() + dismiss(true) } else { HapticFeedback().impact(.medium) From 0afb17091b6b1baa2753795a8b9ea122065a3641 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 9 Feb 2026 22:55:28 +0400 Subject: [PATCH 13/13] Various improvements --- .../Sources/NotificationService.swift | 10 ++- .../Sources/DebugController.swift | 34 +++++++--- .../GalleryUI/Sources/GalleryController.swift | 10 ++- .../VoiceChatShareScreenContextItem.swift | 2 +- .../Sources/BoostSlowModeButton.swift | 4 +- .../Sources/ChatTextInputPanelNode.swift | 13 ++-- .../Sources/GlassBackgroundComponent.swift | 65 ++++++++++++------- .../Sources/LegacyGlassView.swift | 41 +++++++----- .../Chat/ChatControllerLoadDisplayNode.swift | 3 + .../TelegramUI/Sources/ChatController.swift | 6 ++ .../Sources/ExperimentalUISettings.swift | 10 ++- 11 files changed, 137 insertions(+), 61 deletions(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index bde66723dd..b154af734f 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -1980,7 +1980,15 @@ private final class NotificationServiceHandler { } if enableInlineEmoji, let textEntitiesAttribute = message.textEntitiesAttribute, let author = message.author { let authorTitle = author.debugDisplayTitle - let messagePrefix = "\(authorTitle): " + var needsPrefix = false + if message.id.peerId.namespace == Namespaces.Peer.CloudGroup { + needsPrefix = true + } else if let channel = message.peers[message.id.peerId] as? TelegramChannel { + if case .group = channel.info { + needsPrefix = true + } + } + let messagePrefix = needsPrefix ? "\(authorTitle): " : "" let messagePrefixLength = (messagePrefix as NSString).length for entity in textEntitiesAttribute.entities { if case let .CustomEmoji(_, fileId) = entity.type { diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index afc8356b4f..badaffdfe9 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -96,6 +96,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case experimentalCompatibility(Bool) case enableDebugDataDisplay(Bool) case fakeGlass(Bool) + case forceClearGlass(Bool) case browserExperiment(Bool) case allForumsHaveTabs(Bool) case enableReactionOverrides(Bool) @@ -135,7 +136,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.web.rawValue case .keepChatNavigationStack, .skipReadHistory, .alwaysDisplayTyping, .debugRatingLayout, .crashOnSlowQueries, .crashOnMemoryPressure: return DebugControllerSection.experiments.rawValue - case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .pwa, .enableLocalTranslation: + case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .forceClearGlass, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .pwa, .enableLocalTranslation: return DebugControllerSection.experiments.rawValue case .logTranslationRecognition, .resetTranslationStates: return DebugControllerSection.translation.rawValue @@ -228,24 +229,26 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 38 case .fakeGlass: return 39 - case .browserExperiment: + case .forceClearGlass: return 40 - case .allForumsHaveTabs: + case .browserExperiment: return 41 - case .enableReactionOverrides: + case .allForumsHaveTabs: return 42 - case .restorePurchases: + case .enableReactionOverrides: return 43 - case .logTranslationRecognition: + case .restorePurchases: return 44 - case .resetTranslationStates: + case .logTranslationRecognition: return 45 - case .compressedEmojiCache: + case .resetTranslationStates: return 46 - case .storiesJpegExperiment: + case .compressedEmojiCache: return 47 - case .disableReloginTokens: + case .storiesJpegExperiment: return 48 + case .disableReloginTokens: + return 49 case .checkSerializedData: return 50 case .enableQuickReactionSwitch: @@ -1279,6 +1282,16 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) + case let .forceClearGlass(value): + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Force clear glass", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings + settings.forceClearGlass = value + return PreferencesEntry(settings) + }) + }).start() + }) case let .browserExperiment(value): return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Inline UI", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in @@ -1555,6 +1568,7 @@ private func debugControllerEntries(context: AccountContext?, sharedContext: Sha entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility)) entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.fakeGlass(experimentalSettings.fakeGlass)) + entries.append(.forceClearGlass(experimentalSettings.forceClearGlass)) #if DEBUG entries.append(.browserExperiment(experimentalSettings.browserExperiment)) #else diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index eafca476d6..98becb4ca7 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -1469,8 +1469,14 @@ public class GalleryController: ViewController, StandalonePresentableController, } self.galleryNode.controlsVisibilityChanged = { [weak self] visible in - self?.prefersOnScreenNavigationHidden = !visible - self?.galleryNode.pager.centralItemNode()?.controlsVisibilityUpdated(isVisible: visible) + guard let self else { + return + } + self.prefersOnScreenNavigationHidden = !visible + + self.galleryNode.pager.forEachItemNode { itemNode in + itemNode.controlsVisibilityUpdated(isVisible: visible) + } } self.galleryNode.updateOrientation = { [weak self] orientation in diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift index e9f60b2bd7..70360e3234 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift @@ -161,7 +161,7 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM } func canBeHighlighted() -> Bool { - return true + return false } func updateIsHighlighted(isHighlighted: Bool) { diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/BoostSlowModeButton.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/BoostSlowModeButton.swift index a3c2700e73..62021ab1c2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/BoostSlowModeButton.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/BoostSlowModeButton.swift @@ -92,6 +92,8 @@ final class BoostSlowModeButton: HighlightTrackingButtonNode { self?.requestUpdate() }, queue: .mainQueue()) self.updateTimer?.start() + } else { + text = stringForDuration(0) } } else { self.updateTimer?.invalidate() @@ -115,7 +117,7 @@ final class BoostSlowModeButton: HighlightTrackingButtonNode { self.textNode.segments = segments let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true) - let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0) + let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 40.0, height: 40.0) self.containerNode.bounds = CGRect(origin: .zero, size: totalSize) self.containerNode.position = CGPoint(x: totalSize.width / 2.0, y: totalSize.height / 2.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index cb93cff1f3..1f35f5c939 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -2203,9 +2203,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg var rightSlowModeInset: CGFloat = 0.0 var slowModeButtonSize: CGSize = .zero - if let presentationInterfaceState = self.presentationInterfaceState, (presentationInterfaceState.boostsToUnrestrict ?? 0) > 0 { + var hasSlowmodeButton = false + if let presentationInterfaceState = self.presentationInterfaceState, (presentationInterfaceState.boostsToUnrestrict ?? 0) > 0, presentationInterfaceState.slowmodeState != nil { slowModeButtonSize = self.slowModeButton.update(size: CGSize(width: width, height: 40.0), interfaceState: presentationInterfaceState) - rightSlowModeInset = max(0.0, slowModeButtonSize.width - 33.0) + rightSlowModeInset = max(0.0, slowModeButtonSize.width + 4.0) + hasSlowmodeButton = true } self.rightSlowModeInset = rightSlowModeInset @@ -2392,7 +2394,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if additionalSideInsets.right > 0.0 { textFieldInsets.right += additionalSideInsets.right / 3.0 } - if inputHasText || self.extendedSearchLayout || hasMediaDraft || hasForward { + if inputHasText || self.extendedSearchLayout || hasMediaDraft || hasForward || hasSlowmodeButton { } else { if let customRightAction = self.customRightAction, case .empty = customRightAction { textFieldInsets.right = 8.0 @@ -3020,6 +3022,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg var nextButtonTopRight = CGPoint(x: textInputContainerBackgroundFrame.width - accessoryButtonInset, y: textInputContainerBackgroundFrame.height - minimalInputHeight) if self.extendedSearchLayout { nextButtonTopRight.x -= 46.0 + } else if hasSlowmodeButton { } else if inputHasText || hasMediaDraft || hasForward { nextButtonTopRight.x -= sendActionButtonsSize.width } @@ -3169,7 +3172,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } var mediaActionButtonsFrame = CGRect(origin: CGPoint(x: textInputContainerBackgroundFrame.maxX + 6.0, y: textInputContainerBackgroundFrame.maxY - mediaActionButtonsSize.height), size: mediaActionButtonsSize) - if inputHasText || self.extendedSearchLayout || hasMediaDraft || interfaceState.interfaceState.forwardMessageIds != nil { + if inputHasText || self.extendedSearchLayout || hasMediaDraft || interfaceState.interfaceState.forwardMessageIds != nil || hasSlowmodeButton { mediaActionButtonsFrame.origin.x = width + 8.0 } transition.updateFrame(node: self.mediaActionButtons, frame: mediaActionButtonsFrame) @@ -3232,7 +3235,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.sendActionButtons.updateAbsoluteRect(CGRect(x: rect.origin.x + sendActionButtonsFrame.origin.x, y: rect.origin.y + sendActionButtonsFrame.origin.y, width: sendActionButtonsFrame.width, height: sendActionButtonsFrame.height), within: containerSize, transition: transition) } - let slowModeButtonFrame = CGRect(origin: CGPoint(x: hideOffset.x + width - rightInset - 5.0 - slowModeButtonSize.width + composeButtonsOffset, y: hideOffset.y + panelHeight - minimalHeight + 6.0), size: slowModeButtonSize) + let slowModeButtonFrame = CGRect(origin: CGPoint(x: hideOffset.x + width - rightInset - 5.0 - slowModeButtonSize.width + composeButtonsOffset, y: hideOffset.y + panelHeight - minimalHeight), size: slowModeButtonSize) transition.updateFrame(node: self.slowModeButton, frame: slowModeButtonFrame) if let _ = interfaceState.inputTextPanelState.mediaRecordingState { diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 22b6ad70fc..785146b442 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -437,7 +437,21 @@ public class GlassBackgroundView: UIView { if let legacyView = self.legacyView { switch shape { case let .roundedRect(cornerRadius): - legacyView.update(size: size, cornerRadius: cornerRadius, transition: transition) + let style: LegacyGlassView.Style + switch tintColor.kind { + case .panel: + style = .normal + case .clear: + style = .clear + case let .custom(styleValue, _): + switch styleValue { + case .clear: + style = .clear + case .default: + style = .normal + } + } + legacyView.update(size: size, cornerRadius: cornerRadius, style: style, transition: transition) } transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size)) transition.setAlpha(view: legacyView, alpha: isVisible ? 1.0 : 0.0) @@ -512,19 +526,28 @@ public class GlassBackgroundView: UIView { if let foregroundView = self.foregroundView { let fillColor: UIColor + let borderWidthFactor: CGFloat switch tintColor.kind { case .panel: + borderWidthFactor = 1.0 if isDark { fillColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(.black, alpha: 1.0 - 0.11).withAlphaComponent(0.85) } else { fillColor = UIColor(white: 1.0, alpha: 0.7) } case .clear: + borderWidthFactor = 2.0 fillColor = UIColor(white: 1.0, alpha: 0.0) - case let .custom(_, color): + case let .custom(style, color): fillColor = color + switch style { + case .clear: + borderWidthFactor = 2.0 + case .default: + borderWidthFactor = 1.0 + } } - foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: fillColor) + foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, borderWidthFactor: borderWidthFactor, isDark: isDark, fillColor: fillColor) transition.setAlpha(view: foregroundView, alpha: isVisible ? 1.0 : 0.0) } else { if let nativeParamsView = self.nativeParamsView, let nativeView = self.nativeView { @@ -565,11 +588,21 @@ public class GlassBackgroundView: UIView { if glassEffect == nil { if nativeView.effect is UIGlassEffect { - if transition.animation.isImmediate { - nativeView.effect = nil - } else { - transition.animateView { + if #available(iOS 26.1, *) { + if transition.animation.isImmediate { nativeView.effect = nil + } else { + transition.animateView { + nativeView.effect = nil + } + } + } else { + if transition.animation.isImmediate { + nativeView.effect = UIVisualEffect() + } else { + transition.animateView { + nativeView.effect = UIVisualEffect() + } } } } @@ -774,7 +807,7 @@ private extension CGContext { } public extension GlassBackgroundView { - static func generateLegacyGlassImage(size: CGSize, inset: CGFloat, isDark: Bool, fillColor: UIColor) -> UIImage { + static func generateLegacyGlassImage(size: CGSize, inset: CGFloat, borderWidthFactor: CGFloat = 1.0, isDark: Bool, fillColor: UIColor) -> UIImage { var size = size if size == .zero { size = CGSize(width: 2.0, height: 2.0) @@ -914,7 +947,7 @@ public extension GlassBackgroundView { var ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset) context.fillEllipse(in: ellipseRect) - let lineWidth: CGFloat = isDark ? 0.8 : 0.8 + let lineWidth: CGFloat = (isDark ? 0.8 : 0.8) * borderWidthFactor let strokeColor: UIColor let blendMode: CGBlendMode let baseAlpha: CGFloat = isDark ? 0.3 : 0.6 @@ -962,20 +995,6 @@ public extension GlassBackgroundView { context.resetClip() context.setBlendMode(.normal) - - //let image = makeInnerShadowPillImageExact(size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), scale: UIScreenScale, glossColor: UIColor(white: 1.0, alpha: 1.0), borderWidth: 1.33) - /*let image = generateCircleImage(diameter: size.width - inset * 2.0, lineWidth: 0.5, color: UIColor(white: 1.0, alpha: 1.0))! - - if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 1.0) - } else if s <= 0.3 && !isDark { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.7) - } else if b >= 0.2 { - let maxAlpha: CGFloat = isDark ? 0.7 : 0.8 - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .overlay, alpha: max(0.5, min(1.0, maxAlpha * s))) - } else { - image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.5) - }*/ } innerImage.draw(in: CGRect(origin: CGPoint(), size: size)) }.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5)) diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift index 54a0ef4a41..4a855e53dc 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift @@ -81,13 +81,20 @@ private final class BackdropLayerDelegate: NSObject, CALayerDelegate { } final class LegacyGlassView: UIView { + enum Style { + case normal + case clear + } + private struct Params: Equatable { let size: CGSize let cornerRadius: CGFloat + let style: Style - init(size: CGSize, cornerRadius: CGFloat) { + init(size: CGSize, cornerRadius: CGFloat, style: Style) { self.size = size self.cornerRadius = cornerRadius + self.style = style } } @@ -109,19 +116,8 @@ final class LegacyGlassView: UIView { self.layer.addSublayer(backdropLayer) backdropLayer.delegate = self.backdropLayerDelegate - let blur: CGFloat - let scale: CGFloat - - blur = 2.0 - scale = 1.0 - - invokeBackdropLayerSetScaleMethod(object: backdropLayer, scale: scale) - backdropLayer.rasterizationScale = scale - - if let blurFilter = CALayer.blur() { - blurFilter.setValue(blur as NSNumber, forKey: "inputRadius") - backdropLayer.filters = [blurFilter] - } + invokeBackdropLayerSetScaleMethod(object: backdropLayer, scale: 1.0) + backdropLayer.rasterizationScale = 1.0 } } @@ -129,8 +125,9 @@ final class LegacyGlassView: UIView { fatalError("init(coder:) has not been implemented") } - func update(size: CGSize, cornerRadius: CGFloat, transition: ComponentTransition) { - let params = Params(size: size, cornerRadius: cornerRadius) + func update(size: CGSize, cornerRadius: CGFloat, style: Style, transition: ComponentTransition) { + let params = Params(size: size, cornerRadius: cornerRadius, style: style) + let previousParams = self.params if self.params == params { return } @@ -140,6 +137,18 @@ final class LegacyGlassView: UIView { return } + if previousParams?.style != style { + if let blurFilter = CALayer.blur() { + switch style { + case .clear: + blurFilter.setValue(6.0 as NSNumber, forKey: "inputRadius") + case .normal: + blurFilter.setValue(2.0 as NSNumber, forKey: "inputRadius") + } + backdropLayer.filters = [blurFilter] + } + } + transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius) transition.setFrame(layer: backdropLayer, frame: CGRect(origin: CGPoint(), size: size)) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index b4a786bf50..73e4ab9d41 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -379,6 +379,9 @@ extension ChatControllerImpl { if previousState.chatTitleContent != contentData.state.chatTitleContent { animated = true } + if previousState.slowmodeState != contentData.state.slowmodeState || previousState.boostsToUnrestrict != contentData.state.boostsToUnrestrict { + animated = true + } var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate if let forceAnimationTransition { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f344f3ce9c..0521901838 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -743,6 +743,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !self.presentationData.theme.overallDarkAppearance { preferredGlassType = .default } + if self.context.sharedContext.immediateExperimentalUISettings.forceClearGlass { + preferredGlassType = .clear + } if self.presentationInterfaceState.preferredGlassType != preferredGlassType { self.updateChatPresentationInterfaceState(transition: .immediate, interactive: false, { state in return state.updatedPreferredGlassType(preferredGlassType) @@ -6411,6 +6414,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !self.presentationData.theme.overallDarkAppearance { preferredGlassType = .default } + if self.context.sharedContext.immediateExperimentalUISettings.forceClearGlass { + preferredGlassType = .clear + } self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in var state = state state = state.updatedPresentationReady(self.didSetPresentationData) diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 4bf6e9ec9a..1fc900ca8a 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -70,6 +70,7 @@ public struct ExperimentalUISettings: Codable, Equatable { public var debugRatingLayout: Bool public var enableUpdates: Bool public var enablePWA: Bool + public var forceClearGlass: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -117,7 +118,8 @@ public struct ExperimentalUISettings: Codable, Equatable { allForumsHaveTabs: false, debugRatingLayout: false, enableUpdates: false, - enablePWA: false + enablePWA: false, + forceClearGlass: false ) } @@ -166,7 +168,8 @@ public struct ExperimentalUISettings: Codable, Equatable { allForumsHaveTabs: Bool, debugRatingLayout: Bool, enableUpdates: Bool, - enablePWA: Bool + enablePWA: Bool, + forceClearGlass: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -213,6 +216,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.debugRatingLayout = debugRatingLayout self.enableUpdates = enableUpdates self.enablePWA = enablePWA + self.forceClearGlass = forceClearGlass } public init(from decoder: Decoder) throws { @@ -263,6 +267,7 @@ public struct ExperimentalUISettings: Codable, Equatable { self.debugRatingLayout = try container.decodeIfPresent(Bool.self, forKey: "debugRatingLayout") ?? false self.enableUpdates = try container.decodeIfPresent(Bool.self, forKey: "enableUpdates") ?? false self.enablePWA = try container.decodeIfPresent(Bool.self, forKey: "enablePWA") ?? false + self.forceClearGlass = try container.decodeIfPresent(Bool.self, forKey: "forceClearGlass") ?? false } public func encode(to encoder: Encoder) throws { @@ -313,6 +318,7 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encodeIfPresent(self.debugRatingLayout, forKey: "debugRatingLayout") try container.encodeIfPresent(self.enableUpdates, forKey: "enableUpdates") try container.encodeIfPresent(self.enablePWA, forKey: "enablePWA") + try container.encodeIfPresent(self.forceClearGlass, forKey: "forceClearGlass") } }