Various improvements

This commit is contained in:
Ilya Laktyushin 2026-03-06 13:20:49 +01:00
parent d5ccfe2812
commit e279f224a1
20 changed files with 383 additions and 559 deletions

View file

@ -14,6 +14,7 @@ import MultilineTextComponent
import BundleIconComponent
import PlainButtonComponent
import AccountContext
import GlassBackgroundComponent
final class BoostHeaderItem: ItemListControllerHeaderItem {
let context: AccountContext
@ -291,9 +292,9 @@ private final class BoostHeaderComponent: CombinedComponent {
let progress = Child(PremiumLimitDisplayComponent.self)
let text = Child(MultilineTextComponent.self)
let boostButton = Child(PlainButtonComponent.self)
let giveawayButton = Child(PlainButtonComponent.self)
let featuresButton = Child(PlainButtonComponent.self)
let boostButton = Child(HeaderButtonComponent.self)
let giveawayButton = Child(HeaderButtonComponent.self)
let featuresButton = Child(HeaderButtonComponent.self)
return { context in
let size = context.availableSize
@ -393,18 +394,14 @@ private final class BoostHeaderComponent: CombinedComponent {
)
let minButtonWidth: CGFloat = 112.0
// let buttonSpacing = (size.width - sideInset * 2.0 - minButtonWidth * 3.0) / 2.0
let buttonHeight: CGFloat = 58.0
let buttonColor = UIColor(rgb: 0x908eff)
let boostButton = boostButton.update(
component: PlainButtonComponent(
content: AnyComponent(
BoostButtonComponent(
iconName: "Premium/Boosts/Boost",
title: component.strings.ChannelBoost_Header_Boost
)
),
effectAlignment: .center,
component: HeaderButtonComponent(
title: component.strings.ChannelBoost_Header_Boost,
buttonColor: buttonColor,
iconName: "Premium/Boosts/Boost",
action: {
component.openBoost()
}
@ -417,14 +414,10 @@ private final class BoostHeaderComponent: CombinedComponent {
)
let giveawayButton = giveawayButton.update(
component: PlainButtonComponent(
content: AnyComponent(
BoostButtonComponent(
iconName: "Premium/Boosts/Giveaway",
title: component.strings.ChannelBoost_Header_Giveaway
)
),
effectAlignment: .center,
component: HeaderButtonComponent(
title: component.strings.ChannelBoost_Header_Giveaway,
buttonColor: buttonColor,
iconName: "Premium/Boosts/Giveaway",
action: {
component.createGiveaway()
}
@ -437,14 +430,10 @@ private final class BoostHeaderComponent: CombinedComponent {
)
let featuresButton = featuresButton.update(
component: PlainButtonComponent(
content: AnyComponent(
BoostButtonComponent(
iconName: "Premium/Boosts/Features",
title: component.strings.ChannelBoost_Header_Features
)
),
effectAlignment: .center,
component: HeaderButtonComponent(
title: component.strings.ChannelBoost_Header_Features,
buttonColor: buttonColor,
iconName: "Premium/Boosts/Features",
action: {
component.openFeatures()
}
@ -537,3 +526,167 @@ private final class BoostButtonComponent: CombinedComponent {
}
}
}
private final class HeaderButtonComponent: Component {
let title: String
let buttonColor: UIColor
let iconName: String
let isLocked: Bool
let action: () -> Void
public init(
title: String,
buttonColor: UIColor,
iconName: String,
isLocked: Bool = false,
action: @escaping () -> Void
) {
self.title = title
self.buttonColor = buttonColor
self.iconName = iconName
self.isLocked = isLocked
self.action = action
}
static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool {
if lhs.title != rhs.title {
return false
}
if lhs.buttonColor != rhs.buttonColor {
return false
}
if lhs.iconName != rhs.iconName {
return false
}
if lhs.isLocked != rhs.isLocked {
return false
}
return true
}
final class View: UIView {
private var component: HeaderButtonComponent?
private weak var componentState: EmptyComponentState?
private let backgroundView = GlassBackgroundView()
private let title = ComponentView<Empty>()
private let icon = ComponentView<Empty>()
private let lockIcon = ComponentView<Empty>()
private let button = HighlightTrackingButton()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.backgroundView.contentView.addSubview(self.button)
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func buttonPressed() {
if let component = self.component {
component.action()
}
}
func update(component: HeaderButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.component = component
self.componentState = state
let bounds = CGRect(origin: .zero, size: availableSize)
self.backgroundView.update(size: bounds.size, cornerRadius: 16.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)), isInteractive: true, transition: transition)
transition.setFrame(view: self.backgroundView, frame: bounds)
let iconSize = self.icon.update(
transition: transition,
component: AnyComponent(
BundleIconComponent(
name: component.iconName,
tintColor: UIColor.white
)
),
environment: {},
containerSize: availableSize
)
if let iconView = self.icon.view {
if iconView.superview == nil {
iconView.isUserInteractionEnabled = false
self.backgroundView.contentView.addSubview(iconView)
}
iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: floorToScreenPixels(22.0 - iconSize.height * 0.5)), size: iconSize)
}
let titleSize = self.title.update(
transition: transition,
component: AnyComponent(
MultilineTextComponent(
text: .plain(NSAttributedString(
string: component.title,
font: Font.regular(11.0),
textColor: UIColor.white,
paragraphAlignment: .natural
)),
horizontalAlignment: .center,
maximumNumberOfLines: 1
)
),
environment: {},
containerSize: CGSize(width: availableSize.width - 16.0, height: availableSize.height)
)
var totalTitleWidth = titleSize.width
var titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0
if component.isLocked {
let titleSpacing: CGFloat = 3.0
let lockIconSize = self.lockIcon.update(
transition: transition,
component: AnyComponent(
BundleIconComponent(
name: "Chat List/StatusLockIcon",
tintColor: .white
)
),
environment: {},
containerSize: availableSize
)
totalTitleWidth += lockIconSize.width + titleSpacing
titleOriginX = availableSize.width / 2.0 - totalTitleWidth / 2.0
if let lockIconView = self.lockIcon.view {
if lockIconView.superview == nil {
lockIconView.isUserInteractionEnabled = false
self.backgroundView.contentView.addSubview(lockIconView)
}
lockIconView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - lockIconSize.height * 0.5)), size: lockIconSize)
}
titleOriginX += lockIconSize.width + titleSpacing
}
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.isUserInteractionEnabled = false
self.backgroundView.contentView.addSubview(titleView)
}
titleView.frame = CGRect(origin: CGPoint(x: titleOriginX, y: floorToScreenPixels(42.0 - titleSize.height * 0.5)), size: titleSize)
}
self.button.frame = bounds
return availableSize
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View file

@ -932,7 +932,7 @@ private enum StatsEntry: ItemListNodeEntry {
let .adsProceedsInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
case let .overview(_, stats):
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: false, stats: stats, sectionId: self.section, style: .blocks)
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: false, stats: stats, sectionId: self.section, style: .blocks)
case let .growthGraph(_, _, _, graph, type),
let .followersGraph(_, _, _, graph, type),
let .notificationsGraph(_, _, _, graph, type),
@ -943,15 +943,15 @@ private enum StatsEntry: ItemListNodeEntry {
let .reactionsByEmotionGraph(_, _, _, graph, type),
let .storyReactionsByEmotionGraph(_, _, _, graph, type),
let .adsImpressionsGraph(_, _, _, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .adsTonRevenueGraph(_, _, _, graph, type, rate):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks)
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks)
case let .adsStarsRevenueGraph(_, _, _, graph, type, rate):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks)
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, conversionRate: rate, sectionId: self.section, style: .blocks)
case let .postInteractionsGraph(_, _, _, graph, type),
let .instantPageInteractionsGraph(_, _, _, graph, type),
let .storyInteractionsGraph(_, _, _, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, getDetailsData: { date, completion in
let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
if let graph = graph, case let .Loaded(_, data) = graph {
completion(data)
@ -1035,7 +1035,7 @@ private enum StatsEntry: ItemListNodeEntry {
arguments.openBoost(boost)
})
case let .boostersExpand(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandBoosters()
})
case let .boostLevel(_, count, level, position):
@ -1046,15 +1046,15 @@ private enum StatsEntry: ItemListNodeEntry {
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: isGroup, stats: stats, sectionId: self.section, style: .blocks)
case let .boostLink(_, link):
let invite: ExportedInvitation = .link(link: link, title: nil, isPermanent: false, requestApproval: false, isRevoked: false, adminId: PeerId(0), date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, invite: invite, count: 0, peers: [], displayButton: true, displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
arguments.copyBoostLink(link)
}, shareAction: {
arguments.shareBoostLink(link)
}, contextAction: nil, viewAction: nil, openCallAction: nil, tag: nil)
case let .boostersPlaceholder(_, text):
return ItemListPlaceholderItem(theme: presentationData.theme, text: text, sectionId: self.section, style: .blocks)
return ItemListPlaceholderItem(theme: presentationData.theme, systemStyle: .glass, text: text, sectionId: self.section, style: .blocks)
case let .boostGifts(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addBoostsIcon(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.addBoostsIcon(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.openGifts()
})
case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway):
@ -1200,7 +1200,7 @@ private enum StatsEntry: ItemListNodeEntry {
label.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: label.string))
}
return ItemListDisclosureItem(presentationData: presentationData, title: "", attributedTitle: title, label: "", attributedLabel: label, labelStyle: .coloredText(labelColor), additionalDetailLabel: detailText, additionalDetailLabelColor: detailColor, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: "", attributedTitle: title, label: "", attributedLabel: label, labelStyle: .coloredText(labelColor), additionalDetailLabel: detailText, additionalDetailLabelColor: detailColor, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
arguments.openTonTransaction(transaction)
})
case let .adsStarsTransaction(_, _, transaction):
@ -1208,7 +1208,7 @@ private enum StatsEntry: ItemListNodeEntry {
arguments.openStarsTransaction(transaction)
}, sectionId: self.section, style: .blocks)
case let .adsTransactionsExpand(theme, title, stars):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandTransactions(stars)
})
case let .adsCpmToggle(_, title, minLevel, value):
@ -1219,7 +1219,7 @@ private enum StatsEntry: ItemListNodeEntry {
level: Int(minLevel)
))
}
return ItemListSwitchItem(presentationData: presentationData, title: title, titleBadgeComponent: badgeComponent, value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, titleBadgeComponent: badgeComponent, value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in
if value != nil {
arguments.updateCpmEnabled(updatedValue)
} else {
@ -1229,7 +1229,7 @@ private enum StatsEntry: ItemListNodeEntry {
arguments.presentCpmLocked()
})
case .earnStarsInfo:
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: {
arguments.openEarnStars()
})
}

