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

@ -3,22 +3,23 @@ import UIKit
import AsyncDisplayKit
import Display
import TelegramPresentationData
import SolidRoundedButtonNode
import ComponentFlow
import ButtonComponent
import EdgeEffect
final class InviteContactsCountPanelNode: ASDisplayNode {
private let theme: PresentationTheme
private let strings: PresentationStrings
private let action: () -> Void
private let separatorNode: ASDisplayNode
private let button: SolidRoundedButtonNode
private let edgeEffectView = EdgeEffectView()
private let button = ComponentView<Empty>()
private var validLayout: (CGFloat, CGFloat, CGFloat)?
var count: Int = 0 {
didSet {
if self.count != oldValue && self.count > 0 {
self.button.title = self.strings.Contacts_InviteContacts(Int32(self.count))
if let (width, sideInset, bottomInset) = self.validLayout {
let _ = self.updateLayout(width: width, sideInset: sideInset, bottomInset: bottomInset, transition: .immediate)
}
@ -29,37 +30,75 @@ final class InviteContactsCountPanelNode: ASDisplayNode {
init(theme: PresentationTheme, strings: PresentationStrings, action: @escaping () -> Void) {
self.theme = theme
self.strings = strings
self.action = action
self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor
self.button = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), height: 48.0, cornerRadius: 10.0)
super.init()
self.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor
self.addSubnode(self.button)
self.addSubnode(self.separatorNode)
self.button.pressed = {
action()
}
}
func updateLayout(width: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
self.validLayout = (width, sideInset, bottomInset)
let topInset: CGFloat = 9.0
var bottomInset = bottomInset
bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0)
let buttonInset: CGFloat = 16.0 + sideInset
let buttonWidth = width - buttonInset * 2.0
let buttonHeight = self.button.updateLayout(width: buttonWidth, transition: transition)
transition.updateFrame(node: self.button, frame: CGRect(x: buttonInset, y: topInset, width: buttonWidth, height: buttonHeight))
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0)
let height: CGFloat = 52.0 + buttonInsets.bottom
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
let edgeEffectHeight: CGFloat = height
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: edgeEffectHeight))
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(
content: self.theme.list.plainBackgroundColor,
blur: true,
rect: edgeEffectFrame,
edge: .bottom,
edgeSize: edgeEffectFrame.height,
transition: ComponentTransition(transition)
)
if self.edgeEffectView.superview == nil {
self.view.addSubview(self.edgeEffectView)
}
return topInset + buttonHeight + bottomInset
let buttonTransition: ComponentTransition = .easeInOut(duration: 0.2)
let buttonSize = self.button.update(
transition: buttonTransition,
component: AnyComponent(
ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: self.theme.list.itemCheckColors.fillColor,
foreground: self.theme.list.itemCheckColors.foregroundColor,
pressedColor: self.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(ButtonTextContentComponent(
text: self.strings.Contacts_InviteContacts(Int32(self.count)),
badge: 0,
textColor: self.theme.list.itemCheckColors.foregroundColor,
badgeBackground: self.theme.list.itemCheckColors.foregroundColor,
badgeForeground: self.theme.list.itemCheckColors.fillColor,
badgeStyle: .roundedRectangle,
badgeIconName: nil,
combinedAlignment: true
))
),
isEnabled: true,
action: { [weak self] in
guard let self else {
return
}
self.action()
}
)
),
environment: {},
containerSize: CGSize(width: width - sideInset * 2.0 - buttonInsets.left - buttonInsets.right, height: 52.0)
)
let buttonFrame = CGRect(origin: CGPoint(x: sideInset + buttonInsets.left, y: 0.0), size: buttonSize)
if let buttonView = self.button.view {
if buttonView.superview == nil {
self.view.addSubview(buttonView)
}
transition.updateFrame(view: buttonView, frame: buttonFrame)
}
return height
}
}

View file

