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,361 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import ComponentFlow
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import AlertComponent
import TextFieldComponent
import MultilineTextComponent
import AccountContext
import TextFormat
public final class AlertMultilineInputFieldComponent: Component {
public typealias EnvironmentType = AlertComponentEnvironment
public class ExternalState {
public fileprivate(set) var value: NSAttributedString = NSAttributedString()
public fileprivate(set) var animateError: () -> Void = {}
public fileprivate(set) var activateInput: () -> Void = {}
fileprivate let valuePromise = ValuePromise<NSAttributedString>(NSAttributedString())
public var valueSignal: Signal<NSAttributedString, NoError> {
return self.valuePromise.get()
}
public var textAndEntities: (String, [MessageTextEntity]) {
let text = self.value.string
let entities = generateChatInputTextEntities(self.value)
return (text, entities)
}
public init() {
}
}
public enum FormatMenuAvailability: Equatable {
public enum Action: CaseIterable {
case bold
case italic
case monospace
case link
case strikethrough
case underline
case spoiler
case quote
case code
public static var all: [Action] = [
.bold,
.italic,
.monospace,
.link,
.strikethrough,
.underline,
.spoiler,
.quote,
.code
]
var textFieldValue: TextFieldComponent.FormatMenuAvailability.Action {
switch self {
case .bold:
return .bold
case .italic:
return .italic
case .monospace:
return .monospace
case .link:
return .link
case .strikethrough:
return .strikethrough
case .underline:
return .underline
case .spoiler:
return .spoiler
case .quote:
return .quote
case .code:
return .code
}
}
}
case available([Action])
case none
var textFieldValue: TextFieldComponent.FormatMenuAvailability {
switch self {
case let .available(actions):
return .available(actions.map { $0.textFieldValue })
case .none:
return .none
}
}
}
public enum EmptyLineHandling {
case allowed
case oneConsecutive
case notAllowed
var textFieldValue: TextFieldComponent.EmptyLineHandling {
switch self {
case .allowed:
return .allowed
case .oneConsecutive:
return .oneConsecutive
case .notAllowed:
return .notAllowed
}
}
}
let context: AccountContext
let initialValue: NSAttributedString?
let placeholder: String
let prefix: NSAttributedString?
let characterLimit: Int?
let returnKeyType: UIReturnKeyType
let keyboardType: UIKeyboardType
let autocapitalizationType: UITextAutocapitalizationType
let autocorrectionType: UITextAutocorrectionType
let formatMenuAvailability: FormatMenuAvailability
let emptyLineHandling: EmptyLineHandling
let isInitiallyFocused: Bool
let externalState: ExternalState
let present: (ViewController) -> Void
let returnKeyAction: (() -> Void)?
public init(
context: AccountContext,
initialValue: NSAttributedString? = nil,
placeholder: String,
prefix: NSAttributedString? = nil,
characterLimit: Int? = nil,
returnKeyType: UIReturnKeyType = .default,
keyboardType: UIKeyboardType = .default,
autocapitalizationType: UITextAutocapitalizationType = .sentences,
autocorrectionType: UITextAutocorrectionType = .default,
formatMenuAvailability: FormatMenuAvailability = .none,
emptyLineHandling: EmptyLineHandling = .allowed,
isInitiallyFocused: Bool = false,
externalState: ExternalState,
present: @escaping (ViewController) -> Void = { _ in },
returnKeyAction: (() -> Void)? = nil
) {
self.context = context
self.initialValue = initialValue
self.placeholder = placeholder
self.prefix = prefix
self.characterLimit = characterLimit
self.returnKeyType = returnKeyType
self.keyboardType = keyboardType
self.autocapitalizationType = autocapitalizationType
self.autocorrectionType = autocorrectionType
self.formatMenuAvailability = formatMenuAvailability
self.emptyLineHandling = emptyLineHandling
self.isInitiallyFocused = isInitiallyFocused
self.externalState = externalState
self.present = present
self.returnKeyAction = returnKeyAction
}
public static func ==(lhs: AlertMultilineInputFieldComponent, rhs: AlertMultilineInputFieldComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.initialValue != rhs.initialValue {
return false
}
if lhs.placeholder != rhs.placeholder {
return false
}
if lhs.prefix != rhs.prefix {
return false
}
if lhs.returnKeyType != rhs.returnKeyType {
return false
}
if lhs.characterLimit != rhs.characterLimit {
return false
}
if lhs.keyboardType != rhs.keyboardType {
return false
}
if lhs.autocapitalizationType != rhs.autocapitalizationType {
return false
}
if lhs.autocorrectionType != rhs.autocorrectionType {
return false
}
if lhs.formatMenuAvailability != rhs.formatMenuAvailability {
return false
}
if lhs.emptyLineHandling != rhs.emptyLineHandling {
return false
}
if lhs.isInitiallyFocused != rhs.isInitiallyFocused {
return false
}
return true
}
public final class View: UIView {
private let background = ComponentView<Empty>()
private let textField = ComponentView<Empty>()
private let textFieldExternalState = TextFieldComponent.ExternalState()
private let placeholder = ComponentView<Empty>()
private var component: AlertMultilineInputFieldComponent?
private weak var state: EmptyComponentState?
func activateInput() {
if let textFieldView = self.textField.view as? TextFieldComponent.View {
textFieldView.activateInput()
}
}
func animateError() {
if let textFieldView = self.textField.view {
textFieldView.layer.addShakeAnimation()
}
HapticFeedback().error()
}
func update(component: AlertMultilineInputFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
var resetText: NSAttributedString?
if self.component == nil {
resetText = component.initialValue
component.externalState.animateError = { [weak self] in
self?.animateError()
}
component.externalState.activateInput = { [weak self] in
self?.activateInput()
}
}
let isFirstTime = self.component == nil
self.component = component
self.state = state
let environment = environment[AlertComponentEnvironment.self]
let topInset: CGFloat = 15.0
let horizontalInset: CGFloat = 4.0
let verticalInset: CGFloat = 11.0 - UIScreenPixel
let textFieldSize = self.textField.update(
transition: transition,
component: AnyComponent(TextFieldComponent(
context: component.context,
theme: environment.theme,
strings: environment.strings,
externalState: self.textFieldExternalState,
fontSize: 17.0,
textColor: environment.theme.actionSheet.primaryTextColor,
accentColor: environment.theme.actionSheet.controlAccentColor,
insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0),
hideKeyboard: false,
customInputView: nil,
resetText: resetText,
isOneLineWhenUnfocused: false,
characterLimit: component.characterLimit,
emptyLineHandling: component.emptyLineHandling.textFieldValue,
formatMenuAvailability: component.formatMenuAvailability.textFieldValue,
returnKeyType: component.returnKeyType,
keyboardType: component.keyboardType,
autocapitalizationType: component.autocapitalizationType,
autocorrectionType: component.autocorrectionType,
lockedFormatAction: {
},
present: { [weak self] c in
guard let self, let component = self.component else {
return
}
component.present(c)
},
paste: { _ in
},
returnKeyAction: { [weak self] in
guard let self, let component = self.component else {
return
}
component.returnKeyAction?()
},
backspaceKeyAction: {
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width + horizontalInset * 2.0, height: availableSize.height)
)
component.externalState.value = self.textFieldExternalState.text
component.externalState.valuePromise.set(component.externalState.value)
let backgroundPadding: CGFloat = 14.0
let size = CGSize(width: availableSize.width, height: max(50.0, floor(textFieldSize.height + verticalInset * 2.0)))
let backgroundSize = self.background.update(
transition: transition,
component: AnyComponent(
FilledRoundedRectangleComponent(color: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), cornerRadius: .value(25.0), smoothCorners: false)
),
environment: {},
containerSize: CGSize(width: size.width + backgroundPadding * 2.0, height: size.height)
)
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) / 2.0), y: topInset ), size: backgroundSize)
if let backgroundView = self.background.view {
if backgroundView.superview == nil {
self.addSubview(backgroundView)
}
transition.setFrame(view: backgroundView, frame: backgroundFrame)
}
let textFieldFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - textFieldSize.width) / 2.0), y: topInset + 11.0 - UIScreenPixel), size: textFieldSize)
if let textFieldView = self.textField.view {
if textFieldView.superview == nil {
self.addSubview(textFieldView)
self.textField.parentState = state
}
transition.setFrame(view: textFieldView, frame: textFieldFrame)
}
let placeholderSize = self.placeholder.update(
transition: .immediate,
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(
string: component.placeholder,
font: Font.regular(17.0),
textColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.4)
)))
),
environment: {},
containerSize: CGSize(width: size.width, height: 50.0)
)
let placeholderFrame = CGRect(origin: CGPoint(x: 4.0, y: floorToScreenPixels(textFieldFrame.midY - placeholderSize.height / 2.0)), size: placeholderSize)
if let placeholderView = self.placeholder.view {
if placeholderView.superview == nil {
placeholderView.isUserInteractionEnabled = false
self.addSubview(placeholderView)
}
placeholderView.frame = placeholderFrame
placeholderView.isHidden = self.textFieldExternalState.hasText
}
if isFirstTime && component.isInitiallyFocused {
self.activateInput()
}
return CGSize(width: availableSize.width, height: size.height + topInset)
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<AlertComponentEnvironment>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}