Various improvements

This commit is contained in:
Isaac 2026-02-09 22:55:28 +04:00
parent 7606a5a0fe
commit 0afb17091b
11 changed files with 137 additions and 61 deletions

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -161,7 +161,7 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
}
func canBeHighlighted() -> Bool {
return true
return false
}
func updateIsHighlighted(isHighlighted: Bool) {

View file

@ -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)

View file

@ -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 {

View file

@ -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))

View file

@ -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))

View file

@ -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 {

View file

@ -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)

View file

@ -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")
}
}