Group stats improvements

This commit is contained in:
Ilya Laktyushin 2020-06-19 15:19:19 +03:00
parent 9f50fa0640
commit 6b56018756
17 changed files with 4314 additions and 4054 deletions

View File

@ -5565,6 +5565,7 @@ Any member of this group will be able to see messages in the channel.";
"Stats.GroupMessagesTitle" = "MESSAGES";
"Stats.GroupActionsTitle" = "ACTIONS";
"Stats.GroupTopHoursTitle" = "TOP HOURS";
"Stats.GroupTopWeekdaysTitle" = "TOP DAYS OF WEEK";
"Stats.GroupTopPostersTitle" = "TOP MEMBERS";
"Stats.GroupTopAdminsTitle" = "TOP ADMINS";
"Stats.GroupTopInvitersTitle" = "TOP INVITERS";
@ -5583,6 +5584,9 @@ Any member of this group will be able to see messages in the channel.";
"Stats.GroupTopPosterChars_many" = "%@ symbols per message";
"Stats.GroupTopPosterChars_any" = "%@ symbols per message";
"Stats.GroupTopPoster.History" = "History";
"Stats.GroupTopPoster.Promote" = "Promote";
"Stats.GroupTopAdminDeletions_0" = "%@ deletions";
"Stats.GroupTopAdminDeletions_1" = "%@ deletion";
"Stats.GroupTopAdminDeletions_2" = "%@ deletions";
@ -5604,9 +5608,15 @@ Any member of this group will be able to see messages in the channel.";
"Stats.GroupTopAdminBans_many" = "%@ bans";
"Stats.GroupTopAdminBans_any" = "%@ bans";
"Stats.GroupTopAdmin.Actions" = "Actions";
"Stats.GroupTopAdmin.Promote" = "Promote";
"Stats.GroupTopInviterInvites_0" = "%@ invitations";
"Stats.GroupTopInviterInvites_1" = "%@ invitation";
"Stats.GroupTopInviterInvites_2" = "%@ invitations";
"Stats.GroupTopInviterInvites_3_10" = "%@ invitations";
"Stats.GroupTopInviterInvites_many" = "%@ invitations";
"Stats.GroupTopInviterInvites_any" = "%@ invitations";
"Stats.GroupTopInviter.History" = "History";
"Stats.GroupTopInviter.Promote" = "Promote";

View File

