Various improvements

This commit is contained in:
Ilya Laktyushin 2024-03-25 22:36:51 +04:00
parent 3d6c9b1745
commit 54a90be1f4
11 changed files with 235 additions and 39 deletions

View File

@ -11683,6 +11683,7 @@ Sorry for the inconvenience.";
"Monetization.OverviewTitle" = "PROCEEDS OVERVIEW"; "Monetization.OverviewTitle" = "PROCEEDS OVERVIEW";
"Monetization.BalanceTitle" = "AVAILABLE BALANCE"; "Monetization.BalanceTitle" = "AVAILABLE BALANCE";
"Monetization.BalanceInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by the advertizer to pay for the ad. [Learn More >]()"; "Monetization.BalanceInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by the advertizer to pay for the ad. [Learn More >]()";
"Monetization.BalanceInfo_URL" = "https://telegram.org";
"Monetization.BalanceWithdraw" = "Withdraw via Fragment"; "Monetization.BalanceWithdraw" = "Withdraw via Fragment";
"Monetization.TransactionsTitle" = "TRANSACTION HISTORY"; "Monetization.TransactionsTitle" = "TRANSACTION HISTORY";
@ -11695,6 +11696,8 @@ Sorry for the inconvenience.";
"Monetization.Transaction.Refund" = "Refund"; "Monetization.Transaction.Refund" = "Refund";
"Monetization.Transaction.Pending" = "Pending"; "Monetization.Transaction.Pending" = "Pending";
"Monetization.Transaction.Failed" = "Not Completed"; "Monetization.Transaction.Failed" = "Not Completed";
"Monetization.Transaction.ShowMoreTransactions_1" = "Show %@ More Transaction";
"Monetization.Transaction.ShowMoreTransactions_any" = "Show %@ More Transactions";
"Monetization.SwitchOffAds" = "Switch off Ads"; "Monetization.SwitchOffAds" = "Switch off Ads";
"Monetization.SwitchOffAdsInfo" = "You will not be eligible for any rewards if you switch off ads."; "Monetization.SwitchOffAdsInfo" = "You will not be eligible for any rewards if you switch off ads.";
@ -11723,4 +11726,6 @@ Sorry for the inconvenience.";
"Monetization.Intro.Info.Title" = "What's #TON?"; "Monetization.Intro.Info.Title" = "What's #TON?";
"Monetization.Intro.Info.Text" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its record scalability and ultra low commissions on transactions. [Learn More >]()"; "Monetization.Intro.Info.Text" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its record scalability and ultra low commissions on transactions. [Learn More >]()";
"Monetization.Intro.Info.Text_URL" = "https://ton.org";
"Monetization.Intro.Understood" = "Understood"; "Monetization.Intro.Understood" = "Understood";

View File

@ -6,6 +6,7 @@ import SwiftSignalKit
import TelegramPresentationData import TelegramPresentationData
import SwitchNode import SwitchNode
import AppBundle import AppBundle
import ComponentFlow
public enum ItemListSwitchItemNodeType { public enum ItemListSwitchItemNodeType {
case regular case regular
@ -17,6 +18,7 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
let icon: UIImage? let icon: UIImage?
let title: String let title: String
let text: String? let text: String?
let titleBadgeComponent: AnyComponent<Empty>?
let value: Bool let value: Bool
let type: ItemListSwitchItemNodeType let type: ItemListSwitchItemNodeType
let enableInteractiveChanges: Bool let enableInteractiveChanges: Bool
@ -31,11 +33,12 @@ public class ItemListSwitchItem: ListViewItem, ItemListItem {
let activatedWhileDisabled: () -> Void let activatedWhileDisabled: () -> Void
public let tag: ItemListItemTag? public let tag: ItemListItemTag?
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, text: String? = nil, value: Bool, type: ItemListSwitchItemNodeType = .regular, enableInteractiveChanges: Bool = true, enabled: Bool = true, displayLocked: Bool = false, disableLeadingInset: Bool = false, maximumNumberOfLines: Int = 1, noCorners: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, updated: @escaping (Bool) -> Void, activatedWhileDisabled: @escaping () -> Void = {}, tag: ItemListItemTag? = nil) { public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, text: String? = nil, titleBadgeComponent: AnyComponent<Empty>? = nil, value: Bool, type: ItemListSwitchItemNodeType = .regular, enableInteractiveChanges: Bool = true, enabled: Bool = true, displayLocked: Bool = false, disableLeadingInset: Bool = false, maximumNumberOfLines: Int = 1, noCorners: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, updated: @escaping (Bool) -> Void, activatedWhileDisabled: @escaping () -> Void = {}, tag: ItemListItemTag? = nil) {
self.presentationData = presentationData self.presentationData = presentationData
self.icon = icon self.icon = icon
self.title = title self.title = title
self.text = text self.text = text
self.titleBadgeComponent = titleBadgeComponent
self.value = value self.value = value
self.type = type self.type = type
self.enableInteractiveChanges = enableInteractiveChanges self.enableInteractiveChanges = enableInteractiveChanges
@ -134,6 +137,8 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
private let switchGestureNode: ASDisplayNode private let switchGestureNode: ASDisplayNode
private var disabledOverlayNode: ASDisplayNode? private var disabledOverlayNode: ASDisplayNode?
private var titleBadgeComponentView: ComponentView<Empty>?
private var lockedIconNode: ASImageNode? private var lockedIconNode: ASImageNode?
private let activateArea: AccessibilityAreaNode private let activateArea: AccessibilityAreaNode
@ -471,6 +476,32 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
lockedIconNode.removeFromSupernode() lockedIconNode.removeFromSupernode()
} }
if let component = item.titleBadgeComponent {
let componentView: ComponentView<Empty>
if let current = strongSelf.titleBadgeComponentView {
componentView = current
} else {
componentView = ComponentView<Empty>()
strongSelf.titleBadgeComponentView = componentView
}
let badgeSize = componentView.update(
transition: .immediate,
component: component,
environment: {},
containerSize: contentSize
)
if let view = componentView.view {
if view.superview == nil {
strongSelf.view.addSubview(view)
}
view.frame = CGRect(origin: CGPoint(x: strongSelf.titleNode.frame.maxX + 7.0, y: floor((contentSize.height - badgeSize.height) / 2.0)), size: badgeSize)
}
} else if let componentView = strongSelf.titleBadgeComponentView {
strongSelf.titleBadgeComponentView = nil
componentView.view?.removeFromSuperview()
}
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 44.0 + UIScreenPixel + UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: 44.0 + UIScreenPixel + UIScreenPixel))
} }
}) })

