Various improvements

This commit is contained in:
Ilya Laktyushin 2025-06-29 11:13:07 +02:00
parent 30c2bbc94b
commit a05b846703
14 changed files with 96 additions and 41 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -14504,4 +14504,6 @@ Sorry for the inconvenience.";
"Bot.AddToGroup.Title" = "Add to Group"; "Bot.AddToGroup.Title" = "Add to Group";
"Bot.AddToChannel.Title" = "Add to Channel"; "Bot.AddToChannel.Title" = "Add to Channel";
"ScheduledMessages.TodoUnavailable" = "Voting will become available after the message is published."; "ScheduledMessages.TodoUnavailable" = "Completing tasks will become available after the message is published.";
"Attachment.DiscardTodoAlertText" = "Discard checklist items?";

View File

@ -1834,7 +1834,7 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
public func prepareForReuse() { public func prepareForReuse() {
} }
public func requestDismiss(completion: @escaping () -> Void) { public func requestDismiss(completion: @escaping () -> Void) {
completion() completion()
} }

View File

@ -442,7 +442,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
for i in 0 ..< labelRects.count { for i in 0 ..< labelRects.count {
labelRects[i] = labelRects[i].insetBy(dx: -6.0, dy: floor((labelRects[i].height - 22.0) / 2.0)) labelRects[i] = labelRects[i].insetBy(dx: -7.0, dy: floor((labelRects[i].height - 22.0) / 2.0))
labelRects[i].size.height = 22.0 labelRects[i].size.height = 22.0
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0) labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
} }
@ -458,7 +458,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects { if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects {
backgroundMaskImage = (currentOffset, currentImage) backgroundMaskImage = (currentOffset, currentImage)
} else { } else {
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .white, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects, useModernPathCalculation: false) backgroundMaskImage = LinkHighlightingNode.generateImage(color: .white, inset: 0.0, innerRadius: 11.0, outerRadius: 11.0, rects: labelRects, useModernPathCalculation: false)
backgroundMaskUpdated = true backgroundMaskUpdated = true
} }
} }

View File

