GLEGram 12.5 — Initial public release

Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
This commit is contained in:
Leeksov
2026-04-06 09:48:12 +03:00
commit 4647310322
39685 changed files with 11052678 additions and 0 deletions
@@ -0,0 +1,226 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import AccountContext
import TelegramPresentationData
import TelegramStringFormatting
import GlassBackgroundComponent
import ComponentFlow
import ComponentDisplayAdapters
import MultilineTextComponent
private let titleFont = Font.semibold(17.0)
private let dateFont = Font.regular(12.0)
public final class GalleryTitleView: UIView, NavigationBarTitleView {
public final class Content: Equatable {
let message: EngineMessage
let title: String?
let action: (() -> Void)?
public init(message: EngineMessage, title: String?, action: (() -> Void)?) {
self.message = message
self.title = title
self.action = action
}
public static func ==(lhs: Content, rhs: Content) -> Bool {
if lhs.message != rhs.message {
return false
}
if lhs.title != rhs.title {
return false
}
if (lhs.action == nil) != (rhs.action == nil) {
return false
}
return true
}
}
private let context: AccountContext
private let presentationData: PresentationData
private let backgroundContainer: GlassBackgroundContainerView
private let backgroundView: GlassBackgroundView
private let authorNameNode: ASTextNode
private let dateNode: ASTextNode
private let titleBackgroundContainer: GlassBackgroundContainerView
private let titleBackground: GlassBackgroundView
private let title = ComponentView<Empty>()
private var content: Content?
private var titleString: String?
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
private var interactionTimer: Foundation.Timer?
public init(context: AccountContext, presentationData: PresentationData) {
self.context = context
self.presentationData = presentationData
self.backgroundContainer = GlassBackgroundContainerView()
self.backgroundView = GlassBackgroundView()
self.backgroundContainer.contentView.addSubview(self.backgroundView)
self.authorNameNode = ASTextNode()
self.authorNameNode.isUserInteractionEnabled = false
self.authorNameNode.displaysAsynchronously = false
self.authorNameNode.maximumNumberOfLines = 1
self.dateNode = ASTextNode()
self.dateNode.isUserInteractionEnabled = false
self.dateNode.displaysAsynchronously = false
self.dateNode.maximumNumberOfLines = 1
self.titleBackgroundContainer = GlassBackgroundContainerView()
self.titleBackground = GlassBackgroundView()
self.titleBackgroundContainer.contentView.addSubview(self.titleBackground)
super.init(frame: CGRect())
self.addSubview(self.backgroundContainer)
self.backgroundView.contentView.addSubview(self.authorNameNode.view)
self.backgroundView.contentView.addSubview(self.dateNode.view)
self.backgroundContainer.isHidden = true
self.addSubview(self.titleBackgroundContainer)
self.titleBackgroundContainer.alpha = 0.0
self.backgroundView.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.interactionTimer?.invalidate()
}
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.content?.action?()
}
}
public func setContent(content: Content?) {
self.content = content
self.backgroundContainer.isHidden = self.content == nil
if let content {
let authorNameText = stringForFullAuthorName(message: content.message, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, accountPeerId: self.context.account.peerId).first ?? ""
let dateText = humanReadableStringForTimestamp(strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, timestamp: content.message.timestamp).string
self.authorNameNode.attributedText = NSAttributedString(string: authorNameText, font: titleFont, textColor: .white)
self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: UIColor(white: 1.0, alpha: 0.5))
}
self.titleString = content?.title
if !self.bounds.isEmpty {
let _ = self.updateLayout(availableSize: self.bounds.size, transition: .immediate)
}
}
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let size = availableSize
let leftInset: CGFloat = 8.0 + 14.0
let rightInset: CGFloat = 8.0 + 14.0
let authorNameSize = self.authorNameNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0 - leftInset - rightInset), height: CGFloat.greatestFiniteMagnitude))
let dateSize = self.dateNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0), height: CGFloat.greatestFiniteMagnitude))
var backgroundSize = CGSize(width: 0.0, height: 44.0)
if authorNameSize.height.isZero {
backgroundSize.width = dateSize.width
} else {
backgroundSize.width = max(authorNameSize.width, dateSize.width)
}
backgroundSize.width = max(150.0, backgroundSize.width)
backgroundSize.width += 14.0 * 2.0
if authorNameSize.height.isZero {
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - dateSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height) / 2.0)), size: dateSize)
} else {
let labelsSpacing: CGFloat = 2.0
self.authorNameNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - authorNameSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0)), size: authorNameSize)
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - dateSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
}
let backgroundFrame = CGRect(origin: CGPoint(x: floor((size.width - backgroundSize.width) * 0.5), y: floor((size.height - backgroundSize.height) * 0.5)), size: backgroundSize)
self.backgroundContainer.update(size: backgroundFrame.size, isDark: true, transition: ComponentTransition(transition))
ComponentTransition(transition).setFrame(view: self.backgroundContainer, frame: backgroundFrame)
ComponentTransition(transition).setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: true, tintColor: .init(kind: .panel), isInteractive: self.content?.action != nil, transition: ComponentTransition(transition))
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: self.titleString ?? "", font: Font.semibold(12.0), textColor: .white))
)),
environment: {},
containerSize: CGSize(width: 200.0, height: 100.0)
)
let titleInset: CGFloat = 12.0
let titleBackgroundSize = CGSize(width: titleInset * 2.0 + titleSize.width, height: 24.0)
let titleBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleBackgroundSize.width) * 0.5), y: availableSize.height + 2.0), size: titleBackgroundSize)
let titleFrame = titleSize.centered(in: CGRect(origin: CGPoint(), size: titleBackgroundSize))
if let titleView = self.title.view {
if titleView.superview == nil {
self.titleBackground.contentView.addSubview(titleView)
}
titleView.frame = titleFrame
}
do {
let transition = ComponentTransition.immediate
transition.setFrame(view: self.titleBackgroundContainer, frame: titleBackgroundFrame)
self.titleBackgroundContainer.update(size: titleBackgroundSize, isDark: true, transition: transition)
transition.setFrame(view: self.titleBackground, frame: CGRect(origin: CGPoint(), size: titleBackgroundSize))
self.titleBackground.update(size: titleBackgroundSize, cornerRadius: titleBackgroundSize.height * 0.5, isDark: true, tintColor: .init(kind: .panel), transition: transition)
self.titleBackgroundContainer.isHidden = !(self.titleString != nil && self.titleString != "")
}
return availableSize
}
public func animateLayoutTransition() {
}
public func updateIsInteracting(isInteracting: Bool) {
if isInteracting {
self.interactionTimer?.invalidate()
self.interactionTimer = nil
ComponentTransition.easeInOut(duration: 0.2).animateView {
self.titleBackgroundContainer.alpha = 1.0
}
} else {
self.interactionTimer?.invalidate()
self.interactionTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
ComponentTransition.easeInOut(duration: 0.2).animateView {
self.titleBackgroundContainer.alpha = 0.0
}
})
}
}
}