mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Consolidates 137 wave commits + 31 supporting commits (CLAUDE.md bump, typealias additions, AnyObject→EngineMedia restoration) into one squashed commit. Migrates dozens of consumer-side public APIs, struct fields, protocol methods, and enum payloads from Postbox protocols/structs to TelegramEngine engine wrappers and typealiases. Drops `import Postbox` from many files. Adds new TelegramCore typealiases and one TelegramEngineUnauthorized facade. Notable changes by category: **TelegramCore typealias additions** (rule 2 — narrow utility typealiases): - EngineChatListIndex, EngineTempBoxFile, EngineItemCollectionItemIndex, EngineItemCollectionViewEntryIndex, EngineValueBoxEncryptionParameters, EngineMessageAndThreadId, EnginePeerStoryStats, EngineMessageHistoryAnchorIndex, EngineChatListTotalUnreadStateCategory, EngineChatListTotalUnreadStateStats, EnginePeerSummaryCounterTags, EngineChatListTotalUnreadState, EngineItemCacheEntryId, EngineHashFunctions, EngineCachedMediaResourceRepresentationResult, EngineMediaResourceDataFetchResult, EngineMediaResourceDataFetchError, EngineMediaResourceStatus, EngineCachedPeerData **TelegramCore engine extensions/forwarders**: - EngineMessage.engineMedia, EngineMessage.enginePeers, EngineMessage.adAttribute, EngineMessage.effectivelyIncoming - engineFileSize forwarder - TelegramEngine.Resources.clearCachedMediaResources(mediaResourceIds: Set<EngineMediaResource.Id>) - TelegramEngine.Resources.fetchStatus(id:resourceSize:) - TelegramEngineUnauthorized.UnauthorizedResources facade with storeResourceData **Public API/struct migrations to engine types**: - ChatAvailableMessageActions.banAuthor/banAuthors → EnginePeer?/[EnginePeer] - WebSessionsContextState.peers → [EnginePeer.Id: EnginePeer] - CacheUsageStats.peers → [EnginePeer.Id: EnginePeer] - PeerCommand.peer → EnginePeer - PeerInfoControllerMode.calls(messages:) → [EngineMessage] - CallControllerNodeProtocol.updatePeer → EnginePeer params - ChatHistoryListNode.messageInCurrentHistoryView (and 4 variants) → EngineMessage? - ChatHistorySearchContainerNode.messageForGallery → EngineMessage? - PeerInfoPaneNode.findLoadedMessage / ensureMessageIsVisible / transitionNodeForGallery → engine-typed - GalleryHiddenMediaTarget.getTransitionInfo / GalleryHiddenMediaManager.findTarget → engine-typed - ChatPanelInterfaceInteraction.presentReactionDeletionOptions / presentBan*MessageOptions → EnginePeer - DrawingMessageRenderer.messages → [EngineMessage] - ChatVideoGalleryItemScrubberView.setFetchStatusSignal → EngineMediaResource.FetchStatus - ChannelDiscussionGroupActionSheetItem.peer, VoiceChatPeerEntry.peer, VoiceChatFullscreenParticipantItem.peer, MediaStreamComponent.chatPeer, MediaStreamVideoComponent.callPeer, ChatMessageContactBubbleContentNode.contactPeer, ChatMessageForwardInfoNode.peer, ChatMessageCommentFooterContentNode.replyPeers, ChatReportPeerTitlePanelNode.peer, ChatMessageActionUrlAuthController.bot, PeerMediaCollectionInterfaceState.peer, ChatMessageCallBubbleContentNode.peopleAvatars, ChatLoadingNode.renderedPeer (→ EngineRenderedPeer) — all to engine types **Wave-71-shadow stored-field migrations** (Postbox Peer/Message → Engine wrapper): - LegacyCallControllerNode.peer - CallStatusBarNode.currentPeer **Dead-code / dead-field removals**: - CallController.peer, CallControllerNodeV2.account, ContactMultiselectionController PeerNameIndex fields, preparedChatListNodeViewTransition account: Account param, FetchResource.swift entirely (unused function) **Module-level Postbox import drops**: 30+ files including TelegramRootController, EditStories, GiftViewScreen, AnimatedStickerUtils, FetchPhotoLibraryImageResource, PeerInfoGiftsPaneNode, PeerInfoPaneContainerNode, PresentAddMembers, PeerInfoProfileItems, ChatControllerAdminBanUsers, PresentationData typealiases, DefaultDayPresentationTheme, ChatListViewTransition, GalleryHiddenMediaManager, RecentSessionsController, GifContext, AuthorizationSequenceController, PeerInfoHeaderEditingContentNode, PeerInfoHeaderNode, PeerAllowedReactionListController, CallControllerNodeV2, and 6 PeerInfo pane files. **AnyObject restoration**: rule 8 added (never substitute Postbox protocols with Any/AnyObject) — undid previous AnyObject substitutions in waves 141/143 back to EngineMedia. Doc maintenance: CLAUDE.md updated to reflect new typealiases and forwarders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
223 lines
8.6 KiB
Swift
223 lines
8.6 KiB
Swift
import Foundation
|
|
import TelegramCore
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import TelegramStringFormatting
|
|
import TextFormat
|
|
import LocalizedPeerData
|
|
import AccountContext
|
|
|
|
public enum MessageTimestampStatusFormat {
|
|
case full
|
|
case regular
|
|
case minimal
|
|
}
|
|
|
|
private func dateStringForDay(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, timestamp: Int32) -> String {
|
|
var t: time_t = time_t(timestamp)
|
|
var timeinfo: tm = tm()
|
|
localtime_r(&t, &timeinfo)
|
|
|
|
let timestampNow = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
|
var now: time_t = time_t(timestampNow)
|
|
var timeinfoNow: tm = tm()
|
|
localtime_r(&now, &timeinfoNow)
|
|
|
|
if timeinfo.tm_year != timeinfoNow.tm_year {
|
|
return "\(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat))"
|
|
} else {
|
|
return "\(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, dateTimeFormat: dateTimeFormat))"
|
|
}
|
|
}
|
|
|
|
private func monthAtIndex(_ index: Int, strings: PresentationStrings) -> String {
|
|
switch index {
|
|
case 0:
|
|
return strings.Month_ShortJanuary
|
|
case 1:
|
|
return strings.Month_ShortFebruary
|
|
case 2:
|
|
return strings.Month_ShortMarch
|
|
case 3:
|
|
return strings.Month_ShortApril
|
|
case 4:
|
|
return strings.Month_ShortMay
|
|
case 5:
|
|
return strings.Month_ShortJune
|
|
case 6:
|
|
return strings.Month_ShortJuly
|
|
case 7:
|
|
return strings.Month_ShortAugust
|
|
case 8:
|
|
return strings.Month_ShortSeptember
|
|
case 9:
|
|
return strings.Month_ShortOctober
|
|
case 10:
|
|
return strings.Month_ShortNovember
|
|
case 11:
|
|
return strings.Month_ShortDecember
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
public func stringForMessageTimestampStatus(accountPeerId: EnginePeer.Id, message: EngineMessage, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular, associatedData: ChatMessageItemAssociatedData, ignoreAuthor: Bool = false) -> String {
|
|
if let adAttribute = message.adAttribute {
|
|
switch adAttribute.messageType {
|
|
case .sponsored:
|
|
return strings.Message_SponsoredLabel
|
|
case .recommended:
|
|
return strings.Message_RecommendedLabel
|
|
}
|
|
}
|
|
|
|
var timestamp: Int32
|
|
if let scheduleTime = message.scheduleTime {
|
|
timestamp = scheduleTime
|
|
} else {
|
|
timestamp = message.timestamp
|
|
}
|
|
|
|
var displayFullDate = false
|
|
if case .full = format, timestamp > 100000 {
|
|
displayFullDate = true
|
|
} else if let forwardInfo = message.forwardInfo, message.id.peerId == accountPeerId {
|
|
displayFullDate = true
|
|
timestamp = forwardInfo.date
|
|
}
|
|
|
|
if let sourceAuthorInfo = message.sourceAuthorInfo, let orignalDate = sourceAuthorInfo.orignalDate {
|
|
timestamp = orignalDate
|
|
}
|
|
|
|
var dateText = stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat)
|
|
if timestamp == scheduleWhenOnlineTimestamp {
|
|
dateText = " "
|
|
}
|
|
|
|
if let repeatPeriod = message.scheduleRepeatPeriod {
|
|
let repeatString: String
|
|
switch repeatPeriod {
|
|
case 60:
|
|
repeatString = "1 min"
|
|
case 300:
|
|
repeatString = "5 min"
|
|
case 86400:
|
|
repeatString = strings.Message_RepeatPeriod_Daily
|
|
case 7 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_Weekly
|
|
case 14 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_Biweekly
|
|
case 30 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_Monthly
|
|
case 91 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_3Months
|
|
case 182 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_6Months
|
|
case 365 * 86400:
|
|
repeatString = strings.Message_RepeatPeriod_Yearly
|
|
default:
|
|
repeatString = "\(repeatPeriod)s"
|
|
}
|
|
dateText = strings.Message_RepeatAt(repeatString, dateText).string
|
|
}
|
|
|
|
if message.id.namespace == Namespaces.Message.ScheduledCloud, let _ = message.pendingProcessingAttribute {
|
|
return strings.Message_Approximate(dateText).string
|
|
}
|
|
|
|
if displayFullDate {
|
|
let dayText: String
|
|
|
|
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
|
|
|
var t: time_t = time_t(timestamp)
|
|
var timeinfo: tm = tm()
|
|
localtime_r(&t, &timeinfo)
|
|
|
|
var now: time_t = time_t(nowTimestamp)
|
|
var timeinfoNow: tm = tm()
|
|
localtime_r(&now, &timeinfoNow)
|
|
|
|
if timeinfo.tm_year == timeinfoNow.tm_year {
|
|
if format != .full, timeinfo.tm_yday == timeinfoNow.tm_yday {
|
|
dayText = strings.Weekday_Today
|
|
} else {
|
|
dayText = strings.Date_ChatDateHeader(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)").string
|
|
}
|
|
} else {
|
|
dayText = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").string
|
|
}
|
|
dateText = strings.Message_FullDateFormat(dayText, stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat)).string
|
|
} else if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
|
dateText = strings.Message_ImportedDateFormat(dateStringForDay(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: forwardInfo.date), stringForMessageTimestamp(timestamp: forwardInfo.date, dateTimeFormat: dateTimeFormat), dateText).string
|
|
}
|
|
|
|
var authorTitle: String?
|
|
if let author = message.author, case .user = author {
|
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
|
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, message.author?.id != channel.id, info.flags.contains(.messagesShouldHaveProfiles) {
|
|
} else {
|
|
authorTitle = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
|
}
|
|
} else if let forwardInfo = message.forwardInfo, forwardInfo.sourceMessageId?.peerId.namespace == Namespaces.Peer.CloudChannel {
|
|
authorTitle = forwardInfo.authorSignature
|
|
}
|
|
} else {
|
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
|
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, message.author?.id != channel.id, info.flags.contains(.messagesShouldHaveProfiles) {
|
|
} else {
|
|
for attribute in message.attributes {
|
|
if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
|
authorTitle = attribute.signature
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if message.id.peerId != accountPeerId {
|
|
for attribute in message.attributes {
|
|
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
|
if let forwardInfo = message.forwardInfo {
|
|
if forwardInfo.author?.id == attribute.messageId.peerId {
|
|
if authorTitle == nil {
|
|
authorTitle = forwardInfo.authorSignature
|
|
}
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if authorTitle == nil {
|
|
for attribute in message.attributes {
|
|
if let attribute = attribute as? InlineBusinessBotMessageAttribute {
|
|
if let title = attribute.title {
|
|
authorTitle = title
|
|
} else if let peerId = attribute.peerId, let peer = message.peers[peerId] {
|
|
authorTitle = peer.debugDisplayTitle
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let subject = associatedData.subject, case let .messageOptions(_, _, info) = subject, case .forward = info {
|
|
authorTitle = nil
|
|
}
|
|
if ignoreAuthor {
|
|
authorTitle = nil
|
|
}
|
|
|
|
if case .minimal = format {
|
|
|
|
} else {
|
|
if let authorTitle = authorTitle, !authorTitle.isEmpty {
|
|
dateText = "\(authorTitle), \(dateText)"
|
|
}
|
|
}
|
|
|
|
return dateText
|
|
}
|