Update API

This commit is contained in:
Ilya Laktyushin 2026-02-10 12:58:04 +04:00
parent 25952cfef6
commit 189d25c32e
59 changed files with 983 additions and 393 deletions

View file

@ -0,0 +1,177 @@
import Foundation
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
public func stringForEntityFormattedDate(timestamp: Int32, format: MessageTextEntityType.DateTimeFormat, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> String {
switch format {
case .relative:
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
let value = currentTimestamp - timestamp
if value > 0 {
if value < 60 {
return strings.FormattedDate_SecondsAgo(value)
} else if value <= 1 * 60 * 60 {
return strings.FormattedDate_MinutesAgo(Int32(round(Float(value) / 60)))
} else if value <= 24 * 60 * 60 {
return strings.FormattedDate_HoursAgo(Int32(round(Float(value) / (60 * 60))))
} else {
return strings.FormattedDate_DaysAgo(Int32(round(Float(value) / (24 * 60 * 60))))
}
} else {
let value = abs(value)
if value < 60 {
return strings.FormattedDate_InSeconds(value)
} else if value <= 1 * 60 * 60 {
return strings.FormattedDate_InMinutes(Int32(round(Float(value) / 60)))
} else if value <= 24 * 60 * 60 {
return strings.FormattedDate_InHours(Int32(round(Float(value) / (60 * 60))))
} else {
return strings.FormattedDate_InDays(Int32(round(Float(value) / (24 * 60 * 60))))
}
}
case let .full(timeFormat, dateFormat):
var string = ""
if let dateFormat {
switch dateFormat {
case .short:
string += stringForShortDate(timestamp: timestamp, strings: strings, dateTimeFormat: dateTimeFormat)
case .long:
string += stringForFullDate(timestamp: timestamp, strings: strings, dateTimeFormat: dateTimeFormat)
}
}
if let timeFormat {
let timeString: String
switch timeFormat {
case .short:
timeString = stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat)
case .long:
timeString = stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat, withSeconds: true)
}
if !string.isEmpty {
string = strings.Time_AtPreciseDate(string, timeString).string
} else {
string = timeString
}
}
return string
}
}
public func stringForShortTimestamp(hours: Int32, minutes: Int32, seconds: Int32? = nil, dateTimeFormat: PresentationDateTimeFormat, formatAsPlainText: Bool = false) -> String {
switch dateTimeFormat.timeFormat {
case .regular:
let hourString: String
if hours == 0 {
hourString = "12"
} else if hours > 12 {
hourString = "\(hours - 12)"
} else {
hourString = "\(hours)"
}
let periodString: String
if hours >= 12 {
periodString = "PM"
} else {
periodString = "AM"
}
let spaceCharacter: String
if formatAsPlainText {
spaceCharacter = " "
} else {
spaceCharacter = "\u{00a0}"
}
let minuteString = String(format: "%02d", arguments: [Int(minutes)])
if let seconds {
let secondString = String(format: "%02d", arguments: [Int(seconds)])
return "\(hourString):\(minuteString):\(secondString)\(spaceCharacter)\(periodString)"
} else {
return "\(hourString):\(minuteString)\(spaceCharacter)\(periodString)"
}
case .military:
if let seconds {
return String(format: "%02d:%02d:%02d", arguments: [Int(hours), Int(minutes), Int(seconds)])
} else {
return String(format: "%02d:%02d", arguments: [Int(hours), Int(minutes)])
}
}
}
public func stringForMessageTimestamp(timestamp: Int32, dateTimeFormat: PresentationDateTimeFormat, withSeconds: Bool = false, local: Bool = true) -> String {
var t = Int(timestamp)
var timeinfo = tm()
if local {
localtime_r(&t, &timeinfo)
} else {
gmtime_r(&t, &timeinfo)
}
return stringForShortTimestamp(hours: timeinfo.tm_hour, minutes: timeinfo.tm_min, seconds: withSeconds ? timeinfo.tm_sec : nil, dateTimeFormat: dateTimeFormat)
}
private func stringForShortDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, withTime: Bool = true) -> String {
var t: time_t = Int(timestamp)
var timeinfo = tm()
localtime_r(&t, &timeinfo);
let day = timeinfo.tm_mday
let month = timeinfo.tm_mon + 1
let year = timeinfo.tm_year
let dateString: String
let separator = dateTimeFormat.dateSeparator
let suffix = dateTimeFormat.dateSuffix
let displayYear = dateTimeFormat.requiresFullYear ? year - 100 + 2000 : year - 100
switch dateTimeFormat.dateFormat {
case .monthFirst:
dateString = String(format: "%02d%@%02d%@%02d%@", month, separator, day, separator, displayYear, suffix)
case .dayFirst:
dateString = String(format: "%02d%@%02d%@%02d%@", day, separator, month, separator, displayYear, suffix)
}
return dateString
}
private func stringForFullDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> String {
var t: time_t = Int(timestamp)
var timeinfo = tm()
localtime_r(&t, &timeinfo);
let dayString = "\(timeinfo.tm_mday)"
let yearString = "\(2000 + timeinfo.tm_year - 100)"
let monthFormat: (String, String) -> PresentationStrings.FormattedString
switch timeinfo.tm_mon + 1 {
case 1:
monthFormat = strings.FormattedDate_LongDate_m1
case 2:
monthFormat = strings.FormattedDate_LongDate_m2
case 3:
monthFormat = strings.FormattedDate_LongDate_m3
case 4:
monthFormat = strings.FormattedDate_LongDate_m4
case 5:
monthFormat = strings.FormattedDate_LongDate_m5
case 6:
monthFormat = strings.FormattedDate_LongDate_m6
case 7:
monthFormat = strings.FormattedDate_LongDate_m7
case 8:
monthFormat = strings.FormattedDate_LongDate_m8
case 9:
monthFormat = strings.FormattedDate_LongDate_m9
case 10:
monthFormat = strings.FormattedDate_LongDate_m10
case 11:
monthFormat = strings.FormattedDate_LongDate_m11
case 12:
monthFormat = strings.FormattedDate_LongDate_m12
default:
return ""
}
return monthFormat(dayString, yearString).string
}

