Various improvements

This commit is contained in:
Ilya Laktyushin
2025-02-22 19:17:36 +04:00
parent eecb2ef5c0
commit 8d7f9bf372
122 changed files with 3454 additions and 1121 deletions

View File

@@ -12,6 +12,7 @@ import ComponentFlow
import ViewControllerComponent
import SheetComponent
import MultilineTextComponent
import MultilineTextWithEntitiesComponent
import BundleIconComponent
import SolidRoundedButtonComponent
import Markdown
@@ -37,6 +38,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let openMessage: (EngineMessage.Id) -> Void
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
let openAppExamples: () -> Void
let openPaidMessageFee: () -> Void
let copyTransactionId: (String) -> Void
let updateSubscription: () -> Void
let sendGift: (EnginePeer.Id) -> Void
@@ -49,6 +51,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
openMessage: @escaping (EngineMessage.Id) -> Void,
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
openAppExamples: @escaping () -> Void,
openPaidMessageFee: @escaping () -> Void,
copyTransactionId: @escaping (String) -> Void,
updateSubscription: @escaping () -> Void,
sendGift: @escaping (EnginePeer.Id) -> Void
@@ -60,6 +63,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
self.openMessage = openMessage
self.openMedia = openMedia
self.openAppExamples = openAppExamples
self.openPaidMessageFee = openPaidMessageFee
self.copyTransactionId = copyTransactionId
self.updateSubscription = updateSubscription
self.sendGift = sendGift
@@ -238,6 +242,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var isGiftUpgrade = false
var giftAvailability: StarGift.Gift.Availability?
var isRefProgram = false
var isPaidMessage = false
var delayedCloseOnOpenPeer = true
switch subject {
@@ -414,14 +419,21 @@ private final class StarsTransactionSheetContent: CombinedComponent {
isGift = true
} else if let starrefCommissionPermille = transaction.starrefCommissionPermille {
isRefProgram = true
if transaction.starrefPeerId == nil {
if transaction.flags.contains(.isPaidMessage) {
isPaidMessage = true
titleText = strings.Stars_Transaction_PaidMessage(transaction.paidMessageCount ?? 1)
countOnTop = true
descriptionText = strings.Stars_Transaction_PaidMessage_Text(formatPermille(starrefCommissionPermille)).string
} else if transaction.starrefPeerId == nil {
titleText = strings.StarsTransaction_TitleCommission(formatPermille(starrefCommissionPermille)).string
countOnTop = false
descriptionText = ""
} else {
titleText = transaction.title ?? " "
countOnTop = false
descriptionText = ""
}
descriptionText = ""
count = transaction.count
countOnTop = false
transactionId = transaction.id
date = transaction.date
transactionPeer = transaction.peer
@@ -443,7 +455,10 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} else {
switch transaction.peer {
case let .peer(peer):
if !transaction.media.isEmpty {
if transaction.flags.contains(.isPaidMessage) {
isPaidMessage = true
titleText = strings.Stars_Transaction_PaidMessage(transaction.paidMessageCount ?? 1)
} else if !transaction.media.isEmpty {
titleText = strings.Stars_Transaction_MediaPurchase
} else {
titleText = transaction.title ?? peer.compactDisplayTitle
@@ -613,7 +628,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
let absCount = StarsAmount(value: abs(count.value), nanos: abs(count.nanos))
let formattedAmount = presentationStringsFormattedNumber(absCount, dateTimeFormat.groupingSeparator)
let formattedAmount = formatStarsAmountText(absCount, dateTimeFormat: dateTimeFormat)
let countColor: UIColor
var countFont: UIFont = isSubscription || isSubscriber ? Font.regular(17.0) : Font.semibold(17.0)
var countBackgroundColor: UIColor?
@@ -735,8 +750,15 @@ private final class StarsTransactionSheetContent: CombinedComponent {
transition: .immediate
)
}
let amountAttributedText = NSMutableAttributedString(string: amountText, font: countFont, textColor: countColor)
let amountAttributedText: NSAttributedString
if amountText.contains(environment.dateTimeFormat.decimalSeparator) {
let smallCountFont = Font.regular(14.0)
amountAttributedText = tonAmountAttributedString(amountText, integralFont: countFont, fractionalFont: smallCountFont, color: countColor, decimalSeparator: environment.dateTimeFormat.decimalSeparator)
} else {
amountAttributedText = NSAttributedString(string: amountText, font: countFont, textColor: countColor)
}
let amount = amount.update(
component: BalancedTextComponent(
text: .plain(amountAttributedText),
@@ -758,6 +780,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
)
let tableFont = Font.regular(15.0)
let tableBoldFont = Font.semibold(15.0)
let tableTextColor = theme.list.itemPrimaryTextColor
let tableLinkColor = theme.list.itemAccentColor
var tableItems: [TableComponent.Item] = []
@@ -1027,38 +1050,41 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
}
if let starRefPeerId = transaction.starrefPeerId, let starRefPeer = state.peerMap[starRefPeerId] {
tableItems.append(.init(
id: "to",
title: strings.StarsTransaction_StarRefReason_Affiliate,
component: AnyComponent(
Button(
content: AnyComponent(
PeerCellComponent(
context: component.context,
theme: theme,
peer: starRefPeer
)
),
action: {
if delayedCloseOnOpenPeer {
component.openPeer(starRefPeer, false)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
} else {
if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController {
chatController.playShakeAnimation()
if !transaction.flags.contains(.isPaidMessage) {
tableItems.append(.init(
id: "to",
title: strings.StarsTransaction_StarRefReason_Affiliate,
component: AnyComponent(
Button(
content: AnyComponent(
PeerCellComponent(
context: component.context,
theme: theme,
peer: starRefPeer
)
),
action: {
if delayedCloseOnOpenPeer {
component.openPeer(starRefPeer, false)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
} else {
if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController {
chatController.playShakeAnimation()
}
component.cancel(true)
}
component.cancel(true)
}
}
)
)
)
))
))
}
if let toPeer {
tableItems.append(.init(
id: "referred",
title: strings.StarsTransaction_StarRefReason_Referred,
title: transaction.flags.contains(.isPaidMessage) ? strings.Stars_Transaction_From : strings.StarsTransaction_StarRefReason_Referred,
component: AnyComponent(
Button(
content: AnyComponent(
@@ -1087,13 +1113,41 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
}
if let starrefCommissionPermille = transaction.starrefCommissionPermille, transaction.starrefPeerId != nil {
tableItems.append(.init(
id: "commission",
title: strings.StarsTransaction_Commission,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(formatPermille(starrefCommissionPermille))%", font: tableFont, textColor: tableTextColor))
)),
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
))
if transaction.flags.contains(.isPaidMessage) {
var totalStars = transaction.count
if let starrefCount = transaction.starrefAmount {
totalStars = totalStars + starrefCount
}
let valueString = "\(presentationStringsFormattedNumber(abs(Int32(totalStars.value)), dateTimeFormat.groupingSeparator))⭐️"
let valueAttributedString = NSMutableAttributedString(string: valueString, font: tableBoldFont, textColor: theme.list.itemDisclosureActions.constructive.fillColor)
let range = (valueAttributedString.string as NSString).range(of: "⭐️")
if range.location != NSNotFound {
valueAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: range)
valueAttributedString.addAttribute(.baselineOffset, value: 1.0, range: range)
}
tableItems.append(.init(
id: "paid",
title: strings.Stars_Transaction_Paid,
component: AnyComponent(
MultilineTextWithEntitiesComponent(
context: component.context,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer,
placeholderColor: theme.list.mediaPlaceholderColor,
text: .plain(valueAttributedString),
maximumNumberOfLines: 0
)
),
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
))
} else {
tableItems.append(.init(
id: "commission",
title: strings.StarsTransaction_StarRefReason_Commission,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(formatPermille(starrefCommissionPermille))%", font: tableFont, textColor: tableTextColor)))),
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
))
}
}
}
@@ -1234,19 +1288,21 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var descriptionSize: CGSize = .zero
if !descriptionText.isEmpty {
let openAppExamples = component.openAppExamples
let openPaidMessageFee = component.openPaidMessageFee
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
}
let textColor = countOnTop && !isSubscriber ? theme.list.itemPrimaryTextColor : textColor
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
return (TelegramTextAttributes.URL, contents)
})
let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
}
let descriptionAvailableWidth = isPaidMessage ? context.availableSize.width - sideInset * 2.0 - 16.0 : context.availableSize.width - sideInset * 2.0 - 60.0
let description = description.update(
component: MultilineTextComponent(
text: .plain(attributedString),
@@ -1264,11 +1320,15 @@ private final class StarsTransactionSheetContent: CombinedComponent {
},
tapAction: { attributes, _ in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
openAppExamples()
if isPaidMessage {
openPaidMessageFee()
} else {
openAppExamples()
}
}
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
availableSize: CGSize(width: descriptionAvailableWidth, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
descriptionSize = description.size
@@ -1473,6 +1533,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
let openMessage: (EngineMessage.Id) -> Void
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
let openAppExamples: () -> Void
let openPaidMessageFee: () -> Void
let copyTransactionId: (String) -> Void
let updateSubscription: () -> Void
let sendGift: (EnginePeer.Id) -> Void
@@ -1484,6 +1545,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
openMessage: @escaping (EngineMessage.Id) -> Void,
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
openAppExamples: @escaping () -> Void,
openPaidMessageFee: @escaping () -> Void,
copyTransactionId: @escaping (String) -> Void,
updateSubscription: @escaping () -> Void,
sendGift: @escaping (EnginePeer.Id) -> Void
@@ -1494,6 +1556,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
self.openMessage = openMessage
self.openMedia = openMedia
self.openAppExamples = openAppExamples
self.openPaidMessageFee = openPaidMessageFee
self.copyTransactionId = copyTransactionId
self.updateSubscription = updateSubscription
self.sendGift = sendGift
@@ -1540,6 +1603,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
openMessage: context.component.openMessage,
openMedia: context.component.openMedia,
openAppExamples: context.component.openAppExamples,
openPaidMessageFee: context.component.openPaidMessageFee,
copyTransactionId: context.component.copyTransactionId,
updateSubscription: context.component.updateSubscription,
sendGift: context.component.sendGift
@@ -1640,6 +1704,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
var openMessageImpl: ((EngineMessage.Id) -> Void)?
var openMediaImpl: (([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void)?
var openAppExamplesImpl: (() -> Void)?
var openPaidMessageFeeImpl: (() -> Void)?
var copyTransactionIdImpl: ((String) -> Void)?
var updateSubscriptionImpl: (() -> Void)?
var sendGiftImpl: ((EnginePeer.Id) -> Void)?
@@ -1661,6 +1726,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
openAppExamples: {
openAppExamplesImpl?()
},
openPaidMessageFee: {
openPaidMessageFeeImpl?()
},
copyTransactionId: { transactionId in
copyTransactionIdImpl?(transactionId)
},
@@ -1773,6 +1841,20 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
})
}
openPaidMessageFeeImpl = { [weak self] in
guard let self, let navigationController = self.navigationController as? NavigationController else {
return
}
self.dismissAnimated()
let _ = (context.engine.privacy.requestAccountPrivacySettings()
|> deliverOnMainQueue).start(next: { [weak navigationController] privacySettings in
let controller = context.sharedContext.makeIncomingMessagePrivacyScreen(context: context, value: privacySettings.globalSettings.nonContactChatsPrivacy, exceptions: privacySettings.noPaidMessages, update: { settingValue in
let _ = context.engine.privacy.updateNonContactChatsPrivacy(value: settingValue).start()
})
navigationController?.pushViewController(controller)
})
}
copyTransactionIdImpl = { [weak self] transactionId in
guard let self else {
return