@ -460,12 +460,12 @@ public final class DatePickerNode: ASDisplayNode {
self.dateButtonNode = HighlightableButtonNode()
self.dateButtonNode.clipsToBounds = true
self.dateButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
self.dateButtonNode.cornerRadius = 9.0
self.dateButtonNode.cornerRadius = 18.0
self.timeButtonNode = HighlightableButtonNode()
self.timeButtonNode.clipsToBounds = true
self.timeButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
self.timeButtonNode.cornerRadius = 9.0
self.timeButtonNode.cornerRadius = 18.0
super.init()
@ -828,7 +828,7 @@ public final class DatePickerNode: ASDisplayNode {
var timeSize = self.timeButtonNode.measure(size)
timeSize.width += 24.0
timeSize.height = 36.0
self.timeButtonNode.frame = CGRect(x: size.width - timeSize.width - 4.0, y: 4.0, width: timeSize.width, height: timeSize.height)
self.timeButtonNode.frame = CGRect(x: size.width - timeSize.width - 10.0, y: 4.0, width: timeSize.width, height: timeSize.height)
let dateString = stringForMediumDate(timestamp: Int32(date.timeIntervalSince1970), strings: self.strings, dateTimeFormat: self.dateTimeFormat, withTime: false)
self.dateButtonNode.setTitle(dateString, with: Font.with(size: 17.0, traits: .monospacedNumbers), with: self.state.displayingDateSelection ? self.theme.accentColor : self.theme.textColor, for: .normal)
@ -836,7 +836,7 @@ public final class DatePickerNode: ASDisplayNode {
var dateSize = self.dateButtonNode.measure(size)
dateSize.width += 24.0
dateSize.height = 36.0
self.dateButtonNode.frame = CGRect(x: size.width - timeSize.width - 4.0 - 4.0 - dateSize.width, y: 4.0, width: dateSize.width, height: dateSize.height)
self.dateButtonNode.frame = CGRect(x: size.width - timeSize.width - 10.0 - 4.0 - dateSize.width, y: 4.0, width: dateSize.width, height: dateSize.height)
let daysSideInset: CGFloat = 12.0
let cellSize: CGFloat = floor((constrainedSize.width - daysSideInset * 2.0) / 7.0)

View file

@ -388,7 +388,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
case let .timeCustomPicker(_, dateTimeFormat, date, displayingDateSelection, displayingTimeSelection, enabled):
let _ = enabled
let title = presentationData.strings.InviteLink_Create_TimeLimitExpiryTime
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: {
return ItemListDatePickerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, date: date, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: {
arguments.updateState({ state in
var updatedState = state
updatedState.pickingExpiryDate = !updatedState.pickingExpiryDate

View file

@ -7,13 +7,15 @@ import TelegramPresentationData
public class ItemListPlaceholderItem: ListViewItem, ItemListItem {
let theme: PresentationTheme
let systemStyle: ItemListSystemStyle
let text: String
public let sectionId: ItemListSectionId
let style: ItemListStyle
public let tag: ItemListItemTag?
public init(theme: PresentationTheme, text: String, sectionId: ItemListSectionId, style: ItemListStyle, tag: ItemListItemTag? = nil) {
public init(theme: PresentationTheme, systemStyle: ItemListSystemStyle = .legacy, text: String, sectionId: ItemListSectionId, style: ItemListStyle, tag: ItemListItemTag? = nil) {
self.theme = theme
self.systemStyle = systemStyle
self.text = text
self.sectionId = sectionId
self.style = style
@ -198,7 +200,7 @@ public class ItemListPlaceholderItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.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

@ -433,7 +433,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .modeHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .giftPremium(_, title, subtitle, isSelected):
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: .premium, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: .premium, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
var openSelection = false
arguments.updateState { state in
var updatedState = state
@ -448,7 +448,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
}
})
case let .giftStars(_, title, subtitle, isSelected):
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: .stars, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: false, isSelected: isSelected, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: .stars, name: "Peer Info/PremiumIcon"), title: title, subtitle: subtitle, subtitleActive: false, isSelected: isSelected, sectionId: self.section, action: {
arguments.updateState { state in
var updatedState = state
updatedState.mode = .starsGiveaway
@ -480,7 +480,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
icon = "Premium/PremiumStar"
boosts = boostCount
}
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, sectionId: self.section, action: nil)
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, sectionId: self.section, action: nil)
case let .starsHeader(_, text, additionalText):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
case let .stars(_, _, stars, title, subtitle, label, isSelected, maxWinners):
@ -501,7 +501,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .subscriptionsHeader(_, text, additionalText):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
case let .subscriptions(_, value, values):
return SubscriptionsCountItem(theme: presentationData.theme, strings: presentationData.strings, value: value, values: values, sectionId: self.section, updated: { value in
return SubscriptionsCountItem(theme: presentationData.theme, strings: presentationData.strings, systemStyle: .glass, value: value, values: values, sectionId: self.section, updated: { value in
arguments.updateState { state in
var updatedState = state
if state.mode == .giveaway {
@ -536,7 +536,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .usersHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .usersAll(_, title, subtitle, isSelected):
return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
var openSelection = false
arguments.updateState { state in
var updatedState = state
@ -551,7 +551,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
}
})
case let .usersNew(_, title, subtitle, isSelected):
return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleActive: true, isSelected: isSelected, sectionId: self.section, action: {
var openSelection = false
arguments.updateState { state in
var updatedState = state
@ -570,7 +570,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .durationHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .duration(_, _, months, title, subtitle, label, badge, isSelected):
return GiftOptionItem(presentationData: presentationData, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, systemStyle: .glass, context: arguments.context, title: title, subtitle: subtitle, subtitleFont: .small, label: .generic(label), badge: badge, isSelected: isSelected, sectionId: self.section, action: {
arguments.updateState { state in
var updatedState = state
updatedState.selectedMonths = months
@ -635,7 +635,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
})
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate, displayingDateSelection, displayingTimeSelection):
let title = presentationData.strings.BoostGift_DateEnds
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: {
return ItemListDatePickerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, title: title, displayingDateSelection: displayingDateSelection, displayingTimeSelection: displayingTimeSelection, sectionId: self.section, style: .blocks, toggleDateSelection: {
var focus = false
arguments.updateState({ state in
var updatedState = state

View file

@ -7,6 +7,7 @@ import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import ButtonComponent
import EdgeEffect
final class CreateGiveawayFooterItem: ItemListControllerFooterItem {
let theme: PresentationTheme
@ -42,8 +43,7 @@ final class CreateGiveawayFooterItem: ItemListControllerFooterItem {
}
final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
private let backgroundNode: NavigationBackgroundNode
private let separatorNode: ASDisplayNode
private let edgeEffectView = EdgeEffectView()
private let button = ComponentView<Empty>()
private var validLayout: ContainerViewLayout?
@ -51,7 +51,6 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
private var currentIsLoading = false
var item: CreateGiveawayFooterItem {
didSet {
self.updateItem()
if let layout = self.validLayout {
let _ = self.updateLayout(layout: layout, transition: .immediate)
}
@ -60,49 +59,35 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
init(item: CreateGiveawayFooterItem) {
self.item = item
self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.tabBar.backgroundColor)
self.separatorNode = ASDisplayNode()
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.separatorNode)
self.updateItem()
}
private func updateItem() {
self.backgroundNode.updateColor(color: self.item.theme.rootController.tabBar.backgroundColor, transition: .immediate)
self.separatorNode.backgroundColor = self.item.theme.rootController.tabBar.separatorColor
}
override func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
transition.updateAlpha(node: self.backgroundNode, alpha: alpha)
transition.updateAlpha(node: self.separatorNode, alpha: alpha)
}
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
let hadLayout = self.validLayout != nil
self.validLayout = layout
let buttonInset: CGFloat = 16.0
let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0
let inset: CGFloat = 9.0
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.intrinsicInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
let height: CGFloat = 52.0 + buttonInsets.bottom
let insets = layout.insets(options: [.input])
var panelHeight: CGFloat = 50.0 + inset * 2.0
let totalPanelHeight: CGFloat
if let inputHeight = layout.inputHeight, inputHeight > 0.0 {
totalPanelHeight = panelHeight + insets.bottom
} else {
totalPanelHeight = panelHeight + insets.bottom
panelHeight += insets.bottom
let edgeEffectHeight: CGFloat = height
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(
content: self.item.theme.list.plainBackgroundColor,
blur: true,
rect: edgeEffectFrame,
edge: .bottom,
edgeSize: edgeEffectFrame.height,
transition: ComponentTransition(transition)
)
if self.edgeEffectView.superview == nil {
self.view.addSubview(self.edgeEffectView)
}
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - totalPanelHeight), size: CGSize(width: layout.size.width, height: panelHeight))
var buttonTransition: ComponentTransition = .easeInOut(duration: 0.2)
if !hadLayout {
buttonTransition = .immediate
@ -112,6 +97,7 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
component: AnyComponent(
ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: self.item.theme.list.itemCheckColors.fillColor,
foreground: self.item.theme.list.itemCheckColors.foregroundColor,
pressedColor: self.item.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
@ -140,26 +126,21 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
)
),
environment: {},
containerSize: CGSize(width: buttonWidth, height: 50.0)
containerSize: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInsets.left - buttonInsets.right, height: 52.0)
)
if let view = self.button.view {
if view.superview == nil {
self.view.addSubview(view)
let buttonFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInsets.left, y: layout.size.height - buttonInsets.bottom - buttonSize.height), size: buttonSize)
if let buttonView = self.button.view {
if buttonView.superview == nil {
self.view.addSubview(buttonView)
}
transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: panelFrame.minY + inset), size: buttonSize))
transition.updateFrame(view: buttonView, frame: buttonFrame)
}
transition.updateFrame(node: self.backgroundNode, frame: panelFrame)
self.backgroundNode.update(size: panelFrame.size, transition: transition)
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel)))
return totalPanelHeight
return height
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if self.backgroundNode.frame.contains(point) {
if self.edgeEffectView.frame.contains(point) {
return true
} else {
return false

View file

@ -9,6 +9,8 @@ import PresentationDataUtils
import Markdown
import ComponentFlow
import PremiumStarComponent
import GlassBarButtonComponent
import BundleIconComponent
final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
let theme: PresentationTheme
@ -49,13 +51,10 @@ private let titleFont = Font.semibold(20.0)
private let textFont = Font.regular(15.0)
class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
private let backgroundNode: NavigationBackgroundNode
private let separatorNode: ASDisplayNode
private let titleNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private let cancelNode: HighlightableButtonNode
private let cancelButton = ComponentView<Empty>()
private var hostView: ComponentHostView<Empty>?
private var component: AnyComponent<Empty>?
@ -72,11 +71,7 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
init(item: CreateGiveawayHeaderItem) {
self.item = item
self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.navigationBar.blurredBackgroundColor)
self.separatorNode = ASDisplayNode()
self.titleNode = ImmediateTextNode()
self.titleNode.isUserInteractionEnabled = false
self.titleNode.contentMode = .left
@ -87,26 +82,15 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
self.textNode.contentMode = .left
self.textNode.contentsScale = UIScreen.main.scale
self.textNode.maximumNumberOfLines = 0
self.cancelNode = HighlightableButtonNode()
super.init()
self.addSubnode(self.textNode)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.separatorNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.cancelNode)
self.cancelNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
self.updateItem()
}
@objc private func cancelPressed() {
self.item.cancel()
}
override func didLoad() {
super.didLoad()
@ -128,23 +112,18 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
}
func updateItem() {
self.backgroundNode.updateColor(color: self.item.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.separatorNode.backgroundColor = self.item.theme.rootController.navigationBar.separatorColor
let attributedTitle = NSAttributedString(string: self.item.title, font: titleFont, textColor: self.item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center)
let attributedText = NSAttributedString(string: self.item.text, font: textFont, textColor: self.item.theme.list.freeTextColor, paragraphAlignment: .center)
self.titleNode.attributedText = attributedTitle
self.textNode.attributedText = attributedText
self.cancelNode.setAttributedTitle(NSAttributedString(string: self.item.strings.Common_Cancel, font: Font.regular(17.0), textColor: self.item.theme.rootController.navigationBar.accentTextColor), for: .normal)
}
override func updateContentOffset(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) {
guard let layout = self.validLayout else {
return
}
let navigationHeight: CGFloat = 56.0
let navigationHeight: CGFloat = 76.0
let statusBarHeight = layout.statusBarHeight ?? 0.0
let topInset : CGFloat = 0.0
@ -155,10 +134,6 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta))
let titleScale = 1.0 - fraction * 0.18
let topPanelAlpha = min(20.0, max(0.0, contentOffset - 95.0)) / 20.0
transition.updateAlpha(node: self.backgroundNode, alpha: topPanelAlpha)
transition.updateAlpha(node: self.separatorNode, alpha: topPanelAlpha)
let starPosition = CGPoint(
x: layout.size.width / 2.0,
y: -contentOffset + 80.0
@ -185,18 +160,34 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
let leftInset: CGFloat = 24.0
let navigationBarHeight: CGFloat = 56.0
let constrainedSize = CGSize(width: layout.size.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude)
let titleSize = self.titleNode.updateLayout(constrainedSize)
let textSize = self.textNode.updateLayout(constrainedSize)
let cancelSize = self.cancelNode.measure(constrainedSize)
transition.updateFrame(node: self.cancelNode, frame: CGRect(origin: CGPoint(x: 16.0 + layout.safeInsets.left, y: floorToScreenPixels((navigationBarHeight - cancelSize.height) / 2.0)), size: cancelSize))
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight)))
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition)
let cancelButtonSize = self.cancelButton.update(
transition: .immediate,
component: AnyComponent(
GlassBarButtonComponent(
size: CGSize(width: 44.0, height: 44.0),
backgroundColor: nil,
isDark: self.item.theme.overallDarkAppearance,
state: .glass,
component: AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Navigation/Close", tintColor: self.item.theme.chat.inputPanel.panelControlColor))),
action: { [weak self] _ in
self?.item.cancel()
}
)
),
environment: {},
containerSize: CGSize(width: 44.0, height: 44.0)
)
let cancelButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: cancelButtonSize)
if let cancelButtonView = self.cancelButton.view {
if cancelButtonView.superview == nil {
self.view.addSubview(cancelButtonView)
}
cancelButtonView.frame = cancelButtonFrame
}
let colors: [UIColor]
let particleColor: UIColor?

View file

@ -53,6 +53,7 @@ public final class GiftOptionItem: ListViewItem, ItemListItem {
}
let presentationData: ItemListPresentationData
let systemStyle: ItemListSystemStyle
let context: AccountContext
let icon: Icon?
let title: String
@ -68,8 +69,9 @@ public final class GiftOptionItem: ListViewItem, ItemListItem {
public let sectionId: ItemListSectionId
let action: (() -> Void)?
public init(presentationData: ItemListPresentationData, context: AccountContext, icon: Icon? = nil, title: String, titleFont: Font = .regular, titleBadge: String? = nil, subtitle: String?, subtitleFont: SubtitleFont = .regular, subtitleActive: Bool = false, label: Label? = nil, badge: String? = nil, isSelected: Bool? = nil, stars: Int64? = nil, sectionId: ItemListSectionId, action: (() -> Void)?) {
public init(presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .legacy, context: AccountContext, icon: Icon? = nil, title: String, titleFont: Font = .regular, titleBadge: String? = nil, subtitle: String?, subtitleFont: SubtitleFont = .regular, subtitleActive: Bool = false, label: Label? = nil, badge: String? = nil, isSelected: Bool? = nil, stars: Int64? = nil, sectionId: ItemListSectionId, action: (() -> Void)?) {
self.presentationData = presentationData
self.systemStyle = systemStyle
self.icon = icon
self.context = context
self.title = title
@ -518,7 +520,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode {
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.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)

View file

@ -1,280 +0,0 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import AccountContext
import TelegramPresentationData
import UndoUI
import PresentationDataUtils
private struct BoostState {
let level: Int32
let currentLevelBoosts: Int32
let nextLevelBoosts: Int32?
let boosts: Int32
func displayData(peer: EnginePeer, isCurrent: Bool, canBoostAgain: Bool, myBoostCount: Int32, currentMyBoostCount: Int32, replacedBoosts: Int32? = nil) -> (subject: PremiumLimitScreen.Subject, count: Int32) {
var currentLevel = self.level
var nextLevelBoosts = self.nextLevelBoosts
var currentLevelBoosts = self.currentLevelBoosts
var boosts = self.boosts
if let replacedBoosts {
boosts = max(currentLevelBoosts, boosts - replacedBoosts)
}
if currentMyBoostCount > 0 && self.boosts == currentLevelBoosts {
currentLevel = max(0, currentLevel - 1)
nextLevelBoosts = currentLevelBoosts
currentLevelBoosts = max(0, currentLevelBoosts - 1)
}
return (
.storiesChannelBoost(
peer: peer,
boostSubject: .stories,
isCurrent: isCurrent,
level: currentLevel,
currentLevelBoosts: currentLevelBoosts,
nextLevelBoosts: nextLevelBoosts,
link: nil,
myBoostCount: myBoostCount,
canBoostAgain: canBoostAgain
),
boosts
)
}
}
public func PremiumBoostScreen(
context: AccountContext,
contentContext: Any?,
peerId: EnginePeer.Id,
isCurrent: Bool,
status: ChannelBoostStatus?,
myBoostStatus: MyBoostStatus?,
replacedBoosts: (Int32, Int32)? = nil,
forceDark: Bool,
openPeer: @escaping (EnginePeer) -> Void,
presentController: @escaping (ViewController) -> Void,
pushController: @escaping (ViewController) -> Void,
dismissed: @escaping () -> Void
) {
let _ = (context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)
)
|> deliverOnMainQueue).startStandalone(next: { peer, accountPeer in
guard let peer, let accountPeer, let status else {
return
}
let isPremium = accountPeer.isPremium
var myBoostCount: Int32 = 0
var currentMyBoostCount: Int32 = 0
var availableBoosts: [MyBoostStatus.Boost] = []
var occupiedBoosts: [MyBoostStatus.Boost] = []
if let myBoostStatus {
for boost in myBoostStatus.boosts {
if let boostPeer = boost.peer {
if boostPeer.id == peer.id {
myBoostCount += 1
} else {
occupiedBoosts.append(boost)
}
} else {
availableBoosts.append(boost)
}
}
}
let boosts = max(Int32(status.boosts), myBoostCount)
let initialState = BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts)
let updatedState = Promise<BoostState?>()
updatedState.set(.single(BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: boosts + 1)))
var updateImpl: (() -> Void)?
var dismissImpl: (() -> Void)?
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with({ $0 }))
let canBoostAgain = premiumConfiguration.boostsPerGiftCount > 0
let (initialSubject, initialCount) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 0, replacedBoosts: replacedBoosts?.0)
let controller = PremiumLimitScreen(context: context, subject: initialSubject, count: initialCount, forceDark: forceDark, action: {
let dismiss = false
updateImpl?()
return dismiss
},
openPeer: { peer in
openPeer(peer)
})
pushController(controller)
if let (replacedBoosts, inChannels) = replacedBoosts {
currentMyBoostCount += 1
let (subject, count) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 1, replacedBoosts: nil)
controller.updateSubject(subject, count: count)
Queue.mainQueue().after(0.3) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), presentationData.strings.ReassignBoost_OtherChannels(inChannels)).string, round: false, undoText: nil), elevatedLayout: false, position: .bottom, action: { _ in return true })
controller.present(undoController, in: .current)
}
}
controller.disposed = {
dismissed()
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
updateImpl = { [weak controller] in
if let _ = status.nextLevelBoosts {
if let availableBoost = availableBoosts.first {
currentMyBoostCount += 1
myBoostCount += 1
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|> deliverOnMainQueue).startStandalone(completed: {
updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId)
|> map { status in
if let status {
return BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))
} else {
return nil
}
})
})
let _ = (updatedState.get()
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { state in
guard let state else {
return
}
let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount)
controller?.updateSubject(subject, count: count)
})
availableBoosts.removeFirst()
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
if canBoostAgain {
var dismissReplaceImpl: (() -> Void)?
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
var channelIds = Set<EnginePeer.Id>()
for boost in myBoostStatus.boosts {
if slots.contains(boost.slot) {
if let peer = boost.peer {
channelIds.insert(peer.id)
}
}
}
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone(completed: {
let _ = combineLatest(
queue: Queue.mainQueue(),
context.engine.peers.getChannelBoostStatus(peerId: peerId),
context.engine.peers.getMyBoostStatus()
).startStandalone(next: { boostStatus, myBoostStatus in
dismissReplaceImpl?()
PremiumBoostScreen(context: context, contentContext: contentContext, peerId: peerId, isCurrent: isCurrent, status: boostStatus, myBoostStatus: myBoostStatus, replacedBoosts: (Int32(slots.count), Int32(channelIds.count)), forceDark: forceDark, openPeer: openPeer, presentController: presentController, pushController: pushController, dismissed: dismissed)
})
})
})
dismissImpl?()
pushController(replaceController)
dismissReplaceImpl = { [weak replaceController] in
replaceController?.dismiss(animated: true)
}
} else if let boost = occupiedBoosts.first, let occupiedPeer = boost.peer {
if let cooldown = boost.cooldownUntil {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let timeout = cooldown - currentTime
let valueText = timeIntervalString(strings: presentationData.strings, value: timeout, usage: .afterTime, preferLowerValue: false)
let controller = textAlertController(
sharedContext: context.sharedContext,
updatedPresentationData: nil,
title: presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle,
text: presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
],
parseMarkdown: true
)
presentController(controller)
} else {
let replaceController = replaceBoostConfirmationController(context: context, fromPeers: [occupiedPeer], toPeer: peer, commit: {
currentMyBoostCount += 1
myBoostCount += 1
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [boost.slot])
|> deliverOnMainQueue).startStandalone(completed: { [weak controller] in
let _ = (updatedState.get()
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak controller] state in
guard let state else {
return
}
let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount)
controller?.updateSubject(subject, count: count)
})
})
})
presentController(replaceController)
}
} else {
dismissImpl?()
}
} else {
if isPremium {
if !canBoostAgain {
dismissImpl?()
} else {
let controller = textAlertController(
sharedContext: context.sharedContext,
updatedPresentationData: nil,
title: presentationData.strings.ChannelBoost_MoreBoosts_Title,
text: presentationData.strings.ChannelBoost_MoreBoosts_Text(peer.compactDisplayTitle, "\(premiumConfiguration.boostsPerGiftCount)").string,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.ChannelBoost_MoreBoosts_Gift, action: {
dismissImpl?()
Queue.mainQueue().after(0.4) {
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil)
pushController(controller)
}
}),
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Close, action: {})
],
actionLayout: .vertical,
parseMarkdown: true
)
presentController(controller)
}
} else {
let controller = textAlertController(
sharedContext: context.sharedContext,
updatedPresentationData: nil,
title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle,
text: presentationData.strings.ChannelBoost_Error_PremiumNeededText,
actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
dismissImpl?()
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .channelBoost(peerId), forceDark: forceDark, dismissed: nil)
pushController(controller)
})
],
parseMarkdown: true
)
presentController(controller)
}
}
} else {
dismissImpl?()
}
}
dismissImpl = { [weak controller] in
controller?.dismissAnimated()
}
})
}