@ -417,21 +417,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
title = item.presentationData.strings.Notification_StarsGift_Title(Int32(count)) title = item.presentationData.strings.Notification_StarsGift_Title(Int32(count))
text = incoming ? item.presentationData.strings.Notification_StarsGift_Subtitle : item.presentationData.strings.Notification_StarsGift_SubtitleYou(peerName).string text = incoming ? item.presentationData.strings.Notification_StarsGift_Subtitle : item.presentationData.strings.Notification_StarsGift_SubtitleYou(peerName).string
case let .giftTon(currency, amount, cryptoCurrency, cryptoAmount, _): case let .giftTon(_, amount, _, cryptoAmount, _):
months = 1000 if amount < 10000000000 {
months = 1000
} else if amount < 50000000000 {
months = 2000
} else {
months = 3000
}
var peerName = "" var peerName = ""
if let peer = item.message.peers[item.message.id.peerId] { if let peer = item.message.peers[item.message.id.peerId] {
peerName = EnginePeer(peer).compactDisplayTitle peerName = EnginePeer(peer).compactDisplayTitle
} }
//TODO:localize
let _ = currency
let _ = amount
let _ = cryptoCurrency
let cryptoAmount = cryptoAmount ?? 0 let cryptoAmount = cryptoAmount ?? 0
title = "$ \(formatTonAmountText(cryptoAmount, dateTimeFormat: item.presentationData.dateTimeFormat))" title = "$ \(formatTonAmountText(cryptoAmount, dateTimeFormat: item.presentationData.dateTimeFormat, maxDecimalPositions: 3))"
text = incoming ? "Use TON to submit post suggestions to channels." : "With TON, \(peerName) will be able to submit post suggestions to channels." text = incoming ? "Use TON to submit post suggestions to channels." : "With TON, \(peerName) will be able to submit post suggestions to channels."
buttonTitle = "" buttonTitle = ""
case let .prizeStars(count, _, channelId, _, _): case let .prizeStars(count, _, channelId, _, _):
@ -644,7 +645,11 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
switch months { switch months {
case 1000: case 1000:
animationName = "GiftDiamond" animationName = "GiftDiamond1"
case 2000:
animationName = "GiftDiamond2"
case 3000:
animationName = "GiftDiamond3"
case 12: case 12:
animationName = "Gift12" animationName = "Gift12"
case 6: case 6:
@ -787,8 +792,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
for i in 0 ..< labelRects.count { for i in 0 ..< labelRects.count {
labelRects[i] = labelRects[i].insetBy(dx: -6.0, dy: floor((labelRects[i].height - 20.0) / 2.0)) labelRects[i] = labelRects[i].insetBy(dx: -7.0, dy: floor((labelRects[i].height - 22.0) / 2.0))
labelRects[i].size.height = 20.0 labelRects[i].size.height = 22.0
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0) labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
} }
@ -798,7 +803,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects { if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects {
backgroundMaskImage = (currentOffset, currentImage) backgroundMaskImage = (currentOffset, currentImage)
} else { } else {
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects, useModernPathCalculation: false) backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 11.0, outerRadius: 11.0, rects: labelRects, useModernPathCalculation: false)
backgroundMaskUpdated = true backgroundMaskUpdated = true
} }
} else { } else {
@ -807,7 +812,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var backgroundSize = giftSize var backgroundSize = giftSize
if hasServiceMessage { if hasServiceMessage {
backgroundSize.height += labelLayout.size.height + 18.0 backgroundSize.height += labelLayout.size.height + 20.0
} else { } else {
backgroundSize.height += 4.0 backgroundSize.height += 4.0
} }
@ -828,7 +833,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let overlayColor = item.presentationData.theme.theme.overallDarkAppearance && uniquePatternFile == nil ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12) let overlayColor = item.presentationData.theme.theme.overallDarkAppearance && uniquePatternFile == nil ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - giftSize.width) / 2.0), y: hasServiceMessage ? labelLayout.size.height + 12.0 : 0.0), size: giftSize) let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - giftSize.width) / 2.0), y: hasServiceMessage ? labelLayout.size.height + 13.0 : 0.0), size: giftSize)
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0) let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
var iconSize = CGSize(width: 160.0, height: 160.0) var iconSize = CGSize(width: 160.0, height: 160.0)

View File