@ -214,6 +214,34 @@ public final class ChatPeerNearbyData: Equatable {
}
}
public enum ChatSearchDomain: Equatable {
case everything
case members
case member(Peer)
public static func ==(lhs: ChatSearchDomain, rhs: ChatSearchDomain) -> Bool {
switch lhs {
case .everything:
if case .everything = rhs {
return true
} else {
return false
}
case .members:
if case .members = rhs {
return true
} else {
return false
}
case let .member(lhsPeer):
if case let .member(rhsPeer) = rhs, lhsPeer.isEqual(rhsPeer) {
return true
} else {
return false
}
}
}
}
public final class NavigateToChatControllerParams {
public let navigationController: NavigationController
public let chatController: ChatController?
@ -227,7 +255,7 @@ public final class NavigateToChatControllerParams {
public let useExisting: Bool
public let purposefulAction: (() -> Void)?
public let scrollToEndIfExists: Bool
public let activateMessageSearch: Bool
public let activateMessageSearch: (ChatSearchDomain, String)?
public let peekData: ChatPeekTimeout?
public let peerNearbyData: ChatPeerNearbyData?
public let animated: Bool
@ -235,7 +263,7 @@ public final class NavigateToChatControllerParams {
public let parentGroupId: PeerGroupId?
public let completion: (ChatController) -> Void
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: Bool = false, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping (ChatController) -> Void = { _ in }) {
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping (ChatController) -> Void = { _ in }) {
self.navigationController = navigationController
self.chatController = chatController
self.context = context
@ -482,6 +510,7 @@ public protocol SharedAccountContext: class {
func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError>
func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController?
func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController?
func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
func makePeersNearbyController(context: AccountContext) -> ViewController
func makeComposeController(context: AccountContext) -> ViewController

View File

@ -666,6 +666,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
case .destructive:
color = item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor
textColor = item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor
case .accent:
color = item.presentationData.theme.list.itemDisclosureActions.accent.fillColor
textColor = item.presentationData.theme.list.itemDisclosureActions.accent.foregroundColor
}
mappedOptions.append(ItemListRevealOption(key: index, title: option.title, icon: .none, color: color, textColor: textColor))
index += 1

View File

@ -30,6 +30,12 @@ public class PercentPieChartController: BaseChartController {
let pieController: PieChartComponentController
let transitionRenderer: PercentPieAnimationRenderer
var initiallyZoomed = false
public convenience init(chartsCollection: ChartsCollection, initiallyZoomed: Bool) {
self.init(chartsCollection: chartsCollection)
self.initiallyZoomed = initiallyZoomed
}
override public init(chartsCollection: ChartsCollection) {
transitionRenderer = PercentPieAnimationRenderer()
percentController = PercentChartComponentController(isZoomed: false,
@ -95,7 +101,7 @@ public class PercentPieChartController: BaseChartController {
totalVerticalRange: BaseConstants.defaultRange)
switchToChart(chartsCollection: percentController.chartsCollection, isZoomed: false, animated: false)
if let lastDate = initialChartsCollection.axisValues.last {
if let lastDate = initialChartsCollection.axisValues.last, self.initiallyZoomed {
TimeInterval.animationDurationMultipler = 0.00001
self.didTapZoomIn(date: lastDate, animated: false)
TimeInterval.animationDurationMultipler = 1.0

View File

@ -11,6 +11,7 @@ public enum ChartType {
case lines
case twoAxis
case pie
case area
case bars
case step
case twoAxisStep
@ -76,7 +77,9 @@ public func createChartController(_ data: String, type: ChartType, getDetailsDat
controller = TwoAxisLinesChartController(chartsCollection: collection)
controller.isZoomable = false
case .pie:
controller = PercentPieChartController(chartsCollection: collection)
controller = PercentPieChartController(chartsCollection: collection, initiallyZoomed: true)
case .area:
controller = PercentPieChartController(chartsCollection: collection, initiallyZoomed: false)
case .bars:
controller = StackedBarsChartController(chartsCollection: collection)
controller.isZoomable = false

View File

@ -274,6 +274,7 @@ public enum ItemListPeerItemRevealOptionType {
case neutral
case warning
case destructive
case accent
}
public struct ItemListPeerItemRevealOption {
@ -624,6 +625,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
case .destructive:
color = item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor
textColor = item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor
case .accent:
color = item.presentationData.theme.list.itemDisclosureActions.accent.fillColor
textColor = item.presentationData.theme.list.itemDisclosureActions.accent.foregroundColor
}
mappedOptions.append(ItemListRevealOption(key: index, title: option.title, icon: .none, color: color, textColor: textColor))
index += 1

View File

@ -21,11 +21,23 @@ private final class GroupStatsControllerArguments {
let context: AccountContext
let loadDetailedGraph: (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>
let openPeer: (PeerId) -> Void
let openPeerHistory: (PeerId) -> Void
let openPeerAdminActions: (PeerId) -> Void
let promotePeer: (PeerId) -> Void
let setPostersPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let setAdminsPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let setInvitersPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPeer: @escaping (PeerId) -> Void) {
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPeer: @escaping (PeerId) -> Void, openPeerHistory: @escaping (PeerId) -> Void, openPeerAdminActions: @escaping (PeerId) -> Void, promotePeer: @escaping (PeerId) -> Void, setPostersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setAdminsPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setInvitersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void) {
self.context = context
self.loadDetailedGraph = loadDetailedGraph
self.openPeer = openPeer
self.openPeerHistory = openPeerHistory
self.openPeerAdminActions = openPeerAdminActions
self.promotePeer = promotePeer
self.setPostersPeerIdWithRevealedOptions = setPostersPeerIdWithRevealedOptions
self.setAdminsPeerIdWithRevealedOptions = setAdminsPeerIdWithRevealedOptions
self.setInvitersPeerIdWithRevealedOptions = setInvitersPeerIdWithRevealedOptions
}
}
@ -38,6 +50,7 @@ private enum StatsSection: Int32 {
case messages
case actions
case topHours
case topWeekdays
case topPosters
case topAdmins
case topInviters
@ -68,14 +81,17 @@ private enum StatsEntry: ItemListNodeEntry {
case topHoursTitle(PresentationTheme, String)
case topHoursGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case topWeekdaysTitle(PresentationTheme, String)
case topWeekdaysGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case topPostersTitle(PresentationTheme, String, String)
case topPoster(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopPoster)
case topPoster(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopPoster, Bool)
case topAdminsTitle(PresentationTheme, String, String)
case topAdmin(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopAdmin)
case topAdmin(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopAdmin, Bool)
case topInvitersTitle(PresentationTheme, String, String)
case topInviter(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopInviter)
case topInviter(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopInviter, Bool)
var section: ItemListSectionId {
switch self {
@ -95,6 +111,8 @@ private enum StatsEntry: ItemListNodeEntry {
return StatsSection.actions.rawValue
case .topHoursTitle, .topHoursGraph:
return StatsSection.topHours.rawValue
case .topWeekdaysTitle, .topWeekdaysGraph:
return StatsSection.topWeekdays.rawValue
case .topPostersTitle, .topPoster:
return StatsSection.topPosters.rawValue
case .topAdminsTitle, .topAdmin:
@ -138,17 +156,21 @@ private enum StatsEntry: ItemListNodeEntry {
return 14
case .topHoursGraph:
return 15
case .topWeekdaysTitle:
return 16
case .topWeekdaysGraph:
return 17
case .topPostersTitle:
return 1000
case let .topPoster(index, _, _, _, _, _):
case let .topPoster(index, _, _, _, _, _, _):
return 1001 + index
case .topAdminsTitle:
return 2000
case let .topAdmin(index, _, _, _, _, _):
case let .topAdmin(index, _, _, _, _, _, _):
return 2001 + index
case .topInvitersTitle:
return 3000
case let .topInviter(index, _, _, _, _, _):
case let .topInviter(index, _, _, _, _, _, _):
return 30001 + index
}
}
@ -251,14 +273,26 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .topWeekdaysTitle(lhsTheme, lhsText):
if case let .topWeekdaysTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .topWeekdaysGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .topWeekdaysGraph(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, lhsDates):
if case let .topPostersTitle(rhsTheme, rhsText, rhsDates) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsDates == rhsDates {
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 {
case let .topPoster(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopPoster, lhsRevealed):
if case let .topPoster(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopPoster, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopPoster == rhsTopPoster, lhsRevealed == rhsRevealed {
return true
} else {
return false
@ -269,8 +303,8 @@ private enum StatsEntry: ItemListNodeEntry {
} 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 {
case let .topAdmin(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopAdmin, lhsRevealed):
if case let .topAdmin(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopAdmin, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopAdmin == rhsTopAdmin, lhsRevealed == rhsRevealed {
return true
} else {
return false
@ -281,8 +315,8 @@ private enum StatsEntry: ItemListNodeEntry {
} 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 {
case let .topInviter(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopInviter, lhsRevealed):
if case let .topInviter(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopInviter, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopInviter == rhsTopInviter, lhsRevealed == rhsRevealed {
return true
} else {
return false
@ -305,7 +339,8 @@ private enum StatsEntry: ItemListNodeEntry {
let .languagesTitle(_, text),
let .messagesTitle(_, text),
let .actionsTitle(_, text),
let .topHoursTitle(_, text):
let .topHoursTitle(_, text),
let .topWeekdaysTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .topPostersTitle(_, text, dates),
let .topAdminsTitle(_, text, dates),
@ -319,9 +354,10 @@ private enum StatsEntry: ItemListNodeEntry {
let .languagesGraph(_, _, _, graph, type),
let .messagesGraph(_, _, _, graph, type),
let .actionsGraph(_, _, _, graph, type),
let .topHoursGraph(_, _, _, graph, type):
let .topHoursGraph(_, _, _, graph, type),
let .topWeekdaysGraph(_, _, _, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster):
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed):
var textComponents: [String] = []
if topPoster.messageCount > 0 {
textComponents.append(strings.Stats_GroupTopPosterMessages(topPoster.messageCount))
@ -329,10 +365,19 @@ private enum StatsEntry: ItemListNodeEntry {
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: {
var options: [ItemListPeerItemRevealOption] = []
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
arguments.openPeerHistory(peer.id)
}))
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
arguments.promotePeer(peer.id)
}))
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: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), 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):
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed):
var textComponents: [String] = []
if topAdmin.deletedCount > 0 {
textComponents.append(strings.Stats_GroupTopAdminDeletions(topAdmin.deletedCount))
@ -343,22 +388,38 @@ private enum StatsEntry: ItemListNodeEntry {
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: {
var options: [ItemListPeerItemRevealOption] = []
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopAdmin_Actions, action: {
arguments.openPeerAdminActions(peer.id)
}))
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopAdmin_Promote, action: {
arguments.promotePeer(peer.id)
}))
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: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), 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):
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed):
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: {
var options: [ItemListPeerItemRevealOption] = []
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
arguments.openPeerHistory(peer.id)
}))
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
arguments.promotePeer(peer.id)
}))
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: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
arguments.openPeer(peer.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
}, removePeer: { _ in })
}
}
}
private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer]?, presentationData: PresentationData) -> [StatsEntry] {
private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStats?, peers: [PeerId: Peer]?, presentationData: PresentationData) -> [StatsEntry] {
var entries: [StatsEntry] = []
if let data = data {
@ -404,13 +465,18 @@ private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer
entries.append(.topHoursGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.topHoursGraph, .hourlyStep))
}
if !data.topWeekdaysGraph.isEmpty {
entries.append(.topWeekdaysTitle(presentationData.theme, presentationData.strings.Stats_GroupTopWeekdaysTitle))
entries.append(.topWeekdaysGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.topWeekdaysGraph, .area))
}
if let peers = peers {
if !data.topPosters.isEmpty {
entries.append(.topPostersTitle(presentationData.theme, presentationData.strings.Stats_GroupTopPostersTitle, dates))
var index: Int32 = 0
for topPoster in data.topPosters {
if let peer = peers[topPoster.peerId], topPoster.messageCount > 0 {
entries.append(.topPoster(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topPoster))
entries.append(.topPoster(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topPoster, topPoster.peerId == state.posterPeerIdWithRevealedOptions))
index += 1
}
}
@ -420,7 +486,7 @@ private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer
var index: Int32 = 0
for topAdmin in data.topAdmins {
if let peer = peers[topAdmin.peerId], (topAdmin.deletedCount + topAdmin.kickedCount + topAdmin.bannedCount) > 0 {
entries.append(.topAdmin(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topAdmin))
entries.append(.topAdmin(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topAdmin, topAdmin.peerId == state.adminPeerIdWithRevealedOptions))
index += 1
}
}
@ -430,7 +496,7 @@ private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer
var index: Int32 = 0
for topInviter in data.topInviters {
if let peer = peers[topInviter.peerId], topInviter.inviteCount > 0 {
entries.append(.topInviter(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topInviter))
entries.append(.topInviter(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topInviter, topInviter.peerId == state.inviterPeerIdWithRevealedOptions))
index += 1
}
}
@ -441,8 +507,55 @@ private func groupStatsControllerEntries(data: GroupStats?, peers: [PeerId: Peer
return entries
}
private struct GroupStatsState: Equatable {
let posterPeerIdWithRevealedOptions: PeerId?
let adminPeerIdWithRevealedOptions: PeerId?
let inviterPeerIdWithRevealedOptions: PeerId?
init() {
self.posterPeerIdWithRevealedOptions = nil
self.adminPeerIdWithRevealedOptions = nil
self.inviterPeerIdWithRevealedOptions = nil
}
init(posterPeerIdWithRevealedOptions: PeerId?, adminPeerIdWithRevealedOptions: PeerId?, inviterPeerIdWithRevealedOptions: PeerId?) {
self.posterPeerIdWithRevealedOptions = posterPeerIdWithRevealedOptions
self.adminPeerIdWithRevealedOptions = adminPeerIdWithRevealedOptions
self.inviterPeerIdWithRevealedOptions = inviterPeerIdWithRevealedOptions
}
static func ==(lhs: GroupStatsState, rhs: GroupStatsState) -> Bool {
if lhs.posterPeerIdWithRevealedOptions != rhs.posterPeerIdWithRevealedOptions {
return false
}
if lhs.adminPeerIdWithRevealedOptions != rhs.adminPeerIdWithRevealedOptions {
return false
}
if lhs.inviterPeerIdWithRevealedOptions != rhs.inviterPeerIdWithRevealedOptions {
return false
}
return true
}
func withUpdatedPosterPeerIdWithRevealedOptions(_ posterPeerIdWithRevealedOptions: PeerId?) -> GroupStatsState {
return GroupStatsState(posterPeerIdWithRevealedOptions: posterPeerIdWithRevealedOptions, adminPeerIdWithRevealedOptions: posterPeerIdWithRevealedOptions != nil ? nil : self.adminPeerIdWithRevealedOptions, inviterPeerIdWithRevealedOptions: posterPeerIdWithRevealedOptions != nil ? nil : self.inviterPeerIdWithRevealedOptions)
}
func withUpdatedAdminPeerIdWithRevealedOptions(_ adminPeerIdWithRevealedOptions: PeerId?) -> GroupStatsState {
return GroupStatsState(posterPeerIdWithRevealedOptions: adminPeerIdWithRevealedOptions != nil ? nil : self.posterPeerIdWithRevealedOptions, adminPeerIdWithRevealedOptions: adminPeerIdWithRevealedOptions, inviterPeerIdWithRevealedOptions: adminPeerIdWithRevealedOptions != nil ? nil : self.inviterPeerIdWithRevealedOptions)
}
func withUpdatedInviterPeerIdWithRevealedOptions(_ inviterPeerIdWithRevealedOptions: PeerId?) -> GroupStatsState {
return GroupStatsState(posterPeerIdWithRevealedOptions: inviterPeerIdWithRevealedOptions != nil ? nil : self.posterPeerIdWithRevealedOptions, adminPeerIdWithRevealedOptions: inviterPeerIdWithRevealedOptions != nil ? nil : self.adminPeerIdWithRevealedOptions, inviterPeerIdWithRevealedOptions: inviterPeerIdWithRevealedOptions)
}
}
public func groupStatsController(context: AccountContext, peerId: PeerId, cachedPeerData: CachedPeerData) -> ViewController {
var openPeerImpl: ((PeerId) -> Void)?
let statePromise = ValuePromise(GroupStatsState())
let stateValue = Atomic(value: GroupStatsState())
let updateState: ((GroupStatsState) -> GroupStatsState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
let actionsDisposable = DisposableSet()
let dataPromise = Promise<GroupStats?>(nil)
@ -453,13 +566,21 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
datacenterId = cachedData.statsDatacenterId
}
var openPeerImpl: ((PeerId) -> Void)?
var openPeerHistoryImpl: ((PeerId) -> Void)?
var openPeerAdminActionsImpl: ((PeerId) -> Void)?
var promotePeerImpl: ((PeerId) -> Void)?
let peerView = Promise<PeerView>()
peerView.set(context.account.viewTracker.peerView(peerId, updateData: true))
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 {
if case .OnDemand = stats.topWeekdaysGraph {
statsContext.loadGrowthGraph()
statsContext.loadMembersGraph()
statsContext.loadNewMembersBySourceGraph()
@ -467,6 +588,7 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
statsContext.loadMessagesGraph()
statsContext.loadActionsGraph()
statsContext.loadTopHoursGraph()
statsContext.loadTopWeekdaysGraph()
}
}
})
@ -505,15 +627,45 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
return statsContext.loadDetailedGraph(graph, x: x)
}, openPeer: { peerId in
openPeerImpl?(peerId)
}, openPeerHistory: { peerId in
openPeerHistoryImpl?(peerId)
}, openPeerAdminActions: { peerId in
openPeerAdminActionsImpl?(peerId)
}, promotePeer: { peerId in
promotePeerImpl?(peerId)
}, setPostersPeerIdWithRevealedOptions: { peerId, fromPeerId in
updateState { state in
if (peerId == nil && fromPeerId == state.posterPeerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
return state.withUpdatedPosterPeerIdWithRevealedOptions(peerId)
} else {
return state
}
}
}, setAdminsPeerIdWithRevealedOptions: { peerId, fromPeerId in
updateState { state in
if (peerId == nil && fromPeerId == state.adminPeerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
return state.withUpdatedAdminPeerIdWithRevealedOptions(peerId)
} else {
return state
}
}
}, setInvitersPeerIdWithRevealedOptions: { peerId, fromPeerId in
updateState { state in
if (peerId == nil && fromPeerId == state.inviterPeerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) {
return state.withUpdatedInviterPeerIdWithRevealedOptions(peerId)
} else {
return state
}
}
})
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)
let signal = combineLatest(statePromise.get(), context.sharedContext.presentationData, dataPromise.get(), peersPromise.get(), longLoadingSignal)
|> deliverOnMainQueue
|> map { presentationData, data, peers, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
|> map { state, presentationData, data, peers, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
let previous = previousData.swap(data)
var emptyStateItem: ItemListControllerEmptyStateItem?
if data == nil {
@ -525,7 +677,7 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
}
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)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: groupStatsControllerEntries(state: state, data: data, peers: peers, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false)
return (controllerState, (listState, arguments))
}
@ -556,5 +708,35 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
})
}
}
openPeerHistoryImpl = { [weak controller] participantPeerId in
if let navigationController = controller?.navigationController as? NavigationController {
let _ = (context.account.postbox.loadedPeerWithId(participantPeerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peerId), subject: nil, botStart: nil, updateTextInputState: nil, activateInput: false, keepStack: .always, useExisting: false, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: (.member(peer), ""), animated: true))
})
}
}
openPeerAdminActionsImpl = { [weak controller] participantPeerId in
if let navigationController = controller?.navigationController as? NavigationController {
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
let controller = context.sharedContext.makeChatRecentActionsController(context: context, peer: peer)
navigationController.pushViewController(controller)
})
}
}
promotePeerImpl = { [weak controller] participantPeerId in
if let navigationController = controller?.navigationController as? NavigationController {
let _ = (fetchChannelParticipant(account: context.account, peerId: peerId, participantId: participantPeerId)
|> take(1)
|> deliverOnMainQueue).start(next: { participant in
if let participant = participant, let controller = context.sharedContext.makeChannelAdminController(context: context, peerId: peerId, adminId: participantPeerId, initialParticipant: participant) {
navigationController.pushViewController(controller)
}
})
}
}
return controller
}