View File

@ -39,6 +39,7 @@ swift_library(
"//submodules/TextFormat", "//submodules/TextFormat",
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen", "//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
"//submodules/TelegramUI/Components/Settings/BoostLevelIconComponent",
"//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/TelegramUI/Components/PlainButtonComponent",
"//submodules/TelegramUI/Components/EmojiTextAttachmentView", "//submodules/TelegramUI/Components/EmojiTextAttachmentView",
"//submodules/Components/SheetComponent", "//submodules/Components/SheetComponent",

View File

@ -23,8 +23,11 @@ import ItemListPeerActionItem
import PremiumUI import PremiumUI
import StoryContainerScreen import StoryContainerScreen
import TelegramNotices import TelegramNotices
import ComponentFlow
import BoostLevelIconComponent
private let initialBoostersDisplayedLimit: Int32 = 5 private let initialBoostersDisplayedLimit: Int32 = 5
private let initialTransactionsDisplayedLimit: Int32 = 5
private final class ChannelStatsControllerArguments { private final class ChannelStatsControllerArguments {
let context: AccountContext let context: AccountContext
@ -42,13 +45,14 @@ private final class ChannelStatsControllerArguments {
let requestWithdraw: () -> Void let requestWithdraw: () -> Void
let openMonetizationIntro: () -> Void let openMonetizationIntro: () -> Void
let openMonetizationInfo: () -> Void
let openTransaction: (RevenueStatsTransactionsContext.State.Transaction) -> Void let openTransaction: (RevenueStatsTransactionsContext.State.Transaction) -> Void
let expandTransactions: () -> Void let expandTransactions: () -> Void
let updateCpmEnabled: (Bool) -> Void let updateCpmEnabled: (Bool) -> Void
let presentCpmLocked: () -> Void let presentCpmLocked: () -> Void
let dismissInput: () -> Void let dismissInput: () -> Void
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPostStats: @escaping (EnginePeer, StatsPostItem) -> Void, openStory: @escaping (EngineStoryItem, UIView) -> Void, contextAction: @escaping (MessageId, ASDisplayNode, ContextGesture?) -> Void, copyBoostLink: @escaping (String) -> Void, shareBoostLink: @escaping (String) -> Void, openBoost: @escaping (ChannelBoostersContext.State.Boost) -> Void, expandBoosters: @escaping () -> Void, openGifts: @escaping () -> Void, createPrepaidGiveaway: @escaping (PrepaidGiveaway) -> Void, updateGiftsSelected: @escaping (Bool) -> Void, requestWithdraw: @escaping () -> Void, openMonetizationIntro: @escaping () -> Void, openTransaction: @escaping (RevenueStatsTransactionsContext.State.Transaction) -> Void, expandTransactions: @escaping () -> Void, updateCpmEnabled: @escaping (Bool) -> Void, presentCpmLocked: @escaping () -> Void, dismissInput: @escaping () -> Void) { init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPostStats: @escaping (EnginePeer, StatsPostItem) -> Void, openStory: @escaping (EngineStoryItem, UIView) -> Void, contextAction: @escaping (MessageId, ASDisplayNode, ContextGesture?) -> Void, copyBoostLink: @escaping (String) -> Void, shareBoostLink: @escaping (String) -> Void, openBoost: @escaping (ChannelBoostersContext.State.Boost) -> Void, expandBoosters: @escaping () -> Void, openGifts: @escaping () -> Void, createPrepaidGiveaway: @escaping (PrepaidGiveaway) -> Void, updateGiftsSelected: @escaping (Bool) -> Void, requestWithdraw: @escaping () -> Void, openMonetizationIntro: @escaping () -> Void, openMonetizationInfo: @escaping () -> Void, openTransaction: @escaping (RevenueStatsTransactionsContext.State.Transaction) -> Void, expandTransactions: @escaping () -> Void, updateCpmEnabled: @escaping (Bool) -> Void, presentCpmLocked: @escaping () -> Void, dismissInput: @escaping () -> Void) {
self.context = context self.context = context
self.loadDetailedGraph = loadDetailedGraph self.loadDetailedGraph = loadDetailedGraph
self.openPostStats = openPostStats self.openPostStats = openPostStats
@ -63,6 +67,7 @@ private final class ChannelStatsControllerArguments {
self.updateGiftsSelected = updateGiftsSelected self.updateGiftsSelected = updateGiftsSelected
self.requestWithdraw = requestWithdraw self.requestWithdraw = requestWithdraw
self.openMonetizationIntro = openMonetizationIntro self.openMonetizationIntro = openMonetizationIntro
self.openMonetizationInfo = openMonetizationInfo
self.openTransaction = openTransaction self.openTransaction = openTransaction
self.expandTransactions = expandTransactions self.expandTransactions = expandTransactions
self.updateCpmEnabled = updateCpmEnabled self.updateCpmEnabled = updateCpmEnabled
@ -225,8 +230,9 @@ private enum StatsEntry: ItemListNodeEntry {
case adsTransactionsTitle(PresentationTheme, String) case adsTransactionsTitle(PresentationTheme, String)
case adsTransaction(Int32, PresentationTheme, RevenueStatsTransactionsContext.State.Transaction) case adsTransaction(Int32, PresentationTheme, RevenueStatsTransactionsContext.State.Transaction)
case adsTransactionsExpand(PresentationTheme, String)
case adsCpmToggle(PresentationTheme, String, Bool?) case adsCpmToggle(PresentationTheme, String, Int32, Bool?)
case adsCpmInfo(PresentationTheme, String) case adsCpmInfo(PresentationTheme, String)
var section: ItemListSectionId { var section: ItemListSectionId {
@ -281,7 +287,7 @@ private enum StatsEntry: ItemListNodeEntry {
return StatsSection.adsProceeds.rawValue return StatsSection.adsProceeds.rawValue
case .adsBalanceTitle, .adsBalance, .adsBalanceInfo: case .adsBalanceTitle, .adsBalance, .adsBalanceInfo:
return StatsSection.adsBalance.rawValue return StatsSection.adsBalance.rawValue
case .adsTransactionsTitle, .adsTransaction: case .adsTransactionsTitle, .adsTransaction, .adsTransactionsExpand:
return StatsSection.adsTransactions.rawValue return StatsSection.adsTransactions.rawValue
case .adsCpmToggle, .adsCpmInfo: case .adsCpmToggle, .adsCpmInfo:
return StatsSection.adsCpm.rawValue return StatsSection.adsCpm.rawValue
@ -404,10 +410,12 @@ private enum StatsEntry: ItemListNodeEntry {
return 20010 return 20010
case let .adsTransaction(index, _, _): case let .adsTransaction(index, _, _):
return 20011 + index return 20011 + index
case .adsTransactionsExpand:
return 30000
case .adsCpmToggle: case .adsCpmToggle:
return 21000 return 30001
case .adsCpmInfo: case .adsCpmInfo:
return 21002 return 30002
} }
} }
@ -755,8 +763,14 @@ private enum StatsEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .adsCpmToggle(lhsTheme, lhsText, lhsValue): case let .adsTransactionsExpand(lhsTheme, lhsText):
if case let .adsCpmToggle(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { if case let .adsTransactionsExpand(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .adsCpmToggle(lhsTheme, lhsText, lhsMinLevel, lhsValue):
if case let .adsCpmToggle(rhsTheme, rhsText, rhsMinLevel, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsMinLevel == rhsMinLevel, lhsValue == rhsValue {
return true return true
} else { } else {
return false return false
@ -806,7 +820,6 @@ private enum StatsEntry: ItemListNodeEntry {
let .boostersInfo(_, text), let .boostersInfo(_, text),
let .boostLinkInfo(_, text), let .boostLinkInfo(_, text),
let .boostGiftsInfo(_, text), let .boostGiftsInfo(_, text),
let .adsBalanceInfo(_, text),
let .adsCpmInfo(_, text): let .adsCpmInfo(_, 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):
@ -961,6 +974,10 @@ private enum StatsEntry: ItemListNodeEntry {
sectionId: self.section, sectionId: self.section,
style: .blocks style: .blocks
) )
case let .adsBalanceInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
arguments.openMonetizationInfo()
})
case let .adsTransaction(_, theme, transaction): case let .adsTransaction(_, theme, transaction):
let font = Font.regular(presentationData.fontSize.itemListBaseFontSize) let font = Font.regular(presentationData.fontSize.itemListBaseFontSize)
let smallLabelFont = Font.regular(floor(presentationData.fontSize.itemListBaseFontSize / 17.0 * 13.0)) let smallLabelFont = Font.regular(floor(presentationData.fontSize.itemListBaseFontSize / 17.0 * 13.0))
@ -973,22 +990,22 @@ private enum StatsEntry: ItemListNodeEntry {
switch transaction { switch transaction {
case let .proceeds(_, fromDate, toDate): case let .proceeds(_, fromDate, toDate):
title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Proceeds, font: font, textColor: theme.list.itemPrimaryTextColor) title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Proceeds, font: font, textColor: theme.list.itemPrimaryTextColor)
detailText = "\(stringForMediumDate(timestamp: fromDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)) \(stringForMediumDate(timestamp: toDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat))" detailText = "\(stringForMediumCompactDate(timestamp: fromDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)) \(stringForMediumCompactDate(timestamp: toDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat))"
case let .withdrawal(status, _, date, provider, _, _): case let .withdrawal(status, _, date, provider, _, _):
title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Withdrawal(provider).string, font: font, textColor: theme.list.itemPrimaryTextColor) title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Withdrawal(provider).string, font: font, textColor: theme.list.itemPrimaryTextColor)
labelColor = theme.list.itemDestructiveColor labelColor = theme.list.itemDestructiveColor
switch status { switch status {
case .succeed: case .succeed:
detailText = stringForMediumDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
case .failed: case .failed:
detailText = stringForMediumDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + " \(presentationData.strings.Monetization_Transaction_Failed)" detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) + " \(presentationData.strings.Monetization_Transaction_Failed)"
detailColor = .destructive detailColor = .destructive
case .pending: case .pending:
detailText = presentationData.strings.Monetization_Transaction_Pending detailText = presentationData.strings.Monetization_Transaction_Pending
} }
case let .refund(_, date, _): case let .refund(_, date, _):
title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Refund, font: font, textColor: theme.list.itemPrimaryTextColor) title = NSAttributedString(string: presentationData.strings.Monetization_Transaction_Refund, font: font, textColor: theme.list.itemPrimaryTextColor)
detailText = stringForMediumDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) detailText = stringForMediumCompactDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
} }
let label = amountAttributedString(formatBalanceText(transaction.amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, showPlus: true), integralFont: font, fractionalFont: smallLabelFont, color: labelColor).mutableCopy() as! NSMutableAttributedString let label = amountAttributedString(formatBalanceText(transaction.amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, showPlus: true), integralFont: font, fractionalFont: smallLabelFont, color: labelColor).mutableCopy() as! NSMutableAttributedString
@ -997,8 +1014,19 @@ private enum StatsEntry: ItemListNodeEntry {
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, title: "", attributedTitle: title, label: "", attributedLabel: label, labelStyle: .coloredText(labelColor), additionalDetailLabel: detailText, additionalDetailLabelColor: detailColor, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
arguments.openTransaction(transaction) arguments.openTransaction(transaction)
}) })
case let .adsCpmToggle(_, title, value): case let .adsTransactionsExpand(theme, title):
return ItemListSwitchItem(presentationData: presentationData, title: title, value: value == true, enableInteractiveChanges: value != nil, enabled: true, displayLocked: value == nil, sectionId: self.section, style: .blocks, updated: { updatedValue in return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandTransactions()
})
case let .adsCpmToggle(_, title, minLevel, value):
var badgeComponent: AnyComponent<Empty>?
if value == nil {
badgeComponent = AnyComponent(BoostLevelIconComponent(
strings: presentationData.strings,
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
if value != nil { if value != nil {
arguments.updateCpmEnabled(updatedValue) arguments.updateCpmEnabled(updatedValue)
} else { } else {
@ -1022,19 +1050,25 @@ private struct ChannelStatsControllerState: Equatable {
let boostersExpanded: Bool let boostersExpanded: Bool
let moreBoostersDisplayed: Int32 let moreBoostersDisplayed: Int32
let giftsSelected: Bool let giftsSelected: Bool
let transactionsExpanded: Bool
let moreTransactionsDisplayed: Int32
init() { init() {
self.section = .stats self.section = .stats
self.boostersExpanded = false self.boostersExpanded = false
self.moreBoostersDisplayed = 0 self.moreBoostersDisplayed = 0
self.giftsSelected = false self.giftsSelected = false
self.transactionsExpanded = false
self.moreTransactionsDisplayed = 0
} }
init(section: ChannelStatsSection, boostersExpanded: Bool, moreBoostersDisplayed: Int32, giftsSelected: Bool) { init(section: ChannelStatsSection, boostersExpanded: Bool, moreBoostersDisplayed: Int32, giftsSelected: Bool, transactionsExpanded: Bool, moreTransactionsDisplayed: Int32) {
self.section = section self.section = section
self.boostersExpanded = boostersExpanded self.boostersExpanded = boostersExpanded
self.moreBoostersDisplayed = moreBoostersDisplayed self.moreBoostersDisplayed = moreBoostersDisplayed
self.giftsSelected = giftsSelected self.giftsSelected = giftsSelected
self.transactionsExpanded = transactionsExpanded
self.moreTransactionsDisplayed = moreTransactionsDisplayed
} }
static func ==(lhs: ChannelStatsControllerState, rhs: ChannelStatsControllerState) -> Bool { static func ==(lhs: ChannelStatsControllerState, rhs: ChannelStatsControllerState) -> Bool {
@ -1050,23 +1084,37 @@ private struct ChannelStatsControllerState: Equatable {
if lhs.giftsSelected != rhs.giftsSelected { if lhs.giftsSelected != rhs.giftsSelected {
return false return false
} }
if lhs.transactionsExpanded != rhs.transactionsExpanded {
return false
}
if lhs.moreTransactionsDisplayed != rhs.moreTransactionsDisplayed {
return false
}
return true return true
} }
func withUpdatedSection(_ section: ChannelStatsSection) -> ChannelStatsControllerState { func withUpdatedSection(_ section: ChannelStatsSection) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected) return ChannelStatsControllerState(section: section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: self.moreTransactionsDisplayed)
} }
func withUpdatedBoostersExpanded(_ boostersExpanded: Bool) -> ChannelStatsControllerState { func withUpdatedBoostersExpanded(_ boostersExpanded: Bool) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: self.section, boostersExpanded: boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected) return ChannelStatsControllerState(section: self.section, boostersExpanded: boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: self.moreTransactionsDisplayed)
} }
func withUpdatedMoreBoostersDisplayed(_ moreBoostersDisplayed: Int32) -> ChannelStatsControllerState { func withUpdatedMoreBoostersDisplayed(_ moreBoostersDisplayed: Int32) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: moreBoostersDisplayed, giftsSelected: self.giftsSelected) return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: moreBoostersDisplayed, giftsSelected: self.giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: self.moreTransactionsDisplayed)
} }
func withUpdatedGiftsSelected(_ giftsSelected: Bool) -> ChannelStatsControllerState { func withUpdatedGiftsSelected(_ giftsSelected: Bool) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: giftsSelected) return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: self.moreTransactionsDisplayed)
}
func withUpdatedTransactionsExpanded(_ transactionsExpanded: Bool) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: self.moreTransactionsDisplayed)
}
func withUpdatedMoreTransactionsDisplayed(_ moreTransactionsDisplayed: Int32) -> ChannelStatsControllerState {
return ChannelStatsControllerState(section: self.section, boostersExpanded: self.boostersExpanded, moreBoostersDisplayed: self.moreBoostersDisplayed, giftsSelected: self.giftsSelected, transactionsExpanded: self.transactionsExpanded, moreTransactionsDisplayed: moreTransactionsDisplayed)
} }
} }
@ -1309,9 +1357,11 @@ private func boostsEntries(
private func monetizationEntries( private func monetizationEntries(
presentationData: PresentationData, presentationData: PresentationData,
state: ChannelStatsControllerState, state: ChannelStatsControllerState,
peer: EnginePeer?,
data: RevenueStats, data: RevenueStats,
boostData: ChannelBoostStatus?, boostData: ChannelBoostStatus?,
transactions: RevenueStatsTransactionsContext.State, transactionsInfo: RevenueStatsTransactionsContext.State,
adsRestricted: Bool,
animatedEmojis: [String: [StickerPackItem]], animatedEmojis: [String: [StickerPackItem]],
premiumConfiguration: PremiumConfiguration premiumConfiguration: PremiumConfiguration
) -> [StatsEntry] { ) -> [StatsEntry] {
@ -1333,25 +1383,50 @@ private func monetizationEntries(
entries.append(.adsProceedsTitle(presentationData.theme, presentationData.strings.Monetization_OverviewTitle)) entries.append(.adsProceedsTitle(presentationData.theme, presentationData.strings.Monetization_OverviewTitle))
entries.append(.adsProceedsOverview(presentationData.theme, data, diamond)) entries.append(.adsProceedsOverview(presentationData.theme, data, diamond))
var withdrawalAvailable = false
if let peer, case let .channel(channel) = peer, channel.flags.contains(.isCreator) && data.availableBalance > 0 {
withdrawalAvailable = true
}
entries.append(.adsBalanceTitle(presentationData.theme, presentationData.strings.Monetization_BalanceTitle)) entries.append(.adsBalanceTitle(presentationData.theme, presentationData.strings.Monetization_BalanceTitle))
entries.append(.adsBalance(presentationData.theme, data, false, diamond)) entries.append(.adsBalance(presentationData.theme, data, withdrawalAvailable, diamond))
entries.append(.adsBalanceInfo(presentationData.theme, presentationData.strings.Monetization_BalanceInfo)) entries.append(.adsBalanceInfo(presentationData.theme, presentationData.strings.Monetization_BalanceInfo))
if !transactions.transactions.isEmpty { if !transactionsInfo.transactions.isEmpty {
entries.append(.adsTransactionsTitle(presentationData.theme, presentationData.strings.Monetization_TransactionsTitle)) entries.append(.adsTransactionsTitle(presentationData.theme, presentationData.strings.Monetization_TransactionsTitle))
var transactions = transactionsInfo.transactions
var limit: Int32
if state.transactionsExpanded {
limit = 25 + state.moreTransactionsDisplayed
} else {
limit = initialTransactionsDisplayedLimit
}
transactions = Array(transactions.prefix(Int(limit)))
var i: Int32 = 0 var i: Int32 = 0
for transaction in transactions.transactions { for transaction in transactions {
entries.append(.adsTransaction(i, presentationData.theme, transaction)) entries.append(.adsTransaction(i, presentationData.theme, transaction))
i += 1 i += 1
} }
if transactions.count < transactionsInfo.count {
let moreCount: Int32
if !state.transactionsExpanded {
moreCount = min(20, transactionsInfo.count - Int32(transactions.count))
} else {
moreCount = min(500, transactionsInfo.count - Int32(transactions.count))
}
entries.append(.adsTransactionsExpand(presentationData.theme, presentationData.strings.Monetization_Transaction_ShowMoreTransactions(moreCount)))
}
} }
var switchOffAdds: Bool? = nil var switchOffAdds: Bool? = nil
if let boostData, boostData.level >= premiumConfiguration.minChannelRestrictAdsLevel { if let boostData, boostData.level >= premiumConfiguration.minChannelRestrictAdsLevel {
switchOffAdds = false switchOffAdds = adsRestricted
} }
entries.append(.adsCpmToggle(presentationData.theme, presentationData.strings.Monetization_SwitchOffAds, switchOffAdds)) entries.append(.adsCpmToggle(presentationData.theme, presentationData.strings.Monetization_SwitchOffAds, premiumConfiguration.minChannelRestrictAdsLevel, switchOffAdds))
entries.append(.adsCpmInfo(presentationData.theme, presentationData.strings.Monetization_SwitchOffAdsInfo)) entries.append(.adsCpmInfo(presentationData.theme, presentationData.strings.Monetization_SwitchOffAdsInfo))
return entries return entries
@ -1374,6 +1449,7 @@ private func channelStatsControllerEntries(
animatedEmojis: [String: [StickerPackItem]], animatedEmojis: [String: [StickerPackItem]],
revenueState: RevenueStats?, revenueState: RevenueStats?,
revenueTransactions: RevenueStatsTransactionsContext.State, revenueTransactions: RevenueStatsTransactionsContext.State,
adsRestricted: Bool,
premiumConfiguration: PremiumConfiguration premiumConfiguration: PremiumConfiguration
) -> [StatsEntry] { ) -> [StatsEntry] {
switch state.section { switch state.section {
@ -1406,9 +1482,11 @@ private func channelStatsControllerEntries(
return monetizationEntries( return monetizationEntries(
presentationData: presentationData, presentationData: presentationData,
state: state, state: state,
peer: peer,
data: revenueState, data: revenueState,
boostData: boostData, boostData: boostData,
transactions: revenueTransactions, transactionsInfo: revenueTransactions,
adsRestricted: adsRestricted,
animatedEmojis: animatedEmojis, animatedEmojis: animatedEmojis,
premiumConfiguration: premiumConfiguration premiumConfiguration: premiumConfiguration
) )
@ -1418,8 +1496,8 @@ private func channelStatsControllerEntries(
} }
public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil, boostStatusUpdated: ((ChannelBoostStatus) -> Void)? = nil) -> ViewController { public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil, boostStatusUpdated: ((ChannelBoostStatus) -> Void)? = nil) -> ViewController {
let statePromise = ValuePromise(ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false), ignoreRepeated: true) let statePromise = ValuePromise(ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false, transactionsExpanded: false, moreTransactionsDisplayed: 0), ignoreRepeated: true)
let stateValue = Atomic(value: ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false)) let stateValue = Atomic(value: ChannelStatsControllerState(section: section, boostersExpanded: false, moreBoostersDisplayed: 0, giftsSelected: false, transactionsExpanded: false, moreTransactionsDisplayed: 0))
let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) }) statePromise.set(stateValue.modify { f($0) })
} }
@ -1471,6 +1549,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let boostsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: false) let boostsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: false)
let giftsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: true) let giftsContext = ChannelBoostersContext(account: context.account, peerId: peerId, gift: true)
let revenueContext = RevenueStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId) let revenueContext = RevenueStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId)
let revenueState = Promise<RevenueStatsContextState?>()
revenueState.set(.single(nil) |> then(revenueContext.state |> map(Optional.init)))
let revenueTransactions = RevenueStatsTransactionsContext(account: context.account, peerId: peerId) let revenueTransactions = RevenueStatsTransactionsContext(account: context.account, peerId: peerId)
@ -1612,13 +1692,25 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let controller = MonetizationIntroScreen(context: context, openMore: {}) let controller = MonetizationIntroScreen(context: context, openMore: {})
pushImpl?(controller) pushImpl?(controller)
}, },
openMonetizationInfo: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.Monetization_BalanceInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
},
openTransaction: { transaction in openTransaction: { transaction in
openTransactionImpl?(transaction) openTransactionImpl?(transaction)
}, },
expandTransactions: { expandTransactions: {
updateState { state in
if state.transactionsExpanded {
return state.withUpdatedMoreTransactionsDisplayed(state.moreTransactionsDisplayed + 50)
} else {
return state.withUpdatedTransactionsExpanded(true)
}
}
revenueTransactions.loadMore()
}, },
updateCpmEnabled: { value in updateCpmEnabled: { value in
let _ = context.engine.peers.updateChannelRestrictAdMessages(peerId: peerId, value: value ? .restrict(minCpm: nil) : .unrestrict).start()
}, },
presentCpmLocked: { presentCpmLocked: {
let _ = combineLatest( let _ = combineLatest(
@ -1658,6 +1750,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let peer = Promise<EnginePeer?>() let peer = Promise<EnginePeer?>()
peer.set(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))) peer.set(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
let adsRestricted = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.AdsRestricted(id: peerId))
let longLoadingSignal: Signal<Bool, NoError> = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue())) let longLoadingSignal: Signal<Bool, NoError> = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue()))
let previousData = Atomic<ChannelStats?>(value: nil) let previousData = Atomic<ChannelStats?>(value: nil)
@ -1672,13 +1766,14 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
boostDataPromise.get(), boostDataPromise.get(),
boostsContext.state, boostsContext.state,
giftsContext.state, giftsContext.state,
revenueContext.state, revenueState.get(),
revenueTransactions.state, revenueTransactions.state,
adsRestricted,
longLoadingSignal, longLoadingSignal,
context.animatedEmojiStickers context.animatedEmojiStickers
) )
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { presentationData, state, peer, data, messageView, stories, boostData, boostersState, giftsState, revenueState, revenueTransactions, longLoading, animatedEmojiStickers -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state, peer, data, messageView, stories, boostData, boostersState, giftsState, revenueState, revenueTransactions, adsRestricted, longLoading, animatedEmojiStickers -> (ItemListControllerState, (ItemListNodeState, Any)) in
var isGroup = false var isGroup = false
if let peer, case let .channel(channel) = peer, case .group = channel.info { if let peer, case let .channel(channel) = peer, case .group = channel.info {
isGroup = true isGroup = true
@ -1700,8 +1795,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
} }
case .monetization: case .monetization:
emptyStateItem = nil if revenueState == nil {
// emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
}
} }
var existingGroupingKeys = Set<Int64>() var existingGroupingKeys = Set<Int64>()
@ -1762,7 +1858,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(presentationData: presentationData, state: state, peer: peer, data: data, messages: messages, stories: stories, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable, isGroup: isGroup, boostsOnly: boostsOnly, animatedEmojis: animatedEmojiStickers, revenueState: revenueState.stats, revenueTransactions: revenueTransactions, premiumConfiguration: premiumConfiguration), style: .blocks, emptyStateItem: emptyStateItem, headerItem: headerItem, crossfadeState: previous == nil, animateChanges: false) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(presentationData: presentationData, state: state, peer: peer, data: data, messages: messages, stories: stories, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable, isGroup: isGroup, boostsOnly: boostsOnly, animatedEmojis: animatedEmojiStickers, revenueState: revenueState?.stats, revenueTransactions: revenueTransactions, adsRestricted: adsRestricted, premiumConfiguration: premiumConfiguration), style: .blocks, emptyStateItem: emptyStateItem, headerItem: headerItem, crossfadeState: previous == nil, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }
@ -1770,6 +1866,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
actionsDisposable.dispose() actionsDisposable.dispose()
let _ = statsContext.state let _ = statsContext.state
let _ = storyList.state let _ = storyList.state
let _ = revenueContext.state
} }
let controller = ItemListController(context: context, state: signal) let controller = ItemListController(context: context, state: signal)