View file

@ -299,7 +299,30 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
type = .Url
} else if result.resultType == NSTextCheckingResult.CheckingType.date, let date = result.date?.timeIntervalSince1970 {
type = .FormattedDate(format: .relative, date: Int32(date))
#if DEBUG
var format: MessageTextEntityType.DateTimeFormat?
if text.contains("[rel]") {
format = .relative
}
var timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat?
if text.contains("[st]") {
timeFormat = .short
} else if text.contains("[lt]") {
timeFormat = .long
}
var dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat?
if text.contains("[sd]") {
dateFormat = .short
} else if text.contains("[ld]") {
dateFormat = .long
}
if timeFormat != nil || dateFormat != nil {
format = .full(timeFormat: timeFormat, dateFormat: dateFormat)
}
type = .FormattedDate(format: format, date: Int32(date))
#else
type = .FormattedDate(format: nil, date: Int32(date))
#endif
} else {
type = .PhoneNumber
}

View file

@ -5,6 +5,7 @@ import TelegramCore
import Display
import libprisma
import SwiftSignalKit
import TelegramPresentationData
public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
var nsString: NSString?
@ -80,7 +81,7 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M
private let syntaxHighlighter = Syntaxer()
public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseQuoteSecondaryTintColor: UIColor? = nil, baseQuoteTertiaryTintColor: UIColor? = nil, codeBlockTitleColor: UIColor? = nil, codeBlockAccentColor: UIColor? = nil, codeBlockBackgroundColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:], adjustQuoteFontSize: Bool = false, cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil, paragraphAlignment: NSTextAlignment? = nil) -> NSAttributedString {
public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], strings: PresentationStrings? = nil, dateTimeFormat: PresentationDateTimeFormat? = nil, baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseQuoteSecondaryTintColor: UIColor? = nil, baseQuoteTertiaryTintColor: UIColor? = nil, codeBlockTitleColor: UIColor? = nil, codeBlockAccentColor: UIColor? = nil, codeBlockBackgroundColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:], adjustQuoteFontSize: Bool = false, cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil, paragraphAlignment: NSTextAlignment? = nil) -> NSAttributedString {
let baseQuoteTintColor = baseQuoteTintColor ?? baseColor
var nsString: NSString?
@ -94,7 +95,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
var adjustedRanges: [NSRange?] = []
adjustedRanges.reserveCapacity(entities.count)
let rangeDelta = 0
var rangeDelta = 0
for entity in entities {
let originalRange = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
var range = NSRange(location: originalRange.location + rangeDelta, length: originalRange.length)
@ -106,23 +107,22 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
range.length = stringLength - range.location
}
// switch entity.type {
// case let .FormattedDate(format, date):
// let replacement = stringForEntityFormattedDate()
// switch format {
// case .relative:
// replacement = relative
// case let .full(timeFormat, dateFormat):
// }
//
// let replacementString = NSAttributedString(string: replacement, attributes: baseAttributes)
// string.replaceCharacters(in: range, with: replacementString)
// let newRange = NSRange(location: range.location, length: (replacement as NSString).length)
// adjustedRanges.append(newRange)
// rangeDelta += newRange.length - range.length
// default:
switch entity.type {
case let .FormattedDate(format, date):
if let format, let strings, let dateTimeFormat {
let replacement = stringForEntityFormattedDate(timestamp: date, format: format, strings: strings, dateTimeFormat: dateTimeFormat)
let replacementString = NSAttributedString(string: replacement, attributes: baseAttributes)
string.replaceCharacters(in: range, with: replacementString)
let newRange = NSRange(location: range.location, length: (replacement as NSString).length)
adjustedRanges.append(newRange)
rangeDelta += newRange.length - range.length
} else {
adjustedRanges.append(range)
}
default:
adjustedRanges.append(range)
// }
}
}
var fontAttributeMask: [ChatTextFontAttributes] = Array(repeating: [], count: string.length)