Swiftgram/submodules/StatisticsUI/Sources/GroupStatsController.swift
2020-06-19 00:33:54 +03:00

559 lines
29 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import UIKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
import MapKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import ItemListUI
import PresentationDataUtils
import AccountContext
import PresentationDataUtils
import AppBundle
import GraphUI
import ItemListPeerItem
private final class GroupStatsControllerArguments {
let context: AccountContext
let loadDetailedGraph: (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>
let openPeer: (PeerId) -> Void
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPeer: @escaping (PeerId) -> Void) {
self.context = context
self.loadDetailedGraph = loadDetailedGraph
self.openPeer = openPeer
}
}
private enum StatsSection: Int32 {
case overview
case growth
case members
case newMembersBySource
case languages
case messages
case actions
case topHours
case topPosters
case topAdmins
case topInviters
}
private enum StatsEntry: ItemListNodeEntry {
case overviewHeader(PresentationTheme, String, String)
case overview(PresentationTheme, GroupStats)
case growthTitle(PresentationTheme, String)
case growthGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case membersTitle(PresentationTheme, String)
case membersGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case newMembersBySourceTitle(PresentationTheme, String)
case newMembersBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case languagesTitle(PresentationTheme, String)
case languagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case messagesTitle(PresentationTheme, String)
case messagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case actionsTitle(PresentationTheme, String)
case actionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case topHoursTitle(PresentationTheme, String)
case topHoursGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case topPostersTitle(PresentationTheme, String)
case topPoster(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopPoster)
case topAdminsTitle(PresentationTheme, String)
case topAdmin(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopAdmin)
case topInvitersTitle(PresentationTheme, String)
case topInviter(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopInviter)
var section: ItemListSectionId {
switch self {
case .overviewHeader, .overview:
return StatsSection.overview.rawValue
case .growthTitle, .growthGraph:
return StatsSection.growth.rawValue
case .membersTitle, .membersGraph:
return StatsSection.members.rawValue
case .newMembersBySourceTitle, .newMembersBySourceGraph:
return StatsSection.newMembersBySource.rawValue
case .languagesTitle, .languagesGraph:
return StatsSection.languages.rawValue
case .messagesTitle, . messagesGraph:
return StatsSection.messages.rawValue
case .actionsTitle, .actionsGraph:
return StatsSection.actions.rawValue
case .topHoursTitle, .topHoursGraph:
return StatsSection.topHours.rawValue
case .topPostersTitle, .topPoster:
return StatsSection.topPosters.rawValue
case .topAdminsTitle, .topAdmin:
return StatsSection.topAdmins.rawValue
case .topInvitersTitle, .topInviter:
return StatsSection.topInviters.rawValue
}
}
var stableId: Int32 {
switch self {
case .overviewHeader:
return 0
case .overview:
return 1
case .growthTitle:
return 2
case .growthGraph:
return 3
case .membersTitle:
return 4
case .membersGraph:
return 5
case .newMembersBySourceTitle:
return 6
case .newMembersBySourceGraph:
return 7
case .languagesTitle:
return 8
case .languagesGraph:
return 9
case .messagesTitle:
return 10
case .messagesGraph:
return 11
case .actionsTitle:
return 12
case .actionsGraph:
return 13
case .topHoursTitle:
return 14
case .topHoursGraph:
return 15
case .topPostersTitle:
return 1000
case let .topPoster(index, _, _, _, _, _):
return 1001 + index
case .topAdminsTitle:
return 2000
case let .topAdmin(index, _, _, _, _, _):
return 2001 + index
case .topInvitersTitle:
return 3000
case let .topInviter(index, _, _, _, _, _):
return 30001 + index
}
}
static func ==(lhs: StatsEntry, rhs: StatsEntry) -> Bool {
switch lhs {
case let .overviewHeader(lhsTheme, lhsText, lhsDates):
if case let .overviewHeader(rhsTheme, rhsText, rhsDates) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsDates == rhsDates {
return true
} else {
return false
}
case let .overview(lhsTheme, lhsStats):
if case let .overview(rhsTheme, rhsStats) = rhs, lhsTheme === rhsTheme, lhsStats == rhsStats {
return true
} else {
return false
}
case let .growthTitle(lhsTheme, lhsText):
if case let .growthTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .growthGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .growthGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .membersTitle(lhsTheme, lhsText):
if case let .membersTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .membersGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .membersGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .newMembersBySourceTitle(lhsTheme, lhsText):
if case let .newMembersBySourceTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .newMembersBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .newMembersBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .languagesTitle(lhsTheme, lhsText):
if case let .languagesTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .languagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .languagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .messagesTitle(lhsTheme, lhsText):
if case let .messagesTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .messagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .messagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .actionsTitle(lhsTheme, lhsText):
if case let .actionsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .actionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .actionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .topHoursTitle(lhsTheme, lhsText):
if case let .topHoursTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .topHoursGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .topHoursGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
}
case let .topPostersTitle(lhsTheme, lhsText):
if case let .topPostersTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .topPoster(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopPoster):
if case let .topPoster(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopPoster) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopPoster == rhsTopPoster {
return true
} else {
return false
}
case let .topAdminsTitle(lhsTheme, lhsText):
if case let .topAdminsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .topAdmin(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopAdmin):
if case let .topAdmin(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopAdmin) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopAdmin == rhsTopAdmin {
return true
} else {
return false
}
case let .topInvitersTitle(lhsTheme, lhsText):
if case let .topInvitersTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .topInviter(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopInviter):
if case let .topInviter(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopInviter) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopInviter == rhsTopInviter {
return true
} else {
return false
}
}
}
static func <(lhs: StatsEntry, rhs: StatsEntry) -> Bool {
return lhs.stableId < rhs.stableId
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! GroupStatsControllerArguments
switch self {
case let .overviewHeader(_, text, dates):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section)
case let .growthTitle(_, text),
let .membersTitle(_, text),
let .newMembersBySourceTitle(_, text),
let .languagesTitle(_, text),
let .messagesTitle(_, text),
let .actionsTitle(_, text),
let .topHoursTitle(_, text),
let .topPostersTitle(_, text),
let .topAdminsTitle(_, text),
let .topInvitersTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .overview(_, stats):
return StatsOverviewItem(presentationData: presentationData, stats: stats, sectionId: self.section, style: .blocks)
case let .growthGraph(_, _, _, graph, type),
let .membersGraph(_, _, _, graph, type),
let .newMembersBySourceGraph(_, _, _, graph, type),
let .languagesGraph(_, _, _, graph, type),
let .messagesGraph(_, _, _, graph, type),
let .actionsGraph(_, _, _, graph, type),
let .topHoursGraph(_, _, _, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster):
var textComponents: [String] = []
if topPoster.messageCount > 0 {
textComponents.append(strings.Stats_GroupTopPosterMessages(topPoster.messageCount))
if topPoster.averageChars > 0 {
textComponents.append(strings.Stats_GroupTopPosterChars(topPoster.averageChars))
}
}
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: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
arguments.openPeer(peer.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin):
var textComponents: [String] = []
if topAdmin.deletedCount > 0 {
textComponents.append(strings.Stats_GroupTopAdminDeletions(topAdmin.deletedCount))
}
if topAdmin.kickedCount > 0 {
textComponents.append(strings.Stats_GroupTopAdminKicks(topAdmin.kickedCount))
}
if topAdmin.bannedCount > 0 {
textComponents.append(strings.Stats_GroupTopAdminBans(topAdmin.bannedCount))
}
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: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
arguments.openPeer(peer.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter):
var textComponents: [String] = []
textComponents.append(strings.Stats_GroupTopInviterInvites(topInviter.inviteCount))
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: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
arguments.openPeer(peer.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
}
}
}
private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer]?, presentationData: PresentationData) -> [StatsEntry] {
var entries: [StatsEntry] = []
if let data = data {
let minDate = stringForDate(timestamp: data.period.minDate, strings: presentationData.strings)
let maxDate = stringForDate(timestamp: data.period.maxDate, strings: presentationData.strings)
entries.append(.overviewHeader(presentationData.theme, presentationData.strings.Stats_Overview, "\(minDate) \(maxDate)"))
entries.append(.overview(presentationData.theme, data))
if !data.growthGraph.isEmpty {
entries.append(.growthTitle(presentationData.theme, presentationData.strings.Stats_GroupGrowthTitle))
entries.append(.growthGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.growthGraph, .lines))
}
if !data.membersGraph.isEmpty {
entries.append(.membersTitle(presentationData.theme, presentationData.strings.Stats_GroupMembersTitle))
entries.append(.membersGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.membersGraph, .lines))
}
if !data.newMembersBySourceGraph.isEmpty {
entries.append(.newMembersBySourceTitle(presentationData.theme, presentationData.strings.Stats_GroupNewMembersBySourceTitle))
entries.append(.newMembersBySourceGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.newMembersBySourceGraph, .bars))
}
if !data.languagesGraph.isEmpty {
entries.append(.languagesTitle(presentationData.theme, presentationData.strings.Stats_GroupLanguagesTitle))
entries.append(.languagesGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.languagesGraph, .pie))
}
if !data.messagesGraph.isEmpty {
entries.append(.messagesTitle(presentationData.theme, presentationData.strings.Stats_GroupMessagesTitle))
entries.append(.messagesGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.messagesGraph, .bars))
}
if !data.actionsGraph.isEmpty {
entries.append(.actionsTitle(presentationData.theme, presentationData.strings.Stats_GroupActionsTitle))
entries.append(.actionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.actionsGraph, .lines))
}
if !data.topHoursGraph.isEmpty {
entries.append(.topHoursTitle(presentationData.theme, presentationData.strings.Stats_GroupTopHoursTitle))
entries.append(.topHoursGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.topHoursGraph, .lines))
}
if let peers = peers {
if !data.topPosters.isEmpty {
entries.append(.topPostersTitle(presentationData.theme, presentationData.strings.Stats_GroupTopPostersTitle))
var index: Int32 = 0
for topPoster in data.topPosters {
if let peer = peers[topPoster.peerId] {
entries.append(.topPoster(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topPoster))
index += 1
}
}
}
if !data.topAdmins.isEmpty {
entries.append(.topAdminsTitle(presentationData.theme, presentationData.strings.Stats_GroupTopAdminsTitle))
var index: Int32 = 0
for topAdmin in data.topAdmins {
if let peer = peers[topAdmin.peerId] {
entries.append(.topAdmin(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topAdmin))
index += 1
}
}
}
if !data.topInviters.isEmpty {
entries.append(.topInvitersTitle(presentationData.theme, presentationData.strings.Stats_GroupTopInvitersTitle))
var index: Int32 = 0
for topInviter in data.topInviters {
if let peer = peers[topInviter.peerId] {
entries.append(.topInviter(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topInviter))
index += 1
}
}
}
}
}
return entries
}
public func groupStatsController(context: AccountContext, peerId: PeerId, cachedPeerData: CachedPeerData) -> ViewController {
var openPeerImpl: ((PeerId) -> Void)?
let actionsDisposable = DisposableSet()
let dataPromise = Promise<GroupStats?>(nil)
let peersPromise = Promise<[PeerId: Peer]?>(nil)
var datacenterId: Int32 = 0
if let cachedData = cachedPeerData as? CachedChannelData {
datacenterId = cachedData.statsDatacenterId
}
let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peerId)
let dataSignal: Signal<GroupStats?, NoError> = statsContext.state
|> map { state in
return state.stats
} |> afterNext({ [weak statsContext] stats in
if let statsContext = statsContext, let stats = stats {
if case .OnDemand = stats.newMembersBySourceGraph {
statsContext.loadGrowthGraph()
statsContext.loadMembersGraph()
statsContext.loadNewMembersBySourceGraph()
statsContext.loadLanguagesGraph()
statsContext.loadMessagesGraph()
statsContext.loadActionsGraph()
statsContext.loadTopHoursGraph()
}
}
})
dataPromise.set(.single(nil) |> then(dataSignal))
peersPromise.set(.single(nil) |> then(dataPromise.get()
|> filter { value in
return value != nil
}
|> take(1)
|> map { stats -> [PeerId]? in
guard let stats = stats else {
return nil
}
var peerIds = Set<PeerId>()
peerIds.formUnion(stats.topPosters.map { $0.peerId })
peerIds.formUnion(stats.topAdmins.map { $0.peerId })
peerIds.formUnion(stats.topInviters.map { $0.peerId })
return Array(peerIds)
}
|> mapToSignal { peerIds -> Signal<[PeerId: Peer]?, NoError> in
return context.account.postbox.transaction { transaction -> [PeerId: Peer]? in
var peers: [PeerId: Peer] = [:]
if let peerIds = peerIds {
for peerId in peerIds {
if let peer = transaction.getPeer(peerId) {
peers[peerId] = peer
}
}
}
return peers
}
}))
let arguments = GroupStatsControllerArguments(context: context, loadDetailedGraph: { graph, x -> Signal<StatsGraph?, NoError> in
return statsContext.loadDetailedGraph(graph, x: x)
}, openPeer: { peerId in
openPeerImpl?(peerId)
})
let longLoadingSignal: Signal<Bool, NoError> = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue()))
let previousData = Atomic<GroupStats?>(value: nil)
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), peersPromise.get(), longLoadingSignal)
|> deliverOnMainQueue
|> map { presentationData, data, peers, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
let previous = previousData.swap(data)
var emptyStateItem: ItemListControllerEmptyStateItem?
if data == nil {
if longLoading {
emptyStateItem = StatsEmptyStateItem(theme: presentationData.theme, strings: presentationData.strings)
} else {
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
}
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelInfo_Stats), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: groupStatsControllerEntries(data: data, peers: peers, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false)
return (controllerState, (listState, arguments))
}
|> afterDisposed {
actionsDisposable.dispose()
let _ = statsContext.state
}
let controller = ItemListController(context: context, state: signal)
controller.contentOffsetChanged = { [weak controller] _, _ in
controller?.forEachItemNode({ itemNode in
if let itemNode = itemNode as? StatsGraphItemNode {
itemNode.resetInteraction()
}
})
}
controller.didDisappear = { [weak controller] _ in
controller?.clearItemNodesHighlight(animated: true)
}
openPeerImpl = { [weak controller] peerId in
if let navigationController = controller?.navigationController as? NavigationController {
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
navigationController.pushViewController(controller)
}
})
}
}
return controller
}