View file

@ -381,7 +381,7 @@ private enum StatsEntry: ItemListNodeEntry {
let .topInvitersTitle(_, text, dates):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section)
case let .overview(_, stats):
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: true, stats: stats, sectionId: self.section, style: .blocks)
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: true, stats: stats, sectionId: self.section, style: .blocks)
case let .growthGraph(_, _, _, graph, type),
let .membersGraph(_, _, _, graph, type),
let .newMembersBySourceGraph(_, _, _, graph, type),
@ -390,7 +390,7 @@ private enum StatsEntry: ItemListNodeEntry {
let .actionsGraph(_, _, _, graph, type),
let .topHoursGraph(_, _, _, graph, type),
let .topWeekdaysGraph(_, _, _, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed, canPromote):
var textComponents: [String] = []
if topPoster.messageCount > 0 {
@ -410,13 +410,13 @@ private enum StatsEntry: ItemListNodeEntry {
}))
}
}
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
arguments.openPeer(peer)
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
case let .topPostersExpand(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandTopPosters()
})
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed, canPromote):
@ -441,13 +441,13 @@ private enum StatsEntry: ItemListNodeEntry {
}))
}
}
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
arguments.openPeer(peer)
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
case let .topAdminsExpand(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandTopAdmins()
})
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed, canPromote):
@ -464,13 +464,13 @@ private enum StatsEntry: ItemListNodeEntry {
}))
}
}
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
arguments.openPeer(peer)
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
case let .topInvitersExpand(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandTopInviters()
})
}

