Various improvements
This commit is contained in:
parent
d5ccfe2812
commit
e279f224a1
20 changed files with 383 additions and 559 deletions
|
|
@ -3,22 +3,23 @@ import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import SolidRoundedButtonNode
|
import ComponentFlow
|
||||||
|
import ButtonComponent
|
||||||
|
import EdgeEffect
|
||||||
|
|
||||||
final class InviteContactsCountPanelNode: ASDisplayNode {
|
final class InviteContactsCountPanelNode: ASDisplayNode {
|
||||||
private let theme: PresentationTheme
|
private let theme: PresentationTheme
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
|
private let action: () -> Void
|
||||||
|
|
||||||
private let separatorNode: ASDisplayNode
|
private let edgeEffectView = EdgeEffectView()
|
||||||
private let button: SolidRoundedButtonNode
|
private let button = ComponentView<Empty>()
|
||||||
|
|
||||||
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
||||||
|
|
||||||
var count: Int = 0 {
|
var count: Int = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
if self.count != oldValue && self.count > 0 {
|
if self.count != oldValue && self.count > 0 {
|
||||||
self.button.title = self.strings.Contacts_InviteContacts(Int32(self.count))
|
|
||||||
|
|
||||||
if let (width, sideInset, bottomInset) = self.validLayout {
|
if let (width, sideInset, bottomInset) = self.validLayout {
|
||||||
let _ = self.updateLayout(width: width, sideInset: sideInset, bottomInset: bottomInset, transition: .immediate)
|
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) {
|
init(theme: PresentationTheme, strings: PresentationStrings, action: @escaping () -> Void) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
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()
|
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 {
|
func updateLayout(width: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
self.validLayout = (width, sideInset, bottomInset)
|
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 buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0)
|
||||||
let buttonWidth = width - buttonInset * 2.0
|
let height: CGFloat = 52.0 + buttonInsets.bottom
|
||||||
let buttonHeight = self.button.updateLayout(width: buttonWidth, transition: transition)
|
|
||||||
transition.updateFrame(node: self.button, frame: CGRect(x: buttonInset, y: topInset, width: buttonWidth, height: buttonHeight))
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -460,12 +460,12 @@ public final class DatePickerNode: ASDisplayNode {
|
||||||
self.dateButtonNode = HighlightableButtonNode()
|
self.dateButtonNode = HighlightableButtonNode()
|
||||||
self.dateButtonNode.clipsToBounds = true
|
self.dateButtonNode.clipsToBounds = true
|
||||||
self.dateButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
self.dateButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
||||||
self.dateButtonNode.cornerRadius = 9.0
|
self.dateButtonNode.cornerRadius = 18.0
|
||||||
|
|
||||||
self.timeButtonNode = HighlightableButtonNode()
|
self.timeButtonNode = HighlightableButtonNode()
|
||||||
self.timeButtonNode.clipsToBounds = true
|
self.timeButtonNode.clipsToBounds = true
|
||||||
self.timeButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
self.timeButtonNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
||||||
self.timeButtonNode.cornerRadius = 9.0
|
self.timeButtonNode.cornerRadius = 18.0
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
|
@ -828,7 +828,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||||
var timeSize = self.timeButtonNode.measure(size)
|
var timeSize = self.timeButtonNode.measure(size)
|
||||||
timeSize.width += 24.0
|
timeSize.width += 24.0
|
||||||
timeSize.height = 36.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)
|
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)
|
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)
|
var dateSize = self.dateButtonNode.measure(size)
|
||||||
dateSize.width += 24.0
|
dateSize.width += 24.0
|
||||||
dateSize.height = 36.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 daysSideInset: CGFloat = 12.0
|
||||||
let cellSize: CGFloat = floor((constrainedSize.width - daysSideInset * 2.0) / 7.0)
|
let cellSize: CGFloat = floor((constrainedSize.width - daysSideInset * 2.0) / 7.0)
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||||
case let .timeCustomPicker(_, dateTimeFormat, date, displayingDateSelection, displayingTimeSelection, enabled):
|
case let .timeCustomPicker(_, dateTimeFormat, date, displayingDateSelection, displayingTimeSelection, enabled):
|
||||||
let _ = enabled
|
let _ = enabled
|
||||||
let title = presentationData.strings.InviteLink_Create_TimeLimitExpiryTime
|
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
|
arguments.updateState({ state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.pickingExpiryDate = !updatedState.pickingExpiryDate
|
updatedState.pickingExpiryDate = !updatedState.pickingExpiryDate
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,15 @@ import TelegramPresentationData
|
||||||
|
|
||||||
public class ItemListPlaceholderItem: ListViewItem, ItemListItem {
|
public class ItemListPlaceholderItem: ListViewItem, ItemListItem {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let text: String
|
let text: String
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
let style: ItemListStyle
|
||||||
public let tag: ItemListItemTag?
|
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.theme = theme
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.text = text
|
self.text = text
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.style = style
|
self.style = style
|
||||||
|
|
@ -198,7 +200,7 @@ public class ItemListPlaceholderItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
case let .modeHeader(_, text):
|
case let .modeHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .giftPremium(_, title, subtitle, isSelected):
|
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
|
var openSelection = false
|
||||||
arguments.updateState { state in
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
|
|
@ -448,7 +448,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .giftStars(_, title, subtitle, isSelected):
|
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
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.mode = .starsGiveaway
|
updatedState.mode = .starsGiveaway
|
||||||
|
|
@ -480,7 +480,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
icon = "Premium/PremiumStar"
|
icon = "Premium/PremiumStar"
|
||||||
boosts = boostCount
|
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):
|
case let .starsHeader(_, text, additionalText):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
|
||||||
case let .stars(_, _, stars, title, subtitle, label, isSelected, maxWinners):
|
case let .stars(_, _, stars, title, subtitle, label, isSelected, maxWinners):
|
||||||
|
|
@ -501,7 +501,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
case let .subscriptionsHeader(_, text, additionalText):
|
case let .subscriptionsHeader(_, text, additionalText):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
|
||||||
case let .subscriptions(_, value, values):
|
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
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
if state.mode == .giveaway {
|
if state.mode == .giveaway {
|
||||||
|
|
@ -536,7 +536,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
case let .usersHeader(_, text):
|
case let .usersHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .usersAll(_, title, subtitle, isSelected):
|
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
|
var openSelection = false
|
||||||
arguments.updateState { state in
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
|
|
@ -551,7 +551,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .usersNew(_, title, subtitle, isSelected):
|
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
|
var openSelection = false
|
||||||
arguments.updateState { state in
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
|
|
@ -570,7 +570,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
case let .durationHeader(_, text):
|
case let .durationHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .duration(_, _, months, title, subtitle, label, badge, isSelected):
|
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
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.selectedMonths = months
|
updatedState.selectedMonths = months
|
||||||
|
|
@ -635,7 +635,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||||
})
|
})
|
||||||
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate, displayingDateSelection, displayingTimeSelection):
|
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate, displayingDateSelection, displayingTimeSelection):
|
||||||
let title = presentationData.strings.BoostGift_DateEnds
|
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
|
var focus = false
|
||||||
arguments.updateState({ state in
|
arguments.updateState({ state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import TelegramPresentationData
|
||||||
import ItemListUI
|
import ItemListUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import ButtonComponent
|
import ButtonComponent
|
||||||
|
import EdgeEffect
|
||||||
|
|
||||||
final class CreateGiveawayFooterItem: ItemListControllerFooterItem {
|
final class CreateGiveawayFooterItem: ItemListControllerFooterItem {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
|
@ -42,8 +43,7 @@ final class CreateGiveawayFooterItem: ItemListControllerFooterItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
private let edgeEffectView = EdgeEffectView()
|
||||||
private let separatorNode: ASDisplayNode
|
|
||||||
private let button = ComponentView<Empty>()
|
private let button = ComponentView<Empty>()
|
||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
@ -51,7 +51,6 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
private var currentIsLoading = false
|
private var currentIsLoading = false
|
||||||
var item: CreateGiveawayFooterItem {
|
var item: CreateGiveawayFooterItem {
|
||||||
didSet {
|
didSet {
|
||||||
self.updateItem()
|
|
||||||
if let layout = self.validLayout {
|
if let layout = self.validLayout {
|
||||||
let _ = self.updateLayout(layout: layout, transition: .immediate)
|
let _ = self.updateLayout(layout: layout, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
@ -60,49 +59,35 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
|
|
||||||
init(item: CreateGiveawayFooterItem) {
|
init(item: CreateGiveawayFooterItem) {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.tabBar.backgroundColor)
|
|
||||||
self.separatorNode = ASDisplayNode()
|
|
||||||
|
|
||||||
super.init()
|
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) {
|
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 {
|
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
let hadLayout = self.validLayout != nil
|
let hadLayout = self.validLayout != nil
|
||||||
self.validLayout = layout
|
self.validLayout = layout
|
||||||
|
|
||||||
let buttonInset: CGFloat = 16.0
|
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.intrinsicInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||||
let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0
|
let height: CGFloat = 52.0 + buttonInsets.bottom
|
||||||
let inset: CGFloat = 9.0
|
|
||||||
|
|
||||||
let insets = layout.insets(options: [.input])
|
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))
|
||||||
var panelHeight: CGFloat = 50.0 + inset * 2.0
|
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||||
let totalPanelHeight: CGFloat
|
self.edgeEffectView.update(
|
||||||
if let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
content: self.item.theme.list.plainBackgroundColor,
|
||||||
totalPanelHeight = panelHeight + insets.bottom
|
blur: true,
|
||||||
} else {
|
rect: edgeEffectFrame,
|
||||||
totalPanelHeight = panelHeight + insets.bottom
|
edge: .bottom,
|
||||||
panelHeight += insets.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)
|
var buttonTransition: ComponentTransition = .easeInOut(duration: 0.2)
|
||||||
if !hadLayout {
|
if !hadLayout {
|
||||||
buttonTransition = .immediate
|
buttonTransition = .immediate
|
||||||
|
|
@ -112,6 +97,7 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
ButtonComponent(
|
ButtonComponent(
|
||||||
background: ButtonComponent.Background(
|
background: ButtonComponent.Background(
|
||||||
|
style: .glass,
|
||||||
color: self.item.theme.list.itemCheckColors.fillColor,
|
color: self.item.theme.list.itemCheckColors.fillColor,
|
||||||
foreground: self.item.theme.list.itemCheckColors.foregroundColor,
|
foreground: self.item.theme.list.itemCheckColors.foregroundColor,
|
||||||
pressedColor: self.item.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
pressedColor: self.item.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||||
|
|
@ -140,26 +126,21 @@ final class CreateGiveawayFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
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 {
|
let buttonFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInsets.left, y: layout.size.height - buttonInsets.bottom - buttonSize.height), size: buttonSize)
|
||||||
if view.superview == nil {
|
if let buttonView = self.button.view {
|
||||||
self.view.addSubview(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return height
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||||
if self.backgroundNode.frame.contains(point) {
|
if self.edgeEffectView.frame.contains(point) {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import PresentationDataUtils
|
||||||
import Markdown
|
import Markdown
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import PremiumStarComponent
|
import PremiumStarComponent
|
||||||
|
import GlassBarButtonComponent
|
||||||
|
import BundleIconComponent
|
||||||
|
|
||||||
final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
|
final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
|
@ -49,13 +51,10 @@ private let titleFont = Font.semibold(20.0)
|
||||||
private let textFont = Font.regular(15.0)
|
private let textFont = Font.regular(15.0)
|
||||||
|
|
||||||
class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
|
||||||
private let separatorNode: ASDisplayNode
|
|
||||||
private let titleNode: ImmediateTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
private let textNode: ImmediateTextNode
|
private let textNode: ImmediateTextNode
|
||||||
|
|
||||||
private let cancelNode: HighlightableButtonNode
|
private let cancelButton = ComponentView<Empty>()
|
||||||
|
|
||||||
private var hostView: ComponentHostView<Empty>?
|
private var hostView: ComponentHostView<Empty>?
|
||||||
|
|
||||||
private var component: AnyComponent<Empty>?
|
private var component: AnyComponent<Empty>?
|
||||||
|
|
@ -72,11 +71,7 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||||
|
|
||||||
init(item: CreateGiveawayHeaderItem) {
|
init(item: CreateGiveawayHeaderItem) {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.navigationBar.blurredBackgroundColor)
|
|
||||||
|
|
||||||
self.separatorNode = ASDisplayNode()
|
|
||||||
|
|
||||||
self.titleNode = ImmediateTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
self.titleNode.contentMode = .left
|
self.titleNode.contentMode = .left
|
||||||
|
|
@ -87,26 +82,15 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||||
self.textNode.contentMode = .left
|
self.textNode.contentMode = .left
|
||||||
self.textNode.contentsScale = UIScreen.main.scale
|
self.textNode.contentsScale = UIScreen.main.scale
|
||||||
self.textNode.maximumNumberOfLines = 0
|
self.textNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
self.cancelNode = HighlightableButtonNode()
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
self.addSubnode(self.backgroundNode)
|
|
||||||
self.addSubnode(self.separatorNode)
|
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.cancelNode)
|
|
||||||
|
|
||||||
self.cancelNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
|
|
||||||
|
|
||||||
self.updateItem()
|
self.updateItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
|
||||||
self.item.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
|
|
@ -128,23 +112,18 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateItem() {
|
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 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)
|
let attributedText = NSAttributedString(string: self.item.text, font: textFont, textColor: self.item.theme.list.freeTextColor, paragraphAlignment: .center)
|
||||||
|
|
||||||
self.titleNode.attributedText = attributedTitle
|
self.titleNode.attributedText = attributedTitle
|
||||||
self.textNode.attributedText = attributedText
|
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) {
|
override func updateContentOffset(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
guard let layout = self.validLayout else {
|
guard let layout = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let navigationHeight: CGFloat = 56.0
|
let navigationHeight: CGFloat = 76.0
|
||||||
let statusBarHeight = layout.statusBarHeight ?? 0.0
|
let statusBarHeight = layout.statusBarHeight ?? 0.0
|
||||||
|
|
||||||
let topInset : CGFloat = 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 fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta))
|
||||||
let titleScale = 1.0 - fraction * 0.18
|
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(
|
let starPosition = CGPoint(
|
||||||
x: layout.size.width / 2.0,
|
x: layout.size.width / 2.0,
|
||||||
y: -contentOffset + 80.0
|
y: -contentOffset + 80.0
|
||||||
|
|
@ -185,18 +160,34 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
|
||||||
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
|
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
let leftInset: CGFloat = 24.0
|
let leftInset: CGFloat = 24.0
|
||||||
|
|
||||||
let navigationBarHeight: CGFloat = 56.0
|
|
||||||
let constrainedSize = CGSize(width: layout.size.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude)
|
let constrainedSize = CGSize(width: layout.size.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude)
|
||||||
let titleSize = self.titleNode.updateLayout(constrainedSize)
|
let titleSize = self.titleNode.updateLayout(constrainedSize)
|
||||||
let textSize = self.textNode.updateLayout(constrainedSize)
|
let textSize = self.textNode.updateLayout(constrainedSize)
|
||||||
|
|
||||||
let cancelSize = self.cancelNode.measure(constrainedSize)
|
let cancelButtonSize = self.cancelButton.update(
|
||||||
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: .immediate,
|
||||||
|
component: AnyComponent(
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight)))
|
GlassBarButtonComponent(
|
||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
size: CGSize(width: 44.0, height: 44.0),
|
||||||
|
backgroundColor: nil,
|
||||||
self.backgroundNode.update(size: CGSize(width: layout.size.width, height: navigationBarHeight), transition: transition)
|
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 colors: [UIColor]
|
||||||
let particleColor: UIColor?
|
let particleColor: UIColor?
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ public final class GiftOptionItem: ListViewItem, ItemListItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let icon: Icon?
|
let icon: Icon?
|
||||||
let title: String
|
let title: String
|
||||||
|
|
@ -68,8 +69,9 @@ public final class GiftOptionItem: ListViewItem, ItemListItem {
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let action: (() -> Void)?
|
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.presentationData = presentationData
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.context = context
|
self.context = context
|
||||||
self.title = title
|
self.title = title
|
||||||
|
|
@ -518,7 +520,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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)
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -12,14 +12,16 @@ import PresentationDataUtils
|
||||||
final class SubscriptionsCountItem: ListViewItem, ItemListItem {
|
final class SubscriptionsCountItem: ListViewItem, ItemListItem {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let value: Int32
|
let value: Int32
|
||||||
let values: [Int32]
|
let values: [Int32]
|
||||||
let sectionId: ItemListSectionId
|
let sectionId: ItemListSectionId
|
||||||
let updated: (Int32) -> Void
|
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.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.value = value
|
self.value = value
|
||||||
self.values = values
|
self.values = values
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
|
|
@ -229,7 +231,7 @@ private final class SubscriptionsCountItemNode: ListViewItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import MultilineTextComponent
|
||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
import PlainButtonComponent
|
import PlainButtonComponent
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import GlassBackgroundComponent
|
||||||
|
|
||||||
final class BoostHeaderItem: ItemListControllerHeaderItem {
|
final class BoostHeaderItem: ItemListControllerHeaderItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
|
|
@ -291,9 +292,9 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||||
let progress = Child(PremiumLimitDisplayComponent.self)
|
let progress = Child(PremiumLimitDisplayComponent.self)
|
||||||
let text = Child(MultilineTextComponent.self)
|
let text = Child(MultilineTextComponent.self)
|
||||||
|
|
||||||
let boostButton = Child(PlainButtonComponent.self)
|
let boostButton = Child(HeaderButtonComponent.self)
|
||||||
let giveawayButton = Child(PlainButtonComponent.self)
|
let giveawayButton = Child(HeaderButtonComponent.self)
|
||||||
let featuresButton = Child(PlainButtonComponent.self)
|
let featuresButton = Child(HeaderButtonComponent.self)
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let size = context.availableSize
|
let size = context.availableSize
|
||||||
|
|
@ -393,18 +394,14 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||||
)
|
)
|
||||||
|
|
||||||
let minButtonWidth: CGFloat = 112.0
|
let minButtonWidth: CGFloat = 112.0
|
||||||
// let buttonSpacing = (size.width - sideInset * 2.0 - minButtonWidth * 3.0) / 2.0
|
|
||||||
let buttonHeight: CGFloat = 58.0
|
let buttonHeight: CGFloat = 58.0
|
||||||
|
let buttonColor = UIColor(rgb: 0x908eff)
|
||||||
|
|
||||||
let boostButton = boostButton.update(
|
let boostButton = boostButton.update(
|
||||||
component: PlainButtonComponent(
|
component: HeaderButtonComponent(
|
||||||
content: AnyComponent(
|
title: component.strings.ChannelBoost_Header_Boost,
|
||||||
BoostButtonComponent(
|
buttonColor: buttonColor,
|
||||||
iconName: "Premium/Boosts/Boost",
|
iconName: "Premium/Boosts/Boost",
|
||||||
title: component.strings.ChannelBoost_Header_Boost
|
|
||||||
)
|
|
||||||
),
|
|
||||||
effectAlignment: .center,
|
|
||||||
action: {
|
action: {
|
||||||
component.openBoost()
|
component.openBoost()
|
||||||
}
|
}
|
||||||
|
|
@ -417,14 +414,10 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||||
)
|
)
|
||||||
|
|
||||||
let giveawayButton = giveawayButton.update(
|
let giveawayButton = giveawayButton.update(
|
||||||
component: PlainButtonComponent(
|
component: HeaderButtonComponent(
|
||||||
content: AnyComponent(
|
title: component.strings.ChannelBoost_Header_Giveaway,
|
||||||
BoostButtonComponent(
|
buttonColor: buttonColor,
|
||||||
iconName: "Premium/Boosts/Giveaway",
|
iconName: "Premium/Boosts/Giveaway",
|
||||||
title: component.strings.ChannelBoost_Header_Giveaway
|
|
||||||
)
|
|
||||||
),
|
|
||||||
effectAlignment: .center,
|
|
||||||
action: {
|
action: {
|
||||||
component.createGiveaway()
|
component.createGiveaway()
|
||||||
}
|
}
|
||||||
|
|
@ -437,14 +430,10 @@ private final class BoostHeaderComponent: CombinedComponent {
|
||||||
)
|
)
|
||||||
|
|
||||||
let featuresButton = featuresButton.update(
|
let featuresButton = featuresButton.update(
|
||||||
component: PlainButtonComponent(
|
component: HeaderButtonComponent(
|
||||||
content: AnyComponent(
|
title: component.strings.ChannelBoost_Header_Features,
|
||||||
BoostButtonComponent(
|
buttonColor: buttonColor,
|
||||||
iconName: "Premium/Boosts/Features",
|
iconName: "Premium/Boosts/Features",
|
||||||
title: component.strings.ChannelBoost_Header_Features
|
|
||||||
)
|
|
||||||
),
|
|
||||||
effectAlignment: .center,
|
|
||||||
action: {
|
action: {
|
||||||
component.openFeatures()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -932,7 +932,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
let .adsProceedsInfo(_, text):
|
let .adsProceedsInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||||
case let .overview(_, stats):
|
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),
|
case let .growthGraph(_, _, _, graph, type),
|
||||||
let .followersGraph(_, _, _, graph, type),
|
let .followersGraph(_, _, _, graph, type),
|
||||||
let .notificationsGraph(_, _, _, graph, type),
|
let .notificationsGraph(_, _, _, graph, type),
|
||||||
|
|
@ -943,15 +943,15 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
let .reactionsByEmotionGraph(_, _, _, graph, type),
|
let .reactionsByEmotionGraph(_, _, _, graph, type),
|
||||||
let .storyReactionsByEmotionGraph(_, _, _, graph, type),
|
let .storyReactionsByEmotionGraph(_, _, _, graph, type),
|
||||||
let .adsImpressionsGraph(_, _, _, 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):
|
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):
|
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),
|
case let .postInteractionsGraph(_, _, _, graph, type),
|
||||||
let .instantPageInteractionsGraph(_, _, _, graph, type),
|
let .instantPageInteractionsGraph(_, _, _, graph, type),
|
||||||
let .storyInteractionsGraph(_, _, _, 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
|
let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
|
||||||
if let graph = graph, case let .Loaded(_, data) = graph {
|
if let graph = graph, case let .Loaded(_, data) = graph {
|
||||||
completion(data)
|
completion(data)
|
||||||
|
|
@ -1035,7 +1035,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
arguments.openBoost(boost)
|
arguments.openBoost(boost)
|
||||||
})
|
})
|
||||||
case let .boostersExpand(theme, title):
|
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()
|
arguments.expandBoosters()
|
||||||
})
|
})
|
||||||
case let .boostLevel(_, count, level, position):
|
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)
|
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: isGroup, stats: stats, sectionId: self.section, style: .blocks)
|
||||||
case let .boostLink(_, link):
|
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)
|
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)
|
arguments.copyBoostLink(link)
|
||||||
}, shareAction: {
|
}, shareAction: {
|
||||||
arguments.shareBoostLink(link)
|
arguments.shareBoostLink(link)
|
||||||
}, contextAction: nil, viewAction: nil, openCallAction: nil, tag: nil)
|
}, contextAction: nil, viewAction: nil, openCallAction: nil, tag: nil)
|
||||||
case let .boostersPlaceholder(_, text):
|
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):
|
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()
|
arguments.openGifts()
|
||||||
})
|
})
|
||||||
case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway):
|
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))
|
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)
|
arguments.openTonTransaction(transaction)
|
||||||
})
|
})
|
||||||
case let .adsStarsTransaction(_, _, transaction):
|
case let .adsStarsTransaction(_, _, transaction):
|
||||||
|
|
@ -1208,7 +1208,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
arguments.openStarsTransaction(transaction)
|
arguments.openStarsTransaction(transaction)
|
||||||
}, sectionId: self.section, style: .blocks)
|
}, sectionId: self.section, style: .blocks)
|
||||||
case let .adsTransactionsExpand(theme, title, stars):
|
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)
|
arguments.expandTransactions(stars)
|
||||||
})
|
})
|
||||||
case let .adsCpmToggle(_, title, minLevel, value):
|
case let .adsCpmToggle(_, title, minLevel, value):
|
||||||
|
|
@ -1219,7 +1219,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
level: Int(minLevel)
|
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 {
|
if value != nil {
|
||||||
arguments.updateCpmEnabled(updatedValue)
|
arguments.updateCpmEnabled(updatedValue)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1229,7 +1229,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
arguments.presentCpmLocked()
|
arguments.presentCpmLocked()
|
||||||
})
|
})
|
||||||
case .earnStarsInfo:
|
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()
|
arguments.openEarnStars()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
let .topInvitersTitle(_, text, dates):
|
let .topInvitersTitle(_, text, dates):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section)
|
||||||
case let .overview(_, stats):
|
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),
|
case let .growthGraph(_, _, _, graph, type),
|
||||||
let .membersGraph(_, _, _, graph, type),
|
let .membersGraph(_, _, _, graph, type),
|
||||||
let .newMembersBySourceGraph(_, _, _, graph, type),
|
let .newMembersBySourceGraph(_, _, _, graph, type),
|
||||||
|
|
@ -390,7 +390,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
let .actionsGraph(_, _, _, graph, type),
|
let .actionsGraph(_, _, _, graph, type),
|
||||||
let .topHoursGraph(_, _, _, graph, type),
|
let .topHoursGraph(_, _, _, graph, type),
|
||||||
let .topWeekdaysGraph(_, _, _, 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):
|
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed, canPromote):
|
||||||
var textComponents: [String] = []
|
var textComponents: [String] = []
|
||||||
if topPoster.messageCount > 0 {
|
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)
|
arguments.openPeer(peer)
|
||||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||||
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||||
}, removePeer: { _ in })
|
}, removePeer: { _ in })
|
||||||
case let .topPostersExpand(theme, title):
|
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()
|
arguments.expandTopPosters()
|
||||||
})
|
})
|
||||||
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed, canPromote):
|
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)
|
arguments.openPeer(peer)
|
||||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||||
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
|
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||||
}, removePeer: { _ in })
|
}, removePeer: { _ in })
|
||||||
case let .topAdminsExpand(theme, title):
|
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()
|
arguments.expandTopAdmins()
|
||||||
})
|
})
|
||||||
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed, canPromote):
|
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)
|
arguments.openPeer(peer)
|
||||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||||
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||||
}, removePeer: { _ in })
|
}, removePeer: { _ in })
|
||||||
case let .topInvitersExpand(theme, title):
|
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()
|
arguments.expandTopInviters()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,9 +160,9 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
let .publicForwardsTitle(_, text):
|
let .publicForwardsTitle(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .overview(_, stats, storyViews, publicShares):
|
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):
|
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
|
let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
|
||||||
if let graph = graph, case let .Loaded(_, data) = graph {
|
if let graph = graph, case let .Loaded(_, data) = graph {
|
||||||
completion(data)
|
completion(data)
|
||||||
|
|
@ -197,7 +197,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||||
reactions = Int32(story.views?.reactedCount ?? 0)
|
reactions = Int32(story.views?.reactedCount ?? 0)
|
||||||
isStory = true
|
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 {
|
switch item {
|
||||||
case let .message(message):
|
case let .message(message):
|
||||||
arguments.openMessage(message.id)
|
arguments.openMessage(message.id)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import ListItemComponentAdaptor
|
||||||
|
|
||||||
public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let graph: StatsGraph
|
let graph: StatsGraph
|
||||||
let type: ChartType
|
let type: ChartType
|
||||||
let noInitialZoom: Bool
|
let noInitialZoom: Bool
|
||||||
|
|
@ -22,8 +23,9 @@ public final class StatsGraphItem: ListViewItem, ItemListItem, ListItemComponent
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
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.presentationData = presentationData
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.graph = graph
|
self.graph = graph
|
||||||
self.type = type
|
self.type = type
|
||||||
self.noInitialZoom = noInitialZoom
|
self.noInitialZoom = noInitialZoom
|
||||||
|
|
@ -215,6 +217,7 @@ public final class StatsGraphItemNode: ListViewItemNode {
|
||||||
contentSize.height += visibilityHeight
|
contentSize.height += visibilityHeight
|
||||||
}
|
}
|
||||||
contentSize.height += 7.0
|
contentSize.height += 7.0
|
||||||
|
contentSize.height += 8.0
|
||||||
|
|
||||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
||||||
|
|
@ -280,9 +283,9 @@ public final class StatsGraphItemNode: ListViewItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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.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)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import AvatarNode
|
||||||
public class StatsMessageItem: ListViewItem, ItemListItem {
|
public class StatsMessageItem: ListViewItem, ItemListItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let peer: Peer
|
let peer: Peer
|
||||||
let item: StatsPostItem
|
let item: StatsPostItem
|
||||||
let views: Int32
|
let views: Int32
|
||||||
|
|
@ -30,9 +31,10 @@ public class StatsMessageItem: ListViewItem, ItemListItem {
|
||||||
let openStory: (UIView) -> Void
|
let openStory: (UIView) -> Void
|
||||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> 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.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.item = item
|
self.item = item
|
||||||
self.views = views
|
self.views = views
|
||||||
|
|
@ -576,7 +578,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,10 @@ extension StarsRevenueStats: Stats {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatsOverviewItem: ListViewItem, ItemListItem {
|
final class StatsOverviewItem: ListViewItem, ItemListItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let isGroup: Bool
|
let isGroup: Bool
|
||||||
let stats: Stats?
|
let stats: Stats?
|
||||||
let additionalStats: Stats?
|
let additionalStats: Stats?
|
||||||
|
|
@ -51,9 +52,10 @@ class StatsOverviewItem: ListViewItem, ItemListItem {
|
||||||
let sectionId: ItemListSectionId
|
let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
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.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.isGroup = isGroup
|
self.isGroup = isGroup
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
self.additionalStats = additionalStats
|
self.additionalStats = additionalStats
|
||||||
|
|
@ -968,7 +970,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|
|
||||||
|
|
@ -6416,80 +6416,6 @@ private final class HeaderButtonComponent: Component {
|
||||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
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)
|
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 {
|
private struct GiftViewConfiguration {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import DatePickerNode
|
||||||
|
|
||||||
public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
|
let systemStyle: ItemListSystemStyle
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let date: Int32?
|
let date: Int32?
|
||||||
let minDate: Int32?
|
let minDate: Int32?
|
||||||
|
|
@ -28,6 +29,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
presentationData: ItemListPresentationData,
|
presentationData: ItemListPresentationData,
|
||||||
|
systemStyle: ItemListSystemStyle = .legacy,
|
||||||
dateTimeFormat: PresentationDateTimeFormat,
|
dateTimeFormat: PresentationDateTimeFormat,
|
||||||
date: Int32?,
|
date: Int32?,
|
||||||
minDate: Int32? = nil,
|
minDate: Int32? = nil,
|
||||||
|
|
@ -43,6 +45,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||||
tag: ItemListItemTag? = nil
|
tag: ItemListItemTag? = nil
|
||||||
) {
|
) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
self.systemStyle = systemStyle
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.date = date
|
self.date = date
|
||||||
self.minDate = minDate
|
self.minDate = minDate
|
||||||
|
|
@ -158,14 +161,14 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
let width = params.width - params.leftInset - params.rightInset
|
let width = params.width - params.leftInset - params.rightInset
|
||||||
let constrainedWidth = min(390.0, width)
|
let constrainedWidth = min(390.0, width)
|
||||||
let cellSize = floor((constrainedWidth - 12.0 * 2.0) / 7.0)
|
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
|
let height: CGFloat
|
||||||
if item.displayingDateSelection {
|
if item.displayingDateSelection {
|
||||||
height = pickerHeight
|
height = pickerHeight
|
||||||
} else if item.displayingTimeSelection {
|
} else if item.displayingTimeSelection {
|
||||||
height = 260.0
|
height = 260.0
|
||||||
} else {
|
} else {
|
||||||
height = 44.0
|
height = 56.0
|
||||||
}
|
}
|
||||||
switch item.style {
|
switch item.style {
|
||||||
case .plain:
|
case .plain:
|
||||||
|
|
@ -244,7 +247,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
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.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))
|
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)) }
|
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
|
||||||
|
|
||||||
let datePickerSize = CGSize(width: width, height: pickerHeight)
|
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)
|
datePickerNode.updateLayout(size: datePickerSize, transition: .immediate)
|
||||||
|
|
||||||
transition.updateFrame(node: strongSelf.containerNode, frame: CGRect(origin: .zero, size: CGSize(width: params.width, height: contentSize.height)))
|
transition.updateFrame(node: strongSelf.containerNode, frame: CGRect(origin: .zero, size: CGSize(width: params.width, height: contentSize.height)))
|
||||||
|
|
|
||||||
|
|
@ -479,10 +479,8 @@ private final class SheetContent: Component {
|
||||||
contentHeight += itemSize.height
|
contentHeight += itemSize.height
|
||||||
}
|
}
|
||||||
|
|
||||||
contentHeight += 31.0
|
contentHeight += 113.0
|
||||||
|
|
||||||
contentHeight += 82.0
|
|
||||||
|
|
||||||
return CGSize(width: availableSize.width, height: contentHeight)
|
return CGSize(width: availableSize.width, height: contentHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue