chore: bump version to 1.2

This commit is contained in:
IMDelewer 2026-06-30 15:01:22 +03:00
parent f6bb33d193
commit 83f209e44a
101 changed files with 2419 additions and 608 deletions

View file

@ -160,7 +160,7 @@ genrule(
],
)
minimum_os_version = "13.0"
minimum_os_version = "26.5"
notificationServiceExtensionVersion = "v1"
@ -1850,7 +1850,7 @@ ios_application(
xcodeproj(
name = "Telegram_xcodeproj",
bazel_path = telegram_bazel_path,
project_name = "Telegram",
project_name = "WinterGram",
tags = ["manual"],
top_level_targets = [
top_level_target(":Telegram", target_environments = ["device", "simulator"]),

View file

@ -2612,60 +2612,36 @@ typealias CMJImage = UIImage
// MARK: - Public Namespace
/// A lightweight utility for turning ordinary bitmap images into ``NSAdaptiveImageGlyph`` (Apple Genmoji) objects.
///
/// ## Overview
/// The public API is minimal a single call to generate a glyph and helper utilities for
/// de/serialising attributed strings.
///
/// ```swift
/// // Synchronous usage
/// let glyph = try Customoji.makeGlyph(
/// from: image,
/// description: "alt text",
/// cropToSquare: true
/// )
/// let attr = NSAttributedString(adaptiveImageGlyph: glyph, attributes: [:])
/// ```
///
/// ## Requirements
/// * iOS 18 / macOS 15
/// * Swift 5.10+
///
/// ## Topics
/// - Generating glyphs: ``makeGlyph(from:description:identifier:tileSizes:cropToSquare:heicQuality:)``
/// - Generating glyphs asynchronously: ``makeGlyphAsync(from:description:identifier:tileSizes:cropToSquare:heicQuality:)``
/// - Serialising: ``decompose(_:)`` / ``recompose(plain:ranges:blobs:)``
///
/// Utility for converting bitmaps into ``NSAdaptiveImageGlyph`` (Genmoji).
public struct Customoji {
// MARK: Library-scoped Errors
/// Errors that can be thrown by ``Customoji``.
/// Errors thrown by ``Customoji``.
public enum Error: Swift.Error, LocalizedError {
/// The input image is not square and ``makeGlyph(from:description:identifier:tileSizes:cropToSquare:heicQuality:)`` was called with `cropToSquare == false`.
/// Input image is not square and cropping is disabled.
case nonSquare
/// The longest side of the input image exceeds ``maxSide`` pixels (defaults to 4096).
/// Input image exceeds ``maxSide`` pixels.
case imageTooLarge
/// Failed to create an in-memory HEIC container via *Image I/O*.
/// Failed to create the in-memory HEIC container.
case heicDestinationCreationFailed
/// Failed to finalise the HEIC container after writing all tiles.
/// Failed to finalize the HEIC container.
case heicFinalizeFailed
/// Internal resize operation failed when generating a tile.
/// Resizing a tile failed.
case imageScaleFailed
/// A `CGImage` representation could not be extracted from the source image.
/// Could not extract a `CGImage` from the source image.
case cgImageUnavailable
/// The `tileSizes` parameter was empty, not strictly ascending, contained duplicates, or exceeded the source size.
/// `tileSizes` was empty, not strictly ascending, contained duplicates, or exceeded the source size.
case invalidTileSizes
/// No tiles were generated normally unreachable unless all requested sizes were invalid.
/// No tiles were generated; normally unreachable unless all requested sizes were invalid.
case noTilesGenerated
/// The current platform is neither UIKit- nor AppKit-based (unlikely in practice).
/// Unsupported platform.
case unsupportedPlatform
/// The `heicQuality` parameter was outside the 0.0 1.0 range.
/// `heicQuality` was outside the 0.0...1.0 range.
case invalidHeicQuality
/// Cropping or scaling to a square failed.
case squareCropFailed
/// Textual description suitable for presenting to users.
/// Localized error description.
public var errorDescription: String? {
switch self {
case .nonSquare: return "Input image must be square."
@ -2694,26 +2670,16 @@ public struct Customoji {
}
}
/// Default tile sizes (in pixels) officially used by Apple for Genmoji.
/// Default Genmoji tile sizes.
public static let defaultTileSizes = [40, 64, 96, 160, 320]
/// Maximum allowed side length (in pixels) to guard against excessive memory consumption.
/// Maximum allowed side length, in pixels.
private static let maxSide = 4_096
// MARK: - Synchronous Builder
#if swift(>=5.10)
@available(iOS 18.0, macCatalyst 18.0, macOS 15.0, *)
/// Generates an ``NSAdaptiveImageGlyph`` synchronously on the **current** thread.
///
/// - Parameters:
/// - image: Source image. Can be rectangular; see `cropToSquare`.
/// - description: A human-readable accessibility description (VoiceOver/Narrator).
/// - identifier: A globally unique content identifier. Defaults to a fresh UUID.
/// - tileSizes: Desired tile sizes, in pixels. Must be strictly ascending. Defaults to ``defaultTileSizes``.
/// - cropToSquare: When `true`, the central square area is cropped if the input image is not square.
/// - heicQuality: Lossy compression quality between `0.0` and `1.0`. Defaults to `0.8`.
/// - Throws: ``Customoji/Error`` if validation or encoding fails.
/// - Returns: A fully-formed glyph ready for attribution.
/// Generate an ``NSAdaptiveImageGlyph`` from a bitmap.
static func makeGlyph(
from image: CMJImage,
description: String,
@ -2739,19 +2705,7 @@ public struct Customoji {
// MARK: - AttributedString Utilities
#if swift(>=5.10)
@available(iOS 18.0, macCatalyst 18.0, macOS 15.0, *)
/// Breaks an ``NSAttributedString`` that may contain adaptive-image glyphs into three parts.
///
/// This is handy when you need to **serialise** rich text for network transport or persistent
/// storage, because `NSAdaptiveImageGlyph` itself is not `Codable`.
///
/// ```swift
/// let (plain, ranges, blobs) = Customoji.decompose(attrString)
/// ```
///
/// - Returns: A tuple where:
/// - plain: UTF-16 plain text.
/// - ranges: An array mapping text ranges to glyph identifiers.
/// - blobs: A dictionary mapping identifier raw HEIC data.
/// Split an attributed string into plain text, glyph ranges, and HEIC data.
@inlinable
public static func decompose(_ attr: NSAttributedString) -> (
plain: String, ranges: [(NSRange, String)], blobs: [String: Data]
@ -2773,13 +2727,7 @@ public struct Customoji {
}
@available(iOS 18.0, macCatalyst 18.0, macOS 15.0, *)
/// Reassembles an attributed string previously produced by ``decompose(_:)``.
///
/// - Parameters:
/// - plain: The plain UTF-16 text.
/// - ranges: An array of `(NSRange, identifier)` pairs.
/// - blobs: A dictionary of identifier HEIC data. *Must* contain every identifier referenced by `ranges`.
/// - Returns: A fully restored `NSAttributedString` with adaptive-image glyphs reinstated.
/// Reassemble an attributed string from ``decompose(_:)`` output.
public static func recompose(
plain: String,
ranges: [(NSRange, String)],
@ -2805,8 +2753,7 @@ public struct Customoji {
#endif
// MARK: - Helper: Pixel-Side Validation
/// Ensures that the images **longer edge** does not exceed ``maxSide`` pixels, guarding against
/// excessive memory usage during HEIC encoding.
/// Reject images whose longer edge exceeds ``maxSide``.
private static func validatePixelSide(of image: CMJImage) throws {
#if canImport(UIKit)
let scale = image.scale == 0 ? 1 : image.scale
@ -2821,11 +2768,6 @@ public struct Customoji {
}
}
// =============================================================
// Below this line lies the internal implementation.
// Public-facing symbols have already been documented above.
// =============================================================
// MARK: - Core Builder
@available(iOS 18.0, macCatalyst 18.0, macOS 15.0, *)
extension Customoji {

View file

@ -353,6 +353,8 @@
<string>remote-notification</string>
<string>voip</string>
</array>
<key>UIDesignRequiresCompatibility</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>

View file

@ -402,22 +402,22 @@
// Tour
"Tour.Title1" = "WinterGram";
"Tour.Text1" = "The world's **fastest** messaging app.\nIt is **free** and **secure**.";
"Tour.Text1" = "A native **AyuGram** port for iPhone.\nAn open-source **Telegram** fork.";
"Tour.Title2" = "Fast";
"Tour.Text2" = "**WinterGram** delivers messages\nfaster than any other application.";
"Tour.Title2" = "Telegram Fork";
"Tour.Text2" = "Fully compatible with **Telegram**.\nYour chats, channels and accounts.";
"Tour.Title3" = "Powerful";
"Tour.Text3" = "**WinterGram** has no limits on\nthe size of your media and chats.";
"Tour.Title3" = "Power Tools";
"Tour.Text3" = "Saved **deleted messages**, full **edit\nhistory** and a **hidden archive**.";
"Tour.Title4" = "Secure";
"Tour.Text4" = "**WinterGram** keeps your messages\nsafe from hacker attacks.";
"Tour.Title4" = "Make It Yours";
"Tour.Text4" = "Deep **customization**, **Liquid Glass**,\nlocal **Premium** and a device **spoofer**.";
"Tour.Title5" = "Cloud-Based";
"Tour.Text5" = "**WinterGram** lets you access your\nmessages from multiple devices.";
"Tour.Title5" = "Open Source";
"Tour.Text5" = "Free and fully **open-source**.\nInspect and build it yourself.";
"Tour.Title6" = "Free";
"Tour.Text6" = "**WinterGram** provides free unlimited\ncloud storage for chats and media.";
"Tour.Title6" = "Ghost Mode";
"Tour.Text6" = "Read messages without sending\nread receipts, online or typing status.";
"Tour.StartButton" = "Start Messaging";
@ -16509,6 +16509,7 @@ Error: %8$@";
"WinterGram.ShowMessageSeconds" = "Show Message Seconds";
"WinterGram.ShowPeerID" = "Show Peer ID";
"WinterGram.ShowRegistrationDate" = "Show Registration Date";
"WinterGram.RegistrationDate" = "Registration date";
"WinterGram.SingleCornerRadius" = "Single Corner Radius";
"WinterGram.SomeSettingsWillTakeEffectAfterRestart" = "Some settings will take effect after restart.";
"WinterGram.SpoofAppVersion" = "App Version";

View file

@ -34,12 +34,12 @@ class UITests: XCTestCase {
deleteTestAccount(phone: "9996625296")
app.launch()
// Welcome screen tap Start Messaging
// Start messaging.
let startButton = app.buttons["Auth.Welcome.StartButton"]
XCTAssert(startButton.waitForExistence(timeout: 5.0))
startButton.tap()
// Phone entry screen enter test phone number
// Enter the test phone number.
let countryCodeField = app.textFields["Auth.PhoneEntry.CountryCodeField"]
XCTAssert(countryCodeField.waitForExistence(timeout: 10.0))
countryCodeField.tap()
@ -57,12 +57,12 @@ class UITests: XCTestCase {
XCTAssert(continueButton.isEnabled)
continueButton.tap()
// Confirmation dialog tap Continue
// Confirm.
let confirmButton = app.buttons["Auth.PhoneConfirm.ContinueButton"]
XCTAssert(confirmButton.waitForExistence(timeout: 5.0))
confirmButton.tap()
// Code entry screen enter verification code
// Enter the verification code.
let codeEntryTitle = app.staticTexts["Auth.CodeEntry.Title"]
XCTAssert(codeEntryTitle.waitForExistence(timeout: 15.0))
@ -70,7 +70,7 @@ class UITests: XCTestCase {
XCTAssert(codeField.waitForExistence(timeout: 3.0))
codeField.typeText("22222")
// Set name screen enter name and submit
// Set the name.
let firstNameField = app.textFields["Auth.SetName.FirstNameField"]
XCTAssert(firstNameField.waitForExistence(timeout: 15.0))
firstNameField.tap()

View file

@ -4,8 +4,8 @@
against an exported tgwatch source tree, optionally codesigns the result, and exposes
it through the providers that `ios_application(watch_application = ...)` consumes:
* AppleBundleInfo bundle metadata (the host reads only `.product_type`).
* AppleEmbeddableInfo `watch_bundles` (the zipped .app placed under Watch/).
* AppleBundleInfo: bundle metadata (the host reads only `.product_type`).
* AppleEmbeddableInfo: `watch_bundles` (the zipped .app placed under Watch/).
The watch source tree is the committed in-repo snapshot at `Telegram/WatchApp/` (tracked
inputs). To update it, re-sync from the standalone tgwatch repo via

View file

@ -2,7 +2,7 @@
# Worker for the apple_prebuilt_watchos_application Bazel rule.
#
# Builds the tgwatch watch app via xcodebuild (device, Release, UNSIGNED), then
# — if a provisioning profile is supplied — codesigns the app and its nested
# If a provisioning profile is supplied, codesigns the app and its nested
# frameworks with the watchkitapp provisioning profile and a matching identity,
# and finally zips the .app into the rule's output archive.
#
@ -45,7 +45,7 @@ DD="$(mktemp -d)"
trap 'rm -rf "$DD"' EXIT
# Build from a writable copy so xcodebuild/SwiftPM never write into the (possibly
# in-repo, read-only) source tree — e.g. SwiftPM's Package.resolved or the workspace.
# in-repo, read-only) source tree, such as SwiftPM's Package.resolved or the workspace.
# The tree is small (~12M); a plain cp on each (uncached) build is acceptable.
WORKSRC="$DD/src"
mkdir -p "$WORKSRC"
@ -84,7 +84,7 @@ fi
# Sign the watch app whenever a provisioning profile is available. When no explicit
# identity is supplied, derive it from the certificate embedded in that profile, so
# the watch app is signed with the same distribution/development identity as the host
# app (resolved from the shared codesigning material) required for App Store, where
# app (resolved from the shared codesigning material), as required for App Store, where
# every nested bundle must carry the Apple submission certificate. Without a profile
# the app is left unsigned (the host does not re-sign it).
if [ -n "$PROFILE" ]; then