mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Various improvements
This commit is contained in:
parent
61b3025654
commit
179c1341be
@ -14845,7 +14845,16 @@ Sorry for the inconvenience.";
|
||||
"ProfileLevelInfo.MyDescriptionDays_any" = "%@ days";
|
||||
"ProfileLevelInfo.MyDescriptionPoints_1" = "%@ point is";
|
||||
"ProfileLevelInfo.MyDescriptionPoints_any" = "%@ points are";
|
||||
"ProfileLevelInfo.MyDescriptionPreview" = "The rating updates in %@ after purchases.\n\%@ pending. [Preview >]()";
|
||||
"ProfileLevelInfo.MyDescriptionPreview" = "The rating will update in %@.\n\%@ pending. [Preview >]()";
|
||||
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewDays_1" = "%@ day";
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewDays_any" = "%@ days";
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewPoints_1" = "%@ point is";
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewPoints_any" = "%@ points are";
|
||||
"ProfileLevelInfo.MyDescriptionInPreview" = "The rating will update in %1$@,\n after %2$@ added. [Back >]()";
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewToday_1" = "This will be your rating today,\n after %@ is added. [Back >]()";
|
||||
"ProfileLevelInfo.MyDescriptionInPreviewToday_any" = "This will be your rating today,\n after %@ added. [Back >]()";
|
||||
|
||||
"ProfileLevelInfo.OtherDescription" = "The rating reflects **%@'s** activity on Telegram. What affects it:";
|
||||
"ProfileLevelInfo.LevelIndex_1" = "Level %@";
|
||||
"ProfileLevelInfo.LevelIndex_any" = "Level %@";
|
||||
@ -14868,3 +14877,8 @@ Sorry for the inconvenience.";
|
||||
"Stories.Post.AlbumAll" = "All Stories";
|
||||
"Stories.Post.AlbumCount_1" = "%@ Album";
|
||||
"Stories.Post.AlbumCount_any" = "%@ Albums";
|
||||
|
||||
"Stories.ToastAddedToFolder_1" = "Story added to folder.";
|
||||
"Stories.ToastAddedToFolder_any" = "%@ stories added to folder.";
|
||||
"Stories.ToastAlbumNotAvailable" = "The album is no longer available.";
|
||||
|
||||
|
@ -715,7 +715,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: !isForum && self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter)
|
||||
}
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition)
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: true, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -786,7 +786,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
|
||||
let overflowInset: CGFloat = 20.0
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: true, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
||||
if isFirstTime {
|
||||
self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
@ -71,7 +71,7 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
func update(type: ChatListSearchFilter, presentationData: PresentationData, selectionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
func update(type: ChatListSearchFilter, displayNewBadge: Bool, presentationData: PresentationData, selectionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.selectionFraction = selectionFraction
|
||||
|
||||
let title: String
|
||||
@ -94,7 +94,9 @@ private final class ItemNode: ASDisplayNode {
|
||||
icon = nil
|
||||
case .globalPosts:
|
||||
title = presentationData.strings.ChatList_Search_FilterGlobalPosts
|
||||
titleBadge = presentationData.strings.ChatList_ContextMenuBadgeNew
|
||||
if displayNewBadge {
|
||||
titleBadge = presentationData.strings.ChatList_ContextMenuBadgeNew
|
||||
}
|
||||
icon = nil
|
||||
case .media:
|
||||
title = presentationData.strings.ChatList_Search_FilterMedia
|
||||
@ -271,7 +273,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
self.scrollNode.layer.removeAllAnimations()
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], displayGlobalPostsNewBadge: Bool, selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) {
|
||||
let isFirstTime = self.currentParams == nil
|
||||
let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : proposedTransition
|
||||
|
||||
@ -321,7 +323,12 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
||||
selectionFraction = 0.0
|
||||
}
|
||||
|
||||
itemNode.update(type: type, presentationData: presentationData, selectionFraction: selectionFraction, transition: itemNodeTransition)
|
||||
var displayNewBadge = false
|
||||
if case .globalPosts = type {
|
||||
displayNewBadge = displayGlobalPostsNewBadge
|
||||
}
|
||||
|
||||
itemNode.update(type: type, displayNewBadge: displayNewBadge, presentationData: presentationData, selectionFraction: selectionFraction, transition: itemNodeTransition)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6189,6 +6189,7 @@ private final class EmptyResultsButtonSearchContent: Component {
|
||||
}
|
||||
|
||||
func update(component: EmptyResultsButtonSearchContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let sideInset: CGFloat = 8.0
|
||||
let iconSpacing: CGFloat = 2.0
|
||||
let arrowSpacing: CGFloat = 4.0
|
||||
|
||||
@ -6237,7 +6238,7 @@ private final class EmptyResultsButtonSearchContent: Component {
|
||||
text: .plain(string)
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - iconSize.width - iconSpacing - arrowSize.width - arrowSpacing, height: 100.0)
|
||||
containerSize: CGSize(width: availableSize.width - iconSize.width - iconSpacing - arrowSize.width - arrowSpacing - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
let textFrame = CGRect(origin: CGPoint(x: iconSize.width + iconSpacing, y: 0.0), size: textSize)
|
||||
if let textView = self.text.view {
|
||||
|
@ -576,7 +576,7 @@ public struct PreferencesKeys {
|
||||
}
|
||||
|
||||
public static func globalPostSearchState() -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4 + 8)
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: PreferencesKeyValues.globalPostSearchState.rawValue)
|
||||
return key
|
||||
}
|
||||
|
@ -207,6 +207,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case voiceMessagesPauseSuggestion = 80
|
||||
case videoMessagesPauseSuggestion = 81
|
||||
case voiceMessagesResumeTrimWarning = 82
|
||||
case globalPostsSearch = 83
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -584,6 +585,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func voiceMessagesResumeTrimWarning() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.voiceMessagesResumeTrimWarning.key)
|
||||
}
|
||||
|
||||
static func globalPostsSearch() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.globalPostsSearch.key)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificNotice {
|
||||
@ -2554,4 +2559,31 @@ public struct ApplicationSpecificNotice {
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getGlobalPostsSearch(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.globalPostsSearch())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementGlobalPostsSearch(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
|
||||
return accountManager.transaction { transaction -> Int in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.globalPostsSearch())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
currentValue = value.value
|
||||
}
|
||||
let previousValue = currentValue
|
||||
currentValue += Int32(count)
|
||||
|
||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.globalPostsSearch(), entry)
|
||||
}
|
||||
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,6 +287,20 @@ public final class LottieComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public func setFrameIndex(index: Int) {
|
||||
guard let _ = self.animationInstance, let animationFrameRange = self.animationFrameRange else {
|
||||
self.scheduledPlayOnce = true
|
||||
return
|
||||
}
|
||||
|
||||
let currentFrame = max(animationFrameRange.lowerBound, min(animationFrameRange.upperBound, index))
|
||||
|
||||
if self.currentFrame != currentFrame {
|
||||
self.currentFrame = currentFrame
|
||||
self.updateImage()
|
||||
}
|
||||
}
|
||||
|
||||
private func loadPlaceholder(data: Data) {
|
||||
guard let component = self.component, let placeholderColor = component.placeholderColor else {
|
||||
return
|
||||
|
@ -7,10 +7,10 @@ import Svg
|
||||
|
||||
private func generateNumberOffsets() -> [CGPoint] {
|
||||
return [
|
||||
CGPoint(x: 0.33, y: -0.33),
|
||||
CGPoint(x: 0.25, y: -0.33),
|
||||
CGPoint(x: 0.24749999999999983, y: -0.495),
|
||||
CGPoint(x: 0.0, y: -1.4025),
|
||||
CGPoint(x: 0.5775, y: -0.495),
|
||||
CGPoint(x: 0.33, y: -0.495),
|
||||
CGPoint(x: 0.33, y: 0.0),
|
||||
CGPoint(x: 0.5775, y: 0.0),
|
||||
CGPoint(x: 0.49500000000000005, y: 0.0),
|
||||
@ -324,7 +324,7 @@ public final class PeerInfoRatingComponent: Component {
|
||||
let backgroundOffsetsY: [Int: CGFloat] = [
|
||||
3: -0.8250000000000001,
|
||||
7: 0.33,
|
||||
40: 1.4025,
|
||||
40: 1.0,
|
||||
60: 0.2475,
|
||||
70: 0.33,
|
||||
80: 0.2475,
|
||||
|
@ -1990,7 +1990,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
#if DEBUG
|
||||
if let _ = starRating.nextLevelStars {
|
||||
self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level, currentLevelStars: starRating.currentLevelStars, stars: starRating.stars + 234, nextLevelStars: starRating.nextLevelStars), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3)
|
||||
self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level + 1, currentLevelStars: starRating.nextLevelStars!, stars: starRating.nextLevelStars! + starRating.nextLevelStars! / 2, nextLevelStars: starRating.nextLevelStars! * 2), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3)
|
||||
self.currentPendingStarRating = TelegramStarPendingRating(rating: TelegramStarRating(level: starRating.level + 1, currentLevelStars: starRating.nextLevelStars!, stars: starRating.nextLevelStars! + starRating.nextLevelStars! / 2 + starRating.nextLevelStars! / 4, nextLevelStars: starRating.nextLevelStars! * 2), timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 3)
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
@ -2092,7 +2092,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let subtitleBadgeFrame: CGRect
|
||||
subtitleBadgeFrame = CGRect(origin: CGPoint(x: (-subtitleSize.width) * 0.5 - subtitleRatingSize.width + 1.0, y: subtitleOffset + floor((-subtitleRatingSize.height) * 0.5)), size: subtitleRatingSize)
|
||||
transition.updateFrameAdditive(view: subtitleRatingView, frame: subtitleBadgeFrame)
|
||||
transition.updateAlpha(layer: subtitleRatingView.layer, alpha: (1.0 - transitionFraction))
|
||||
transition.updateAlpha(layer: subtitleRatingView.layer, alpha: subtitleAlpha)
|
||||
}
|
||||
} else {
|
||||
let titleScale: CGFloat
|
||||
|
@ -2601,9 +2601,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
let text: String
|
||||
text = "Story added to folder."
|
||||
text = presentationData.strings.Stories_ToastAddedToFolder(1)
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: text, cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
})))
|
||||
}
|
||||
@ -2999,8 +2998,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.initialStoryFolderId = nil
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "The album is no longer available.", cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: presentationData.strings.Stories_ToastAlbumNotAvailable, cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
self.currentListState = state
|
||||
@ -5111,13 +5109,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
let _ = listSource.addToFolder(id: folderId, items: items)
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
let text: String
|
||||
if items.count == 1 {
|
||||
text = "Story added to folder."
|
||||
} else {
|
||||
text = "\(items.count) stories added to folder."
|
||||
}
|
||||
let text: String = presentationData.strings.Stories_ToastAddedToFolder(Int32(items.count))
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: text, cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
})
|
||||
|
@ -355,11 +355,15 @@ private final class ProfileLevelInfoScreenComponent: Component {
|
||||
if pendingStarRating.rating.stars > component.starRating.stars {
|
||||
let pendingPoints = pendingStarRating.rating.stars - component.starRating.stars
|
||||
|
||||
let dayCount = (pendingStarRating.timestamp - timestamp) / (24 * 60 * 60)
|
||||
|
||||
if self.isPreviewingPendingRating {
|
||||
//TODO:localize
|
||||
secondaryDescriptionTextString = "This will be your rating in 21 days,\n after \(pendingPoints) points are added. [Back >]()"
|
||||
if dayCount == 0 {
|
||||
secondaryDescriptionTextString = environment.strings.ProfileLevelInfo_MyDescriptionInPreviewToday(Int32(pendingPoints))
|
||||
} else {
|
||||
secondaryDescriptionTextString = environment.strings.ProfileLevelInfo_MyDescriptionInPreview(environment.strings.ProfileLevelInfo_MyDescriptionInPreviewDays(Int32(dayCount)), environment.strings.ProfileLevelInfo_MyDescriptionInPreviewPoints(Int32(pendingPoints))).string
|
||||
}
|
||||
} else {
|
||||
let dayCount = (pendingStarRating.timestamp - timestamp) / (24 * 60 * 60)
|
||||
if dayCount == 0 {
|
||||
secondaryDescriptionTextString = environment.strings.ProfileLevelInfo_MyDescriptionToday(Int32(pendingPoints))
|
||||
} else {
|
||||
@ -615,23 +619,23 @@ private final class ProfileLevelInfoScreenComponent: Component {
|
||||
}
|
||||
let items: [Item] = [
|
||||
Item(
|
||||
title: "Gifts from Telegram",
|
||||
text: "100% of the Stars spent on gifts purchased from Telegram.",
|
||||
badgeText: "ADDED",
|
||||
title: environment.strings.ProfileLevelInfo_Item0_Title,
|
||||
text: environment.strings.ProfileLevelInfo_Item0_Text,
|
||||
badgeText: environment.strings.ProfileLevelInfo_Item0_Badge,
|
||||
isBadgeAccent: true,
|
||||
icon: "Chat/Input/Accessory Panels/Gift"
|
||||
),
|
||||
Item(
|
||||
title: "Gifts and Posts from Users",
|
||||
text: "20% of the Stars spent on gifts or posts from users and channels.",
|
||||
badgeText: "ADDED",
|
||||
title: environment.strings.ProfileLevelInfo_Item1_Title,
|
||||
text: environment.strings.ProfileLevelInfo_Item1_Text,
|
||||
badgeText: environment.strings.ProfileLevelInfo_Item1_Badge,
|
||||
isBadgeAccent: true,
|
||||
icon: "Peer Info/ProfileLevelInfo2"
|
||||
),
|
||||
Item(
|
||||
title: "Refunds and Conversions",
|
||||
text: "10x of refunded Stars and 85% of bought gifts converted to Stars.",
|
||||
badgeText: "DEDUCTED",
|
||||
title: environment.strings.ProfileLevelInfo_Item2_Title,
|
||||
text: environment.strings.ProfileLevelInfo_Item2_Text,
|
||||
badgeText: environment.strings.ProfileLevelInfo_Item2_Badge,
|
||||
isBadgeAccent: false,
|
||||
icon: "Peer Info/ProfileLevelInfo3"
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ import ComponentFlow
|
||||
import RoundedRectWithTailPath
|
||||
import AnimatedTextComponent
|
||||
import MultilineTextComponent
|
||||
import LottieComponent
|
||||
|
||||
final class ProfileLevelRatingBarBadge: Component {
|
||||
final class TransitionHint {
|
||||
@ -47,6 +48,7 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
private let badgeView: UIView
|
||||
private let badgeMaskView: UIView
|
||||
private let badgeShapeLayer = SimpleShapeLayer()
|
||||
private let badgeShapeAnimation = ComponentView<Empty>()
|
||||
|
||||
private let badgeForeground: SimpleLayer
|
||||
let badgeIcon: UIImageView
|
||||
@ -64,6 +66,7 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
override init(frame: CGRect) {
|
||||
self.badgeView = UIView()
|
||||
self.badgeView.alpha = 0.0
|
||||
self.badgeView.layer.anchorPoint = CGPoint()
|
||||
|
||||
self.badgeShapeLayer.fillColor = UIColor.white.cgColor
|
||||
self.badgeShapeLayer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
@ -147,7 +150,7 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
containerSize: CGSize(width: 300.0, height: 100.0)
|
||||
)
|
||||
|
||||
var badgeWidth: CGFloat = badgeLabelSize.width + 3.0 + 54.0
|
||||
var badgeWidth: CGFloat = badgeLabelSize.width + 3.0 + 60.0
|
||||
if component.suffix != nil {
|
||||
badgeWidth += badgeSuffixSize.width + badgeSuffixSpacing
|
||||
}
|
||||
@ -161,7 +164,7 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
|
||||
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 600.0, height: badgeFullSize.height + 10.0))
|
||||
|
||||
self.badgeIcon.frame = CGRect(x: 10.0, y: 8.0, width: 30.0, height: 30.0)
|
||||
self.badgeIcon.frame = CGRect(x: 13.0, y: 8.0, width: 30.0, height: 30.0)
|
||||
|
||||
self.badgeView.alpha = 1.0
|
||||
|
||||
@ -172,7 +175,7 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
badgeContentWidth += badgeSuffixSpacing + badgeSuffixSize.width
|
||||
}
|
||||
|
||||
let badgeLabelFrame = CGRect(origin: CGPoint(x: 14.0 + floorToScreenPixels((badgeFullSize.width - badgeContentWidth) / 2.0), y: 9.0), size: badgeLabelSize)
|
||||
let badgeLabelFrame = CGRect(origin: CGPoint(x: 15.0 + floorToScreenPixels((badgeFullSize.width - badgeContentWidth) / 2.0), y: 9.0), size: badgeLabelSize)
|
||||
if let badgeLabelView = self.badgeLabel.view {
|
||||
if badgeLabelView.superview == nil {
|
||||
self.badgeView.addSubview(badgeLabelView)
|
||||
@ -214,6 +217,11 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
}
|
||||
|
||||
func adjustTail(size: CGSize, overflowWidth: CGFloat, transition: ComponentTransition) {
|
||||
guard let component else {
|
||||
return
|
||||
}
|
||||
let _ = component
|
||||
|
||||
var tailPosition = size.width * 0.5
|
||||
tailPosition += overflowWidth
|
||||
tailPosition = max(36.0, min(size.width - 36.0, tailPosition))
|
||||
@ -221,9 +229,46 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
let tailPositionFraction = tailPosition / size.width
|
||||
transition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: size, tailPosition: tailPositionFraction, transformTail: false).cgPath)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: tailPositionFraction, y: 1.0))
|
||||
transition.updatePosition(layer: self.badgeView.layer, position: CGPoint(x: (tailPositionFraction - 0.5) * size.width, y: 0.0))
|
||||
let badgeShapeSize = CGSize(width: 128, height: 128)
|
||||
let _ = self.badgeShapeAnimation.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "badge_with_tail"),
|
||||
color: .red,//component.theme.list.itemCheckColors.fillColor,
|
||||
placeholderColor: nil,
|
||||
startingPosition: .begin,
|
||||
size: badgeShapeSize,
|
||||
renderingScale: nil,
|
||||
loop: false,
|
||||
playOnce: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: badgeShapeSize
|
||||
)
|
||||
if let badgeShapeAnimationView = self.badgeShapeAnimation.view as? LottieComponent.View, !"".isEmpty {
|
||||
if badgeShapeAnimationView.superview == nil {
|
||||
badgeShapeAnimationView.layer.anchorPoint = CGPoint()
|
||||
self.addSubview(badgeShapeAnimationView)
|
||||
}
|
||||
|
||||
let transition: ComponentTransition = .immediate
|
||||
|
||||
let shapeFrame = CGRect(origin: CGPoint(x: 0.0, y: -10.0), size: badgeShapeSize)
|
||||
badgeShapeAnimationView.center = shapeFrame.origin
|
||||
badgeShapeAnimationView.bounds = CGRect(origin: CGPoint(), size: shapeFrame.size)
|
||||
|
||||
let scaleFactor: CGFloat = 144.0 / (946.0 / 4.0)
|
||||
transition.setScale(view: badgeShapeAnimationView, scale: scaleFactor)
|
||||
|
||||
let badgeShapeWidth = floor(shapeFrame.width * scaleFactor)
|
||||
let badgeShapeOffset = -overflowWidth / badgeShapeWidth
|
||||
let _ = badgeShapeOffset
|
||||
|
||||
//badgeShapeAnimationView.setFrameIndex(index: 0)
|
||||
}
|
||||
|
||||
//let transition: ContainedViewLayoutTransition = .immediate
|
||||
//transition.updateAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: tailPositionFraction, y: 1.0))
|
||||
}
|
||||
|
||||
func updateBadgeAngle(angle: CGFloat) {
|
||||
@ -272,168 +317,3 @@ final class ProfileLevelRatingBarBadge: Component {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private let labelWidth: CGFloat = 16.0
|
||||
private let labelHeight: CGFloat = 36.0
|
||||
private let labelSize = CGSize(width: labelWidth, height: labelHeight)
|
||||
private let font = Font.with(size: 24.0, design: .round, weight: .semibold, traits: [])
|
||||
|
||||
private final class BadgeLabelView: UIView {
|
||||
private class StackView: UIView {
|
||||
var labels: [UILabel] = []
|
||||
|
||||
var currentValue: Int32 = 0
|
||||
|
||||
var color: UIColor = .white {
|
||||
didSet {
|
||||
for view in self.labels {
|
||||
view.textColor = self.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect(origin: .zero, size: labelSize))
|
||||
|
||||
var height: CGFloat = -labelHeight
|
||||
for i in -1 ..< 10 {
|
||||
let label = UILabel()
|
||||
if i == -1 {
|
||||
label.text = "9"
|
||||
} else {
|
||||
label.text = "\(i)"
|
||||
}
|
||||
label.textColor = self.color
|
||||
label.font = font
|
||||
label.textAlignment = .center
|
||||
label.frame = CGRect(x: 0, y: height, width: labelWidth, height: labelHeight)
|
||||
self.addSubview(label)
|
||||
self.labels.append(label)
|
||||
|
||||
height += labelHeight
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(value: Int32, isFirst: Bool, isLast: Bool, transition: ComponentTransition) {
|
||||
let previousValue = self.currentValue
|
||||
self.currentValue = value
|
||||
|
||||
self.labels[1].alpha = isFirst && !isLast ? 0.0 : 1.0
|
||||
|
||||
if previousValue == 9 && value < 9 {
|
||||
self.bounds = CGRect(
|
||||
origin: CGPoint(
|
||||
x: 0.0,
|
||||
y: -1.0 * labelSize.height
|
||||
),
|
||||
size: labelSize
|
||||
)
|
||||
}
|
||||
|
||||
let bounds = CGRect(
|
||||
origin: CGPoint(
|
||||
x: 0.0,
|
||||
y: CGFloat(value) * labelSize.height
|
||||
),
|
||||
size: labelSize
|
||||
)
|
||||
transition.setBounds(view: self, bounds: bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private var itemViews: [Int: StackView] = [:]
|
||||
private var staticLabel = UILabel()
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
var color: UIColor = .white {
|
||||
didSet {
|
||||
self.staticLabel.textColor = self.color
|
||||
for (_, view) in self.itemViews {
|
||||
view.color = self.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(value: String, transition: ComponentTransition) -> CGSize {
|
||||
if value.contains(" ") {
|
||||
for (_, view) in self.itemViews {
|
||||
view.isHidden = true
|
||||
}
|
||||
|
||||
if self.staticLabel.superview == nil {
|
||||
self.staticLabel.textColor = self.color
|
||||
self.staticLabel.font = font
|
||||
|
||||
self.addSubview(self.staticLabel)
|
||||
}
|
||||
|
||||
self.staticLabel.text = value
|
||||
let size = self.staticLabel.sizeThatFits(CGSize(width: 100.0, height: 100.0))
|
||||
self.staticLabel.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: labelHeight))
|
||||
|
||||
return CGSize(width: ceil(self.staticLabel.bounds.width), height: ceil(self.staticLabel.bounds.height))
|
||||
}
|
||||
|
||||
let string = value
|
||||
let stringArray = Array(string.map { String($0) }.reversed())
|
||||
|
||||
let labelSpacing: CGFloat = 0.0
|
||||
|
||||
let totalWidth = CGFloat(stringArray.count) * labelWidth + CGFloat(stringArray.count - 1) * labelSpacing
|
||||
|
||||
var validIds: [Int] = []
|
||||
for i in 0 ..< stringArray.count {
|
||||
validIds.append(i)
|
||||
|
||||
let itemView: StackView
|
||||
var itemTransition = transition
|
||||
if let current = self.itemViews[i] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = transition.withAnimation(.none)
|
||||
itemView = StackView()
|
||||
itemView.color = self.color
|
||||
self.itemViews[i] = itemView
|
||||
self.addSubview(itemView)
|
||||
}
|
||||
|
||||
let digit = Int32(stringArray[i]) ?? 0
|
||||
itemView.update(value: digit, isFirst: i == stringArray.count - 1, isLast: i == 0, transition: transition)
|
||||
|
||||
itemTransition.setFrame(
|
||||
view: itemView,
|
||||
frame: CGRect(x: totalWidth - labelWidth * CGFloat(i + 1) + labelSpacing * CGFloat(i), y: 0.0, width: labelWidth, height: labelHeight)
|
||||
)
|
||||
}
|
||||
|
||||
var removeIds: [Int] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
|
||||
transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
return CGSize(width: totalWidth, height: labelHeight)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import BundleIconComponent
|
||||
import HierarchyTrackingLayer
|
||||
import AnimatedTextComponent
|
||||
|
||||
final class ProfileLevelRatingBarComponent: Component {
|
||||
final class TransitionHint {
|
||||
@ -286,33 +287,53 @@ final class ProfileLevelRatingBarComponent: Component {
|
||||
let labelFont = Font.semibold(14.0)
|
||||
|
||||
let leftLabelSize = self.backgroundLeftLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.leftLabel, font: labelFont, textColor: component.theme.list.itemPrimaryTextColor))
|
||||
transition: labelsTransition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
font: labelFont,
|
||||
color: component.theme.list.itemPrimaryTextColor,
|
||||
items: [AnimatedTextComponent.Item(
|
||||
id: AnyHashable(0),
|
||||
content: .text(component.leftLabel)
|
||||
)]
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0)
|
||||
)
|
||||
let _ = self.foregroundLeftLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.leftLabel, font: labelFont, textColor: component.theme.list.itemCheckColors.foregroundColor))
|
||||
transition: labelsTransition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
font: labelFont,
|
||||
color: component.theme.list.itemCheckColors.foregroundColor,
|
||||
items: [AnimatedTextComponent.Item(
|
||||
id: AnyHashable(0),
|
||||
content: .text(component.leftLabel)
|
||||
)]
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0)
|
||||
)
|
||||
let rightLabelSize = self.backgroundRightLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.rightLabel, font: labelFont, textColor: component.theme.list.itemPrimaryTextColor))
|
||||
transition: labelsTransition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
font: labelFont,
|
||||
color: component.theme.list.itemPrimaryTextColor,
|
||||
items: [AnimatedTextComponent.Item(
|
||||
id: AnyHashable(0),
|
||||
content: .text(component.rightLabel)
|
||||
)]
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0)
|
||||
)
|
||||
let _ = self.foregroundRightLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.rightLabel, font: labelFont, textColor: component.theme.list.itemCheckColors.foregroundColor))
|
||||
transition: labelsTransition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
font: labelFont,
|
||||
color: component.theme.list.itemCheckColors.foregroundColor,
|
||||
items: [AnimatedTextComponent.Item(
|
||||
id: AnyHashable(0),
|
||||
content: .text(component.rightLabel)
|
||||
)]
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: barBackgroundFrame.width, height: 100.0)
|
||||
@ -365,36 +386,32 @@ final class ProfileLevelRatingBarComponent: Component {
|
||||
containerSize: CGSize(width: 200.0, height: 200.0)
|
||||
)
|
||||
|
||||
var badgeFrame = CGRect(origin: CGPoint(x: barBackgroundFrame.minX + barForegroundFrame.width, y: barBackgroundFrame.minY - 7.0), size: badgeSize)
|
||||
if let badgeView = self.badge.view as? ProfileLevelRatingBarBadge.View {
|
||||
if badgeView.superview == nil {
|
||||
self.addSubview(badgeView)
|
||||
}
|
||||
|
||||
let apparentBadgeSize: CGSize
|
||||
var apparentBadgeOffset: CGFloat = 0.0
|
||||
if let animationState = self.animationState {
|
||||
apparentBadgeSize = animationState.badgeSize(at: CACurrentMediaTime(), endValue: badgeSize)
|
||||
apparentBadgeOffset = (animationState.fromBadgeSize.width - badgeSize.width) * (1.0 - animationState.fraction(at: CACurrentMediaTime()))
|
||||
apparentBadgeOffset = -apparentBadgeOffset * 0.25
|
||||
} else {
|
||||
apparentBadgeSize = badgeSize
|
||||
}
|
||||
|
||||
badgeFrame.size = apparentBadgeSize
|
||||
var badgeFrame = CGRect(origin: CGPoint(x: barBackgroundFrame.minX + barForegroundFrame.width - apparentBadgeSize.width * 0.5, y: barBackgroundFrame.minY - 18.0 - badgeSize.height), size: apparentBadgeSize)
|
||||
|
||||
let badgeSideInset: CGFloat = 0.0
|
||||
|
||||
let badgeOverflowWidth: CGFloat
|
||||
if badgeFrame.minX - apparentBadgeSize.width * 0.5 < badgeSideInset {
|
||||
badgeOverflowWidth = badgeSideInset - (badgeFrame.minX - apparentBadgeSize.width * 0.5)
|
||||
} else if badgeFrame.minX + apparentBadgeSize.width * 0.5 > availableSize.width - badgeSideInset {
|
||||
badgeOverflowWidth = availableSize.width - badgeSideInset - (badgeFrame.minX + apparentBadgeSize.width * 0.5)
|
||||
if badgeFrame.minX < badgeSideInset {
|
||||
badgeOverflowWidth = badgeSideInset - badgeFrame.minX
|
||||
} else if badgeFrame.minX + badgeFrame.width > availableSize.width - badgeSideInset {
|
||||
badgeOverflowWidth = availableSize.width - badgeSideInset - badgeFrame.width - badgeFrame.minX
|
||||
} else {
|
||||
badgeOverflowWidth = 0.0
|
||||
}
|
||||
|
||||
badgeFrame.origin.x += badgeOverflowWidth + apparentBadgeOffset
|
||||
badgeFrame.origin.x += badgeOverflowWidth
|
||||
badgeView.frame = badgeFrame
|
||||
|
||||
badgeView.adjustTail(size: apparentBadgeSize, overflowWidth: -badgeOverflowWidth, transition: transition)
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user