diff --git a/submodules/PremiumUI/Sources/GiftOptionItem.swift b/submodules/PremiumUI/Sources/GiftOptionItem.swift index e1f3108bff..4f29d66b28 100644 --- a/submodules/PremiumUI/Sources/GiftOptionItem.swift +++ b/submodules/PremiumUI/Sources/GiftOptionItem.swift @@ -37,11 +37,12 @@ public final class GiftOptionItem: ListViewItem, ItemListItem { public enum Label { case generic(String) + case semitransparent(String) case boosts(Int32) var string: String { switch self { - case let .generic(value): + case let .generic(value), let .semitransparent(value): return value case let .boosts(value): return "\(value)" @@ -265,6 +266,9 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode { if let label = item.label, case .boosts = label { labelColor = item.presentationData.theme.list.itemAccentColor labelFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0)) + } else if let label = item.label, case .semitransparent = label { + labelColor = item.presentationData.theme.list.itemAccentColor + labelFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0)) } else { labelColor = item.presentationData.theme.list.itemSecondaryTextColor labelFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 17.0 / 17.0)) @@ -539,6 +543,23 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame) transition.updateFrame(node: iconNode, frame: iconFrame) } + } else if let label = item.label, case .semitransparent = label { + let backgroundNode: ASImageNode + if let currentBackground = strongSelf.labelBackgroundNode { + backgroundNode = currentBackground + } else { + backgroundNode = ASImageNode() + backgroundNode.displaysAsynchronously = false + backgroundNode.image = generateStretchableFilledCircleImage(radius: 13.0, color: item.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.1)) + strongSelf.containerNode.insertSubnode(backgroundNode, at: 1) + + strongSelf.labelBackgroundNode = backgroundNode + } + + let labelFrame = CGRect(origin: CGPoint(x: layoutSize.width - rightInset - labelLayout.size.width - 19.0, y: floorToScreenPixels((layout.contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size) + let totalFrame = CGRect(x: labelFrame.minX - 7.0, y: labelFrame.minY - 5.0, width: labelFrame.width + 14.0, height: 26.0) + transition.updateFrame(node: backgroundNode, frame: totalFrame) + transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame) } else { transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: layoutSize.width - rightInset - labelLayout.size.width - 18.0, y: floorToScreenPixels((layout.contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)) } diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index a9782d1b48..5c46b22f65 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -118,7 +118,7 @@ private enum StatsEntry: ItemListNodeEntry { case boostersTitle(PresentationTheme, String) case boostersPlaceholder(PresentationTheme, String) case boosterTabs(PresentationTheme, String, String, Bool) - case booster(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer?, Int32, ChannelBoostersContext.State.Boost.Flags, Int32) + case booster(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer?, Int32, ChannelBoostersContext.State.Boost.Flags, Int32, Int32) case boostersExpand(PresentationTheme, String) case boostersInfo(PresentationTheme, String) @@ -232,7 +232,7 @@ private enum StatsEntry: ItemListNodeEntry { return 2102 case .boosterTabs: return 2103 - case let .booster(index, _, _, _, _, _, _): + case let .booster(index, _, _, _, _, _, _, _): return 2104 + index case .boostersExpand: return 10000 @@ -439,8 +439,8 @@ private enum StatsEntry: ItemListNodeEntry { } else { return false } - case let .booster(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsCount, lhsFlags, lhsExpires): - if case let .booster(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsCount, rhsFlags, rhsExpires) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsPeer == rhsPeer, lhsCount == rhsCount, lhsFlags == rhsFlags, lhsExpires == rhsExpires { + case let .booster(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsCount, lhsFlags, lhsDate, lhsExpires): + if case let .booster(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsCount, rhsFlags, rhsDate, rhsExpires) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsPeer == rhsPeer, lhsCount == rhsCount, lhsFlags == rhsFlags, lhsDate == rhsDate, lhsExpires == rhsExpires { return true } else { return false @@ -548,28 +548,47 @@ private enum StatsEntry: ItemListNodeEntry { return BoostsTabsItem(theme: presentationData.theme, boostsText: boostText, giftsText: giftText, selectedTab: giftSelected ? .gifts : .boosts, sectionId: self.section, selectionUpdated: { tab in arguments.updateGiftsSelected(tab == .gifts) }) - case let .booster(_, _, _, peer, count, flags, expires): + case let .booster(_, _, _, peer, count, flags, date, expires): let expiresValue = stringForDate(timestamp: expires, strings: presentationData.strings) - let expiresString = presentationData.strings.Stats_Boosts_ExpiresOn(expiresValue).string + let expiresString: String + + let durationMonths = Int32(round(Float(expires - date) / (86400.0 * 30.0))) + let durationString = "\(durationMonths)m" let title: String let icon: GiftOptionItem.Icon + var label: String? + if flags.contains(.isGiveaway) { + label = "🏆 Giveaway" + } else if flags.contains(.isGift) { + label = "🎁 Gift" + } if let peer { title = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) icon = .peer(peer) + expiresString = presentationData.strings.Stats_Boosts_ExpiresOn(expiresValue).string } else { + let color: GiftOptionItem.Icon.Color + if durationMonths > 11 { + color = .red + } else if durationMonths > 5 { + color = .blue + } else { + color = .green + } if flags.contains(.isUnclaimed) { title = "Unclaimed" - icon = .image(color: .red, name: "Premium/Unclaimed") + icon = .image(color: color, name: "Premium/Unclaimed") } else if flags.contains(.isGiveaway) { title = "To be distributed" - icon = .image(color: .blue, name: "Premium/ToBeDistributed") + icon = .image(color: color, name: "Premium/ToBeDistributed") } else { title = "Unknown" - icon = .image(color: .red, name: "Premium/ToBeDistributed") + icon = .image(color: color, name: "Premium/ToBeDistributed") } + expiresString = "\(durationString) • \(expiresValue)" } - return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: icon, title: title, titleFont: .bold, titleBadge: count > 1 ? "\(count)" : nil, subtitle: expiresString, sectionId: self.section, action: peer != nil && peer?.id != arguments.context.account.peerId ? { + return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: icon, title: title, titleFont: .bold, titleBadge: count > 1 ? "\(count)" : nil, subtitle: expiresString, label: label.flatMap { .semitransparent($0) }, sectionId: self.section, action: peer != nil && peer?.id != arguments.context.account.peerId ? { arguments.openPeer(peer!) } : nil) case let .boostersExpand(theme, title): @@ -805,7 +824,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p } for booster in boosters { - entries.append(.booster(boosterIndex, presentationData.theme, presentationData.dateTimeFormat, booster.peer, booster.multiplier, booster.flags, booster.expires)) + entries.append(.booster(boosterIndex, presentationData.theme, presentationData.dateTimeFormat, booster.peer, booster.multiplier, booster.flags, booster.date, booster.expires)) boosterIndex += 1 }