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.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 requestDismiss(completion: @escaping () -> Void) {
public func requestDismiss(completion: @escaping () -> Void) {
completion()
}

View File

@ -442,7 +442,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
}
}
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].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 {
backgroundMaskImage = (currentOffset, currentImage)
} 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
}
}

View File

@ -417,21 +417,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
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
case let .giftTon(currency, amount, cryptoCurrency, cryptoAmount, _):
months = 1000
case let .giftTon(_, amount, _, cryptoAmount, _):
if amount < 10000000000 {
months = 1000
} else if amount < 50000000000 {
months = 2000
} else {
months = 3000
}
var peerName = ""
if let peer = item.message.peers[item.message.id.peerId] {
peerName = EnginePeer(peer).compactDisplayTitle
}
//TODO:localize
let _ = currency
let _ = amount
let _ = cryptoCurrency
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."
buttonTitle = ""
case let .prizeStars(count, _, channelId, _, _):
@ -644,7 +645,11 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
switch months {
case 1000:
animationName = "GiftDiamond"
animationName = "GiftDiamond1"
case 2000:
animationName = "GiftDiamond2"
case 3000:
animationName = "GiftDiamond3"
case 12:
animationName = "Gift12"
case 6:
@ -787,8 +792,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
}
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].size.height = 20.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].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 {
backgroundMaskImage = (currentOffset, currentImage)
} 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
}
} else {
@ -807,7 +812,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var backgroundSize = giftSize
if hasServiceMessage {
backgroundSize.height += labelLayout.size.height + 18.0
backgroundSize.height += labelLayout.size.height + 20.0
} else {
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 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)
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 backgroundNode: ChatMessageBackground?
private var extractedRadioView: UIView?
private var extractedIconView: UIView?
private var extractedAvatarView: UIView?
private var extractedTitleNode: TextNodeWithEntities?
private var extractedNameView: UIView?
@ -549,6 +550,11 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
// 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() {
self.extractedRadioView = 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
self.extractedRadioView?.removeFromSuperview()
self.extractedRadioView = nil
self.extractedIconView?.removeFromSuperview()
self.extractedIconView = nil
self.extractedAvatarView?.removeFromSuperview()
self.extractedAvatarView = nil
self.extractedTitleNode?.textNode.removeFromSupernode()
@ -737,7 +745,7 @@ private final class ChatMessageTodoItemNode: ASDisplayNode {
} else {
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)
}

View File

@ -28,6 +28,7 @@ import TextFormat
import TextFieldComponent
import ListComposePollOptionComponent
import Markdown
import PresentationDataUtils
final class ComposeTodoScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -1750,10 +1751,30 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont
}
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 {
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 self.deleteRevealView == nil {
let deleteRevealView = DeleteRevealView(title: component.strings.Common_Delete, color: component.theme.list.itemDisclosureActions.destructive.fillColor)
var deleteRevealViewTransition = transition
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
guard let self else {
return
@ -818,14 +823,17 @@ public final class ListComposePollOptionComponent: Component {
}
self.deleteRevealView = 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 recognizer = RevealOptionsGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
recognizer.delegate = self
self.addGestureRecognizer(recognizer)
self.recognizer = recognizer
}
let _ = deleteRevealView.updateLayout(availableSize: size, revealOffset: self.revealOffset, transition: deleteRevealViewTransition)
deleteRevealView.frame = CGRect(origin: .zero, size: size)
} else {
if let deleteRevealView = self.deleteRevealView {
self.deleteRevealView = nil
@ -841,11 +849,6 @@ public final class ListComposePollOptionComponent: Component {
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
return size

View File

@ -851,10 +851,26 @@ public final class StarsImageComponent: Component {
if let current = self.animationNode {
animationNode = current
} 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.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
containerNode.view.addSubview(animationNode.view)
self.animationNode = animationNode

View File

@ -222,14 +222,12 @@ extension ChatControllerImpl {
self.canReadHistory.set(false)
//TODO:localize
var sources: [ContextController.Source] = []
sources.append(
ContextController.Source(
id: AnyHashable(OptionsId.item),
title: self.presentationData.strings.Chat_Todo_ContextMenu_SectionTask,
footer: self.presentationData.strings.Chat_Todo_ContextMenu_SectionsInfo,
//source: .extracted(ChatMessageLinkContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)),
source: .extracted(ChatTodoItemContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)),
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) {
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) {
contentNode.view.superview?.addSubview(snapshotView)
self.snapshotView = snapshotView
Queue.mainQueue().justDispatch {
if self.snapshot, let snapshotView = contentNode.contentNode.view.snapshotContentTree(unhide: false, keepPortals: true, keepTransform: true) {
contentNode.view.superview?.addSubview(snapshotView)
self.snapshotView = snapshotView
}
}
}
}