View File

@ -544,7 +544,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) }
dict[1957577280] = { return Api.Updates.parse_updates($0) }
dict[301019932] = { return Api.Updates.parse_updateShortSentMessage($0) }
dict[447818040] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }

View File

@ -663,13 +663,13 @@ public struct stats {
}
public enum MegagroupStats: TypeConstructorDescription {
case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User])
case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let topPosters, let topAdmins, let topInviters, let users):
case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users):
if boxed {
buffer.appendInt32(447818040)
buffer.appendInt32(-276825834)
}
period.serialize(buffer, true)
members.serialize(buffer, true)
@ -683,6 +683,7 @@ public struct stats {
messagesGraph.serialize(buffer, true)
actionsGraph.serialize(buffer, true)
topHoursGraph.serialize(buffer, true)
weekdaysGraph.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(topPosters.count))
for item in topPosters {
@ -709,8 +710,8 @@ public struct stats {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let topPosters, let topAdmins, let topInviters, let users):
return ("megagroupStats", [("period", period), ("members", members), ("messages", messages), ("viewers", viewers), ("posters", posters), ("growthGraph", growthGraph), ("membersGraph", membersGraph), ("newMembersBySourceGraph", newMembersBySourceGraph), ("languagesGraph", languagesGraph), ("messagesGraph", messagesGraph), ("actionsGraph", actionsGraph), ("topHoursGraph", topHoursGraph), ("topPosters", topPosters), ("topAdmins", topAdmins), ("topInviters", topInviters), ("users", users)])
case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users):
return ("megagroupStats", [("period", period), ("members", members), ("messages", messages), ("viewers", viewers), ("posters", posters), ("growthGraph", growthGraph), ("membersGraph", membersGraph), ("newMembersBySourceGraph", newMembersBySourceGraph), ("languagesGraph", languagesGraph), ("messagesGraph", messagesGraph), ("actionsGraph", actionsGraph), ("topHoursGraph", topHoursGraph), ("weekdaysGraph", weekdaysGraph), ("topPosters", topPosters), ("topAdmins", topAdmins), ("topInviters", topInviters), ("users", users)])
}
}
@ -763,21 +764,25 @@ public struct stats {
if let signature = reader.readInt32() {
_12 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _13: [Api.StatsGroupTopPoster]?
if let _ = reader.readInt32() {
_13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self)
var _13: Api.StatsGraph?
if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _14: [Api.StatsGroupTopAdmin]?
var _14: [Api.StatsGroupTopPoster]?
if let _ = reader.readInt32() {
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self)
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self)
}
var _15: [Api.StatsGroupTopInviter]?
var _15: [Api.StatsGroupTopAdmin]?
if let _ = reader.readInt32() {
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self)
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self)
}
var _16: [Api.User]?
var _16: [Api.StatsGroupTopInviter]?
if let _ = reader.readInt32() {
_16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self)
}
var _17: [Api.User]?
if let _ = reader.readInt32() {
_17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -795,8 +800,9 @@ public struct stats {
let _c14 = _14 != nil
let _c15 = _15 != nil
let _c16 = _16 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 {
return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, topPosters: _13!, topAdmins: _14!, topInviters: _15!, users: _16!)
let _c17 = _17 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!)
}
else {
return nil

View File

@ -553,11 +553,12 @@ public final class GroupStats: Equatable {
public let messagesGraph: StatsGraph
public let actionsGraph: StatsGraph
public let topHoursGraph: StatsGraph
public let topWeekdaysGraph: StatsGraph
public let topPosters: [GroupStatsTopPoster]
public let topAdmins: [GroupStatsTopAdmin]
public let topInviters: [GroupStatsTopInviter]
init(period: StatsDateRange, members: StatsValue, messages: StatsValue, viewers: StatsValue, posters: StatsValue, growthGraph: StatsGraph, membersGraph: StatsGraph, newMembersBySourceGraph: StatsGraph, languagesGraph: StatsGraph, messagesGraph: StatsGraph, actionsGraph: StatsGraph, topHoursGraph: StatsGraph, topPosters: [GroupStatsTopPoster], topAdmins: [GroupStatsTopAdmin], topInviters: [GroupStatsTopInviter]) {
init(period: StatsDateRange, members: StatsValue, messages: StatsValue, viewers: StatsValue, posters: StatsValue, growthGraph: StatsGraph, membersGraph: StatsGraph, newMembersBySourceGraph: StatsGraph, languagesGraph: StatsGraph, messagesGraph: StatsGraph, actionsGraph: StatsGraph, topHoursGraph: StatsGraph, topWeekdaysGraph: StatsGraph, topPosters: [GroupStatsTopPoster], topAdmins: [GroupStatsTopAdmin], topInviters: [GroupStatsTopInviter]) {
self.period = period
self.members = members
self.messages = messages
@ -570,6 +571,7 @@ public final class GroupStats: Equatable {
self.messagesGraph = messagesGraph
self.actionsGraph = actionsGraph
self.topHoursGraph = topHoursGraph
self.topWeekdaysGraph = topWeekdaysGraph
self.topPosters = topPosters
self.topAdmins = topAdmins
self.topInviters = topInviters
@ -612,6 +614,9 @@ public final class GroupStats: Equatable {
if lhs.topHoursGraph != rhs.topHoursGraph {
return false
}
if lhs.topWeekdaysGraph != rhs.topWeekdaysGraph {
return false
}
if lhs.topPosters != rhs.topPosters {
return false
}
@ -625,31 +630,35 @@ public final class GroupStats: Equatable {
}
public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedMembersGraph(_ membersGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedNewMembersBySourceGraph(_ newMembersBySourceGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedLanguagesGraph(_ languagesGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedMessagesGraph(_ messagesGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedActionsGraph(_ actionsGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: actionsGraph, topHoursGraph: self.topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedTopHoursGraph(_ topHoursGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: topHoursGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: topHoursGraph, topWeekdaysGraph: self.topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
public func withUpdatedTopWeekdaysGraph(_ topWeekdaysGraph: StatsGraph) -> GroupStats {
return GroupStats(period: self.period, members: self.members, messages: self.messages, viewers: self.viewers, posters: self.posters, growthGraph: self.growthGraph, membersGraph: self.membersGraph, newMembersBySourceGraph: self.newMembersBySourceGraph, languagesGraph: self.languagesGraph, messagesGraph: self.messagesGraph, actionsGraph: self.actionsGraph, topHoursGraph: self.topHoursGraph, topWeekdaysGraph: topWeekdaysGraph, topPosters: self.topPosters, topAdmins: self.topAdmins, topInviters: self.topInviters)
}
}
@ -684,7 +693,7 @@ private func requestGroupStats(postbox: Postbox, network: Network, datacenterId:
return signal
|> mapToSignal { result -> Signal<GroupStats?, MTRpcError> in
return postbox.transaction { transaction -> GroupStats? in
if case let .megagroupStats(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, users) = result {
if case let .megagroupStats(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, users) = result {
var parsedUsers: [Peer] = []
for user in users {
parsedUsers.append(TelegramUser(user: user))
@ -858,6 +867,21 @@ private final class GroupStatsContextImpl {
}
}
func loadTopWeekdaysGraph() {
guard let stats = self._state.stats else {
return
}
if case let .OnDemand(token) = stats.topWeekdaysGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopWeekdaysGraph(graph))
strongSelf._statePromise.set(.single(strongSelf._state))
}
}), forKey: token)
}
}
func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
if let token = graph.token {
return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x)
@ -930,6 +954,12 @@ public final class GroupStatsContext {
}
}
public func loadTopWeekdaysGraph() {
self.impl.with { impl in
impl.loadTopWeekdaysGraph()
}
}
public func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -973,7 +1003,11 @@ extension StatsGraph {
case let .statsGraphError(error):
self = .Failed(error: error)
case let .statsGraphAsync(token):
if !token.isEmpty {
self = .OnDemand(token: token)
} else {
self = .Failed(error: "An error occured. Please try again later.")
}
}
}
}
@ -1056,11 +1090,11 @@ extension GroupStatsTopInviter {
extension GroupStats {
convenience init(apiMegagroupStats: Api.stats.MegagroupStats) {
switch apiMegagroupStats {
case let .megagroupStats(period, members, messages, viewers, posters, apiGrowthGraph, apiMembersGraph, apiNewMembersBySourceGraph, apiLanguagesGraph, apiMessagesGraph, apiActionsGraph, apiTopHoursGraph, topPosters, topAdmins, topInviters, users):
case let .megagroupStats(period, members, messages, viewers, posters, apiGrowthGraph, apiMembersGraph, apiNewMembersBySourceGraph, apiLanguagesGraph, apiMessagesGraph, apiActionsGraph, apiTopHoursGraph, apiTopWeekdaysGraph, topPosters, topAdmins, topInviters, users):
let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph)
let isEmpty = growthGraph.isEmpty
self.init(period: StatsDateRange(apiStatsDateRangeDays: period), members: StatsValue(apiStatsAbsValueAndPrev: members), messages: StatsValue(apiStatsAbsValueAndPrev: messages), viewers: StatsValue(apiStatsAbsValueAndPrev: viewers), posters: StatsValue(apiStatsAbsValueAndPrev: posters), growthGraph: growthGraph, membersGraph: StatsGraph(apiStatsGraph: apiMembersGraph), newMembersBySourceGraph: StatsGraph(apiStatsGraph: apiNewMembersBySourceGraph), languagesGraph: StatsGraph(apiStatsGraph: apiLanguagesGraph), messagesGraph: StatsGraph(apiStatsGraph: apiMessagesGraph), actionsGraph: StatsGraph(apiStatsGraph: apiActionsGraph), topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph), topPosters: topPosters.map { GroupStatsTopPoster(apiStatsGroupTopPoster: $0) }, topAdmins: topAdmins.map { GroupStatsTopAdmin(apiStatsGroupTopAdmin: $0) }, topInviters: topInviters.map { GroupStatsTopInviter(apiStatsGroupTopInviter: $0) })
self.init(period: StatsDateRange(apiStatsDateRangeDays: period), members: StatsValue(apiStatsAbsValueAndPrev: members), messages: StatsValue(apiStatsAbsValueAndPrev: messages), viewers: StatsValue(apiStatsAbsValueAndPrev: viewers), posters: StatsValue(apiStatsAbsValueAndPrev: posters), growthGraph: growthGraph, membersGraph: StatsGraph(apiStatsGraph: apiMembersGraph), newMembersBySourceGraph: StatsGraph(apiStatsGraph: apiNewMembersBySourceGraph), languagesGraph: StatsGraph(apiStatsGraph: apiLanguagesGraph), messagesGraph: StatsGraph(apiStatsGraph: apiMessagesGraph), actionsGraph: StatsGraph(apiStatsGraph: apiActionsGraph), topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph), topWeekdaysGraph: StatsGraph(apiStatsGraph: apiTopWeekdaysGraph), topPosters: topPosters.map { GroupStatsTopPoster(apiStatsGroupTopPoster: $0) }, topAdmins: topAdmins.map { GroupStatsTopAdmin(apiStatsGroupTopAdmin: $0) }, topInviters: topInviters.map { GroupStatsTopInviter(apiStatsGroupTopInviter: $0) })
}
}
}

View File

@ -314,7 +314,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var isDismissed = false
private var focusOnSearchAfterAppearance: Bool = false
private var focusOnSearchAfterAppearance: (ChatSearchDomain, String)?
private let keepPeerInfoScreenDataHotDisposable = MetaDisposable()
@ -4738,9 +4738,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.interfaceInteraction = interfaceInteraction
if self.focusOnSearchAfterAppearance {
self.focusOnSearchAfterAppearance = false
self.interfaceInteraction?.beginMessageSearch(.everything, "")
if let search = self.focusOnSearchAfterAppearance {
self.focusOnSearchAfterAppearance = nil
self.interfaceInteraction?.beginMessageSearch(search.0, search.1)
}
self.chatDisplayNode.interfaceInteraction = interfaceInteraction
@ -5005,8 +5005,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
if self.focusOnSearchAfterAppearance {
self.focusOnSearchAfterAppearance = false
if let _ = self.focusOnSearchAfterAppearance {
self.focusOnSearchAfterAppearance = nil
if let searchNode = self.navigationBar?.contentNode as? ChatSearchNavigationContentNode {
searchNode.activate()
}
@ -9253,9 +9253,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
func activateSearch() {
self.focusOnSearchAfterAppearance = true
self.interfaceInteraction?.beginMessageSearch(.everything, "")
func activateSearch(domain: ChatSearchDomain = .everything, query: String = "") {
self.focusOnSearchAfterAppearance = (domain, query)
self.interfaceInteraction?.beginMessageSearch(domain, query)
}
}

View File

@ -129,35 +129,6 @@ struct ChatSearchResultsState: Equatable {
let completed: Bool
}
enum ChatSearchDomain: Equatable {
case everything
case members
case member(Peer)
static func ==(lhs: ChatSearchDomain, rhs: ChatSearchDomain) -> Bool {
switch lhs {
case .everything:
if case .everything = rhs {
return true
} else {
return false
}
case .members:
if case .members = rhs {
return true
} else {
return false
}
case let .member(lhsPeer):
if case let .member(rhsPeer) = rhs, lhsPeer.isEqual(rhsPeer) {
return true
} else {
return false
}
}
}
}
enum ChatSearchDomainSuggestionContext: Equatable {
case none
case members(String)

View File

@ -34,8 +34,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
controller.scrollToEndOfHistory()
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
params.completion(controller)
} else if params.activateMessageSearch {
controller.activateSearch()
} else if let search = params.activateMessageSearch {
controller.activateSearch(domain: search.0, query: search.1)
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
params.completion(controller)
} else {
@ -70,8 +70,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.subject, botStart: params.botStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData)
}
controller.purposefulAction = params.purposefulAction
if params.activateMessageSearch {
controller.activateSearch()
if let search = params.activateMessageSearch {
controller.activateSearch(domain: search.0, query: search.1)
}
let resolvedKeepStack: Bool
switch params.keepStack {

View File

@ -2410,7 +2410,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
private func openChatWithMessageSearch() {
if let navigationController = (self.controller?.navigationController as? NavigationController) {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), activateMessageSearch: true))
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), activateMessageSearch: (.everything, "")))
}
}

View File

@ -1014,6 +1014,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return controller
}
public func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController? {
let controller = channelAdminController(context: context, peerId: peerId, adminId: adminId, initialParticipant: initialParticipant, updated: { _ in }, upgradedToSupergroup: { _, _ in }, transferedOwnership: { _ in })
return controller
}
public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
openExternalUrlImpl(context: context, urlContext: urlContext, url: url, forceExternal: forceExternal, presentationData: presentationData, navigationController: navigationController, dismissInput: dismissInput)
}