View file

@ -12,14 +12,16 @@ import PresentationDataUtils
final class SubscriptionsCountItem: ListViewItem, ItemListItem {
let theme: PresentationTheme
let strings: PresentationStrings
let systemStyle: ItemListSystemStyle
let value: Int32
let values: [Int32]
let sectionId: ItemListSectionId
let updated: (Int32) -> Void
init(theme: PresentationTheme, strings: PresentationStrings, value: Int32, values: [Int32], sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) {
init(theme: PresentationTheme, strings: PresentationStrings, systemStyle: ItemListSystemStyle = .legacy, value: Int32, values: [Int32], sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) {
self.theme = theme
self.strings = strings
self.systemStyle = systemStyle
self.value = value
self.values = values
self.sectionId = sectionId
@ -229,7 +231,7 @@ private final class SubscriptionsCountItemNode: ListViewItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.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

@ -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)

View file

@ -6416,80 +6416,6 @@ private final class HeaderButtonComponent: Component {
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)
}
// static var body: Body {
// let background = Child(RoundedRectangle.self)
// let title = Child(MultilineTextComponent.self)
// let icon = Child(BundleIconComponent.self)
// let lockIcon = Child(BundleIconComponent.self)
//
// return { context in
// let component = context.component
//
// let background = background.update(
// component: RoundedRectangle(
// color: UIColor.white.withAlphaComponent(0.16),
// cornerRadius: 16.0
// ),
// availableSize: context.availableSize,
// transition: .immediate
// )
// context.add(background
// .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
// )
//
// let icon = icon.update(
// component: BundleIconComponent(
// name: component.iconName,
// tintColor: UIColor.white
// ),
// availableSize: context.availableSize,
// transition: .immediate
// )
// context.add(icon
// .position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0))
// )
//
// let title = title.update(
// component: MultilineTextComponent(
// text: .plain(NSAttributedString(
// string: component.title,
// font: Font.regular(11.0),
// textColor: UIColor.white,
// paragraphAlignment: .natural
// )),
// horizontalAlignment: .center,
// maximumNumberOfLines: 1
// ),
// availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
// transition: .immediate
// )
// var totalTitleWidth = title.size.width
// var titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0
// if component.isLocked {
// let titleSpacing: CGFloat = 3.0
// let lockIcon = lockIcon.update(
// component: BundleIconComponent(
// name: "Chat List/StatusLockIcon",
// tintColor: UIColor.white
// ),
// availableSize: context.availableSize,
// transition: .immediate
// )
// totalTitleWidth += lockIcon.size.width + titleSpacing
// titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0
// context.add(lockIcon
// .position(CGPoint(x: titleOriginX + lockIcon.size.width / 2.0, y: 42.0))
// )
// titleOriginX += lockIcon.size.width + titleSpacing
// }
// context.add(title
// .position(CGPoint(x: titleOriginX + title.size.width / 2.0, y: 42.0))
// )
//
// return context.availableSize
// }
// }
}
private struct GiftViewConfiguration {

View file

@ -9,6 +9,7 @@ import DatePickerNode
public class ItemListDatePickerItem: ListViewItem, ItemListItem {
let presentationData: ItemListPresentationData
let systemStyle: ItemListSystemStyle
let dateTimeFormat: PresentationDateTimeFormat
let date: Int32?
let minDate: Int32?
@ -28,6 +29,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
public init(
presentationData: ItemListPresentationData,
systemStyle: ItemListSystemStyle = .legacy,
dateTimeFormat: PresentationDateTimeFormat,
date: Int32?,
minDate: Int32? = nil,
@ -43,6 +45,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
tag: ItemListItemTag? = nil
) {
self.presentationData = presentationData
self.systemStyle = systemStyle
self.dateTimeFormat = dateTimeFormat
self.date = date
self.minDate = minDate
@ -158,14 +161,14 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
let width = params.width - params.leftInset - params.rightInset
let constrainedWidth = min(390.0, width)
let cellSize = floor((constrainedWidth - 12.0 * 2.0) / 7.0)
let pickerHeight = 122.0 + cellSize * 6.0
let pickerHeight = 132.0 + cellSize * 6.0
let height: CGFloat
if item.displayingDateSelection {
height = pickerHeight
} else if item.displayingTimeSelection {
height = 260.0
} else {
height = 44.0
height = 56.0
}
switch item.style {
case .plain:
@ -244,7 +247,7 @@ public class ItemListDatePickerItemNode: 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
transition.updateFrame(node: 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))))
transition.updateFrame(node: strongSelf.maskNode, frame: strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0))
@ -291,7 +294,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
let datePickerSize = CGSize(width: width, height: pickerHeight)
datePickerNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - datePickerSize.width) / 2.0), y: 0.0), size: datePickerSize)
datePickerNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - datePickerSize.width) / 2.0), y: 6.0), size: datePickerSize)
datePickerNode.updateLayout(size: datePickerSize, transition: .immediate)
transition.updateFrame(node: strongSelf.containerNode, frame: CGRect(origin: .zero, size: CGSize(width: params.width, height: contentSize.height)))

View file

@ -479,10 +479,8 @@ private final class SheetContent: Component {
contentHeight += itemSize.height
}
contentHeight += 31.0
contentHeight += 82.0
contentHeight += 113.0
return CGSize(width: availableSize.width, height: contentHeight)
}
}