View file

@ -160,9 +160,9 @@ private enum StatsEntry: ItemListNodeEntry {
let .publicForwardsTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .overview(_, stats, storyViews, publicShares):
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: false, stats: stats as? Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks)
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, isGroup: false, stats: stats as? Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks)
case let .interactionsGraph(_, _, _, graph, type, noInitialZoom), let .reactionsGraph(_, _, _, graph, type, noInitialZoom):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in
return StatsGraphItem(presentationData: presentationData, systemStyle: .glass, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in
let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
if let graph = graph, case let .Loaded(_, data) = graph {
completion(data)
@ -197,7 +197,7 @@ private enum StatsEntry: ItemListNodeEntry {
reactions = Int32(story.views?.reactedCount ?? 0)
isStory = true
}
return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: item, views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: {
return StatsMessageItem(context: arguments.context, presentationData: presentationData, systemStyle: .glass, peer: peer, item: item, views: views, reactions: reactions, forwards: forwards, isPeer: true, sectionId: self.section, style: .blocks, action: {
switch item {
case let .message(message):
arguments.openMessage(message.id)

View file

@ -14,6 +14,7 @@ import ListItemComponentAdaptor
public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
let presentationData: ItemListPresentationData
let systemStyle: ItemListSystemStyle
let graph: StatsGraph
let type: ChartType
let noInitialZoom: Bool
@ -22,8 +23,9 @@ public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponent
public let sectionId: ItemListSectionId
let style: ItemListStyle
public init(presentationData: ItemListPresentationData, graph: StatsGraph, type: ChartType, noInitialZoom: Bool = false, conversionRate: Double = 1.0, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
public init(presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .legacy, graph: StatsGraph, type: ChartType, noInitialZoom: Bool = false, conversionRate: Double = 1.0, getDetailsData: ((Date, @escaping (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
self.presentationData = presentationData
self.systemStyle = systemStyle
self.graph = graph
self.type = type
self.noInitialZoom = noInitialZoom
@ -215,6 +217,7 @@ public final class StatsGraphItemNode: ListViewItemNode {
contentSize.height += visibilityHeight
}
contentSize.height += 7.0
contentSize.height += 8.0
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
@ -280,9 +283,9 @@ public final class StatsGraphItemNode: ListViewItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.chartContainerNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: contentSize.height))
strongSelf.chartContainerNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: contentSize.height - 8.0))
strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: 0.0, y: item.type == .hourlyStep ? -40.0 : 0.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 750.0))
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)

View file

@ -18,6 +18,7 @@ import AvatarNode
public class StatsMessageItem: ListViewItem, ItemListItem {
let context: AccountContext
let presentationData: ItemListPresentationData
let systemStyle: ItemListSystemStyle
let peer: Peer
let item: StatsPostItem
let views: Int32
@ -30,9 +31,10 @@ public class StatsMessageItem: ListViewItem, ItemListItem {
let openStory: (UIView) -> Void
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
init(context: AccountContext, presentationData: ItemListPresentationData, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
self.context = context
self.presentationData = presentationData
self.systemStyle = systemStyle
self.peer = peer
self.item = item
self.views = views
@ -576,7 +578,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)

View file

@ -40,9 +40,10 @@ extension StarsRevenueStats: Stats {
}
class StatsOverviewItem: ListViewItem, ItemListItem {
final class StatsOverviewItem: ListViewItem, ItemListItem {
let context: AccountContext
let presentationData: ItemListPresentationData
let systemStyle: ItemListSystemStyle
let isGroup: Bool
let stats: Stats?
let additionalStats: Stats?
@ -51,9 +52,10 @@ class StatsOverviewItem: ListViewItem, ItemListItem {
let sectionId: ItemListSectionId
let style: ItemListStyle
init(context: AccountContext, presentationData: ItemListPresentationData, isGroup: Bool, stats: Stats?, additionalStats: Stats? = nil, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, isGroup: Bool, stats: Stats?, additionalStats: Stats? = nil, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
self.context = context
self.presentationData = presentationData
self.systemStyle = systemStyle
self.isGroup = isGroup
self.stats = stats
self.additionalStats = additionalStats
@ -968,7 +970,7 @@ class StatsOverviewItemNode: ListViewItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)