Various fixes

This commit is contained in:
Ilya Laktyushin 2026-01-08 19:22:48 +04:00
parent 937eae7081
commit 4463c7f26c
13 changed files with 64 additions and 46 deletions

View file

@ -1407,7 +1407,7 @@ public protocol SharedAccountContext: AnyObject {
func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?) -> ViewController
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController

View file

@ -38,6 +38,7 @@ import MultilineTextComponent
import ButtonComponent
import BundleIconComponent
import AnimatedTextComponent
import TextFormat
private enum ChatListRecentEntryStableId: Hashable {
case topPeers
@ -4507,7 +4508,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return
}
dismissImpl?()
if let value = attributes[NSAttributedString.Key(rawValue: "URL")] as? String {
if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
if !value.isEmpty {
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {})
} else {

View file

@ -1474,7 +1474,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
}
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
}
@ -1508,7 +1508,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
}
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
}
@ -2454,7 +2454,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
let leftInset: CGFloat = params.leftInset + avatarLeftInset
enum ContentData {
case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
case group(peers: [EngineChatList.GroupItem.Item])
}
@ -2463,7 +2463,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var hideAuthor = false
switch contentPeer {
case let .chat(itemPeer):
var (peer, initialHideAuthor, messageText, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
var (peer, initialHideAuthor, messageText, messageEntities, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText {
initialHideAuthor = true
@ -2491,7 +2491,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
break
}
contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, spoilers: spoilers, customEmojiRanges: customEmojiRanges)
contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, messageEntities: messageEntities, spoilers: spoilers, customEmojiRanges: customEmojiRanges)
hideAuthor = initialHideAuthor
case let .group(groupPeers):
contentData = .group(peers: groupPeers)
@ -2510,7 +2510,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
forumTopicData = nil
topForumTopicItems = []
if case let .chat(itemPeer, _, _, _, _, _, _) = contentData {
if case let .chat(itemPeer, _, _, _, _, _, _, _) = contentData {
if let messagePeer = itemPeer.chatMainPeer {
switch messagePeer {
case let .channel(channel):
@ -2558,7 +2558,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var ignoreForwardedIcon = false
switch contentData {
case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges):
case let .chat(itemPeer, _, _, _, text, entities, spoilers, customEmojiRanges):
var isUser = false
if case .user = itemPeer.chatMainPeer {
isUser = true
@ -2641,7 +2641,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
chatListText = (text, messageText)
}
if inlineAuthorPrefix == nil, let mediaDraftContentType {
hasDraft = true
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
@ -2673,8 +2673,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
if let peerText = peerText {
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
}
var entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
var entities = entities.filter { entity in
switch entity.type {
case .Spoiler, .CustomEmoji:
return true
@ -2692,14 +2692,14 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
regex = loginCodeRegex
}
if let cached = currentCustomTextEntities, cached.matches(text: message.text) {
if let cached = currentCustomTextEntities, cached.matches(text: messageText) {
customTextEntities = cached
} else if let matches = regex?.matches(in: message.text, options: [], range: NSMakeRange(0, (message.text as NSString).length)) {
} else if let matches = regex?.matches(in: messageText, options: [], range: NSMakeRange(0, (messageText as NSString).length)) {
var entities: [MessageTextEntity] = []
if let first = matches.first {
entities.append(MessageTextEntity(range: first.range.location ..< first.range.location + first.range.length, type: .Spoiler))
}
customTextEntities = CachedCustomTextEntities(text: message.text, textEntities: entities)
customTextEntities = CachedCustomTextEntities(text: messageText, textEntities: entities)
}
}
@ -2708,14 +2708,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let messageString: NSAttributedString
if !message.text.isEmpty && entities.count > 0 {
var messageText = message.text
var entities = entities
if !"".isEmpty, let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, !translation.text.isEmpty {
messageText = translation.text
entities = translation.entities
}
if !messageText.isEmpty && entities.count > 0 {
messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()))
} else if spoilers != nil || customEmojiRanges != nil {
let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
@ -3102,7 +3095,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
switch contentData {
case let .chat(itemPeer, threadInfo, _, _, _, _, _):
case let .chat(itemPeer, threadInfo, _, _, _, _, _, _):
if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData {
if customMessageListData.commandPrefix != nil {
titleAttributedString = nil

View file

@ -77,20 +77,21 @@ private func paidContentGroupType(paidContent: TelegramMediaPaidContent) -> Mess
return currentType
}
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
let peer: EnginePeer?
let message = messages.last
if let restrictionReason = message?._asMessage().restrictionReason(platform: "ios", contentSettings: contentSettings) {
return (nil, false, restrictionReason, nil, nil)
return (nil, false, restrictionReason, [], nil, nil)
}
if let restrictionReason = chatPeer.chatMainPeer?.restrictionText(platform: "ios", contentSettings: contentSettings) {
return (nil, false, restrictionReason, nil, nil)
return (nil, false, restrictionReason, [], nil, nil)
}
var hideAuthor = false
var messageText: String
var messageEntities: [MessageTextEntity] = []
var spoilers: [NSRange]?
var customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?
if let message = message {
@ -104,6 +105,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
for message in messages {
if !message.text.isEmpty {
messageText = message.text
messageEntities = message._asMessage().textEntitiesAttribute?.entities ?? []
break
}
}
@ -469,5 +471,5 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
}
}
return (peer, hideAuthor, messageText, spoilers, customEmojiRanges)
return (peer, hideAuthor, messageText, messageEntities, spoilers, customEmojiRanges)
}

View file

@ -75,6 +75,9 @@ public func textAlertController(
),
title: title,
text: text,
textAction: { attributes in
linkAction?(attributes, 0)
},
actions: mappedActions,
updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal)
)

View file

@ -264,7 +264,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
dismissImpl?()
})
])
pushControllerImpl?(alertController)
presentControllerImpl?(alertController, nil)
})
let signal = combineLatest(queue: .mainQueue(),

View file

@ -1676,18 +1676,23 @@ private final class StickerPackContainer: ASDisplayNode {
self.onLoading()
var loadedCount = 0
var error = false
var fetchingCount = 0
for content in contents {
if case .result = content {
switch content {
case .result:
loadedCount += 1
} else if case .none = content {
error = true
case .fetching:
fetchingCount += 1
default:
break
}
}
if error {
self.onError()
} else if loadedCount == contents.count {
if fetchingCount == 0 {
if loadedCount == 0 {
self.onError()
return
}
self.onReady()
if !contents.isEmpty && self.currentStickerPacks.isEmpty {

View file

@ -725,6 +725,7 @@ open class AlertScreen: ViewControllerComponentContainer, KeyShortcutResponder {
configuration: Configuration = Configuration(),
title: String? = nil,
text: String,
textAction: @escaping ([NSAttributedString.Key: Any]) -> Void = { _ in },
actions: [Action],
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)
) {
@ -741,7 +742,7 @@ open class AlertScreen: ViewControllerComponentContainer, KeyShortcutResponder {
content.append(AnyComponentWithIdentity(
id: "text",
component: AnyComponent(
AlertTextComponent(content: .plain(text))
AlertTextComponent(content: .plain(text), action: textAction)
)
))
}

View file

@ -112,7 +112,7 @@ public final class ChatMessageAccessibilityData {
if let chatPeer = message.peers[item.message.id.peerId] {
let authorName = message.author.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
let (_, _, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId)
let (_, _, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId)
var text = messageText

View file

@ -369,7 +369,7 @@ final class GiftOptionsScreenComponent: Component {
return
}
if gift.flags.contains(.isAuction) {
if gift.flags.contains(.isAuction) && !((gift.availability?.resale ?? 0) > 0 && component.peerId != component.context.account.peerId) {
guard let giftAuctionsManager = component.context.giftAuctionsManager else {
return
}
@ -419,6 +419,7 @@ final class GiftOptionsScreenComponent: Component {
let giftController = component.context.sharedContext.makeGiftAuctionViewScreen(
context: component.context,
auctionContext: auctionContext,
peerId: component.peerId,
completion: { [weak mainController] acquiredGifts, upgradeAttributes in
if component.peerId == context.account.peerId, let upgradeAttributes, let navigationController = mainController?.navigationController as? NavigationController {
let controller = context.sharedContext.makeGiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, acquiredGifts: acquiredGifts, attributes: upgradeAttributes, completion: {

View file

@ -2083,7 +2083,7 @@ private final class GiftAuctionBidScreenComponent: Component {
if case .finished = auctionState?.auctionState, let controller = self.environment?.controller() {
if let navigationController = controller.navigationController as? NavigationController {
controller.dismiss()
let auctionController = context.sharedContext.makeGiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: { _, _ in })
let auctionController = context.sharedContext.makeGiftAuctionViewScreen(context: context, auctionContext: auctionContext, peerId: nil, completion: { _, _ in })
navigationController.pushViewController(auctionController)
}
}

View file

@ -35,17 +35,20 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
let context: AccountContext
let auctionContext: GiftAuctionContext
let peerId: EnginePeer.Id?
let animateOut: ActionSlot<Action<()>>
let getController: () -> ViewController?
init(
context: AccountContext,
auctionContext: GiftAuctionContext,
peerId: EnginePeer.Id?,
animateOut: ActionSlot<Action<()>>,
getController: @escaping () -> ViewController?
) {
self.context = context
self.auctionContext = auctionContext
self.peerId = peerId
self.animateOut = animateOut
self.getController = getController
}
@ -62,6 +65,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
private let context: AccountContext
private let auctionContext: GiftAuctionContext
private let peerId: EnginePeer.Id?
private let animateOut: ActionSlot<Action<()>>
private let getController: () -> ViewController?
@ -93,11 +97,13 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
init(
context: AccountContext,
auctionContext: GiftAuctionContext,
peerId: EnginePeer.Id?,
animateOut: ActionSlot<Action<()>>,
getController: @escaping () -> ViewController?
) {
self.context = context
self.auctionContext = auctionContext
self.peerId = peerId
self.animateOut = animateOut
self.getController = getController
@ -399,7 +405,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
}
let storeController = self.context.sharedContext.makeGiftStoreController(
context: self.context,
peerId: self.context.account.peerId,
peerId: self.peerId ?? self.context.account.peerId,
gift: gift
)
controller.push(storeController)
@ -473,7 +479,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
}
func makeState() -> State {
return State(context: self.context, auctionContext: self.auctionContext, animateOut: self.animateOut, getController: self.getController)
return State(context: self.context, auctionContext: self.auctionContext, peerId: self.peerId, animateOut: self.animateOut, getController: self.getController)
}
static var body: Body {
@ -1277,13 +1283,16 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
let context: AccountContext
let auctionContext: GiftAuctionContext
let peerId: EnginePeer.Id?
init(
context: AccountContext,
auctionContext: GiftAuctionContext
auctionContext: GiftAuctionContext,
peerId: EnginePeer.Id?
) {
self.context = context
self.auctionContext = auctionContext
self.peerId = peerId
}
static func ==(lhs: GiftAuctionViewSheetComponent, rhs: GiftAuctionViewSheetComponent) -> Bool {
@ -1308,6 +1317,7 @@ final class GiftAuctionViewSheetComponent: CombinedComponent {
content: AnyComponent<EnvironmentType>(GiftAuctionViewSheetContent(
context: context.component.context,
auctionContext: context.component.auctionContext,
peerId: context.component.peerId,
animateOut: animateOut,
getController: controller
)),
@ -1392,6 +1402,7 @@ public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
public init(
context: AccountContext,
auctionContext: GiftAuctionContext,
peerId: EnginePeer.Id?,
completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void
) {
self.completion = completion
@ -1400,7 +1411,8 @@ public final class GiftAuctionViewScreen: ViewControllerComponentContainer {
context: context,
component: GiftAuctionViewSheetComponent(
context: context,
auctionContext: auctionContext
auctionContext: auctionContext,
peerId: peerId
),
navigationBarAppearance: .none,
statusBarStyle: .ignore,

View file

@ -3863,8 +3863,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return GiftAuctionBidScreen(context: context, toPeerId: toPeerId, text: text, entities: entities, hideName: hideName, auctionContext: auctionContext, acquiredGifts: acquiredGifts)
}
public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController {
return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: completion)
public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController {
return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, peerId: peerId, completion: completion)
}
public func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController {