Improvements

This commit is contained in:
Isaac 2025-07-29 19:22:41 +02:00
parent b625243040
commit e574e2def7
3 changed files with 115 additions and 27 deletions

View File

@ -219,8 +219,9 @@ public final class PeerInfoRatingComponent: Component {
var textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textLayout.size.width) * 0.5), y: floorToScreenPixels((size.height - textLayout.size.height) * 0.5)), size: textLayout.size)
if level == 1 {
} else {
textFrame.origin.x += UIScreenPixel
} else {
textFrame.origin.x += 0.0
}
context.saveGState()

View File

@ -1959,7 +1959,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.currentPendingStarRating = cachedData.pendingStarRating
#if DEBUG
self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level, currentLevelStars: starRating.currentLevelStars, stars: starRating.stars + 123, nextLevelStars: starRating.nextLevelStars), timestamp: 0)
self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level, currentLevelStars: starRating.currentLevelStars, stars: starRating.stars + 123, nextLevelStars: starRating.nextLevelStars), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3)
#endif
} else {
self.currentStarRating = nil

View File

@ -42,6 +42,14 @@ private final class ProfileLevelInfoScreenComponent: Component {
return true
}
private final class TransitionHint {
let isChangingPreview: Bool
init(isChangingPreview: Bool) {
self.isChangingPreview = isChangingPreview
}
}
private final class ScrollView: UIScrollView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event)
@ -98,10 +106,12 @@ private final class ProfileLevelInfoScreenComponent: Component {
private weak var state: EmptyComponentState?
private var environment: ViewControllerComponentContainer.Environment?
private var isUpdating: Bool = false
private var isPreviewingPendingRating: Bool = false
private var itemLayout: ItemLayout?
private var topOffsetDistance: CGFloat?
private var cachedChevronImage: UIImage?
private var cachedCloseImage: UIImage?
override init(frame: CGRect) {
@ -219,7 +229,7 @@ private final class ProfileLevelInfoScreenComponent: Component {
transition.setPosition(view: self.navigationBarContainer, position: CGPoint(x: 0.0, y: topOffset + itemLayout.containerInset))
let topOffsetDistance: CGFloat = min(200.0, floor(itemLayout.containerSize.height * 0.25))
let topOffsetDistance: CGFloat = 80.0
self.topOffsetDistance = topOffsetDistance
var topOffsetFraction = topOffset / topOffsetDistance
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
@ -273,6 +283,8 @@ private final class ProfileLevelInfoScreenComponent: Component {
self.isUpdating = false
}
let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.16)
let environment = environment[ViewControllerComponentContainer.Environment.self].value
let themeUpdated = self.environment?.theme !== environment.theme
@ -337,10 +349,21 @@ private final class ProfileLevelInfoScreenComponent: Component {
if component.peer.id == component.context.account.peerId {
descriptionTextString = "The rating reflects your activity on Telegram. What affects it:"
if let pendingStarRating = component.pendingStarRating {
let timestamp = Int32(Date().timeIntervalSince1970)
if let pendingStarRating = component.pendingStarRating, pendingStarRating.timestamp > timestamp {
if pendingStarRating.rating.stars > component.starRating.stars {
let pendingPoints = pendingStarRating.rating.stars - component.starRating.stars
secondaryDescriptionTextString = "The rating updates in 21 days after purchases.\n\(pendingPoints) points are pending."
if self.isPreviewingPendingRating {
secondaryDescriptionTextString = "This will be your rating in 21 days,\n after \(pendingPoints) points are added. [Back >]()"
} else {
let dayCount = (pendingStarRating.timestamp - timestamp) / (24 * 60 * 60)
if dayCount == 0 {
secondaryDescriptionTextString = "The rating updates today.\n\(pendingPoints) points are pending."
} else {
secondaryDescriptionTextString = "The rating updates in \(dayCount) days after purchases.\n\(pendingPoints) points are pending."
}
}
}
}
} else {
@ -378,16 +401,36 @@ private final class ProfileLevelInfoScreenComponent: Component {
]
let levelFraction: CGFloat
if let nextLevelStars = component.starRating.nextLevelStars {
levelFraction = Double(component.starRating.stars) / Double(nextLevelStars)
} else {
levelFraction = 1.0
}
let badgeText = starCountString(Int64(component.starRating.stars), decimalSeparator: ".")
let badgeText: String
var badgeTextSuffix: String?
if let nextLevelStars = component.starRating.nextLevelStars {
badgeTextSuffix = " / \(starCountString(Int64(nextLevelStars), decimalSeparator: "."))"
let currentLevel: Int32
let nextLevel: Int32?
if let pendingStarRating = component.pendingStarRating, pendingStarRating.rating.stars > component.starRating.stars, self.isPreviewingPendingRating {
badgeText = starCountString(Int64(pendingStarRating.rating.stars), decimalSeparator: ".")
currentLevel = pendingStarRating.rating.level
nextLevel = pendingStarRating.rating.nextLevelStars == nil ? nil : currentLevel + 1
if let nextLevelStars = pendingStarRating.rating.nextLevelStars {
badgeTextSuffix = " / \(starCountString(Int64(nextLevelStars), decimalSeparator: "."))"
}
if let nextLevelStars = pendingStarRating.rating.nextLevelStars {
levelFraction = Double(pendingStarRating.rating.stars) / Double(nextLevelStars)
} else {
levelFraction = 1.0
}
} else {
badgeText = starCountString(Int64(component.starRating.stars), decimalSeparator: ".")
currentLevel = component.starRating.level
nextLevel = component.starRating.nextLevelStars == nil ? nil : currentLevel + 1
if let nextLevelStars = component.starRating.nextLevelStars {
badgeTextSuffix = " / \(starCountString(Int64(nextLevelStars), decimalSeparator: "."))"
}
if let nextLevelStars = component.starRating.nextLevelStars {
levelFraction = Double(component.starRating.stars) / Double(nextLevelStars)
} else {
levelFraction = 1.0
}
}
let levelInfoSize = self.levelInfo.update(
@ -399,7 +442,7 @@ private final class ProfileLevelInfoScreenComponent: Component {
inactiveValue: "",
inactiveTitleColor: environment.theme.list.itemPrimaryTextColor,
activeTitle: "",
activeValue: component.starRating.nextLevelStars == nil ? "" : "Level \(component.starRating.level + 1)",
activeValue: nextLevel.flatMap { "Level \($0)" } ?? "",
activeTitleColor: .white,
badgeIconName: "Peer Info/ProfileLevelProgressIcon",
badgeText: badgeText,
@ -421,32 +464,72 @@ private final class ProfileLevelInfoScreenComponent: Component {
contentHeight += 129.0
let isChangingPreview = transition.userData(TransitionHint.self)?.isChangingPreview ?? false
if let secondaryDescriptionTextString {
if isChangingPreview, let secondaryDescriptionTextView = self.secondaryDescriptionText?.view {
self.secondaryDescriptionText = nil
transition.setTransform(view: secondaryDescriptionTextView, transform: CATransform3DMakeScale(0.9, 0.9, 1.0))
alphaTransition.setAlpha(view: secondaryDescriptionTextView, alpha: 0.0, completion: { [weak secondaryDescriptionTextView] _ in
secondaryDescriptionTextView?.removeFromSuperview()
})
}
contentHeight -= 8.0
let secondaryDescriptionText: ComponentView<Empty>
var secondaryDescriptionTextTransition = transition
if let current = self.secondaryDescriptionText {
secondaryDescriptionText = current
} else {
secondaryDescriptionTextTransition = .immediate
secondaryDescriptionText = ComponentView()
self.secondaryDescriptionText = secondaryDescriptionText
}
let secondaryDescriptionAttributedString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(secondaryDescriptionTextString, attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
linkAttribute: { url in
return ("URL", url)
}
)))
let chevronImage: UIImage?
if let current = self.cachedChevronImage {
chevronImage = current
} else {
chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: .white)
self.cachedChevronImage = chevronImage
}
if let range = secondaryDescriptionAttributedString.string.range(of: ">"), let chevronImage {
secondaryDescriptionAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: secondaryDescriptionAttributedString.string))
}
let secondaryDescriptionTextSize = secondaryDescriptionText.update(
transition: .immediate,
component: AnyComponent(BalancedTextComponent(
text: .markdown(
text: secondaryDescriptionTextString,
attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
linkAttribute: { url in
return ("URL", url)
}
)
),
text: .plain(secondaryDescriptionAttributedString),
horizontalAlignment: .center,
maximumNumberOfLines: 0,
lineSpacing: 0.2
lineSpacing: 0.2,
highlightColor: environment.theme.list.itemAccentColor.withMultipliedAlpha(0.1),
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
return NSAttributedString.Key(rawValue: "URL")
} else {
return nil
}
},
tapAction: { [weak self] attributes, _ in
guard let self else {
return
}
self.isPreviewingPendingRating = !self.isPreviewingPendingRating
var transition: ComponentTransition = .spring(duration: 0.4)
transition = transition.withUserData(TransitionHint(isChangingPreview: true))
self.state?.updated(transition: transition)
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
@ -455,8 +538,12 @@ private final class ProfileLevelInfoScreenComponent: Component {
if let secondaryDescriptionTextView = secondaryDescriptionText.view {
if secondaryDescriptionTextView.superview == nil {
self.scrollContentView.addSubview(secondaryDescriptionTextView)
if isChangingPreview {
transition.animateScale(view: secondaryDescriptionTextView, from: 0.9, to: 1.0)
alphaTransition.animateAlpha(view: secondaryDescriptionTextView, from: 0.0, to: 1.0)
}
}
transition.setPosition(view: secondaryDescriptionTextView, position: secondaryDescriptionTextFrame.center)
secondaryDescriptionTextTransition.setPosition(view: secondaryDescriptionTextView, position: secondaryDescriptionTextFrame.center)
secondaryDescriptionTextView.bounds = CGRect(origin: CGPoint(), size: secondaryDescriptionTextFrame.size)
}
contentHeight += secondaryDescriptionTextSize.height