View File

@ -224,6 +224,7 @@ private final class SheetContent: CombinedComponent {
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme) state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
} }
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let infoString = strings.Monetization_Intro_Info_Text let infoString = strings.Monetization_Intro_Info_Text
let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
@ -234,7 +235,17 @@ private final class SheetContent: CombinedComponent {
text: .plain(infoAttributedString), text: .plain(infoAttributedString),
horizontalAlignment: .center, horizontalAlignment: .center,
maximumNumberOfLines: 0, maximumNumberOfLines: 0,
lineSpacing: 0.2 lineSpacing: 0.2,
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
},
tapAction: { _, _ in
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Monetization_Intro_Info_Text_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
}
), ),
availableSize: CGSize(width: context.availableSize.width - (textSideInset + sideInset - 2.0) * 2.0, height: context.availableSize.height), availableSize: CGSize(width: context.availableSize.width - (textSideInset + sideInset - 2.0) * 2.0, height: context.availableSize.height),
transition: .immediate transition: .immediate

View File

@ -139,7 +139,7 @@ private final class SheetContent: CombinedComponent {
case let .proceeds(amount, fromDate, toDate): case let .proceeds(amount, fromDate, toDate):
amountString = amountAttributedString(formatBalanceText(amount, decimalSeparator: dateTimeFormat.decimalSeparator, showPlus: true), integralFont: integralFont, fractionalFont: fractionalFont, color: theme.list.itemDisclosureActions.constructive.fillColor).mutableCopy() as! NSMutableAttributedString amountString = amountAttributedString(formatBalanceText(amount, decimalSeparator: dateTimeFormat.decimalSeparator, showPlus: true), integralFont: integralFont, fractionalFont: fractionalFont, color: theme.list.itemDisclosureActions.constructive.fillColor).mutableCopy() as! NSMutableAttributedString
amountString.append(NSAttributedString(string: " TON", font: fractionalFont, textColor: theme.list.itemDisclosureActions.constructive.fillColor)) amountString.append(NSAttributedString(string: " TON", font: fractionalFont, textColor: theme.list.itemDisclosureActions.constructive.fillColor))
dateString = "\(stringForFullDate(timestamp: fromDate, strings: strings, dateTimeFormat: dateTimeFormat)) \(stringForFullDate(timestamp: toDate, strings: strings, dateTimeFormat: dateTimeFormat))" dateString = "\(stringForMediumCompactDate(timestamp: fromDate, strings: strings, dateTimeFormat: dateTimeFormat)) \(stringForMediumCompactDate(timestamp: toDate, strings: strings, dateTimeFormat: dateTimeFormat))"
titleString = strings.Monetization_TransactionInfo_Proceeds titleString = strings.Monetization_TransactionInfo_Proceeds
buttonTitle = strings.Common_OK buttonTitle = strings.Common_OK
explorerUrl = nil explorerUrl = nil

View File

@ -240,7 +240,9 @@ private final class RevenueStatsTransactionsContextImpl {
return .complete() return .complete()
} }
let offset = lastOffset ?? 0 let offset = lastOffset ?? 0
let request = Api.functions.stats.getBroadcastRevenueTransactions(channel: inputChannel, offset: offset, limit: 50) let limit: Int32 = lastOffset == nil ? 25 : 50
let request = Api.functions.stats.getBroadcastRevenueTransactions(channel: inputChannel, offset: offset, limit: limit)
let signal: Signal<Api.stats.BroadcastRevenueTransactions, MTRpcError> let signal: Signal<Api.stats.BroadcastRevenueTransactions, MTRpcError>
if let statsDatacenterId = statsDatacenterId, account.network.datacenterId != statsDatacenterId { if let statsDatacenterId = statsDatacenterId, account.network.datacenterId != statsDatacenterId {
signal = account.network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) signal = account.network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)