@ -376,6 +376,7 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
private var backgroundWallpaperNode: ChatMessageBubbleBackdrop? private var backgroundWallpaperNode: ChatMessageBubbleBackdrop?
private var backgroundNode: ChatMessageBackground? private var backgroundNode: ChatMessageBackground?
private var extractedRadioView: UIView? private var extractedRadioView: UIView?
private var extractedIconView: UIView?
private var extractedAvatarView: UIView? private var extractedAvatarView: UIView?
private var extractedTitleNode: TextNodeWithEntities? private var extractedTitleNode: TextNodeWithEntities?
private var extractedNameView: UIView? private var extractedNameView: UIView?
@ -549,6 +550,11 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
// self.backgroundWallpaperNode?.update(rect: mappedRect, within: containerSize) // self.backgroundWallpaperNode?.update(rect: mappedRect, within: containerSize)
// } // }
if let extractedIconView = self.iconNode?.view.snapshotContentTree() {
self.extractedIconView = extractedIconView
self.contextSourceNode.contentNode.view.addSubview(extractedIconView)
}
if let extractedRadioView = self.radioNode?.view.snapshotContentTree() { if let extractedRadioView = self.radioNode?.view.snapshotContentTree() {
self.extractedRadioView = extractedRadioView self.extractedRadioView = extractedRadioView
self.contextSourceNode.contentNode.view.addSubview(extractedRadioView) self.contextSourceNode.contentNode.view.addSubview(extractedRadioView)
@ -585,6 +591,8 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
transition.updateAlpha(node: backgroundNode, alpha: 0.0, completion: { [weak backgroundNode] _ in transition.updateAlpha(node: backgroundNode, alpha: 0.0, completion: { [weak backgroundNode] _ in
self.extractedRadioView?.removeFromSuperview() self.extractedRadioView?.removeFromSuperview()
self.extractedRadioView = nil self.extractedRadioView = nil
self.extractedIconView?.removeFromSuperview()
self.extractedIconView = nil
self.extractedAvatarView?.removeFromSuperview() self.extractedAvatarView?.removeFromSuperview()
self.extractedAvatarView = nil self.extractedAvatarView = nil
self.extractedTitleNode?.textNode.removeFromSupernode() self.extractedTitleNode?.textNode.removeFromSupernode()
@ -737,7 +745,7 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
} else { } else {
titleNodeFrame = CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: titleLayout.size) titleNodeFrame = CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: titleLayout.size)
} }
if let _ = completion, canMark && todo.flags.contains(.othersCanComplete) { if let _ = completion, todo.flags.contains(.othersCanComplete) {
titleNodeFrame = titleNodeFrame.offsetBy(dx: 0.0, dy: -6.0) titleNodeFrame = titleNodeFrame.offsetBy(dx: 0.0, dy: -6.0)
} }

View File

@ -28,6 +28,7 @@ import TextFormat
import TextFieldComponent import TextFieldComponent
import ListComposePollOptionComponent import ListComposePollOptionComponent
import Markdown import Markdown
import PresentationDataUtils
final class ComposeTodoScreenComponent: Component { final class ComposeTodoScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -1750,10 +1751,30 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont
} }
public func requestDismiss(completion: @escaping () -> Void) { public func requestDismiss(completion: @escaping () -> Void) {
completion() guard let componentView = self.node.hostView.componentView as? ComposeTodoScreenComponent.View else {
return
}
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
if let input = componentView.validatedInput(), !input.text.isEmpty || !input.items.isEmpty {
let text = presentationData.strings.Attachment_DiscardTodoAlertText
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Attachment_CancelSelectionAlertYes, action: {
completion()
})])
self.present(controller, in: .window(.root))
} else {
completion()
}
} }
public func shouldDismissImmediately() -> Bool { public func shouldDismissImmediately() -> Bool {
return true guard let componentView = self.node.hostView.componentView as? ComposeTodoScreenComponent.View else {
return true
}
if let input = componentView.validatedInput(), !input.text.isEmpty || !input.items.isEmpty {
return false
} else {
return true
}
} }
} }

View File

@ -802,8 +802,13 @@ public final class ListComposePollOptionComponent: Component {
} }
if let deleteAction = component.deleteAction { if let deleteAction = component.deleteAction {
if self.deleteRevealView == nil { var deleteRevealViewTransition = transition
let deleteRevealView = DeleteRevealView(title: component.strings.Common_Delete, color: component.theme.list.itemDisclosureActions.destructive.fillColor) let deleteRevealView: DeleteRevealView
if let current = self.deleteRevealView {
deleteRevealView = current
} else {
deleteRevealViewTransition = .immediate
deleteRevealView = DeleteRevealView(title: component.strings.Common_Delete, color: component.theme.list.itemDisclosureActions.destructive.fillColor)
deleteRevealView.tapped = { [weak self] action in deleteRevealView.tapped = { [weak self] action in
guard let self else { guard let self else {
return return
@ -818,14 +823,17 @@ public final class ListComposePollOptionComponent: Component {
} }
self.deleteRevealView = deleteRevealView self.deleteRevealView = deleteRevealView
self.addSubview(deleteRevealView) self.addSubview(deleteRevealView)
if self.recognizer == nil {
let recognizer = RevealOptionsGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
recognizer.delegate = self
self.addGestureRecognizer(recognizer)
self.recognizer = recognizer
}
} }
if self.recognizer == nil { let _ = deleteRevealView.updateLayout(availableSize: size, revealOffset: self.revealOffset, transition: deleteRevealViewTransition)
let recognizer = RevealOptionsGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) deleteRevealView.frame = CGRect(origin: .zero, size: size)
recognizer.delegate = self
self.addGestureRecognizer(recognizer)
self.recognizer = recognizer
}
} else { } else {
if let deleteRevealView = self.deleteRevealView { if let deleteRevealView = self.deleteRevealView {
self.deleteRevealView = nil self.deleteRevealView = nil
@ -841,11 +849,6 @@ public final class ListComposePollOptionComponent: Component {
self.revealOffset = 0.0 self.revealOffset = 0.0
} }
if let deleteRevealView = self.deleteRevealView {
let _ = deleteRevealView.updateLayout(availableSize: size, revealOffset: self.revealOffset, transition: transition)
deleteRevealView.frame = CGRect(origin: .zero, size: size)
}
self.separatorInset = leftInset self.separatorInset = leftInset
return size return size

View File

@ -851,10 +851,26 @@ public final class StarsImageComponent: Component {
if let current = self.animationNode { if let current = self.animationNode {
animationNode = current animationNode = current
} else { } else {
let stickerName: String = count == 1000 ? "GiftDiamond" : "Gift\(count)" let animationName: String
switch count {
case 1000:
animationName = "GiftDiamond1"
case 2000:
animationName = "GiftDiamond2"
case 3000:
animationName = "GiftDiamond3"
case 12:
animationName = "Gift12"
case 6:
animationName = "Gift6"
case 3:
animationName = "Gift3"
default:
animationName = "Gift3"
}
animationNode = DefaultAnimatedStickerNodeImpl() animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.autoplay = true animationNode.autoplay = true
animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: stickerName), width: 384, height: 384, playbackMode: .still(.end), mode: .direct(cachePathPrefix: nil)) animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: 384, height: 384, playbackMode: .still(.end), mode: .direct(cachePathPrefix: nil))
animationNode.visibility = true animationNode.visibility = true
containerNode.view.addSubview(animationNode.view) containerNode.view.addSubview(animationNode.view)
self.animationNode = animationNode self.animationNode = animationNode

View File

@ -222,14 +222,12 @@ extension ChatControllerImpl {
self.canReadHistory.set(false) self.canReadHistory.set(false)
//TODO:localize
var sources: [ContextController.Source] = [] var sources: [ContextController.Source] = []
sources.append( sources.append(
ContextController.Source( ContextController.Source(
id: AnyHashable(OptionsId.item), id: AnyHashable(OptionsId.item),
title: self.presentationData.strings.Chat_Todo_ContextMenu_SectionTask, title: self.presentationData.strings.Chat_Todo_ContextMenu_SectionTask,
footer: self.presentationData.strings.Chat_Todo_ContextMenu_SectionsInfo, footer: self.presentationData.strings.Chat_Todo_ContextMenu_SectionsInfo,
//source: .extracted(ChatMessageLinkContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)),
source: .extracted(ChatTodoItemContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)), source: .extracted(ChatTodoItemContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)),
items: .single(ContextController.Items(content: .list(items))) items: .single(ContextController.Items(content: .list(items)))
) )

View File

@ -90,9 +90,11 @@ final class ChatMessageContextExtractedContentSource: ContextExtractedContentSou
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) { if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) {
result = ContextControllerTakeViewInfo(containingItem: .node(contentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil)) result = ContextControllerTakeViewInfo(containingItem: .node(contentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
if self.snapshot, let snapshotView = contentNode.contentNode.view.snapshotContentTree(unhide: false, keepPortals: true, keepTransform: true) { Queue.mainQueue().justDispatch {
contentNode.view.superview?.addSubview(snapshotView) if self.snapshot, let snapshotView = contentNode.contentNode.view.snapshotContentTree(unhide: false, keepPortals: true, keepTransform: true) {
self.snapshotView = snapshotView contentNode.view.superview?.addSubview(snapshotView)
self.snapshotView = snapshotView
}
} }
} }
} }