View File

@ -1812,5 +1812,33 @@ public extension TelegramEngine.EngineData.Item {
} }
} }
} }
public struct AdsRestricted: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Bool
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .cachedPeerData(peerId: self.id)
}
func extract(view: PostboxView) -> Result {
guard let view = view as? CachedPeerDataView else {
preconditionFailure()
}
if let cachedData = view.cachedPeerData as? CachedChannelData {
return cachedData.flags.contains(.adsRestricted)
} else {
return false
}
}
}
} }
} }

View File

@ -58,6 +58,26 @@ public func getDateTimeComponents(timestamp: Int32) -> (day: Int32, month: Int32
return (timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year, timeinfo.tm_hour, timeinfo.tm_min) return (timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year, timeinfo.tm_hour, timeinfo.tm_min)
} }
public func stringForMediumCompactDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> String {
var t: time_t = Int(timestamp)
var timeinfo = tm()
localtime_r(&t, &timeinfo);
let day = timeinfo.tm_mday
let month = monthAtIndex(Int(timeinfo.tm_mon), strings: strings)
let timeString = stringForShortTimestamp(hours: Int32(timeinfo.tm_hour), minutes: Int32(timeinfo.tm_min), dateTimeFormat: dateTimeFormat)
let dateString: String
switch dateTimeFormat.dateFormat {
case .monthFirst:
dateString = String(format: "%@ %02d %@", month, day, timeString)
case .dayFirst:
dateString = String(format: "%02d %@ %@", day, month, timeString)
}
return dateString
}
public func stringForMediumDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, withTime: Bool = true) -> String { public func stringForMediumDate(timestamp: Int32, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, withTime: Bool = true) -> String {
var t: time_t = Int(timestamp) var t: time_t = Int(timestamp)
var timeinfo = tm() var timeinfo = tm()

View File

@ -107,7 +107,7 @@ public func stringForMonth(strings: PresentationStrings, month: Int32, ofYear ye
} }
} }
private func monthAtIndex(_ index: Int, strings: PresentationStrings) -> String { func monthAtIndex(_ index: Int, strings: PresentationStrings) -> String {
switch index { switch index {
case 0: case 0:
return strings.Month_ShortJanuary return strings.Month_ShortJanuary

View File

@ -32,6 +32,7 @@ final class PeerInfoBirthdayOverlay: ASDisplayNode {
self.setupAnimations(size: size, birthday: birthday, sourceRect: sourceRect) self.setupAnimations(size: size, birthday: birthday, sourceRect: sourceRect)
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
HapticFeedback().success()
self.view.addSubview(ConfettiView(frame: CGRect(origin: .zero, size: size))) self.view.addSubview(ConfettiView(frame: CGRect(origin: .zero, size: size)))
} }
} }